Compare commits

...

22 Commits

Author SHA1 Message Date
xiaoxian521
aa845fc3f5 release: update 2.8.0 2022-01-04 19:52:05 +08:00
xiaoxian521
079a455181 perf: router 2022-01-04 17:28:36 +08:00
xiaoxian521
07794d000c chore: update element-plus@1.3.0-beta.1 2021-12-31 14:09:59 +08:00
啝裳
e787f46414 Merge pull request #166 from xiaoxian521/perf/storage
perf: storage
2021-12-30 13:57:30 +08:00
lrl
e61d6109d7 perf: modify showLogo type: from string to Boolean 2021-12-29 22:45:18 +08:00
lrl
34eda14473 perf: storage 2021-12-29 21:51:05 +08:00
xiaoxian521
0eb0b7395e perf: layout style 2021-12-29 13:26:56 +08:00
xiaoxian521
73705eb0e4 perf: 优化国际化 2021-12-29 11:21:59 +08:00
xiaoxian521
d94fb0ef06 docs: update 2021-12-28 15:09:17 +08:00
xiaoxian521
17470ecce2 docs: update 2021-12-28 13:21:16 +08:00
一万
12db6966fb feat: guide page (#164)
* feat: add guide page

* perf: guide page
2021-12-27 10:51:25 +08:00
xiaoxian521
5070568b89 perf: theme 2021-12-24 10:46:39 +08:00
lrl
ef91f113ee fix: 动态标签页标记改为refreshRedirect 2021-12-22 22:49:34 +08:00
xiaoxian521
c875d20e83 perf: layout set 2021-12-22 13:40:59 +08:00
xiaoxian521
c86aae7b8b fix: 首页同一图表组件引入多次,只显示一个 2021-12-22 09:53:59 +08:00
一万
ee30cba471 perf: theme (#157) 2021-12-21 22:06:09 +08:00
xiaoxian521
7bcd8a800a perf: theme 2021-12-21 16:57:35 +08:00
xiaoxian521
903e298951 fix: delete realPath 2021-12-21 10:34:28 +08:00
啝裳
955b76f30a feat: ep theme (#156)
* feat: ep-theme

* perf: ep-theme

Co-authored-by: lrl <742798240@qq.com>
2021-12-20 22:27:47 +08:00
lrl
1b052023b6 perf: multiTags route delete realPath 2021-12-20 22:25:45 +08:00
xiaoxian521
3af52cf6c4 docs: update 2021-12-20 16:10:36 +08:00
xiaoxian521
660b6f4be8 feat: 暗黑模式 2021-12-20 14:19:38 +08:00
82 changed files with 1211 additions and 657 deletions

11
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"recommendations": [
"stylelint.vscode-stylelint",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"johnsoncodehk.volar",
"lokalise.i18n-ally",
"mikestead.dotenv",
"antfu.iconify"
]
}

27
.vscode/settings.json vendored
View File

@@ -1,11 +1,14 @@
{
// You should install these plugins:
// ESLint
// Prettier - Code formatter
// stylelint
// vscode-icons
// TypeScript Vue Plugin (Volar)
// Vue Language Features (Volar)
/** 便
* ESLint
* Prettier - Code formatter
* stylelint
* vscode-icons
* i18n Ally
* Iconify IntelliSense
* TypeScript Vue Plugin (Volar)
* Vue Language Features (Volar)
*/
"terminal.integrated.rendererType": "dom",
"editor.formatOnType": true,
"editor.formatOnSave": true,
@@ -45,5 +48,13 @@
},
"volar.tsPlugin": true,
"typescript.tsdk": "node_modules/typescript/lib",
"i18n-ally.localesPaths": ["src/plugins/i18n"]
"i18n-ally.localesPaths": ["src/plugins/i18n"],
"i18n-ally.keystyle": "nested",
"i18n-ally.sortKeys": true,
"i18n-ally.namespace": true,
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
"i18n-ally.enabledParsers": ["ts"],
"i18n-ally.sourceLanguage": "en",
"i18n-ally.displayLanguage": "zh-CN",
"i18n-ally.enabledFrameworks": ["vue", "react"]
}

17
.vscode/vue3.2+.setup-snippets.json vendored Normal file
View File

@@ -0,0 +1,17 @@
{
"Vue3.2+快速生成模板": {
"prefix": "Vue3.2+",
"body": [
"<script setup lang='ts'>",
"</script>\n",
"<template>",
"\t<div>\n",
"\t</div>",
"</template>\n",
"<style scoped>\n",
"</style>",
"$2"
],
"description": "Vue3.2+"
}
}

View File

@@ -1,20 +0,0 @@
{
"Vue3.2快速生成模板": {
"prefix": "Vue3.2",
"body": [
"<!-- $1 -->",
"<script setup lang='ts'>",
"\t$2",
"</script>\n",
"<template>",
"\t<div>",
"\t\t$3",
"\t</div>",
"</template>\n",
"<style scoped>",
"\t$4",
"</style>"
],
"description": "Vue3.2"
}
}

View File

@@ -1,3 +1,17 @@
# 2.8.0(2022-1-4)
### 🎫 Feat
-Added dark theme
-Add element-plus custom theme
-Add guide page
### 🍏 Perf
-Optimize internationalization, compatible with the vscode plug-in i18n Ally smart reminder
-Optimize the back-end return routing structure
-Optimize local storage, with four built-in buttons `responsive-configure`, `responsive-locale`, `responsive-layout`, `responsive-tags`, which are basic configuration, international configuration, layout configuration, and tab persistent configuration
# 2.7.0(2021-12-18)
### 🎫 Feat

View File

@@ -1,3 +1,17 @@
# 2.8.0(2022-1-4)
### 🎫 Feat
-Added dark theme
-Add element-plus custom theme
-Add guide page
### 🍏 Perf
-Optimize internationalization, compatible with the vscode plug-in i18n Ally smart reminder
-Optimize the back-end return routing structure
-Optimize local storage, with four built-in buttons `responsive-configure`, `responsive-locale`, `responsive-layout`, `responsive-tags`, which are basic configuration, international configuration, layout configuration, and tab persistent configuration
# 2.7.0(2021-12-18)
### 🎫 Feat

View File

@@ -1,3 +1,17 @@
# 2.8.0(2022-1-4)
### 🎫 Feat
- 添加暗黑主题
- 添加 element-plus 自定义主题
- 添加引导页
### 🍏 Perf
- 优化国际化,兼容 vscode 插件 i18n Ally 智能提醒
- 优化后端返回路由结构
- 优化本地存储,内置四个键`responsive-configure``responsive-locale``responsive-layout``responsive-tags`,分别为基本配置、国际化配置、布局配置、标签页持久化配置
# 2.7.0(2021-12-18)
### 🎫 Feat

View File

@@ -28,6 +28,7 @@ Github Address: <https://github.com/xiaoxian521/pure-admin-thin>
<p align="center">
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b4857fc7eb7d4c0f8deeefc644c1f7dd~tplv-k3u1fbpfcp-watermark.awebp?">
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/549c3184697f4d268a78c9833e5ec2ea~tplv-k3u1fbpfcp-watermark.awebp?">
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/381fc957fac143db9f06efdd389d88a3~tplv-k3u1fbpfcp-watermark.awebp?">
</p>
### Use Gitpod
@@ -117,9 +118,15 @@ Support modern browsers, not IE
## Donate
If you think this project is helpful to you, you can help the author buy a cup of coffee to show your support!
If you think this project is helpful to you, you can help the author buy a cup of coffee to show your support
<img src="http://yiming_chang.gitee.io/manages/pay.png" width="360px" height="480px" />
<img src="http://yiming_chang.gitee.io/manages/pay.jpg" width="150px" height="150px" />
## WeChat Exchange Group
For the better development of the project, you can choose to donate 10 yuan and add the following WeChat to pull you into the group. After adding, please consciously send a screenshot of the donation
<img src="http://yiming_chang.gitee.io/manages/kf.jpg" width="150px" height="195px" />
## License

View File

@@ -28,6 +28,7 @@ UI 设计:<https://www.bilibili.com/video/BV17g411T7rq/>
<p align="center">
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b4857fc7eb7d4c0f8deeefc644c1f7dd~tplv-k3u1fbpfcp-watermark.awebp?">
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/549c3184697f4d268a78c9833e5ec2ea~tplv-k3u1fbpfcp-watermark.awebp?">
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/381fc957fac143db9f06efdd389d88a3~tplv-k3u1fbpfcp-watermark.awebp?">
</p>
### 使用 Gitpod
@@ -121,11 +122,11 @@ pnpm build
<img src="http://yiming_chang.gitee.io/manages/pay.jpg" width="150px" height="150px" />
## 付费咨询、需求定制
## 微信交流群
作者精力有限,需要提供技术服务的可扫下面的二维码加微信,添加请备注来意
为了项目更好的发展,你可选择捐赠 10 元后添加下图微信拉你进群,添加后请自觉发捐赠截图
<img src="http://yiming_chang.gitee.io/manages/wechat.jpg" width="150px" height="150px" />
<img src="http://yiming_chang.gitee.io/manages/kf.jpg" width="150px" height="195px" />
## 许可证

View File

@@ -5,29 +5,29 @@ import { MockMethod } from "vite-plugin-mock";
const systemRouter = {
path: "/system",
name: "system",
redirect: "/system/user",
redirect: "/system/user/index",
meta: {
icon: "Setting",
title: "message.hssysManagement",
title: "menus.hssysManagement",
i18n: true,
showLink: true,
rank: 6
},
children: [
{
path: "/system/user",
path: "/system/user/index",
name: "user",
meta: {
title: "message.hsBaseinfo",
title: "menus.hsBaseinfo",
i18n: true,
showLink: true
}
},
{
path: "/system/dict",
path: "/system/dict/index",
name: "dict",
meta: {
title: "message.hsDict",
title: "menus.hsDict",
i18n: true,
showLink: true,
keepAlive: true
@@ -39,9 +39,9 @@ const systemRouter = {
const permissionRouter = {
path: "/permission",
name: "permission",
redirect: "/permission/page",
redirect: "/permission/page/index",
meta: {
title: "message.permission",
title: "menus.permission",
icon: "Lollipop",
i18n: true,
showLink: true,
@@ -49,19 +49,19 @@ const permissionRouter = {
},
children: [
{
path: "/permission/page",
path: "/permission/page/index",
name: "permissionPage",
meta: {
title: "message.permissionPage",
title: "menus.permissionPage",
i18n: true,
showLink: true
}
},
{
path: "/permission/button",
path: "/permission/button/index",
name: "permissionButton",
meta: {
title: "message.permissionButton",
title: "menus.permissionButton",
i18n: true,
showLink: true,
authority: []
@@ -76,7 +76,7 @@ const tabsRouter = {
redirect: "/tabs/index",
meta: {
icon: "IF-team-icontabs",
title: "message.hstabs",
title: "menus.hstabs",
i18n: true,
showLink: true,
rank: 8
@@ -86,7 +86,7 @@ const tabsRouter = {
path: "/tabs/index",
name: "reTabs",
meta: {
title: "message.hstabs",
title: "menus.hstabs",
showLink: true,
i18n: true
}
@@ -99,7 +99,6 @@ const tabsRouter = {
showLink: false,
i18n: false,
dynamicLevel: 3,
realPath: "/tabs/detail",
refreshRedirect: "/tabs/index"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "vue-pure-admin",
"version": "2.7.0",
"version": "2.8.0",
"private": true,
"engines": {
"node": ">= 16",
@@ -29,6 +29,7 @@
],
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"@ctrl/tinycolor": "^3.4.0",
"@element-plus/icons-vue": "^0.2.4",
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-solid-svg-icons": "^5.15.4",
@@ -38,29 +39,27 @@
"@vueuse/core": "^6.7.1",
"@vueuse/motion": "^2.0.0-beta.4",
"animate.css": "^4.1.1",
"await-to-js": "^3.0.0",
"axios": "^0.21.1",
"cropperjs": "^1.5.11",
"css-color-function": "^1.3.3",
"dayjs": "^1.10.7",
"driver.js": "^0.9.8",
"echarts": "^5.2.1",
"element-plus": "1.2.0-beta.6",
"element-plus": "1.3.0-beta.1",
"element-resize-detector": "^1.2.3",
"font-awesome": "^4.7.0",
"js-cookie": "^3.0.1",
"lodash-es": "^4.17.21",
"lowdb": "^3.0.0",
"mitt": "^3.0.0",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"path": "^0.12.7",
"path-to-regexp": "^6.2.0",
"pinia": "^2.0.0-rc.14",
"qs": "^6.10.1",
"remixicon": "^2.5.0",
"resize-observer-polyfill": "^1.5.1",
"responsive-storage": "^1.0.11",
"sortablejs": "1.13.0",
"typescript-cookie": "^1.0.0",
"rgb-hex": "^4.0.0",
"v-contextmenu": "3.0.0",
"vue": "^3.2.24",
"vue-i18n": "^9.2.0-beta.3",
@@ -68,10 +67,9 @@
"vue-router": "^4.0.12",
"vue-types": "^4.1.0",
"vuedraggable": "4.1.0",
"vxe-table": "4.0.30",
"vxe-table": "^4.1.18",
"wangeditor": "^4.7.9",
"xe-ajax": "4.0.5",
"xe-utils": "3.4.0",
"xe-utils": "^3.5.2",
"xgplayer": "2.28.0"
},
"devDependencies": {
@@ -94,7 +92,6 @@
"@zougt/vite-plugin-theme-preprocessor": "^1.4.0",
"autoprefixer": "10.2.4",
"babel-plugin-transform-remove-console": "6.9.4",
"chalk": "2.4.2",
"cross-env": "7.0.3",
"eslint": "7.30.0",
"eslint-plugin-prettier": "3.4.0",

183
pnpm-lock.yaml generated
View File

@@ -4,6 +4,7 @@ specifiers:
"@amap/amap-jsapi-loader": ^1.0.1
"@commitlint/cli": 13.1.0
"@commitlint/config-conventional": 13.1.0
"@ctrl/tinycolor": ^3.4.0
"@element-plus/icons-vue": ^0.2.4
"@fortawesome/fontawesome-svg-core": ^1.2.36
"@fortawesome/free-solid-svg-icons": ^5.15.4
@@ -29,15 +30,15 @@ specifiers:
"@zougt/vite-plugin-theme-preprocessor": ^1.4.0
animate.css: ^4.1.1
autoprefixer: 10.2.4
await-to-js: ^3.0.0
axios: ^0.21.1
babel-plugin-transform-remove-console: 6.9.4
chalk: 2.4.2
cropperjs: ^1.5.11
cross-env: 7.0.3
css-color-function: ^1.3.3
dayjs: ^1.10.7
driver.js: ^0.9.8
echarts: ^5.2.1
element-plus: 1.2.0-beta.6
element-plus: 1.3.0-beta.1
element-resize-detector: ^1.2.3
eslint: 7.30.0
eslint-plugin-prettier: 3.4.0
@@ -47,12 +48,10 @@ specifiers:
js-cookie: ^3.0.1
lint-staged: 11.1.2
lodash-es: ^4.17.21
lowdb: ^3.0.0
mitt: ^3.0.0
mockjs: ^1.1.0
nprogress: ^0.2.0
path: ^0.12.7
path-to-regexp: ^6.2.0
pinia: ^2.0.0-rc.14
postcss: 8.2.6
postcss-import: 14.0.0
@@ -62,16 +61,15 @@ specifiers:
remixicon: ^2.5.0
resize-observer-polyfill: ^1.5.1
responsive-storage: ^1.0.11
rgb-hex: ^4.0.0
rimraf: 3.0.2
sass: ^1.45.0
sass-loader: ^12.3.0
sortablejs: 1.13.0
stylelint: 13.13.1
stylelint-config-prettier: 8.0.2
stylelint-config-standard: 22.0.0
stylelint-order: 4.1.0
typescript: 4.4.2
typescript-cookie: ^1.0.0
unplugin-element-plus: ^0.1.3
v-contextmenu: 3.0.0
vite: 2.6.14
@@ -85,14 +83,14 @@ specifiers:
vue-router: ^4.0.12
vue-types: ^4.1.0
vuedraggable: 4.1.0
vxe-table: 4.0.30
vxe-table: ^4.1.18
wangeditor: ^4.7.9
xe-ajax: 4.0.5
xe-utils: 3.4.0
xe-utils: ^3.5.2
xgplayer: 2.28.0
dependencies:
"@amap/amap-jsapi-loader": 1.0.1
"@ctrl/tinycolor": 3.4.0
"@element-plus/icons-vue": 0.2.4_vue@3.2.24
"@fortawesome/fontawesome-svg-core": 1.2.36
"@fortawesome/free-solid-svg-icons": 5.15.4
@@ -102,29 +100,27 @@ dependencies:
"@vueuse/core": 6.7.5_vue@3.2.24
"@vueuse/motion": 2.0.0-beta.4_vue@3.2.24
animate.css: 4.1.1
await-to-js: 3.0.0
axios: 0.21.4
cropperjs: 1.5.12
css-color-function: 1.3.3
dayjs: 1.10.7
driver.js: 0.9.8
echarts: 5.2.2
element-plus: 1.2.0-beta.6_vue@3.2.24
element-plus: 1.3.0-beta.1_vue@3.2.24
element-resize-detector: 1.2.3
font-awesome: 4.7.0
js-cookie: 3.0.1
lodash-es: 4.17.21
lowdb: 3.0.0
mitt: 3.0.0
mockjs: 1.1.0
nprogress: 0.2.0
path: 0.12.7
path-to-regexp: 6.2.0
pinia: 2.0.2_typescript@4.4.2+vue@3.2.24
qs: 6.10.1
remixicon: 2.5.0
resize-observer-polyfill: 1.5.1
responsive-storage: 1.0.11_vue@3.2.24
sortablejs: 1.13.0
typescript-cookie: 1.0.0
rgb-hex: 4.0.0
v-contextmenu: 3.0.0_vue@3.2.24
vue: 3.2.24
vue-i18n: 9.2.0-beta.17_vue@3.2.24
@@ -132,10 +128,9 @@ dependencies:
vue-router: 4.0.12_vue@3.2.24
vue-types: 4.1.1_vue@3.2.24
vuedraggable: 4.1.0_vue@3.2.24
vxe-table: 4.0.30_vue@3.2.24+xe-utils@3.4.0
vxe-table: 4.1.18_vue@3.2.24+xe-utils@3.5.2
wangeditor: 4.7.9
xe-ajax: 4.0.5
xe-utils: 3.4.0
xe-utils: 3.5.2
xgplayer: 2.28.0
devDependencies:
@@ -158,7 +153,6 @@ devDependencies:
"@zougt/vite-plugin-theme-preprocessor": 1.4.0_sass@1.45.0
autoprefixer: 10.2.4_postcss@8.2.6
babel-plugin-transform-remove-console: 6.9.4
chalk: 2.4.2
cross-env: 7.0.3
eslint: 7.30.0
eslint-plugin-prettier: 3.4.0_eslint@7.30.0+prettier@2.3.2
@@ -793,6 +787,14 @@ packages:
chalk: 4.1.2
dev: true
/@ctrl/tinycolor/3.4.0:
resolution:
{
integrity: sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ==
}
engines: { node: ">=10" }
dev: false
/@element-plus/icons-vue/0.2.4_vue@3.2.24:
resolution:
{
@@ -1594,10 +1596,10 @@ packages:
vue-demi: 0.12.1_vue@3.2.24
dev: false
/@vueuse/core/7.2.2_vue@3.2.24:
/@vueuse/core/7.4.3_vue@3.2.24:
resolution:
{
integrity: sha512-T9oksrPflNhsgG/Y/7IeCSmITPZ0VKDnTpK8y7SQl4ZIdLIL8L7fJyhJEgSMWyo497j/XK3RKFkOTh4GFTVeIQ==
integrity: sha512-OflPLlQ7eqaDJgKYw9DpRCbtfpkFkg1EqHFq7CFrbIEElB39wIfB9ccNypCar8e5KRDrAbOJLIOOvDZO6aC2Gg==
}
peerDependencies:
"@vue/composition-api": ^1.1.0
@@ -1608,7 +1610,7 @@ packages:
vue:
optional: true
dependencies:
"@vueuse/shared": 7.2.2_vue@3.2.24
"@vueuse/shared": 7.4.3_vue@3.2.24
vue: 3.2.24
vue-demi: 0.12.1_vue@3.2.24
dev: false
@@ -1649,10 +1651,10 @@ packages:
vue-demi: 0.12.1_vue@3.2.24
dev: false
/@vueuse/shared/7.2.2_vue@3.2.24:
/@vueuse/shared/7.4.3_vue@3.2.24:
resolution:
{
integrity: sha512-9vevEvvQgx4snSrDfZ5BFd7FmlIl9rwTtr8ySzPZhZQslx6lbcsXK3Q97I06Fv8S2TedR//X9fn2QbNtbFmdog==
integrity: sha512-tNOCbpD3uZyzxNuw2GX5LTVkMB//IZ7GS452F7TwlY07aIoGjPgixi2Yfk/xSR4CcC9iU56zprZW33aEGj1O1Q==
}
peerDependencies:
"@vue/composition-api": ^1.1.0
@@ -1943,14 +1945,6 @@ packages:
postcss-value-parser: 4.1.0
dev: true
/await-to-js/3.0.0:
resolution:
{
integrity: sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==
}
engines: { node: ">=6.0.0" }
dev: false
/axios/0.21.4:
resolution:
{
@@ -1973,6 +1967,10 @@ packages:
}
dev: true
/balanced-match/0.1.0:
resolution: { integrity: sha1-tQS9BYabOSWd0MXvw12EMXbczEo= }
dev: false
/balanced-match/1.0.2:
resolution:
{
@@ -2297,6 +2295,11 @@ packages:
is-regexp: 2.1.0
dev: true
/clone/1.0.4:
resolution: { integrity: sha1-2jCcwmPfFZlMaIypAheco8fNfH4= }
engines: { node: ">=0.8" }
dev: false
/clone/2.1.2:
resolution: { integrity: sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= }
engines: { node: ">=0.8" }
@@ -2328,7 +2331,12 @@ packages:
{
integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
}
dev: true
/color-string/0.3.0:
resolution: { integrity: sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE= }
dependencies:
color-name: 1.1.4
dev: false
/color-string/1.9.0:
resolution:
@@ -2340,6 +2348,14 @@ packages:
simple-swizzle: 0.2.2
dev: true
/color/0.11.4:
resolution: { integrity: sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q= }
dependencies:
clone: 1.0.4
color-convert: 1.9.3
color-string: 0.3.0
dev: false
/color/4.1.0:
resolution:
{
@@ -2547,6 +2563,15 @@ packages:
which: 2.0.2
dev: true
/css-color-function/1.3.3:
resolution: { integrity: sha1-jtJMLAIFBzM5+voAS8jBQfzLKC4= }
dependencies:
balanced-match: 0.1.0
color: 0.11.4
debug: 3.2.7
rgb: 0.1.0
dev: false
/css-declaration-sorter/6.1.3_postcss@8.3.11:
resolution:
{
@@ -2746,6 +2771,15 @@ packages:
ms: 2.0.0
dev: true
/debug/3.2.7:
resolution:
{
integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
}
dependencies:
ms: 2.1.2
dev: false
/debug/4.3.2:
resolution:
{
@@ -2926,6 +2960,13 @@ packages:
unidragger: 2.3.1
dev: false
/driver.js/0.9.8:
resolution:
{
integrity: sha512-bczjyKdX6XmFyCDkwtRmlaORDwfBk1xXmRO0CAe5VwNQTM98aWaG2LAIiIdTe53iV/B7W5lXlIy2xYtf0JRb7Q==
}
dev: false
/echarts/5.2.2:
resolution:
{
@@ -2947,17 +2988,18 @@ packages:
}
dev: true
/element-plus/1.2.0-beta.6_vue@3.2.24:
/element-plus/1.3.0-beta.1_vue@3.2.24:
resolution:
{
integrity: sha512-8EdSIR/5/FHcSB8w1diAh+gJMHgxIvxuZoayY99k6taAR1QyEFHuPTgFccZLopJ1+iP4UEsZFz49l57qS08Utw==
integrity: sha512-q3vMaKElPpuSTeIF7kuDmMOE+N1YVCCIG3fshXpz6qgjnxPbgZumVM0qGfhr8DTu9JxRbBoDok49dqtX/BWn3w==
}
peerDependencies:
vue: ^3.2.0
dependencies:
"@ctrl/tinycolor": 3.4.0
"@element-plus/icons-vue": 0.2.4_vue@3.2.24
"@popperjs/core": 2.11.0
"@vueuse/core": 7.2.2_vue@3.2.24
"@vueuse/core": 7.4.3_vue@3.2.24
async-validator: 4.0.7
dayjs: 1.10.7
lodash: 4.17.21
@@ -4738,16 +4780,6 @@ packages:
}
dev: true
/lowdb/3.0.0:
resolution:
{
integrity: sha512-9KZRulmIcU8fZuWiaM0d5e2/nPnrFyXkeXVpqT+MJS+vgbgOf1EbtvgQmba8HwUFgDl1oeZR6XqEJnkJmQdKmg==
}
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
dependencies:
steno: 2.1.0
dev: false
/lower-case/2.0.2:
resolution:
{
@@ -5016,7 +5048,6 @@ packages:
{
integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
}
dev: true
/multimatch/4.0.0:
resolution:
@@ -5367,6 +5398,7 @@ packages:
{
integrity: sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg==
}
dev: true
/path-type/4.0.0:
resolution:
@@ -6310,6 +6342,19 @@ packages:
engines: { iojs: ">=1.0.0", node: ">=0.10.0" }
dev: true
/rgb-hex/4.0.0:
resolution:
{
integrity: sha512-Eg2ev5CiMBnQ9Gpflmqbwbso0CCdISqtVIow7OpYSLN1ULUv2jTB9YieS1DSSn/17AD7KkPWDPzSFzI4GSuu/Q==
}
engines: { node: ">=12" }
dev: false
/rgb/0.1.0:
resolution: { integrity: sha1-vieykej+/+rBvZlylyG/pA/AN7U= }
hasBin: true
dev: false
/rimraf/3.0.2:
resolution:
{
@@ -6526,13 +6571,6 @@ packages:
tslib: 2.3.1
dev: true
/sortablejs/1.13.0:
resolution:
{
integrity: sha512-RBJirPY0spWCrU5yCmWM1eFs/XgX2J5c6b275/YyxFRgnzPhKl/TDeU2hNR8Dt7ITq66NRPM4UlOt+e5O4CFHg==
}
dev: false
/sortablejs/1.14.0:
resolution:
{
@@ -6649,14 +6687,6 @@ packages:
engines: { node: ">= 0.6" }
dev: true
/steno/2.1.0:
resolution:
{
integrity: sha512-mauOsiaqTNGFkWqIfwcm3y/fq+qKKaIWf1vf3ocOuTdco9XoHCO2AGF1gFYXuZFSWuP38Q8LBHBGJv2KnJSXyA==
}
engines: { node: ^14.13.1 || >=16.0.0 }
dev: false
/string-argv/0.3.1:
resolution:
{
@@ -7138,14 +7168,6 @@ packages:
is-typedarray: 1.0.0
dev: true
/typescript-cookie/1.0.0:
resolution:
{
integrity: sha512-oYHAgQWDqheZXiq1ODzVwwl+8lGzK/ApsxAu1a4uyl+Yd9BuF0M5I3bowgb5oAlU3Qyqejcj5kDyOZe+y+W4SA==
}
engines: { node: ">=14" }
dev: false
/typescript/4.4.2:
resolution:
{
@@ -7585,17 +7607,17 @@ packages:
vue: 3.2.24
dev: false
/vxe-table/4.0.30_vue@3.2.24+xe-utils@3.4.0:
/vxe-table/4.1.18_vue@3.2.24+xe-utils@3.5.2:
resolution:
{
integrity: sha512-sHUJL+XazADSTJlAZDwIAVeOMB3hBSj+5IeAUKHH+eKmSYju+l31og7NbgYsgRVTpG959F9EpUOnMMXvWptq/w==
integrity: sha512-hiaJv45rl8swU8YrtZVM7UhVMCrlkhxnZKuyjM1zcFhSNhuQWIy7KkewZctk83foAxlTBjEzwGNsXDuWjBW3vQ==
}
peerDependencies:
vue: ^3.2.2
xe-utils: ^3.2.0
xe-utils: ^3.5.0
dependencies:
vue: 3.2.24
xe-utils: 3.4.0
xe-utils: 3.5.2
dev: false
/wangeditor/4.7.9:
@@ -7685,17 +7707,10 @@ packages:
typedarray-to-buffer: 3.1.5
dev: true
/xe-ajax/4.0.5:
/xe-utils/3.5.2:
resolution:
{
integrity: sha512-SlVaSfOyBH2Dy51zevNbTIY1EcRlIjoP25YXcN+l9gYfl1rsQ68EsVPD6gw47sM/zzQhiPvqmzM4RzOUggbgaw==
}
dev: false
/xe-utils/3.4.0:
resolution:
{
integrity: sha512-V2o/Ew/iXBduWbj0ixqA7atUe4HFxo0HcpBsxnrLHbKVspfZ2O/7+m325AHcN2xxPZtBZGQVg1w/cQ5Q8WmcTA==
integrity: sha512-TIQct9Ed43O5J0YYRMmD15KHKUzGIW+Hetv9/diEMHT5YQVr0csC0FCFOfSgsIcuamziPVAWNFLl1QKYJE2oxA==
}
dev: false

View File

@@ -1,5 +1,5 @@
{
"Version": "2.7.0",
"Version": "2.8.0",
"Title": "PureAdmin",
"FixedHeader": true,
"HiddenSideBar": false,
@@ -8,9 +8,14 @@
"Locale": "zh",
"Layout": "vertical",
"Theme": "default",
"DarkMode": false,
"Grey": false,
"Weak": false,
"HideTabs": false,
"SidebarStatus": true,
"EpThemeColor": "#409EFF",
"ShowLogo": true,
"ShowModel": "smart",
"MapConfigure": {
"amapKey": "97b3248d1553172e81f168cf94ea667e",
"options": {

1
src/assets/svg/dark.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.38 2.019a7.5 7.5 0 1 0 10.6 10.6C21.662 17.854 17.316 22 12.001 22 6.477 22 2 17.523 2 12c0-5.315 4.146-9.661 9.38-9.981z"/></svg>

After

Width:  |  Height:  |  Size: 263 B

1
src/assets/svg/day.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></svg>

After

Width:  |  Height:  |  Size: 480 B

View File

@@ -12,8 +12,15 @@ import { useEventListener, tryOnUnmounted, useTimeoutFn } from "@vueuse/core";
let echartInstance: ECharts;
const props = defineProps({
index: {
type: Number,
default: 0
}
});
function initechartInstance() {
const echartDom = document.querySelector(".bar");
const echartDom = document.querySelector(".bar" + props.index);
if (!echartDom) return;
// @ts-ignore
echartInstance = echarts.init(echartDom);
@@ -85,12 +92,5 @@ tryOnUnmounted(() => {
</script>
<template>
<div class="bar"></div>
<div :class="'bar' + props.index" style="width: 100%; height: 35vh"></div>
</template>
<style scoped>
.bar {
width: 100%;
height: 35vh;
}
</style>

View File

@@ -10,52 +10,40 @@ const lists = ref([
</script>
<template>
<el-descriptions
class="margin-top"
direction="vertical"
:column="3"
size="medium"
border
>
<el-descriptions class="margin-top" direction="vertical" :column="3" border>
<el-descriptions-item>
<template #label>
<i class="el-icon-user"></i>
<el-icon><user /></el-icon>
用户名
</template>
xiaoxian
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<i class="el-icon-mobile-phone"></i>
<el-icon><iphone /></el-icon>
手机号
</template>
123456789
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<i class="el-icon-location-outline"></i>
<el-icon><location /></el-icon>
居住地
</template>
上海
</el-descriptions-item>
</el-descriptions>
<el-descriptions
class="margin-top"
direction="vertical"
:column="2"
size="medium"
border
>
<el-descriptions class="margin-top" direction="vertical" :column="2" border>
<el-descriptions-item>
<template #label>
<i class="el-icon-tickets"></i>
<el-icon><tickets /></el-icon>
标签
</template>
<el-tag
v-for="item in lists"
:key="item.label"
:type="item.type"
size="mini"
size="small"
effect="dark"
>
{{ item.label }}
@@ -63,22 +51,16 @@ const lists = ref([
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<i class="el-icon-office-building"></i>
<el-icon><office-building /></el-icon>
联系地址
</template>
上海市徐汇区
</el-descriptions-item>
</el-descriptions>
<el-descriptions
class="margin-top"
direction="vertical"
:column="1"
size="medium"
border
>
<el-descriptions class="margin-top" direction="vertical" :column="1" border>
<el-descriptions-item>
<template #label>
<i class="el-icon-notebook-1"></i>
<el-icon><notebook /></el-icon>
留言
</template>
好好学习天天向上
@@ -87,7 +69,7 @@ const lists = ref([
</template>
<style scoped>
.el-tag--mini {
.el-tag {
margin-right: 10px !important;
}
</style>

View File

@@ -12,8 +12,15 @@ import { useEventListener, tryOnUnmounted, useTimeoutFn } from "@vueuse/core";
let echartInstance: ECharts;
const props = defineProps({
index: {
type: Number,
default: 0
}
});
function initechartInstance() {
const echartDom = document.querySelector(".line");
const echartDom = document.querySelector(".line" + props.index);
if (!echartDom) return;
// @ts-ignore
echartInstance = echarts.init(echartDom);
@@ -73,12 +80,5 @@ tryOnUnmounted(() => {
</script>
<template>
<div class="line"></div>
<div :class="'line' + props.index" style="width: 100%; height: 35vh"></div>
</template>
<style scoped>
.line {
width: 100%;
height: 35vh;
}
</style>

View File

@@ -12,8 +12,15 @@ import { useEventListener, tryOnUnmounted, useTimeoutFn } from "@vueuse/core";
let echartInstance: ECharts;
const props = defineProps({
index: {
type: Number,
default: 0
}
});
function initechartInstance() {
const echartDom = document.querySelector(".pie");
const echartDom = document.querySelector(".pie" + props.index);
if (!echartDom) return;
// @ts-ignore
echartInstance = echarts.init(echartDom);
@@ -76,12 +83,5 @@ tryOnUnmounted(() => {
</script>
<template>
<div class="pie"></div>
<div :class="'pie' + props.index" style="width: 100%; height: 35vh"></div>
</template>
<style scoped>
.pie {
width: 100%;
height: 35vh;
}
</style>

View File

@@ -9,20 +9,24 @@ import { iconComponents } from "/@/plugins/element-plus";
* @returns component
*/
export function findIconReg(icon: string) {
// fontawesome
const faReg = /^FA-/;
// fontawesome4
const fa4Reg = /^fa-/;
// fontawesome5+
const fa5Reg = /^FA-/;
// iconfont
const iFReg = /^IF-/;
// remixicon
const riReg = /^RI-/;
// typeof icon === "function" 属于SVG
if (faReg.test(icon)) {
const text = icon.split(faReg)[1];
if (fa5Reg.test(icon)) {
const text = icon.split(fa5Reg)[1];
return findIcon(
text.slice(0, text.indexOf(" ")),
text.slice(0, text.indexOf(" ") == -1 ? text.length : text.indexOf(" ")),
"FA",
text.slice(text.indexOf(" ") + 1, text.length)
);
} else if (fa4Reg.test(icon)) {
return findIcon(icon.split(fa4Reg)[1], "fa");
} else if (iFReg.test(icon)) {
return findIcon(icon.split(iFReg)[1], "IF");
} else if (typeof icon === "function") {
@@ -45,6 +49,14 @@ export function findIcon(icon: String, type = "EL", property?: string) {
components: { FontAwesomeIcon },
template: `<font-awesome-icon :icon="icon" v-bind:[property]="true" />`
});
} else if (type === "fa") {
return defineComponent({
name: "faIcon",
data() {
return { icon: `fa ${icon}` };
},
template: `<i :class="icon" />`
});
} else if (type === "IF") {
return defineComponent({
name: "IfIcon",

View File

@@ -27,12 +27,23 @@ const transitions = computed(() => {
});
const hideTabs = computed(() => {
return instance?.sets.hideTabs;
return instance?.configure.hideTabs;
});
const layout = computed(() => {
return instance?.layout.layout === "vertical";
});
const getSectionStyle = computed(() => {
return [
hideTabs.value && layout ? "padding-top: 48px;" : "",
!hideTabs.value && layout ? "padding-top: 85px;" : "",
hideTabs.value && !layout.value ? "padding-top: 48px" : "",
!hideTabs.value && !layout.value ? "padding-top: 85px;" : "",
props.fixedHeader ? "" : "padding-top: 0;"
];
});
const transitionMain = defineComponent({
render() {
return h(
@@ -71,12 +82,7 @@ const transitionMain = defineComponent({
<template>
<section
:class="[props.fixedHeader ? 'app-main' : 'app-main-nofixed-header']"
:style="[
hideTabs && layout ? 'padding-top: 48px;' : '',
!hideTabs && layout ? 'padding-top: 85px;' : '',
hideTabs && !layout ? 'padding-top: 48px' : '',
!hideTabs && !layout ? 'padding-top: 85px;' : ''
]"
:style="getSectionStyle"
>
<router-view>
<template #default="{ Component, route }">

View File

@@ -1,4 +1,5 @@
<script setup lang="ts">
import { computed } from "vue";
import { useI18n } from "vue-i18n";
import { emitter } from "/@/utils/mitt";
import Notice from "./notice/index.vue";
@@ -22,6 +23,15 @@ const route = useRoute();
let usename = storageSession.getItem("info")?.username;
const { locale } = useI18n();
const getDropdownItemStyle = computed(() => {
return t => {
return {
background: locale.value === t ? "#1b2a47" : "",
color: locale.value === t ? "#f4f4f5" : "#000"
};
};
});
watch(
() => locale.value,
() => {
@@ -73,29 +83,23 @@ function translationEn() {
<div class="vertical-header-right">
<!-- 通知 -->
<Notice />
<Notice id="header-notice" />
<!-- 全屏 -->
<screenfull v-show="!deviceDetection()" />
<screenfull id="header-screenfull" v-show="!deviceDetection()" />
<!-- 国际化 -->
<el-dropdown trigger="click">
<el-dropdown id="header-translation" trigger="click">
<globalization />
<template #dropdown>
<el-dropdown-menu class="translation">
<el-dropdown-item
:style="{
background: locale === 'zh' ? '#1b2a47' : '',
color: locale === 'zh' ? '#f4f4f5' : '#000'
}"
:style="getDropdownItemStyle('zh')"
@click="translationCh"
><el-icon class="check-zh" v-show="locale === 'zh'"
><check /></el-icon
>简体中文</el-dropdown-item
>
<el-dropdown-item
:style="{
background: locale === 'en' ? '#1b2a47' : '',
color: locale === 'en' ? '#f4f4f5' : '#000'
}"
:style="getDropdownItemStyle('en')"
@click="translationEn"
><el-icon class="check-en" v-show="locale === 'en'"
><check /></el-icon
@@ -114,14 +118,14 @@ function translationEn() {
<el-dropdown-menu class="logout">
<el-dropdown-item @click="logout">
<i class="ri-logout-circle-r-line"></i
>{{ $t("message.hsLoginOut") }}</el-dropdown-item
>{{ $t("buttons.hsLoginOut") }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-icon
class="el-icon-setting"
:title="$t('message.hssystemSet')"
:title="$t('buttons.hssystemSet')"
@click="onPanel"
>
<Setting />
@@ -230,7 +234,7 @@ function translationEn() {
.translation {
.el-dropdown-menu__item {
padding: 0 40px !important;
padding: 5px 40px !important;
}
.el-dropdown-menu__item:focus,
@@ -242,12 +246,10 @@ function translationEn() {
.check-zh {
position: absolute;
left: 20px;
top: 13px;
}
.check-en {
position: absolute;
bottom: 13px;
left: 20px;
}
}
@@ -259,7 +261,6 @@ function translationEn() {
min-width: 100%;
display: inline-flex;
flex-wrap: wrap;
padding: 0 18px !important;
}
.el-dropdown-menu__item:focus,

View File

@@ -8,8 +8,8 @@ const { isFullscreen, toggle } = useFullscreen();
<i
:title="
isFullscreen
? $t('message.hsexitfullscreen')
: $t('message.hsfullscreen')
? $t('buttons.hsexitfullscreen')
: $t('buttons.hsfullscreen')
"
:class="
isFullscreen

View File

@@ -9,20 +9,28 @@ import {
useCssModule,
getCurrentInstance
} from "vue";
import panel from "../panel/index.vue";
import rgbHex from "rgb-hex";
import { find } from "lodash-es";
import { getConfig } from "/@/config";
import { useRouter } from "vue-router";
import panel from "../panel/index.vue";
import { emitter } from "/@/utils/mitt";
import { templateRef } from "@vueuse/core";
import dayIcon from "/@/assets/svg/day.svg";
import { debounce } from "/@/utils/debounce";
import darkIcon from "/@/assets/svg/dark.svg";
import { themeColorsType } from "../../types";
import { useAppStoreHook } from "/@/store/modules/app";
import { shadeBgColor } from "../../theme/element-plus";
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
import { storageLocal, storageSession } from "/@/utils/storage";
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
import { createNewStyle, writeNewStyle } from "../../theme/element-plus";
import { toggleTheme } from "@zougt/vite-plugin-theme-preprocessor/dist/browser-utils";
const router = useRouter();
const { isSelect } = useCssModule();
const body = document.documentElement as HTMLElement;
const instance =
getCurrentInstance().appContext.app.config.globalProperties.$storage;
@@ -71,17 +79,33 @@ if (unref(layoutTheme)) {
}
// 默认灵动模式
const markValue = ref(storageLocal.getItem("showModel") || "smart");
const markValue = ref(instance.configure?.showModel ?? "smart");
const logoVal = ref(storageLocal.getItem("logoVal") || "1");
const logoVal = ref(instance.configure?.showLogo ?? true);
const epThemeColor = ref(useEpThemeStoreHook().getEpThemeColor);
const settings = reactive({
greyVal: instance.sets.grey,
weakVal: instance.sets.weak,
tabsVal: instance.sets.hideTabs,
multiTagsCache: instance.sets.multiTagsCache
greyVal: instance.configure.grey,
weakVal: instance.configure.weak,
tabsVal: instance.configure.hideTabs,
showLogo: instance.configure.showLogo,
showModel: instance.configure.showModel,
multiTagsCache: instance.configure.multiTagsCache
});
const getThemeColorStyle = computed(() => {
return rgb => {
return { background: `rgb(${rgb})` };
};
});
function changeStorageConfigure(key, val) {
const storageConfigure = instance.configure;
storageConfigure[key] = val;
instance.configure = storageConfigure;
}
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
const targetEl = target || document.body;
let { className } = targetEl;
@@ -92,12 +116,7 @@ function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
// 灰色模式设置
const greyChange = (value): void => {
toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
instance.sets = {
grey: value,
weak: instance.sets.weak,
hideTabs: instance.sets.hideTabs,
multiTagsCache: instance.sets.multiTagsCache
};
changeStorageConfigure("grey", value);
};
// 色弱模式设置
@@ -107,77 +126,58 @@ const weekChange = (value): void => {
"html-weakness",
document.querySelector("html")
);
instance.sets = {
grey: instance.sets.grey,
weak: value,
hideTabs: instance.sets.hideTabs,
multiTagsCache: instance.sets.multiTagsCache
};
changeStorageConfigure("weak", value);
};
const tagsChange = () => {
let showVal = settings.tabsVal;
instance.sets = {
grey: instance.sets.grey,
weak: instance.sets.weak,
hideTabs: showVal,
multiTagsCache: instance.sets.multiTagsCache
};
changeStorageConfigure("hideTabs", showVal);
emitter.emit("tagViewsChange", showVal);
};
const multiTagsCacheChange = () => {
let multiTagsCache = settings.multiTagsCache;
instance.sets = {
grey: instance.sets.grey,
weak: instance.sets.weak,
hideTabs: instance.sets.hideTabs,
multiTagsCache: multiTagsCache
};
changeStorageConfigure("multiTagsCache", multiTagsCache);
useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
};
//初始化项目配置
nextTick(() => {
settings.greyVal &&
document.querySelector("html")?.setAttribute("class", "html-grey");
settings.weakVal &&
document.querySelector("html")?.setAttribute("class", "html-weakness");
settings.tabsVal && tagsChange();
});
// 清空缓存并返回登录页
function onReset() {
storageLocal.clear();
storageSession.clear();
toggleClass(false, "html-grey", document.querySelector("html"));
toggleClass(false, "html-weakness", document.querySelector("html"));
toggleClass(getConfig().Grey, "html-grey", document.querySelector("html"));
toggleClass(
getConfig().Weak,
"html-weakness",
document.querySelector("html")
);
useMultiTagsStoreHook().handleTags("equal", [
{
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
title: "menus.hshome",
icon: "HomeFilled",
i18n: true,
showLink: true
}
}
]);
useMultiTagsStoreHook().multiTagsCacheChange(getConfig().MultiTagsCache);
useEpThemeStoreHook().setEpThemeColor(getConfig().EpThemeColor);
storageLocal.clear();
storageSession.clear();
router.push("/login");
}
function onChange(label) {
storageLocal.setItem("showModel", label);
changeStorageConfigure("showModel", label);
emitter.emit("tagViewsShowModel", label);
}
// 侧边栏Logo
function logoChange() {
unref(logoVal) === "1"
? storageLocal.setItem("logoVal", "1")
: storageLocal.setItem("logoVal", "-1");
unref(logoVal)
? changeStorageConfigure("showLogo", true)
: changeStorageConfigure("showLogo", false);
emitter.emit("logoChange", unref(logoVal));
}
@@ -223,23 +223,97 @@ const getThemeColor = computed(() => {
function setLayoutModel(layout: string) {
layoutTheme.value.layout = layout;
window.document.body.setAttribute("layout", layout);
instance.layout = { layout, theme: layoutTheme.value.theme };
instance.layout = {
layout,
theme: layoutTheme.value.theme,
darkMode: instance.layout.darkMode,
sidebarStatus: instance.layout.sidebarStatus,
epThemeColor: instance.layout.epThemeColor
};
useAppStoreHook().setLayout(layout);
}
// 存放夜间主题切换前的导航主题色
let tempLayoutThemeColor;
// 设置导航主题色
function setLayoutThemeColor(theme: string) {
tempLayoutThemeColor = instance.layout.theme;
layoutTheme.value.theme = theme;
toggleTheme({
scopeName: `layout-theme-${theme}`
});
instance.layout = { layout: useAppStoreHook().layout, theme };
instance.layout = {
layout: useAppStoreHook().layout,
theme,
darkMode: dataTheme.value,
sidebarStatus: instance.layout.sidebarStatus,
epThemeColor: instance.layout.epThemeColor
};
if (theme === "default" || theme === "light") {
setEpThemeColor(getConfig().EpThemeColor);
} else {
const colors = find(themeColors.value, { themeColor: theme });
const color = "#" + rgbHex(colors.rgb);
setEpThemeColor(color);
}
}
// 设置ep主题色
const setEpThemeColor = (color: string) => {
writeNewStyle(createNewStyle(color));
useEpThemeStoreHook().setEpThemeColor(color);
body.style.setProperty("--el-color-primary-active", shadeBgColor(color));
};
let dataTheme = ref<boolean>(instance.layout.darkMode);
// 日间、夜间主题切换
function dataThemeChange() {
if (dataTheme.value) {
body.setAttribute("data-theme", "dark");
setLayoutThemeColor("light");
} else {
body.setAttribute("data-theme", "");
tempLayoutThemeColor && setLayoutThemeColor(tempLayoutThemeColor);
instance.layout = {
layout: useAppStoreHook().layout,
theme: instance.layout.theme,
darkMode: dataTheme.value,
sidebarStatus: instance.layout.sidebarStatus,
epThemeColor: instance.layout.epThemeColor
};
}
}
//初始化项目配置
nextTick(() => {
settings.greyVal &&
document.querySelector("html")?.setAttribute("class", "html-grey");
settings.weakVal &&
document.querySelector("html")?.setAttribute("class", "html-weakness");
settings.tabsVal && tagsChange();
writeNewStyle(createNewStyle(epThemeColor.value));
dataThemeChange();
});
</script>
<template>
<panel>
<el-divider>主题风格</el-divider>
<el-divider>主题</el-divider>
<el-switch
v-model="dataTheme"
inline-prompt
class="pure-datatheme"
:active-icon="dayIcon"
:inactive-icon="darkIcon"
@change="dataThemeChange"
>
</el-switch>
<el-divider>导航栏模式</el-divider>
<ul class="pure-theme">
<el-tooltip class="item" content="左侧菜单模式" placement="bottom">
<li
@@ -264,12 +338,12 @@ function setLayoutThemeColor(theme: string) {
</el-tooltip>
</ul>
<el-divider>主题色</el-divider>
<ul class="theme-color">
<el-divider v-show="!dataTheme">主题色</el-divider>
<ul class="theme-color" v-show="!dataTheme">
<li
v-for="(item, index) in themeColors"
:key="index"
:style="{ background: `rgb(${item.rgb})` }"
:style="getThemeColorStyle(item.rgb)"
@click="setLayoutThemeColor(item.themeColor)"
>
<el-icon
@@ -284,7 +358,7 @@ function setLayoutThemeColor(theme: string) {
<el-divider>界面显示</el-divider>
<ul class="setting">
<li>
<li v-show="!dataTheme">
<span>灰色模式</span>
<el-switch
v-model="settings.greyVal"
@@ -296,7 +370,7 @@ function setLayoutThemeColor(theme: string) {
>
</el-switch>
</li>
<li>
<li v-show="!dataTheme">
<span>色弱模式</span>
<el-switch
v-model="settings.weakVal"
@@ -325,8 +399,8 @@ function setLayoutThemeColor(theme: string) {
<el-switch
v-model="logoVal"
inline-prompt
active-value="1"
inactive-value="-1"
:active-value="true"
:inactive-value="false"
inactive-color="#a6a6a6"
active-text=""
inactive-text=""
@@ -391,6 +465,14 @@ function setLayoutThemeColor(theme: string) {
font-weight: 700;
}
.pure-datatheme {
width: 100%;
height: 50px;
text-align: center;
display: block;
padding-top: 25px;
}
.pure-theme {
margin-top: 25px;
width: 100%;

View File

@@ -65,7 +65,7 @@ const getBreadcrumb = (): void => {
{
path: "/welcome",
parentPath: "/",
meta: { title: "message.hshome", i18n: true }
meta: { title: "menus.hshome", i18n: true }
} as unknown as RouteLocationMatched
].concat(matched);
}

View File

@@ -35,6 +35,15 @@ const routers = useRouter().options.routes;
let usename = storageSession.getItem("info")?.username;
const { locale, t } = useI18n();
const getDropdownItemStyle = computed(() => {
return t => {
return {
background: locale.value === t ? "#1b2a47" : "",
color: locale.value === t ? "#f4f4f5" : "#000"
};
};
});
watch(
() => locale.value,
() => {
@@ -140,29 +149,23 @@ onMounted(() => {
</el-menu>
<div class="horizontal-header-right">
<!-- 通知 -->
<Notice />
<Notice id="header-notice" />
<!-- 全屏 -->
<screenfull v-show="!deviceDetection()" />
<screenfull id="header-screenfull" v-show="!deviceDetection()" />
<!-- 国际化 -->
<el-dropdown trigger="click">
<el-dropdown id="header-translation" trigger="click">
<globalization />
<template #dropdown>
<el-dropdown-menu class="translation">
<el-dropdown-item
:style="{
background: locale === 'zh' ? '#1b2a47' : '',
color: locale === 'zh' ? '#f4f4f5' : '#000'
}"
:style="getDropdownItemStyle('zh')"
@click="translationCh"
><el-icon class="check-zh" v-show="locale === 'zh'"
><check /></el-icon
>简体中文</el-dropdown-item
>
<el-dropdown-item
:style="{
background: locale === 'en' ? '#1b2a47' : '',
color: locale === 'en' ? '#f4f4f5' : '#000'
}"
:style="getDropdownItemStyle('en')"
@click="translationEn"
><el-icon class="check-en" v-show="locale === 'en'"
><check /></el-icon
@@ -181,14 +184,14 @@ onMounted(() => {
<el-dropdown-menu class="logout">
<el-dropdown-item @click="logout">
<i class="ri-logout-circle-r-line"></i
>{{ $t("message.hsLoginOut") }}</el-dropdown-item
>{{ $t("buttons.hsLoginOut") }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-icon
class="el-icon-setting"
:title="$t('message.hssystemSet')"
:title="$t('buttons.hssystemSet')"
@click="onPanel"
>
<Setting />
@@ -200,7 +203,7 @@ onMounted(() => {
<style lang="scss" scoped>
.translation {
.el-dropdown-menu__item {
padding: 0 40px !important;
padding: 5px 40px !important;
}
.el-dropdown-menu__item:focus,
@@ -212,12 +215,10 @@ onMounted(() => {
.check-zh {
position: absolute;
left: 20px;
top: 13px;
}
.check-en {
position: absolute;
bottom: 13px;
left: 20px;
}
}
@@ -229,7 +230,6 @@ onMounted(() => {
min-width: 100%;
display: inline-flex;
flex-wrap: wrap;
padding: 0 18px !important;
}
.el-dropdown-menu__item:focus,

View File

@@ -1,11 +1,19 @@
<script setup lang="ts">
import {
ref,
PropType,
nextTick,
computed,
CSSProperties,
getCurrentInstance
} from "vue";
import path from "path";
import { PropType, ref, nextTick, getCurrentInstance } from "vue";
import { childrenType } from "../../types";
import { useAppStoreHook } from "/@/store/modules/app";
import Icon from "/@/components/ReIcon/src/Icon.vue";
import { transformI18n } from "/@/plugins/i18n";
import { findIconReg } from "/@/components/ReIcon";
import Icon from "/@/components/ReIcon/src/Icon.vue";
import { useAppStoreHook } from "/@/store/modules/app";
const instance = getCurrentInstance().appContext.app.config.globalProperties;
const menuMode = instance.$storage.layout?.layout === "vertical";
const pureApp = useAppStoreHook();
@@ -24,6 +32,48 @@ const props = defineProps({
}
});
const getNoDropdownStyle = computed((): CSSProperties => {
return {
display: "flex",
alignItems: "center"
};
});
const getDivStyle = computed((): CSSProperties => {
return {
width: pureApp.sidebar.opened ? "" : "100%",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
overflow: "hidden"
};
});
const getMenuTextStyle = computed((): CSSProperties => {
return {
width: pureApp.sidebar.opened ? "125px" : "",
overflow: "hidden",
textOverflow: "ellipsis",
outline: "none"
};
});
const getSubTextStyle = computed((): CSSProperties => {
return {
width: pureApp.sidebar.opened ? "125px" : "",
display: "inline-block",
overflow: "hidden",
textOverflow: "ellipsis"
};
});
const getSpanStyle = computed((): CSSProperties => {
return {
overflow: "hidden",
textOverflow: "ellipsis"
};
});
const onlyOneChild: childrenType = ref(null);
// 存放菜单是否存在showTooltip属性标识
const hoverMenuMap = new WeakMap();
@@ -88,7 +138,7 @@ function resolvePath(routePath) {
<el-menu-item
:index="resolvePath(onlyOneChild.path)"
:class="{ 'submenu-title-noDropdown': !isNest }"
style="display: flex; align-items: center"
:style="getNoDropdownStyle"
>
<el-icon v-show="props.item.meta.icon">
<component
@@ -101,15 +151,7 @@ function resolvePath(routePath) {
></component>
</el-icon>
<template #title>
<div
:style="{
width: pureApp.sidebar.opened ? '' : '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
overflow: 'hidden'
}"
>
<div :style="getDivStyle">
<span v-if="!menuMode">{{
transformI18n(onlyOneChild.meta.title, onlyOneChild.meta.i18n)
}}</span>
@@ -126,12 +168,7 @@ function resolvePath(routePath) {
</template>
<span
ref="menuTextRef"
:style="{
width: pureApp.sidebar.opened ? '125px' : '',
overflow: 'hidden',
textOverflow: 'ellipsis',
outline: 'none'
}"
:style="getMenuTextStyle"
@mouseover="hoverMenu(onlyOneChild)"
>
{{
@@ -175,15 +212,10 @@ function resolvePath(routePath) {
</template>
<div
ref="menuTextRef"
:style="{
width: pureApp.sidebar.opened ? '125px' : '',
display: 'inline-block',
overflow: 'hidden',
textOverflow: 'ellipsis'
}"
:style="getSubTextStyle"
@mouseover="hoverMenu(props.item)"
>
<span style="overflow: hidden; text-overflow: ellipsis">
<span :style="getSpanStyle">
{{ transformI18n(props.item.meta.title, props.item.meta.i18n) }}
</span>
</div>

View File

@@ -12,7 +12,9 @@ import { usePermissionStoreHook } from "/@/store/modules/permission";
const route = useRoute();
const pureApp = useAppStoreHook();
const router = useRouter().options.routes;
const showLogo = ref(storageLocal.getItem("logoVal") || "1");
const showLogo = ref(
storageLocal.getItem("responsive-configure")?.showLogo ?? true
);
const isCollapse = computed(() => {
return !pureApp.getSidebarStatus;
});
@@ -58,7 +60,7 @@ onBeforeMount(() => {
<template>
<div :class="['sidebar-container', showLogo ? 'has-logo' : '']">
<Logo v-if="showLogo === '1'" :collapse="isCollapse" />
<Logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"

View File

@@ -207,7 +207,6 @@
li {
width: 100%;
margin: 0;
padding: 0 12px;
cursor: pointer;
display: flex;
align-items: center;

View File

@@ -2,12 +2,13 @@
import {
ref,
watch,
onBeforeMount,
unref,
nextTick,
computed,
getCurrentInstance,
ComputedRef
ComputedRef,
CSSProperties,
onBeforeMount,
getCurrentInstance
} from "vue";
import close from "/@/assets/svg/close.svg";
@@ -17,6 +18,7 @@ import closeLeft from "/@/assets/svg/close_left.svg";
import closeOther from "/@/assets/svg/close_other.svg";
import closeRight from "/@/assets/svg/close_right.svg";
import { $t as t } from "/@/plugins/i18n";
import { emitter } from "/@/utils/mitt";
import { isEqual, isEmpty } from "lodash-es";
import { transformI18n } from "/@/plugins/i18n";
@@ -187,42 +189,42 @@ const handleScroll = (offset: number): void => {
const tagsViews = ref<Array<tagsViewsType>>([
{
icon: refresh,
text: "message.hsreload",
text: t("buttons.hsreload"),
divided: false,
disabled: false,
show: true
},
{
icon: close,
text: "message.hscloseCurrentTab",
text: t("buttons.hscloseCurrentTab"),
divided: false,
disabled: multiTags.value.length > 1 ? false : true,
show: true
},
{
icon: closeLeft,
text: "message.hscloseLeftTabs",
text: t("buttons.hscloseLeftTabs"),
divided: true,
disabled: multiTags.value.length > 1 ? false : true,
show: true
},
{
icon: closeRight,
text: "message.hscloseRightTabs",
text: t("buttons.hscloseRightTabs"),
divided: false,
disabled: multiTags.value.length > 1 ? false : true,
show: true
},
{
icon: closeOther,
text: "message.hscloseOtherTabs",
text: t("buttons.hscloseOtherTabs"),
divided: true,
disabled: multiTags.value.length > 2 ? false : true,
show: true
},
{
icon: closeAll,
text: "message.hscloseAllTabs",
text: t("buttons.hscloseAllTabs"),
divided: false,
disabled: multiTags.value.length > 1 ? false : true,
show: true
@@ -230,9 +232,13 @@ const tagsViews = ref<Array<tagsViewsType>>([
]);
// 显示模式,默认灵动模式显示
const showModel = ref(storageLocal.getItem("showModel") || "smart");
const showModel = ref(
storageLocal.getItem("responsive-configure")?.showModel || "smart"
);
if (!showModel.value) {
storageLocal.setItem("showModel", "card");
const configure = storageLocal.getItem("responsive-configure");
configure.showModel = "card";
storageLocal.setItem("responsive-configure", configure);
}
let visible = ref(false);
@@ -306,7 +312,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
title: "menus.hshome",
i18n: true,
icon: "el-icon-s-home",
showLink: true
@@ -609,17 +615,23 @@ onBeforeMount(() => {
});
});
});
const getTabStyle = computed((): CSSProperties => {
return {
transform: `translateX(${translateX.value}px)`
};
});
const getContextMenuStyle = computed((): CSSProperties => {
return { left: buttonLeft.value + "px", top: buttonTop.value + "px" };
});
</script>
<template>
<div ref="containerDom" class="tags-view" v-if="!showTags">
<i class="ri-arrow-left-s-line" @click="handleScroll(200)"></i>
<div ref="scrollbarDom" class="scroll-container">
<div
class="tab"
ref="tabDom"
:style="{ transform: `translateX(${translateX}px)` }"
>
<div class="tab" ref="tabDom" :style="getTabStyle">
<div
:ref="'dynamic' + index"
v-for="(item, index) in multiTags"
@@ -663,7 +675,7 @@ onBeforeMount(() => {
<ul
v-show="visible"
:key="Math.random()"
:style="{ left: buttonLeft + 'px', top: buttonTop + 'px' }"
:style="getContextMenuStyle"
class="contextmenu"
>
<div
@@ -682,7 +694,7 @@ onBeforeMount(() => {
<ul class="right-button">
<li>
<el-icon
:title="$t('message.hsrefreshRoute')"
:title="$t('buttons.hsrefreshRoute')"
class="el-icon-refresh-right rotate"
@click="onFresh"
>

View File

@@ -16,8 +16,8 @@ import { useAppStoreHook } from "/@/store/modules/app";
import fullScreen from "/@/assets/svg/full_screen.svg";
import exitScreen from "/@/assets/svg/exit_screen.svg";
import { deviceDetection } from "/@/utils/deviceDetection";
import { useSettingStoreHook } from "/@/store/modules/settings";
import { useMultiTagsStore } from "/@/store/modules/multiTags";
import { useSettingStoreHook } from "/@/store/modules/settings";
import navbar from "./components/navbar.vue";
import tag from "./components/tag/index.vue";
@@ -51,16 +51,21 @@ const layout = computed(() => {
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
instance.$storage.layout = {
layout: instance.$config?.Layout ?? "vertical",
theme: instance.$config?.Theme ?? "default"
theme: instance.$config?.Theme ?? "default",
darkMode: instance.$config?.DarkMode ?? false,
sidebarStatus: instance.$config?.SidebarStatus ?? true,
epThemeColor: instance.$config?.EpThemeColor ?? "#409EFF"
};
}
// 灰色模式、色弱模式、隐藏标签页
if (!instance.$storage.sets) {
if (!instance.$storage.configure) {
// eslint-disable-next-line
instance.$storage.sets = {
instance.$storage.configure = {
grey: instance.$config?.Grey ?? false,
weak: instance.$config?.Weak ?? false,
hideTabs: instance.$config?.HideTabs ?? false,
showLogo: instance.$config?.ShowLogo ?? true,
showModel: instance.$config?.ShowModel ?? "smart",
multiTagsCache: instance.$config?.MultiTagsCache ?? false
};
}
@@ -90,7 +95,7 @@ const set: setType = reactive({
}),
hideTabs: computed(() => {
return instance.$storage?.sets.hideTabs;
return instance.$storage?.configure.hideTabs;
})
});
@@ -98,7 +103,10 @@ function setTheme(layoutModel: string) {
window.document.body.setAttribute("layout", layoutModel);
instance.$storage.layout = {
layout: `${layoutModel}`,
theme: instance.$storage.layout?.theme
theme: instance.$storage.layout?.theme,
darkMode: instance.$storage.layout?.darkMode,
sidebarStatus: instance.$storage.layout?.sidebarStatus,
epThemeColor: instance.$storage.layout?.epThemeColor
};
}

View File

@@ -0,0 +1,80 @@
/* 动态改变element-plus主题色 */
import rgbHex from "rgb-hex";
import color from "css-color-function";
import { TinyColor } from "@ctrl/tinycolor";
import epCss from "element-plus/dist/index.css";
// 色值表
const formula = {
"shade-1": "color(primary shade(10%))",
"light-1": "color(primary tint(10%))",
"light-2": "color(primary tint(20%))",
"light-3": "color(primary tint(30%))",
"light-4": "color(primary tint(40%))",
"light-5": "color(primary tint(50%))",
"light-6": "color(primary tint(60%))",
"light-7": "color(primary tint(70%))",
"light-8": "color(primary tint(80%))",
"light-9": "color(primary tint(90%))"
};
// 把生成的样式表写入到style中
export const writeNewStyle = (newStyle: string): void => {
const style = window.document.createElement("style");
style.innerText = newStyle;
window.document.head.appendChild(style);
};
// 根据主题色,生成最新的样式表
export const createNewStyle = (primaryStyle: string): string => {
// 根据主色生成色值表
const colors = createColors(primaryStyle);
// 在当前ep的默认样式表中标记需要替换的色值
let cssText = getStyleTemplate(epCss);
// 遍历生成的色值表,在 默认样式表 进行全局替换
Object.keys(colors).forEach(key => {
cssText = cssText.replace(
new RegExp("(:|\\s+)" + key, "g"),
"$1" + colors[key]
);
});
return cssText;
};
export const createColors = (primary: string) => {
if (!primary) return;
const colors = {
primary
};
Object.keys(formula).forEach(key => {
const value = formula[key].replace(/primary/, primary);
colors[key] = "#" + rgbHex(color.convert(value));
});
return colors;
};
const getStyleTemplate = (data: string): string => {
const colorMap = {
"#3a8ee6": "shade-1",
"#409eff": "primary",
"#53a8ff": "light-1",
"#66b1ff": "light-2",
"#79bbff": "light-3",
"#8cc5ff": "light-4",
"#a0cfff": "light-5",
"#b3d8ff": "light-6",
"#c6e2ff": "light-7",
"#d9ecff": "light-8",
"#ecf5ff": "light-9"
};
Object.keys(colorMap).forEach(key => {
const value = colorMap[key];
data = data.replace(new RegExp(key, "ig"), value);
});
return data;
};
// 自动计算hover和active颜色 https://element-plus.gitee.io/zh-CN/component/button.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E9%A2%9C%E8%89%B2-%E6%B5%8B%E8%AF%95%E7%89%88
export const shadeBgColor = (color: string): string => {
return new TinyColor(color).shade(10).toString();
};

View File

@@ -3,9 +3,9 @@ export const routerArrays: Array<RouteConfigs> = [
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
title: "menus.hshome",
i18n: true,
icon: "el-icon-s-home",
icon: "HomeFilled",
showLink: true
}
}

View File

@@ -115,7 +115,14 @@ import {
ArrowDown,
Close,
CloseBold,
Bell
Bell,
Guide,
User,
Iphone,
Location,
Tickets,
OfficeBuilding,
Notebook
} from "@element-plus/icons-vue";
// Icon
@@ -134,7 +141,14 @@ export const iconComponents = [
ArrowDown,
Close,
CloseBold,
Bell
Bell,
Guide,
User,
Iphone,
Location,
Tickets,
OfficeBuilding,
Notebook
];
export function useElementPlus(app: App) {

View File

@@ -1,4 +1,4 @@
// 菜单国际化配置
import { siphonI18n } from "./index";
// vxe-table组件国际化
import zhVxeTable from "vxe-table/lib/locale/lang/zh-CN";
import enVxeTable from "vxe-table/lib/locale/lang/en-US";
@@ -7,156 +7,18 @@ import enVxeTable from "vxe-table/lib/locale/lang/en-US";
import enLocale from "element-plus/lib/locale/lang/en";
import zhLocale from "element-plus/lib/locale/lang/zh-cn";
// 导航菜单配置
export const menusConfig = {
zh: {
message: {
hshome: "首页",
hssysManagement: "系统管理",
hsBaseinfo: "基础信息",
hsDict: "字典管理",
hseditor: "编辑器",
hserror: "错误页面",
hsfourZeroFour: "404",
hsfourZeroOne: "401",
hscomponents: "组件",
hsvideo: "视频组件",
hsmap: "地图组件",
hsdraggable: "拖拽组件",
hssplitPane: "切割面板",
hsbutton: "按钮组件",
hscropping: "图片裁剪",
hscountTo: "数字动画",
hsselector: "选择器组件",
hsflowChart: "流程图",
hsseamless: "无缝滚动",
hscontextmenu: "右键菜单",
hsmenus: "多级菜单",
hsmenu1: "菜单1",
"hsmenu1-1": "菜单1-1",
"hsmenu1-2": "菜单1-2",
"hsmenu1-2-1": "菜单1-2-1",
"hsmenu1-2-2": "菜单1-2-2",
"hsmenu1-3": "菜单1-3",
hsmenu2: "菜单2",
permission: "权限管理",
permissionPage: "页面权限",
permissionButton: "按钮权限",
hstabs: "标签页操作",
hsMenuTree: "菜单树结构",
externalLink: "外链"
}
},
en: {
message: {
hshome: "Home",
hssysManagement: "System Manage",
hsBaseinfo: "Base Info",
hsDict: "Dict Manage",
hseditor: "Editor",
hserror: "Error Page",
hsfourZeroFour: "404",
hsfourZeroOne: "401",
hscomponents: "Components",
hsvideo: "Video Components",
hsmap: "Map Components",
hsdraggable: "Draggable Components",
hssplitPane: "Split Pane",
hsbutton: "Button Components",
hscropping: "Picture Cropping",
hscountTo: "Digital Animation",
hsselector: "Selector Components",
hsflowChart: "Flow Chart",
hsseamless: "Seamless Scroll",
hscontextmenu: "Context Menu",
hsmenus: "MultiLevel Menu",
hsmenu1: "Menu1",
"hsmenu1-1": "Menu1-1",
"hsmenu1-2": "Menu1-2",
"hsmenu1-2-1": "Menu1-2-1",
"hsmenu1-2-2": "Menu1-2-2",
"hsmenu1-3": "Menu1-3",
hsmenu2: "Menu2",
permission: "Permission Manage",
permissionPage: "Page Permission",
permissionButton: "Button Permission",
hstabs: "Tabs Operate",
hsMenuTree: "Menu Tree",
externalLink: "External Link"
}
}
};
// 按钮配置
export const buttonConfig = {
zh: {
message: {
hsLoginOut: "退出系统",
hsfullscreen: "全屏",
hsexitfullscreen: "退出全屏",
hsrefreshRoute: "刷新路由",
hslogin: "登陆",
hsadd: "新增",
hsmark: "标记/取消",
hssave: "保存",
hssearch: "搜索",
hsexpendAll: "全部展开",
hscollapseAll: "全部折叠",
hssystemSet: "打开项目配置",
hsdelete: "删除",
hsreload: "重新加载",
hscloseCurrentTab: "关闭当前标签页",
hscloseLeftTabs: "关闭左侧标签页",
hscloseRightTabs: "关闭右侧标签页",
hscloseOtherTabs: "关闭其他标签页",
hscloseAllTabs: "关闭全部标签页"
}
},
en: {
message: {
hsLoginOut: "loginOut",
hsfullscreen: "fullScreen",
hsexitfullscreen: "exitFullscreen",
hsrefreshRoute: "refreshRoute",
hslogin: "login",
hsadd: "Add",
hsmark: "Mark/Cancel",
hssave: "Save",
hssearch: "Search",
hsexpendAll: "Expand All",
hscollapseAll: "Collapse All",
hssystemSet: "Open ProjectConfig",
hsdelete: "Delete",
hsreload: "Reload",
hscloseCurrentTab: "Close CurrentTab",
hscloseLeftTabs: "Close LeftTabs",
hscloseRightTabs: "Close RightTabs",
hscloseOtherTabs: "Close OtherTabs",
hscloseAllTabs: "Close AllTabs"
}
}
};
// 配置
// export const xxxx = {
// zh: {
// message: {},
// },
// en: {
// message: {},
// },
// };
const localesList = [menusConfig, buttonConfig];
// 项目内自定义国际化
const zhModules = import.meta.globEager("./zh-CN/**/*.ts");
const enModules = import.meta.globEager("./en/**/*.ts");
export const localesConfigs = {
zh: {
message: Object.assign({}, ...localesList.map(v => v.zh.message)),
...siphonI18n(zhModules, "zh-CN"),
...zhVxeTable,
...zhLocale
},
en: {
message: Object.assign({}, ...localesList.map(v => v.en.message)),
...siphonI18n(enModules, "en"),
...enVxeTable,
...enLocale
}

View File

@@ -0,0 +1,21 @@
export default {
hsLoginOut: "LoginOut",
hsfullscreen: "FullScreen",
hsexitfullscreen: "ExitFullscreen",
hsrefreshRoute: "RefreshRoute",
hslogin: "Login",
hsadd: "Add",
hsmark: "Mark/Cancel",
hssave: "Save",
hssearch: "Search",
hsexpendAll: "Expand All",
hscollapseAll: "Collapse All",
hssystemSet: "Open ProjectConfig",
hsdelete: "Delete",
hsreload: "Reload",
hscloseCurrentTab: "Close CurrentTab",
hscloseLeftTabs: "Close LeftTabs",
hscloseRightTabs: "Close RightTabs",
hscloseOtherTabs: "Close OtherTabs",
hscloseAllTabs: "Close AllTabs"
};

View File

@@ -0,0 +1,38 @@
export default {
hshome: "Home",
hslogin: "Login",
hssysManagement: "System Manage",
hsBaseinfo: "Base Info",
hsDict: "Dict Manage",
hseditor: "Editor",
hserror: "Error Page",
hsfourZeroFour: "404",
hsfourZeroOne: "401",
hscomponents: "Components",
hsvideo: "Video Components",
hsmap: "Map Components",
hsdraggable: "Draggable Components",
hssplitPane: "Split Pane",
hsbutton: "Button Components",
hscropping: "Picture Cropping",
hscountTo: "Digital Animation",
hsselector: "Selector Components",
hsflowChart: "Flow Chart",
hsseamless: "Seamless Scroll",
hscontextmenu: "Context Menu",
hsmenus: "MultiLevel Menu",
hsmenu1: "Menu1",
"hsmenu1-1": "Menu1-1",
"hsmenu1-2": "Menu1-2",
"hsmenu1-2-1": "Menu1-2-1",
"hsmenu1-2-2": "Menu1-2-2",
"hsmenu1-3": "Menu1-3",
hsmenu2: "Menu2",
permission: "Permission Manage",
permissionPage: "Page Permission",
permissionButton: "Button Permission",
hstabs: "Tabs Operate",
hsMenuTree: "Menu Tree",
hsguide: "Guide",
externalLink: "External Link"
};

View File

@@ -1,19 +1,10 @@
// 多组件库的国际化和本地项目国际化兼容
import { App } from "vue";
import { set } from "lodash-es";
import { createI18n } from "vue-i18n";
import { localesConfigs } from "./config";
import { storageLocal } from "/@/utils/storage";
export const i18n = createI18n({
locale: storageLocal.getItem("responsive-locale")?.locale ?? "zh",
fallbackLocale: "en",
messages: localesConfigs
});
export function usI18n(app: App) {
app.use(i18n);
}
/**
* 国际化转换工具函数
* @param message message
@@ -37,3 +28,47 @@ export function transformI18n(message: string | object = "", isI18n = false) {
return message;
}
}
/**
* 从模块中抽取国际化
* @param langs 存放国际化模块
* @param prefix 语言 默认 zh-CN
* @returns obj 格式:{模块名.**}
*/
export function siphonI18n(
langs: Record<string, Record<string, any>>,
prefix = "zh-CN"
) {
const langsObj: Recordable = {};
Object.keys(langs).forEach((key: string) => {
let fileName = key.replace(`./${prefix}/`, "").replace(/^\.\//, "");
fileName = fileName.substring(0, fileName.lastIndexOf("."));
const keyList = fileName.split("/");
const moduleName = keyList.shift();
const objKey = keyList.join(".");
const langFileModule = langs[key].default;
if (moduleName) {
if (objKey) {
set(langsObj, moduleName, langsObj[moduleName] || {});
set(langsObj[moduleName], objKey, langFileModule);
} else {
set(langsObj, moduleName, langFileModule || {});
}
}
});
return langsObj;
}
// 此函数只是配合i18n Ally插件来进行国际化智能提示并无实际意义只对提示起作用如果不需要国际化可删除
export const $t = (key: string) => key;
export const i18n = createI18n({
locale: storageLocal.getItem("responsive-locale")?.locale ?? "zh",
fallbackLocale: "en",
messages: localesConfigs
});
export function usI18n(app: App) {
app.use(i18n);
}

View File

@@ -0,0 +1,21 @@
export default {
hsLoginOut: "退出系统",
hsfullscreen: "全屏",
hsexitfullscreen: "退出全屏",
hsrefreshRoute: "刷新路由",
hslogin: "登陆",
hsadd: "新增",
hsmark: "标记/取消",
hssave: "保存",
hssearch: "搜索",
hsexpendAll: "全部展开",
hscollapseAll: "全部折叠",
hssystemSet: "打开项目配置",
hsdelete: "删除",
hsreload: "重新加载",
hscloseCurrentTab: "关闭当前标签页",
hscloseLeftTabs: "关闭左侧标签页",
hscloseRightTabs: "关闭右侧标签页",
hscloseOtherTabs: "关闭其他标签页",
hscloseAllTabs: "关闭全部标签页"
};

View File

@@ -0,0 +1,38 @@
export default {
hshome: "首页",
hslogin: "登陆",
hssysManagement: "系统管理",
hsBaseinfo: "基础信息",
hsDict: "字典管理",
hseditor: "编辑器",
hserror: "错误页面",
hsfourZeroFour: "404",
hsfourZeroOne: "401",
hscomponents: "组件",
hsvideo: "视频组件",
hsmap: "地图组件",
hsdraggable: "拖拽组件",
hssplitPane: "切割面板",
hsbutton: "按钮组件",
hscropping: "图片裁剪",
hscountTo: "数字动画",
hsselector: "选择器组件",
hsflowChart: "流程图",
hsseamless: "无缝滚动",
hscontextmenu: "右键菜单",
hsmenus: "多级菜单",
hsmenu1: "菜单1",
"hsmenu1-1": "菜单1-1",
"hsmenu1-2": "菜单1-2",
"hsmenu1-2-1": "菜单1-2-1",
"hsmenu1-2-2": "菜单1-2-2",
"hsmenu1-3": "菜单1-3",
hsmenu2: "菜单2",
permission: "权限管理",
permissionPage: "页面权限",
permissionButton: "按钮权限",
hstabs: "标签页操作",
hsMenuTree: "菜单树结构",
hsguide: "引导页",
externalLink: "外链"
};

View File

@@ -65,8 +65,8 @@ VXETable.setup({
i18n: (key, args) => i18n.global.t(key, args),
// 可选,对参数中的列头、校验提示..等进行自动翻译(只对支持国际化的有效)
translate(key, args) {
// 例如,只翻译 "message." 开头的键值
if (key && key.indexOf("message.") > -1) {
// 例如,只翻译 "buttons." 开头的键值
if (key && key.indexOf("buttons.") > -1) {
return i18n.global.t(key, args);
}
if (key && key.indexOf("el.") > -1) {

View File

@@ -89,7 +89,7 @@ router.beforeEach((to: toRouteType, _from, next) => {
});
};
// 未开启标签页缓存,刷新页面重定向到顶级路由(参考标签页操作例子,只针对静态路由)
if (to.meta?.realPath) {
if (to.meta?.refreshRedirect) {
const routes = router.options.routes;
const { refreshRedirect } = to.meta;
const { name, meta } = findRouteByPath(refreshRedirect, routes);
@@ -117,8 +117,9 @@ router.beforeEach((to: toRouteType, _from, next) => {
route?.meta?.rank !== 0 &&
routePartent.length === 0
) {
if (!route?.meta?.refreshRedirect) return;
const { name, meta } = findRouteByPath(
route?.meta?.refreshRedirect,
route.meta.refreshRedirect,
routes
);
handTag(

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const componentsRouter = {
@@ -7,7 +8,7 @@ const componentsRouter = {
redirect: "/components/video",
meta: {
icon: "Menu",
title: "message.hscomponents",
title: $t("menus.hscomponents"),
i18n: true,
showLink: true,
rank: 4
@@ -18,7 +19,7 @@ const componentsRouter = {
name: "video",
component: () => import("/@/views/components/video/index.vue"),
meta: {
title: "message.hsvideo",
title: $t("menus.hsvideo"),
showLink: true,
i18n: true
}
@@ -28,7 +29,7 @@ const componentsRouter = {
name: "map",
component: () => import("/@/views/components/map/index.vue"),
meta: {
title: "message.hsmap",
title: $t("menus.hsmap"),
showLink: true,
keepAlive: true,
i18n: true,
@@ -42,7 +43,7 @@ const componentsRouter = {
name: "draggable",
component: () => import("/@/views/components/draggable/index.vue"),
meta: {
title: "message.hsdraggable",
title: $t("menus.hsdraggable"),
showLink: true,
i18n: true,
transition: {
@@ -57,7 +58,7 @@ const componentsRouter = {
name: "splitPane",
component: () => import("/@/views/components/split-pane/index.vue"),
meta: {
title: "message.hssplitPane",
title: $t("menus.hssplitPane"),
showLink: true,
i18n: true,
extraIcon: {
@@ -71,7 +72,7 @@ const componentsRouter = {
name: "button",
component: () => import("/@/views/components/button/index.vue"),
meta: {
title: "message.hsbutton",
title: $t("menus.hsbutton"),
i18n: true,
showLink: true
}
@@ -81,7 +82,7 @@ const componentsRouter = {
name: "cropping",
component: () => import("/@/views/components/cropping/index.vue"),
meta: {
title: "message.hscropping",
title: $t("menus.hscropping"),
i18n: true,
showLink: true
}
@@ -91,7 +92,7 @@ const componentsRouter = {
name: "countTo",
component: () => import("/@/views/components/count-to/index.vue"),
meta: {
title: "message.hscountTo",
title: $t("menus.hscountTo"),
i18n: true,
showLink: true
}
@@ -101,7 +102,7 @@ const componentsRouter = {
name: "selector",
component: () => import("/@/views/components/selector/index.vue"),
meta: {
title: "message.hsselector",
title: $t("menus.hsselector"),
i18n: true,
showLink: true
}
@@ -111,7 +112,7 @@ const componentsRouter = {
name: "seamlessScroll",
component: () => import("/@/views/components/seamless-scroll/index.vue"),
meta: {
title: "message.hsseamless",
title: $t("menus.hsseamless"),
i18n: true,
showLink: true
}
@@ -121,7 +122,7 @@ const componentsRouter = {
name: "contextmenu",
component: () => import("/@/views/components/contextmenu/index.vue"),
meta: {
title: "message.hscontextmenu",
title: $t("menus.hscontextmenu"),
i18n: true,
showLink: true
}

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const editorRouter = {
@@ -7,7 +8,7 @@ const editorRouter = {
redirect: "/editor/index",
meta: {
icon: "Edit",
title: "message.hseditor",
title: $t("menus.hseditor"),
i18n: true,
showLink: true,
rank: 2
@@ -18,7 +19,7 @@ const editorRouter = {
name: "reEditor",
component: () => import("/@/views/editor/index.vue"),
meta: {
title: "message.hseditor",
title: $t("menus.hseditor"),
showLink: true,
i18n: true,
keepAlive: true,

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const errorRouter = {
@@ -7,7 +8,7 @@ const errorRouter = {
redirect: "/error/401",
meta: {
icon: "Position",
title: "message.hserror",
title: $t("menus.hserror"),
showLink: true,
i18n: true,
rank: 7
@@ -18,7 +19,7 @@ const errorRouter = {
name: "401",
component: () => import("/@/views/error/401.vue"),
meta: {
title: "message.hsfourZeroOne",
title: $t("menus.hsfourZeroOne"),
i18n: true,
showLink: true
}
@@ -28,7 +29,7 @@ const errorRouter = {
name: "404",
component: () => import("/@/views/error/404.vue"),
meta: {
title: "message.hsfourZeroFour",
title: $t("menus.hsfourZeroFour"),
i18n: true,
showLink: true
}

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const externalLink = {
@@ -6,7 +7,7 @@ const externalLink = {
component: Layout,
meta: {
icon: "Link",
title: "message.externalLink",
title: $t("menus.externalLink"),
showLink: true,
i18n: true,
rank: 190
@@ -15,7 +16,7 @@ const externalLink = {
{
path: "https://github.com/xiaoxian521/vue-pure-admin",
meta: {
title: "message.externalLink",
title: $t("menus.externalLink"),
showLink: true,
i18n: true,
rank: 191

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const flowChartRouter = {
@@ -7,7 +8,7 @@ const flowChartRouter = {
redirect: "/flowChart/index",
meta: {
icon: "SetUp",
title: "message.hsflowChart",
title: $t("menus.hsflowChart"),
showLink: true,
i18n: true,
rank: 1
@@ -18,7 +19,7 @@ const flowChartRouter = {
name: "flowChart",
component: () => import("/@/views/flow-chart/index.vue"),
meta: {
title: "message.hsflowChart",
title: $t("menus.hsflowChart"),
i18n: true,
showLink: true
}

View File

@@ -0,0 +1,30 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const guideRouter = {
path: "/guide",
name: "reGuide",
component: Layout,
redirect: "/guide/index",
meta: {
icon: "Guide",
title: $t("menus.hsguide"),
i18n: true,
showLink: true,
rank: 10
},
children: [
{
path: "/guide/index",
name: "reGuide",
component: () => import("/@/views/guide/index.vue"),
meta: {
title: $t("menus.hsguide"),
showLink: true,
i18n: true
}
}
]
};
export default guideRouter;

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const homeRouter = {
@@ -7,7 +8,7 @@ const homeRouter = {
redirect: "/welcome",
meta: {
icon: "HomeFilled",
title: "message.hshome",
title: $t("menus.hshome"),
showLink: true,
i18n: true,
rank: 0
@@ -18,7 +19,7 @@ const homeRouter = {
name: "welcome",
component: () => import("/@/views/welcome.vue"),
meta: {
title: "message.hshome",
title: $t("menus.hshome"),
i18n: true,
showLink: true
}

View File

@@ -1,6 +1,7 @@
// 静态路由
import homeRouter from "./home";
import errorRouter from "./error";
import guideRouter from "./guide";
import editorRouter from "./editor";
import nestedRouter from "./nested";
import menuTreeRouter from "./menuTree";
@@ -20,6 +21,7 @@ import {
const routes = [
homeRouter,
errorRouter,
guideRouter,
nestedRouter,
externalLink,
editorRouter,

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const menuTreeRouter = {
@@ -7,7 +8,7 @@ const menuTreeRouter = {
redirect: "/menuTree/index",
meta: {
icon: "RI-node-tree",
title: "message.hsMenuTree",
title: $t("menus.hsMenuTree"),
i18n: true,
showLink: true,
rank: 9
@@ -18,7 +19,7 @@ const menuTreeRouter = {
name: "reMenuTree",
component: () => import("/@/views/menu-tree/index.vue"),
meta: {
title: "message.hsMenuTree",
title: $t("menus.hsMenuTree"),
showLink: true,
i18n: true
}

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const nestedRouter = {
@@ -6,7 +7,7 @@ const nestedRouter = {
redirect: "/nested/menu1/menu1-1",
name: "Nested",
meta: {
title: "message.hsmenus",
title: $t("menus.hsmenus"),
icon: "Histogram",
showLink: true,
i18n: true,
@@ -18,7 +19,7 @@ const nestedRouter = {
component: () => import("/@/layout/routerView/parent.vue"),
name: "Menu1",
meta: {
title: "message.hsmenu1",
title: $t("menus.hsmenu1"),
showLink: true,
i18n: true,
keepAlive: true
@@ -30,7 +31,7 @@ const nestedRouter = {
component: () => import("/@/views/nested/menu1/menu1-1/index.vue"),
name: "Menu1-1",
meta: {
title: "message.hsmenu1-1",
title: $t("menus.hsmenu1-1"),
showLink: true,
i18n: true,
keepAlive: true
@@ -42,7 +43,7 @@ const nestedRouter = {
name: "Menu1-2",
redirect: "/nested/menu1/menu1-2/menu1-2-1",
meta: {
title: "message.hsmenu1-2",
title: $t("menus.hsmenu1-2"),
showLink: true,
i18n: true,
keepAlive: true
@@ -54,7 +55,7 @@ const nestedRouter = {
import("/@/views/nested/menu1/menu1-2/menu1-2-1/index.vue"),
name: "Menu1-2-1",
meta: {
title: "message.hsmenu1-2-1",
title: $t("menus.hsmenu1-2-1"),
showLink: true,
i18n: true,
keepAlive: true
@@ -66,7 +67,7 @@ const nestedRouter = {
import("/@/views/nested/menu1/menu1-2/menu1-2-2/index.vue"),
name: "Menu1-2-2",
meta: {
title: "message.hsmenu1-2-2",
title: $t("menus.hsmenu1-2-2"),
showLink: true,
keepAlive: true,
i18n: true,
@@ -83,7 +84,7 @@ const nestedRouter = {
component: () => import("/@/views/nested/menu1/menu1-3/index.vue"),
name: "Menu1-3",
meta: {
title: "message.hsmenu1-3",
title: $t("menus.hsmenu1-3"),
showLink: true,
i18n: true,
keepAlive: true
@@ -96,7 +97,7 @@ const nestedRouter = {
name: "Menu2",
component: () => import("/@/views/nested/menu2/index.vue"),
meta: {
title: "message.hsmenu2",
title: $t("menus.hsmenu2"),
showLink: true,
i18n: true,
keepAlive: true

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const remainingRouter = [
@@ -6,7 +7,7 @@ const remainingRouter = [
name: "login",
component: () => import("/@/views/login.vue"),
meta: {
title: "message.hslogin",
title: $t("menus.hslogin"),
showLink: false,
i18n: true,
rank: 101
@@ -18,7 +19,7 @@ const remainingRouter = [
component: Layout,
meta: {
icon: "HomeFilled",
title: "message.hshome",
title: $t("menus.hshome"),
i18n: true,
showLink: false,
rank: 104

View File

@@ -2,8 +2,8 @@ import { RouteLocationNormalized } from "vue-router";
export interface toRouteType extends RouteLocationNormalized {
meta: {
keepAlive: boolean;
keepAlive?: boolean;
refreshRedirect: string;
realPath: string;
dynamicLevel?: string;
};
}

View File

@@ -13,7 +13,7 @@ import { useTimeoutFn } from "@vueuse/core";
import { RouteConfigs } from "/@/layout/types";
import { usePermissionStoreHook } from "/@/store/modules/permission";
// https://cn.vitejs.dev/guide/features.html#glob-import
const modulesRoutes = import.meta.glob("/src/views/*/*/*.vue");
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
// 动态路由
import { getAsyncRoutes } from "/@/api/routes";
@@ -212,15 +212,13 @@ const handleAliveRoute = (matched: RouteRecordNormalized[], mode?: string) => {
// 过滤后端传来的动态路由 重新生成规范路由
const addAsyncRoutes = (arrRoutes: Array<RouteRecordRaw>) => {
if (!arrRoutes || !arrRoutes.length) return;
const modulesRoutesKeys = Object.keys(modulesRoutes);
arrRoutes.forEach((v: RouteRecordRaw) => {
if (v.redirect) {
v.component = Layout;
} else {
if (v.meta.realPath) {
v.component = modulesRoutes[`/src/views${v.meta.realPath}/index.vue`];
} else {
v.component = modulesRoutes[`/src/views${v.path}/index.vue`];
}
const index = modulesRoutesKeys.findIndex(ev => ev.includes(v.path));
v.component = modulesRoutes[modulesRoutesKeys[index]];
}
if (v.children) {
addAsyncRoutes(v.children);

View File

@@ -1,17 +1,17 @@
import { storageLocal } from "/@/utils/storage";
import { deviceDetection } from "/@/utils/deviceDetection";
import { store } from "/@/store";
import { appType } from "./types";
import { defineStore } from "pinia";
import { getConfig } from "/@/config";
import { storageLocal } from "/@/utils/storage";
import { deviceDetection } from "/@/utils/deviceDetection";
export const useAppStore = defineStore({
id: "pure-app",
state: (): appType => ({
sidebar: {
opened: storageLocal.getItem("sidebarStatus")
? !!+storageLocal.getItem("sidebarStatus")
: true,
opened:
storageLocal.getItem("responsive-layout")?.sidebarStatus ??
getConfig().SidebarStatus,
withoutAnimation: false,
isClickHamburger: false
},
@@ -30,20 +30,22 @@ export const useAppStore = defineStore({
},
actions: {
TOGGLE_SIDEBAR(opened?: boolean, resize?: string) {
const layout = storageLocal.getItem("responsive-layout");
if (opened && resize) {
this.sidebar.withoutAnimation = true;
this.sidebar.opened = true;
storageLocal.setItem("sidebarStatus", true);
layout.sidebarStatus = true;
} else if (!opened && resize) {
this.sidebar.withoutAnimation = true;
this.sidebar.opened = false;
storageLocal.setItem("sidebarStatus", false);
layout.sidebarStatus = false;
} else if (!opened && !resize) {
this.sidebar.withoutAnimation = false;
this.sidebar.opened = !this.sidebar.opened;
this.sidebar.isClickHamburger = !this.sidebar.opened;
storageLocal.setItem("sidebarStatus", this.sidebar.opened);
layout.sidebarStatus = this.sidebar.opened;
}
storageLocal.setItem("responsive-layout", layout);
},
TOGGLE_DEVICE(device: string) {
this.device = device;

View File

@@ -0,0 +1,30 @@
import { store } from "/@/store";
import { defineStore } from "pinia";
import { getConfig } from "/@/config";
import { storageLocal } from "/@/utils/storage";
export const useEpThemeStore = defineStore({
id: "pure-epTheme",
state: () => ({
epThemeColor:
storageLocal.getItem("responsive-layout")?.epThemeColor ??
getConfig().EpThemeColor
}),
getters: {
getEpThemeColor() {
return this.epThemeColor;
}
},
actions: {
setEpThemeColor(newColor) {
const layout = storageLocal.getItem("responsive-layout");
this.epThemeColor = newColor;
layout.epThemeColor = newColor;
storageLocal.setItem("responsive-layout", layout);
}
}
});
export function useEpThemeStoreHook() {
return useEpThemeStore(store);
}

View File

@@ -8,21 +8,21 @@ export const useMultiTagsStore = defineStore({
id: "pure-multiTags",
state: () => ({
// 存储标签页信息(路由信息)
multiTags: storageLocal.getItem("responsive-sets").multiTagsCache
multiTags: storageLocal.getItem("responsive-configure").multiTagsCache
? storageLocal.getItem("responsive-tags")
: [
{
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
title: "menus.hshome",
icon: "HomeFilled",
i18n: true,
showLink: true
}
}
],
multiTagsCache: storageLocal.getItem("responsive-sets").multiTagsCache
multiTagsCache: storageLocal.getItem("responsive-configure").multiTagsCache
}),
getters: {
getMultiTagsCache() {
@@ -55,31 +55,30 @@ export const useMultiTagsStore = defineStore({
case "push":
{
const tagVal = value as multiType;
const tagPath = tagVal?.path;
// 判断tag是否已存在
const tagHasExits = this.multiTags.some(tag => {
return tag.path === tagVal?.path;
return tag.path === tagPath;
});
// 判断tag中的query键值是否相等
const tagQueryHasExits = this.multiTags.some(tag => {
return isEqual(tag.query, tagVal.query);
return isEqual(tag.query, tagVal?.query);
});
if (tagHasExits && tagQueryHasExits) return;
const meta = tagVal?.meta;
const dynamicLevel = meta?.dynamicLevel ?? -1;
const dynamicLevel = tagVal?.meta?.dynamicLevel ?? -1;
if (dynamicLevel > 0) {
// dynamicLevel动态路由可打开的数量
const realPath = meta?.realPath ?? "";
// 获取到已经打开的动态路由数, 判断是否大于dynamicLevel
if (
this.multiTags.filter(e => e.meta?.realPath ?? "" === realPath)
.length >= dynamicLevel
this.multiTags.filter(e => e?.path === tagPath).length >=
dynamicLevel
) {
// 关闭第一个
const index = this.multiTags.findIndex(
item => item.meta?.realPath === realPath
item => item?.path === tagPath
);
index !== -1 && this.multiTags.splice(index, 1);
}

View File

@@ -25,8 +25,8 @@ export type multiType = {
path: string;
parentPath: string;
name: string;
query: object;
meta: any;
query?: object;
};
export type setType = {

View File

@@ -58,8 +58,8 @@ export const useUserStore = defineStore({
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
title: "menus.hshome",
icon: "HomeFilled",
i18n: true,
showLink: true
}

29
src/style/dark.scss Normal file
View File

@@ -0,0 +1,29 @@
/* 暗黑模式 */
[data-theme="dark"] {
filter: invert(0.9) hue-rotate(180deg);
#mse,
img,
.icon-svg,
.login-container {
filter: invert(1) hue-rotate(180deg);
}
// element plus
.el-radio-button__original-radio:checked + .el-radio-button__inner,
.el-image-viewer__close,
.el-image-viewer__actions__inner,
.el-image-viewer__next,
.el-image-viewer__prev {
color: #000 !important;
}
.el-overlay {
background-color: rgba(0, 0, 0, 0.05) !important;
}
.el-drawer {
box-shadow: 0 8px 10px -5px rgb(0 0 0 / 1%), 0 16px 24px 2px rgb(0 0 0 / 2%),
0 6px 30px 5px rgb(0 0 0 / 1%);
}
}

View File

@@ -35,3 +35,30 @@
.is-dark {
z-index: 99999 !important;
}
/* 动态改变cssvar 用于主题切换 https://github.com/element-plus/element-plus/issues/4856#issuecomment-1000174357 */
.el-button--primary {
--el-button-bg-color: var(--el-color-primary) !important;
--el-button-border-color: var(--el-color-primary) !important;
--el-button-hover-bg-color: var(--el-color-primary-light-2) !important;
--el-button-hover-border-color: var(--el-color-primary-light-2) !important;
--el-button-active-bg-color: var(--el-color-primary-active) !important;
--el-button-active-border-color: var(--el-color-primary-active) !important;
}
/* nprogress适配ep的primary */
#nprogress {
& .bar {
background-color: var(--el-color-primary) !important;
}
& .peg {
box-shadow: 0 0 10px var(--el-color-primary),
0 0 5px var(--el-color-primary) !important;
}
& .spinner-icon {
border-top-color: var(--el-color-primary);
border-left-color: var(--el-color-primary);
}
}

View File

@@ -2,6 +2,7 @@
@import "./transition.scss";
@import "./element-plus.scss";
@import "./sidebar.scss";
@import "./dark.scss";
body {
width: 100%;

View File

@@ -6,7 +6,7 @@
z-index: -1;
}
.container {
.login-container {
width: 100vw;
height: 100vh;
display: grid;
@@ -183,7 +183,7 @@ a:hover {
}
@media screen and (max-width: 1080px) {
.container {
.login-container {
grid-gap: 9rem;
}
}
@@ -217,7 +217,7 @@ a:hover {
display: none;
}
.container {
.login-container {
grid-template-columns: 1fr;
}

View File

@@ -568,6 +568,7 @@
body[layout="vertical"] {
$sideBarWidth: 210px;
@include merge-style($sideBarWidth);
.el-menu--collapse {
width: 54px;
}

View File

@@ -18,8 +18,8 @@ http.request('get', '/xxx?message=' + msg);
import { http } from "/@/utils/http";
// params传参
http.request('get', '/xxx', { params: param });
http.request('post', '/xxx', { params: param });
// data传参
http.request('get', '/xxx', { data: param });
http.request('post', '/xxx', { data: param });
```

View File

@@ -17,15 +17,20 @@ export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
type: Object,
default: Storage.getData(undefined, "layout") ?? {
layout: config.Layout ?? "vertical",
theme: config.Theme ?? "default"
theme: config.Theme ?? "default",
darkMode: config.DarkMode ?? false,
sidebarStatus: config.SidebarStatus ?? true,
epThemeColor: config.EpThemeColor ?? "409EFF"
}
},
sets: {
configure: {
type: Object,
default: Storage.getData(undefined, "sets") ?? {
default: Storage.getData(undefined, "configure") ?? {
grey: config.Grey ?? false,
weak: config.Weak ?? false,
hideTabs: config.HideTabs ?? false,
showLogo: config.ShowLogo ?? true,
showModel: config.ShowModel ?? "smart",
multiTagsCache: config.MultiTagsCache ?? false
}
}
@@ -40,7 +45,7 @@ export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
title: "menus.hshome",
i18n: true,
icon: "HomeFilled",
showLink: true

93
src/views/guide/index.vue Normal file
View File

@@ -0,0 +1,93 @@
<script lang="ts">
export default {
name: "reGuide"
};
</script>
<script setup lang="ts">
import Driver from "driver.js";
import "driver.js/dist/driver.min.css";
// 步骤
const steps = [
{
element: "#header-notice",
popover: {
title: "消息通知",
description: "你可以在这里查看管理员发送的消息",
position: "left"
}
},
{
element: "#header-screenfull",
popover: {
title: "全屏",
description: "你可以在这里进行全屏切换",
position: "left"
}
},
{
element: "#header-translation",
popover: {
title: "国际化",
description: "你可以在这里进行语言切换",
position: "left"
}
},
{
element: ".el-icon-setting",
popover: {
title: "项目配置",
description: "你可以在这里查看项目配置",
position: "left"
}
},
{
element: ".tags-view",
popover: {
title: "多标签页",
description: "这里是你访问过的页面的历史",
position: "buttom"
}
}
];
const driver = new Driver({
className: "scoped-class",
animate: true,
opacity: 0.75,
padding: 0,
allowClose: true,
overlayClickNext: false,
doneBtnText: "完成",
closeBtnText: "关闭",
nextBtnText: "下一步",
prevBtnText: "上一步"
});
const guide = () => {
driver.defineSteps(steps);
driver.start();
};
</script>
<template>
<div>
<p>引导页对于一些第一次进入项目的人很有用你可以简单介绍下项目的功能</p>
<el-button
type="primary"
style="margin-top: 10px"
@click.prevent.stop="guide"
>
打开引导页
</el-button>
</div>
</template>
<style>
div#driver-highlighted-element-stage,
div#driver-page-overlay {
background: transparent !important;
outline: 5000px solid rgba(0, 0, 0, 0.75);
}
</style>

View File

@@ -71,7 +71,7 @@ function onPwdBlur() {
<template>
<img :src="bg" class="wave" />
<div class="container">
<div class="login-container">
<div class="img">
<component :is="currentWeek"></component>
</div>

View File

@@ -1,7 +1,7 @@
<template>
<div>
<p>{{ $t("message.hsmenu1") }}</p>
<p style="text-indent: 2em">{{ $t("message.hsmenu1-1") }}</p>
<p>{{ $t("menus.hsmenu1") }}</p>
<p style="text-indent: 2em">{{ $t("menus.hsmenu1-1") }}</p>
<el-input v-model="input" />
</div>
</template>

View File

@@ -1,8 +1,8 @@
<template>
<div>
<p>{{ $t("message.hsmenu1") }}</p>
<p style="text-indent: 2em">{{ $t("message.hsmenu1-2") }}</p>
<p style="text-indent: 4em">{{ $t("message.hsmenu1-2-1") }}</p>
<p>{{ $t("menus.hsmenu1") }}</p>
<p style="text-indent: 2em">{{ $t("menus.hsmenu1-2") }}</p>
<p style="text-indent: 4em">{{ $t("menus.hsmenu1-2-1") }}</p>
<el-input v-model="input" />
</div>
</template>

View File

@@ -1,8 +1,8 @@
<template>
<div>
<p>{{ $t("message.hsmenu1") }}</p>
<p style="text-indent: 2em">{{ $t("message.hsmenu1-2") }}</p>
<p style="text-indent: 4em">{{ $t("message.hsmenu1-2-2") }}</p>
<p>{{ $t("menus.hsmenu1") }}</p>
<p style="text-indent: 2em">{{ $t("menus.hsmenu1-2") }}</p>
<p style="text-indent: 4em">{{ $t("menus.hsmenu1-2-2") }}</p>
<el-input v-model="input" />
</div>
</template>

View File

@@ -1,7 +1,7 @@
<template>
<div>
<p>{{ $t("message.hsmenu1") }}</p>
<p style="text-indent: 2em">{{ $t("message.hsmenu1-3") }}</p>
<p>{{ $t("menus.hsmenu1") }}</p>
<p style="text-indent: 2em">{{ $t("menus.hsmenu1-3") }}</p>
<el-input v-model="input" />
</div>
</template>

View File

@@ -1,6 +1,6 @@
<template>
<div>
<p>{{ $t("message.hsmenu2") }}</p>
<p>{{ $t("menus.hsmenu2") }}</p>
<el-input v-model="input" />
</div>
</template>

View File

@@ -101,13 +101,13 @@ const checkboxChangeEvent: VxeTableEvents.CheckboxChange = ({ records }) => {
<template #default="{ row }">
<vxe-button
type="text"
icon="el-icon-edit"
icon="fa fa-pencil-square-o"
@click="editConfig(row)"
>编辑</vxe-button
>
<vxe-button
type="text"
icon="el-icon-delete"
icon="fa fa-trash-o"
@click="delConfig(row)"
>删除</vxe-button
>
@@ -140,7 +140,7 @@ const checkboxChangeEvent: VxeTableEvents.CheckboxChange = ({ records }) => {
<span class="select-count"
>已选中{{ configData.selectRecords.length }}</span
>
<vxe-button size="small">{{ $t("message.hsdelete") }}</vxe-button>
<vxe-button size="small">{{ $t("buttons.hsdelete") }}</vxe-button>
</span>
</template>
</vxe-pager>
@@ -179,10 +179,4 @@ const checkboxChangeEvent: VxeTableEvents.CheckboxChange = ({ records }) => {
:deep(.el-divider--horizontal) {
margin: 13px 0;
}
:deep(.el-icon-close) {
&:hover {
color: red;
}
}
</style>

View File

@@ -209,28 +209,28 @@ function handleClose() {
<template #buttons>
<vxe-input
v-model="dictData.filterName"
:placeholder="$t('message.hssearch')"
:placeholder="$t('buttons.hssearch')"
@keyup="searchEvent"
></vxe-input>
</template>
<template #tools>
<vxe-button
icon="el-icon-circle-plus-outline"
icon="fa fa-plus-square-o"
status="primary"
@click="onAdd"
>{{ $t("message.hsadd") }}</vxe-button
>{{ $t("buttons.hsadd") }}</vxe-button
>
<vxe-button
icon="el-icon-folder-opened"
icon="fa fa-folder-open-o"
status="primary"
@click="$refs.xTree.setAllTreeExpand(true)"
>{{ $t("message.hsexpendAll") }}</vxe-button
>{{ $t("buttons.hsexpendAll") }}</vxe-button
>
<vxe-button
icon="el-icon-folder"
icon="fa fa-folder-o"
status="primary"
@click="$refs.xTree.clearTreeExpand()"
>{{ $t("message.hscollapseAll") }}</vxe-button
>{{ $t("buttons.hscollapseAll") }}</vxe-button
>
</template>
</vxe-toolbar>
@@ -266,23 +266,26 @@ function handleClose() {
</vxe-table-column>
<vxe-table-column title="操作" width="330" fixed="right">
<template #default="{ row }">
<vxe-button type="text" icon="el-icon-edit" @click="onEdit(row)"
<vxe-button
type="text"
icon="fa fa-pencil-square-o"
@click="onEdit(row)"
>编辑</vxe-button
>
<vxe-button
type="text"
icon="el-icon-circle-plus-outline"
icon="fa fa-plus-square-o"
@click="onAddChild(row)"
>新增子类型</vxe-button
>
<vxe-button
v-show="row.model"
type="text"
icon="el-icon-setting"
icon="fa fa-cog"
@click="onDeploy(row)"
>字典配置</vxe-button
>
<vxe-button type="text" icon="el-icon-delete" @click="confirmEvent"
<vxe-button type="text" icon="fa fa-trash-o" @click="confirmEvent"
>删除</vxe-button
>
</template>

View File

@@ -6,6 +6,7 @@ export default {
<script setup lang="ts">
import { reactive } from "vue";
import { $t } from "/@/plugins/i18n";
import { VxeGridProps } from "vxe-table";
const gridOptions = reactive({
@@ -29,19 +30,19 @@ const gridOptions = reactive({
buttons: [
{
code: "insert_actived",
name: "message.hsadd",
name: $t("buttons.hsadd"),
status: "perfect",
icon: "fa fa-plus"
},
{
code: "mark_cancel",
name: "message.hsmark",
name: $t("buttons.hsmark"),
status: "perfect",
icon: "fa fa-trash-o"
},
{
code: "save",
name: "message.hssave",
name: $t("buttons.hssave"),
status: "perfect",
icon: "fa fa-save"
}

View File

@@ -17,8 +17,7 @@ function toDetail(index: number) {
title: { zh: `No.${index} - 详情信息`, en: `No.${index} - DetailInfo` },
showLink: false,
i18n: false,
dynamicLevel: 3,
realPath: "/tabs/detail"
dynamicLevel: 3
}
});
router.push({ name: "tabDetail", query: { id: String(index) } });
@@ -31,12 +30,7 @@ function toDetail(index: number) {
title="标签页复用超出限制自动关闭(使用场景: 动态路由)"
name="tag"
>
<el-button
v-for="index in 6"
:key="index"
size="medium"
@click="toDetail(index)"
>
<el-button v-for="index in 6" :key="index" @click="toDetail(index)">
打开{{ index }}详情页
</el-button>
</el-collapse-item>

View File

@@ -63,9 +63,7 @@ const openDepot = (): void => {
>
<el-card>
<template #header>
<div>
<span>GitHub信息</span>
</div>
<span style="font-size: 16px; font-weight: 500">GitHub信息</span>
</template>
<el-skeleton animated :rows="7" :loading="loading">
<template #default>
@@ -97,9 +95,9 @@ const openDepot = (): void => {
>
<el-card>
<template #header>
<div>
<span>GitHub滚动信息</span>
</div>
<span style="font-size: 16px; font-weight: 500"
>GitHub滚动信息</span
>
</template>
<el-skeleton animated :rows="7" :loading="loading">
<template #default>
@@ -131,9 +129,9 @@ const openDepot = (): void => {
>
<el-card>
<template #header>
<div>
<span>GitHub饼图信息</span>
</div>
<span style="font-size: 16px; font-weight: 500"
>GitHub饼图信息</span
>
</template>
<el-skeleton animated :rows="7" :loading="loading">
<template #default>
@@ -165,9 +163,9 @@ const openDepot = (): void => {
>
<el-card>
<template #header>
<div>
<span>GitHub折线图信息</span>
</div>
<span style="font-size: 16px; font-weight: 500"
>GitHub折线图信息</span
>
</template>
<el-skeleton animated :rows="7" :loading="loading">
<template #default>
@@ -199,9 +197,9 @@ const openDepot = (): void => {
>
<el-card>
<template #header>
<div>
<span>GitHub柱状图信息</span>
</div>
<span style="font-size: 16px; font-weight: 500"
>GitHub柱状图信息</span
>
</template>
<el-skeleton animated :rows="7" :loading="loading">
<template #default>

5
types/global.d.ts vendored
View File

@@ -82,9 +82,14 @@ declare global {
Locale?: string;
Layout?: string;
Theme?: string;
DarkMode?: boolean;
Grey?: boolean;
Weak?: boolean;
HideTabs?: boolean;
SidebarStatus?: boolean;
EpThemeColor?: string;
ShowLogo?: boolean;
ShowModel?: string;
MapConfigure?: {
amapKey?: string;
options: {