This commit is contained in:
doudou201703
2026-08-07 21:40:55 +08:00
commit 5955119fff
81 changed files with 7867 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
+6
View File
@@ -0,0 +1,6 @@
node_modules
dist
out
.DS_Store
.eslintcache
*.log*
+10
View File
@@ -0,0 +1,10 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# 已忽略包含查询文件的默认文件夹
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="LanguageServerSettingsState">
<state>
<map>
<entry key="8597695b-6414-4110-b1ec-388313143283">
<value>
<LanguageServerDefinitionSettings>
<option name="debugPort" value="" />
<option name="debugSuspend" value="true" />
<option name="errorReportingKind" value="as_notification" />
<option name="serverTrace" value="off" />
</LanguageServerDefinitionSettings>
</value>
</entry>
</map>
</state>
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="cn.fjdmy.uniapp.UniappProjectDataService">
<option name="type" value="no" />
</component>
</project>
+70
View File
@@ -0,0 +1,70 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<HTMLCodeStyleSettings>
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
</HTMLCodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</JSCodeStyleSettings>
<TypeScriptCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</TypeScriptCodeStyleSettings>
<VueCodeStyleSettings>
<option name="INDENT_CHILDREN_OF_TOP_LEVEL" value="template, script, style" />
<option name="UNIFORM_INDENT" value="false" />
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
</VueCodeStyleSettings>
<codeStyleSettings language="HTML">
<option name="SOFT_MARGINS" value="80" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JSON">
<indentOptions>
<option name="INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="SOFT_MARGINS" value="80" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="TypeScript">
<option name="SOFT_MARGINS" value="80" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Vue">
<option name="SOFT_MARGINS" value="80" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="yaml">
<indentOptions>
<option name="INDENT_SIZE" value="4" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>
+5
View File
@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/renderer/src/env.d.ts" charset="US-ASCII" />
<file url="file://$PROJECT_DIR$/src/renderer/src/stores/index.ts" charset="US-ASCII" />
<file url="file://$PROJECT_DIR$/src/renderer/src/utils/request.ts" charset="US-ASCII" />
<file url="file://$PROJECT_DIR$/src/renderer/src/主页面/demo.vue" charset="US-ASCII" />
<file url="file://$PROJECT_DIR$/src/renderer/src/主页面/中文组件.vue" charset="US-ASCII" />
</component>
</project>
+15
View File
@@ -0,0 +1,15 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ES6ShorthandObjectProperty" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSNonASCIINames" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NonAsciiCharacters" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="TsLint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TypeScriptExplicitMemberType" enabled="false" level="INFORMATION" enabled_by_default="false" />
</profile>
</component>
+31
View File
@@ -0,0 +1,31 @@
<component name="libraryTable">
<library name="Dart SDK">
<CLASSES>
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/_internal" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/async" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/cli" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/collection" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/concurrent" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/convert" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/core" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/developer" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/ffi" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/html" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/indexed_db" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/io" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/isolate" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/js" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/js_interop" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/js_interop_unsafe" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/js_util" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/math" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/mirrors" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/svg" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/typed_data" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/web_audio" />
<root url="file:///opt/homebrew/Caskroom/flutter/3.44.2/flutter/bin/cache/dart-sdk/lib/web_gl" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+8
View File
@@ -0,0 +1,8 @@
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="FLOW" />
</component>
<component name="ProjectRootManager" version="2">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/xiao_electron.iml" filepath="$PROJECT_DIR$/.idea/xiao_electron.iml" />
</modules>
</component>
</project>
Generated
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MessDetectorOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCSFixerOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCodeSnifferOptionsConfiguration">
<option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" />
</component>
<component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" />
</component>
</project>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myConfigurationMode" value="DISABLED" />
<option name="myRunOnSave" value="true" />
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SwiftToolchain">
<option name="toolchain" value="system-/usr/bin/swift" />
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="wxLogin" level="application" />
<orderEntry type="library" name="wx-login" level="application" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/node_modules/app-builder-lib/templates" />
</list>
</option>
</component>
</module>
+2
View File
@@ -0,0 +1,2 @@
electron_mirror=https://npmmirror.com/mirrors/electron/
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
+31
View File
@@ -0,0 +1,31 @@
# 是否加分号
semi: true
# 一行最多多少个字符换行
printWidth: 100
# 末尾逗号,可选 'none' | 'es5' | 'all'
trailingComma: 'all'
# 缩进空格数
tabWidth: 4
# 对象大括号间是否加空格 { foo: bar }
bracketSpacing: true
# 箭头函数参数是否加括号 (x) => x
arrowParens: 'always'
# 换行符类型
endOfLine: 'auto'
# 是否使用单引号 这里配置的是不使用 单引号 为true则为使用单引号
singleQuote: false
# 对象属性名是否加引号
quoteProps: 'as-needed'
# JSX 中是否使用单引号
jsxSingleQuote: false
# 作用:Vue <script> 和 <style> 是否缩进
vueIndentScriptAndStyle: true
+3
View File
@@ -0,0 +1,3 @@
{
"recommendations": ["dbaeumer.vscode-eslint"]
}
+39
View File
@@ -0,0 +1,39 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Main Process",
"type": "node",
"request": "launch",
"cwd": "${workspaceRoot}",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite",
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
},
"runtimeArgs": ["--sourcemap"],
"env": {
"REMOTE_DEBUGGING_PORT": "9222"
}
},
{
"name": "Debug Renderer Process",
"port": 9222,
"request": "attach",
"type": "chrome",
"webRoot": "${workspaceFolder}/src/renderer",
"timeout": 60000,
"presentation": {
"hidden": true
}
}
],
"compounds": [
{
"name": "Debug All",
"configurations": ["Debug Main Process", "Debug Renderer Process"],
"presentation": {
"order": 1
}
}
]
}
+11
View File
@@ -0,0 +1,11 @@
{
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
+34
View File
@@ -0,0 +1,34 @@
# xiao_electron
An Electron application with Vue and TypeScript
## Recommended IDE Setup
- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
## Project Setup
### Install
```bash
$ yarn
```
### Development
```bash
$ yarn dev
```
### Build
```bash
# For windows
$ yarn build:win
# For macOS
$ yarn build:mac
# For Linux
$ yarn build:linux
```
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>
</plist>
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

+3
View File
@@ -0,0 +1,3 @@
provider: generic
url: https://example.com/auto-updates
updaterCacheDirName: xiao_electron-updater
+56
View File
@@ -0,0 +1,56 @@
appId: com.electron.app
productName: 六果服务商管理后台
# ✅✅✅ 关键:声明自定义协议 ✅✅✅
protocols:
name: myapp
schemes:
- myapp # 👈 这里就是你想要的 myapp://
directories:
buildResources: build
files:
- '!**/.vscode/*'
- '!src/*'
- '!electron.vite.config.{js,ts,mjs,cjs}'
- '!{.eslintcache,eslint.config.mjs,.prettierignore,..prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
asarUnpack:
- resources/**
win:
executableName: xiao_electron
nsis:
artifactName: ${name}-${version}-setup.${ext}
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always
mac:
entitlementsInherit: build/entitlements.mac.plist
extendInfo:
- NSCameraUsageDescription: Application requests access to the device's camera.
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
- CFBundleURLTypes:
- CFBundleURLSchemes:
- myapp # ✅ macOS 必须
CFBundleURLName: com.electron.app
notarize: false
dmg:
artifactName: ${name}-${version}.${ext}
linux:
target:
- AppImage
- snap
- deb
maintainer: electronjs.org
category: Utility
appImage:
artifactName: ${name}-${version}.${ext}
npmRebuild: false
publish:
provider: generic
url: https://example.com/auto-updates
electronDownload:
mirror: https://npmmirror.com/mirrors/electron/
+45
View File
@@ -0,0 +1,45 @@
import { resolve } from "path";
import { defineConfig } from "electron-vite";
import vue from "@vitejs/plugin-vue";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
export default defineConfig({
main: {},
preload: {},
renderer: {
resolve: {
alias: {
"@renderer": resolve("src/renderer/src")
}
},
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()]
}),
Components({
resolvers: [ElementPlusResolver()]
})
],
server: {
proxy: {
"/api": {
// target: "http://127.0.0.1:8000/", // 目标服务器地址
target: "https://six.sixgou.com/", // 目标服务器地址
changeOrigin: true, // 开启跨域
// 重写路径,将 /api/six_demo/demo 重写为 /six_demo/demo
rewrite: (path) => path.replace(/^\/api/, "")
},
"/map": {
target: "https://datav.aliyun.com",
changeOrigin: true, // 开启跨域
// 重写路径,将 /api/six_demo/demo 重写为 /six_demo/demo
rewrite: (path) => path.replace(/^\/map/, "")
}
},
allowedHosts: ["xiao.sixgou.com"]
}
}
});
+49
View File
@@ -0,0 +1,49 @@
// import { defineConfig } from "eslint/config";
import tseslint from "@electron-toolkit/eslint-config-ts";
import eslintConfigPrettier from "@electron-toolkit/eslint-config-prettier";
import eslintPluginVue from "eslint-plugin-vue";
import vueParser from "vue-eslint-parser";
import { defineConfig } from "@eslint/config-helpers"; // 👈 导入这个 defineConfig
export default defineConfig(
{ ignores: ["**/node_modules", "**/dist", "**/out"] },
tseslint.configs.recommended,
eslintPluginVue.configs["flat/recommended"],
{
files: ["**/*.vue"],
languageOptions: {
parser: vueParser,
parserOptions: {
ecmaFeatures: {
jsx: true
},
extraFileExtensions: [".vue"],
parser: tseslint.parser
}
}
},
{
files: ["**/*.{ts,mts,tsx,vue}"],
rules: {
"no-undef": "off", // ✅ 关键
"vue/require-default-prop": "off",
"vue/multi-word-component-names": "off",
"vue/block-lang": [
"error",
{
script: {
lang: "ts"
}
}
],
"@typescript-eslint/no-unused-vars": [
"warn",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_"
}
]
}
},
eslintConfigPrettier
);
Vendored
+48
View File
@@ -0,0 +1,48 @@
// // src/types/global.d.ts
// export {};
//
// declare global {
// interface Window {
// electron: typeof import("@electron-toolkit/preload").electronAPI;
// api: {
// xiao: string;
// };
// demo_xiao: {
// demoFun: (data: string) => void;
// read_file: () => Promise<string>;
// onBarcodeScanned: (callback: BarcodeCallback) => void;
// };
// electronAPI: (callback: (args) => void) => Promise<string>;
// 向主进程发送消息事件监听器: (msg_data: string) => void;
// 主进程向渲染进程发消息: (回调函数: (返回的数据: string) => void) => void;
// }
// }
// src/types/global.d.ts
export {};
declare global {
interface Window {
/* ===== 你原来的 ===== */
electron: typeof import("@electron-toolkit/preload").electronAPI;
//
// api: {
// xiao: string;
// };
//
// demo_xiao: {
// demoFun: (data: string) => void;
// read_file: () => Promise<string>;
// onBarcodeScanned: (callback: BarcodeCallback) => void;
// };
//
// electronAPI: (callback: (args: any) => void) => Promise<string>;
//
// 向主进程发送消息事件监听器: (msg_data: string) => void;
// 主进程向渲染进程发消息: (回调函数: (返回的数据: string) => void) => void;
// on发送登陆状态事件: (回调函数: (推送的值: WeChatUserInfoType) => void) => void;
/* ===== 新增:微信 PC OpenSDK ===== */
}
}
+63
View File
@@ -0,0 +1,63 @@
{
"name": "six",
"version": "1.0.0",
"description": "An Electron application with Vue and TypeScript",
"main": "./out/main/index.js",
"author": "example.com",
"homepage": "https://electron-vite.org",
"scripts": {
"format": "prettier --write .",
"lint": "eslint --cache .",
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
"typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false",
"typecheck": "npm run typecheck:node && npm run typecheck:web",
"start": "electron-vite preview",
"dev": "electron-vite dev",
"build": "npm run typecheck && electron-vite build",
"postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --dir",
"build:win": "npm run build && electron-builder --win",
"build:mac": "npm run build && electron-builder --mac",
"build:linux": "npm run build && electron-builder --linux"
},
"dependencies": {
"@cordova-ohos/cordova-plugin-wechat": "^3.1.0",
"@electron-toolkit/preload": "^3.0.2",
"@electron-toolkit/utils": "^4.0.0",
"@element-plus/icons-vue": "^2.3.2",
"@vueuse/core": "^14.3.0",
"axios": "^1.18.0",
"echarts": "^6.1.0",
"electron-updater": "^6.3.9",
"element-plus": "^2.14.2",
"node-global-key-listener": "^0.3.0",
"pinia": "^3.0.4",
"pinia-plugin-persistedstate": "^4.7.1",
"unplugin-element-plus": "^0.11.2",
"vue-echarts": "^8.0.1",
"vue-next-wxlogin": "^1.0.4",
"vue-router": "4"
},
"devDependencies": {
"@electron-toolkit/eslint-config-prettier": "3.0.0",
"@electron-toolkit/eslint-config-ts": "^3.1.0",
"@electron-toolkit/tsconfig": "^2.0.0",
"@eslint/config-helpers": "^0.6.0",
"@types/node": "^22.19.1",
"@vitejs/plugin-vue": "^6.0.2",
"electron": "^39.2.6",
"electron-builder": "^26.0.12",
"electron-vite": "^5.0.0",
"eslint": "^9.39.1",
"eslint-plugin-vue": "^10.6.2",
"prettier": "^3.7.4",
"sass-embedded": "^1.100.0",
"typescript": "^5.9.3",
"unplugin-auto-import": "^21.0.0",
"unplugin-vue-components": "^32.1.0",
"vite": "^7.2.6",
"vue": "^3.5.25",
"vue-eslint-parser": "^10.2.0",
"vue-tsc": "^3.1.6"
}
}
+6
View File
@@ -0,0 +1,6 @@
out
dist
#pnpm-lock.yaml
LICENSE.md
#tsconfig.json
#tsconfig.*.json
Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

