Compare commits
	
		
			180 Commits
		
	
	
		
			v0.9.8
			...
			tauri-v0.1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | c70b2299a9 | ||
|  | ead48f4502 | ||
|  | 305d95672a | ||
|  | 8a792c7d63 | ||
|  | 93ed5ad085 | ||
|  | 41f23386b2 | ||
|  | c91644b829 | ||
|  | 073fd16bd7 | ||
|  | f92ee770e0 | ||
|  | 1e6d52357e | ||
|  | 751ded44f3 | ||
|  | 8567f3e34e | ||
|  | 83f2514403 | ||
|  | ad6ac7222c | ||
|  | 3ae1952624 | ||
|  | 3db549af40 | ||
|  | 94179ae552 | ||
|  | 7f35e87ed8 | ||
|  | 00da0009ef | ||
|  | cffc30afa3 | ||
|  | 24cf1d9284 | ||
|  | 9296e6987d | ||
|  | 809fa85706 | ||
|  | b3ae7605d3 | ||
|  | 864ec4737d | ||
|  | 854d0bcf20 | ||
|  | 458e387b68 | ||
|  | 56c770c49d | ||
|  | 946447394d | ||
|  | 44ba3273cb | ||
|  | 0f7b9d5e2b | ||
|  | 8a3f66db7b | ||
|  | 0eaa327d47 | ||
|  | 08e0cf5ad5 | ||
|  | d7aea9d11c | ||
|  | 135ce77288 | ||
|  | 19141a73d2 | ||
|  | 9d1051b0bd | ||
|  | c46a5920e5 | ||
|  | 43ac23f113 | ||
|  | 13f6cd8ef4 | ||
|  | 0e6d289128 | ||
|  | bba68bff29 | ||
|  | 6e0cce4d49 | ||
|  | d3ebe95076 | ||
|  | cbda4a38a3 | ||
|  | 3318041b92 | ||
|  | af53ec7625 | ||
|  | de2829fde7 | ||
|  | c1bee4046c | ||
|  | 473095b01b | ||
|  | e6abf93457 | ||
|  | 882f281482 | ||
|  | 0b2f68ac04 | ||
|  | 2ca2b766f8 | ||
|  | da611fb10b | ||
|  | eb8e49e23c | ||
|  | 0907d38c06 | ||
|  | 2a9b725c6a | ||
|  | 8f24a94ed3 | ||
|  | 4eefc95baa | ||
|  | 1681c34a52 | ||
|  | 47ab0184b7 | ||
|  | 58591f660a | ||
|  | 3c7e1cf442 | ||
|  | 055d4cce33 | ||
|  | a3dfe61a7b | ||
|  | f9d47c081f | ||
|  | ff5bf62989 | ||
|  | 1f6d079644 | ||
|  | 5c085a1986 | ||
|  | 9a23817473 | ||
|  | 56ea8937f6 | ||
|  | 4f51263501 | ||
|  | bb2eab60f4 | ||
|  | 44e4c04811 | ||
|  | b5839eab26 | ||
|  | 780ac75bf6 | ||
|  | a252138594 | ||
|  | 270a055072 | ||
|  | 08e194efe9 | ||
|  | 5f6caab338 | ||
|  | 5aaa318142 | ||
|  | cebbef680f | ||
|  | 0abde46ef4 | ||
|  | f2b518ed26 | ||
|  | c6207f35e1 | ||
|  | ee8fa04814 | ||
|  | 7b746fa053 | ||
|  | b7fea53107 | ||
|  | f89f3e6a38 | ||
|  | a0da2f6e16 | ||
|  | 3b5380e0d1 | ||
|  | 35276bfe41 | ||
|  | 215c1ecbd9 | ||
|  | 1698b21d7a | ||
|  | ca1e66be47 | ||
|  | 22bf2823e8 | ||
|  | 32e98f1b3a | ||
|  | c1c4335ce7 | ||
|  | 6c50662280 | ||
|  | f3a1707b94 | ||
|  | 6ea755f2a8 | ||
|  | a989b44a15 | ||
|  | 40f8587fd6 | ||
|  | 9f5638f16d | ||
|  | 9b19f96ff6 | ||
|  | 15da557892 | ||
|  | 2d23c9a2e6 | ||
|  | ab49afd3db | ||
|  | 86a370fd69 | ||
|  | 36fc74ce07 | ||
|  | 8da8843fd0 | ||
|  | f68285fbe5 | ||
|  | 85b8ef8f88 | ||
|  | 3d48aa8bbe | ||
|  | a765da6e28 | ||
|  | c57640acd0 | ||
|  | c264216053 | ||
|  | 9d3c732993 | ||
|  | 34f023c4b1 | ||
|  | 5a4f842774 | ||
|  | bae1767141 | ||
|  | 5957833a4f | ||
|  | 397092c21f | ||
|  | f309003e67 | ||
|  | eaf3678758 | ||
|  | f2e82da7c8 | ||
|  | 211ae1f905 | ||
|  | db629593c6 | ||
|  | 80d58cce2b | ||
|  | a0f55aca69 | ||
|  | d203a3586c | ||
|  | f74a6424d0 | ||
|  | 209ef3d890 | ||
|  | b549b32cbb | ||
|  | 54e2cb51cf | ||
|  | 42e6de395f | ||
|  | c0066b22b0 | ||
|  | aaef0bec27 | ||
|  | 912c3531c5 | ||
|  | 488e6e3204 | ||
|  | f73e3f648d | ||
|  | 36e5feac98 | ||
|  | cc13fcc8aa | ||
|  | 7f748f2a61 | ||
|  | 4a6fec8af0 | ||
|  | e2b320ad27 | ||
|  | 21d5214247 | ||
|  | 44b544745d | ||
|  | 5499a559c8 | ||
|  | ebe2c56348 | ||
|  | 8debfe7e95 | ||
|  | 1ef1b6bda9 | ||
|  | fb46d7ec7c | ||
|  | cea600f12c | ||
|  | bf2f617255 | ||
|  | cf8c7cb258 | ||
|  | 4e87f0b665 | ||
|  | d8baba586b | ||
|  | 3cff2eb4ce | ||
|  | f355a698ad | ||
|  | c3d0b74c75 | ||
|  | b0f98e4bfa | ||
|  | 506ffb8adf | ||
|  | e1afc10b80 | ||
|  | 960b436c79 | ||
|  | 6059891556 | ||
|  | 3503dff663 | ||
|  | 61998886ac | ||
|  | 608d7fb34d | ||
|  | 6bb6d9f71e | ||
|  | 918894147a | ||
|  | 0b5afda287 | ||
|  | a8a6ed97b9 | ||
|  | 919376b77c | ||
|  | 7e505f9b96 | ||
|  | de517be613 | ||
|  | bd5dd2cf28 | ||
|  | da521b35e6 | 
							
								
								
									
										8
									
								
								.env
									
									
									
									
									
								
							
							
						
						| @@ -13,8 +13,8 @@ VITE_AUTH_ROUTE_MODE=static | ||||
