Compare commits
104 Commits
fast-crud
...
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 |
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
|
||||
|
@@ -1 +1,2 @@
|
||||
VITE_HTTP_PROXY=Y
|
||||
VITE_SOYBEAN_ROUTE_PLUGIN=Y
|
||||
|
@@ -1,4 +1,4 @@
|
||||
!.env-config.ts
|
||||
components.d.ts
|
||||
router-page.d.ts
|
||||
*.svg
|
||||
src-tauri/target
|
||||
|
@@ -10,7 +10,8 @@ module.exports = {
|
||||
{
|
||||
files: ['*.vue'],
|
||||
rules: {
|
||||
'no-undef': 'off' // use tsc to check the ts code of the vue
|
||||
'no-undef': 'off', // use tsc to check the ts code of the vue
|
||||
'vue/no-setup-props-destructure': 'off' // wait to fix this rule
|
||||
}
|
||||
}
|
||||
],
|
||||
|
16
.github/workflows/release.yml
vendored
@@ -1,27 +1,25 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.**"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Create github releases
|
||||
run: npx changelogithub
|
||||
- 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,27 +1,19 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"afzalsayed96.icones",
|
||||
"antfu.iconify",
|
||||
"antfu.unocss",
|
||||
"christian-kohler.path-intellisense",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"eamodio.gitlens",
|
||||
"editorconfig.editorconfig",
|
||||
"esbenp.prettier-vscode",
|
||||
"formulahendry.auto-close-tag",
|
||||
"formulahendry.auto-complete-tag",
|
||||
"formulahendry.auto-close-tag",
|
||||
"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",
|
||||
"sdras.vue-vscode-snippets",
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"vue.volar",
|
||||
"vue.vscode-typescript-vue-plugin",
|
||||
"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}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
123
.vscode/settings.json
vendored
@@ -1,75 +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.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue", "json"],
|
||||
"eslint.validate": ["json"],
|
||||
"files.associations": {
|
||||
"*.env.*": "dotenv"
|
||||
"*.env.*": "dotenv",
|
||||
"*.svg": "html"
|
||||
},
|
||||
"files.eol": "\n",
|
||||
"git.enableSmartCommit": true,
|
||||
"gutterpreview.paths": {
|
||||
"@": "/src",
|
||||
"~@": "/src"
|
||||
},
|
||||
"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.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.localesPaths": ["src/locales", "src/locales/lang"]
|
||||
"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
|
||||
}
|
||||
}
|
||||
|
1393
CHANGELOG.md
62
README.md
@@ -1,5 +1,5 @@
|
||||
<div align="center">
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean.svg" style="width: 160px;"/>
|
||||
<img src="./public/favicon.svg" style="width: 160px;"/>
|
||||
<h1>Soybean Admin</h1>
|
||||
</div>
|
||||
|
||||
@@ -19,22 +19,35 @@
|
||||
- **权限路由**:提供前端静态和后端动态两种路由模式,基于 mock 的动态路由能快速实现后端动态路由
|
||||
- **请求函数**:基于 axios 的完善的请求函数封装,提供 Promise 和 hooks 两种请求函数,加入请求结果数据转换的适配器
|
||||
|
||||
## SoybeanJS 工具库
|
||||
|
||||
- [@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://soybean.pro/)
|
||||
- [Soybean Admin 预览地址](https://admin.soybeanjs.cn/)
|
||||
|
||||
## 文档
|
||||
|
||||
- [项目文档预览地址](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) |
|
||||
|
||||
## 更新日志
|
||||
|
||||
@@ -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
|
||||
@@ -117,11 +139,7 @@ docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6
|
||||
|
||||
项目已经内置 Angular 提交规范,直接执行 commit 命令即可生成符合 Angular 提交规范的 commit。
|
||||
|
||||
项目已用 simple-git-hooks 代替了 husky, 旧版本用了 husky,执行 pnpm soy init-git-hooks 进行初始化配置
|
||||
|
||||
## 基于 SoybeanAdmin 二次开发的项目
|
||||
[electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的mock。
|
||||
[T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH客户端。
|
||||
项目已用 simple-git-hooks 代替了 husky, 旧版本用了 husky,执行 pnpm soy init-simple-git-hooks 进行初始化配置
|
||||
|
||||
## 浏览器支持
|
||||
|
||||
@@ -142,17 +160,13 @@ docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6
|
||||
`Soybean Admin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群,使用问题欢迎在群内提问。
|
||||
|
||||
<div style="display:flex;">
|
||||
<!-- <div style="padding-right:24px;">
|
||||
<p>微信交流群</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybeanjs-wechat0503.jpeg" style="width:200px" />
|
||||
</div> -->
|
||||
<div style="padding-right:24px;">
|
||||
<p>QQ交流群</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin.jpg" style="width:200px" />
|
||||
<img src="https://i.loli.net/2021/11/24/1J6REWXiHomU2kM.jpg" style="width:200px" />
|
||||
</div>
|
||||
<div>
|
||||
<p>添加本人微信,欢迎来技术交流,业务咨询</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybeanjs.jpeg" style="width:180px" />
|
||||
<img src="https://s2.loli.net/2023/06/07/sVyCUFBvzQ9f5b7.jpg" style="width:200px" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -164,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) 协议,仅供参考学习,商用时请保留作者的版权信息,作者不对软件做担保和负责。
|
||||
|
@@ -3,7 +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 { webUpdateNotice } from '@plugin-web-update-notification/vite';
|
||||
import unplugin from './unplugin';
|
||||
import mock from './mock';
|
||||
import visualizer from './visualizer';
|
||||
@@ -22,11 +24,19 @@ export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | Plugin
|
||||
}
|
||||
}),
|
||||
vueJsx(),
|
||||
VueDevtools(),
|
||||
...unplugin(viteEnv),
|
||||
unocss(),
|
||||
mock(viteEnv),
|
||||
progress(),
|
||||
pageRoute()
|
||||
webUpdateNotice({
|
||||
notificationProps: {
|
||||
title: '👋 有新版本了',
|
||||
description: '点击刷新页面获取最新版本',
|
||||
buttonText: '刷新',
|
||||
dismissButtonText: '忽略'
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
if (viteEnv.VITE_VISUALIZER === 'Y') {
|
||||
@@ -38,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;
|
||||
}
|
||||
|
@@ -7,13 +7,13 @@ 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 [
|
||||
Icons({
|
||||
@@ -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,21 +0,0 @@
|
||||
{
|
||||
"types": {
|
||||
"feat": { "title": "🚀 Features" },
|
||||
"perf": { "title": "🔥 Performance" },
|
||||
"fix": { "title": "🩹 Fixes" },
|
||||
"refactor": { "title": "💅 Refactors" },
|
||||
"docs": { "title": "📖 Documentation" },
|
||||
"types": { "title": "🌊 Types" },
|
||||
"chore": { "title": "🏡 Chore" },
|
||||
"test": { "title": "🧪 Tests" },
|
||||
"style": { "title": "🎨 Styles" },
|
||||
"ci": { "title": "🤖 CI" }
|
||||
},
|
||||
"scopeMap": {},
|
||||
"titles": {
|
||||
"breakingChanges": "🚨 Breaking Changes"
|
||||
},
|
||||
"contributors": true,
|
||||
"capitalize": true,
|
||||
"group": true
|
||||
}
|
@@ -1,7 +1,12 @@
|
||||
<!-- prettier-ignore -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cmn-Hans">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<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>%VITE_APP_NAME%</title>
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
|
145
package.json
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "soybean-admin",
|
||||
"version": "0.9.9",
|
||||
"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,88 +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 --ext .js,.jsx,.mjs,.json,.ts,.tsx,.vue",
|
||||
"format": "soy prettier-format",
|
||||
"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": "soy init-git-hooks"
|
||||
"prepare": "soy init-simple-git-hooks"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/data-set": "^0.11.8",
|
||||
"@antv/g2": "^4.2.10",
|
||||
"@better-scroll/core": "^2.5.1",
|
||||
"@soybeanjs/vue-materials": "^0.1.9",
|
||||
"@vueuse/core": "^10.1.2",
|
||||
"axios": "1.4.0",
|
||||
"clipboard": "^2.0.11",
|
||||
"colord": "^2.9.3",
|
||||
"crypto-js": "^4.1.1",
|
||||
"dayjs": "^1.11.7",
|
||||
"echarts": "^5.4.2",
|
||||
"form-data": "^4.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"@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.3",
|
||||
"print-js": "^1.6.0",
|
||||
"qs": "^6.11.2",
|
||||
"swiper": "^9.3.2",
|
||||
"ua-parser-js": "^1.0.35",
|
||||
"vditor": "^3.9.2",
|
||||
"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.2.2",
|
||||
"vue-router": "^4.2.1",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"wangeditor": "^4.7.15",
|
||||
"xgplayer": "^3.0.2"
|
||||
"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.13",
|
||||
"@iconify/json": "^2.2.67",
|
||||
"@iconify/vue": "^4.1.1",
|
||||
"@soybeanjs/cli": "^0.1.9",
|
||||
"@soybeanjs/vite-plugin-vue-page-route": "^0.0.5",
|
||||
"@types/bmapgl": "^0.0.7",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/node": "20.2.1",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
"@unocss/preset-uno": "^0.52.0",
|
||||
"@unocss/transformer-directives": "^0.52.0",
|
||||
"@unocss/vite": "^0.52.0",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||
"conventional-changelog": "^3.1.25",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-config-soybeanjs": "^0.3.7",
|
||||
"lint-staged": "13.2.2",
|
||||
"mockjs": "^1.1.0",
|
||||
"rollup-plugin-visualizer": "^5.9.0",
|
||||
"sass": "^1.62.1",
|
||||
"simple-git-hooks": "^2.8.1",
|
||||
"standard-version": "^9.5.0",
|
||||
"tsx": "^3.12.7",
|
||||
"typescript": "5.0.4",
|
||||
"unplugin-icons": "^0.16.1",
|
||||
"unplugin-vue-components": "0.24.1",
|
||||
"vite": "^4.3.8",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"@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.15.0",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-tsc": "1.6.5"
|
||||
"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": {
|
||||
@@ -128,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": {
|
||||
"*.{js,jsx,mjs,json,ts,tsx,vue}": "eslint . --fix"
|
||||
"soybean": {
|
||||
"useSoybeanToken": true
|
||||
}
|
||||
}
|
||||
|
6786
pnpm-lock.yaml
generated
@@ -1 +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="#1890ff"/><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="#1890ff"/></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="#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>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
12
src/App.vue
@@ -13,26 +13,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { dateZhCN, zhCN } from 'naive-ui';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { subscribeStore, useThemeStore } from '@/store';
|
||||
import { useGlobalEvents } from '@/composables';
|
||||
|
||||
const theme = useThemeStore();
|
||||
const { locale, t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
subscribeStore();
|
||||
useGlobalEvents();
|
||||
|
||||
watch(
|
||||
() => locale.value,
|
||||
() => {
|
||||
document.title = route.meta.i18nTitle ? t(route.meta.i18nTitle) : route.meta.title;
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@@ -4,25 +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, getRgbOfColor } 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',
|
||||
@@ -31,7 +29,7 @@ const lodingClasses = [
|
||||
|
||||
function addThemeColorCssVars() {
|
||||
const defaultColor = themeSettings.themeColor;
|
||||
const themeColor = localStg.get('themeColor') || defaultColor;
|
||||
const themeColor = sessionStg.get('themeColor') || defaultColor;
|
||||
|
||||
const { r, g, b } = getRgbOfColor(themeColor);
|
||||
|
||||
|
@@ -13,6 +13,8 @@ defineOptions({ name: 'DarkModeSwitch' });
|
||||
interface Props {
|
||||
/** 暗黑模式 */
|
||||
dark?: boolean;
|
||||
/** 自定义暗黑模式动画过渡 */
|
||||
customizeTransition?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -34,32 +36,35 @@ const darkMode = computed({
|
||||
}
|
||||
});
|
||||
|
||||
function handleSwitch(event: MouseEvent) {
|
||||
async function handleSwitch(event: MouseEvent) {
|
||||
const x = event.clientX;
|
||||
const y = event.clientY;
|
||||
const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y));
|
||||
// @ts-expect-error: Transition API
|
||||
if (!document.startViewTransition) {
|
||||
|
||||
if (!props.customizeTransition || !document.startViewTransition) {
|
||||
darkMode.value = !darkMode.value;
|
||||
return;
|
||||
}
|
||||
// @ts-expect-error: Transition API
|
||||
|
||||
const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y));
|
||||
|
||||
const transition = document.startViewTransition(() => {
|
||||
darkMode.value = !darkMode.value;
|
||||
});
|
||||
transition.ready.then(() => {
|
||||
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)'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
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>
|
||||
|
||||
|
@@ -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>(), {
|
||||
|
@@ -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 '~/src/components/custom/svg-icon.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,4 +1,4 @@
|
||||
import { computed } from 'vue';
|
||||
import { computed, watch } from 'vue';
|
||||
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core';
|
||||
import { useAppStore, useThemeStore } from '@/store';
|
||||
|
||||
@@ -63,6 +63,16 @@ export function useBasicLayout() {
|
||||
return w;
|
||||
});
|
||||
|
||||
watch(
|
||||
isMobile,
|
||||
newValue => {
|
||||
if (newValue) {
|
||||
app.setSiderCollapse(true);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return {
|
||||
mode,
|
||||
isMobile,
|
||||
|
@@ -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,33 +1,27 @@
|
||||
import { $t } from '@/locales';
|
||||
import { transformObjectToOption } from './_shared';
|
||||
|
||||
export const loginModuleLabels: Record<UnionKey.LoginModule, string> = {
|
||||
'pwd-login': '账密登录',
|
||||
'code-login': '手机验证码登录',
|
||||
register: '注册',
|
||||
'reset-pwd': '重置密码',
|
||||
'bind-wechat': '微信绑定'
|
||||
'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: '超级管理员',
|
||||
admin: '管理员',
|
||||
user: '普通用户'
|
||||
super: $t('page.login.pwdLogin.superAdmin'),
|
||||
admin: $t('page.login.pwdLogin.admin'),
|
||||
user: $t('page.login.pwdLogin.user')
|
||||
};
|
||||
|
||||
export const userRoleOptions: Common.OptionWithKey<Auth.RoleType>[] = [
|
||||
{ value: 'super', label: userRoleLabels.super },
|
||||
{ value: 'admin', label: userRoleLabels.admin },
|
||||
{ value: 'user', label: userRoleLabels.user }
|
||||
];
|
||||
export const userRoleOptions = transformObjectToOption(userRoleLabels);
|
||||
|
||||
/** 用户性别 */
|
||||
export const genderLabels: Record<UserManagement.GenderKey, string> = {
|
||||
0: '女',
|
||||
1: '男'
|
||||
};
|
||||
|
||||
export const genderOptions: Common.OptionWithKey<UserManagement.GenderKey>[] = [
|
||||
{ value: '0', label: genderLabels['0'] },
|
||||
{ value: '1', label: genderLabels['1'] }
|
||||
];
|
||||
export const genderOptions = transformObjectToOption(genderLabels);
|
||||
|
||||
/** 用户状态 */
|
||||
export const userStatusLabels: Record<UserManagement.UserStatusKey, string> = {
|
||||
@@ -36,10 +30,4 @@ export const userStatusLabels: Record<UserManagement.UserStatusKey, string> = {
|
||||
3: '冻结',
|
||||
4: '软删除'
|
||||
};
|
||||
|
||||
export const userStatusOptions: Common.OptionWithKey<UserManagement.UserStatusKey>[] = [
|
||||
{ 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);
|
||||
|
@@ -1,81 +1,31 @@
|
||||
import { transformObjectToOption } from './_shared';
|
||||
|
||||
export const themeLayoutModeLabels: Record<UnionKey.ThemeLayoutMode, string> = {
|
||||
vertical: '左侧菜单模式',
|
||||
horizontal: '顶部菜单模式',
|
||||
'vertical-mix': '左侧菜单混合模式',
|
||||
'horizontal-mix': '顶部菜单混合模式'
|
||||
};
|
||||
|
||||
export const themeLayoutModeOptions: Common.OptionWithKey<UnionKey.ThemeLayoutMode>[] = [
|
||||
{
|
||||
value: 'vertical',
|
||||
label: themeLayoutModeLabels.vertical
|
||||
},
|
||||
{
|
||||
value: 'horizontal',
|
||||
label: themeLayoutModeLabels.horizontal
|
||||
},
|
||||
{
|
||||
value: 'vertical-mix',
|
||||
label: themeLayoutModeLabels['vertical-mix']
|
||||
},
|
||||
{
|
||||
value: 'horizontal-mix',
|
||||
label: themeLayoutModeLabels['horizontal-mix']
|
||||
}
|
||||
];
|
||||
export const themeLayoutModeOptions = transformObjectToOption(themeLayoutModeLabels);
|
||||
|
||||
export const themeScrollModeLabels: Record<UnionKey.ThemeScrollMode, string> = {
|
||||
wrapper: '外层滚动',
|
||||
content: '主体滚动'
|
||||
};
|
||||
|
||||
export const themeScrollModeOptions: Common.OptionWithKey<UnionKey.ThemeScrollMode>[] = [
|
||||
{
|
||||
value: 'wrapper',
|
||||
label: themeScrollModeLabels.wrapper
|
||||
},
|
||||
{
|
||||
value: 'content',
|
||||
label: themeScrollModeLabels.content
|
||||
}
|
||||
];
|
||||
export const themeScrollModeOptions = transformObjectToOption(themeScrollModeLabels);
|
||||
|
||||
export const themeTabModeLabels: Record<UnionKey.ThemeTabMode, string> = {
|
||||
chrome: '谷歌风格',
|
||||
button: '按钮风格'
|
||||
};
|
||||
|
||||
export const themeTabModeOptions: Common.OptionWithKey<UnionKey.ThemeTabMode>[] = [
|
||||
{
|
||||
value: 'chrome',
|
||||
label: themeTabModeLabels.chrome
|
||||
},
|
||||
{
|
||||
value: 'button',
|
||||
label: themeTabModeLabels.button
|
||||
}
|
||||
];
|
||||
export const themeTabModeOptions = transformObjectToOption(themeTabModeLabels);
|
||||
|
||||
export const themeHorizontalMenuPositionLabels: Record<UnionKey.ThemeHorizontalMenuPosition, string> = {
|
||||
'flex-start': '居左',
|
||||
center: '居中',
|
||||
'flex-end': '居右'
|
||||
};
|
||||
|
||||
export const themeHorizontalMenuPositionOptions: Common.OptionWithKey<UnionKey.ThemeHorizontalMenuPosition>[] = [
|
||||
{
|
||||
value: 'flex-start',
|
||||
label: themeHorizontalMenuPositionLabels['flex-start']
|
||||
},
|
||||
{
|
||||
value: 'center',
|
||||
label: themeHorizontalMenuPositionLabels.center
|
||||
},
|
||||
{
|
||||
value: 'flex-end',
|
||||
label: themeHorizontalMenuPositionLabels['flex-end']
|
||||
}
|
||||
];
|
||||
export const themeHorizontalMenuPositionOptions = transformObjectToOption(themeHorizontalMenuPositionLabels);
|
||||
|
||||
export const themeAnimateModeLabels: Record<UnionKey.ThemeAnimateMode, string> = {
|
||||
'zoom-fade': '渐变',
|
||||
@@ -85,30 +35,4 @@ export const themeAnimateModeLabels: Record<UnionKey.ThemeAnimateMode, string> =
|
||||
'fade-bottom': '底部消退',
|
||||
'fade-scale': '缩放消退'
|
||||
};
|
||||
|
||||
export const themeAnimateModeOptions: Common.OptionWithKey<UnionKey.ThemeAnimateMode>[] = [
|
||||
{
|
||||
value: 'zoom-fade',
|
||||
label: themeAnimateModeLabels['zoom-fade']
|
||||
},
|
||||
{
|
||||
value: 'zoom-out',
|
||||
label: themeAnimateModeLabels['zoom-out']
|
||||
},
|
||||
{
|
||||
value: 'fade-slide',
|
||||
label: themeAnimateModeLabels['fade-slide']
|
||||
},
|
||||
{
|
||||
value: 'fade',
|
||||
label: themeAnimateModeLabels.fade
|
||||
},
|
||||
{
|
||||
value: 'fade-bottom',
|
||||
label: themeAnimateModeLabels['fade-bottom']
|
||||
},
|
||||
{
|
||||
value: 'fade-scale',
|
||||
label: themeAnimateModeLabels['fade-scale']
|
||||
}
|
||||
];
|
||||
export const themeAnimateModeOptions = transformObjectToOption(themeAnimateModeLabels);
|
||||
|
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
|
||||
};
|
||||
}
|
@@ -1,151 +0,0 @@
|
||||
import { ref, reactive } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
import type { DataTableBaseColumn, DataTableSelectionColumn, DataTableExpandColumn, PaginationProps } from 'naive-ui';
|
||||
import type { TableColumnGroup, InternalRowData } from 'naive-ui/es/data-table/src/interface';
|
||||
import { useLoadingEmpty } from '../common';
|
||||
|
||||
/**
|
||||
* 表格分页参数
|
||||
*/
|
||||
type PaginationParams = Pick<PaginationProps, 'page' | 'pageSize'>;
|
||||
|
||||
/**
|
||||
* 表格请求接口的参数
|
||||
*/
|
||||
type ApiParams = Record<string, unknown> & PaginationParams;
|
||||
|
||||
/**
|
||||
* 表格请求接口的结果
|
||||
* @description 这里用属性list来表示后端接口返回的表格数据
|
||||
*/
|
||||
type ApiData<TableData = Record<string, unknown>> = Record<string, unknown> & { list: TableData[] };
|
||||
|
||||
/**
|
||||
* 表格接口的请求函数
|
||||
*/
|
||||
type ApiFn<Params = ApiParams, TableData = Record<string, unknown>> = (
|
||||
params: Params
|
||||
) => Promise<Service.RequestResult<ApiData<TableData>>>;
|
||||
|
||||
/**
|
||||
* 表格接口请求后转换后的数据
|
||||
*/
|
||||
type TransformedTableData<TableData = Record<string, unknown>> = {
|
||||
data: TableData[];
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* 表格的列
|
||||
*/
|
||||
type DataTableColumn<T = InternalRowData> =
|
||||
| (Omit<TableColumnGroup<T>, 'key'> & { key: keyof T })
|
||||
| (Omit<DataTableBaseColumn<T>, 'key'> & { key: keyof T })
|
||||
| DataTableSelectionColumn<T>
|
||||
| DataTableExpandColumn<T>;
|
||||
|
||||
/**
|
||||
* 表格数据转换器
|
||||
* @description 将不同接口的表格数据转换成统一的类型
|
||||
*/
|
||||
type Transformer<TableData = Record<string, unknown>> = (
|
||||
apiData: ApiData<TableData>
|
||||
) => TransformedTableData<TableData>;
|
||||
|
||||
type TableParams<TableData = Record<string, unknown>, Params = ApiParams> = {
|
||||
apiFn: ApiFn<Params, TableData>;
|
||||
apiParams: Params;
|
||||
transformer: Transformer<TableData>;
|
||||
columns: DataTableColumn<TableData>[];
|
||||
pagination?: PaginationProps;
|
||||
};
|
||||
|
||||
export function useTable<TableData extends Record<string, unknown>, Params extends ApiParams>(
|
||||
params: TableParams<TableData, Params>,
|
||||
immediate = true
|
||||
) {
|
||||
const { loading, startLoading, endLoading, empty, setEmpty } = useLoadingEmpty();
|
||||
const data: Ref<TableData[]> = ref([]);
|
||||
|
||||
function updateData(update: TableData[]) {
|
||||
data.value = update;
|
||||
}
|
||||
|
||||
let dataSource: TableData[] = [];
|
||||
function setDataSource(source: TableData[]) {
|
||||
dataSource = source;
|
||||
}
|
||||
|
||||
function resetData() {
|
||||
data.value = dataSource;
|
||||
}
|
||||
|
||||
const columns = ref(params.columns) as Ref<DataTableColumn<TableData>[]>;
|
||||
|
||||
const pagination = reactive({
|
||||
...getPagination(params.pagination),
|
||||
onChange: (page: number) => {
|
||||
pagination.page = page;
|
||||
},
|
||||
onUpdatePageSize: (pageSize: number) => {
|
||||
pagination.pageSize = pageSize;
|
||||
pagination.page = 1;
|
||||
}
|
||||
}) as PaginationProps;
|
||||
|
||||
function updatePagination(update: Partial<PaginationProps>) {
|
||||
Object.assign(pagination, update);
|
||||
}
|
||||
|
||||
async function getData() {
|
||||
const apiParams: Params = { ...params.apiParams };
|
||||
apiParams.page = apiParams.page || pagination.page;
|
||||
apiParams.pageSize = apiParams.pageSize || pagination.pageSize;
|
||||
|
||||
startLoading();
|
||||
const { data: apiData } = await params.apiFn(apiParams);
|
||||
|
||||
if (apiData) {
|
||||
const transformedData = params.transformer(apiData);
|
||||
|
||||
updateData(transformedData.data);
|
||||
|
||||
setDataSource(transformedData.data);
|
||||
|
||||
setEmpty(transformedData.data.length === 0);
|
||||
|
||||
updatePagination({ page: transformedData.pageNum, pageSize: transformedData.pageSize });
|
||||
}
|
||||
|
||||
endLoading();
|
||||
}
|
||||
|
||||
if (immediate) {
|
||||
getData();
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
columns,
|
||||
loading,
|
||||
empty,
|
||||
pagination,
|
||||
getData,
|
||||
updateData,
|
||||
resetData
|
||||
};
|
||||
}
|
||||
|
||||
function getPagination(pagination?: Partial<PaginationProps>) {
|
||||
const defaultPagination: Partial<PaginationProps> = {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 15, 20, 25, 30]
|
||||
};
|
||||
Object.assign(defaultPagination, pagination);
|
||||
|
||||
return defaultPagination;
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<admin-layout
|
||||
:mode="mode"
|
||||
:is-mobile="isMobile"
|
||||
:scroll-mode="theme.scrollMode"
|
||||
:scroll-el-id="app.scrollElId"
|
||||
:full-content="app.contentFull"
|
||||
@@ -16,6 +17,7 @@
|
||||
:footer-visible="theme.footer.visible"
|
||||
:fixed-footer="theme.footer.fixed"
|
||||
:right-footer="theme.footer.right"
|
||||
@click-mobile-sider-mask="app.setSiderCollapse(true)"
|
||||
>
|
||||
<template #header>
|
||||
<global-header v-bind="headerProps" />
|
||||
@@ -46,7 +48,7 @@ defineOptions({ name: 'BasicLayout' });
|
||||
const app = useAppStore();
|
||||
const theme = useThemeStore();
|
||||
|
||||
const { mode, headerProps, siderVisible, siderWidth, siderCollapsedWidth } = useBasicLayout();
|
||||
const { mode, isMobile, headerProps, siderVisible, siderWidth, siderCollapsedWidth } = useBasicLayout();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
@@ -9,7 +9,7 @@
|
||||
v-if="theme.header.crumb.showIcon"
|
||||
class="inline-block align-text-bottom mr-4px text-16px"
|
||||
/>
|
||||
<span>{{ breadcrumb.i18nTitle ? t(breadcrumb.i18nTitle) : breadcrumb.label }}</span>
|
||||
<span>{{ breadcrumb.label }}</span>
|
||||
</span>
|
||||
</n-dropdown>
|
||||
<template v-else>
|
||||
@@ -19,9 +19,9 @@
|
||||
class="inline-block align-text-bottom mr-4px text-16px"
|
||||
:class="{ 'text-#BBBBBB': theme.header.inverted }"
|
||||
/>
|
||||
<span :class="{ 'text-#BBBBBB': theme.header.inverted }">{{
|
||||
breadcrumb.i18nTitle ? t(breadcrumb.i18nTitle) : breadcrumb.label
|
||||
}}</span>
|
||||
<span :class="{ 'text-#BBBBBB': theme.header.inverted }">
|
||||
{{ breadcrumb.label }}
|
||||
</span>
|
||||
</template>
|
||||
</n-breadcrumb-item>
|
||||
</template>
|
||||
@@ -35,7 +35,7 @@ import { routePath } from '@/router';
|
||||
import { useRouteStore, useThemeStore } from '@/store';
|
||||
import { useRouterPush } from '@/composables';
|
||||
import { getBreadcrumbByRouteKey } from '@/utils';
|
||||
import { t } from '@/locales';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({ name: 'GlobalBreadcrumb' });
|
||||
|
||||
@@ -45,7 +45,13 @@ const routeStore = useRouteStore();
|
||||
const { routerPush } = useRouterPush();
|
||||
|
||||
const breadcrumbs = computed(() =>
|
||||
getBreadcrumbByRouteKey(route.name as string, routeStore.menus as App.GlobalMenuOption[], routePath('root'))
|
||||
getBreadcrumbByRouteKey(route.name as string, routeStore.menus as App.GlobalMenuOption[], routePath('root')).map(
|
||||
item => ({
|
||||
...item,
|
||||
label: item.i18nTitle ? $t(item.i18nTitle) : item.label,
|
||||
options: item.options?.map(oItem => ({ ...oItem, label: oItem.i18nTitle ? $t(oItem.i18nTitle) : oItem.label }))
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
function dropdownSelect(key: string) {
|
||||
|
@@ -1,6 +1,11 @@
|
||||
<template>
|
||||
<hover-container class="w-40px" :inverted="theme.header.inverted" tooltip-content="主题模式">
|
||||
<dark-mode-switch :dark="theme.darkMode" class="wh-full" @update:dark="theme.setDarkMode" />
|
||||
<dark-mode-switch
|
||||
:dark="theme.darkMode"
|
||||
:customize-transition="theme.isCustomizeDarkModeTransition"
|
||||
class="wh-full"
|
||||
@update:dark="theme.setDarkMode"
|
||||
/>
|
||||
</hover-container>
|
||||
</template>
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<hover-container class="w-40px h-full">
|
||||
<hover-container class="w-40px h-full" :inverted="theme.header.inverted">
|
||||
<n-dropdown :options="options" trigger="hover" :value="language" @select="handleSelect">
|
||||
<icon-cil:language class="text-18px outline-transparent" />
|
||||
</n-dropdown>
|
||||
@@ -9,11 +9,13 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useThemeStore } from '@/store';
|
||||
import { localStg } from '@/utils';
|
||||
|
||||
const theme = useThemeStore();
|
||||
const { locale } = useI18n();
|
||||
|
||||
const language = ref<I18nType.langType>(localStg.get('lang') || 'zh-CN');
|
||||
const language = ref<I18nType.LangType>(localStg.get('lang') || 'zh-CN');
|
||||
const options = [
|
||||
{
|
||||
label: '中文',
|
||||
@@ -22,12 +24,16 @@ const options = [
|
||||
{
|
||||
label: 'English',
|
||||
key: 'en'
|
||||
},
|
||||
{
|
||||
label: 'ភាសាខ្មែរ',
|
||||
key: 'km-KH'
|
||||
}
|
||||
];
|
||||
const handleSelect = (key: string) => {
|
||||
language.value = key as I18nType.langType;
|
||||
language.value = key as I18nType.LangType;
|
||||
locale.value = key;
|
||||
localStg.set('lang', key as I18nType.langType);
|
||||
localStg.set('lang', key as I18nType.LangType);
|
||||
};
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
@@ -2,14 +2,14 @@
|
||||
<router-link :to="routeHomePath" class="flex-center w-full nowrap-hidden">
|
||||
<system-logo class="text-32px text-primary" />
|
||||
<h2 v-show="showTitle" class="pl-8px text-16px font-bold text-primary transition duration-300 ease-in-out">
|
||||
{{ t('message.system.title') }}
|
||||
{{ $t('system.title') }}
|
||||
</h2>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { routePath } from '@/router';
|
||||
import { t } from '@/locales';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({ name: 'GlobalLogo' });
|
||||
|
||||
|
@@ -22,6 +22,9 @@ defineOptions({ name: 'SearchFooter' });
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.icon {
|
||||
box-shadow: inset 0 -2px #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px #1e235a66;
|
||||
box-shadow:
|
||||
inset 0 -2px #cdcde6,
|
||||
inset 0 0 1px 1px #fff,
|
||||
0 1px 2px 1px #1e235a66;
|
||||
}
|
||||
</style>
|
||||
|
@@ -6,7 +6,7 @@
|
||||
>
|
||||
<component :is="icon" :class="[isMini ? 'text-16px' : 'text-20px']" />
|
||||
<p
|
||||
class="text-12px overflow-hidden transition-height duration-300 ease-in-out"
|
||||
class="w-full text-center ellipsis-text text-12px transition-height duration-300 ease-in-out"
|
||||
:class="[isMini ? 'h-0 pt-0' : 'h-24px pt-4px']"
|
||||
>
|
||||
{{ label }}
|
||||
|
@@ -9,7 +9,7 @@
|
||||
:style="{ width: showDrawer ? theme.sider.mixChildMenuWidth + 'px' : '0px' }"
|
||||
>
|
||||
<header class="header-height flex-y-center justify-between" :style="{ height: theme.header.height + 'px' }">
|
||||
<h2 class="text-primary pl-8px text-16px font-bold">{{ title }}</h2>
|
||||
<h2 class="text-primary pl-8px text-16px font-bold">{{ $t('system.title') }}</h2>
|
||||
<div class="px-8px text-16px text-gray-600 cursor-pointer" @click="app.toggleMixSiderFixed">
|
||||
<icon-mdi-pin-off v-if="app.mixSiderFixed" />
|
||||
<icon-mdi-pin v-else />
|
||||
@@ -35,8 +35,9 @@ import { computed, ref, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import type { MenuOption } from 'naive-ui';
|
||||
import { useAppStore, useThemeStore } from '@/store';
|
||||
import { useAppInfo, useRouterPush } from '@/composables';
|
||||
import { useRouterPush } from '@/composables';
|
||||
import { getActiveKeyPathsOfMenus } from '@/utils';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({ name: 'MixMenuDrawer' });
|
||||
|
||||
@@ -53,7 +54,6 @@ const route = useRoute();
|
||||
const app = useAppStore();
|
||||
const theme = useThemeStore();
|
||||
const { routerPush } = useRouterPush();
|
||||
const { title } = useAppInfo();
|
||||
|
||||
const showDrawer = computed(() => (props.visible && props.menus.length) || app.mixSiderFixed);
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<dark-mode-container class="flex h-full" :inverted="theme.sider.inverted" @mouseleave="resetFirstDegreeMenus">
|
||||
<div class="flex-1 flex-col-stretch h-full">
|
||||
<div class="flex-1-hidden flex-col-stretch h-full">
|
||||
<global-logo :show-title="false" :style="{ height: theme.header.height + 'px' }" />
|
||||
<n-scrollbar class="flex-1-hidden">
|
||||
<mix-menu-detail
|
||||
@@ -28,7 +28,7 @@ import { useRouterPush } from '@/composables';
|
||||
import { useBoolean } from '@/hooks';
|
||||
import { translateMenuLabel } from '@/utils';
|
||||
import { GlobalLogo } from '@/layouts/common';
|
||||
import { t } from '@/locales';
|
||||
import { $t } from '@/locales';
|
||||
import { MixMenuCollapse, MixMenuDetail, MixMenuDrawer } from './components';
|
||||
|
||||
defineOptions({ name: 'VerticalMixSider' });
|
||||
@@ -53,7 +53,7 @@ const firstDegreeMenus = computed(() =>
|
||||
|
||||
return {
|
||||
routeName,
|
||||
label: i18nTitle ? t(i18nTitle) : label,
|
||||
label: i18nTitle ? $t(i18nTitle) : label,
|
||||
icon,
|
||||
hasChildren
|
||||
};
|
||||
|
@@ -6,26 +6,21 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useAppStore, useRouteStore } from '@/store';
|
||||
import { useRouteStore } from '@/store';
|
||||
import { useLoading } from '@/hooks';
|
||||
|
||||
defineOptions({ name: 'ReloadButton' });
|
||||
|
||||
const app = useAppStore();
|
||||
const routeStore = useRouteStore();
|
||||
const { reCacheRoute } = useRouteStore();
|
||||
const route = useRoute();
|
||||
const { loading, startLoading, endLoading } = useLoading();
|
||||
|
||||
function handleRefresh() {
|
||||
const isCached = routeStore.cacheRoutes.includes(String(route.name));
|
||||
if (isCached) {
|
||||
routeStore.removeCacheRoute(route.name as AuthRoute.AllRouteKey);
|
||||
}
|
||||
async function handleRefresh() {
|
||||
startLoading();
|
||||
app.reloadPage();
|
||||
|
||||
await reCacheRoute(route.name as AuthRoute.AllRouteKey);
|
||||
|
||||
setTimeout(() => {
|
||||
if (isCached) {
|
||||
routeStore.addCacheRoute(route.name as AuthRoute.AllRouteKey);
|
||||
}
|
||||
endLoading();
|
||||
}, 1000);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div ref="tabRef" class="flex h-full pr-18px" :class="[isChromeMode ? 'items-end' : 'items-center gap-12px']">
|
||||
<AdminTab
|
||||
<PageTab
|
||||
v-for="item in tab.tabs"
|
||||
:key="item.fullPath"
|
||||
:mode="theme.tab.mode"
|
||||
@@ -19,8 +19,8 @@
|
||||
class="inline-block align-text-bottom text-16px"
|
||||
/>
|
||||
</template>
|
||||
{{ item.meta.i18nTitle ? t(item.meta.i18nTitle) : item.meta.title }}
|
||||
</AdminTab>
|
||||
{{ item.meta.i18nTitle ? $t(item.meta.i18nTitle) : item.meta.title }}
|
||||
</PageTab>
|
||||
</div>
|
||||
<context-menu
|
||||
:visible="dropdown.visible"
|
||||
@@ -34,9 +34,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { AdminTab } from '@soybeanjs/vue-materials';
|
||||
import { PageTab } from '@soybeanjs/vue-materials';
|
||||
import { useTabStore, useThemeStore } from '@/store';
|
||||
import { t } from '@/locales';
|
||||
import { $t } from '@/locales';
|
||||
import { ContextMenu } from './components';
|
||||
|
||||
defineOptions({ name: 'TabDetail' });
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<n-divider title-placement="center">主题模式</n-divider>
|
||||
<n-divider title-placement="center">{{ $t('layout.settingDrawer.themeModeTitle') }}</n-divider>
|
||||
<n-space vertical size="large">
|
||||
<setting-menu label="深色主题">
|
||||
<setting-menu :label="$t('layout.settingDrawer.darkMode')">
|
||||
<n-switch :value="theme.darkMode" @update:value="theme.setDarkMode">
|
||||
<template #checked>
|
||||
<icon-mdi-white-balance-sunny class="text-14px text-white" />
|
||||
@@ -11,7 +11,7 @@
|
||||
</template>
|
||||
</n-switch>
|
||||
</setting-menu>
|
||||
<setting-menu label="跟随系统">
|
||||
<setting-menu :label="$t('layout.settingDrawer.followSystemTheme')">
|
||||
<n-switch :value="theme.followSystemTheme" @update:value="theme.setFollowSystemTheme">
|
||||
<template #checked>
|
||||
<icon-ic-baseline-do-not-disturb class="text-14px text-white" />
|
||||
@@ -21,13 +21,23 @@
|
||||
</template>
|
||||
</n-switch>
|
||||
</setting-menu>
|
||||
<setting-menu label="侧边栏深色">
|
||||
<setting-menu :label="$t('layout.settingDrawer.isCustomizeDarkModeTransition')">
|
||||
<n-switch :value="theme.isCustomizeDarkModeTransition" @update:value="theme.setIsCustomizeDarkModeTransition">
|
||||
<template #checked>
|
||||
<icon-ic-baseline-do-not-disturb class="text-14px text-white" />
|
||||
</template>
|
||||
<template #unchecked>
|
||||
<icon-ic-round-hdr-auto class="text-14px text-white" />
|
||||
</template>
|
||||
</n-switch>
|
||||
</setting-menu>
|
||||
<setting-menu :label="$t('layout.settingDrawer.sider.inverted')">
|
||||
<n-switch :value="theme.sider.inverted" @update:value="theme.setSiderInverted" />
|
||||
</setting-menu>
|
||||
<setting-menu label="头部深色">
|
||||
<setting-menu :label="$t('layout.settingDrawer.header.inverted')">
|
||||
<n-switch :value="theme.header.inverted" @update:value="theme.setHeaderInverted" />
|
||||
</setting-menu>
|
||||
<setting-menu label="底部深色">
|
||||
<setting-menu :label="$t('layout.settingDrawer.footer.inverted')">
|
||||
<n-switch :value="theme.footer.inverted" @update:value="theme.setFooterInverted" />
|
||||
</setting-menu>
|
||||
</n-space>
|
||||
@@ -35,6 +45,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useThemeStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
import SettingMenu from '../setting-menu/index.vue';
|
||||
|
||||
defineOptions({ name: 'DarkMode' });
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<n-divider title-placement="center">布局模式</n-divider>
|
||||
<n-divider title-placement="center">{{ $t('layout.settingDrawer.layoutModelTitle') }}</n-divider>
|
||||
<n-space justify="space-around" :wrap="true" :size="24" class="px-12px">
|
||||
<layout-card
|
||||
v-for="item in theme.layout.modeList"
|
||||
@@ -43,6 +43,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useThemeStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
import { LayoutCard } from './components';
|
||||
|
||||
defineOptions({ name: 'LayoutMode' });
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<n-divider title-placement="center">界面功能</n-divider>
|
||||
<n-divider title-placement="center">{{ $t('layout.settingDrawer.pageFunctionsTitle') }}</n-divider>
|
||||
<n-space vertical size="large">
|
||||
<setting-menu label="滚动模式">
|
||||
<setting-menu :label="$t('layout.settingDrawer.scrollMode')">
|
||||
<n-select
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -10,10 +10,10 @@
|
||||
@update:value="theme.setScrollMode"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="固定头部和多页签">
|
||||
<setting-menu :label="$t('layout.settingDrawer.fixedHeaderAndTab')">
|
||||
<n-switch :value="theme.fixedHeaderAndTab" @update:value="theme.setIsFixedHeaderAndTab" />
|
||||
</setting-menu>
|
||||
<setting-menu label="顶部菜单位置">
|
||||
<setting-menu :label="$t('layout.settingDrawer.menu.horizontalPosition')">
|
||||
<n-select
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -22,7 +22,7 @@
|
||||
@update:value="theme.setHorizontalMenuPosition"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="头部高度">
|
||||
<setting-menu :label="$t('layout.settingDrawer.header.height')">
|
||||
<n-input-number
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -31,7 +31,7 @@
|
||||
@update:value="theme.setHeaderHeight"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="多页签高度">
|
||||
<setting-menu :label="$t('layout.settingDrawer.tab.height')">
|
||||
<n-input-number
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -40,10 +40,10 @@
|
||||
@update:value="theme.setTabHeight"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="多页签缓存">
|
||||
<setting-menu :label="$t('layout.settingDrawer.tab.isCache')">
|
||||
<n-switch :value="theme.tab.isCache" @update:value="theme.setTabIsCache" />
|
||||
</setting-menu>
|
||||
<setting-menu label="侧边栏展开宽度">
|
||||
<setting-menu :label="$t('layout.settingDrawer.sider.width')">
|
||||
<n-input-number
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -52,7 +52,7 @@
|
||||
@update:value="theme.setSiderWidth"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="左侧混合侧边栏展开宽度">
|
||||
<setting-menu :label="$t('layout.settingDrawer.sider.mixWidth')">
|
||||
<n-input-number
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -61,13 +61,13 @@
|
||||
@update:value="theme.setMixSiderWidth"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="显示底部">
|
||||
<setting-menu :label="$t('layout.settingDrawer.footer.visible')">
|
||||
<n-switch :value="theme.footer.visible" @update:value="theme.setFooterVisible" />
|
||||
</setting-menu>
|
||||
<setting-menu label="固定底部">
|
||||
<setting-menu :label="$t('layout.settingDrawer.footer.fixed')">
|
||||
<n-switch :value="theme.footer.fixed" @update:value="theme.setFooterIsFixed" />
|
||||
</setting-menu>
|
||||
<setting-menu label="底部居右">
|
||||
<setting-menu :label="$t('layout.settingDrawer.footer.right')">
|
||||
<n-switch :value="theme.footer.right" @update:value="theme.setFooterIsRight" />
|
||||
</setting-menu>
|
||||
</n-space>
|
||||
@@ -75,6 +75,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useThemeStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
import SettingMenu from '../setting-menu/index.vue';
|
||||
|
||||
defineOptions({ name: 'PageFunc' });
|
||||
|
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<n-divider title-placement="center">界面显示</n-divider>
|
||||
<n-divider title-placement="center">{{ $t('layout.settingDrawer.pageViewTitle') }}</n-divider>
|
||||
<n-space vertical size="large">
|
||||
<setting-menu label="面包屑">
|
||||
<setting-menu :label="$t('layout.settingDrawer.header.crumb.visible')">
|
||||
<n-switch :value="theme.header.crumb.visible" @update:value="theme.setHeaderCrumbVisible" />
|
||||
</setting-menu>
|
||||
<setting-menu label="面包屑图标">
|
||||
<setting-menu :label="$t('layout.settingDrawer.header.crumb.icon')">
|
||||
<n-switch :value="theme.header.crumb.showIcon" @update:value="theme.setHeaderCrumbIconVisible" />
|
||||
</setting-menu>
|
||||
<setting-menu label="多页签">
|
||||
<setting-menu :label="$t('layout.settingDrawer.tab.visible')">
|
||||
<n-switch :value="theme.tab.visible" @update:value="theme.setTabVisible" />
|
||||
</setting-menu>
|
||||
<setting-menu label="多页签风格">
|
||||
<setting-menu :label="$t('layout.settingDrawer.tab.modeList.mode')">
|
||||
<n-select
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -19,10 +19,10 @@
|
||||
@update:value="theme.setTabMode"
|
||||
/>
|
||||
</setting-menu>
|
||||
<setting-menu label="页面切换动画">
|
||||
<setting-menu :label="$t('layout.settingDrawer.page.animate')">
|
||||
<n-switch :value="theme.page.animate" @update:value="theme.setPageIsAnimate" />
|
||||
</setting-menu>
|
||||
<setting-menu label="页面切换动画类型">
|
||||
<setting-menu :label="$t('layout.settingDrawer.page.animateMode')">
|
||||
<n-select
|
||||
class="w-120px"
|
||||
size="small"
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useThemeStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
import SettingMenu from '../setting-menu/index.vue';
|
||||
|
||||
defineOptions({ name: 'PageView' });
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<n-divider title-placement="center">系统主题</n-divider>
|
||||
<n-divider title-placement="center">{{ $t('layout.settingDrawer.systemThemeTitle') }}</n-divider>
|
||||
<n-grid :cols="8" :x-gap="8" :y-gap="12">
|
||||
<n-grid-item v-for="color in theme.themeColorList" :key="color" class="flex-x-center">
|
||||
<color-checkbox :color="color" :checked="color === theme.themeColor" @click="theme.setThemeColor(color)" />
|
||||
@@ -7,7 +7,9 @@
|
||||
</n-grid>
|
||||
<n-space :vertical="true" class="pt-12px">
|
||||
<n-color-picker :value="theme.themeColor" :show-alpha="false" @update-value="theme.setThemeColor" />
|
||||
<n-button :block="true" :type="otherColorBtnType" @click="openModal">更多颜色</n-button>
|
||||
<n-button :block="true" :type="otherColorBtnType" @click="openModal">
|
||||
{{ $t('layout.settingDrawer.systemTheme.moreColors') }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
<color-modal :visible="visible" @close="closeModal" />
|
||||
</template>
|
||||
@@ -17,6 +19,7 @@ import { computed } from 'vue';
|
||||
import { isInTraditionColors } from '@/settings';
|
||||
import { useThemeStore } from '@/store';
|
||||
import { useBoolean } from '@/hooks';
|
||||
import { $t } from '@/locales';
|
||||
import { ColorCheckbox, ColorModal } from './components';
|
||||
|
||||
defineOptions({ name: 'ThemeColorSelect' });
|
||||
|
@@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<n-divider title-placement="center">主题配置</n-divider>
|
||||
<n-divider title-placement="center">{{ $t('layout.settingDrawer.themeConfiguration.title') }}</n-divider>
|
||||
<textarea id="themeConfigCopyTarget" v-model="dataClipboardText" class="absolute opacity-0" />
|
||||
<n-space vertical>
|
||||
<div ref="copyRef" data-clipboard-target="#themeConfigCopyTarget">
|
||||
<n-button type="primary" :block="true">拷贝当前配置</n-button>
|
||||
<n-button type="primary" :block="true">{{ $t('layout.settingDrawer.themeConfiguration.copy') }}</n-button>
|
||||
</div>
|
||||
<n-button type="warning" :block="true" @click="handleResetConfig">重置当前配置</n-button>
|
||||
<n-button type="warning" :block="true" @click="handleResetConfig">
|
||||
{{ $t('layout.settingDrawer.themeConfiguration.reset') }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
@@ -13,6 +15,7 @@
|
||||
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
import Clipboard from 'clipboard';
|
||||
import { useThemeStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({ name: 'ThemeConfig' });
|
||||
|
||||
@@ -28,7 +31,7 @@ function getClipboardText() {
|
||||
|
||||
function handleResetConfig() {
|
||||
theme.resetThemeStore();
|
||||
window.$message?.success('已重置配置,请重新拷贝!');
|
||||
window.$message?.success($t('layout.settingDrawer.themeConfiguration.resetSuccess'));
|
||||
}
|
||||
|
||||
function clipboardEventListener() {
|
||||
@@ -36,9 +39,9 @@ function clipboardEventListener() {
|
||||
const copy = new Clipboard(copyRef.value);
|
||||
copy.on('success', () => {
|
||||
window.$dialog?.success({
|
||||
title: '操作成功',
|
||||
content: '复制成功,请替换 src/settings/theme.json的内容!',
|
||||
positiveText: '确定'
|
||||
title: $t('layout.settingDrawer.themeConfiguration.operateSuccess'),
|
||||
content: $t('layout.settingDrawer.themeConfiguration.copySuccess'),
|
||||
positiveText: $t('layout.settingDrawer.themeConfiguration.confirmCopy')
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<n-drawer :show="app.settingDrawerVisible" display-directive="show" :width="330" @mask-click="app.closeSettingDrawer">
|
||||
<n-drawer-content title="主题配置" :native-scrollbar="false">
|
||||
<n-drawer-content :title="$t('layout.settingDrawer.title')" :native-scrollbar="false">
|
||||
<dark-mode />
|
||||
<layout-mode />
|
||||
<theme-color-select />
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store';
|
||||
import { $t } from '@/locales';
|
||||
import { DarkMode, DrawerButton, LayoutMode, PageFunc, PageView, ThemeColorSelect, ThemeConfig } from './components';
|
||||
|
||||
defineOptions({ name: 'SettingDrawer' });
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import type { App } from 'vue';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
import { localStg } from '@/utils';
|
||||
import messages from './lang';
|
||||
import type { LocaleKey } from './lang';
|
||||
import type { TranslateOptions } from 'vue-i18n';
|
||||
import { localStg } from '@/utils/storage';
|
||||
import messages from './locale';
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: localStg.get('lang') || 'zh-CN',
|
||||
@@ -15,10 +15,20 @@ export function setupI18n(app: App) {
|
||||
app.use(i18n);
|
||||
}
|
||||
|
||||
export function t(key: string) {
|
||||
return i18n.global.t(key);
|
||||
interface T {
|
||||
(key: I18nType.I18nKey): string;
|
||||
(key: I18nType.I18nKey, plural: number, options?: TranslateOptions<I18nType.LangType>): string;
|
||||
(key: I18nType.I18nKey, defaultMsg: string, options?: TranslateOptions<I18nType.I18nKey>): string;
|
||||
(key: I18nType.I18nKey, list: unknown[], options?: TranslateOptions<I18nType.I18nKey>): string;
|
||||
(key: I18nType.I18nKey, list: unknown[], plural: number): string;
|
||||
(key: I18nType.I18nKey, list: unknown[], defaultMsg: string): string;
|
||||
(key: I18nType.I18nKey, named: Record<string, unknown>, options?: TranslateOptions<I18nType.LangType>): string;
|
||||
(key: I18nType.I18nKey, named: Record<string, unknown>, plural: number): string;
|
||||
(key: I18nType.I18nKey, named: Record<string, unknown>, defaultMsg: string): string;
|
||||
}
|
||||
|
||||
export function setLocale(locale: LocaleKey) {
|
||||
export const $t = i18n.global.t as T;
|
||||
|
||||
export function setLocale(locale: I18nType.LangType) {
|
||||
i18n.global.locale.value = locale;
|
||||
}
|
||||
|
@@ -1,83 +1,217 @@
|
||||
import type { LocaleMessages } from 'vue-i18n';
|
||||
|
||||
const locale: LocaleMessages<I18nType.Schema> = {
|
||||
message: {
|
||||
system: {
|
||||
title: 'SoybeanAdmin'
|
||||
const locale: I18nType.Schema = {
|
||||
system: {
|
||||
title: 'SoybeanAdmin'
|
||||
},
|
||||
common: {
|
||||
add: 'Add',
|
||||
addSuccess: 'Add Success',
|
||||
edit: 'Edit',
|
||||
editSuccess: 'Edit Success',
|
||||
delete: 'Delete',
|
||||
deleteSuccess: 'Delete Success',
|
||||
batchDelete: 'Batch Delete',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
pleaseCheckValue: 'Please check the value is valid',
|
||||
action: 'Action'
|
||||
},
|
||||
routes: {
|
||||
dashboard: {
|
||||
_value: 'Dashboard',
|
||||
analysis: 'Analysis',
|
||||
workbench: 'Workbench'
|
||||
},
|
||||
routes: {
|
||||
dashboard: {
|
||||
dashboard: 'Dashboard',
|
||||
analysis: 'Analysis',
|
||||
workbench: 'Workbench'
|
||||
document: {
|
||||
_value: 'Document',
|
||||
vue: 'Vue Document',
|
||||
vite: 'Vite Document',
|
||||
naive: 'NaiveUI Document',
|
||||
project: 'Project Document',
|
||||
'project-link': 'Project Document(href)'
|
||||
},
|
||||
component: {
|
||||
_value: 'Component',
|
||||
button: 'Button',
|
||||
card: 'Card',
|
||||
table: 'Table'
|
||||
},
|
||||
plugin: {
|
||||
_value: 'Plugin',
|
||||
charts: {
|
||||
_value: 'Chart',
|
||||
echarts: 'ECharts',
|
||||
antv: 'AntV'
|
||||
},
|
||||
document: {
|
||||
_value: 'Document',
|
||||
vue: 'Vue Document',
|
||||
vite: 'Vite Document',
|
||||
naive: 'NaiveUI Document',
|
||||
project: 'Project Document',
|
||||
'project-link': 'Project Document(href)'
|
||||
copy: 'Copy',
|
||||
editor: {
|
||||
_value: 'Editor',
|
||||
quill: 'Quill',
|
||||
markdown: 'Markdown'
|
||||
},
|
||||
component: {
|
||||
_value: 'Component',
|
||||
button: 'Button',
|
||||
card: 'Card',
|
||||
table: 'Table'
|
||||
icon: 'Icon',
|
||||
map: 'Map',
|
||||
print: 'Print',
|
||||
swiper: 'Swiper',
|
||||
video: 'Video'
|
||||
},
|
||||
'auth-demo': {
|
||||
_value: 'Auth Demo',
|
||||
permission: 'Toggle Permission',
|
||||
super: 'Super Auth'
|
||||
},
|
||||
function: {
|
||||
_value: 'Function',
|
||||
tab: 'System Tab'
|
||||
},
|
||||
exception: {
|
||||
_value: 'Exception',
|
||||
403: '403',
|
||||
404: '404',
|
||||
500: '500'
|
||||
},
|
||||
'multi-menu': {
|
||||
_value: 'Multi Degree Menu',
|
||||
first: {
|
||||
_value: 'First Degree',
|
||||
second: 'Second Degree',
|
||||
'second-new': {
|
||||
_value: 'Second Degree With Children',
|
||||
third: 'Third Degree'
|
||||
}
|
||||
}
|
||||
},
|
||||
management: {
|
||||
_value: 'System Management',
|
||||
auth: 'Auth',
|
||||
role: 'Role',
|
||||
route: 'Route',
|
||||
user: 'User'
|
||||
},
|
||||
about: 'About'
|
||||
},
|
||||
layout: {
|
||||
settingDrawer: {
|
||||
title: 'Theme configuration',
|
||||
themeModeTitle: 'Theme mode',
|
||||
darkMode: 'Dark mode',
|
||||
layoutModelTitle: 'Layout mode',
|
||||
systemThemeTitle: 'System theme',
|
||||
pageFunctionsTitle: 'Page functions',
|
||||
pageViewTitle: 'Page view',
|
||||
followSystemTheme: 'Follow the system',
|
||||
isCustomizeDarkModeTransition: 'Custom dark theme animation transition',
|
||||
scrollMode: 'scrollMode',
|
||||
scrollModeList: {
|
||||
wrapper: 'Outer layer scroll',
|
||||
content: 'Main body scroll'
|
||||
},
|
||||
plugin: {
|
||||
_value: 'Plugin',
|
||||
charts: {
|
||||
_value: 'Chart',
|
||||
echarts: 'ECharts',
|
||||
antv: 'AntV'
|
||||
},
|
||||
copy: 'Copy',
|
||||
editor: {
|
||||
_value: 'Editor',
|
||||
quill: 'Quill',
|
||||
markdown: 'Markdown'
|
||||
},
|
||||
icon: 'Icon',
|
||||
map: 'Map',
|
||||
print: 'Print',
|
||||
swiper: 'Swiper',
|
||||
video: 'Video'
|
||||
},
|
||||
'auth-demo': {
|
||||
_value: 'Auth Demo',
|
||||
permission: 'Toggle Permission',
|
||||
super: 'Super Auth'
|
||||
},
|
||||
function: {
|
||||
_value: 'Function',
|
||||
tab: 'System Tab'
|
||||
},
|
||||
exception: {
|
||||
_value: 'Exception',
|
||||
403: '403',
|
||||
404: '404',
|
||||
500: '500'
|
||||
},
|
||||
'multi-menu': {
|
||||
_value: 'Multi Degree Menu',
|
||||
first: {
|
||||
_value: 'First Degree',
|
||||
second: 'Second Degree',
|
||||
'second-new': {
|
||||
_value: 'Second Degree With Children',
|
||||
third: 'Third Degree'
|
||||
}
|
||||
fixedHeaderAndTab: 'Fixed header and multiple tabs',
|
||||
header: {
|
||||
inverted: 'darkHead',
|
||||
height: 'Head Height',
|
||||
crumb: {
|
||||
visible: 'Crumb',
|
||||
icon: 'Crumb icon'
|
||||
}
|
||||
},
|
||||
management: {
|
||||
_value: 'System Management',
|
||||
auth: 'Auth',
|
||||
role: 'Role',
|
||||
route: 'Route',
|
||||
user: 'User'
|
||||
tab: {
|
||||
visible: 'Multi-page tab',
|
||||
height: 'Multiple tab height',
|
||||
modeList: {
|
||||
mode: 'Multi-tab style',
|
||||
chrome: 'Google style',
|
||||
button: 'Button style'
|
||||
},
|
||||
isCache: 'Multiple tab caching'
|
||||
},
|
||||
about: 'About'
|
||||
sider: {
|
||||
inverted: 'Dark sidebar',
|
||||
width: 'Sidebar expanded width',
|
||||
mixWidth: 'Left hybrid sidebar expanded width'
|
||||
},
|
||||
menu: {
|
||||
horizontalPosition: 'Top menu position',
|
||||
horizontalPositionList: {
|
||||
flexStart: 'Right',
|
||||
center: 'center',
|
||||
flexEnd: 'Left'
|
||||
}
|
||||
},
|
||||
footer: {
|
||||
inverted: 'Dark bottom',
|
||||
visible: 'Show bottom',
|
||||
fixed: 'Fixed bottom',
|
||||
right: 'Bottom to the right'
|
||||
},
|
||||
page: {
|
||||
animate: 'switch animation',
|
||||
animateMode: 'switch animation type',
|
||||
animateModeList: {
|
||||
zoomFade: 'Gradual change',
|
||||
zoomOut: 'Flash',
|
||||
fadeSlide: 'Slide',
|
||||
fade: 'Fade away',
|
||||
fadeBottom: 'Bottom fade',
|
||||
fadeScale: 'Resizing fade away'
|
||||
}
|
||||
},
|
||||
systemTheme: {
|
||||
moreColors: 'More colors'
|
||||
},
|
||||
themeConfiguration: {
|
||||
title: 'Theme configuration',
|
||||
copy: 'Copy the current configuration',
|
||||
reset: 'Reset the current configuration',
|
||||
resetSuccess: 'The configuration has been reset, please copy it again!',
|
||||
operateSuccess: 'Successful operation',
|
||||
copySuccess: 'Copy success, please replace the content of src/settings/theme.json!',
|
||||
confirmCopy: 'Confirm'
|
||||
}
|
||||
}
|
||||
},
|
||||
page: {
|
||||
login: {
|
||||
common: {
|
||||
userNamePlaceholder: 'Please enter user name',
|
||||
phonePlaceholder: 'Please enter phone number',
|
||||
codePlaceholder: 'Please enter verification code',
|
||||
passwordPlaceholder: 'Please enter password',
|
||||
confirmPasswordPlaceholder: 'Please enter password again',
|
||||
codeLogin: 'Verification code login',
|
||||
confirm: 'Confirm',
|
||||
back: 'Back',
|
||||
validateSuccess: 'Verification passed',
|
||||
loginSuccess: 'Login success',
|
||||
welcomeBack: 'Welcome back, {userName}!'
|
||||
},
|
||||
pwdLogin: {
|
||||
title: 'Password Login',
|
||||
rememberMe: 'Remember me',
|
||||
forgetPassword: 'Forget password?',
|
||||
register: 'Register account',
|
||||
otherAccountLogin: 'Other Account Login',
|
||||
otherLoginMode: 'Other Login Mode',
|
||||
superAdmin: 'Super Administrator',
|
||||
admin: 'Administrator',
|
||||
user: 'Ordinary User'
|
||||
},
|
||||
codeLogin: {
|
||||
title: 'Verification Code Login',
|
||||
getCode: 'Get verification code',
|
||||
imageCodePlaceholder: 'Please enter image verification code'
|
||||
},
|
||||
register: {
|
||||
title: 'Register Account',
|
||||
agreement: 'I have read and agree to',
|
||||
protocol: '《User Agreement》',
|
||||
policy: '《Privacy Policy》'
|
||||
},
|
||||
resetPwd: {
|
||||
title: 'Reset Password'
|
||||
},
|
||||
bindWeChat: {
|
||||
title: 'Bind WeChat'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -1,11 +0,0 @@
|
||||
import zhCN from './zh-cn';
|
||||
import en from './en';
|
||||
|
||||
const locales = {
|
||||
'zh-CN': zhCN,
|
||||
en
|
||||
};
|
||||
|
||||
export type LocaleKey = keyof typeof locales;
|
||||
|
||||
export default locales;
|
219
src/locales/lang/km-KH.ts
Normal file
@@ -0,0 +1,219 @@
|
||||
const locale: I18nType.Schema = {
|
||||
system: {
|
||||
title: 'ប្រព័ន្ធគ្រប់គ្រង'
|
||||
},
|
||||
common: {
|
||||
add: 'បន្ថែម',
|
||||
addSuccess: 'បន្ថែមជោគជ័យ',
|
||||
edit: 'កែប្រែ',
|
||||
editSuccess: 'កែប្រែជោគជ័យ',
|
||||
delete: 'លុប',
|
||||
deleteSuccess: 'លុបជោគជ័យ',
|
||||
batchDelete: 'លុបច្រើន',
|
||||
confirm: 'យល់ព្រម',
|
||||
cancel: 'បោះបង់',
|
||||
pleaseCheckValue: 'សូមពិនិត្យមើលតម្លៃដែលបានបញ្ចូលដើម្បីបញ្ជាក់ថាត្រូវប្រើប្រាស់បាន',
|
||||
action: 'សកម្មភាព'
|
||||
},
|
||||
routes: {
|
||||
dashboard: {
|
||||
_value: 'ផ្ទាំងទិន្នន័យ',
|
||||
analysis: 'ផ្ទាំងវិភាគ',
|
||||
workbench: 'ផ្ទាំងការងារ'
|
||||
},
|
||||
document: {
|
||||
_value: 'ឯកសារ',
|
||||
vue: 'ឯកសារ Vue',
|
||||
vite: 'ឯកសារ Vite',
|
||||
naive: 'ឯកសារ NaiveUI',
|
||||
project: 'ឯកសារគម្រោង',
|
||||
'project-link': 'ឯកសារគម្រោង(href)'
|
||||
},
|
||||
component: {
|
||||
_value: 'សមាសភាគ',
|
||||
button: 'ប៊ូតុង',
|
||||
card: 'កាត',
|
||||
table: 'តារាង'
|
||||
},
|
||||
plugin: {
|
||||
_value: 'មុខងារជំនួយ',
|
||||
charts: {
|
||||
_value: 'តារាង Chart',
|
||||
echarts: 'តារាង ECharts',
|
||||
antv: 'AntV'
|
||||
},
|
||||
copy: 'ចម្លង',
|
||||
editor: {
|
||||
_value: 'កែប្រែ',
|
||||
quill: 'Quill',
|
||||
markdown: 'Markdown'
|
||||
},
|
||||
icon: 'អាយខន',
|
||||
map: 'ផែនទី',
|
||||
print: 'បោះពុម្ភ',
|
||||
swiper: 'Swiper',
|
||||
video: 'វីដេអូ'
|
||||
},
|
||||
'auth-demo': {
|
||||
_value: 'ឌីមូ Auth',
|
||||
permission: 'បិទ/បើកការអនុញ្ញាត',
|
||||
super: 'Super Auth'
|
||||
},
|
||||
function: {
|
||||
_value: 'មុខងារ',
|
||||
tab: 'ថេបប្រព័ន្ធ'
|
||||
},
|
||||
exception: {
|
||||
_value: 'ករណីពិេសស',
|
||||
403: '403',
|
||||
404: '404',
|
||||
500: '500'
|
||||
},
|
||||
'multi-menu': {
|
||||
_value: 'ម៉ឺនុយពហុដឺក្រេ',
|
||||
first: {
|
||||
_value: 'ដឺក្រេទី១',
|
||||
second: 'ដែក្រេទី២',
|
||||
'second-new': {
|
||||
_value: 'ដឺក្រេទី២មានអនុក្រោម',
|
||||
third: 'ដឺក្រេទី៣'
|
||||
}
|
||||
}
|
||||
},
|
||||
management: {
|
||||
_value: 'ការគ្រប់គ្រងប្រព័ន្ធ',
|
||||
auth: 'Auth',
|
||||
role: 'សិទ្ធី',
|
||||
route: 'ផ្លូវប្រព័ន្ធ',
|
||||
user: 'អ្នកប្រើប្រាស់'
|
||||
},
|
||||
about: 'អំពីប្រព័ន្ធ'
|
||||
},
|
||||
layout: {
|
||||
settingDrawer: {
|
||||
title: 'ការកំណត់ស្បែក',
|
||||
themeModeTitle: 'ស្បែករបស់របស់អ្នក',
|
||||
darkMode: 'របៀបងារស្បែកងងឹត',
|
||||
layoutModelTitle: 'របៀបប្រើប្រាស់របស់អ្នក',
|
||||
systemThemeTitle: 'ស្បែករបស់ប្រព័ន្ធគ្រប់គ្រង',
|
||||
pageFunctionsTitle: 'មុខងារទំនាក់ទំនងរបស់ទំព័រ',
|
||||
pageViewTitle: 'ទំព័រទស្សន៍ទាយ',
|
||||
followSystemTheme: 'តាមដានស្បែកប្រព័ន្ធគ្រប់គ្រង',
|
||||
isCustomizeDarkModeTransition: 'ប្រើប្រាស់របៀបងារស្បែកងងឹតផ្ទាល់ខ្លួន',
|
||||
scrollMode: 'របៀបរុករក',
|
||||
scrollModeList: {
|
||||
wrapper: 'រុករកជាក់លាក់',
|
||||
content: 'រុករកមានមុខងារ'
|
||||
},
|
||||
fixedHeaderAndTab: 'បិទការរុករកជាក់លាក់និងរុករកមានមុខងារ',
|
||||
header: {
|
||||
inverted: 'បង្កើតការរុករកជាក់លាក់',
|
||||
height: 'កម្ពស់',
|
||||
crumb: {
|
||||
visible: 'បង្ហាញរុករកជាក់លាក់',
|
||||
icon: 'រុករកជាក់លាក់រូបតំណាង'
|
||||
}
|
||||
},
|
||||
tab: {
|
||||
visible: 'បង្ហាញរុករកជាក់លាក់',
|
||||
height: 'កម្ពស់',
|
||||
modeList: {
|
||||
mode: 'របៀប',
|
||||
chrome: 'ក្រុមហ៊ុន',
|
||||
button: 'ប៊ូតុង'
|
||||
},
|
||||
isCache: 'រក្សាទុកការរុករកជាក់លាក់'
|
||||
},
|
||||
sider: {
|
||||
inverted: 'បង្កើតការរុករកជាក់លាក់',
|
||||
width: 'ទទឹង',
|
||||
mixWidth: 'ទទឹងបញ្ចូល'
|
||||
},
|
||||
menu: {
|
||||
horizontalPosition: 'ទីតាំងផ្ដេក',
|
||||
horizontalPositionList: {
|
||||
flexStart: 'ចាប់ផ្ដើមឈុត',
|
||||
center: 'កណ្តាល',
|
||||
flexEnd: 'ចាប់ផ្ដើមចុងក្រោយ'
|
||||
}
|
||||
},
|
||||
footer: {
|
||||
inverted: 'បង្កើតការរុករកជាក់លាក់',
|
||||
visible: 'បង្ហាញការរុករកជាក់លាក់',
|
||||
fixed: 'ការរុករកជាក់លាក់',
|
||||
right: 'ត្រឡប់ទៅស្តាំ'
|
||||
},
|
||||
page: {
|
||||
animate: 'ការផ្លាស់ប្តូរ',
|
||||
animateMode: 'របៀបផ្លាស់ប្តូរ',
|
||||
animateModeList: {
|
||||
zoomFade: 'ពង្រីកបង្ហាញនិងលាស់ប្តូរ',
|
||||
zoomOut: 'ពង្រីកបង្ហាញនិងលាស់ប្តូរ',
|
||||
fadeSlide: 'ពង្រីកបង្ហាញនិងលាស់ប្តូរ',
|
||||
fade: 'ពង្រីកបង្ហាញនិងលាស់ប្តូរ',
|
||||
fadeBottom: 'ពង្រីកបង្ហាញនិងលាស់ប្តូរ',
|
||||
fadeScale: 'ពង្រីកបង្ហាញនិងលាស់ប្តូរ'
|
||||
}
|
||||
},
|
||||
systemTheme: {
|
||||
moreColors: 'ពន្លឺច្រើនទៀត'
|
||||
},
|
||||
themeConfiguration: {
|
||||
title: 'ការកំណត់ស្បែក',
|
||||
copy: 'ចម្លង',
|
||||
reset: 'កំណត់ឡើងវិញ',
|
||||
resetSuccess: 'កំណត់ឡើងវិញជោគជ័យ, សូមចម្លងឯកសារស្បែកឡើងវិញ!',
|
||||
operateSuccess: 'សម្រាប់ការប្រើប្រាស់ជោគជ័យ',
|
||||
copySuccess: 'ចម្លងជោគជ័យ, សូមជោគជ័យឯកសារ src/settings/theme.json!',
|
||||
confirmCopy: 'យល់ព្រម'
|
||||
}
|
||||
}
|
||||
},
|
||||
page: {
|
||||
login: {
|
||||
common: {
|
||||
userNamePlaceholder: 'ឈ្មោះអ្នកប្រើប្រាស់',
|
||||
phonePlaceholder: 'លេខទូរស័ព្ទ',
|
||||
codePlaceholder: 'លេខកូដ',
|
||||
passwordPlaceholder: 'លេខសម្ងាត់',
|
||||
confirmPasswordPlaceholder: 'បញ្ជាក់លេខសម្ងាត់',
|
||||
codeLogin: 'ចូលតាមលេខកូដ',
|
||||
confirm: 'យល់ព្រម',
|
||||
back: 'ត្រឡប់ក្រោយ',
|
||||
validateSuccess: 'បញ្ជាក់ជោគជ័យ',
|
||||
loginSuccess: 'ចូលជោគជ័យ',
|
||||
welcomeBack: 'សូមស្វាគមន៍ម្តងទៀត, {userName}!'
|
||||
},
|
||||
pwdLogin: {
|
||||
title: 'ចូលគណនី',
|
||||
rememberMe: 'ចងចាំខ្ញុំ',
|
||||
forgetPassword: 'ភ្លេចលេខសម្ងាត់',
|
||||
register: 'ចុះឈ្មោះ',
|
||||
otherAccountLogin: 'ចូលតាមគណនីផ្សេងទៀត',
|
||||
otherLoginMode: 'របៀបចូលគណនីផ្សេងទៀត',
|
||||
superAdmin: 'អ្នកគ្រប់គ្រងសុវត្ថិភាព',
|
||||
admin: 'អ្នកគ្រប់គ្រង',
|
||||
user: 'អ្នកប្រើប្រាស់'
|
||||
},
|
||||
codeLogin: {
|
||||
title: 'ចូលតាមលេខកូដ',
|
||||
getCode: 'ទទួលលេខកូដ',
|
||||
imageCodePlaceholder: 'លេខកូដរូបភាព'
|
||||
},
|
||||
register: {
|
||||
title: 'ចុះឈ្មោះ',
|
||||
agreement: 'យល់ព្រមនឹង',
|
||||
protocol: 'សម្រាប់ការប្រើប្រាស់',
|
||||
policy: 'គោលការណ៍ផ្សេងៗ'
|
||||
},
|
||||
resetPwd: {
|
||||
title: 'កំណត់លេខសម្ងាត់ថ្មី'
|
||||
},
|
||||
bindWeChat: {
|
||||
title: 'ភ្ជាប់គណនីរបស់អ្នកជាមួយគណនីរបស់អ្នក'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default locale;
|
219
src/locales/lang/zh-CN.ts
Normal file
@@ -0,0 +1,219 @@
|
||||
const locale: I18nType.Schema = {
|
||||
system: {
|
||||
title: 'Soybean管理系统'
|
||||
},
|
||||
common: {
|
||||
add: '添加',
|
||||
addSuccess: '添加成功',
|
||||
edit: '修改',
|
||||
editSuccess: '修改成功',
|
||||
delete: '删除',
|
||||
deleteSuccess: '删除成功',
|
||||
batchDelete: '批量删除',
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
pleaseCheckValue: '请检查输入的值是否合法',
|
||||
action: '操作'
|
||||
},
|
||||
routes: {
|
||||
dashboard: {
|
||||
_value: '仪表盘',
|
||||
analysis: '分析页',
|
||||
workbench: '工作台'
|
||||
},
|
||||
document: {
|
||||
_value: '文档',
|
||||
vue: 'Vue文档',
|
||||
vite: 'Vite文档',
|
||||
naive: 'NaiveUI文档',
|
||||
project: '项目文档',
|
||||
'project-link': '项目文档(外链)'
|
||||
},
|
||||
component: {
|
||||
_value: '组件示例',
|
||||
button: '按钮',
|
||||
card: '卡片',
|
||||
table: '表格'
|
||||
},
|
||||
plugin: {
|
||||
_value: '插件示例',
|
||||
charts: {
|
||||
_value: '图表',
|
||||
echarts: 'ECharts',
|
||||
antv: 'AntV'
|
||||
},
|
||||
copy: '剪贴板',
|
||||
editor: {
|
||||
_value: '编辑器',
|
||||
quill: '富文本',
|
||||
markdown: 'Markdown'
|
||||
},
|
||||
icon: '图标',
|
||||
map: '地图',
|
||||
print: '打印',
|
||||
swiper: 'Swiper',
|
||||
video: '视频'
|
||||
},
|
||||
'auth-demo': {
|
||||
_value: '权限示例',
|
||||
permission: '切换权限',
|
||||
super: '超级管理员可见'
|
||||
},
|
||||
function: {
|
||||
_value: '功能',
|
||||
tab: 'Tab页签'
|
||||
},
|
||||
exception: {
|
||||
_value: '异常页',
|
||||
403: '403',
|
||||
404: '404',
|
||||
500: '500'
|
||||
},
|
||||
'multi-menu': {
|
||||
_value: '多级菜单',
|
||||
first: {
|
||||
_value: '一级菜单',
|
||||
second: '二级菜单',
|
||||
'second-new': {
|
||||
_value: '二级菜单(有子菜单)',
|
||||
third: '三级菜单'
|
||||
}
|
||||
}
|
||||
},
|
||||
management: {
|
||||
_value: '系统管理',
|
||||
auth: '权限管理',
|
||||
role: '角色管理',
|
||||
route: '路由管理',
|
||||
user: '用户管理'
|
||||
},
|
||||
about: '关于'
|
||||
},
|
||||
layout: {
|
||||
settingDrawer: {
|
||||
title: '主题配置',
|
||||
themeModeTitle: '主题模式',
|
||||
darkMode: '深色主题',
|
||||
layoutModelTitle: '布局模式',
|
||||
systemThemeTitle: '系统主题',
|
||||
pageFunctionsTitle: '界面功能',
|
||||
pageViewTitle: '界面显示',
|
||||
followSystemTheme: '跟随系统',
|
||||
isCustomizeDarkModeTransition: '自定义暗黑主题动画过渡',
|
||||
scrollMode: '滚动模式',
|
||||
scrollModeList: {
|
||||
wrapper: '外层滚动',
|
||||
content: '主体滚动'
|
||||
},
|
||||
fixedHeaderAndTab: '固定头部和多页签',
|
||||
header: {
|
||||
inverted: '头部深色',
|
||||
height: '头部高度',
|
||||
crumb: {
|
||||
visible: '面包屑',
|
||||
icon: '面包屑图标'
|
||||
}
|
||||
},
|
||||
tab: {
|
||||
visible: '多页签',
|
||||
height: '多页签高度',
|
||||
modeList: {
|
||||
mode: '多页签风格',
|
||||
chrome: '谷歌风格',
|
||||
button: '按钮风格'
|
||||
},
|
||||
isCache: '多页签缓存'
|
||||
},
|
||||
sider: {
|
||||
inverted: '侧边栏深色',
|
||||
width: '侧边栏展开宽度',
|
||||
mixWidth: '左侧混合侧边栏展开宽度'
|
||||
},
|
||||
menu: {
|
||||
horizontalPosition: '顶部菜单位置',
|
||||
horizontalPositionList: {
|
||||
flexStart: '居左',
|
||||
center: '居中',
|
||||
flexEnd: '居右'
|
||||
}
|
||||
},
|
||||
footer: {
|
||||
inverted: '底部深色',
|
||||
visible: '显示底部',
|
||||
fixed: '固定底部',
|
||||
right: '底部居右'
|
||||
},
|
||||
page: {
|
||||
animate: '页面切换动画',
|
||||
animateMode: '页面切换动画类型',
|
||||
animateModeList: {
|
||||
zoomFade: '渐变',
|
||||
zoomOut: '闪现',
|
||||
fadeSlide: '滑动',
|
||||
fade: '消退',
|
||||
fadeBottom: '底部消退',
|
||||
fadeScale: '缩放消退'
|
||||
}
|
||||
},
|
||||
systemTheme: {
|
||||
moreColors: '更多颜色'
|
||||
},
|
||||
themeConfiguration: {
|
||||
title: '主题配置',
|
||||
copy: '拷贝当前配置',
|
||||
reset: '重置当前配置',
|
||||
resetSuccess: '已重置配置,请重新拷贝!',
|
||||
operateSuccess: '操作成功',
|
||||
copySuccess: '复制成功,请替换 src/settings/theme.json的内容!',
|
||||
confirmCopy: '确认'
|
||||
}
|
||||
}
|
||||
},
|
||||
page: {
|
||||
login: {
|
||||
common: {
|
||||
userNamePlaceholder: '请输入用户名',
|
||||
phonePlaceholder: '请输入手机号',
|
||||
codePlaceholder: '请输入验证码',
|
||||
passwordPlaceholder: '请输入密码',
|
||||
confirmPasswordPlaceholder: '请再次输入密码',
|
||||
codeLogin: '验证码登录',
|
||||
confirm: '确定',
|
||||
back: '返回',
|
||||
validateSuccess: '验证成功',
|
||||
loginSuccess: '登录成功',
|
||||
welcomeBack: '欢迎回来,{userName}!'
|
||||
},
|
||||
pwdLogin: {
|
||||
title: '密码登录',
|
||||
rememberMe: '记住我',
|
||||
forgetPassword: '忘记密码?',
|
||||
register: '注册账号',
|
||||
otherAccountLogin: '其他账号登录',
|
||||
otherLoginMode: '其他登录方式',
|
||||
superAdmin: '超级管理员',
|
||||
admin: '管理员',
|
||||
user: '普通用户'
|
||||
},
|
||||
codeLogin: {
|
||||
title: '验证码登录',
|
||||
getCode: '获取验证码',
|
||||
imageCodePlaceholder: '请输入图片验证码'
|
||||
},
|
||||
register: {
|
||||
title: '注册账号',
|
||||
agreement: '我已经仔细阅读并接受',
|
||||
protocol: '《用户协议》',
|
||||
policy: '《隐私权政策》'
|
||||
},
|
||||
resetPwd: {
|
||||
title: '重置密码'
|
||||
},
|
||||
bindWeChat: {
|
||||
title: '绑定微信'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default locale;
|
@@ -1,85 +0,0 @@
|
||||
import type { LocaleMessages } from 'vue-i18n';
|
||||
|
||||
const locale: LocaleMessages<I18nType.Schema> = {
|
||||
message: {
|
||||
system: {
|
||||
title: 'Soybean管理系统'
|
||||
},
|
||||
routes: {
|
||||
dashboard: {
|
||||
dashboard: '仪表盘',
|
||||
analysis: '分析页',
|
||||
workbench: '工作台'
|
||||
},
|
||||
document: {
|
||||
_value: '文档',
|
||||
vue: 'Vue文档',
|
||||
vite: 'Vite文档',
|
||||
naive: 'NaiveUI文档',
|
||||
project: '项目文档',
|
||||
'project-link': '项目文档(外链)'
|
||||
},
|
||||
component: {
|
||||
_value: '组件示例',
|
||||
button: '按钮',
|
||||
card: '卡片',
|
||||
table: '表格'
|
||||
},
|
||||
plugin: {
|
||||
_value: '插件示例',
|
||||
charts: {
|
||||
_value: '图表',
|
||||
echarts: 'ECharts',
|
||||
antv: 'AntV'
|
||||
},
|
||||
copy: '剪贴板',
|
||||
editor: {
|
||||
_value: '编辑器',
|
||||
quill: '富文本',
|
||||
markdown: 'Markdown'
|
||||
},
|
||||
icon: '图标',
|
||||
map: '地图',
|
||||
print: '打印',
|
||||
swiper: 'Swiper',
|
||||
video: '视频'
|
||||
},
|
||||
'auth-demo': {
|
||||
_value: '权限示例',
|
||||
permission: '切换权限',
|
||||
super: '超级管理员可见'
|
||||
},
|
||||
function: {
|
||||
_value: '功能',
|
||||
tab: 'Tab页签'
|
||||
},
|
||||
exception: {
|
||||
_value: '异常页',
|
||||
403: '403',
|
||||
404: '404',
|
||||
500: '500'
|
||||
},
|
||||
'multi-menu': {
|
||||
_value: '多级菜单',
|
||||
first: {
|
||||
_value: '一级菜单',
|
||||
second: '二级菜单',
|
||||
'second-new': {
|
||||
_value: '二级菜单(有子菜单)',
|
||||
third: '三级菜单'
|
||||
}
|
||||
}
|
||||
},
|
||||
management: {
|
||||
_value: '系统管理',
|
||||
auth: '权限管理',
|
||||
role: '角色管理',
|
||||
route: '路由管理',
|
||||
user: '用户管理'
|
||||
},
|
||||
about: '关于'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default locale;
|
11
src/locales/locale.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import zhCN from './lang/zh-CN';
|
||||
import en from './lang/en';
|
||||
import kmKH from './lang/km-KH';
|
||||
|
||||
const locales: Record<I18nType.LangType, I18nType.Schema> = {
|
||||
'zh-CN': zhCN,
|
||||
en,
|
||||
'km-KH': kmKH
|
||||
};
|
||||
|
||||
export default locales;
|
@@ -29,6 +29,8 @@ async function setupApp() {
|
||||
|
||||
setupI18n(app);
|
||||
|
||||
appLoading.unmount();
|
||||
|
||||
// mount app
|
||||
app.mount('#app');
|
||||
}
|
||||
|
@@ -7,4 +7,6 @@ import 'virtual:svg-icons-register';
|
||||
import '../styles/css/global.css';
|
||||
|
||||
/** import static assets: css, js , font and so on. - [引入静态资源,css、js和字体文件等] */
|
||||
export default function setupAssets() {}
|
||||
export default function setupAssets() {
|
||||
//
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { Router } from 'vue-router';
|
||||
import { useTitle } from '@vueuse/core';
|
||||
import { t } from '@/locales';
|
||||
import { $t } from '@/locales';
|
||||
import { createPermissionGuard } from './permission';
|
||||
|
||||
/**
|
||||
@@ -16,7 +16,7 @@ export function createRouterGuard(router: Router) {
|
||||
});
|
||||
router.afterEach(to => {
|
||||
// 设置document title
|
||||
useTitle(to.meta.i18nTitle ? t(to.meta.i18nTitle) : to.meta.title);
|
||||
useTitle(to.meta.i18nTitle ? $t(to.meta.i18nTitle) : to.meta.title);
|
||||
// 结束 loadingBar
|
||||
window.$loadingBar?.finish();
|
||||
});
|
||||
|
@@ -1,10 +1,10 @@
|
||||
const about1: AuthRoute.Route = {
|
||||
const about: AuthRoute.Route = {
|
||||
name: 'about',
|
||||
path: '/about',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '关于',
|
||||
i18nTitle: 'message.routes.about',
|
||||
i18nTitle: 'routes.about',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
singleLayout: 'basic',
|
||||
@@ -14,4 +14,4 @@ const about1: AuthRoute.Route = {
|
||||
}
|
||||
};
|
||||
|
||||
export default about1;
|
||||
export default about;
|
||||
|
@@ -9,7 +9,7 @@ const authDemo: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限切换',
|
||||
i18nTitle: 'message.routes.auth-demo.permission',
|
||||
i18nTitle: 'routes.auth-demo.permission',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-construction'
|
||||
}
|
||||
@@ -20,7 +20,7 @@ const authDemo: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '超级管理员可见',
|
||||
i18nTitle: 'message.routes.auth-demo.super',
|
||||
i18nTitle: 'routes.auth-demo.super',
|
||||
requiresAuth: true,
|
||||
permissions: ['super'],
|
||||
icon: 'ic:round-supervisor-account'
|
||||
@@ -29,7 +29,7 @@ const authDemo: AuthRoute.Route = {
|
||||
],
|
||||
meta: {
|
||||
title: '权限示例',
|
||||
i18nTitle: 'message.routes.auth-demo._value',
|
||||
i18nTitle: 'routes.auth-demo._value',
|
||||
icon: 'ic:baseline-security',
|
||||
order: 5
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ const component: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '按钮',
|
||||
i18nTitle: 'message.routes.component.button',
|
||||
i18nTitle: 'routes.component.button',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:button-cursor'
|
||||
}
|
||||
@@ -20,7 +20,7 @@ const component: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '卡片',
|
||||
i18nTitle: 'message.routes.component.card',
|
||||
i18nTitle: 'routes.component.card',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:card-outline'
|
||||
}
|
||||
@@ -31,7 +31,7 @@ const component: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '表格',
|
||||
i18nTitle: 'message.routes.component.table',
|
||||
i18nTitle: 'routes.component.table',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:table-large'
|
||||
}
|
||||
@@ -39,7 +39,7 @@ const component: AuthRoute.Route = {
|
||||
],
|
||||
meta: {
|
||||
title: '组件示例',
|
||||
i18nTitle: 'message.routes.component._value',
|
||||
i18nTitle: 'routes.component._value',
|
||||
icon: 'cib:app-store',
|
||||
order: 3
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@ const dashboard: AuthRoute.Route = {
|
||||
title: '分析页',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:analysis',
|
||||
i18nTitle: 'message.routes.dashboard.analysis'
|
||||
i18nTitle: 'routes.dashboard.analysis'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -22,7 +22,7 @@ const dashboard: AuthRoute.Route = {
|
||||
title: '工作台',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:workbench',
|
||||
i18nTitle: 'message.routes.dashboard.workbench'
|
||||
i18nTitle: 'routes.dashboard.workbench'
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -30,7 +30,7 @@ const dashboard: AuthRoute.Route = {
|
||||
title: '仪表盘',
|
||||
icon: 'mdi:monitor-dashboard',
|
||||
order: 1,
|
||||
i18nTitle: 'message.routes.dashboard.dashboard'
|
||||
i18nTitle: 'routes.dashboard._value'
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -9,7 +9,7 @@ const document: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vue文档',
|
||||
i18nTitle: 'message.routes.document.vue',
|
||||
i18nTitle: 'routes.document.vue',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vue'
|
||||
}
|
||||
@@ -20,7 +20,7 @@ const document: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vite文档',
|
||||
i18nTitle: 'message.routes.document.vite',
|
||||
i18nTitle: 'routes.document.vite',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vitejs'
|
||||
}
|
||||
@@ -31,7 +31,7 @@ const document: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'naive文档',
|
||||
i18nTitle: 'message.routes.document.naive',
|
||||
i18nTitle: 'routes.document.naive',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:naiveui'
|
||||
}
|
||||
@@ -42,7 +42,7 @@ const document: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '项目文档',
|
||||
i18nTitle: 'message.routes.document.project',
|
||||
i18nTitle: 'routes.document.project',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo'
|
||||
}
|
||||
@@ -52,16 +52,16 @@ const document: AuthRoute.Route = {
|
||||
path: '/document/project-link',
|
||||
meta: {
|
||||
title: '项目文档(外链)',
|
||||
i18nTitle: 'message.routes.document.project-link',
|
||||
i18nTitle: 'routes.document.project-link',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo',
|
||||
href: 'https://docs.soybean.pro/'
|
||||
href: 'https://admin-docs.soybeanjs.cn/'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '文档',
|
||||
i18nTitle: 'message.routes.document._value',
|
||||
i18nTitle: 'routes.document._value',
|
||||
icon: 'mdi:file-document-multiple-outline',
|
||||
order: 2
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ const exception: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页403',
|
||||
i18nTitle: 'message.routes.exception.403',
|
||||
i18nTitle: 'routes.exception.403',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-block'
|
||||
}
|
||||
@@ -20,7 +20,7 @@ const exception: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页404',
|
||||
i18nTitle: 'message.routes.exception.404',
|
||||
i18nTitle: 'routes.exception.404',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-web-asset-off'
|
||||
}
|
||||
@@ -31,14 +31,14 @@ const exception: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页500',
|
||||
i18nTitle: 'message.routes.exception.500',
|
||||
i18nTitle: 'routes.exception.500',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-wifi-off'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
i18nTitle: 'message.routes.exception._value',
|
||||
i18nTitle: 'routes.exception._value',
|
||||
title: '异常页',
|
||||
icon: 'ant-design:exception-outlined',
|
||||
order: 7
|
||||
|
@@ -9,7 +9,7 @@ const functionRoute: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab',
|
||||
i18nTitle: 'message.routes.function.tab',
|
||||
i18nTitle: 'routes.function.tab',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
@@ -42,7 +42,7 @@ const functionRoute: AuthRoute.Route = {
|
||||
],
|
||||
meta: {
|
||||
title: '功能',
|
||||
i18nTitle: 'message.routes.function._value',
|
||||
i18nTitle: 'routes.function._value',
|
||||
icon: 'icon-park-outline:all-application',
|
||||
order: 6
|
||||
}
|
||||
|
@@ -9,8 +9,9 @@ const management: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限管理',
|
||||
i18nTitle: 'message.routes.management.auth',
|
||||
i18nTitle: 'routes.management.auth',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
icon: 'ic:baseline-security'
|
||||
}
|
||||
},
|
||||
@@ -20,8 +21,9 @@ const management: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
i18nTitle: 'message.routes.management.role',
|
||||
i18nTitle: 'routes.management.role',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
icon: 'carbon:user-role'
|
||||
}
|
||||
},
|
||||
@@ -31,8 +33,9 @@ const management: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '用户管理',
|
||||
i18nTitle: 'message.routes.management.user',
|
||||
i18nTitle: 'routes.management.user',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
icon: 'ic:round-manage-accounts'
|
||||
}
|
||||
},
|
||||
@@ -42,15 +45,16 @@ const management: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '路由管理',
|
||||
i18nTitle: 'message.routes.management.route',
|
||||
i18nTitle: 'routes.management.route',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
icon: 'material-symbols:route'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '系统管理',
|
||||
i18nTitle: 'message.routes.management._value',
|
||||
i18nTitle: 'routes.management._value',
|
||||
icon: 'carbon:cloud-service-management',
|
||||
order: 9
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ const multiMenu: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '二级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu.first.second',
|
||||
i18nTitle: 'routes.multi-menu.first.second',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
@@ -30,7 +30,7 @@ const multiMenu: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '三级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu.first.second-new.third',
|
||||
i18nTitle: 'routes.multi-menu.first.second-new.third',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
@@ -38,21 +38,21 @@ const multiMenu: AuthRoute.Route = {
|
||||
],
|
||||
meta: {
|
||||
title: '二级菜单(有子菜单)',
|
||||
i18nTitle: 'message.routes.multi-menu.first.second-new._value',
|
||||
i18nTitle: 'routes.multi-menu.first.second-new._value',
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '一级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu.first._value',
|
||||
i18nTitle: 'routes.multi-menu.first._value',
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '多级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu._value',
|
||||
i18nTitle: 'routes.multi-menu._value',
|
||||
icon: 'carbon:menu',
|
||||
order: 8
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ const plugin: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'ECharts',
|
||||
i18nTitle: 'message.routes.plugin.charts.echarts',
|
||||
i18nTitle: 'routes.plugin.charts.echarts',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:apacheecharts'
|
||||
}
|
||||
@@ -25,7 +25,7 @@ const plugin: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'AntV',
|
||||
i18nTitle: 'message.routes.plugin.charts.antv',
|
||||
i18nTitle: 'routes.plugin.charts.antv',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:antdesign'
|
||||
}
|
||||
@@ -33,7 +33,7 @@ const plugin: AuthRoute.Route = {
|
||||
],
|
||||
meta: {
|
||||
title: '图表',
|
||||
i18nTitle: 'message.routes.plugin.charts._value',
|
||||
i18nTitle: 'routes.plugin.charts._value',
|
||||
icon: 'mdi:chart-areaspline'
|
||||
}
|
||||
},
|
||||
@@ -43,7 +43,7 @@ const plugin: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '地图',
|
||||
i18nTitle: 'message.routes.plugin.map',
|
||||
i18nTitle: 'routes.plugin.map',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:map'
|
||||
}
|
||||
@@ -54,7 +54,7 @@ const plugin: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '视频',
|
||||
i18nTitle: 'message.routes.plugin.video',
|
||||
i18nTitle: 'routes.plugin.video',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:video'
|
||||
}
|
||||
@@ -70,7 +70,7 @@ const plugin: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '富文本编辑器',
|
||||
i18nTitle: 'message.routes.plugin.editor.quill',
|
||||
i18nTitle: 'routes.plugin.editor.quill',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:file-document-edit-outline'
|
||||
}
|
||||
@@ -81,7 +81,7 @@ const plugin: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'markdown编辑器',
|
||||
i18nTitle: 'message.routes.plugin.editor.markdown',
|
||||
i18nTitle: 'routes.plugin.editor.markdown',
|
||||
requiresAuth: true,
|
||||
icon: 'ri:markdown-line'
|
||||
}
|
||||
@@ -89,7 +89,7 @@ const plugin: AuthRoute.Route = {
|
||||
],
|
||||
meta: {
|
||||
title: '编辑器',
|
||||
i18nTitle: 'message.routes.plugin.editor._value',
|
||||
i18nTitle: 'routes.plugin.editor._value',
|
||||
icon: 'icon-park-outline:editor'
|
||||
}
|
||||
},
|
||||
@@ -99,7 +99,7 @@ const plugin: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Swiper插件',
|
||||
i18nTitle: 'message.routes.plugin.swiper',
|
||||
i18nTitle: 'routes.plugin.swiper',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:swiper'
|
||||
}
|
||||
@@ -110,7 +110,7 @@ const plugin: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '剪贴板',
|
||||
i18nTitle: 'message.routes.plugin.copy',
|
||||
i18nTitle: 'routes.plugin.copy',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:clipboard-outline'
|
||||
}
|
||||
@@ -121,7 +121,7 @@ const plugin: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '图标',
|
||||
i18nTitle: 'message.routes.plugin.icon',
|
||||
i18nTitle: 'routes.plugin.icon',
|
||||
requiresAuth: true,
|
||||
localIcon: 'custom-icon'
|
||||
}
|
||||
@@ -132,7 +132,7 @@ const plugin: AuthRoute.Route = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '打印',
|
||||
i18nTitle: 'message.routes.plugin.print',
|
||||
i18nTitle: 'routes.plugin.print',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:printer'
|
||||
}
|
||||
@@ -140,7 +140,7 @@ const plugin: AuthRoute.Route = {
|
||||
],
|
||||
meta: {
|
||||
title: '插件示例',
|
||||
i18nTitle: 'message.routes.plugin._value',
|
||||
i18nTitle: 'routes.plugin._value',
|
||||
icon: 'clarity:plugin-line',
|
||||
order: 4
|
||||
}
|
||||
|
@@ -11,6 +11,8 @@ import {
|
||||
} from '@/utils';
|
||||
import { handleRefreshToken } from './helpers';
|
||||
|
||||
type RefreshRequestQueue = (config: AxiosRequestConfig) => void;
|
||||
|
||||
/**
|
||||
* 封装axios请求类
|
||||
* @author Soybean<honghuangdc@gmail.com>
|
||||
@@ -20,6 +22,10 @@ export default class CustomAxiosInstance {
|
||||
|
||||
backendConfig: Service.BackendResultConfig;
|
||||
|
||||
isRefreshing: boolean;
|
||||
|
||||
retryQueues: RefreshRequestQueue[];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param axiosConfig - axios配置
|
||||
@@ -37,6 +43,8 @@ export default class CustomAxiosInstance {
|
||||
this.backendConfig = backendConfig;
|
||||
this.instance = axios.create(axiosConfig);
|
||||
this.setInterceptor();
|
||||
this.isRefreshing = false;
|
||||
this.retryQueues = [];
|
||||
}
|
||||
|
||||
/** 设置请求拦截器 */
|
||||
@@ -60,7 +68,7 @@ export default class CustomAxiosInstance {
|
||||
);
|
||||
this.instance.interceptors.response.use(
|
||||
(async response => {
|
||||
const { status } = response;
|
||||
const { status, config } = response;
|
||||
if (status === 200 || status < 300 || status === 304) {
|
||||
const backend = response.data;
|
||||
const { codeKey, dataKey, successCode } = this.backendConfig;
|
||||
@@ -71,10 +79,24 @@ export default class CustomAxiosInstance {
|
||||
|
||||
// token失效, 刷新token
|
||||
if (REFRESH_TOKEN_CODE.includes(backend[codeKey])) {
|
||||
const config = await handleRefreshToken(response.config);
|
||||
if (config) {
|
||||
return this.instance.request(config);
|
||||
// 原始请求
|
||||
const originRequest = new Promise(resolve => {
|
||||
this.retryQueues.push((refreshConfig: AxiosRequestConfig) => {
|
||||
config.headers.Authorization = refreshConfig.headers?.Authorization;
|
||||
resolve(this.instance.request(config));
|
||||
});
|
||||
});
|
||||
|
||||
if (!this.isRefreshing) {
|
||||
this.isRefreshing = true;
|
||||
const refreshConfig = await handleRefreshToken(response.config);
|
||||
if (refreshConfig) {
|
||||
this.retryQueues.map(cb => cb(refreshConfig));
|
||||
}
|
||||
this.retryQueues = [];
|
||||
this.isRefreshing = false;
|
||||
}
|
||||
return originRequest;
|
||||
}
|
||||
|
||||
const error = handleBackendError(backend, this.backendConfig);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"darkMode": false,
|
||||
"followSystemTheme": true,
|
||||
"isCustomizeDarkModeTransition": false,
|
||||
"layout": {
|
||||
"minWidth": 900,
|
||||
"mode": "vertical",
|
||||
@@ -34,15 +35,15 @@
|
||||
"label": "主体滚动"
|
||||
}
|
||||
],
|
||||
"themeColor": "#1890ff",
|
||||
"themeColor": "#646cff",
|
||||
"themeColorList": [
|
||||
"#1890ff",
|
||||
"#409EFF",
|
||||
"#2d8cf0",
|
||||
"#007AFF",
|
||||
"#5ac8fa",
|
||||
"#5856D6",
|
||||
"#536dfe",
|
||||
"#646cff",
|
||||
"#9c27b0",
|
||||
"#AF52DE",
|
||||
"#0096c7",
|
||||
|
@@ -10,11 +10,11 @@ import jsonSetting from './theme.json';
|
||||
const themeColorList = [
|
||||
'#1890ff',
|
||||
'#409EFF',
|
||||
'#2d8cf0',
|
||||
'#007AFF',
|
||||
'#5ac8fa',
|
||||
'#5856D6',
|
||||
'#536dfe',
|
||||
'#646cff',
|
||||
'#9c27b0',
|
||||
'#AF52DE',
|
||||
'#0096c7',
|
||||
@@ -37,6 +37,7 @@ const themeColorList = [
|
||||
const defaultThemeSetting: Theme.Setting = {
|
||||
darkMode: false,
|
||||
followSystemTheme: true,
|
||||
isCustomizeDarkModeTransition: false,
|
||||
layout: {
|
||||
minWidth: 900,
|
||||
mode: 'vertical',
|
||||
@@ -44,7 +45,7 @@ const defaultThemeSetting: Theme.Setting = {
|
||||
},
|
||||
scrollMode: 'content',
|
||||
scrollModeList: themeScrollModeOptions,
|
||||
themeColor: themeColorList[0],
|
||||
themeColor: themeColorList[6],
|
||||
themeColorList,
|
||||
otherColor: {
|
||||
info: '#2080f0',
|
||||
|