+149
View File
@@ -0,0 +1,149 @@
import { app, BrowserWindow, ipcMain, screen, shell } from "electron";
import { join } from "path";
import { electronApp, is, optimizer } from "@electron-toolkit/utils";
import icon from "../../resources/icon.png?asset";
import { initGlobalScanner } from "./key";
//
let mainWindow: BrowserWindow | null = null;
// // ✅ 注册 myapp 协议
app.setAsDefaultProtocolClient("myapp");
// ✅ 2. macOS 监听
app.on("open-url", (event, url) => {
event.preventDefault();
// console.log("✅ macOS 收到 myapp:", url);
let uri = new URL(url);
const paramsObj = Object.fromEntries(uri.searchParams.entries());
// console.log("解析后的参数对象:", paramsObj);
if (mainWindow && mainWindow.webContents) {
mainWindow.webContents.send("wx", paramsObj);
}
});
// ✅ 3. Windows / Linux 监听
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) app.quit();
app.on("second-instance", (_, argv) => {
const url = argv.find(arg => arg.startsWith("myapp://"));
if (url) {
console.log("✅ Windows/Linux 收到 myapp:", url);
}
});
function createWindow(windowWidth: number = 1920, windowHeight: number = 997): void {
mainWindow = new BrowserWindow({
width: windowWidth, // 设置窗口宽度
height: windowHeight, //设置窗口高度
minHeight: windowHeight,
show: false,
x: 0,
y: 0,
autoHideMenuBar: true,
// accentColor: "rgb(254, 38, 53)",
// titleBarStyle: "hidden",
// backgroundColor: "#2e2c29", //设置背景颜色
// frame: false, // 这里可以去掉标题栏,Mac上的体现为 左上角的 x 没有了
backgroundMaterial: "auto", //设置窗口的系统绘制背景材质,包括非客户区的背景。
title: "江苏六果科技有限公司", //默认窗口标题。默认值为:"Electron"。 如果由loadURL()加载的HTML文件中含有标签<title>,此属性将被忽略。
...(process.platform === "linux" ? { icon } : {}),
webPreferences: {
preload: join(__dirname, "../preload/index.js"),
sandbox: false,
webSecurity: true
}
});
initGlobalScanner(mainWindow); // ★ 现在 mainWindow 已经有值了
// 可以打开,开发者工具
mainWindow.webContents.openDevTools();
// 关键:修改 CSP 允许连接 six.sixgou.com
mainWindow.on("ready-to-show", () => {
mainWindow?.show();
});
// mainWindow.webContents
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url).then();
return { action: "deny" };
});
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local HTML file for production.
if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"]).then();
} else {
mainWindow.loadFile(join(__dirname, "../renderer/index.html")).then(() => {
console.log("加 载成功了");
}).catch((reason) => {
console.log("打包后的状态错误回调", reason);
});
}
}
// 当电子完成后,该方法将被调用
// 初始化,准备创建浏览器窗口。
// 有些 API 只能在该事件发生后使用。
// 初始化完成后的生命周期
app.whenReady().then(() => {
// Set app user model id for windows
electronApp.setAppUserModelId("com.electron");
const primaryDisplay = screen.getPrimaryDisplay();
const { width: workAreaWidth, height: workAreaHeight } = primaryDisplay.workAreaSize;
// 配置窗口大小
const windowWidth = Math.floor(workAreaWidth * 0.7);
const windowHeight = Math.floor(workAreaHeight * 0.9);
// 开发中的 F12 默认打开或关闭 DevTools
// 并且在生产环境中忽略 CommandOrControl + R。
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
app.on("browser-window-created", (_, window) => {
optimizer.watchWindowShortcuts(window);
});
// IPC test
ipcMain.on("ping", (_, args: string) => {
console.log("单向的通信成交了啦", args);
});
// ✅ 监听关闭事件
ipcMain.on("app-close", () => {
app.quit(); // ✅ Mac / Win 都适用
});
//✅ 双向通过的示例
ipcMain.handle("read_file", () => {
console.log("双向通信打印了啦");
return "双向通信打印了啦";
});
createWindow(windowWidth, windowHeight);
app.on("activate", function() {
// On macOS, it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow(windowWidth, windowHeight);
});
// ✅ 注册 myapp 协议
});
//当所有窗口关闭时退出,macOS除外。在那里,这很常见
// 应用程序及其菜单栏保持活跃,直到用户退出
//明确表示 Cmd + Q。
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
// app.quit(); // ✅ macOS / Windows 都退出
});
//在这个文件里,你可以包含你应用的具体主要流程的其他部分
// code. You can also put them in separate files and require them here.
// app.on("browser-window-blur", (event) => {
// console.log(event, "失去光标会触发的");
// });
// app.on("browser-window-focus", () => {
// console.log("获取光标时得到光标");
// });
// app.on("did-resign-active", () => {
// console.log("切换时的触发 实测,切换了别的app的会触发的事件");
// });
+38
View File
@@ -0,0 +1,38 @@
import { GlobalKeyboardListener, IGlobalKeyDownMap, IGlobalKeyEvent } from "node-global-key-listener";
import { BrowserWindow } from "electron";
export function initGlobalScanner(mainWindow: BrowserWindow): void {
const listener = new GlobalKeyboardListener();
let buffer = "";
let lastTime = Date.now();
listener
.addListener((e: IGlobalKeyEvent, _down: IGlobalKeyDownMap) => {
if (e.state === "DOWN") {
const now = Date.now();
if (now - lastTime > 50) {
buffer = "";
}
lastTime = now;
if (e.rawKey?.name === "Return") {
if (
buffer.length > 8 && mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send("扫码", buffer);
}
buffer = "";
} else if (e.rawKey?.name?.length === 1) {
buffer += e.name;
}
}
})
.then((data) => {
console.log("成功后的回调", data);
})
.catch((err: Error) => {
console.error("全局键盘监听失败:", err);
});
}
/**
* 微信用户信息及授权Token接口
*/
+8
View File
@@ -0,0 +1,8 @@
import { ElectronAPI } from '@electron-toolkit/preload'
declare global {
interface Window {
electron: ElectronAPI
api: unknown
}
}
+25
View File
@@ -0,0 +1,25 @@
import { contextBridge } from "electron";
import { electronAPI } from "@electron-toolkit/preload";
// ✅ 正确(用 alias)
// Custom APIs for renderer
const api = {};
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld("electron", electronAPI);
contextBridge.exposeInMainWorld("api", api);
} catch (error) {
console.error(error);
}
} else {
// @ts-ignore (define in dts)
window.electron = electronAPI;
// @ts-ignore (define in dts)
window.api = api;
}
console.log("预加载脚本", process.version);
+10
View File
@@ -0,0 +1,10 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const ElMessage: typeof import('element-plus/es').ElMessage
}
+23
View File
@@ -0,0 +1,23 @@
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
ElButton: typeof import('element-plus/es')['ElButton']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElResult: typeof import('element-plus/es')['ElResult']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Versions: typeof import('./src/components/Versions.vue')['default']
}
}
+29
View File
@@ -0,0 +1,29 @@
<!doctype html>
<html class="dark">
<head>
<meta charset="UTF-8" />
<title>Limited Company-管理端</title>
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP-->
<!-- 重点: 这里配置了请求头,可以解决csp,也能解决跨域的问题 -->
<meta
http-equiv="Content-Security-Policy"
content="
default-src 'self';
script-src 'self' https://at.alicdn.com https://res.wx.qq.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: *;
font-src *;
frame-src 'self' https://open.weixin.qq.com https://*.open.weixin.qq.com;
connect-src 'self' https://six.sixgou.com http://localhost:5173 https://res.wx.qq.com https://at.alicdn.com https://open.weixin.qq.com https://datav.aliyun.com ws://127.0.0.1:8000;
"
/>
<link href="https://hudie.xiao.band/static/wx-login.css">
<script src="https://at.alicdn.com/t/c/font_5198345_yvza0orv9pd.js"></script>
<script src="https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
+95
View File
@@ -0,0 +1,95 @@
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import { useMainXiaoStore } from "@renderer/stores";
import { instance } from "@renderer/utils/request";
let xiaoMain = useMainXiaoStore();
let loading = ref(false);
const router_path = useRouter();
// router_path.push({ name: "图表页" });
onMounted(() => {
// router_path.push({ name: "登陆页" });
instance.post<string>("https://six.sixgou.com/demo/xiaozizi/", {},
{ headers: { "Authorization": xiaoMain.weChatUserInfo?.token } }
).then((data) => {
console.log(data);
router_path.replace({ name: "学习页面" });
}).catch((error) => {
console.log("错误被捕获了", error);
router_path.replace({ name: "登陆页" });
});
});
</script>
<template>
<div v-if="loading" class="fullscreen-loading">
<div class="loading-content">
<div class="spinner"></div>
<p class="loading-text">加载中,请稍候...</p>
</div>
</div>
<router-view></router-view>
</template>
<style lang="scss">
#app {
height: 100vh;
border: #a855f7 1px dashed;
.传送门 {
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
display: flex;
align-items: center;
justify-content: center;
}
}
.fullscreen-loading {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: #f0f2f5;
z-index: 9999;
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
}
.spinner {
width: 48px;
height: 48px;
border: 4px solid #e0e0e0;
border-top-color: #409eff;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
.loading-text {
font-size: 16px;
color: #666;
margin: 0;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
}
</style>
+71
View File
@@ -0,0 +1,71 @@
:root {
--ev-c-white: #ffffff;
--ev-c-white-soft: #f8f8f8;
--ev-c-white-mute: #f2f2f2;
--ev-c-black: #1b1b1f;
--ev-c-black-soft: #222222;
--ev-c-black-mute: #282828;
--ev-c-gray-1: #515c67;
--ev-c-gray-2: #414853;
--ev-c-gray-3: #32363f;
--ev-c-text-1: rgba(255, 255, 245, 0.86);
--ev-c-text-2: rgba(235, 235, 245, 0.6);
--ev-c-text-3: rgba(235, 235, 245, 0.38);
--ev-button-alt-border: transparent;
--ev-button-alt-text: var(--ev-c-text-1);
--ev-button-alt-bg: var(--ev-c-gray-3);
--ev-button-alt-hover-border: transparent;
--ev-button-alt-hover-text: var(--ev-c-text-1);
--ev-button-alt-hover-bg: var(--ev-c-gray-2);
--中文-测试: red
}
:root {
--color-background: var(--ev-c-black);
--color-background-soft: var(--ev-c-black-soft);
--color-background-mute: var(--ev-c-black-mute);
--color-text: var(--ev-c-text-1);
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
ul {
list-style: none;
}
body {
min-height: 100vh;
/*color: var(--color-text);*/
/*background: var(--color-background);*/
line-height: 1.6;
font-family: Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
html.dark {
/* 自定义深色背景颜色 */
--el-bg-color: #626aef;
}
+4
View File
@@ -0,0 +1,4 @@
$中国红: red;
@mixin 边框 {
border: #a855f7 1px dashed;
}
+10
View File
@@ -0,0 +1,10 @@
<svg viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="64" cy="64" r="64" fill="#2F3242"/>
<ellipse cx="63.9835" cy="23.2036" rx="4.48794" ry="4.495" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
<path d="M51.3954 39.5028C52.3733 39.6812 53.3108 39.033 53.4892 38.055C53.6676 37.0771 53.0194 36.1396 52.0414 35.9612L51.3954 39.5028ZM28.6153 43.5751L30.1748 44.4741L30.1748 44.4741L28.6153 43.5751ZM28.9393 60.9358C29.4332 61.7985 30.5329 62.0976 31.3957 61.6037C32.2585 61.1098 32.5575 60.0101 32.0636 59.1473L28.9393 60.9358ZM37.6935 66.7457C37.025 66.01 35.8866 65.9554 35.1508 66.6239C34.415 67.2924 34.3605 68.4308 35.029 69.1666L37.6935 66.7457ZM53.7489 81.7014L52.8478 83.2597L53.7489 81.7014ZM96.9206 89.515C97.7416 88.9544 97.9526 87.8344 97.3919 87.0135C96.8313 86.1925 95.7113 85.9815 94.8904 86.5422L96.9206 89.515ZM52.0414 35.9612C46.4712 34.9451 41.2848 34.8966 36.9738 35.9376C32.6548 36.9806 29.0841 39.1576 27.0559 42.6762L30.1748 44.4741C31.5693 42.0549 34.1448 40.3243 37.8188 39.4371C41.5009 38.5479 46.1547 38.5468 51.3954 39.5028L52.0414 35.9612ZM27.0559 42.6762C24.043 47.9029 25.2781 54.5399 28.9393 60.9358L32.0636 59.1473C28.6579 53.1977 28.1088 48.0581 30.1748 44.4741L27.0559 42.6762ZM35.029 69.1666C39.6385 74.24 45.7158 79.1355 52.8478 83.2597L54.6499 80.1432C47.8081 76.1868 42.0298 71.5185 37.6935 66.7457L35.029 69.1666ZM52.8478 83.2597C61.344 88.1726 70.0465 91.2445 77.7351 92.3608C85.359 93.4677 92.2744 92.6881 96.9206 89.515L94.8904 86.5422C91.3255 88.9767 85.4902 89.849 78.2524 88.7982C71.0793 87.7567 62.809 84.8612 54.6499 80.1432L52.8478 83.2597ZM105.359 84.9077C105.359 81.4337 102.546 78.6127 99.071 78.6127V82.2127C100.553 82.2127 101.759 83.4166 101.759 84.9077H105.359ZM99.071 78.6127C95.5956 78.6127 92.7831 81.4337 92.7831 84.9077H96.3831C96.3831 83.4166 97.5892 82.2127 99.071 82.2127V78.6127ZM92.7831 84.9077C92.7831 88.3817 95.5956 91.2027 99.071 91.2027V87.6027C97.5892 87.6027 96.3831 86.3988 96.3831 84.9077H92.7831ZM99.071 91.2027C102.546 91.2027 105.359 88.3817 105.359 84.9077H101.759C101.759 86.3988 100.553 87.6027 99.071 87.6027V91.2027Z" fill="#A2ECFB"/>
<path d="M91.4873 65.382C90.8456 66.1412 90.9409 67.2769 91.7002 67.9186C92.4594 68.5603 93.5951 68.465 94.2368 67.7058L91.4873 65.382ZM99.3169 43.6354L97.7574 44.5344L99.3169 43.6354ZM84.507 35.2412C83.513 35.2282 82.6967 36.0236 82.6838 37.0176C82.6708 38.0116 83.4661 38.8279 84.4602 38.8409L84.507 35.2412ZM74.9407 39.8801C75.9127 39.6716 76.5315 38.7145 76.323 37.7425C76.1144 36.7706 75.1573 36.1517 74.1854 36.3603L74.9407 39.8801ZM53.7836 46.3728L54.6847 47.931L53.7836 46.3728ZM25.5491 80.9047C25.6932 81.8883 26.6074 82.5688 27.5911 82.4247C28.5747 82.2806 29.2552 81.3664 29.1111 80.3828L25.5491 80.9047ZM94.2368 67.7058C97.8838 63.3907 100.505 58.927 101.752 54.678C103.001 50.4213 102.9 46.2472 100.876 42.7365L97.7574 44.5344C99.1494 46.9491 99.3603 50.0419 98.2974 53.6644C97.2323 57.2945 94.9184 61.3223 91.4873 65.382L94.2368 67.7058ZM100.876 42.7365C97.9119 37.5938 91.7082 35.335 84.507 35.2412L84.4602 38.8409C91.1328 38.9278 95.7262 41.0106 97.7574 44.5344L100.876 42.7365ZM74.1854 36.3603C67.4362 37.8086 60.0878 40.648 52.8826 44.8146L54.6847 47.931C61.5972 43.9338 68.5948 41.2419 74.9407 39.8801L74.1854 36.3603ZM52.8826 44.8146C44.1366 49.872 36.9669 56.0954 32.1491 62.3927C27.3774 68.63 24.7148 75.2115 25.5491 80.9047L29.1111 80.3828C28.4839 76.1026 30.4747 70.5062 35.0084 64.5802C39.496 58.7143 46.2839 52.7889 54.6847 47.931L52.8826 44.8146Z" fill="#A2ECFB"/>
<path d="M49.0825 87.2295C48.7478 86.2934 47.7176 85.8059 46.7816 86.1406C45.8455 86.4753 45.358 87.5055 45.6927 88.4416L49.0825 87.2295ZM78.5635 96.4256C79.075 95.5732 78.7988 94.4675 77.9464 93.9559C77.0941 93.4443 75.9884 93.7205 75.4768 94.5729L78.5635 96.4256ZM79.5703 85.1795C79.2738 86.1284 79.8027 87.1379 80.7516 87.4344C81.7004 87.7308 82.71 87.2019 83.0064 86.2531L79.5703 85.1795ZM84.3832 64.0673H82.5832H84.3832ZM69.156 22.5301C68.2477 22.1261 67.1838 22.535 66.7799 23.4433C66.3759 24.3517 66.7848 25.4155 67.6931 25.8194L69.156 22.5301ZM45.6927 88.4416C47.5994 93.7741 50.1496 98.2905 53.2032 101.505C56.2623 104.724 59.9279 106.731 63.9835 106.731V103.131C61.1984 103.131 58.4165 101.765 55.8131 99.0249C53.2042 96.279 50.8768 92.2477 49.0825 87.2295L45.6927 88.4416ZM63.9835 106.731C69.8694 106.731 74.8921 102.542 78.5635 96.4256L75.4768 94.5729C72.0781 100.235 68.0122 103.131 63.9835 103.131V106.731ZM83.0064 86.2531C85.0269 79.7864 86.1832 72.1831 86.1832 64.0673H82.5832C82.5832 71.8536 81.4723 79.0919 79.5703 85.1795L83.0064 86.2531ZM86.1832 64.0673C86.1832 54.1144 84.4439 44.922 81.4961 37.6502C78.5748 30.4436 74.3436 24.8371 69.156 22.5301L67.6931 25.8194C71.6364 27.5731 75.3846 32.1564 78.1598 39.0026C80.9086 45.7836 82.5832 54.507 82.5832 64.0673H86.1832Z" fill="#A2ECFB"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M103.559 84.9077C103.559 82.4252 101.55 80.4127 99.071 80.4127C96.5924 80.4127 94.5831 82.4252 94.5831 84.9077C94.5831 87.3902 96.5924 89.4027 99.071 89.4027C101.55 89.4027 103.559 87.3902 103.559 84.9077V84.9077Z" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.8143 89.4027C31.2929 89.4027 33.3023 87.3902 33.3023 84.9077C33.3023 82.4252 31.2929 80.4127 28.8143 80.4127C26.3357 80.4127 24.3264 82.4252 24.3264 84.9077C24.3264 87.3902 26.3357 89.4027 28.8143 89.4027V89.4027V89.4027Z" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M64.8501 68.0857C62.6341 68.5652 60.451 67.1547 59.9713 64.9353C59.4934 62.7159 60.9007 60.5293 63.1167 60.0489C65.3326 59.5693 67.5157 60.9798 67.9954 63.1992C68.4742 65.4186 67.066 67.6052 64.8501 68.0857Z" fill="#A2ECFB"/>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 KiB

+42
View File
@@ -0,0 +1,42 @@
@import './base.css';
body {
/*这里是body的样式设置,基本是主页面的*/
box-sizing: border-box;
}
#app {
height: 100vh;
/*border: #a855f7 1px dashed;*/
box-sizing: border-box;
}
@font-face {
font-family: "xiaozizi"; /* Project id 5095277 */
/* Color fonts */
src: url('https://at.alicdn.com/t/c/font_5095277_gej2ey1r899.woff2?t=1781968322166') format('woff2'),
url('https://at.alicdn.com/t/c/font_5095277_gej2ey1r899.woff?t=1781968322166') format('woff'),
url('https://at.alicdn.com/t/c/font_5095277_gej2ey1r899.ttf?t=1781968322166') format('truetype');
}
.xiaozizi {
font-family: "xiaozizi", sans-serif !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.xiaozizi-a-027_jisuanqi-27:before {
content: "\e687";
}
.xiaozizi-xinli:before {
content: "\e6c8";
}
.xiaozizi-a-025_queren:before {
content: "\e682";
}
+25
View File
@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1422 800" opacity="0.3">
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="oooscillate-grad">
<stop stop-color="hsl(206, 75%, 49%)" stop-opacity="1" offset="0%"></stop>
<stop stop-color="hsl(331, 90%, 56%)" stop-opacity="1" offset="100%"></stop>
</linearGradient>
</defs>
<g stroke-width="1" stroke="url(#oooscillate-grad)" fill="none" stroke-linecap="round">
<path d="M 0 448 Q 355.5 -100 711 400 Q 1066.5 900 1422 448" opacity="0.05"></path>
<path d="M 0 420 Q 355.5 -100 711 400 Q 1066.5 900 1422 420" opacity="0.11"></path>
<path d="M 0 392 Q 355.5 -100 711 400 Q 1066.5 900 1422 392" opacity="0.18"></path>
<path d="M 0 364 Q 355.5 -100 711 400 Q 1066.5 900 1422 364" opacity="0.24"></path>
<path d="M 0 336 Q 355.5 -100 711 400 Q 1066.5 900 1422 336" opacity="0.30"></path>
<path d="M 0 308 Q 355.5 -100 711 400 Q 1066.5 900 1422 308" opacity="0.37"></path>
<path d="M 0 280 Q 355.5 -100 711 400 Q 1066.5 900 1422 280" opacity="0.43"></path>
<path d="M 0 252 Q 355.5 -100 711 400 Q 1066.5 900 1422 252" opacity="0.49"></path>
<path d="M 0 224 Q 355.5 -100 711 400 Q 1066.5 900 1422 224" opacity="0.56"></path>
<path d="M 0 196 Q 355.5 -100 711 400 Q 1066.5 900 1422 196" opacity="0.62"></path>
<path d="M 0 168 Q 355.5 -100 711 400 Q 1066.5 900 1422 168" opacity="0.68"></path>
<path d="M 0 140 Q 355.5 -100 711 400 Q 1066.5 900 1422 140" opacity="0.75"></path>
<path d="M 0 112 Q 355.5 -100 711 400 Q 1066.5 900 1422 112" opacity="0.81"></path>
<path d="M 0 84 Q 355.5 -100 711 400 Q 1066.5 900 1422 84" opacity="0.87"></path>
<path d="M 0 56 Q 355.5 -100 711 400 Q 1066.5 900 1422 56" opacity="0.94"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
/* 缩小二维码 */
.web_qrcode_panel_normal_login .qrcode {
width: 180px !important;
height: 180px !important;
}
/* 隐藏“请使用微信扫码”标题 */
.impowerBox .title {
display: none !important;
}
/* 限制 info 宽度,居中 */
.impowerBox .info {
width: 200px !important;
margin: 0 auto;
}
/* 隐藏状态图标 */
.status_icon {
display: none !important;
}
/* 扫码状态文字居中 */
.impowerBox .status {
text-align: center;
}
+13
View File
@@ -0,0 +1,13 @@
<script setup lang="ts">
import { reactive } from "vue";
const versions = reactive({ ...window.electron.process.versions });
</script>
<template>
<ul class="versions">
<li class="electron-version">Electron v{{ versions.electron }}</li>
<li class="chrome-version">Chromium v{{ versions.chrome }}</li>
<li class="node-version">Node v{{ versions.node }}</li>
</ul>
</template>
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+22
View File
@@ -0,0 +1,22 @@
import "./assets/main.css";
import "element-plus/theme-chalk/dark/css-vars.css";
// import "./styles/dark/css-vars.css";
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
import { router_list } from "@renderer/router";
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
// main.ts / main.js
import "element-plus/dist/index.css";
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
const app = createApp(App);
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
app.use(pinia);
app.use(router_list);
app.mount("#app");
+57
View File
@@ -0,0 +1,57 @@
// 创建一个路由器
import { createRouter, createWebHashHistory } from "vue-router";
export const router_list = createRouter({
history: createWebHashHistory(),
sensitive: undefined,
strict: false,
routes: [
{
path: "/",
name: "登陆页",
component: () => import("@renderer/页面目录/登陆页面/登陆页.vue")
},
{
path: "/xuxie",
name: "学习页面",
component: () => import("@renderer/页面目录/学习页面/学习页.vue")
},
{
path: "/home",
name: "home",
component: () => import("@renderer/页面目录/主页面/Home.vue"),
children: [
{
path: "goods",
name: "商品统计页面",
component: () => import("@renderer/导航栏页面/商品统计/商品统计页面.vue")
},
{
path: "userManagement",
name: "用户管理页面",
component: () => import("@renderer/导航栏页面/用户管理/用户管理页面.vue")
},
{
path: "systemSettings",
name: "系统设置页面",
component: () => import("@renderer/导航栏页面/系统设置/系统设置页面.vue")
},
{
path: "statistics",
name: "数据统计页面",
component: () => import("@renderer/导航栏页面/数据统计/数据统计页面.vue")
}
]
},
{
path: "/auth",
name: "授权页面",
component: () => import("@renderer/页面目录/授权页面/授权页.vue")
},
{
path: "/echars",
name: "图表页",
component: () => import("@renderer/页面目录/图表页面/图表页.vue")
}
]
});
+21
View File
@@ -0,0 +1,21 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { 企微用户分页类型 } from "@renderer/导航栏页面/用户管理/用户管理类型";
import { WeChatUserInfoType } from "@renderer/页面目录/登陆页面/登陆页面用户类型";
export const useMainXiaoStore = defineStore(
"xiaozizi",
() => {
let xiao = ref("这是一个示例的工具");
const token = ref<string>();
const wecom用户列表 = ref<企微用户分页类型>();
let weChatUserInfo = ref<WeChatUserInfoType>();
return { xiao, token, wecom用户列表, weChatUserInfo };
},
{
persist: {
storage: localStorage,
pick: ["xiao", "token", "wecom用户列表", "weChatUserInfo"]
}
}
);
+9
View File
@@ -0,0 +1,9 @@
import axios from "axios";
// 判断是否为 Electron 生产环境
console.log(import.meta.env.MODE);
export const instance = axios.create({
// 开发环境用相对路径(走 Vite 代理)
// 生产环境用完整 URL import.meta.env.PROD 这里的值,是生产环境,判断用的
baseURL: import.meta.env.PROD ? "https://six.sixgou.com" : "/api"
});
@@ -0,0 +1,12 @@
<style lang="scss" scoped>
div.商品统计页面根元素 {
background-color: #5a67d8;
}
</style>
<template>
<div class="商品统计页面根元素">goods_page</div>
</template>
<script setup lang="ts">
defineOptions({ name: "goods_page" });
</script>
@@ -0,0 +1,11 @@
<style lang="scss" scoped>
div.数据统计页面根元素 {
}
</style>
<template>
<div class="数据统计页面根元素">数据统计页面根元素</div>
</template>
<script setup lang="ts">
defineOptions({ name: "PageDataStatistics" });
</script>
@@ -0,0 +1,48 @@
export interface WecomUser {
/** 创建时间 */
dateAndTime: string;
/** 主键ID */
idKeyWecom企微用户: string;
/** 更新时间 */
updateTime: string;
/** 企微ID */
wecom企微ID: string;
/** 企微好友添加方式 */
wecom企微好友添加方式: string;
/** 企微对好友添加的手机号码 */
wecom企微对好友添加的手机号码: string[];
/** 企微状态 */
wecom企微状态State: null | string;
/** 微信 UNION ID */
wecom好友UNIONID: string;
/** 好友关系状态 */
wecom好友关系状态: null | string;
/** 好友头像 URL */
wecom好友头像: string;
/** 好友性别(1=男,2=女) */
wecom好友性别: string;
/** 好友昵称 */
wecom好友昵称: string;
/** 好友的唯一ID */
wecom好友的ID: string;
/** 对好友打的标签 ID 列表 */
wecom对好友打的标签: string[];
/** 对好友的备注信息 */
wecom对好友的备注信息: string;
/** 对好友的备注名称 */
wecom对好友的备注名称: string;
/** 添加好友的时间戳(秒) */
wecom对好友的添加时间: number;
}
// @renderer/types/pagination.ts
/**
* DRF PageNumberPagination 返回结构
*/
export interface 企微用户分页类型 {
count: number;
next: string | null;
previous: string | null;
results: Array<WecomUser>;
}
@@ -0,0 +1,188 @@
<style lang="scss" scoped>
.用户管理 {
padding: 32px;
//height: calc(100vh - 120px);
height: 100%;
//display: flex;
//flex-direction: column;
color: #e2e8f0;
.用户管理__标题 {
font-size: 22px;
font-weight: 700;
margin-bottom: 24px;
background: linear-gradient(90deg, #818cf8, #60a5fa);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.用户管理__表格容器 {
height: 650px;
display: flex;
border: #a855f7 1px dashed;
flex-direction: column;
background: linear-gradient(180deg, #1e293b, #0f172a);
border-radius: 12px;
padding: 20px;
overflow: hidden;
.用户管理__表格容器__表头,
.用户管理__表格容器__行 {
display: grid;
grid-template-columns: 60px 1fr 1fr 1fr 100px;
align-items: center;
padding: 12px 14px;
.用户管理__表格容器__列_昵称列 {
min-width: 0; /* ✅ 关键 */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.用户管理__表格容器__表头 {
font-size: 13px;
color: #94a3b8;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.用户管理__表格容器__行 {
font-size: 14px;
transition: background 0.2s;
cursor: pointer;
&:hover {
background: rgba(255, 255, 255, 0.04);
}
}
.用户管理__表格容器__头像 {
width: 30px;
height: 30px;
border-radius: 50%;
object-fit: cover;
}
}
.用户管理__分页 {
margin-top: auto;
padding-top: 16px;
display: flex;
justify-content: flex-end;
}
}
</style>
<template>
<div class="用户管理">
<h2 class="用户管理__标题">用户管理</h2>
<div class="用户管理__表格容器">
<!-- 表头 -->
<div class="用户管理__表格容器__表头">
<span>序号</span>
<span>用户</span>
<span>企微ID</span>
<span>备注名称</span>
<span>状态</span>
</div>
<!-- 数据行 -->
<div v-for="(用户, index) in xiaoMain.wecom用户列表?.results" :key="用户.wecom企微ID"
class="用户管理__表格容器__行"
@click="点击行(用户)">
<!-- 序号 -->
<span>
{{ (当前页 - 1) * 每页条数 + index + 1 }}
</span>
<!-- 用户 -->
<span style="display:flex;align-items:center;gap:10px;" class="用户管理__表格容器__列_昵称列">
<img :src="用户.wecom好友头像" class="用户管理__表格容器__头像" alt="" />
{{ 用户.wecom好友昵称 }}
</span>
<!-- 企微ID -->
<span>{{ 用户.wecom企微ID }}</span>
<!-- 备注 -->
<span>{{ 用户.wecom对好友的备注名称 }}</span>
<!-- 状态 -->
<span>
正常
</span>
</div>
<!-- 分页 -->
</div>
<div class="用户管理__分页">
<el-pagination
v-model:current-page="当前页"
v-model:page-size="每页条数"
:total="xiaoMain.wecom用户列表?.count"
layout="prev,pager,next,jumper"
:background="true" @currentChange="点击事件"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { instance } from "@renderer/utils/request";
import { WecomUser, 企微用户分页类型 } from "@renderer/导航栏页面/用户管理/用户管理类型";
import { useMainXiaoStore } from "@renderer/stores";
import { onBeforeRouteLeave, onBeforeRouteUpdate } from "vue-router";
import { tryOnUnmounted } from "@vueuse/core";
let xiaoMain = useMainXiaoStore();
defineOptions({ name: "PageUserManagement" });
onMounted(async () => {
try {
let data = await instance.post<企微用户分页类型>("/web/企微用户/查询添加的企微用户/");
console.log(data.data);
// 企微用户列表.value = data.data;
xiaoMain.wecom用户列表 = data.data;
} catch (error) {
console.error("请求失败:", error);
}
});
// ✅ 翻页状态
let 当前页 = ref(1);
let 每页条数 = ref(10);
tryOnUnmounted(() => {
console.log("啥时候消失的呢");
});
onBeforeRouteUpdate((to, from) => {
console.log("路由更新", from.path, "→", to.path);
});
// ✅ 点击行(可选)
function 点击行(row: WecomUser) {
console.log("点击了用户:", row.wecom好友昵称);
}
// 离开当前路由
onBeforeRouteLeave((to, from) => {
console.log("要离开当前页面了", to, from);
});
async function 点击事件(current_page_number: number): Promise<void> {
console.log("0000000000------------===========");
try {
let data = await instance.post<企微用户分页类型>("/web/企微用户/查询添加的企微用户/",
{}, { params: { page: current_page_number } }
);
console.log(data.data);
// 企微用户列表.value = data.data;
xiaoMain.wecom用户列表 = data.data;
当前页.value = current_page_number;
} catch (error) {
console.error("请求失败:", error);
}
}
</script>
@@ -0,0 +1,178 @@
<template>
<div class="系统设置">
<h2 class="系统设置__标题">系统设置</h2>
<div class="系统设置__表单">
<!-- 主题选择 -->
<div class="表单项">
<label class="表单项__标签">主题配色</label>
<div class="主题选择">
<span
class="主题选择__项"
:class="{ 'is-active': 表单.主题 === 'dark' }"
@click="表单.主题 = 'dark'"
>
暗黑
</span>
<span
class="主题选择__项"
:class="{ 'is-active': 表单.主题 === 'light' }"
@click="表单.主题 = 'light'"
>
明亮
</span>
</div>
</div>
<!-- 账号名 -->
<div class="表单项">
<label class="表单项__标签">账号名称</label>
<input
v-model="表单.账号名"
class="表单项__输入框"
placeholder="请输入账号名称"
/>
</div>
<!-- 原密码 -->
<div class="表单项">
<label class="表单项__标签">原密码</label>
<input
v-model="表单.原密码"
type="password"
class="表单项__输入框"
placeholder="请输入原密码"
/>
</div>
<!-- 新密码 -->
<div class="表单项">
<label class="表单项__标签">新密码</label>
<input
v-model="表单.新密码"
type="password"
class="表单项__输入框"
placeholder="请输入新密码"
/>
</div>
<!-- 保存按钮 -->
<div class="表单项">
<button class="保存按钮" @click="保存设置">
保存设置
</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive } from "vue";
const 表单 = reactive({
主题: "dark",
账号名: "admin",
原密码: "",
新密码: ""
});
const 保存设置 = () => {
alert("保存成功 ✅");
console.log("当前设置:", 表单);
};
</script>
<style lang="scss" scoped>
.系统设置 {
padding: 32px;
box-sizing: border-box;
width: 100%;
color: #e2e8f0;
height: 100%;
//max-width: 680px;
&__标题 {
font-size: 22px;
font-weight: 700;
margin-bottom: 24px;
background: linear-gradient(90deg, #818cf8, #60a5fa);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
&__表单 {
background: linear-gradient(180deg, #1e293b, #0f172a);
padding: 24px;
border-radius: 12px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
display: flex;
flex-direction: column;
gap: 20px;
}
}
.表单项 {
display: flex;
flex-direction: column;
gap: 8px;
&__标签 {
font-size: 14px;
color: #94a3b8;
font-weight: 500;
}
&__输入框 {
padding: 10px 14px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.05);
color: #e2e8f0;
font-size: 14px;
outline: none;
transition: all 0.3s;
&:focus {
border-color: #818cf8;
box-shadow: 0 0 10px rgba(99, 102, 241, 0.4);
}
}
}
.主题选择 {
display: flex;
gap: 12px;
&__项 {
padding: 6px 16px;
border-radius: 6px;
cursor: pointer;
background: rgba(255, 255, 255, 0.05);
font-size: 13px;
transition: all 0.3s;
&.is-active {
background: linear-gradient(90deg, #6366f1, #818cf8);
color: #fff;
box-shadow: 0 0 10px rgba(99, 102, 241, 0.5);
}
}
}
.保存按钮 {
padding: 10px 24px;
border-radius: 8px;
border: none;
background: linear-gradient(90deg, #6366f1, #818cf8);
color: #fff;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(99, 102, 241, 0.5);
}
}
</style>
@@ -0,0 +1,36 @@
<style>
.theme-toggle {
display: inline-block;
position: fixed;
right: 0;
}
</style>
<template>
<div class="theme-toggle">
<el-button class="theme-toggle__btn" @click="切换主题按钮">
{{ isDark ? "☀️ 亮色" : "🌙️ 暗黑" }}
</el-button>
</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
defineOptions({ name: "theme-toggle" });
let isDark = ref(false);
async function 切换主题按钮(): Promise<void> {
const html = document.querySelector("html");
console.log(html?.className);
if (html) {
// 如果存在就把类名去掉
if (html.className === "dark") {
console.log("判断生效了吗?");
html.classList.remove("dark");
isDark.value = !isDark.value;
} else {
html.classList.add("dark");
isDark.value = !isDark.value;
}
}
}
</script>
@@ -0,0 +1,41 @@
<style lang="scss" scoped>
.root-home-demo {
//border: #a855f7 1px dashed;
}
</style>
<template>
<div class="root-home-demo">
<div @click="修改xiaozizi的值">修改xiaozizi的值</div>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: "test" });
// 1. 定义 props 类型
interface definePropsType {
// modelValue?: { xiao: string };// 对象类型的 prop
// xiaozizi: string;// 字符串类型的 prop
// count?: number;// 数字类型
// items?: string[];// 数组类型
// deme?: () => void;// 函数类型
}
// 2. 使用 withDefaults 定义默认值
const props = withDefaults(defineProps<definePropsType>(), {});
const emit = defineEmits<{
/*update:modelValue 用法:
* 在父组件中绑定 v-model=父组件变量
* 2:在子组件中直接调用 emit("update:modelValue",值)
* 3:重点,此时传递给父组件的值,会自动更新 v-model绑定的父组件的 变量的值;
* 示例: <Child v-model:zizi="变量" />
* 4:update:zizi 是如何实现的 在父组件中传递 v-model:zizi = xxx;*/
(eventName: "update:modelValue", value: string): void
(eventName: "update:zizi", value: string): void
(eventName: "update:xiaozizi", value: string): void
}>();
async function 修改xiaozizi的值(): Promise<void> {
emit("update:xiaozizi", "修改后的值");
}
</script>
@@ -0,0 +1,81 @@
<style lang="scss" scoped>
.路由样式 {
position: fixed;
right: 8px;
top: 8px;
//transform: translateY(-50%);
z-index: 1000;
.flex容器 {
display: flex;
gap: 10px;
padding: 10px;
background: rgba(255, 255, 255, 0.05);
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: inset 0 0 20px rgba(255, 255, 255, 0.02), 0 4px 20px rgba(0, 0, 0, 0.3);
}
button {
padding: 10px 18px;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
background: linear-gradient(145deg, rgba(255, 255, 255, 0.1), rgba(0, 0, 0, 0.1));
color: #e0e0e0;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
backdrop-filter: blur(5px);
&:hover {
background: rgba(255, 255, 255, 0.2);
color: #fff;
border-color: rgba(255, 255, 255, 0.4);
transform: scale(1.05);
}
&:active {
transform: scale(0.95);
}
}
}
</style>
<template>
<div class="路由样式">
<div class="flex容器">
<div>
<button @click="路由后退">后退</button>
</div>
<div>
<button @click="路由前进">前进</button>
</div>
<div>
<button @click="刷新">刷新</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useRoute, useRouter } from "vue-router";
defineOptions({ name: "AdvanceAndRetreat" });
let router = useRouter();
const route = useRoute();
async function 路由后退(): Promise<void> {
router.back();
}
async function 路由前进(): Promise<void> {
router.forward();
}
async function 刷新(): Promise<void> {
console.log(route.fullPath, "这里是个啥啊");
window.location.href = route.fullPath;
await router.replace({
path: route.fullPath
});
}
</script>
@@ -0,0 +1,75 @@
<style lang="scss" scoped>
.主页面根元素 {
box-sizing: border-box;
height: 100%; // 这里的高度是继承了#app的高度
button.毛玻璃按钮 {
padding: 10px 18px;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
background: linear-gradient(145deg, rgba(255, 255, 255, 0.1), rgba(0, 0, 0, 0.1));
color: #e0e0e0;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
backdrop-filter: blur(5px);
&:hover {
background: rgba(255, 255, 255, 0.2);
color: #fff;
border-color: rgba(255, 255, 255, 0.4);
transform: scale(1.05);
}
&:active {
transform: scale(0.95);
}
}
display: flex;
.左侧导航栏 {
}
.右侧视图区域 {
flex: 1;
box-sizing: border-box;
}
}
</style>
<template>
<div class="主页面根元素">
<AdvanceAndRetreat></AdvanceAndRetreat>
<div class="左侧导航栏">
<Sidebar></Sidebar>
</div>
<div class="右侧视图区域">
<router-view></router-view>
</div>
</div>
</template>
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from "vue";
import AdvanceAndRetreat from "@renderer/通用组件/浮动路由组件.vue";
import Sidebar from "@renderer/页面目录/主页面/主页面组件/左侧导航菜单.vue";
import { useRouter } from "vue-router";
let router = useRouter();
onMounted(() => {
router.push({ name: "商品统计页面" });
});
// 组件销毁时清理监听器,防止内存泄漏
onBeforeUnmount(() => {
window.electron.ipcRenderer.removeAllListeners("扫码");
});
let goods_card_list = ref<{ codeid: string, goods_number: number }[]>([]);
</script>
@@ -0,0 +1,151 @@
<template>
<aside class="sidebar">
<!-- Logo -->
<div class="sidebar__logo">
<span class="sidebar__logo-text">Admin</span>
</div>
<!-- 菜单 -->
<nav class="sidebar__menu">
<div
v-for="item in menu"
:key="item.path"
class="menu-item"
:class="{ 'is-active': activePath === item.path }"
@click="选项导航栏类目(item)"
>
<span class="menu-item__icon">
{{ item.icon }}
</span>
<span class="menu-item__title">
{{ item.title }}
</span>
</div>
</nav>
</aside>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
let router = useRouter();
defineOptions({ name: "Sidebar" });
type MenuItem = {
title: string
path: string
icon: string
}
const activePath = ref("商品统计页面");
async function 选项导航栏类目(item: MenuItem): Promise<void> {
console.log("===========");
activePath.value = item.path;
await router.push({ name: item.path });
}
onMounted(() => {
router.push({ name: "商品统计页面" }).then(() => {
console.log("默认路由进入页面了");
});
});
const menu: MenuItem[] = [
{ title: "商品统计", path: "商品统计页面", icon: "📊" },
{ title: "用户管理", path: "用户管理页面", icon: "👤" },
{ title: "系统设置", path: "系统设置页面", icon: "⚙️" },
{ title: "数据统计", path: "数据统计页面", icon: "📈" }
];
</script>
<style lang="scss" scoped>
.sidebar {
width: 220px;
height: 100%;
//border: #a855f7 1px dashed;
box-sizing: border-box;
background: linear-gradient(180deg, #0f172a, #1e293b);
color: #cbd5e1;
display: flex;
flex-direction: column;
box-shadow: 4px 0 20px rgba(0, 0, 0, 0.4);
&__logo {
padding: 24px 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
&__logo-text {
font-size: 20px;
font-weight: 700;
background: linear-gradient(90deg, #818cf8, #60a5fa);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
&__menu {
flex: 1;
padding: 12px;
display: flex;
flex-direction: column;
gap: 6px;
}
}
.menu-item {
display: flex;
align-items: center;
padding: 12px 14px;
border-radius: 8px;
cursor: pointer;
position: relative;
transition: all 0.25s;
&:hover {
background: rgba(255, 255, 255, 0.06);
.menu-item__icon {
color: #60a5fa;
transform: scale(1.15);
}
}
&.is-active {
background: linear-gradient(
90deg,
rgba(99, 102, 241, 0.25),
transparent
);
color: #fff;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 60%;
background: linear-gradient(180deg, #818cf8, #6366f1);
border-radius: 0 4px 4px 0;
box-shadow: 0 0 10px rgba(99, 102, 241, 0.8);
}
.menu-item__icon {
color: #818cf8;
}
}
&__icon {
margin-right: 12px;
font-size: 16px;
transition: all 0.3s;
}
&__title {
font-size: 14px;
font-weight: 500;
}
}
</style>
@@ -0,0 +1,58 @@
<script setup lang="ts">
import type { EChartsOption } from "echarts";
import * as echarts from "echarts";
import { onBeforeUnmount, onMounted, useTemplateRef } from "vue";
import { instance } from "@renderer/utils/request";
const chartRef = useTemplateRef<HTMLDivElement>("chartRef");
let myChart: echarts.ECharts | null = null;
let observer: ResizeObserver | null = null;
/* ✅ 生命周期必须放在最外层 */
onBeforeUnmount(() => {
observer?.disconnect();
myChart?.dispose();
});
onMounted(() => {
// https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json
// https://geo.datav.aliyun.com/areas_v3/bound/100000_full_city.json
instance
.get("/map/areas_v3/bound/100000_full.json")
.then((res) => {
const geoMapData = res.data;
myChart = echarts.init(chartRef.value!, "dark");
echarts.registerMap("chinaMap", geoMapData);
myChart.setOption<EChartsOption>({
geo: {
type: "map", // 类型为 地图
map: "chinaMap", // 对应注册时的名称
roam: "scale", // 允许地图缩放和拖拉
zoom: 10, // 放大地图
center: [116.387803, 39.940361], // 显示地图的中心点
label: {
show: true
}
}
});
observer = new ResizeObserver(() => {
myChart?.resize();
});
observer.observe(chartRef.value!);
});
});
</script>
<template>
<div ref="chartRef" class="echares"></div>
</template>
<style lang="scss" scoped>
.echares {
border: #a855f7 1px dashed;
height: 70%;
width: 100%;
}
</style>
@@ -0,0 +1,52 @@
<script setup lang="ts">
defineOptions({ name: "Zhong" });
let root_url = "https://open.weixin.qq.com/connect/qrconnect?";
let response_type = "response_type=code";
let appid = "appid=wx6c9d4541e0285d02";
let scope = "scope=snsapi_login";
const encoded = encodeURIComponent("https://six.sixgou.com/web/auth/get_code/");
let redirect_uri = "redirect_uri=" + encoded;
let url = `${root_url}&${appid}&${redirect_uri}&${response_type}&${scope}&state=STATE#wechat_redirect`;
async function 测试跨域(): Promise<void> {
// let data = await instance.post("https://six.sixgou.com/demo/xiaozizi/", {});
console.log(url);
// let data = await instance.get(url, {});
window.location.href = url;
}
</script>
<template>
<div class="子组件">
<div>
<button class="毛玻璃按钮" @click="测试跨域">测试跨域</button>
</div>
</div>
</template>
<style lang="scss" scoped>
.子组件 {
.毛玻璃按钮 {
padding: 10px 18px;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
background: linear-gradient(145deg, rgba(255, 255, 255, 0.1), rgba(0, 0, 0, 0.1));
color: #e0e0e0;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
backdrop-filter: blur(5px);
&:hover {
background: rgba(255, 255, 255, 0.2);
color: #fff;
border-color: rgba(255, 255, 255, 0.4);
transform: scale(1.05);
}
&:active {
transform: scale(0.95);
}
}
}
</style>
@@ -0,0 +1,51 @@
<script setup lang="ts">
import ThemeToggle from "@renderer/通用组件/主题切换组件.vue";
import { onMounted, ref } from "vue";
const code_list = ref<Array<string>>([]);
onMounted(async () => {
window.electron.ipcRenderer.on("扫码", (_, args: string) => {
console.log(args, "扫码结果");
code_list.value.push(args);
});
});
const 向主进程发送ping事件 = async (): Promise<void> => {
window.electron.ipcRenderer.send("ping", "前端发送来的消息哟");
};
</script>
<template>
<div class="学习页面根元素">
<theme-toggle></theme-toggle>
<div>学习页面根元素</div>
<div>
<el-button type="danger" @click="向主进程发送ping事件">向主进程发送ping事件</el-button>
</div>
<div>
<div class="" v-for="(value,index) in code_list" v-bind:key="index">
<div>{{ value }}</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
div.学习页面根元素 {
.icon {
width: 20px;
height: 20px;
}
.表单容器 {
border: #a855f7 1px dashed;
}
.icon {
width: 2em;
height: 2em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
}
</style>
@@ -0,0 +1,44 @@
<style lang="scss" scoped>
div.授权页面根元素 {
}
</style>
<template>
<div class="授权页面根元素">
<div>
<el-input v-model="input" style="width: 240px" placeholder="Please input" />
</div>
<div>
<el-button @click="测试跨域" type="danger">测试跨域</el-button>
</div>
<a href="myapp://open/home?token=123">点我唤起</a>
<div>
<el-result
icon="success"
title="Success Tip"
sub-title="Please follow the instructions"
>
<template #extra>
<el-button type="primary">Back</el-button>
</template>
</el-result>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
const input = ref("");
let router = useRouter();
const 测试跨域 = async (): Promise<void> => {
console.log("========");
};
onMounted(() => {
window.on发送登陆状态事件((res) => {
console.log("===============", res);
router.push({ name: "学习页面" });
});
});
</script>
@@ -0,0 +1,53 @@
<script setup lang="ts">
import { onMounted, useTemplateRef } from "vue";
defineOptions({ name: "WxLoginCode" });
const wxLoginCss = `
.impowerBox .qrcode {width:180px!important;height:180px!important;}
.impowerBox .title {display:none!important;}
.impowerBox .info {width:200px!important;margin:0 auto;}
.status_icon {display:none!important;}
.impowerBox .status {text-align:center;}
`;
let containerRef = useTemplateRef("containerRef");
const bytes = new TextEncoder().encode(wxLoginCss);
const bin = Array.from(bytes, (b) => String.fromCharCode(b)).join("");
const base64 = btoa(bin);
const href = "data:text/css;base64," + base64;
onMounted(() => {
new WxLogin({
// self_redirect: false,
id: "login_container",
appid: "wx6c9d4541e0285d02",
scope: "snsapi_login",
redirect_uri: "https://six.sixgou.com/web/auth/get_code/",
// redirect_uri: "myapp://login/callback",
state: "xiaozizi",
style: "black",
href: href,
onReady: function(isReady) {
console.log(isReady);
let iframeEl = containerRef.value?.querySelector("iframe") as HTMLIFrameElement;
console.log("iframe:", iframeEl);
iframeEl.style.width = "320px";
iframeEl.style.height = "200px";
iframeEl.style.borderRadius = "20px ";
iframeEl.style.background = "black";
iframeEl.style.border = "3px ";
}
});
});
</script>
<template>
<div id="login_container" ref="containerRef"></div>
</template>
<style scoped lang="scss">
#login_container {
//z-index: 1000;
width: 320px;
height: 220px;
box-sizing: border-box;
}
</style>
@@ -0,0 +1,197 @@
<template>
<div class="登录容器">
<div class="登录卡片包裹层">
<!-- 左侧图片区 -->
<div class="左侧图片区域">
<div class="图片内容">
<img src="../../assets/logo.png" alt="登录插图" class="插画" />
<div class="图片文字">
<h2>高效管理</h2>
<p>让工作更简单,让协作更顺畅</p>
</div>
</div>
</div>
<!-- 右侧微信登录 -->
<WeChatLoginComponent v-if="判断是表单组件还是微信组件==='微信'"
v-model:修改为表单="判断是表单组件还是微信组件"></WeChatLoginComponent>
<FormRegistrationComponent v-if="判断是表单组件还是微信组件==='表单'"
v-model:修改为微信="判断是表单组件还是微信组件"></FormRegistrationComponent>
</div>
</div>
</template>
<script setup lang="ts">
import WeChatLoginComponent from "@renderer/页面目录/登陆页面/登陆页面微信登陆注册组件.vue";
import { onMounted, onUnmounted, ref } from "vue";
import FormRegistrationComponent from "@renderer/页面目录/登陆页面/登陆页面表单注册组件.vue";
import { useRouter } from "vue-router";
import { useMainXiaoStore } from "@renderer/stores";
import { WeChatUserInfoType } from "@renderer/页面目录/登陆页面/登陆页面用户类型";
let xiaoMain = useMainXiaoStore();
let 判断是表单组件还是微信组件 = ref<"表单" | "微信">("微信");
let router = useRouter();
onMounted(() => {
window.electron.ipcRenderer.on("wx", (_, args: WeChatUserInfoType) => {
console.log(args);
xiaoMain.weChatUserInfo = args;
router.push({ name: "学习页面" });
});
});
onUnmounted(() => {
window.electron.ipcRenderer.removeAllListeners("wx");
});
</script>
<style scoped lang="scss">
.登录容器 {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
border: red 1px dashed;
box-sizing: border-box;
.登录卡片包裹层 {
display: flex;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 80px rgba(0, 0, 0, 0.15);
animation: 卡片进入 0.6s ease-out;
@keyframes 卡片进入 {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.左侧图片区域 {
width: 380px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 60%);
display: flex;
align-items: center;
justify-content: center;
padding: 40px;
.图片内容 {
text-align: center;
color: #ffffff;
.插画 {
width: 280px;
margin-bottom: 32px;
filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.2));
}
.图片文字 {
h2 {
font-size: 28px;
font-weight: 700;
margin-bottom: 12px;
}
p {
font-size: 15px;
opacity: 0.85;
}
}
}
}
//.登录卡片 {
// width: 440px;
// padding: 44px 42px 50px;
// background: #ffffff;
//
// .登录头部 {
// text-align: center;
// margin-bottom: 34px;
//
// .公司图标 {
// width: 72px;
// height: 72px;
// border-radius: 16px;
// margin-bottom: 16px;
// }
//
// .公司名称 {
// font-size: 26px;
// font-weight: 800;
// color: #1a1a2e;
// margin-bottom: 10px;
// }
//
// .欢迎文字 {
// font-size: 14px;
// color: #8b8fa3;
// }
// }
//
// .微信登录区域 {
// text-align: center;
//
// .二维码外壳 {
// position: relative;
// padding: 24px;
// background: #f7fafc;
// border-radius: 16px;
// border: 1px solid #e2e8f0;
//
// .扫码提示 {
// margin-top: 24px;
// font-size: 13px;
// color: #718096;
// }
// }
//
// .刷新提示 {
// margin-top: 16px;
// font-size: 12px;
// color: #a0aec0;
// display: flex;
// justify-content: center;
// gap: 12px;
// align-items: center;
//
// .刷新按钮 {
// background: none;
// border: none;
// color: #667eea;
// cursor: pointer;
// font-size: 12px;
//
// &:hover {
// text-decoration: underline;
// }
// }
// }
// }
//
// .底部操作区 {
// margin-top: 30px;
// text-align: center;
//
// .备用登录方式 {
// background: none;
// border: none;
// color: #667eea;
// font-size: 14px;
// cursor: pointer;
//
// &:hover {
// text-decoration: underline;
// }
// }
// }
//}
}
}
</style>
@@ -0,0 +1,140 @@
<style lang="scss" scoped>
.登录卡片 {
width: 440px;
padding: 44px 42px 50px;
background: #ffffff;
.登录头部 {
text-align: center;
margin-bottom: 34px;
.公司图标 {
width: 72px;
height: 72px;
border-radius: 16px;
margin-bottom: 16px;
}
.公司名称 {
font-size: 26px;
font-weight: 800;
color: #1a1a2e;
margin-bottom: 10px;
}
.欢迎文字 {
font-size: 14px;
color: #8b8fa3;
}
}
.微信登录区域 {
text-align: center;
.二维码外壳 {
position: relative;
padding: 24px;
background: #f7fafc;
border-radius: 16px;
border: 1px solid #e2e8f0;
.扫码提示 {
margin-top: 24px;
font-size: 13px;
color: #718096;
}
}
.刷新提示 {
margin-top: 16px;
font-size: 12px;
color: #a0aec0;
display: flex;
justify-content: center;
gap: 12px;
align-items: center;
.刷新按钮 {
background: none;
border: none;
color: #667eea;
cursor: pointer;
font-size: 12px;
&:hover {
text-decoration: underline;
}
}
}
}
.底部操作区 {
margin-top: 30px;
text-align: center;
.备用登录方式 {
background: none;
border: none;
color: #667eea;
font-size: 14px;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
}
}
</style>
<template>
<div class="登录卡片">
<div class="登录头部">
<img src="../../assets/logo.png" alt="公司Logo" class="公司图标" />
<h1 class="公司名称">Limited Company</h1>
<p class="欢迎文字">请使用微信扫码登录</p>
</div>
<div class="微信登录区域">
<div class="二维码外壳">
<WxLoginCode></WxLoginCode>
<p class="扫码提示">打开微信扫一扫,安全登录</p>
</div>
<div class="刷新提示">
<span>二维码 · 2分钟内有效</span>
<button class="刷新按钮" @click="刷新按钮">刷新</button>
</div>
</div>
<div class="底部操作区">
<button class="备用登录方式" @click="使用账号密码登录">使用账号密码登录</button>
</div>
</div>
</template>
<script setup lang="ts">
import WxLoginCode from "./登陆组件.vue";
defineOptions({ name: "WeChatLoginComponent" });
const 刷新按钮 = async (): Promise<void> => {
window.location.reload();
};
const 使用账号密码登录 = async (): Promise<void> => {
console.log("=============");
emit("update:修改为表单", "表单");
};
const emit = defineEmits<{
/*update:modelValue 用法:
* 在父组件中绑定 v-model=父组件变量
* 2:在子组件中直接调用 emit("update:modelValue",值)
* 3:重点,此时传递给父组件的值,会自动更新 v-model绑定的父组件的 变量的值;
* 示例: <Child v-model:zizi="变量" />
* 4:update:zizi 是如何实现的 在父组件中传递 v-model:zizi = xxx;*/
(eventName: "update:修改为表单", value: "表单"): void
}>();
</script>
@@ -0,0 +1,25 @@
export interface WeChatUserInfoType {
/** 微信用户的唯一标识符 */
openid: string;
/** 用户的昵称 */
nickname: string;
/** 用户的性别 */
sex: string;
/** 用户所在的省份 */
province: string;
/** 用户所在的城市 */
city: string;
/** 用户所在的国家 */
country: string;
/** 用户头像的URL */
headimgurl: string;
/** 用户特权信息 */
privilege: string;
/** 只有在用户将公众号绑定到微信开放平台帐号时才会出现 */
unionid: string;
/**
* JSON Web Token (JWT) 格式的授权令牌
* 用于后续请求的身份验证和授权
*/
token: string;
}
@@ -0,0 +1,499 @@
<template>
<div class="login-card">
<div class="login-header">
<img src="../../assets/logo.png" alt="六果科技" class="logo" />
<h1 class="company-name">Limited Company</h1>
<p class="welcome-text">欢迎回来,请登录您的账号</p>
</div>
<form class="login-form" @submit.prevent="handleLogin">
<div class="form-group">
<label for="username">用户名</label>
<div class="input-wrapper">
<span class="input-icon">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
<circle cx="12" cy="7" r="4" />
</svg>
</span>
<input
id="username"
v-model="loginForm.username"
type="text"
autocomplete="username"
name="username"
placeholder="请输入用户名或手机号"
:class="{ 'input-error': errors.username }"
@blur="validateField('username')"
/>
</div>
<span v-if="errors.username" class="error-message">{{
errors.username
}}</span>
</div>
<div class="form-group">
<label for="password">密码</label>
<div class="input-wrapper">
<span class="input-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
</svg>
</span>
<input
:id="passwordFieldId"
v-model="loginForm.password"
:type="showPassword ? 'text' : 'password'"
placeholder="请输入密码"
autocomplete="password"
:class="{ 'input-error': errors.password }"
@blur="validateField('password')"
/>
<button class="toggle-password" @click="showPassword = !showPassword">
<svg
v-if="!showPassword"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
<circle cx="12" cy="12" r="3" />
</svg>
<svg
v-else
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path
d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"
/>
<path
d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"
/>
<line x1="1" y1="1" x2="23" y2="23" />
</svg>
</button>
</div>
<span v-if="errors.password" class="error-message">{{
errors.password
}}</span>
</div>
<div class="form-options">
<label class="remember-me">
<input type="checkbox" v-model="rememberMe" />
<span class="checkmark"></span>
记住密码
</label>
<button class="forgot-password" @click="handleForgotPassword">
忘记密码?
</button>
</div>
<button class="login-btn" :disabled="isLoading">
<span v-if="isLoading" class="spinner"></span>
<span>{{ isLoading ? "登录中..." : "登 录" }}</span>
</button>
</form>
<div class="register-link">
还没有账号?
<button class="link-btn" @click="handleRegister">立即注册</button>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, reactive, ref } from "vue";
// import {useMainXiaoStore} from "@renderer/stores";
import { useRouter } from "vue-router";
// import {instance} from "@renderer/utils/request";
defineOptions({ name: "FormRegistrationComponent" });
// let xiaoMain = useMainXiaoStore();
const isLoading = ref(false);
const showPassword = ref(false);
const rememberMe = ref(false);
const passwordFieldId = computed(() => `password-${Math.random().toString(36).slice(2, 8)}`);
let router = useRouter();
const emit = defineEmits<{
(eventName: "update:修改为微信", value: "微信"): void
}>();
interface LoginForm {
username: string;
password: string;
}
interface Errors {
username: string;
password: string;
}
const loginForm = reactive<LoginForm>({
username: "",
password: ""
});
const errors = reactive<Errors>({
username: "",
password: ""
});
const validateField = (field: keyof LoginForm): boolean => {
switch (field) {
case "username":
if (!loginForm.username.trim()) {
errors.username = "请输入用户名";
return false;
}
if (loginForm.username.length < 3 || loginForm.username.length > 20) {
errors.username = "用户名长度在 3 到 20 个字符之间";
return false;
}
errors.username = "";
return true;
case "password":
if (!loginForm.password) {
errors.password = "请输入密码";
return false;
}
if (loginForm.password.length < 6 || loginForm.password.length > 30) {
errors.password = "密码长度在 6 到 30 个字符之间";
return false;
}
errors.password = "";
return true;
default:
return false;
}
};
const validateAll = (): boolean => {
const isUsernameValid = validateField("username");
const isPasswordValid = validateField("password");
return isUsernameValid && isPasswordValid;
};
const handleLogin = async () => {
if (!validateAll()) return;
isLoading.value = true;
// try {
// let data = await instance.post("/web/用户/登陆/", {});
// xiaoMain.token = data.headers.authorization;
// isLoading.value = false;
// await router.replace({name: "学习页面"});
// } catch (e) {
// console.log("捕获错误❌🙅🙅‍❌🙅🙅‍❌🙅🙅‍❌🙅🙅‍❌🙅🙅‍❌🙅🙅‍❌🙅🙅‍♀️", e);
// }
};
const handleForgotPassword = () => {
console.log("跳转到忘记密码页");
};
const handleRegister = async () => {
// console.log("跳转到注册页");
// console.log(xiaoMain.xiao);
// window.electron.ipcRenderer.send("ping", "xiaozizi");
// let result = await window.electron.ipcRenderer.invoke("read_file");
// console.log("双向通信接收到的数据", result);
await router.push("/home");
};
</script>
<style scoped lang="scss">
.login-card {
width: 440px;
padding: 44px 42px 50px;
background: #ffffff;
.login-header {
text-align: center;
margin-bottom: 34px;
.logo {
width: 72px;
height: 72px;
margin-bottom: 16px;
border-radius: 16px;
}
.company-name {
font-size: 26px;
font-weight: 800;
color: #1a1a2e;
margin: 0 0 10px;
letter-spacing: 3px;
}
.welcome-text {
font-size: 14px;
color: #8b8fa3;
margin: 0;
line-height: 1.5;
}
}
.login-form {
.form-group {
margin-bottom: 22px;
label {
display: block;
font-size: 14px;
font-weight: 600;
color: #2d3748;
margin-bottom: 8px;
}
.input-wrapper {
position: relative;
display: flex;
align-items: center;
.input-icon {
position: absolute;
left: 14px;
top: 50%;
transform: translateY(-50%);
width: 18px;
height: 18px;
color: #a0aec0;
pointer-events: none;
svg {
width: 100%;
height: 100%;
}
}
input {
width: 87%;
padding: 13px 14px 13px 46px;
font-size: 15px;
color: #2d3748;
background: #f7fafc;
border: 2px solid #e2e8f0;
border-radius: 12px;
outline: none;
transition: all 0.3s ease;
&::placeholder {
color: #a0aec0;
}
&:focus {
border-color: #667eea;
background: #ffffff;
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
}
&.input-error {
border-color: #fc8181;
background: #fff5f5;
&:focus {
box-shadow: 0 0 0 4px rgba(252, 129, 129, 0.1);
}
}
}
.toggle-password {
position: absolute;
right: 50px;
top: 55%;
transform: translateY(-51%);
background: none;
border: none;
cursor: pointer;
padding: 4px;
color: #a0aec0;
transition: color 0.2s;
&:hover {
color: #667eea;
}
svg {
width: 20px;
height: 25px;
}
}
}
.error-message {
display: block;
margin-top: 6px;
font-size: 12px;
color: #fc8181;
animation: errorSlideIn 0.3s ease;
@keyframes errorSlideIn {
from {
opacity: 0;
transform: translateY(-4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
}
}
.form-options {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 26px;
.remember-me {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #4a5568;
cursor: pointer;
user-select: none;
input[type="checkbox"] {
display: none;
&:checked ~ .checkmark {
background: #667eea;
border-color: #667eea;
&::after {
display: block;
}
}
}
.checkmark {
position: relative;
width: 18px;
height: 18px;
background: #f7fafc;
border: 2px solid #e2e8f0;
border-radius: 4px;
transition: all 0.2s;
&::after {
content: "";
position: absolute;
display: none;
left: 5px;
top: 1px;
width: 5px;
height: 9px;
border: solid #ffffff;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
}
&:hover .checkmark {
border-color: #667eea;
}
}
.forgot-password {
background: none;
border: none;
font-size: 14px;
color: #667eea;
cursor: pointer;
transition: color 0.2s;
&:hover {
color: #5a67d8;
text-decoration: underline;
}
}
}
.login-btn {
width: 104%;
padding: 14px;
font-size: 16px;
font-weight: 700;
color: #ffffff;
background: linear-gradient(135deg, #667eea 0%, #764ba2 107%);
border: none;
border-radius: 56px;
cursor: pointer;
letter-spacing: 5px;
transition: all 0.37s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
&:hover:not(:disabled) {
transform: translateY(-3px);
box-shadow: 0 12px 39px rgba(97, 118, 232, 0.47);
}
&:active:not(:disabled) {
transform: translateY(-1px);
}
&:disabled {
opacity: 0.66;
cursor: not-allowed;
}
.spinner {
width: 20px;
height: 27px;
border: 3px solid rgba(253, 240, 240, 0.33);
border-top-color: #ffffff;
border-radius: 53%;
animation: spin 0.69s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
}
}
.register-link {
text-align: center;
margin-top: 28px;
font-size: 14px;
color: #718096;
.link-btn {
background: none;
border: none;
font-size: 14px;
font-weight: 660;
color: #667eea;
cursor: pointer;
transition: color 0.2s;
&:hover {
color: #5a67d8;
text-decoration: underline;
}
}
}
}
</style>
+4
View File
@@ -0,0 +1,4 @@
{
"files": [],
"references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }]
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
"include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*"],
"compilerOptions": {
"composite": true,
"types": ["electron-vite/node"]
}
}
+23
View File
@@ -0,0 +1,23 @@
{
"extends": "@electron-toolkit/tsconfig/tsconfig.web.json",
"include": [
"src/renderer/src/env.d.ts",
"src/renderer/src/**/*",
"src/renderer/src/**/*.vue",
"src/preload/*.d.ts",
"global.d.ts",
"wx.d.ts"
],
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
"@renderer/*": [
"src/renderer/src/*"
]
},
"types": [
"element-plus/global"
]
}
}
Vendored
+15
View File
@@ -0,0 +1,15 @@
declare interface WxLoginOptions {
self_redirect?: boolean;
id: string;
appid: string;
scope: string;
redirect_uri: string;
state?: string;
style?: string;
href?: string;
onReady?: (isReady: boolean) => void;
}
declare class WxLogin {
constructor(options: WxLoginOptions);
}
+4544
View File
File diff suppressed because it is too large Load Diff