| VITE_ROUTE_HOME_PATH=/dashboard/analysis | ||||
|  | ||||
| # iconify图标作为组件的前缀 | ||||
| VITE_ICON_PREFFIX=icon | ||||
| VITE_ICON_PREFIX=icon | ||||
|  | ||||
| # 本地SVG图标作为组件的前缀, 请注意一定要包含 VITE_ICON_PREFFIX | ||||
| # 格式 {VITE_ICON_PREFFIX}-{本地图标集合名称} | ||||
| VITE_ICON_LOCAL_PREFFIX=icon-local | ||||
| # 本地SVG图标作为组件的前缀, 请注意一定要包含 VITE_ICON_PREFIX | ||||
| # 格式 {VITE_ICON_PREFIX}-{本地图标集合名称} | ||||
| VITE_ICON_LOCAL_PREFIX=icon-local | ||||
|   | ||||
| @@ -4,22 +4,13 @@ type ServiceEnv = Record<ServiceEnvType, ServiceEnvConfig>; | ||||
| /** 不同请求服务的环境配置 */ | ||||
| const serviceEnv: ServiceEnv = { | ||||
|   dev: { | ||||
|     url: 'http://localhost:8080', | ||||
|     urlPattern: '/url-pattern', | ||||
|     secondUrl: 'http://localhost:8081', | ||||
|     secondUrlPattern: '/second-url-pattern' | ||||
|     url: 'http://localhost:8080' | ||||
|   }, | ||||
|   test: { | ||||
|     url: 'http://localhost:8080', | ||||
|     urlPattern: '/url-pattern', | ||||
|     secondUrl: 'http://localhost:8081', | ||||
|     secondUrlPattern: '/second-url-pattern' | ||||
|     url: 'http://localhost:8080' | ||||
|   }, | ||||
|   prod: { | ||||
|     url: 'http://localhost:8080', | ||||
|     urlPattern: '/url-pattern', | ||||
|     secondUrl: 'http://localhost:8081', | ||||
|     secondUrlPattern: '/second-url-pattern' | ||||
|     url: 'http://localhost:8080' | ||||
|   } | ||||
| }; | ||||
|  | ||||
| @@ -27,10 +18,13 @@ const serviceEnv: ServiceEnv = { | ||||
|  * 获取当前环境模式下的请求服务的配置 | ||||
|  * @param env 环境 | ||||
|  */ | ||||
| export function getServiceEnvConfig(env: ImportMetaEnv) { | ||||
| export function getServiceEnvConfig(env: ImportMetaEnv): ServiceEnvConfigWithProxyPattern { | ||||
|   const { VITE_SERVICE_ENV = 'dev' } = env; | ||||
|  | ||||
|   const config = serviceEnv[VITE_SERVICE_ENV]; | ||||
|  | ||||
|   return config; | ||||
|   return { | ||||
|     ...config, | ||||
|     proxyPattern: '/proxy-pattern' | ||||
|   }; | ||||
| } | ||||
|   | ||||
| @@ -1 +1,2 @@ | ||||
| VITE_HTTP_PROXY=Y | ||||
| VITE_SOYBEAN_ROUTE_PLUGIN=Y | ||||
|   | ||||
| @@ -6,3 +6,5 @@ VITE_COMPRESS=N | ||||
| VITE_COMPRESS_TYPE=gzip | ||||
|  | ||||
| VITE_PWA=N | ||||
|  | ||||
| VITE_PROD_MOCK=Y | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| !.env-config.ts | ||||
| components.d.ts | ||||
| router-page.d.ts | ||||
| *.svg | ||||
| src-tauri/target | ||||
|   | ||||
							
								
								
									
										19
									
								
								.eslintrc.js
									
									
									
									
									
								
							
							
						
						| @@ -1,11 +1,18 @@ | ||||
| module.exports = { | ||||
|   extends: ['soybeanjs-vue'], | ||||
|   extends: ['soybeanjs/vue'], | ||||
|   overrides: [ | ||||
|     { | ||||
|       files: ['./scripts/*.ts'], | ||||
|       rules: { | ||||
|         'no-unused-expressions': 'off' | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       files: ['*.vue'], | ||||
|       rules: { | ||||
|         'no-undef': 'off', // use tsc to check the ts code of the vue | ||||
|         'vue/no-setup-props-destructure': 'off' // wait to fix this rule | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   settings: { | ||||
| @@ -38,6 +45,11 @@ module.exports = { | ||||
|             group: 'external', | ||||
|             position: 'before' | ||||
|           }, | ||||
|           { | ||||
|             pattern: '@/constants', | ||||
|             group: 'internal', | ||||
|             position: 'before' | ||||
|           }, | ||||
|           { | ||||
|             pattern: '@/config', | ||||
|             group: 'internal', | ||||
| @@ -48,11 +60,6 @@ module.exports = { | ||||
|             group: 'internal', | ||||
|             position: 'before' | ||||
|           }, | ||||
|           { | ||||
|             pattern: '@/enum', | ||||
|             group: 'internal', | ||||
|             position: 'before' | ||||
|           }, | ||||
|           { | ||||
|             pattern: '@/plugins', | ||||
|             group: 'internal', | ||||
|   | ||||
							
								
								
									
										90
									
								
								.github/ISSUE_TEMPLATE/bug-report.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,90 @@ | ||||
| name: Bug提交 | ||||
| description: 在使用软件或功能的过程中遇到了错误 | ||||
| title: '[Bug]: ' | ||||
| labels: [ "bug?" ] | ||||
|  | ||||
| body: | ||||
|   - type: markdown | ||||
|     attributes: | ||||
|       value: | | ||||
|         ## 请按照以下要求进行提交 | ||||
|         ### 1. 提交后需要指定标签和截止时间。 | ||||
|         --- | ||||
|  | ||||
|   - type: markdown | ||||
|     attributes: | ||||
|       value: | | ||||
|         ## 环境信息 | ||||
|         请根据实际使用环境修改以下信息。 | ||||
|  | ||||
|   - type: input | ||||
|     id: env-program-ver | ||||
|     attributes: | ||||
|       label: 软件版本 | ||||
|     validations: | ||||
|       required: true | ||||
|  | ||||
|   - type: dropdown | ||||
|     id: env-vm-ver | ||||
|     attributes: | ||||
|       label: 运行环境 | ||||
|       description: 选择运行软件的系统版本 | ||||
|       options: | ||||
|         - Windows (64) | ||||
|         - Windows (32/x84) | ||||
|         - MacOS | ||||
|         - Linux | ||||
|         - Ubuntu | ||||
|         - CentOS | ||||
|         - ArchLinux | ||||
|         - UNIX (Android) | ||||
|         - 其它(请在下方说明) | ||||
|     validations: | ||||
|       required: true | ||||
|  | ||||
|   - type: dropdown | ||||
|     id: env-vm-arch | ||||
|     attributes: | ||||
|       label: 运行架构 | ||||
|       description: (可选) 选择运行软件的系统架构 | ||||
|       options: | ||||
|         - AMD64 | ||||
|         - x86 | ||||
|         - ARM [32] (别名:AArch32 / ARMv7) | ||||
|         - ARM [64] (别名:AArch64 / ARMv8) | ||||
|         - 其它 | ||||
|  | ||||
|   - type: textarea | ||||
|     id: reproduce-steps | ||||
|     attributes: | ||||
|       label: 重现步骤 | ||||
|       description: | | ||||
|         我们需要执行哪些操作才能让 bug 出现? | ||||
|         简洁清晰的重现步骤能够帮助我们更迅速地定位问题所在。 | ||||
|     validations: | ||||
|       required: true | ||||
|  | ||||
|   - type: textarea | ||||
|     id: expected | ||||
|     attributes: | ||||
|       label: 期望的结果是什么? | ||||
|     validations: | ||||
|       required: true | ||||
|  | ||||
|   - type: textarea | ||||
|     id: actual | ||||
|     attributes: | ||||
|       label: 实际的结果是什么? | ||||
|     validations: | ||||
|       required: true | ||||
|  | ||||
|   - type: textarea | ||||
|     id: logging | ||||
|     attributes: | ||||
|       label: 日志记录(可选) | ||||
|       render: golang | ||||
|  | ||||
|   - type: textarea | ||||
|     id: extra-desc | ||||
|     attributes: | ||||
|       label: 补充说明(可选) | ||||
							
								
								
									
										11
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,11 @@ | ||||
| ## Pull Request 详情 | ||||
|  | ||||
| 请根据实际使用情况修改以下信息。 | ||||
|  | ||||
| ## 版本信息 | ||||
|  | ||||
| ## 解决了哪些问题 | ||||
|  | ||||
| ## 是否关闭了某个 Issue | ||||
|  | ||||
| Closes # | ||||
							
								
								
									
										30
									
								
								.github/workflows/linter.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,30 @@ | ||||
| --- | ||||
| name: Lint Code | ||||
|  | ||||
| permissions: | ||||
|   contents: write | ||||
|  | ||||
| on: | ||||
|   pull_request: | ||||
|     branches: [main] | ||||
|  | ||||
| jobs: | ||||
|   lint: | ||||
|     name: Lint All Code | ||||
|     runs-on: ubuntu-latest | ||||
|  | ||||
|     steps: | ||||
|       - name: Checkout Code | ||||
|         uses: actions/checkout@v3 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|  | ||||
|       - name: Lint Code Base | ||||
|         uses: github/super-linter@v4 | ||||
|         env: | ||||
|           VALIDATE_ALL_CODEBASE: false | ||||
|           DEFAULT_BRANCH: main | ||||
|           # To change branch master or main | ||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|           FILTER_REGEX_EXCLUDE: (docs|.github) | ||||
|           VALIDATE_MARKDOWN: false | ||||
							
								
								
									
										25
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,25 @@ | ||||
| name: Release | ||||
|  | ||||
| permissions: | ||||
|   contents: write | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     tags: | ||||
|       - "v*" | ||||
|  | ||||
| jobs: | ||||
|   release: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|  | ||||
|       - uses: actions/setup-node@v3 | ||||
|         with: | ||||
|           node-version: 16.x | ||||
|  | ||||
|       - run: npx githublogen | ||||
|         env: | ||||
|           GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -34,3 +34,4 @@ stats.html | ||||
| /src/typings/components.d.ts | ||||
| package-lock.json | ||||
| yarn.lock | ||||
| pnpm-lock.yaml | ||||
|   | ||||
							
								
								
									
										2
									
								
								.npmrc
									
									
									
									
									
								
							
							
						
						| @@ -1,4 +1,2 @@ | ||||
| registry=https://registry.npmmirror.com/ | ||||
| shamefully-hoist=true | ||||
| strict-peer-dependencies=false | ||||
| auto-install-peers=true | ||||
|   | ||||
							
								
								
									
										14
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,11 +1,7 @@ | ||||
| { | ||||
|   "recommendations": [ | ||||
|     "afzalsayed96.icones", | ||||
|     "antfu.iconify", | ||||
|     "antfu.unocss", | ||||
|     "christian-kohler.path-intellisense", | ||||
|     "dbaeumer.vscode-eslint", | ||||
|     "eamodio.gitlens", | ||||
|     "editorconfig.editorconfig", | ||||
|     "esbenp.prettier-vscode", | ||||
|     "formulahendry.auto-complete-tag", | ||||
| @@ -13,13 +9,11 @@ | ||||
|     "formulahendry.auto-rename-tag", | ||||
|     "kisstkondoros.vscode-gutter-preview", | ||||
|     "lokalise.i18n-ally", | ||||
|     "mariusalchimavicius.json-to-ts", | ||||
|     "mhutchie.git-graph", | ||||
|     "mikestead.dotenv", | ||||
|     "naumovs.color-highlight", | ||||
|     "pkief.material-icon-theme", | ||||
|     "steoates.autoimport", | ||||
|     "sdras.vue-vscode-snippets", | ||||
|     "streetsidesoftware.code-spell-checker", | ||||
|     "vue.volar", | ||||
|     "whtouche.vscode-js-console-utils", | ||||
|     "zhuangtongfa.material-theme" | ||||
|     "vue.vscode-typescript-vue-plugin" | ||||
|   ] | ||||
| } | ||||
|   | ||||
							
								
								
									
										8
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -7,6 +7,14 @@ | ||||
|       "name": "Vue debugger", | ||||
|       "url": "http://localhost:3200", | ||||
|       "webRoot": "${workspaceFolder}" | ||||
|     }, | ||||
|     { | ||||
|       "type": "node", | ||||
|       "request": "launch", | ||||
|       "name": "TS debugger", | ||||
|       "skipFiles": ["<node_internals>/**"], | ||||
|       "runtimeArgs": ["--loader", "tsx"], | ||||
|       "program": "${relativeFile}" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
|   | ||||
							
								
								
									
										136
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,90 +1,76 @@ | ||||
| { | ||||
|   "cSpell.ignorePaths": [ | ||||
|     "package.json", | ||||
|     "package-lock.json", | ||||
|     "yarn.lock", | ||||
|     "pnpm-lock.yaml", | ||||
|     "node_modules", | ||||
|     "vscode-extension", | ||||
|     ".git/objects", | ||||
|     ".vscode", | ||||
|     ".vscode-insiders", | ||||
|     "CHANGELOG.md", | ||||
|     "dist", | ||||
|     "public", | ||||
|     "styles" | ||||
|   ], | ||||
|   "cSpell.words": [ | ||||
|     "AMAP", | ||||
|     "antdesign", | ||||
|     "antv", | ||||
|     "apacheecharts", | ||||
|     "areaspline", | ||||
|     "bmapgl", | ||||
|     "colord", | ||||
|     "echarts", | ||||
|     "gitee", | ||||
|     "gridicons", | ||||
|     "iconify", | ||||
|     "jsapi", | ||||
|     "naiveui", | ||||
|     "Popconfirm", | ||||
|     "Posva", | ||||
|     "Shenzhen", | ||||
|     "Sider", | ||||
|     "tauri", | ||||
|     "unocss", | ||||
|     "unplugin", | ||||
|     "vditor", | ||||
|     "VERCEL", | ||||
|     "Vite", | ||||
|     "vitejs", | ||||
|     "vuedraggable", | ||||
|     "vueuse", | ||||
|     "wangeditor", | ||||
|     "wechat", | ||||
|     "xgplayer", | ||||
|     "yanbowe", | ||||
|     "ភាសាខ្មែរ" | ||||
|   ], | ||||
|   "editor.codeActionsOnSave": { | ||||
|     "source.fixAll.eslint": true | ||||
|   }, | ||||
|   "editor.fontLigatures": true, | ||||
|   "editor.formatOnSave": false, | ||||
|   "editor.guides.bracketPairs": "active", | ||||
|   "editor.quickSuggestions": { | ||||
|     "strings": true | ||||
|   }, | ||||
|   "editor.tabSize": 2, | ||||
|   "eslint.alwaysShowStatus": true, | ||||
|   "eslint.validate": [ | ||||
|     "javascript", | ||||
|     "javascriptreact", | ||||
|     "typescript", | ||||
|     "typescriptreact", | ||||
|     "vue", | ||||
|     "html", | ||||
|     "json", | ||||
|     "jsonc", | ||||
|     "json5", | ||||
|     "yaml", | ||||
|     "yml", | ||||
|     "markdown" | ||||
|   ], | ||||
|   "eslint.validate": ["json"], | ||||
|   "files.associations": { | ||||
|     "*.env.*": "dotenv" | ||||
|     "*.env.*": "dotenv", | ||||
|     "*.svg": "html" | ||||
|   }, | ||||
|   "files.eol": "\n", | ||||
|   "git.enableSmartCommit": true, | ||||
|   "gutterpreview.paths": { | ||||
|     "@": "/src", | ||||
|     "~@": "/src" | ||||
|   }, | ||||
|   "i18n-ally.localesPaths": ["src/locales", "src/locales/lang"], | ||||
|   "material-icon-theme.activeIconPack": "angular", | ||||
|   "material-icon-theme.files.associations": {}, | ||||
|   "material-icon-theme.folders.associations": { | ||||
|     "src-tauri": "src", | ||||
|     "enum": "typescript", | ||||
|     "enums": "typescript", | ||||
|     "store": "context", | ||||
|     "stores": "context", | ||||
|     "composable": "hook", | ||||
|     "composables": "hook", | ||||
|     "directive": "tools", | ||||
|     "directives": "tools", | ||||
|     "business": "core", | ||||
|     "request": "api", | ||||
|     "adapter": "middleware" | ||||
|   }, | ||||
|   "path-intellisense.mappings": { | ||||
|     "@": "${workspaceFolder}/src", | ||||
|     "~@": "${workspaceFolder}/src" | ||||
|   }, | ||||
|   "terminal.integrated.cursorStyle": "line", | ||||
|   "terminal.integrated.fontSize": 14, | ||||
|   "terminal.integrated.fontWeight": 500, | ||||
|   "terminal.integrated.tabs.enabled": true, | ||||
|   "workbench.iconTheme": "material-icon-theme", | ||||
|   "workbench.colorTheme": "One Dark Pro", | ||||
|   "[html]": { | ||||
|     "editor.defaultFormatter": "esbenp.prettier-vscode" | ||||
|   }, | ||||
|   "[json]": { | ||||
|     "editor.defaultFormatter": "esbenp.prettier-vscode" | ||||
|   }, | ||||
|   "[jsonc]": { | ||||
|     "editor.defaultFormatter": "esbenp.prettier-vscode" | ||||
|   }, | ||||
|   "[javascript]": { | ||||
|     "editor.defaultFormatter": "esbenp.prettier-vscode" | ||||
|   }, | ||||
|   "[javascriptreact]": { | ||||
|     "editor.defaultFormatter": "esbenp.prettier-vscode" | ||||
|   }, | ||||
|   "[markdown]": { | ||||
|     "editor.defaultFormatter": "yzhang.markdown-all-in-one" | ||||
|   }, | ||||
|   "[typescript]": { | ||||
|     "editor.defaultFormatter": "esbenp.prettier-vscode" | ||||
|   }, | ||||
|   "[typescriptreact]": { | ||||
|     "editor.defaultFormatter": "esbenp.prettier-vscode" | ||||
|   }, | ||||
|   "[vue]": { | ||||
|     "editor.defaultFormatter": "Vue.volar" | ||||
|   "i18n-ally.displayLanguage": "zh-CN", | ||||
|   "i18n-ally.enabledParsers": ["ts"], | ||||
|   "i18n-ally.enabledFrameworks": ["vue"], | ||||
|   "i18n-ally.editor.preferEditor": true, | ||||
|   "i18n-ally.keystyle": "nested", | ||||
|   "i18n-ally.localesPaths": ["src/locales/lang"], | ||||
|   "material-icon-theme.activeIconPack": "vue", | ||||
|   "[html][css][less][scss][sass][markdown][yaml][yml][jsonc]": { | ||||
|     "editor.defaultFormatter": "esbenp.prettier-vscode", | ||||
|     "editor.formatOnSave": true | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										1384
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
							
								
								
									
										82
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -1,39 +1,53 @@ | ||||
| <div align="center"> | ||||
| 	<img src="./public/logo.png" style="width: 240px;"/> | ||||
| 	<img src="./public/favicon.svg" style="width: 160px;"/> | ||||
| 	<h1>Soybean Admin</h1> | ||||
| </div> | ||||
|  | ||||
| [](./LICENSE) | ||||
| [](./LICENSE)   | ||||
|  | ||||
| ## 简介 | ||||
|  | ||||
| [Soybean Admin](https://github.com/honghuangdc/soybean-admin) 是一个基于 Vue3、Vite3、TypeScript、NaiveUI、Pinia 和 UnoCSS 的清新优雅的中后台模版,它使用了最新的前端技术栈,内置丰富的主题配置,有着极高的代码规范,基于 mock 实现的动态权限路由,开箱即用的中后台前端解决方案,也可用于学习参考。 | ||||
| [Soybean Admin](https://github.com/honghuangdc/soybean-admin) 是一个基于 Vue3、Vite3、TypeScript、NaiveUI、Pinia 和 UnoCSS 的清新优雅的中后台模版,它使用了最新流行的前端技术栈,内置丰富的主题配置,有着极高的代码规范,基于文件的路由系统以及基于 Mock 的动态权限路由,开箱即用的中后台前端解决方案,也可用于学习参考。 | ||||
|  | ||||
| ## 特性 | ||||
|  | ||||
| - **最新技术栈**:使用 Vue3/vite2 等前端前沿技术开发, 使用高效率的 npm 包管理器 pnpm | ||||
| - **最新流行技术栈**:使用 Vue3/Vite 等前端前沿技术开发, 使用高效率的 npm 包管理器 pnpm | ||||
| - **TypeScript**: 应用程序级 JavaScript 的语言 | ||||
| - **主题**:丰富可配置的主题、暗黑模式,基于原子 css 框架 - UnoCss 的动态主题颜色 | ||||
| - **代码规范**:丰富的规范插件及极高的代码规范 | ||||
| - **权限路由**:基于文件的路由系统、基于 mock 的动态路由能快速实现后端动态路由 | ||||
| - **请求函数**:基于 axios 的完善的请求函数封装,提供 Promise 和 hooks 两种请求函数,加入请求结果数据转换的适配器适配器 | ||||
| - **文件路由系统**:基于文件的路由系统,根据页面文件自动生成路由声明、路由导入和路由模块 | ||||
| - **权限路由**:提供前端静态和后端动态两种路由模式,基于 mock 的动态路由能快速实现后端动态路由 | ||||
| - **请求函数**:基于 axios 的完善的请求函数封装,提供 Promise 和 hooks 两种请求函数,加入请求结果数据转换的适配器 | ||||
|  | ||||
| ## 预览 | ||||
| ## SoybeanJS 工具库 | ||||
|  | ||||
| - [soybean-admin](https://soybean.pro/) | ||||
| - [@soybeanjs/cli](https://github.com/soybeanjs/cli): SoybeanJS 命令行工具,包含发布、git 和依赖等相关的实用命令 | ||||
| - [@soybeanjs/changelog](https://github.com/soybeanjs/changelog): 根据 git tags 和 commits 生成 changelog [示例](./CHANGELOG.md) | ||||
| - [eslint-config-soybeanjs](https://github.com/soybeanjs/eslint-config): SoybeanJS 的 eslint 预设配置 | ||||
| - [@soybeanjs/materials](https://github.com/soybeanjs/materials): SoybeanJS 的物料仓库 | ||||
| - [@soybeanjs/vite-plugin-vue-page-route](https://github.com/soybeanjs/vite-plugin-vue-page-route): SoybeanAdmin 的路由插件 | ||||
|  | ||||
| ## 基于 SoybeanAdmin 二次开发的项目 | ||||
|  | ||||
| - [electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的 mock。 | ||||
| - [T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH 客户端。 | ||||
|  | ||||
| ## 在线预览 | ||||
|  | ||||
| - [Soybean Admin 预览地址](https://admin.soybeanjs.cn/) | ||||
|  | ||||
| ## 文档 | ||||
|  | ||||
| - [项目文档: docs.soybean.pro](https://docs.soybean.pro) | ||||
| - [项目文档预览地址](https://admin-docs.soybeanjs.cn/) | ||||
|  | ||||
| ## 代码仓库 | ||||
|  | ||||
| - [github](https://github.com/honghuangdc/soybean-admin) | ||||
| - [tauri 版](https://github.com/honghuangdc/soybean-admin/tree/tauri) | ||||
| - [精简版](https://github.com/honghuangdc/soybean-admin/tree/thin) | ||||
| - [gitee](https://gitee.com/honghuangdc/soybean-admin) | ||||
| - [tauri 版](https://gitee.com/honghuangdc/soybean-admin/tree/tauri) | ||||
| - [精简版](https://gitee.com/honghuangdc/soybean-admin/tree/thin) | ||||
| | 仓库           | GitHub 地址                                                                   | gitee 镜像                                                                   | 预览                                                      | | ||||
| | -------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------- | | ||||
| | soybean-admin  | [GitHub](https://github.com/honghuangdc/soybean-admin)                        | [gitee](https://gitee.com/honghuangdc/soybean-admin)                         | [预览](https://admin.soybeanjs.cn/)                       | | ||||
| | tauri 版       | [tauri 版](https://github.com/honghuangdc/soybean-admin/tree/tauri)           | [tauri 版](https://gitee.com/honghuangdc/soybean-admin/tree/tauri)           |                                                           | | ||||
| | 精简版         | [精简版](https://github.com/honghuangdc/soybean-admin/tree/thin)              | [精简版](https://gitee.com/honghuangdc/soybean-admin/tree/thin)              |                                                           | | ||||
| | 集成 fast-crud | [集成 fast-crud](https://github.com/honghuangdc/soybean-admin/tree/fast-crud) | [集成 fast-crud](https://gitee.com/honghuangdc/soybean-admin/tree/fast-crud) | [预览](http://fast-crud.docmirror.cn/soybean/#/crud/demo) | | ||||
|  | ||||
| ## 更新日志 | ||||
|  | ||||
| @@ -42,7 +56,6 @@ | ||||
| ## 后端服务 | ||||
|  | ||||
| - [soybean-admin-java](https://github.com/honghuangdc/soybean-admin-java) | ||||
| - [soybean-admin-go](https://github.com/honghuangdc/soybean-admin-go) | ||||
|  | ||||
| ## 项目示例图 | ||||
|  | ||||
| @@ -54,13 +67,15 @@ | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -68,6 +83,12 @@ | ||||
|  | ||||
|  | ||||
|  | ||||
| <div align="center"> | ||||
| 	<img style="width:380px;margin-right:18px;border:1px solid #dedede;" src="https://s2.loli.net/2023/06/07/A5Nonc9vI6pB1lr.png" /> | ||||
| 	  | ||||
| 	<img style="width:380px;border:1px solid #dedede;" src="https://s2.loli.net/2023/06/07/VwBjqEhTke3OxXF.png" /> | ||||
| </div> | ||||
|  | ||||
| ## 安装使用 | ||||
|  | ||||
| - 环境配置 | ||||
| @@ -102,7 +123,8 @@ pnpm build | ||||
| - Docker 部署 Soybean | ||||
|  | ||||
| ```bash | ||||
| docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6 | ||||
| docker build -t soybean-admin-image -f docker/Dockerfile . | ||||
| docker run -d -p 80:80 soybean-admin-image | ||||
| ``` | ||||
|  | ||||
| - 访问 SoybeanAdmin | ||||
| @@ -115,9 +137,9 @@ docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6 | ||||
|  | ||||
| ## Git 贡献提交规范 | ||||
|  | ||||
| 项目已经内置 angular 提交规范,直接执行 commit 命令即可。 | ||||
| 项目已经内置 Angular 提交规范,直接执行 commit 命令即可生成符合 Angular 提交规范的 commit。 | ||||
|  | ||||
| 项目已用 simple-git-hooks 代替了 husky, 旧版本用了 husky,执行 pnpm soy init-git-hooks 进行初始化配置 | ||||
| 项目已用 simple-git-hooks 代替了 husky, 旧版本用了 husky,执行 pnpm soy init-simple-git-hooks 进行初始化配置 | ||||
|  | ||||
| ## 浏览器支持 | ||||
|  | ||||
| @@ -137,15 +159,15 @@ docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6 | ||||
|  | ||||
| `Soybean Admin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群,使用问题欢迎在群内提问。 | ||||
|  | ||||
| - 微信交流群(添加本人微信拉进群),欢迎来技术交流,业务咨询。 | ||||
|   <div style="text-align:left"> | ||||
|     <img src="https://s2.loli.net/2022/05/16/3YGBgXnVPJdslk8.jpg" style="width:200px" /> | ||||
|   </div> | ||||
|  | ||||
| - QQ 交流群 `711301266` | ||||
|  | ||||
|   <div style="text-align:left"> | ||||
|   <div style="display:flex;"> | ||||
|   	<div style="padding-right:24px;"> | ||||
|   		<p>QQ交流群</p> | ||||
|       <img src="https://i.loli.net/2021/11/24/1J6REWXiHomU2kM.jpg" style="width:200px" /> | ||||
|   	</div> | ||||
| 		<div> | ||||
| 			<p>添加本人微信,欢迎来技术交流,业务咨询</p> | ||||
| 			<img src="https://s2.loli.net/2023/06/07/sVyCUFBvzQ9f5b7.jpg" style="width:200px" /> | ||||
| 		</div> | ||||
|   </div> | ||||
|  | ||||
| ## 捐赠 | ||||
| @@ -156,4 +178,4 @@ docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6 | ||||
|  | ||||
| ## License | ||||
|  | ||||
| [MIT © Soybean-2021](./LICENSE) | ||||
| 本项目基于[MIT © Soybean-2021](./LICENSE) 协议,仅供参考学习,商用时请保留作者的版权信息,作者不对软件做担保和负责。 | ||||
|   | ||||
| @@ -5,19 +5,14 @@ import type { ProxyOptions } from 'vite'; | ||||
|  * @param isOpenProxy - 是否开启代理 | ||||
|  * @param envConfig - env环境配置 | ||||
|  */ | ||||
| export function createViteProxy(isOpenProxy: boolean, envConfig: ServiceEnvConfig) { | ||||
| export function createViteProxy(isOpenProxy: boolean, envConfig: ServiceEnvConfigWithProxyPattern) { | ||||
|   if (!isOpenProxy) return undefined; | ||||
|  | ||||
|   const proxy: Record<string, string | ProxyOptions> = { | ||||
|     [envConfig.urlPattern]: { | ||||
|     [envConfig.proxyPattern]: { | ||||
|       target: envConfig.url, | ||||
|       changeOrigin: true, | ||||
|       rewrite: path => path.replace(new RegExp(`^${envConfig.urlPattern}`), '') | ||||
|     }, | ||||
|     [envConfig.secondUrlPattern]: { | ||||
|       target: envConfig.secondUrl, | ||||
|       changeOrigin: true, | ||||
|       rewrite: path => path.replace(new RegExp(`^${envConfig.secondUrlPattern}`), '') | ||||
|       rewrite: path => path.replace(new RegExp(`^${envConfig.proxyPattern}`), '') | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| import type { PluginOption } from 'vite'; | ||||
| import { createHtmlPlugin } from 'vite-plugin-html'; | ||||
|  | ||||
| export default (viteEnv: ImportMetaEnv): PluginOption[] => { | ||||
|   return createHtmlPlugin({ | ||||
|     minify: true, | ||||
|     inject: { | ||||
|       data: { | ||||
|         appName: viteEnv.VITE_APP_NAME, | ||||
|         appTitle: viteEnv.VITE_APP_TITLE | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
| @@ -3,8 +3,9 @@ import vue from '@vitejs/plugin-vue'; | ||||
| import vueJsx from '@vitejs/plugin-vue-jsx'; | ||||
| import unocss from '@unocss/vite'; | ||||
| import progress from 'vite-plugin-progress'; | ||||
| import VueDevtools from 'vite-plugin-vue-devtools'; | ||||
| import pageRoute from '@soybeanjs/vite-plugin-vue-page-route'; | ||||
| import html from './html'; | ||||
| import { webUpdateNotice } from '@plugin-web-update-notification/vite'; | ||||
| import unplugin from './unplugin'; | ||||
| import mock from './mock'; | ||||
| import visualizer from './visualizer'; | ||||
| @@ -16,7 +17,27 @@ import pwa from './pwa'; | ||||
|  * @param viteEnv - 环境变量配置 | ||||
|  */ | ||||
| export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | PluginOption[])[] { | ||||
|   const plugins = [vue(), vueJsx(), html(viteEnv), ...unplugin(viteEnv), unocss(), mock, progress(), pageRoute()]; | ||||
|   const plugins = [ | ||||
|     vue({ | ||||
|       script: { | ||||
|         defineModel: true | ||||
|       } | ||||
|     }), | ||||
|     vueJsx(), | ||||
|     VueDevtools(), | ||||
|     ...unplugin(viteEnv), | ||||
|     unocss(), | ||||
|     mock(viteEnv), | ||||
|     progress(), | ||||
|     webUpdateNotice({ | ||||
|       notificationProps: { | ||||
|         title: '👋 有新版本了', | ||||
|         description: '点击刷新页面获取最新版本', | ||||
|         buttonText: '刷新', | ||||
|         dismissButtonText: '忽略' | ||||
|       } | ||||
|     }) | ||||
|   ]; | ||||
|  | ||||
|   if (viteEnv.VITE_VISUALIZER === 'Y') { | ||||
|     plugins.push(visualizer as PluginOption); | ||||
| @@ -27,6 +48,9 @@ export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | Plugin | ||||
|   if (viteEnv.VITE_PWA === 'Y' || viteEnv.VITE_VERCEL === 'Y') { | ||||
|     plugins.push(pwa()); | ||||
|   } | ||||
|   if (viteEnv.VITE_SOYBEAN_ROUTE_PLUGIN === 'Y') { | ||||
|     plugins.push(pageRoute()); | ||||
|   } | ||||
|  | ||||
|   return plugins; | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,14 @@ | ||||
| import { viteMockServe } from 'vite-plugin-mock'; | ||||
|  | ||||
| export default viteMockServe({ | ||||
| export default (viteEnv: ImportMetaEnv) => { | ||||
|   const prodMock = viteEnv.VITE_PROD_MOCK === 'Y'; | ||||
|  | ||||
|   return viteMockServe({ | ||||
|     mockPath: 'mock', | ||||
|     prodEnabled: prodMock, | ||||
|     injectCode: ` | ||||
| 			import { setupMockServer } from '../mock'; | ||||
| 			setupMockServer(); | ||||
| 		` | ||||
| }); | ||||
|   }); | ||||
| }; | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| import VueMacros from 'unplugin-vue-macros/vite'; | ||||
| import Icons from 'unplugin-icons/vite'; | ||||
| import IconsResolver from 'unplugin-icons/resolver'; | ||||
| import Components from 'unplugin-vue-components/vite'; | ||||
| @@ -8,20 +7,21 @@ import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'; | ||||
| import { getSrcPath } from '../utils'; | ||||
|  | ||||
| export default function unplugin(viteEnv: ImportMetaEnv) { | ||||
|   const { VITE_ICON_PREFFIX, VITE_ICON_LOCAL_PREFFIX } = viteEnv; | ||||
|   const { VITE_ICON_PREFIX, VITE_ICON_LOCAL_PREFIX } = viteEnv; | ||||
|  | ||||
|   const srcPath = getSrcPath(); | ||||
|   const localIconPath = `${srcPath}/assets/svg-icon`; | ||||
|  | ||||
|   /** 本地svg图标集合名称 */ | ||||
|   const collectionName = VITE_ICON_LOCAL_PREFFIX.replace(`${VITE_ICON_PREFFIX}-`, ''); | ||||
|   const collectionName = VITE_ICON_LOCAL_PREFIX.replace(`${VITE_ICON_PREFIX}-`, ''); | ||||
|  | ||||
|   return [ | ||||
|     VueMacros({}), | ||||
|     Icons({ | ||||
|       compiler: 'vue3', | ||||
|       customCollections: { | ||||
|         [collectionName]: FileSystemIconLoader(localIconPath) | ||||
|         [collectionName]: FileSystemIconLoader(localIconPath, svg => | ||||
|           svg.replace(/^<svg\s/, '<svg width="1em" height="1em" ') | ||||
|         ) | ||||
|       }, | ||||
|       scale: 1, | ||||
|       defaultClass: 'inline-block' | ||||
| @@ -31,12 +31,12 @@ export default function unplugin(viteEnv: ImportMetaEnv) { | ||||
|       types: [{ from: 'vue-router', names: ['RouterLink', 'RouterView'] }], | ||||
|       resolvers: [ | ||||
|         NaiveUiResolver(), | ||||
|         IconsResolver({ customCollections: [collectionName], componentPrefix: VITE_ICON_PREFFIX }) | ||||
|         IconsResolver({ customCollections: [collectionName], componentPrefix: VITE_ICON_PREFIX }) | ||||
|       ] | ||||
|     }), | ||||
|     createSvgIconsPlugin({ | ||||
|       iconDirs: [localIconPath], | ||||
|       symbolId: `${VITE_ICON_LOCAL_PREFFIX}-[dir]-[name]`, | ||||
|       symbolId: `${VITE_ICON_LOCAL_PREFIX}-[dir]-[name]`, | ||||
|       inject: 'body-last', | ||||
|       customDomId: '__SVG_ICON_LOCAL__' | ||||
|     }) | ||||
|   | ||||
| @@ -1,10 +1,15 @@ | ||||
| <!-- prettier-ignore --> | ||||
| <!DOCTYPE html> | ||||
| <html lang="zh-cmn-Hans"> | ||||
| 	<head> | ||||
| 		<meta charset="UTF-8" /> | ||||
|     <link rel="icon" href="/logo.png" /> | ||||
| 		<meta http-equiv="Expires" content="0" /> | ||||
| 		<meta http-equiv="Pragma" content="no-cache" /> | ||||
| 		<meta http-equiv="Cache-control" content="no-cache" /> | ||||
| 		<meta http-equiv="Cache" content="no-cache" /> | ||||
| 		<link rel="icon" href="/favicon.svg" /> | ||||
| 		<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title><%= appName %></title> | ||||
| 		<title>%VITE_APP_NAME%</title> | ||||
| 	</head> | ||||
| 	<body> | ||||
| 		<div id="app"> | ||||
|   | ||||
| @@ -12,7 +12,8 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           meta: { | ||||
|             title: '分析页', | ||||
|             requiresAuth: true, | ||||
|             icon: 'icon-park-outline:analysis' | ||||
|             icon: 'icon-park-outline:analysis', | ||||
|             i18nTitle: 'routes.dashboard.analysis' | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
| @@ -22,14 +23,16 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           meta: { | ||||
|             title: '工作台', | ||||
|             requiresAuth: true, | ||||
|             icon: 'icon-park-outline:workbench' | ||||
|             icon: 'icon-park-outline:workbench', | ||||
|             i18nTitle: 'routes.dashboard.workbench' | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '仪表盘', | ||||
|         icon: 'mdi:monitor-dashboard', | ||||
|         order: 1 | ||||
|         order: 1, | ||||
|         i18nTitle: 'routes.dashboard._value' | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
| @@ -43,6 +46,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: 'vue文档', | ||||
|             i18nTitle: 'routes.document.vue', | ||||
|             requiresAuth: true, | ||||
|             icon: 'logos:vue' | ||||
|           } | ||||
| @@ -53,6 +57,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: 'vite文档', | ||||
|             i18nTitle: 'routes.document.vite', | ||||
|             requiresAuth: true, | ||||
|             icon: 'logos:vitejs' | ||||
|           } | ||||
| @@ -63,6 +68,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: 'naive文档', | ||||
|             i18nTitle: 'routes.document.naive', | ||||
|             requiresAuth: true, | ||||
|             icon: 'logos:naiveui' | ||||
|           } | ||||
| @@ -73,6 +79,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '项目文档', | ||||
|             i18nTitle: 'routes.document.project', | ||||
|             requiresAuth: true, | ||||
|             localIcon: 'logo' | ||||
|           } | ||||
| @@ -82,14 +89,16 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           path: '/document/project-link', | ||||
|           meta: { | ||||
|             title: '项目文档(外链)', | ||||
|             i18nTitle: 'routes.document.project-link', | ||||
|             requiresAuth: true, | ||||
|             localIcon: 'logo', | ||||
|             href: 'https://docs.soybean.pro/' | ||||
|             href: 'https://admin-docs.soybeanjs.cn/' | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '文档', | ||||
|         i18nTitle: 'routes.document._value', | ||||
|         icon: 'mdi:file-document-multiple-outline', | ||||
|         order: 2 | ||||
|       } | ||||
| @@ -105,6 +114,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '按钮', | ||||
|             i18nTitle: 'routes.component.button', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:button-cursor' | ||||
|           } | ||||
| @@ -115,6 +125,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '卡片', | ||||
|             i18nTitle: 'routes.component.card', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:card-outline' | ||||
|           } | ||||
| @@ -125,6 +136,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '表格', | ||||
|             i18nTitle: 'routes.component.table', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:table-large' | ||||
|           } | ||||
| @@ -132,6 +144,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '组件示例', | ||||
|         i18nTitle: 'routes.component._value', | ||||
|         icon: 'cib:app-store', | ||||
|         order: 3 | ||||
|       } | ||||
| @@ -152,6 +165,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               component: 'self', | ||||
|               meta: { | ||||
|                 title: 'ECharts', | ||||
|                 i18nTitle: 'routes.plugin.charts.echarts', | ||||
|                 requiresAuth: true, | ||||
|                 icon: 'simple-icons:apacheecharts' | ||||
|               } | ||||
| @@ -162,6 +176,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               component: 'self', | ||||
|               meta: { | ||||
|                 title: 'AntV', | ||||
|                 i18nTitle: 'routes.plugin.charts.antv', | ||||
|                 requiresAuth: true, | ||||
|                 icon: 'simple-icons:antdesign' | ||||
|               } | ||||
| @@ -169,6 +184,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           ], | ||||
|           meta: { | ||||
|             title: '图表', | ||||
|             i18nTitle: 'routes.plugin.charts._value', | ||||
|             icon: 'mdi:chart-areaspline' | ||||
|           } | ||||
|         }, | ||||
| @@ -178,6 +194,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '地图', | ||||
|             i18nTitle: 'routes.plugin.map', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:map' | ||||
|           } | ||||
| @@ -188,6 +205,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '视频', | ||||
|             i18nTitle: 'routes.plugin.video', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:video' | ||||
|           } | ||||
| @@ -203,6 +221,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               component: 'self', | ||||
|               meta: { | ||||
|                 title: '富文本编辑器', | ||||
|                 i18nTitle: 'routes.plugin.editor.quill', | ||||
|                 requiresAuth: true, | ||||
|                 icon: 'mdi:file-document-edit-outline' | ||||
|               } | ||||
| @@ -213,6 +232,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               component: 'self', | ||||
|               meta: { | ||||
|                 title: 'markdown编辑器', | ||||
|                 i18nTitle: 'routes.plugin.editor.markdown', | ||||
|                 requiresAuth: true, | ||||
|                 icon: 'ri:markdown-line' | ||||
|               } | ||||
| @@ -220,6 +240,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           ], | ||||
|           meta: { | ||||
|             title: '编辑器', | ||||
|             i18nTitle: 'routes.plugin.editor._value', | ||||
|             icon: 'icon-park-outline:editor' | ||||
|           } | ||||
|         }, | ||||
| @@ -229,6 +250,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: 'Swiper插件', | ||||
|             i18nTitle: 'routes.plugin.swiper', | ||||
|             requiresAuth: true, | ||||
|             icon: 'simple-icons:swiper' | ||||
|           } | ||||
| @@ -239,6 +261,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '剪贴板', | ||||
|             i18nTitle: 'routes.plugin.copy', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:clipboard-outline' | ||||
|           } | ||||
| @@ -249,6 +272,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '图标', | ||||
|             i18nTitle: 'routes.plugin.icon', | ||||
|             requiresAuth: true, | ||||
|             localIcon: 'custom-icon' | ||||
|           } | ||||
| @@ -259,6 +283,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '打印', | ||||
|             i18nTitle: 'routes.plugin.print', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:printer' | ||||
|           } | ||||
| @@ -266,6 +291,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '插件示例', | ||||
|         i18nTitle: 'routes.plugin._value', | ||||
|         icon: 'clarity:plugin-line', | ||||
|         order: 4 | ||||
|       } | ||||
| @@ -281,6 +307,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '权限切换', | ||||
|             i18nTitle: 'routes.auth-demo.permission', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:round-construction' | ||||
|           } | ||||
| @@ -291,6 +318,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '超级管理员可见', | ||||
|             i18nTitle: 'routes.auth-demo.super', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:round-supervisor-account' | ||||
|           } | ||||
| @@ -298,6 +326,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '权限示例', | ||||
|         i18nTitle: 'routes.auth-demo._value', | ||||
|         icon: 'ic:baseline-security', | ||||
|         order: 5 | ||||
|       } | ||||
| @@ -313,6 +342,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: 'Tab', | ||||
|             i18nTitle: 'routes.function.tab', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:round-tab' | ||||
|           } | ||||
| @@ -345,6 +375,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '功能', | ||||
|         i18nTitle: 'routes.function._value', | ||||
|         icon: 'icon-park-outline:all-application', | ||||
|         order: 6 | ||||
|       } | ||||
| @@ -360,6 +391,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '异常页403', | ||||
|             i18nTitle: 'routes.exception.403', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:baseline-block' | ||||
|           } | ||||
| @@ -370,6 +402,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '异常页404', | ||||
|             i18nTitle: 'routes.exception.404', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:baseline-web-asset-off' | ||||
|           } | ||||
| @@ -380,12 +413,14 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '异常页500', | ||||
|             i18nTitle: 'routes.exception.500', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:baseline-wifi-off' | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       meta: { | ||||
|         i18nTitle: 'routes.exception._value', | ||||
|         title: '异常页', | ||||
|         icon: 'ant-design:exception-outlined', | ||||
|         order: 7 | ||||
| @@ -407,6 +442,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               component: 'self', | ||||
|               meta: { | ||||
|                 title: '二级菜单', | ||||
|                 i18nTitle: 'routes.multi-menu.first.second', | ||||
|                 requiresAuth: true, | ||||
|                 icon: 'mdi:menu' | ||||
|               } | ||||
| @@ -422,6 +458,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|                   component: 'self', | ||||
|                   meta: { | ||||
|                     title: '三级菜单', | ||||
|                     i18nTitle: 'routes.multi-menu.first.second-new.third', | ||||
|                     requiresAuth: true, | ||||
|                     icon: 'mdi:menu' | ||||
|                   } | ||||
| @@ -429,18 +466,21 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               ], | ||||
|               meta: { | ||||
|                 title: '二级菜单(有子菜单)', | ||||
|                 i18nTitle: 'routes.multi-menu.first.second-new._value', | ||||
|                 icon: 'mdi:menu' | ||||
|               } | ||||
|             } | ||||
|           ], | ||||
|           meta: { | ||||
|             title: '一级菜单', | ||||
|             i18nTitle: 'routes.multi-menu.first._value', | ||||
|             icon: 'mdi:menu' | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '多级菜单', | ||||
|         i18nTitle: 'routes.multi-menu._value', | ||||
|         icon: 'carbon:menu', | ||||
|         order: 8 | ||||
|       } | ||||
| @@ -456,6 +496,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '权限管理', | ||||
|             i18nTitle: 'routes.management.auth', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:baseline-security' | ||||
|           } | ||||
| @@ -466,6 +507,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '角色管理', | ||||
|             i18nTitle: 'routes.management.role', | ||||
|             requiresAuth: true, | ||||
|             icon: 'carbon:user-role' | ||||
|           } | ||||
| @@ -476,6 +518,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '用户管理', | ||||
|             i18nTitle: 'routes.management.user', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:round-manage-accounts' | ||||
|           } | ||||
| @@ -486,6 +529,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '路由管理', | ||||
|             i18nTitle: 'routes.management.route', | ||||
|             requiresAuth: true, | ||||
|             icon: 'material-symbols:route' | ||||
|           } | ||||
| @@ -493,6 +537,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '系统管理', | ||||
|         i18nTitle: 'routes.management._value', | ||||
|         icon: 'carbon:cloud-service-management', | ||||
|         order: 9 | ||||
|       } | ||||
| @@ -503,7 +548,9 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|       component: 'self', | ||||
|       meta: { | ||||
|         title: '关于', | ||||
|         i18nTitle: 'routes.about', | ||||
|         requiresAuth: true, | ||||
|         keepAlive: true, | ||||
|         singleLayout: 'basic', | ||||
|         icon: 'fluent:book-information-24-regular', | ||||
|         order: 10 | ||||
| @@ -523,7 +570,8 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           meta: { | ||||
|             title: '分析页', | ||||
|             requiresAuth: true, | ||||
|             icon: 'icon-park-outline:analysis' | ||||
|             icon: 'icon-park-outline:analysis', | ||||
|             i18nTitle: 'routes.dashboard.analysis' | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
| @@ -533,14 +581,16 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           meta: { | ||||
|             title: '工作台', | ||||
|             requiresAuth: true, | ||||
|             icon: 'icon-park-outline:workbench' | ||||
|             icon: 'icon-park-outline:workbench', | ||||
|             i18nTitle: 'routes.dashboard.workbench' | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '仪表盘', | ||||
|         icon: 'mdi:monitor-dashboard', | ||||
|         order: 1 | ||||
|         order: 1, | ||||
|         i18nTitle: 'routes.dashboard._value' | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
| @@ -554,6 +604,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: 'vue文档', | ||||
|             i18nTitle: 'routes.document.vue', | ||||
|             requiresAuth: true, | ||||
|             icon: 'logos:vue' | ||||
|           } | ||||
| @@ -564,6 +615,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: 'vite文档', | ||||
|             i18nTitle: 'routes.document.vite', | ||||
|             requiresAuth: true, | ||||
|             icon: 'logos:vitejs' | ||||
|           } | ||||
| @@ -574,6 +626,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: 'naive文档', | ||||
|             i18nTitle: 'routes.document.naive', | ||||
|             requiresAuth: true, | ||||
|             icon: 'logos:naiveui' | ||||
|           } | ||||
| @@ -584,6 +637,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '项目文档', | ||||
|             i18nTitle: 'routes.document.project', | ||||
|             requiresAuth: true, | ||||
|             localIcon: 'logo' | ||||
|           } | ||||
| @@ -593,14 +647,16 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           path: '/document/project-link', | ||||
|           meta: { | ||||
|             title: '项目文档(外链)', | ||||
|             i18nTitle: 'routes.document.project-link', | ||||
|             requiresAuth: true, | ||||
|             localIcon: 'logo', | ||||
|             href: 'https://docs.soybean.pro/' | ||||
|             href: 'https://admin-docs.soybeanjs.cn/' | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '文档', | ||||
|         i18nTitle: 'routes.document._value', | ||||
|         icon: 'mdi:file-document-multiple-outline', | ||||
|         order: 2 | ||||
|       } | ||||
| @@ -616,6 +672,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '按钮', | ||||
|             i18nTitle: 'routes.component.button', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:button-cursor' | ||||
|           } | ||||
| @@ -626,6 +683,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '卡片', | ||||
|             i18nTitle: 'routes.component.card', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:card-outline' | ||||
|           } | ||||
| @@ -636,6 +694,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '表格', | ||||
|             i18nTitle: 'routes.component.table', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:table-large' | ||||
|           } | ||||
| @@ -643,6 +702,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '组件示例', | ||||
|         i18nTitle: 'routes.component._value', | ||||
|         icon: 'cib:app-store', | ||||
|         order: 3 | ||||
|       } | ||||
| @@ -663,6 +723,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               component: 'self', | ||||
|               meta: { | ||||
|                 title: 'ECharts', | ||||
|                 i18nTitle: 'routes.plugin.charts.echarts', | ||||
|                 requiresAuth: true, | ||||
|                 icon: 'simple-icons:apacheecharts' | ||||
|               } | ||||
| @@ -673,6 +734,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               component: 'self', | ||||
|               meta: { | ||||
|                 title: 'AntV', | ||||
|                 i18nTitle: 'routes.plugin.charts.antv', | ||||
|                 requiresAuth: true, | ||||
|                 icon: 'simple-icons:antdesign' | ||||
|               } | ||||
| @@ -680,6 +742,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           ], | ||||
|           meta: { | ||||
|             title: '图表', | ||||
|             i18nTitle: 'routes.plugin.charts._value', | ||||
|             icon: 'mdi:chart-areaspline' | ||||
|           } | ||||
|         }, | ||||
| @@ -689,6 +752,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '地图', | ||||
|             i18nTitle: 'routes.plugin.map', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:map' | ||||
|           } | ||||
| @@ -699,6 +763,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '视频', | ||||
|             i18nTitle: 'routes.plugin.video', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:video' | ||||
|           } | ||||
| @@ -714,6 +779,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               component: 'self', | ||||
|               meta: { | ||||
|                 title: '富文本编辑器', | ||||
|                 i18nTitle: 'routes.plugin.editor.quill', | ||||
|                 requiresAuth: true, | ||||
|                 icon: 'mdi:file-document-edit-outline' | ||||
|               } | ||||
| @@ -724,6 +790,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               component: 'self', | ||||
|               meta: { | ||||
|                 title: 'markdown编辑器', | ||||
|                 i18nTitle: 'routes.plugin.editor.markdown', | ||||
|                 requiresAuth: true, | ||||
|                 icon: 'ri:markdown-line' | ||||
|               } | ||||
| @@ -731,6 +798,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           ], | ||||
|           meta: { | ||||
|             title: '编辑器', | ||||
|             i18nTitle: 'routes.plugin.editor._value', | ||||
|             icon: 'icon-park-outline:editor' | ||||
|           } | ||||
|         }, | ||||
| @@ -740,6 +808,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: 'Swiper插件', | ||||
|             i18nTitle: 'routes.plugin.swiper', | ||||
|             requiresAuth: true, | ||||
|             icon: 'simple-icons:swiper' | ||||
|           } | ||||
| @@ -750,6 +819,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '剪贴板', | ||||
|             i18nTitle: 'routes.plugin.copy', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:clipboard-outline' | ||||
|           } | ||||
| @@ -760,6 +830,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '图标', | ||||
|             i18nTitle: 'routes.plugin.icon', | ||||
|             requiresAuth: true, | ||||
|             localIcon: 'custom-icon' | ||||
|           } | ||||
| @@ -770,6 +841,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '打印', | ||||
|             i18nTitle: 'routes.plugin.print', | ||||
|             requiresAuth: true, | ||||
|             icon: 'mdi:printer' | ||||
|           } | ||||
| @@ -777,6 +849,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '插件示例', | ||||
|         i18nTitle: 'routes.plugin._value', | ||||
|         icon: 'clarity:plugin-line', | ||||
|         order: 4 | ||||
|       } | ||||
| @@ -792,13 +865,26 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '权限切换', | ||||
|             i18nTitle: 'routes.auth-demo.permission', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:round-construction' | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           name: 'auth-demo_super', | ||||
|           path: '/auth-demo/super', | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '超级管理员可见', | ||||
|             i18nTitle: 'routes.auth-demo.super', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:round-supervisor-account' | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '权限示例', | ||||
|         i18nTitle: 'routes.auth-demo._value', | ||||
|         icon: 'ic:baseline-security', | ||||
|         order: 5 | ||||
|       } | ||||
| @@ -814,6 +900,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: 'Tab', | ||||
|             i18nTitle: 'routes.function.tab', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:round-tab' | ||||
|           } | ||||
| @@ -846,6 +933,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '功能', | ||||
|         i18nTitle: 'routes.function._value', | ||||
|         icon: 'icon-park-outline:all-application', | ||||
|         order: 6 | ||||
|       } | ||||
| @@ -861,6 +949,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '异常页403', | ||||
|             i18nTitle: 'routes.exception.403', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:baseline-block' | ||||
|           } | ||||
| @@ -871,6 +960,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '异常页404', | ||||
|             i18nTitle: 'routes.exception.404', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:baseline-web-asset-off' | ||||
|           } | ||||
| @@ -881,12 +971,14 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '异常页500', | ||||
|             i18nTitle: 'routes.exception.500', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:baseline-wifi-off' | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       meta: { | ||||
|         i18nTitle: 'routes.exception._value', | ||||
|         title: '异常页', | ||||
|         icon: 'ant-design:exception-outlined', | ||||
|         order: 7 | ||||
| @@ -908,6 +1000,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               component: 'self', | ||||
|               meta: { | ||||
|                 title: '二级菜单', | ||||
|                 i18nTitle: 'routes.multi-menu.first.second', | ||||
|                 requiresAuth: true, | ||||
|                 icon: 'mdi:menu' | ||||
|               } | ||||
| @@ -923,6 +1016,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|                   component: 'self', | ||||
|                   meta: { | ||||
|                     title: '三级菜单', | ||||
|                     i18nTitle: 'routes.multi-menu.first.second-new.third', | ||||
|                     requiresAuth: true, | ||||
|                     icon: 'mdi:menu' | ||||
|                   } | ||||
| @@ -930,18 +1024,21 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               ], | ||||
|               meta: { | ||||
|                 title: '二级菜单(有子菜单)', | ||||
|                 i18nTitle: 'routes.multi-menu.first.second-new._value', | ||||
|                 icon: 'mdi:menu' | ||||
|               } | ||||
|             } | ||||
|           ], | ||||
|           meta: { | ||||
|             title: '一级菜单', | ||||
|             i18nTitle: 'routes.multi-menu.first._value', | ||||
|             icon: 'mdi:menu' | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '多级菜单', | ||||
|         i18nTitle: 'routes.multi-menu._value', | ||||
|         icon: 'carbon:menu', | ||||
|         order: 8 | ||||
|       } | ||||
| @@ -957,6 +1054,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '权限管理', | ||||
|             i18nTitle: 'routes.management.auth', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:baseline-security' | ||||
|           } | ||||
| @@ -967,6 +1065,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '角色管理', | ||||
|             i18nTitle: 'routes.management.role', | ||||
|             requiresAuth: true, | ||||
|             icon: 'carbon:user-role' | ||||
|           } | ||||
| @@ -977,6 +1076,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '用户管理', | ||||
|             i18nTitle: 'routes.management.user', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:round-manage-accounts' | ||||
|           } | ||||
| @@ -987,6 +1087,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '路由管理', | ||||
|             i18nTitle: 'routes.management.route', | ||||
|             requiresAuth: true, | ||||
|             icon: 'material-symbols:route' | ||||
|           } | ||||
| @@ -994,6 +1095,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '系统管理', | ||||
|         i18nTitle: 'routes.management._value', | ||||
|         icon: 'carbon:cloud-service-management', | ||||
|         order: 9 | ||||
|       } | ||||
| @@ -1004,7 +1106,9 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|       component: 'self', | ||||
|       meta: { | ||||
|         title: '关于', | ||||
|         i18nTitle: 'routes.about', | ||||
|         requiresAuth: true, | ||||
|         keepAlive: true, | ||||
|         singleLayout: 'basic', | ||||
|         icon: 'fluent:book-information-24-regular', | ||||
|         order: 10 | ||||
| @@ -1024,14 +1128,27 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           meta: { | ||||
|             title: '分析页', | ||||
|             requiresAuth: true, | ||||
|             icon: 'icon-park-outline:analysis' | ||||
|             icon: 'icon-park-outline:analysis', | ||||
|             i18nTitle: 'routes.dashboard.analysis' | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           name: 'dashboard_workbench', | ||||
|           path: '/dashboard/workbench', | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '工作台', | ||||
|             requiresAuth: true, | ||||
|             icon: 'icon-park-outline:workbench', | ||||
|             i18nTitle: 'routes.dashboard.workbench' | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '仪表盘', | ||||
|         icon: 'mdi:monitor-dashboard', | ||||
|         order: 1 | ||||
|         order: 1, | ||||
|         i18nTitle: 'routes.dashboard._value' | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
| @@ -1045,13 +1162,26 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '权限切换', | ||||
|             i18nTitle: 'routes.auth-demo.permission', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:round-construction' | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           name: 'auth-demo_super', | ||||
|           path: '/auth-demo/super', | ||||
|           component: 'self', | ||||
|           meta: { | ||||
|             title: '超级管理员可见', | ||||
|             i18nTitle: 'routes.auth-demo.super', | ||||
|             requiresAuth: true, | ||||
|             icon: 'ic:round-supervisor-account' | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '权限示例', | ||||
|         i18nTitle: 'routes.auth-demo._value', | ||||
|         icon: 'ic:baseline-security', | ||||
|         order: 5 | ||||
|       } | ||||
| @@ -1072,6 +1202,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               component: 'self', | ||||
|               meta: { | ||||
|                 title: '二级菜单', | ||||
|                 i18nTitle: 'routes.multi-menu.first.second', | ||||
|                 requiresAuth: true, | ||||
|                 icon: 'mdi:menu' | ||||
|               } | ||||
| @@ -1087,6 +1218,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|                   component: 'self', | ||||
|                   meta: { | ||||
|                     title: '三级菜单', | ||||
|                     i18nTitle: 'routes.multi-menu.first.second-new.third', | ||||
|                     requiresAuth: true, | ||||
|                     icon: 'mdi:menu' | ||||
|                   } | ||||
| @@ -1094,20 +1226,23 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|               ], | ||||
|               meta: { | ||||
|                 title: '二级菜单(有子菜单)', | ||||
|                 i18nTitle: 'routes.multi-menu.first.second-new._value', | ||||
|                 icon: 'mdi:menu' | ||||
|               } | ||||
|             } | ||||
|           ], | ||||
|           meta: { | ||||
|             title: '一级菜单', | ||||
|             i18nTitle: 'routes.multi-menu.first._value', | ||||
|             icon: 'mdi:menu' | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       meta: { | ||||
|         title: '多级菜单', | ||||
|         i18nTitle: 'routes.multi-menu._value', | ||||
|         icon: 'carbon:menu', | ||||
|         order: 7 | ||||
|         order: 8 | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
| @@ -1116,10 +1251,12 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = { | ||||
|       component: 'self', | ||||
|       meta: { | ||||
|         title: '关于', | ||||
|         i18nTitle: 'routes.about', | ||||
|         requiresAuth: true, | ||||
|         keepAlive: true, | ||||
|         singleLayout: 'basic', | ||||
|         icon: 'fluent:book-information-24-regular', | ||||
|         order: 8 | ||||
|         order: 10 | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
|   | ||||
							
								
								
									
										152
									
								
								package.json
									
									
									
									
									
								
							
							
						
						| @@ -1,11 +1,11 @@ | ||||
| { | ||||
|   "name": "soybean-admin", | ||||
|   "version": "0.9.8", | ||||
|   "version": "0.10.4", | ||||
|   "description": "A fresh and elegant admin template, based on Vue3、Vite3、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite3、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。", | ||||
|   "author": { | ||||
|     "name": "Soybean", | ||||
|     "email": "honghuangdc@gmail.com", | ||||
|     "url": "https://github.com/honghuangdc" | ||||
|     "email": "soybeanjs@outlook.com", | ||||
|     "url": "https://github.com/soybeanjs" | ||||
|   }, | ||||
|   "license": "MIT", | ||||
|   "homepage": "https://github.com/honghuangdc/soybean-admin", | ||||
| @@ -38,91 +38,91 @@ | ||||
|     "dev": "cross-env VITE_SERVICE_ENV=dev vite", | ||||
|     "dev:test": "cross-env VITE_SERVICE_ENV=test vite", | ||||
|     "dev:prod": "cross-env VITE_SERVICE_ENV=prod vite", | ||||
|     "dev:tauri": "pnpm tauri dev", | ||||
|     "build": "npm run typecheck && cross-env VITE_SERVICE_ENV=prod vite build", | ||||
|     "build:dev": "npm run typecheck && cross-env VITE_SERVICE_ENV=dev vite build", | ||||
|     "build:test": "npm run typecheck && cross-env VITE_SERVICE_ENV=test vite build", | ||||
|     "build:vercel": "cross-env VITE_HASH_ROUTE=Y VITE_VERCEL=Y vite build", | ||||
|     "build:tauri": "pnpm tauri build", | ||||
|     "tauri-icon": "pnpm tauri icon ./public/logo.png", | ||||
|     "preview": "vite preview", | ||||
|     "typecheck": "vue-tsc --noEmit --skipLibCheck", | ||||
|     "lint": "eslint . --fix", | ||||
|     "format": "soy prettier-write", | ||||
|     "commit": "soy git-commit", | ||||
|     "cleanup": "soy cleanup", | ||||
|     "update-pkg": "soy update-pkg", | ||||
|     "update-pkg": "soy ncu", | ||||
|     "release": "soy release", | ||||
|     "tsx": "tsx", | ||||
|     "logo": "tsx ./scripts/logo.ts", | ||||
|     "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", | ||||
|     "release": "standard-version", | ||||
|     "prepare": "simple-git-hooks" | ||||
|     "prepare": "soy init-simple-git-hooks" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@antv/data-set": "^0.11.8", | ||||
|     "@antv/g2": "^4.2.8", | ||||
|     "@better-scroll/core": "^2.5.0", | ||||
|     "@soybeanjs/vue-admin-layout": "^1.1.1", | ||||
|     "@soybeanjs/vue-admin-tab": "^1.0.5", | ||||
|     "@vueuse/core": "^9.10.0", | ||||
|     "axios": "0.27.2", | ||||
|     "clipboard": "^2.0.11", | ||||
|     "colord": "^2.9.3", | ||||
|     "crypto-js": "^4.1.1", | ||||
|     "dayjs": "^1.11.7", | ||||
|     "echarts": "^5.4.1", | ||||
|     "form-data": "^4.0.0", | ||||
|     "lodash-es": "^4.17.21", | ||||
|     "naive-ui": "2.34.3", | ||||
|     "pinia": "^2.0.28", | ||||
|     "print-js": "^1.6.0", | ||||
|     "qs": "^6.11.0", | ||||
|     "swiper": "^8.4.5", | ||||
|     "ua-parser-js": "^1.0.32", | ||||
|     "vditor": "^3.9.0", | ||||
|     "vue": "3.2.45", | ||||
|     "vue-i18n": "^9.2.2", | ||||
|     "vue-router": "^4.1.6", | ||||
|     "vuedraggable": "^4.1.0", | ||||
|     "wangeditor": "^4.7.15", | ||||
|     "xgplayer": "^2.32.2" | ||||
|     "@antv/data-set": "0.11.8", | ||||
|     "@antv/g2": "4.2.10", | ||||
|     "@better-scroll/core": "2.5.1", | ||||
|     "@soybeanjs/vue-materials": "0.2.0", | ||||
|     "@vueuse/core": "10.4.1", | ||||
|     "axios": "1.5.0", | ||||
|     "clipboard": "2.0.11", | ||||
|     "colord": "2.9.3", | ||||
|     "crypto-js": "4.1.1", | ||||
|     "dayjs": "1.11.10", | ||||
|     "echarts": "5.4.3", | ||||
|     "form-data": "4.0.0", | ||||
|     "lodash-es": "4.17.21", | ||||
|     "naive-ui": "2.34.4", | ||||
|     "pinia": "2.1.6", | ||||
|     "print-js": "1.6.0", | ||||
|     "qs": "6.11.2", | ||||
|     "socket.io-client": "4.7.2", | ||||
|     "swiper": "10.2.0", | ||||
|     "ua-parser-js": "1.0.36", | ||||
|     "vditor": "3.9.5", | ||||
|     "vue": "3.3.4", | ||||
|     "vue-i18n": "9.4.1", | ||||
|     "vue-router": "4.2.4", | ||||
|     "vuedraggable": "4.1.0", | ||||
|     "wangeditor": "4.7.15", | ||||
|     "xgplayer": "3.0.9" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@amap/amap-jsapi-types": "^0.0.10", | ||||
|     "@iconify/json": "^2.2.7", | ||||
|     "@iconify/vue": "^4.0.2", | ||||
|     "@soybeanjs/cli": "^0.1.6", | ||||
|     "@soybeanjs/vite-plugin-vue-page-route": "^0.0.5", | ||||
|     "@types/bmapgl": "^0.0.5", | ||||
|     "@types/crypto-js": "^4.1.1", | ||||
|     "@types/node": "18.11.18", | ||||
|     "@types/qs": "^6.9.7", | ||||
|     "@types/ua-parser-js": "^0.7.36", | ||||
|     "@unocss/preset-uno": "^0.48.4", | ||||
|     "@unocss/vite": "^0.48.4", | ||||
|     "@vitejs/plugin-vue": "^4.0.0", | ||||
|     "@vitejs/plugin-vue-jsx": "^3.0.0", | ||||
|     "conventional-changelog": "^3.1.25", | ||||
|     "cross-env": "^7.0.3", | ||||
|     "eslint": "^8.32.0", | ||||
|     "eslint-config-soybeanjs-vue": "^0.2.2", | ||||
|     "lint-staged": "12.5.0", | ||||
|     "mockjs": "^1.1.0", | ||||
|     "node-html-to-image": "^3.2.4", | ||||
|     "rollup-plugin-visualizer": "^5.9.0", | ||||
|     "sass": "^1.57.1", | ||||
|     "simple-git-hooks": "^2.8.1", | ||||
|     "standard-version": "^9.5.0", | ||||
|     "tsx": "^3.12.2", | ||||
|     "typescript": "4.9.4", | ||||
|     "unplugin-icons": "^0.15.1", | ||||
|     "unplugin-vue-components": "0.22.12", | ||||
|     "unplugin-vue-macros": "^1.3.3", | ||||
|     "utility-types": "^3.10.0", | ||||
|     "vite": "^4.0.4", | ||||
|     "vite-plugin-compression": "^0.5.1", | ||||
|     "vite-plugin-html": "^3.2.0", | ||||
|     "vite-plugin-mock": "^2.9.6", | ||||
|     "vite-plugin-progress": "^0.0.6", | ||||
|     "vite-plugin-pwa": "^0.14.1", | ||||
|     "vite-plugin-svg-icons": "^2.0.1", | ||||
|     "vue-tsc": "^1.0.24" | ||||
|     "@amap/amap-jsapi-types": "0.0.13", | ||||
|     "@iconify/json": "2.2.118", | ||||
|     "@iconify/vue": "4.1.1", | ||||
|     "@plugin-web-update-notification/vite": "^1.6.5", | ||||
|     "@soybeanjs/cli": "0.7.1", | ||||
|     "@soybeanjs/vite-plugin-vue-page-route": "0.0.10", | ||||
|     "@tauri-apps/cli": "^1.3.1", | ||||
|     "@types/bmapgl": "0.0.7", | ||||
|     "@types/crypto-js": "4.1.2", | ||||
|     "@types/node": "20.6.3", | ||||
|     "@types/qs": "6.9.8", | ||||
|     "@types/ua-parser-js": "0.7.37", | ||||
|     "@unocss/preset-uno": "0.56.0", | ||||
|     "@unocss/transformer-directives": "0.56.0", | ||||
|     "@unocss/vite": "0.56.0", | ||||
|     "@vitejs/plugin-vue": "4.3.4", | ||||
|     "@vitejs/plugin-vue-jsx": "3.0.2", | ||||
|     "cross-env": "7.0.3", | ||||
|     "eslint": "8.49.0", | ||||
|     "eslint-config-soybeanjs": "0.5.6", | ||||
|     "mockjs": "1.1.0", | ||||
|     "rollup-plugin-visualizer": "5.9.2", | ||||
|     "sass": "1.67.0", | ||||
|     "simple-git-hooks": "2.9.0", | ||||
|     "tsx": "3.12.10", | ||||
|     "typescript": "5.2.2", | ||||
|     "unplugin-icons": "0.17.0", | ||||
|     "unplugin-vue-components": "0.25.2", | ||||
|     "vite": "4.4.9", | ||||
|     "vite-plugin-compression": "0.5.1", | ||||
|     "vite-plugin-mock": "2.9.8", | ||||
|     "vite-plugin-progress": "0.0.7", | ||||
|     "vite-plugin-pwa": "0.16.5", | ||||
|     "vite-plugin-svg-icons": "2.0.1", | ||||
|     "vite-plugin-vue-devtools": "1.0.0-rc.4", | ||||
|     "vue-tsc": "1.8.13" | ||||
|   }, | ||||
|   "pnpm": { | ||||
|     "patchedDependencies": { | ||||
| @@ -131,9 +131,9 @@ | ||||
|   }, | ||||
|   "simple-git-hooks": { | ||||
|     "commit-msg": "pnpm soy git-commit-verify", | ||||
|     "pre-commit": "pnpm typecheck && pnpm lint-staged" | ||||
|     "pre-commit": "pnpm typecheck && pnpm lint" | ||||
|   }, | ||||
|   "lint-staged": { | ||||
|     "*": "eslint . --fix" | ||||
|   "soybean": { | ||||
|     "useSoybeanToken": true | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										11505
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| <svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg"><path d="M81.28 55.9c-.1-11.67-2.93-22.55-9.37-32.38-1-1.5-2.14-2.86-2.5-4.71a8.1 8.1 0 014-8.61 7.89 7.89 0 019.3 1.23 35.999 35.999 0 015.9 8.83 75.18 75.18 0 018.44 28.58 83.211 83.211 0 01-5.23 36.74 102.983 102.983 0 01-3 7.28 1.2 1.2 0 000 1.41c9.58 13.3 21.76 23 37.85 27.24a54.37 54.37 0 0019.68 1.57 7.72 7.72 0 018.36 6.9 7.903 7.903 0 01-6.7 9 64.744 64.744 0 01-23-1.33 77.68 77.68 0 01-36.93-19.88 93.628 93.628 0 01-11.91-13.71 2.18 2.18 0 00-2.3-1.06 72.744 72.744 0 00-27.38 7.55c-11.6 6-20.67 14.58-26.4 26.45a10.134 10.134 0 01-3.7 4.7 8 8 0 01-9.19-.7 7.86 7.86 0 01-2.36-9.28 60.324 60.324 0 018.72-14.52c12.2-15.43 28.21-24.59 47.32-28.57A85.085 85.085 0 0173.07 87c.524.015 1-.307 1.18-.8a76.06 76.06 0 006.53-22.3c.351-2.652.518-5.325.5-8z" fill="#646cff"/><path d="M136.26 108.34a44.742 44.742 0 01-11.13-2.87 46.108 46.108 0 01-19.66-13.76 8 8 0 015.72-13.22 7.93 7.93 0 016.54 2.93 33.27 33.27 0 0018.87 10.75c1.546.155 3.058.553 4.48 1.18a8.08 8.08 0 013.84 9.21c-.92 3.52-4.13 5.81-8.66 5.78zm-80.6-75.02a7.61 7.61 0 016.64 5 49.139 49.139 0 013.64 17 46.33 46.33 0 01-2.46 17.28c-2 5.77-8.24 7.79-12.89 4.15a8.1 8.1 0 01-2.39-9 31.679 31.679 0 001.68-12.36 35.77 35.77 0 00-2.43-11c-2.1-5.45 1.75-11.07 8.21-11.07zm22.26 93.25a8 8 0 01-6.68 7.86 32.88 32.88 0 00-19.7 12.19 8.13 8.13 0 01-11.21 1.62 8 8 0 01-1.41-11.58A51.043 51.043 0 0154 123.81a45.842 45.842 0 0114-5.1c5.35-1.04 9.91 2.56 9.92 7.86z" fill="#646cff"/></svg> | ||||
| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/logo.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 23 KiB | 
| @@ -1,17 +1,12 @@ | ||||
| import { readFile } from 'fs/promises'; | ||||
| import nodeHtmlToImage from 'node-html-to-image'; | ||||
| import { readFile, writeFile } from 'fs/promises'; | ||||
| import themeSettings from '../src/settings/theme.json'; | ||||
|  | ||||
| async function generatePngLogoFromSvg(svgPath: string, color: string) { | ||||
| async function updateFavicon(svgPath: string, color: string) { | ||||
|   const svgStr = await readFile(svgPath, 'utf-8'); | ||||
|  | ||||
|   const svgStrWithColor = svgStr.replace(/currentColor/g, color); | ||||
|  | ||||
|   await nodeHtmlToImage({ | ||||
|     output: './public/logo.png', | ||||
|     html: svgStrWithColor, | ||||
|     transparent: true | ||||
|   }); | ||||
|   await writeFile('./public/favicon.svg', svgStrWithColor); | ||||
| } | ||||
|  | ||||
| generatePngLogoFromSvg('./src/assets/svg-icon/logo.svg', themeSettings.themeColor); | ||||
| updateFavicon('./src/assets/svg-icon/logo.svg', themeSettings.themeColor); | ||||
|   | ||||
							
								
								
									
										3
									
								
								src-tauri/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | ||||
| # Generated by Cargo | ||||
| # will have compiled files and executables | ||||
| /target/ | ||||
							
								
								
									
										4302
									
								
								src-tauri/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										28
									
								
								src-tauri/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,28 @@ | ||||
| [package] | ||||
| name = "app" | ||||
| version = "0.1.0" | ||||
| description = "A Tauri App" | ||||
| authors = ["you"] | ||||
| license = "" | ||||
| repository = "" | ||||
| default-run = "app" | ||||
| edition = "2021" | ||||
| rust-version = "1.57" | ||||
|  | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
|  | ||||
| [build-dependencies] | ||||
| tauri-build = { version = "1.1.1", features = [] } | ||||
|  | ||||
| [dependencies] | ||||
| serde_json = "1.0" | ||||
| serde = { version = "1.0", features = ["derive"] } | ||||
| tauri = { version = "1.1.1", features = ["api-all"] } | ||||
|  | ||||
| [features] | ||||
| # by default Tauri runs in production mode | ||||
| # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL | ||||
| default = [ "custom-protocol" ] | ||||
| # this feature is used for production builds where `devPath` points to the filesystem | ||||
| # DO NOT remove this | ||||
| custom-protocol = [ "tauri/custom-protocol" ] | ||||
							
								
								
									
										3
									
								
								src-tauri/build.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | ||||
| fn main() { | ||||
|   tauri_build::build() | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/128x128.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 9.0 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/128x128@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 20 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/32x32.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/Square107x107Logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 7.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/Square142x142Logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 10 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/Square150x150Logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/Square284x284Logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 22 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/Square30x30Logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/Square310x310Logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 24 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/Square44x44Logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/Square71x71Logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/Square89x89Logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 6.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/StoreLogo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/icon.icns
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/icon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 32 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src-tauri/icons/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 42 KiB | 
							
								
								
									
										10
									
								
								src-tauri/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,10 @@ | ||||
| #![cfg_attr( | ||||
|   all(not(debug_assertions), target_os = "windows"), | ||||
|   windows_subsystem = "windows" | ||||
| )] | ||||
|  | ||||
| fn main() { | ||||
|   tauri::Builder::default() | ||||
|     .run(tauri::generate_context!()) | ||||
|     .expect("error while running tauri application"); | ||||
| } | ||||
							
								
								
									
										60
									
								
								src-tauri/tauri.conf.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,60 @@ | ||||
| { | ||||
|   "$schema": "../node_modules/@tauri-apps/cli/schema.json", | ||||
|   "build": { | ||||
|     "beforeBuildCommand": "npm run build", | ||||
|     "beforeDevCommand": "npm run dev", | ||||
|     "devPath": "http://localhost:3200", | ||||
|     "distDir": "../dist" | ||||
|   }, | ||||
|   "package": { | ||||
|     "productName": "soybean-admin", | ||||
|     "version": "0.10.4" | ||||
|   }, | ||||
|   "tauri": { | ||||
|     "allowlist": { | ||||
|       "all": true | ||||
|     }, | ||||
|     "bundle": { | ||||
|       "active": true, | ||||
|       "category": "DeveloperTool", | ||||
|       "copyright": "", | ||||
|       "deb": { | ||||
|         "depends": [] | ||||
|       }, | ||||
|       "externalBin": [], | ||||
|       "icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"], | ||||
|       "identifier": "cn.soybeanjs.tauri-admin", | ||||
|       "longDescription": "", | ||||
|       "macOS": { | ||||
|         "entitlements": null, | ||||
|         "exceptionDomain": "", | ||||
|         "frameworks": [], | ||||
|         "providerShortName": null, | ||||
|         "signingIdentity": null | ||||
|       }, | ||||
|       "resources": [], | ||||
|       "shortDescription": "", | ||||
|       "targets": "all", | ||||
|       "windows": { | ||||
|         "certificateThumbprint": null, | ||||
|         "digestAlgorithm": "sha256", | ||||
|         "timestampUrl": "" | ||||
|       } | ||||
|     }, | ||||
|     "security": { | ||||
|       "csp": null | ||||
|     }, | ||||
|     "updater": { | ||||
|       "active": false | ||||
|     }, | ||||
|     "windows": [ | ||||
|       { | ||||
|         "fullscreen": false, | ||||
|         "height": 800, | ||||
|         "resizable": true, | ||||
|         "title": "soybean-admin", | ||||
|         "width": 1000 | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| } | ||||
| @@ -1 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-activity"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg> | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 202 B | 
| @@ -1 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-at-sign"><circle cx="12" cy="12" r="4"></circle><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94"></path></svg> | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="prefix__prefix__feather prefix__prefix__feather-at-sign"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 006 0v-1a10 10 0 10-3.92 7.94"/></svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 322 B After Width: | Height: | Size: 315 B | 
| Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 6.3 KiB | 
| @@ -1 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-cast"><path d="M2 16.1A5 5 0 0 1 5.9 20M2 12.05A9 9 0 0 1 9.95 20M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6"></path><line x1="2" y1="20" x2="2.01" y2="20"></line></svg> | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="prefix__prefix__feather prefix__prefix__feather-cast"><path d="M2 16.1A5 5 0 015.9 20M2 12.05A9 9 0 019.95 20M2 8V6a2 2 0 012-2h16a2 2 0 012 2v12a2 2 0 01-2 2h-6M2 20h.01"/></svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 387 B After Width: | Height: | Size: 345 B | 
| @@ -1 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chrome"><circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="4"></circle><line x1="21.17" y1="8" x2="12" y2="8"></line><line x1="3.95" y1="6.06" x2="8.54" y2="14"></line><line x1="10.88" y1="21.94" x2="15.46" y2="14"></line></svg> | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="4"/><path d="M21.17 8H12M3.95 6.06L8.54 14m2.34 7.94L15.46 14"/></svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 448 B After Width: | Height: | Size: 288 B | 
| @@ -1 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-copy"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 351 B After Width: | Height: | Size: 283 B | 
| @@ -1 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--mdi" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path fill="currentColor" d="M19 10c0 1.38-2.12 2.5-3.5 2.5s-2.75-1.12-2.75-2.5h-1.5c0 1.38-1.37 2.5-2.75 2.5S5 11.38 5 10h-.75c-.16.64-.25 1.31-.25 2a8 8 0 0 0 8 8a8 8 0 0 0 8-8c0-.69-.09-1.36-.25-2H19m-7-6C9.04 4 6.45 5.61 5.07 8h13.86C17.55 5.61 14.96 4 12 4m10 8a10 10 0 0 1-10 10A10 10 0 0 1 2 12A10 10 0 0 1 12 2a10 10 0 0 1 10 10m-10 5.23c-1.75 0-3.29-.73-4.19-1.81L9.23 14c.45.72 1.52 1.23 2.77 1.23s2.32-.51 2.77-1.23l1.42 1.42c-.9 1.08-2.44 1.81-4.19 1.81Z"></path></svg> | ||||
| <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M19 10c0 1.38-2.12 2.5-3.5 2.5s-2.75-1.12-2.75-2.5h-1.5c0 1.38-1.37 2.5-2.75 2.5S5 11.38 5 10h-.75c-.16.64-.25 1.31-.25 2a8 8 0 008 8 8 8 0 008-8c0-.69-.09-1.36-.25-2H19m-7-6C9.04 4 6.45 5.61 5.07 8h13.86C17.55 5.61 14.96 4 12 4m10 8a10 10 0 01-10 10A10 10 0 012 12 10 10 0 0112 2a10 10 0 0110 10m-10 5.23c-1.75 0-3.29-.73-4.19-1.81L9.23 14c.45.72 1.52 1.23 2.77 1.23s2.32-.51 2.77-1.23l1.42 1.42c-.9 1.08-2.44 1.81-4.19 1.81z"/></svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 544 B | 
| @@ -1 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-heart"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg> | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z"/></svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 371 B After Width: | Height: | Size: 309 B | 
| @@ -1 +1 @@ | ||||
| <svg viewBox="0 0 158.88 158.88"><path d="M158.86.3v157.48c0 .9-.2 1.1-1.1 1.1H.24a2.61 2.61 0 01-.11-1.3V1.67C.14 0 0 .18 1.61.18h156a2.62 2.62 0 011.25.12z" fill="currentColor"/><path d="M158.86.3H2C0 .31.27 0 .27 2v156.85c-.07-.05-.25.12-.24-.12s0-.64 0-1Q0 79.46 0 1.14C0 .24.2 0 1.1 0h156.68c.35.08.81-.2 1.08.3z" fill="#fff"/><path d="M93.65 51.52a68.65 68.65 0 01-6.47 28.81 1.72 1.72 0 00.19 2c6.08 8.28 13.58 14.79 23.19 18.69a46.22 46.22 0 0017.15 3.39 28.87 28.87 0 003.34-.25 6.2 6.2 0 017 5.12 6.07 6.07 0 01-5.15 7.14 50.39 50.39 0 01-18.06-1c-15.85-3.66-28-12.75-37.44-25.7a2.15 2.15 0 00-2.23-1.09c-14 1.37-26.17 6.43-35.5 17.21a38.47 38.47 0 00-6.23 9.74 6.21 6.21 0 01-8.17 3.42 6.14 6.14 0 01-3.27-8.2 49.31 49.31 0 019.63-14.62c10.56-11.44 23.8-17.54 39.09-19.54a13.93 13.93 0 012.84-.34c1.61.14 2.18-.73 2.73-2A54.38 54.38 0 0081.12 51a44 44 0 00-8-25 6.11 6.11 0 01-.65-6.46A6 6 0 0177.75 16a6.34 6.34 0 015.66 3 53.61 53.61 0 017.17 14.28 59.33 59.33 0 013.07 18.24z" fill="#fff"/><path d="M46.92 118.63a6 6 0 011.35-3.88 37.89 37.89 0 0122.5-14 6.08 6.08 0 016.65 2.47 6.18 6.18 0 01-3.84 9.63 26.09 26.09 0 00-15.71 9.77 6.2 6.2 0 01-10.95-4zM124.3 92.8a34.66 34.66 0 01-9.82-2.48 35.46 35.46 0 01-14.65-10.45 6.19 6.19 0 012.84-9.93 5.79 5.79 0 016.44 1.73 26.79 26.79 0 0016.51 8.85 6 6 0 015 5.54 6.21 6.21 0 01-4.29 6.46 6.55 6.55 0 01-2.03.28zM69.32 53.27a33.46 33.46 0 01-2.27 12.52 6.21 6.21 0 01-10.94 1 6.09 6.09 0 01-.65-5.4 26 26 0 00-.53-18.25 6.21 6.21 0 0111.49-4.72 40.24 40.24 0 012.9 14.85z" fill="#fff"/></svg> | ||||
| <svg xmlns="http://www.w3.org/2000/svg"><path d="M0 0h160v160H0V0z" fill="currentColor"/><path d="M94.322 51.888A69.12 69.12 0 0187.806 80.9a1.732 1.732 0 00.191 2.014c6.124 8.338 13.677 14.894 23.356 18.821a46.564 46.564 0 0017.273 3.414 29.101 29.101 0 003.364-.252 6.245 6.245 0 017.051 5.156 6.112 6.112 0 01-5.187 7.19 50.758 50.758 0 01-18.19-1.007c-15.964-3.686-28.2-12.84-37.709-25.88a2.165 2.165 0 00-2.246-1.098c-14.1 1.38-26.357 6.475-35.754 17.331a38.721 38.721 0 00-6.275 9.808 6.255 6.255 0 01-8.229 3.444 6.184 6.184 0 01-3.293-8.258 49.662 49.662 0 019.699-14.722c10.636-11.52 23.97-17.663 39.37-19.677a14.06 14.06 0 012.86-.342c1.622.14 2.197-.735 2.75-2.014a54.752 54.752 0 004.865-23.463 44.302 44.302 0 00-8.057-25.175 6.152 6.152 0 01-.655-6.506 6.043 6.043 0 015.318-3.564 6.386 6.386 0 015.7 3.02 53.98 53.98 0 017.222 14.38 59.734 59.734 0 013.092 18.368z" fill="#fff"/><path d="M47.257 119.468a6.04 6.04 0 011.36-3.907 38.165 38.165 0 0122.66-14.098 6.124 6.124 0 016.699 2.487 6.223 6.223 0 01-3.868 9.698 26.276 26.276 0 00-15.823 9.838 6.245 6.245 0 01-11.028-4.028v.01zm77.935-26.01a34.908 34.908 0 01-9.89-2.498 35.717 35.717 0 01-14.756-10.523 6.233 6.233 0 012.861-10 5.832 5.832 0 016.486 1.742 26.986 26.986 0 0016.628 8.912 6.042 6.042 0 015.036 5.58 6.253 6.253 0 01-4.32 6.504 6.588 6.588 0 01-2.045.282zM69.817 53.65a33.69 33.69 0 01-2.286 12.607 6.255 6.255 0 01-11.018 1.007 6.132 6.132 0 01-.655-5.438 26.178 26.178 0 00-.534-18.377 6.256 6.256 0 0111.572-4.753 40.515 40.515 0 012.921 14.954z" fill="#fff"/></svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB | 
| @@ -1 +1 @@ | ||||
| <svg viewBox="0 0 158.88 158.88"><path d="M0 158.86V1.1C0 .2.2 0 1.1 0h156.68c.9 0 1.1.2 1.1 1.1v156.68c0 .9-.2 1.1-1.1 1.1Q78.9 158.83 0 158.86z" fill="#ffffff00"/><path d="M81.28 55.9c-.1-11.67-2.93-22.55-9.37-32.38-1-1.5-2.14-2.86-2.5-4.71a8.1 8.1 0 014-8.61 7.89 7.89 0 019.3 1.23 36 36 0 015.9 8.83 75.18 75.18 0 018.44 28.58 83.21 83.21 0 01-5.23 36.74 103 103 0 01-3 7.28 1.2 1.2 0 000 1.41c9.58 13.3 21.76 23 37.85 27.24a54.35 54.35 0 0019.68 1.57 7.72 7.72 0 018.36 6.9 7.9 7.9 0 01-6.7 9 64.74 64.74 0 01-23-1.33 77.68 77.68 0 01-36.93-19.88 93.64 93.64 0 01-11.91-13.71 2.18 2.18 0 00-2.3-1.06 72.75 72.75 0 00-27.38 7.55c-11.6 6-20.67 14.58-26.4 26.45a10.13 10.13 0 01-3.7 4.7 8 8 0 01-9.19-.7 7.86 7.86 0 01-2.36-9.28 60.32 60.32 0 018.72-14.52c12.2-15.43 28.21-24.59 47.32-28.57A85.08 85.08 0 0173.07 87a1.22 1.22 0 001.18-.8 76.06 76.06 0 006.53-22.3 57.87 57.87 0 00.5-8z" fill="currentColor"/><path d="M136.26 108.34a44.72 44.72 0 01-11.13-2.87 46.11 46.11 0 01-19.66-13.76 8 8 0 015.72-13.22 7.93 7.93 0 016.54 2.93 33.27 33.27 0 0018.87 10.75 14.76 14.76 0 014.48 1.18 8.08 8.08 0 013.84 9.21c-.92 3.52-4.13 5.81-8.66 5.78zM55.66 33.32a7.61 7.61 0 016.64 5 49.14 49.14 0 013.64 17 46.33 46.33 0 01-2.46 17.28c-2 5.77-8.24 7.79-12.89 4.15a8.1 8.1 0 01-2.39-9 31.68 31.68 0 001.68-12.36 35.77 35.77 0 00-2.43-11c-2.1-5.45 1.75-11.07 8.21-11.07zM77.92 126.57a8 8 0 01-6.68 7.86 32.88 32.88 0 00-19.7 12.19 8.13 8.13 0 01-11.21 1.62 8 8 0 01-1.41-11.58A51.05 51.05 0 0154 123.81a45.85 45.85 0 0114-5.1c5.35-1.04 9.91 2.56 9.92 7.86z" fill="currentColor"/></svg> | ||||
| <svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg"><path d="M81.28 55.9c-.1-11.67-2.93-22.55-9.37-32.38-1-1.5-2.14-2.86-2.5-4.71a8.1 8.1 0 014-8.61 7.89 7.89 0 019.3 1.23 35.999 35.999 0 015.9 8.83 75.18 75.18 0 018.44 28.58 83.211 83.211 0 01-5.23 36.74 102.983 102.983 0 01-3 7.28 1.2 1.2 0 000 1.41c9.58 13.3 21.76 23 37.85 27.24a54.37 54.37 0 0019.68 1.57 7.72 7.72 0 018.36 6.9 7.903 7.903 0 01-6.7 9 64.744 64.744 0 01-23-1.33 77.68 77.68 0 01-36.93-19.88 93.628 93.628 0 01-11.91-13.71 2.18 2.18 0 00-2.3-1.06 72.744 72.744 0 00-27.38 7.55c-11.6 6-20.67 14.58-26.4 26.45a10.134 10.134 0 01-3.7 4.7 8 8 0 01-9.19-.7 7.86 7.86 0 01-2.36-9.28 60.324 60.324 0 018.72-14.52c12.2-15.43 28.21-24.59 47.32-28.57A85.085 85.085 0 0173.07 87c.524.015 1-.307 1.18-.8a76.06 76.06 0 006.53-22.3c.351-2.652.518-5.325.5-8z" fill="currentColor"/><path d="M136.26 108.34a44.742 44.742 0 01-11.13-2.87 46.108 46.108 0 01-19.66-13.76 8 8 0 015.72-13.22 7.93 7.93 0 016.54 2.93 33.27 33.27 0 0018.87 10.75c1.546.155 3.058.553 4.48 1.18a8.08 8.08 0 013.84 9.21c-.92 3.52-4.13 5.81-8.66 5.78zm-80.6-75.02a7.61 7.61 0 016.64 5 49.139 49.139 0 013.64 17 46.33 46.33 0 01-2.46 17.28c-2 5.77-8.24 7.79-12.89 4.15a8.1 8.1 0 01-2.39-9 31.679 31.679 0 001.68-12.36 35.77 35.77 0 00-2.43-11c-2.1-5.45 1.75-11.07 8.21-11.07zm22.26 93.25a8 8 0 01-6.68 7.86 32.88 32.88 0 00-19.7 12.19 8.13 8.13 0 01-11.21 1.62 8 8 0 01-1.41-11.58A51.043 51.043 0 0154 123.81a45.842 45.842 0 0114-5.1c5.35-1.04 9.91 2.56 9.92 7.86z" fill="currentColor"/></svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB | 
| Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.1 KiB | 
| @@ -54,7 +54,7 @@ const props = withDefaults(defineProps<Props>(), { | ||||
|   placeholderClass: 'bg-white dark:bg-dark transition-background-color duration-300 ease-in-out', | ||||
|   emptyDesc: '暂无数据', | ||||
|   iconClass: 'text-320px text-primary', | ||||
|   descClass: 'text-16px text-[#666]', | ||||
|   descClass: 'text-16px text-#666', | ||||
|   showNetworkReload: false | ||||
| }); | ||||
| 
 | ||||
| @@ -1,42 +0,0 @@ | ||||
| <template> | ||||
|   <div class="flex-center text-18px cursor-pointer" @click="handleSwitch"> | ||||
|     <icon-mdi-moon-waning-crescent v-if="darkMode" /> | ||||
|     <icon-mdi-white-balance-sunny v-else /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { computed } from 'vue'; | ||||
|  | ||||
| defineOptions({ name: 'DarkModeSwitch' }); | ||||
|  | ||||
| interface Props { | ||||
|   /** 暗黑模式 */ | ||||
|   dark?: boolean; | ||||
| } | ||||
|  | ||||
| const props = withDefaults(defineProps<Props>(), { | ||||
|   dark: false | ||||
| }); | ||||
|  | ||||
| interface Emits { | ||||
|   (e: 'update:dark', darkMode: boolean): void; | ||||
| } | ||||
|  | ||||
| const emit = defineEmits<Emits>(); | ||||
|  | ||||
| const darkMode = computed({ | ||||
|   get() { | ||||
|     return props.dark; | ||||
|   }, | ||||
|   set(newValue: boolean) { | ||||
|     emit('update:dark', newValue); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| function handleSwitch() { | ||||
|   darkMode.value = !darkMode.value; | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped></style> | ||||
| @@ -4,24 +4,23 @@ | ||||
|     <div class="w-56px h-56px my-36px"> | ||||
|       <div class="relative h-full animate-spin"> | ||||
|         <div | ||||
|           v-for="(item, index) in lodingClasses" | ||||
|           v-for="(item, index) in loadingClasses" | ||||
|           :key="index" | ||||
|           class="absolute w-16px h-16px bg-primary rounded-8px animate-pulse" | ||||
|           :class="item" | ||||
|         ></div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <h2 class="text-28px font-500 text-#646464">{{ title }}</h2> | ||||
|     <h2 class="text-28px font-500 text-#646464">{{ $t('system.title') }}</h2> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { useAppInfo } from '@/composables'; | ||||
| import { localStg } from '@/utils'; | ||||
| import { sessionStg, getRgbOfColor } from '@/utils'; | ||||
| import { $t } from '@/locales'; | ||||
| import themeSettings from '@/settings/theme.json'; | ||||
| 
 | ||||
| const { title } = useAppInfo(); | ||||
| 
 | ||||
| const lodingClasses = [ | ||||
| const loadingClasses = [ | ||||
|   'left-0 top-0', | ||||
|   'left-0 bottom-0 animate-delay-500', | ||||
|   'right-0 top-0 animate-delay-1000', | ||||
| @@ -29,9 +28,12 @@ const lodingClasses = [ | ||||
| ]; | ||||
| 
 | ||||
| function addThemeColorCssVars() { | ||||
|   const defaultColor = '#1890ff'; | ||||
|   const themeColor = localStg.get('themeColor') || defaultColor; | ||||
|   const cssVars = `--primary-color: ${themeColor}`; | ||||
|   const defaultColor = themeSettings.themeColor; | ||||
|   const themeColor = sessionStg.get('themeColor') || defaultColor; | ||||
| 
 | ||||
|   const { r, g, b } = getRgbOfColor(themeColor); | ||||
| 
 | ||||
|   const cssVars = `--primary-color: ${r},${g},${b}`; | ||||
|   document.documentElement.style.cssText = cssVars; | ||||
| } | ||||
| 
 | ||||
| @@ -1,7 +1,7 @@ | ||||
| <template> | ||||
|   <div | ||||
|     class="dark:bg-[#18181c] dark:text-white dark:text-opacity-82 transition-all duration-300 ease-in-out" | ||||
|     :class="inverted ? 'bg-[#001428] text-white' : 'bg-white text-[#333639]'" | ||||
|     class="dark:bg-dark dark:text-white dark:text-opacity-82 transition-all" | ||||
|     :class="inverted ? 'bg-#001428 text-white' : 'bg-white text-#333639'" | ||||
|   > | ||||
|     <slot></slot> | ||||
|   </div> | ||||
							
								
								
									
										89
									
								
								src/components/common/dark-mode-switch.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,89 @@ | ||||
| <template> | ||||
|   <div class="flex-center text-18px cursor-pointer" @click="handleSwitch"> | ||||
|     <icon-mdi-moon-waning-crescent v-if="darkMode" /> | ||||
|     <icon-mdi-white-balance-sunny v-else /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { computed } from 'vue'; | ||||
|  | ||||
| defineOptions({ name: 'DarkModeSwitch' }); | ||||
|  | ||||
| interface Props { | ||||
|   /** 暗黑模式 */ | ||||
|   dark?: boolean; | ||||
|   /** 自定义暗黑模式动画过渡 */ | ||||
|   customizeTransition?: boolean; | ||||
| } | ||||
|  | ||||
| const props = withDefaults(defineProps<Props>(), { | ||||
|   dark: false | ||||
| }); | ||||
|  | ||||
| interface Emits { | ||||
|   (e: 'update:dark', darkMode: boolean): void; | ||||
| } | ||||
|  | ||||
| const emit = defineEmits<Emits>(); | ||||
|  | ||||
| const darkMode = computed({ | ||||
|   get() { | ||||
|     return props.dark; | ||||
|   }, | ||||
|   set(newValue: boolean) { | ||||
|     emit('update:dark', newValue); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| async function handleSwitch(event: MouseEvent) { | ||||
|   const x = event.clientX; | ||||
|   const y = event.clientY; | ||||
|  | ||||
|   if (!props.customizeTransition || !document.startViewTransition) { | ||||
|     darkMode.value = !darkMode.value; | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y)); | ||||
|  | ||||
|   const transition = document.startViewTransition(() => { | ||||
|     darkMode.value = !darkMode.value; | ||||
|   }); | ||||
|  | ||||
|   await transition.ready; | ||||
|  | ||||
|   const clipPath = [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`]; | ||||
|  | ||||
|   document.documentElement.animate( | ||||
|     { | ||||
|       clipPath: darkMode.value ? clipPath : [...clipPath].reverse() | ||||
|     }, | ||||
|     { | ||||
|       duration: 300, | ||||
|       easing: 'ease-in', | ||||
|       pseudoElement: darkMode.value ? '::view-transition-new(root)' : '::view-transition-old(root)' | ||||
|     } | ||||
|   ); | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style> | ||||
| ::view-transition-old(root), | ||||
| ::view-transition-new(root) { | ||||
|   animation: none; | ||||
|   mix-blend-mode: normal; | ||||
| } | ||||
| ::view-transition-old(root) { | ||||
|   z-index: 9999; | ||||
| } | ||||
| ::view-transition-new(root) { | ||||
|   z-index: 1; | ||||
| } | ||||
| .dark::view-transition-old(root) { | ||||
|   z-index: 1; | ||||
| } | ||||
| .dark::view-transition-new(root) { | ||||
|   z-index: 9999; | ||||
| } | ||||
| </style> | ||||
| @@ -1,6 +1,6 @@ | ||||
| <template> | ||||
|   <div class="flex-col-center wh-full"> | ||||
|     <div class="text-400px text-primary"> | ||||
|   <div class="flex-col-center gap-24px min-h-520px wh-full overflow-hidden"> | ||||
|     <div class="flex text-400px text-primary"> | ||||
|       <icon-local-no-permission v-if="type === '403'" /> | ||||
|       <icon-local-not-found v-if="type === '404'" /> | ||||
|       <icon-local-service-error v-if="type === '500'" /> | ||||
| @@ -2,14 +2,14 @@ | ||||
|   <div v-if="showTooltip"> | ||||
|     <n-tooltip :placement="placement" trigger="hover"> | ||||
|       <template #trigger> | ||||
|         <div class="flex-center h-full cursor-pointer dark:hover:bg-[#333]" :class="contentClassName"> | ||||
|         <div class="flex-center h-full cursor-pointer dark:hover:bg-#333" :class="contentClassName"> | ||||
|           <slot></slot> | ||||
|         </div> | ||||
|       </template> | ||||
|       {{ tooltipContent }} | ||||
|     </n-tooltip> | ||||
|   </div> | ||||
|   <div v-else class="flex-center cursor-pointer dark:hover:bg-[#333]" :class="contentClassName"> | ||||
|   <div v-else class="flex-center cursor-pointer dark:hover:bg-#333" :class="contentClassName"> | ||||
|     <slot></slot> | ||||
|   </div> | ||||
| </template> | ||||
| @@ -41,7 +41,7 @@ const props = withDefaults(defineProps<Props>(), { | ||||
| const showTooltip = computed(() => Boolean(props.tooltipContent)); | ||||
| 
 | ||||
| const contentClassName = computed( | ||||
|   () => `${props.contentClass} ${props.inverted ? 'hover:bg-primary' : 'hover:bg-[#f6f6f6]'}` | ||||
|   () => `${props.contentClass} ${props.inverted ? 'hover:bg-primary' : 'hover:bg-#f6f6f6'}` | ||||
| ); | ||||
| </script> | ||||
| 
 | ||||
| @@ -8,7 +8,7 @@ import { isNumber } from '@/utils'; | ||||
| 
 | ||||
| defineOptions({ name: 'CountTo' }); | ||||
| 
 | ||||
| type TansitionKey = keyof typeof TransitionPresets; | ||||
| type TransitionKey = keyof typeof TransitionPresets; | ||||
| 
 | ||||
| interface Props { | ||||
|   /** 初始值 */ | ||||
| @@ -32,7 +32,7 @@ interface Props { | ||||
|   /** 使用缓冲动画函数 */ | ||||
|   useEasing?: boolean; | ||||
|   /** 缓冲动画函数类型 */ | ||||
|   transition?: TansitionKey; | ||||
|   transition?: TransitionKey; | ||||
| } | ||||
| 
 | ||||
| const props = withDefaults(defineProps<Props>(), { | ||||
| @@ -3,7 +3,7 @@ | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import WebSiteLink from './WebSiteLink.vue'; | ||||
| import WebSiteLink from './web-site-link.vue'; | ||||
| 
 | ||||
| defineOptions({ name: 'GithubLink' }); | ||||
| 
 | ||||
| @@ -11,14 +11,13 @@ | ||||
|       <n-input v-model:value="searchValue" placeholder="搜索图标"></n-input> | ||||
|     </template> | ||||
|     <div v-if="iconsList.length > 0" class="grid grid-cols-9 h-auto overflow-auto"> | ||||
|       <template v-for="iconItem in iconsList" :key="iconItem"> | ||||
|       <span v-for="iconItem in iconsList" :key="iconItem" @click="handleChange(iconItem)"> | ||||
|         <svg-icon | ||||
|           :icon="iconItem" | ||||
|           class="border-1px border-[#d9d9d9] text-30px m-2px p-5px" | ||||
|           class="border-1px border-#d9d9d9 text-30px m-2px p-5px cursor-pointer" | ||||
|           :class="{ 'border-primary': modelValue === iconItem }" | ||||
|           @click="handleChange(iconItem)" | ||||
|         /> | ||||
|       </template> | ||||
|       </span> | ||||
|     </div> | ||||
|     <n-empty v-else class="w-306px" description="你什么也找不到" /> | ||||
|   </n-popover> | ||||
| @@ -37,13 +37,13 @@ const bindAttrs = computed<{ class: string; style: string }>(() => ({ | ||||
| })); | ||||
| 
 | ||||
| const symbolId = computed(() => { | ||||
|   const { VITE_ICON_LOCAL_PREFFIX: preffix } = import.meta.env; | ||||
|   const { VITE_ICON_LOCAL_PREFIX: prefix } = import.meta.env; | ||||
| 
 | ||||
|   const defaultLocalIcon = 'no-icon'; | ||||
| 
 | ||||
|   const icon = props.localIcon || defaultLocalIcon; | ||||
| 
 | ||||
|   return `#${preffix}-${icon}`; | ||||
|   return `#${prefix}-${icon}`; | ||||
| }); | ||||
| 
 | ||||
| /** 渲染本地icon */ | ||||
| @@ -1,14 +1,34 @@ | ||||
| import { effectScope, onScopeDispose, watch } from 'vue'; | ||||
| import { useRoute } from 'vue-router'; | ||||
| import { useEventListener } from '@vueuse/core'; | ||||
| import { useI18n } from 'vue-i18n'; | ||||
| import { useTabStore, useThemeStore } from '@/store'; | ||||
|  | ||||
| /** 全局事件 */ | ||||
| export function useGlobalEvents() { | ||||
|   const theme = useThemeStore(); | ||||
|   const tab = useTabStore(); | ||||
|   const route = useRoute(); | ||||
|   const { locale, t } = useI18n(); | ||||
|   const scope = effectScope(); | ||||
|  | ||||
|   /** 页面离开时缓存多页签数据 */ | ||||
|   useEventListener(window, 'beforeunload', () => { | ||||
|     theme.cacheThemeSettings(); | ||||
|     tab.cacheTabRoutes(); | ||||
|   }); | ||||
|  | ||||
|   scope.run(() => { | ||||
|     // 国际化切换时更新浏览器标签文本 | ||||
|     watch( | ||||
|       () => locale.value, | ||||
|       () => { | ||||
|         document.title = route.meta.i18nTitle ? t(route.meta.i18nTitle) : route.meta.title; | ||||
|       } | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   onScopeDispose(() => { | ||||
|     scope.stop(); | ||||
|   }); | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { h } from 'vue'; | ||||
| import SvgIcon from '@/components/custom/SvgIcon.vue'; | ||||
| import SvgIcon from '@/components/custom/svg-icon.vue'; | ||||
|  | ||||
| /** | ||||
|  * 图标渲染 | ||||
|   | ||||
| @@ -4,3 +4,4 @@ export * from './layout'; | ||||
| export * from './events'; | ||||
| export * from './echarts'; | ||||
| export * from './icon'; | ||||
| export * from './websocket'; | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { computed } from 'vue'; | ||||
| import { computed, watch } from 'vue'; | ||||
| import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'; | ||||
| import { useAppStore, useThemeStore } from '@/store'; | ||||
|  | ||||
| type LayoutMode = 'vertical' | 'horizontal'; | ||||
| type LayoutHeaderProps = Record<EnumType.ThemeLayoutMode, App.GlobalHeaderProps>; | ||||
| type LayoutHeaderProps = Record<UnionKey.ThemeLayoutMode, App.GlobalHeaderProps>; | ||||
|  | ||||
| export function useBasicLayout() { | ||||
|   const app = useAppStore(); | ||||
| @@ -63,6 +63,16 @@ export function useBasicLayout() { | ||||
|     return w; | ||||
|   }); | ||||
|  | ||||
|   watch( | ||||
|     isMobile, | ||||
|     newValue => { | ||||
|       if (newValue) { | ||||
|         app.setSiderCollapse(true); | ||||
|       } | ||||
|     }, | ||||
|     { immediate: true } | ||||
|   ); | ||||
|  | ||||
|   return { | ||||
|     mode, | ||||
|     isMobile, | ||||
|   | ||||
| @@ -19,9 +19,9 @@ export function useRouterPush(inSetup = true) { | ||||
|     if (newTab) { | ||||
|       const routerData = router.resolve(to); | ||||
|       window.open(routerData.href, '_blank'); | ||||
|     } else { | ||||
|       router.push(to); | ||||
|       return Promise.resolve(); | ||||
|     } | ||||
|     return router.push(to); | ||||
|   } | ||||
|  | ||||
|   /** 返回上一级路由 */ | ||||
| @@ -42,8 +42,8 @@ export function useRouterPush(inSetup = true) { | ||||
|    * @param loginModule - 展示的登录模块 | ||||
|    * @param redirectUrl - 重定向地址(登录成功后跳转的地址),默认undefined表示取当前地址为重定向地址 | ||||
|    */ | ||||
|   function toLogin(loginModule?: EnumType.LoginModuleKey, redirectUrl?: string) { | ||||
|     const module: EnumType.LoginModuleKey = loginModule || 'pwd-login'; | ||||
|   function toLogin(loginModule?: UnionKey.LoginModule, redirectUrl?: string) { | ||||
|     const module: UnionKey.LoginModule = loginModule || 'pwd-login'; | ||||
|     const routeLocation: RouteLocationRaw = { | ||||
|       name: routeName('login'), | ||||
|       params: { module } | ||||
| @@ -57,7 +57,7 @@ export function useRouterPush(inSetup = true) { | ||||
|    * 登录页切换其他模块 | ||||
|    * @param module - 切换后的登录模块 | ||||
|    */ | ||||
|   function toLoginModule(module: EnumType.LoginModuleKey) { | ||||
|   function toLoginModule(module: UnionKey.LoginModule) { | ||||
|     const { query } = route.value; | ||||
|     routerPush({ name: routeName('login'), params: { module }, query }); | ||||
|   } | ||||
|   | ||||
| @@ -2,26 +2,6 @@ import UAParser from 'ua-parser-js'; | ||||
| import { useAuthStore } from '@/store'; | ||||
| import { isArray, isString } from '@/utils'; | ||||
|  | ||||
| interface AppInfo { | ||||
|   /** 项目名称 */ | ||||
|   name: string; | ||||
|   /** 项目标题 */ | ||||
|   title: string; | ||||
|   /** 项目描述 */ | ||||
|   desc: string; | ||||
| } | ||||
|  | ||||
| /** 项目信息 */ | ||||
| export function useAppInfo(): AppInfo { | ||||
|   const { VITE_APP_NAME: name, VITE_APP_TITLE: title, VITE_APP_DESC: desc } = import.meta.env; | ||||
|  | ||||
|   return { | ||||
|     name, | ||||
|     title, | ||||
|     desc | ||||
|   }; | ||||
| } | ||||
|  | ||||
| /** 获取设备信息 */ | ||||
| export function useDeviceInfo() { | ||||
|   const parser = new UAParser(); | ||||
|   | ||||
							
								
								
									
										50
									
								
								src/composables/websocket.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,50 @@ | ||||
| import { io } from 'socket.io-client'; | ||||
| import type { Socket } from 'socket.io-client'; | ||||
| import { useAppStore } from '../store'; | ||||
|  | ||||
| type ListenEvents = { | ||||
|   update: (id: string, data: { name: string; age: number }) => void; | ||||
| }; | ||||
|  | ||||
| type EmitEvents = { | ||||
|   update: (id: string, data: { name: string; age: number }) => void; | ||||
| }; | ||||
|  | ||||
| export function useWebsocket() { | ||||
|   const app = useAppStore(); | ||||
|  | ||||
|   const socket: Socket<ListenEvents, EmitEvents> = (app.socket || io('ws://localhost:8080')) as Socket< | ||||
|     ListenEvents, | ||||
|     EmitEvents | ||||
|   >; | ||||
|  | ||||
|   if (!app.socket) { | ||||
|     app.setSocket(socket); | ||||
|   } | ||||
|  | ||||
|   function init() { | ||||
|     window.console.log('[socket.io] connecting...'); | ||||
|  | ||||
|     socket.on('connect', () => { | ||||
|       window.console.log('[socket.io] connected.'); | ||||
|     }); | ||||
|  | ||||
|     socket.on('disconnect', () => { | ||||
|       window.console.log('[socket.io] disconnected.'); | ||||
|     }); | ||||
|  | ||||
|     socket.on('update', (id, data) => { | ||||
|       window.console.log('[socket.io] update', id, data); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   function handleUpdate(id: string, data: { name: string; age: number }) { | ||||
|     socket.emit('update', id, data); | ||||
|   } | ||||
|  | ||||
|   init(); | ||||
|  | ||||
|   return { | ||||
|     handleUpdate | ||||
|   }; | ||||
| } | ||||
| @@ -2,7 +2,7 @@ | ||||
| export const BAIDU_MAP_SDK_URL = `https://api.map.baidu.com/getscript?v=3.0&ak=KSezYymXPth1DIGILRX3oYN9PxbOQQmU&services=&t=20210201100830&s=1`; | ||||
|  | ||||
| /** 高德地图sdk地址 */ | ||||
| export const GAODE_MAP_SDK_URL = 'https://webapi.amap.com/maps?v=2.0&key=e7bd02bd504062087e6563daf4d6721d'; | ||||
| export const AMAP_SDK_URL = 'https://webapi.amap.com/maps?v=2.0&key=e7bd02bd504062087e6563daf4d6721d'; | ||||
|  | ||||
| /** 腾讯地图sdk地址 */ | ||||
| export const TENCENT_MAP_SDK_URL = 'https://map.qq.com/api/gljs?v=1.exp&key=A6DBZ-KXPLW-JKSRY-ONZF4-CPHY3-K6BL7'; | ||||
|   | ||||
							
								
								
									
										6
									
								
								src/constants/_shared.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,6 @@ | ||||
| export function transformObjectToOption<T extends object>(obj: T) { | ||||
|   return Object.entries(obj).map(([value, label]) => ({ | ||||
|     value, | ||||
|     label | ||||
|   })) as Common.OptionWithKey<keyof T>[]; | ||||
| } | ||||
| @@ -1,13 +1,27 @@ | ||||
| import { $t } from '@/locales'; | ||||
| import { transformObjectToOption } from './_shared'; | ||||
|  | ||||
| export const loginModuleLabels: Record<UnionKey.LoginModule, string> = { | ||||
|   'pwd-login': $t('page.login.pwdLogin.title'), | ||||
|   'code-login': $t('page.login.codeLogin.title'), | ||||
|   register: $t('page.login.register.title'), | ||||
|   'reset-pwd': $t('page.login.resetPwd.title'), | ||||
|   'bind-wechat': $t('page.login.bindWeChat.title') | ||||
| }; | ||||
|  | ||||
| export const userRoleLabels: Record<Auth.RoleType, string> = { | ||||
|   super: $t('page.login.pwdLogin.superAdmin'), | ||||
|   admin: $t('page.login.pwdLogin.admin'), | ||||
|   user: $t('page.login.pwdLogin.user') | ||||
| }; | ||||
| export const userRoleOptions = transformObjectToOption(userRoleLabels); | ||||
|  | ||||
| /** 用户性别 */ | ||||
| export const genderLabels: Record<UserManagement.GenderKey, string> = { | ||||
|   0: '女', | ||||
|   1: '男' | ||||
| }; | ||||
|  | ||||
| export const genderOptions: { value: UserManagement.GenderKey; label: string }[] = [ | ||||
|   { value: '0', label: genderLabels['0'] }, | ||||
|   { value: '1', label: genderLabels['1'] } | ||||
| ]; | ||||
| export const genderOptions = transformObjectToOption(genderLabels); | ||||
|  | ||||
| /** 用户状态 */ | ||||
| export const userStatusLabels: Record<UserManagement.UserStatusKey, string> = { | ||||
| @@ -16,10 +30,4 @@ export const userStatusLabels: Record<UserManagement.UserStatusKey, string> = { | ||||
|   3: '冻结', | ||||
|   4: '软删除' | ||||
| }; | ||||
|  | ||||
| export const userStatusOptions: { value: UserManagement.UserStatusKey; label: string }[] = [ | ||||
|   { value: '1', label: userStatusLabels['1'] }, | ||||
|   { value: '2', label: userStatusLabels['2'] }, | ||||
|   { value: '3', label: userStatusLabels['3'] }, | ||||
|   { value: '4', label: userStatusLabels['4'] } | ||||
| ]; | ||||
| export const userStatusOptions = transformObjectToOption(userStatusLabels); | ||||
|   | ||||
							
								
								
									
										18
									
								
								src/constants/common.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,18 @@ | ||||
| export const dataTypeLabels: { [K in TypeUtil.DataTypeStringKey]: TypeUtil.DataTypeString<K> } = { | ||||
|   string: '[object String]', | ||||
|   number: '[object Number]', | ||||
|   boolean: '[object Boolean]', | ||||
|   null: '[object Null]', | ||||
|   undefined: '[object Undefined]', | ||||
|   symbol: '[object Symbol]', | ||||
|   bigInt: '[object BigInt]', | ||||
|   object: '[object Object]', | ||||
|   function: '[object Function]', | ||||
|   array: '[object Array]', | ||||
|   date: '[object Date]', | ||||
|   regExp: '[object RegExp]', | ||||
|   promise: '[object Promise]', | ||||
|   set: '[object Set]', | ||||
|   map: '[object Map]', | ||||
|   file: '[object File]' | ||||
| }; | ||||
| @@ -1 +1,3 @@ | ||||
| export * from './common'; | ||||
| export * from './system'; | ||||
| export * from './business'; | ||||
|   | ||||
							
								
								
									
										38
									
								
								src/constants/system.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,38 @@ | ||||
| import { transformObjectToOption } from './_shared'; | ||||
|  | ||||
| export const themeLayoutModeLabels: Record<UnionKey.ThemeLayoutMode, string> = { | ||||
|   vertical: '左侧菜单模式', | ||||
|   horizontal: '顶部菜单模式', | ||||
|   'vertical-mix': '左侧菜单混合模式', | ||||
|   'horizontal-mix': '顶部菜单混合模式' | ||||
| }; | ||||
| export const themeLayoutModeOptions = transformObjectToOption(themeLayoutModeLabels); | ||||
|  | ||||
| export const themeScrollModeLabels: Record<UnionKey.ThemeScrollMode, string> = { | ||||
|   wrapper: '外层滚动', | ||||
|   content: '主体滚动' | ||||
| }; | ||||
| export const themeScrollModeOptions = transformObjectToOption(themeScrollModeLabels); | ||||
|  | ||||
| export const themeTabModeLabels: Record<UnionKey.ThemeTabMode, string> = { | ||||
|   chrome: '谷歌风格', | ||||
|   button: '按钮风格' | ||||
| }; | ||||
| export const themeTabModeOptions = transformObjectToOption(themeTabModeLabels); | ||||
|  | ||||
| export const themeHorizontalMenuPositionLabels: Record<UnionKey.ThemeHorizontalMenuPosition, string> = { | ||||
|   'flex-start': '居左', | ||||
|   center: '居中', | ||||
|   'flex-end': '居右' | ||||
| }; | ||||
| export const themeHorizontalMenuPositionOptions = transformObjectToOption(themeHorizontalMenuPositionLabels); | ||||
|  | ||||
| export const themeAnimateModeLabels: Record<UnionKey.ThemeAnimateMode, string> = { | ||||
|   'zoom-fade': '渐变', | ||||
|   'zoom-out': '闪现', | ||||
|   'fade-slide': '滑动', | ||||
|   fade: '消退', | ||||
|   'fade-bottom': '底部消退', | ||||
|   'fade-scale': '缩放消退' | ||||
| }; | ||||
| export const themeAnimateModeOptions = transformObjectToOption(themeAnimateModeLabels); | ||||
| @@ -1,15 +0,0 @@ | ||||
| /** 用户角色 */ | ||||
| export enum EnumUserRole { | ||||
|   super = '超级管理员', | ||||
|   admin = '管理员', | ||||
|   user = '普通用户' | ||||
| } | ||||
|  | ||||
| /** 登录模块 */ | ||||
| export enum EnumLoginModule { | ||||
|   'pwd-login' = '账密登录', | ||||
|   'code-login' = '手机验证码登录', | ||||
|   'register' = '注册', | ||||
|   'reset-pwd' = '重置密码', | ||||
|   'bind-wechat' = '微信绑定' | ||||
| } | ||||
| @@ -1,24 +0,0 @@ | ||||
| /** http请求头的content-type类型 */ | ||||
| export enum EnumContentType { | ||||
|   json = 'application/json', | ||||
|   formUrlencoded = 'application/x-www-form-urlencoded', | ||||
|   formData = 'multipart/form-data' | ||||
| } | ||||
|  | ||||
| /** 数据类型 */ | ||||
| export enum EnumDataType { | ||||
|   number = '[object Number]', | ||||
|   string = '[object String]', | ||||
|   boolean = '[object Boolean]', | ||||
|   null = '[object Null]', | ||||
|   undefined = '[object Undefined]', | ||||
|   object = '[object Object]', | ||||
|   array = '[object Array]', | ||||
|   function = '[object Function]', | ||||
|   date = '[object Date]', | ||||
|   regexp = '[object RegExp]', | ||||
|   promise = '[object Promise]', | ||||
|   set = '[object Set]', | ||||
|   map = '[object Map]', | ||||
|   file = '[object File]' | ||||
| } | ||||
| @@ -1,3 +0,0 @@ | ||||
| export * from './common'; | ||||
| export * from './system'; | ||||
| export * from './business'; | ||||
| @@ -1,36 +0,0 @@ | ||||
| /** 布局组件的名称 */ | ||||
| export enum EnumLayoutComponentName { | ||||
|   basic = 'basic-layout', | ||||
|   blank = 'blank-layout' | ||||
| } | ||||
|  | ||||
| /** 布局模式 */ | ||||
| export enum EnumThemeLayoutMode { | ||||
|   'vertical' = '左侧菜单模式', | ||||
|   'horizontal' = '顶部菜单模式', | ||||
|   'vertical-mix' = '左侧菜单混合模式', | ||||
|   'horizontal-mix' = '顶部菜单混合模式' | ||||
| } | ||||
|  | ||||
| /** 多页签风格 */ | ||||
| export enum EnumThemeTabMode { | ||||
|   'chrome' = '谷歌风格', | ||||
|   'button' = '按钮风格' | ||||
| } | ||||
|  | ||||
| /** 水平模式的菜单位置 */ | ||||
| export enum EnumThemeHorizontalMenuPosition { | ||||
|   'flex-start' = '居左', | ||||
|   'center' = '居中', | ||||
|   'flex-end' = '居右' | ||||
| } | ||||
|  | ||||
| /** 过渡动画类型 */ | ||||
| export enum EnumThemeAnimateMode { | ||||
|   'zoom-fade' = '渐变', | ||||
|   'zoom-out' = '闪现', | ||||
|   'fade-slide' = '滑动', | ||||
|   'fade' = '消退', | ||||
|   'fade-bottom' = '底部消退', | ||||
|   'fade-scale' = '缩放消退' | ||||
| } | ||||
| @@ -1,5 +1,5 @@ | ||||
| import useCountDown from './useCountDown'; | ||||
| import useSmsCode from './useSmsCode'; | ||||
| import useImageVerify from './useImageVerify'; | ||||
| import useCountDown from './use-count-down'; | ||||
| import useSmsCode from './use-sms-code'; | ||||
| import useImageVerify from './use-image-verify'; | ||||
|  | ||||
| export { useCountDown, useSmsCode, useImageVerify }; | ||||
|   | ||||
							
								
								
									
										180
									
								
								src/hooks/business/use-hook-table.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,180 @@ | ||||
| import { ref, reactive } from 'vue'; | ||||
| import type { Ref } from 'vue'; | ||||
| import type { PaginationProps, DataTableBaseColumn, DataTableSelectionColumn, DataTableExpandColumn } from 'naive-ui'; | ||||
| import type { TableColumnGroup } from 'naive-ui/es/data-table/src/interface'; | ||||
| import { useLoadingEmpty } from '../common'; | ||||
|  | ||||
| /** | ||||
|  * 接口请求函数 | ||||
|  */ | ||||
| type ApiFn<T = any, R = any> = (args: T) => Promise<Service.RequestResult<R>>; | ||||
|  | ||||
| /** | ||||
|  * 接口请求函数的参数 | ||||
|  */ | ||||
| type GetApiFnParameters<T extends ApiFn, R = any> = T extends (args: infer P) => Promise<Service.RequestResult<R>> | ||||
|   ? P | ||||
|   : never; | ||||
|  | ||||
| /** | ||||
|  * 接口请求函数的返回值 | ||||
|  */ | ||||
| type GetApiFnReturnType<T extends ApiFn, P = any> = T extends (args: P) => Promise<Service.RequestResult<infer R>> | ||||
|   ? R | ||||
|   : never; | ||||
|  | ||||
| /** | ||||
|  * 表格接口请求后转换后的数据 | ||||
|  */ | ||||
| type Transformer<TableData, Response> = (response: Response) => { | ||||
|   data: TableData[]; | ||||
|   pageNum: number; | ||||
|   pageSize: number; | ||||
|   total: number; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * 列表接口参数更新 | ||||
|  */ | ||||
| type ApiParamsUpdater<P, R> = (params: P) => R; | ||||
|  | ||||
| /** | ||||
|  * 分页参数 | ||||
|  */ | ||||
| type PagePropsOfPagination = Pick<PaginationProps, 'page' | 'pageSize'>; | ||||
|  | ||||
| /** | ||||
|  * 自定义的列 key | ||||
|  */ | ||||
| type CustomColumnKey<K = never> = K | 'action'; | ||||
|  | ||||
| /** | ||||
|  * 表格的列 | ||||
|  */ | ||||
| type HookTableColumn<T = Record<string, unknown>> = | ||||
|   | (Omit<TableColumnGroup<T>, 'key'> & { key: CustomColumnKey<keyof T> }) | ||||
|   | (Omit<DataTableBaseColumn<T>, 'key'> & { key: CustomColumnKey<keyof T> }) | ||||
|   | DataTableSelectionColumn<T> | ||||
|   | DataTableExpandColumn<T>; | ||||
|  | ||||
| /** | ||||
|  * 表格配置 | ||||
|  */ | ||||
| type HookTableConfig<TableData, Fn extends ApiFn> = { | ||||
|   /** | ||||
|    * 列表接口参数 | ||||
|    */ | ||||
|   apiParams: GetApiFnParameters<Fn>; | ||||
|   /** | ||||
|    * 列表接口返回数据转换 | ||||
|    */ | ||||
|   transformer: Transformer<TableData, GetApiFnReturnType<Fn>>; | ||||
|   /** | ||||
|    * 列表列 | ||||
|    */ | ||||
|   columns: () => HookTableColumn<TableData>[]; | ||||
|   /** | ||||
|    * 列表接口参数更新 | ||||
|    * @description 用于更新分页参数, 如果列表接口的参数不包含同名分页参数属性 `page` 和 `pageSize`, 需要通过此函数更新 | ||||
|    * @default p => p | ||||
|    */ | ||||
|   apiParamsUpdater?: ApiParamsUpdater<GetApiFnParameters<Fn> & Partial<PagePropsOfPagination>, GetApiFnParameters<Fn>>; | ||||
|   /** | ||||
|    * 列表分页参数 | ||||
|    */ | ||||
|   pagination?: PaginationProps; | ||||
|   /** | ||||
|    * 是否立即请求 | ||||
|    * @default true | ||||
|    */ | ||||
|   immediate?: boolean; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * 通用表格 hook | ||||
|  * @param apiFn 接口请求函数 | ||||
|  * @param config 表格配置 | ||||
|  */ | ||||
| export default function useHookTable<TableData, Fn extends ApiFn>(apiFn: Fn, config: HookTableConfig<TableData, Fn>) { | ||||
|   const { loading, startLoading, endLoading, empty, setEmpty } = useLoadingEmpty(); | ||||
|  | ||||
|   const { apiParams, transformer, apiParamsUpdater = p => p, immediate = true } = config; | ||||
|  | ||||
|   const data: Ref<TableData[]> = ref([]); | ||||
|  | ||||
|   function updateData(update: TableData[]) { | ||||
|     data.value = update; | ||||
|   } | ||||
|  | ||||
|   const columns = ref(config.columns()) as Ref<HookTableColumn<TableData>[]>; | ||||
|  | ||||
|   const requestParams = ref(apiParams) as Ref<HookTableConfig<TableData, Fn>['apiParams']>; | ||||
|  | ||||
|   function updateRequestParamsByPagination(p: PagePropsOfPagination) { | ||||
|     requestParams.value = apiParamsUpdater({ ...requestParams.value, ...p }); | ||||
|   } | ||||
|  | ||||
|   const pagination = reactive({ | ||||
|     page: 1, | ||||
|     pageSize: 10, | ||||
|     showSizePicker: true, | ||||
|     pageSizes: [10, 15, 20, 25, 30], | ||||
|     onChange: (page: number) => { | ||||
|       pagination.page = page; | ||||
|  | ||||
|       updateRequestParamsByPagination({ page }); | ||||
|       getData(); | ||||
|     }, | ||||
|     onUpdatePageSize: (pageSize: number) => { | ||||
|       pagination.pageSize = pageSize; | ||||
|       pagination.page = 1; | ||||
|  | ||||
|       updateRequestParamsByPagination({ pageSize }); | ||||
|       getData(); | ||||
|     }, | ||||
|     ...config.pagination | ||||
|   }) as PaginationProps; | ||||
|  | ||||
|   function updatePagination(update: Partial<PaginationProps>) { | ||||
|     Object.assign(pagination, update); | ||||
|  | ||||
|     updateRequestParamsByPagination({ page: pagination.page, pageSize: pagination.pageSize }); | ||||
|   } | ||||
|  | ||||
|   async function getData() { | ||||
|     startLoading(); | ||||
|  | ||||
|     const { data: apiData, error } = await apiFn(requestParams.value); | ||||
|  | ||||
|     if (!error && data) { | ||||
|       const { data: tableData, pageNum, pageSize, total } = transformer(apiData); | ||||
|  | ||||
|       updateData(tableData); | ||||
|  | ||||
|       setEmpty(tableData.length === 0); | ||||
|  | ||||
|       updatePagination({ page: pageNum, pageSize, itemCount: total }); | ||||
|     } | ||||
|  | ||||
|     endLoading(); | ||||
|   } | ||||
|  | ||||
|   function reloadColumns() { | ||||
|     columns.value = config.columns(); | ||||
|   } | ||||
|  | ||||
|   if (immediate) { | ||||
|     getData(); | ||||
|   } | ||||
|  | ||||
|   return { | ||||
|     data, | ||||
|     columns, | ||||
|     loading, | ||||
|     empty, | ||||
|     pagination, | ||||
|     getData, | ||||
|     updatePagination, | ||||
|     reloadColumns | ||||
|   }; | ||||
| } | ||||