Compare commits
134 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbe0815ac4 | ||
|
|
fd4cad8d4c | ||
|
|
e33bdb52f3 | ||
|
|
46a48a5650 | ||
|
|
8fa3448af2 | ||
|
|
f236829b0f | ||
|
|
47dea87275 | ||
|
|
5e9198c2e0 | ||
|
|
c9c8f20cc5 | ||
|
|
a19c97f152 | ||
|
|
5e1e9d3c5f | ||
|
|
96153f8a14 | ||
|
|
14adf692ab | ||
|
|
aa845fc3f5 | ||
|
|
079a455181 | ||
|
|
07794d000c | ||
|
|
e787f46414 | ||
|
|
e61d6109d7 | ||
|
|
34eda14473 | ||
|
|
0eb0b7395e | ||
|
|
73705eb0e4 | ||
|
|
d94fb0ef06 | ||
|
|
17470ecce2 | ||
|
|
12db6966fb | ||
|
|
5070568b89 | ||
|
|
ef91f113ee | ||
|
|
c875d20e83 | ||
|
|
c86aae7b8b | ||
|
|
ee30cba471 | ||
|
|
7bcd8a800a | ||
|
|
903e298951 | ||
|
|
955b76f30a | ||
|
|
1b052023b6 | ||
|
|
3af52cf6c4 | ||
|
|
660b6f4be8 | ||
|
|
836c9e7cab | ||
|
|
4ded2a7a0c | ||
|
|
dc1caecf1c | ||
|
|
8de3e8b37f | ||
|
|
ab93216dbe | ||
|
|
86177e430e | ||
|
|
93ac4fa813 | ||
|
|
0903008ced | ||
|
|
10fa0ee8c8 | ||
|
|
eb0771e7ec | ||
|
|
39159d5e7b | ||
|
|
501891a21c | ||
|
|
cbffe31c70 | ||
|
|
3ef9444bcb | ||
|
|
c81227bb4c | ||
|
|
05ed941638 | ||
|
|
77c798eaed | ||
|
|
6ab9997a56 | ||
|
|
b961659c2f | ||
|
|
81bf66eca9 | ||
|
|
b251f8ff79 | ||
|
|
bae16008db | ||
|
|
a0c54a6ac9 | ||
|
|
438aab9bfc | ||
|
|
e97bd9c8c4 | ||
|
|
653bafaa2b | ||
|
|
b82a3d3a2e | ||
|
|
b8c8251c64 | ||
|
|
00cc5a88e0 | ||
|
|
5d6ed8da33 | ||
|
|
d57e0e379e | ||
|
|
7811f6bdeb | ||
|
|
113e5f9db2 | ||
|
|
1758711174 | ||
|
|
12879f9553 | ||
|
|
d6a358e851 | ||
|
|
9e7d78fd80 | ||
|
|
570154a4f1 | ||
|
|
5564250e7d | ||
|
|
8d65f8ee92 | ||
|
|
11bf711838 | ||
|
|
a845d4f237 | ||
|
|
02c3a88ed6 | ||
|
|
a8a3e5b303 | ||
|
|
cec5af55d9 | ||
|
|
d04ba7563a | ||
|
|
0450f004d3 | ||
|
|
24a899bba0 | ||
|
|
6c75296a02 | ||
|
|
6cca0d3ab2 | ||
|
|
3d34663eda | ||
|
|
4bbf4c8548 | ||
|
|
8685092260 | ||
|
|
622464a8a4 | ||
|
|
be3a8a6949 | ||
|
|
3acb65d42c | ||
|
|
6d3a8c5a88 | ||
|
|
b65b972353 | ||
|
|
8c31ca1bad | ||
|
|
8cb21b6321 | ||
|
|
6d6eb98562 | ||
|
|
aca6a667f3 | ||
|
|
d79e63f673 | ||
|
|
e67d2df677 | ||
|
|
d44da67dc4 | ||
|
|
9d45e80856 | ||
|
|
be66c8bfb9 | ||
|
|
e26a0f949d | ||
|
|
3e991e6e43 | ||
|
|
0337a0300c | ||
|
|
ee8e0eb733 | ||
|
|
39cca9ac25 | ||
|
|
95140986b9 | ||
|
|
034f1577c2 | ||
|
|
c3645fd760 | ||
|
|
b1b236f736 | ||
|
|
0b79b65575 | ||
|
|
067ed96de4 | ||
|
|
f25e5d19a4 | ||
|
|
0380d4a17a | ||
|
|
89dc4e5052 | ||
|
|
12492a522f | ||
|
|
5d9638758b | ||
|
|
6b064bdef9 | ||
|
|
7aa895a2b7 | ||
|
|
35f2f9e93f | ||
|
|
f0a5f02588 | ||
|
|
c4a6a337a3 | ||
|
|
44a4c9346b | ||
|
|
bcf533af62 | ||
|
|
aa8005a982 | ||
|
|
2d6ad99f6f | ||
|
|
1e1747a355 | ||
|
|
6ba0bd7739 | ||
|
|
b4088f4612 | ||
|
|
3c4619d071 | ||
|
|
10e8b296e3 | ||
|
|
b702703472 | ||
|
|
7590dc308c |
14
.env
@@ -1,14 +1,2 @@
|
|||||||
# port
|
# 项目本地运行端口号
|
||||||
VITE_PORT = 8848
|
VITE_PORT = 8848
|
||||||
# title
|
|
||||||
VITE_TITLE = vue-pure-admin
|
|
||||||
# version
|
|
||||||
VITE_VERSION = 2.6.0
|
|
||||||
# open
|
|
||||||
VITE_OPEN = false
|
|
||||||
|
|
||||||
# public path
|
|
||||||
VITE_PUBLIC_PATH = /
|
|
||||||
|
|
||||||
# Cross-domain proxy, you can configure multiple
|
|
||||||
VITE_PROXY = [ ["/api", "http://127.0.0.1:3000" ] ]
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# port
|
# 项目本地运行端口号
|
||||||
VITE_PORT = 8848
|
VITE_PORT = 8848
|
||||||
# title
|
|
||||||
VITE_TITLE = vue-pure-admin
|
|
||||||
# version
|
|
||||||
VITE_VERSION = 2.6.0
|
|
||||||
# open
|
|
||||||
VITE_OPEN = false
|
|
||||||
|
|
||||||
# public path
|
# 开发环境读取配置文件路径
|
||||||
VITE_PUBLIC_PATH = /
|
VITE_PUBLIC_PATH = /
|
||||||
|
|
||||||
# Cross-domain proxy, you can configure multiple
|
# 开发环境代理
|
||||||
VITE_PROXY = [ ["/api", "http://127.0.0.1:3000" ] ]
|
VITE_PROXY_DOMAIN = /api
|
||||||
|
|
||||||
|
# 开发环境路由历史模式
|
||||||
|
VITE_ROUTER_HISTORY = "hash"
|
||||||
|
|
||||||
|
# 开发环境后端地址
|
||||||
|
VITE_PROXY_DOMAIN_REAL = "http://127.0.0.1:3000"
|
||||||
@@ -1,2 +1,11 @@
|
|||||||
# public path
|
# 线上环境项目打包路径
|
||||||
VITE_PUBLIC_PATH = /manages/
|
VITE_PUBLIC_PATH = /
|
||||||
|
|
||||||
|
# 线上环境路由历史模式
|
||||||
|
VITE_ROUTER_HISTORY = "hash"
|
||||||
|
|
||||||
|
# 线上环境后端地址
|
||||||
|
VITE_PROXY_DOMAIN_REAL = ""
|
||||||
|
|
||||||
|
# 是否为打包后的文件提供传统浏览器兼容性支持 支持 true 不支持 false
|
||||||
|
VITE_LEGACY = false
|
||||||
@@ -70,6 +70,12 @@ module.exports = {
|
|||||||
argsIgnorePattern: "^_",
|
argsIgnorePattern: "^_",
|
||||||
varsIgnorePattern: "^_"
|
varsIgnorePattern: "^_"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"prettier/prettier": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
endOfLine: "auto"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
|
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
|
||||||
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
|
"{!(package)*.json,.!(browserslist)*rc}": ["prettier --write--parser json"],
|
||||||
"prettier --write--parser json"
|
|
||||||
],
|
|
||||||
"package.json": ["prettier --write"],
|
"package.json": ["prettier --write"],
|
||||||
"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
|
"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
|
||||||
"*.{vue,css,scss,postcss,less}": ["stylelint --fix", "prettier --write"],
|
"*.{vue,css,scss,postcss,less}": ["stylelint --fix", "prettier --write"],
|
||||||
|
|||||||
16
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"johnsoncodehk.vscode-typescript-vue-plugin",
|
||||||
|
"voorjaar.windicss-intellisense",
|
||||||
|
"vscode-icons-team.vscode-icons",
|
||||||
|
"davidanson.vscode-markdownlint",
|
||||||
|
"stylelint.vscode-stylelint",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"johnsoncodehk.volar",
|
||||||
|
"lokalise.i18n-ally",
|
||||||
|
"mikestead.dotenv",
|
||||||
|
"eamodio.gitlens",
|
||||||
|
"antfu.iconify"
|
||||||
|
]
|
||||||
|
}
|
||||||
24
.vscode/settings.json
vendored
@@ -1,12 +1,4 @@
|
|||||||
{
|
{
|
||||||
// You should install these plugins:
|
|
||||||
// ESLint
|
|
||||||
// Prettier - Code formatter
|
|
||||||
// stylelint
|
|
||||||
// vscode-icons
|
|
||||||
// TypeScript Vue Plugin (Volar)
|
|
||||||
// Vue Language Features (Volar)
|
|
||||||
"terminal.integrated.rendererType": "dom",
|
|
||||||
"editor.formatOnType": true,
|
"editor.formatOnType": true,
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"javascript.updateImportsOnFileMove.enabled": "always",
|
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||||
@@ -24,26 +16,28 @@
|
|||||||
"editor.suggestSelection": "first",
|
"editor.suggestSelection": "first",
|
||||||
"editor.acceptSuggestionOnCommitCharacter": false,
|
"editor.acceptSuggestionOnCommitCharacter": false,
|
||||||
"css.lint.propertyIgnoredDueToDisplay": "ignore",
|
"css.lint.propertyIgnoredDueToDisplay": "ignore",
|
||||||
// Prevent inline styles from being automatically formatted to all lowercase
|
|
||||||
"editor.quickSuggestions": {
|
"editor.quickSuggestions": {
|
||||||
"other": true,
|
"other": true,
|
||||||
"comments": true,
|
"comments": true,
|
||||||
"strings": true
|
"strings": true
|
||||||
},
|
},
|
||||||
// Automatically fix some syntax errors of ts
|
|
||||||
"tslint.autoFixOnSave": true,
|
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
// Specifies the location of snippets in the suggestion widget
|
|
||||||
"editor.snippetSuggestions": "top"
|
"editor.snippetSuggestions": "top"
|
||||||
},
|
},
|
||||||
"[css]": {
|
"[css]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"cSpell.userWords": ["sourcemap", "vite"],
|
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": true
|
||||||
},
|
},
|
||||||
"volar.tsPlugin": true,
|
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"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.code-snippets
vendored
Normal 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+"
|
||||||
|
}
|
||||||
|
}
|
||||||
20
.vscode/vue3.2.setup-snippets
vendored
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,59 @@
|
|||||||
|
# 2.9.0 (2022-2-5)
|
||||||
|
|
||||||
|
### 🎫 Feat
|
||||||
|
|
||||||
|
- Added package size analysis, command `pnpm report`
|
||||||
|
|
||||||
|
### 🍏 Perf
|
||||||
|
|
||||||
|
- Use `iconify` to introduce icons on demand, optimize icon size, and reduce network requests
|
||||||
|
- Optimize the route, the route can not pass `showLink: true`, it is displayed by default
|
||||||
|
|
||||||
|
# 2.8.5 (2022-1-21)
|
||||||
|
|
||||||
|
### 🎫 Feat
|
||||||
|
|
||||||
|
- Added `WindiCSS` support
|
||||||
|
- Add online environment remove console plugin `vite-plugin-remove-console`
|
||||||
|
|
||||||
|
### ✔️ refactor
|
||||||
|
|
||||||
|
- Replace `@element-plus/icons-vue` with `@iconify-icons/ep`
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
- New tab reuse
|
||||||
|
- New message reminder template
|
||||||
|
- Added front-end menu tree structure example
|
||||||
|
- Refactor routing, optimize permissions modules, and bring a more convenient experience
|
||||||
|
- Refactor the env environment and http request to bring a more convenient experience
|
||||||
|
- Currently, the tabs of the platform are forced to associate with local storage. The next step is to put the tabs in the memory by default and support configurable persistent tabs
|
||||||
|
- Navigation menu icons support fontawesome, iconfont, remixicon, element-plus/icons, custom svg
|
||||||
|
- Update font-awesome to version 5.0, because versions below 5.0 are no longer officially maintained, but the platform will still be compatible with font-awesome4 version
|
||||||
|
|
||||||
|
### 🍏 Perf
|
||||||
|
|
||||||
|
- Optimize the tab page to bring a better interactive experience
|
||||||
|
- Routing title supports direct writing in Chinese, which can be separated from internationalization
|
||||||
|
- Route history mode is read from env and supports base parameter
|
||||||
|
- Packaged files provide traditional browser compatibility support, configure VITE_LEGACY to true
|
||||||
|
|
||||||
# 2.6.0(2021-11-10)
|
# 2.6.0(2021-11-10)
|
||||||
|
|
||||||
### 🎫 Feat
|
### 🎫 Feat
|
||||||
|
|||||||
56
CHANGELOG.md
@@ -1,3 +1,59 @@
|
|||||||
|
# 2.9.0 (2022-2-5)
|
||||||
|
|
||||||
|
### 🎫 Feat
|
||||||
|
|
||||||
|
- Added package size analysis, command `pnpm report`
|
||||||
|
|
||||||
|
### 🍏 Perf
|
||||||
|
|
||||||
|
- Use `iconify` to introduce icons on demand, optimize icon size, and reduce network requests
|
||||||
|
- Optimize the route, the route can not pass `showLink: true`, it is displayed by default
|
||||||
|
|
||||||
|
# 2.8.5 (2022-1-21)
|
||||||
|
|
||||||
|
### 🎫 Feat
|
||||||
|
|
||||||
|
- Added `WindiCSS` support
|
||||||
|
- Add online environment remove console plugin `vite-plugin-remove-console`
|
||||||
|
|
||||||
|
### ✔️ refactor
|
||||||
|
|
||||||
|
- Replace `@element-plus/icons-vue` with `@iconify-icons/ep`
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
- New tab reuse
|
||||||
|
- New message reminder template
|
||||||
|
- Added front-end menu tree structure example
|
||||||
|
- Refactor routing, optimize permissions modules, and bring a more convenient experience
|
||||||
|
- Refactor the env environment and http request to bring a more convenient experience
|
||||||
|
- Currently, the tabs of the platform are forced to associate with local storage. The next step is to put the tabs in the memory by default and support configurable persistent tabs
|
||||||
|
- Navigation menu icons support fontawesome, iconfont, remixicon, element-plus/icons, custom svg
|
||||||
|
- Update font-awesome to version 5.0, because versions below 5.0 are no longer officially maintained, but the platform will still be compatible with font-awesome4 version
|
||||||
|
|
||||||
|
### 🍏 Perf
|
||||||
|
|
||||||
|
- Optimize the tab page to bring a better interactive experience
|
||||||
|
- Routing title supports direct writing in Chinese, which can be separated from internationalization
|
||||||
|
- Route history mode is read from env and supports base parameter
|
||||||
|
- Packaged files provide traditional browser compatibility support, configure VITE_LEGACY to true
|
||||||
|
|
||||||
# 2.6.0(2021-11-10)
|
# 2.6.0(2021-11-10)
|
||||||
|
|
||||||
### 🎫 Feat
|
### 🎫 Feat
|
||||||
|
|||||||
@@ -1,3 +1,59 @@
|
|||||||
|
# 2.9.0(2022-2-5)
|
||||||
|
|
||||||
|
### 🎫 Feat
|
||||||
|
|
||||||
|
- 添加打包大小分析,命令`pnpm report`
|
||||||
|
|
||||||
|
### 🍏 Perf
|
||||||
|
|
||||||
|
- 采用`iconify`按需引入图标,优化图标大小,减少网络请求
|
||||||
|
- 优化路由,路由可不传`showLink: true`,默认显示
|
||||||
|
|
||||||
|
# 2.8.5(2022-1-21)
|
||||||
|
|
||||||
|
### 🎫 Feat
|
||||||
|
|
||||||
|
- 添加 `WindiCSS` 支持
|
||||||
|
- 添加线上环境删 console 插件`vite-plugin-remove-console`
|
||||||
|
|
||||||
|
### ✔️ refactor
|
||||||
|
|
||||||
|
- 使用`@iconify-icons/ep`替换`@element-plus/icons-vue`
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
- 新增标签页复用
|
||||||
|
- 新增消息提醒模版
|
||||||
|
- 新增前端菜单树结构例子
|
||||||
|
- 重构路由,优化权限模块,带来更方便的体验
|
||||||
|
- 重构 env 环境和 http 请求,带来更方便的体验
|
||||||
|
- 目前平台的标签页强制关联了本地存储,下一步标签页默认放到内存中并支持可配置持久化标签页
|
||||||
|
- 导航菜单图标支持 fontawesome、iconfont、remixicon、element-plus/icons、自定义 svg
|
||||||
|
- 更新 font-awesome 到 5.0 版本,因为 5.0 以下的版本官方不再维护,但平台依旧会兼容 font-awesome4 版本
|
||||||
|
|
||||||
|
### 🍏 Perf
|
||||||
|
|
||||||
|
- 优化标签页,带来更好的交互体验
|
||||||
|
- 路由 title 支持直接写中文,可脱离国际化
|
||||||
|
- 路由历史模式从 env 读取并支持 base 参数
|
||||||
|
- 打包后的文件提供传统浏览器兼容性支持,配置 VITE_LEGACY 为 true
|
||||||
|
|
||||||
# 2.6.0(2021-11-10)
|
# 2.6.0(2021-11-10)
|
||||||
|
|
||||||
### 🎫 Feat
|
### 🎫 Feat
|
||||||
|
|||||||
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 啝裳
|
Copyright (c) 2022 啝裳
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<h1>vue-pure-admin</h1>
|
<h1>vue-pure-admin</h1>
|
||||||
|
|
||||||
[](LICENSE)
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
**English** | [中文](./README.md)
|
**English** | [中文](./README.md)
|
||||||
|
|
||||||
@@ -8,34 +10,27 @@
|
|||||||
|
|
||||||
vue-pure-admin is a free and open source middle and back-end template. Using the latest `vue3` `vite2` `Element-Plus` `TypeScript` and other mainstream technology development, the out-of-the-box middle and back-end front-end solutions can also be used for learning reference.
|
vue-pure-admin is a free and open source middle and back-end template. Using the latest `vue3` `vite2` `Element-Plus` `TypeScript` and other mainstream technology development, the out-of-the-box middle and back-end front-end solutions can also be used for learning reference.
|
||||||
|
|
||||||
## Supporting video tutorial
|
## Supporting Video
|
||||||
|
|
||||||
bilibili:<https://www.bilibili.com/video/BV1534y1S7HV/>
|
- [Click Watch Tutorial](https://www.bilibili.com/video/BV1534y1S7HV)
|
||||||
|
- [Click Watch UI Design](https://www.bilibili.com/video/BV17g411T7rq)
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
- [Click Watch Docs](https://pure-admin-doc.vercel.app)
|
||||||
|
|
||||||
|
## Thin
|
||||||
|
|
||||||
|
- [Click Watch Thin](https://github.com/xiaoxian521/pure-admin-thin)
|
||||||
|
|
||||||
## Preview
|
## Preview
|
||||||
|
|
||||||
- [vue-pure-admin](http://yiming_chang.gitee.io/manages)
|
- [vue-pure-admin](http://yiming_chang.gitee.io/manages)
|
||||||
|
|
||||||
Click to log in without password
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f5ee80eee1014fb4a53c5bb37574a5f5~tplv-k3u1fbpfcp-watermark.image">
|
<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://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dec0672a62e141f3b7f626c22ff6c7ef~tplv-k3u1fbpfcp-watermark.image">
|
<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/f586f1353de74b1b88cc9f89fce2146e~tplv-k3u1fbpfcp-watermark.image">
|
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/381fc957fac143db9f06efdd389d88a3~tplv-k3u1fbpfcp-watermark.awebp?">
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a28fc0af7ac44e3b8f30469cba4a9993~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a17e54329cda4d76aa9c1c4f2a4715d3~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d884fb611da74ee0bdc17c29014d0260~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/abed44ac1f2744e897c28d1689bcb517~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf2f068650f44a0787c699f5a20c75a6~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/73c575dd06474731ad8ab9d853f1ddfd~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/99389b90f5ac4db9b0d61d99dd9a1454~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f1546f1c6014446db6c9983934aedc86~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cfb0093b77c34e87b094daaa4304bc2d~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fe133cc6db3245f9b1b37f231d040550~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d3110a12d63e4f6fb314e60bf18bdb66~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/45b93ec453e3406a939affe65ddcc803~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cdc2dc88c1ef4aafbaaade820442c986~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f4c91b162206485b88cc58a72ff54a01~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
### Use Gitpod
|
### Use Gitpod
|
||||||
@@ -125,15 +120,15 @@ Support modern browsers, not IE
|
|||||||
|
|
||||||
## Donate
|
## 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" />
|
||||||
|
|
||||||
## Exchange Group
|
## WeChat Exchange Group
|
||||||
|
|
||||||
[WeChat exchange group, click to scan the code to enter the group](https://juejin.cn/post/6948419379566477342/)
|
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
|
||||||
|
|
||||||
My WeChat: 18237613535, pull you into the group
|
<img src="http://yiming_chang.gitee.io/manages/kf.jpg" width="150px" height="195px" />
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
49
README.md
@@ -1,6 +1,8 @@
|
|||||||
<h1>vue-pure-admin</h1>
|
<h1>vue-pure-admin</h1>
|
||||||
|
|
||||||
[](LICENSE)
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
**中文** | [English](./README.en-US.md)
|
**中文** | [English](./README.en-US.md)
|
||||||
|
|
||||||
@@ -8,34 +10,27 @@
|
|||||||
|
|
||||||
vue-pure-admin 是一个免费开源的中后台模版。使用了最新的`vue3` `vite2` `Element-Plus` `TypeScript`等主流技术开发,开箱即用的中后台前端解决方案,也可用于学习参考。
|
vue-pure-admin 是一个免费开源的中后台模版。使用了最新的`vue3` `vite2` `Element-Plus` `TypeScript`等主流技术开发,开箱即用的中后台前端解决方案,也可用于学习参考。
|
||||||
|
|
||||||
## 配套视频教程
|
## 配套视频
|
||||||
|
|
||||||
bilibili:<https://www.bilibili.com/video/BV1534y1S7HV/>
|
- [点我查看教程](https://www.bilibili.com/video/BV1534y1S7HV)
|
||||||
|
- [点我查看 UI 设计](https://www.bilibili.com/video/BV17g411T7rq)
|
||||||
|
|
||||||
|
## 配套文档
|
||||||
|
|
||||||
|
- [点我查看文档](https://pure-admin-doc.vercel.app)
|
||||||
|
|
||||||
|
## 精简版
|
||||||
|
|
||||||
|
- [点我查看精简版](https://github.com/xiaoxian521/pure-admin-thin)
|
||||||
|
|
||||||
## 预览
|
## 预览
|
||||||
|
|
||||||
- [vue-pure-admin](http://yiming_chang.gitee.io/manages)
|
- [vue-pure-admin](http://yiming_chang.gitee.io/manages)
|
||||||
|
|
||||||
点击免密登录
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f5ee80eee1014fb4a53c5bb37574a5f5~tplv-k3u1fbpfcp-watermark.image">
|
<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://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dec0672a62e141f3b7f626c22ff6c7ef~tplv-k3u1fbpfcp-watermark.image">
|
<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/f586f1353de74b1b88cc9f89fce2146e~tplv-k3u1fbpfcp-watermark.image">
|
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/381fc957fac143db9f06efdd389d88a3~tplv-k3u1fbpfcp-watermark.awebp?">
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a28fc0af7ac44e3b8f30469cba4a9993~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a17e54329cda4d76aa9c1c4f2a4715d3~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d884fb611da74ee0bdc17c29014d0260~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/abed44ac1f2744e897c28d1689bcb517~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf2f068650f44a0787c699f5a20c75a6~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/73c575dd06474731ad8ab9d853f1ddfd~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/99389b90f5ac4db9b0d61d99dd9a1454~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f1546f1c6014446db6c9983934aedc86~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cfb0093b77c34e87b094daaa4304bc2d~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fe133cc6db3245f9b1b37f231d040550~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d3110a12d63e4f6fb314e60bf18bdb66~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/45b93ec453e3406a939affe65ddcc803~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cdc2dc88c1ef4aafbaaade820442c986~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
<img alt="PureAdmin Logo" width="100%" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f4c91b162206485b88cc58a72ff54a01~tplv-k3u1fbpfcp-watermark.image">
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
### 使用 Gitpod
|
### 使用 Gitpod
|
||||||
@@ -125,15 +120,15 @@ pnpm build
|
|||||||
|
|
||||||
## 捐赠
|
## 捐赠
|
||||||
|
|
||||||
如果你觉得这个项目对你有帮助,你可以帮作者买一杯咖啡表示支持!
|
如果你觉得这个项目对你有帮助,你可以帮作者买一杯咖啡表示支持
|
||||||
|
|
||||||
<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" />
|
||||||
|
|
||||||
## 交流群
|
## 微信交流群
|
||||||
|
|
||||||
[微信交流群,点击扫码进群](https://juejin.cn/post/6948419379566477342/)
|
为了项目更好的发展,你可选择捐赠 10 元后添加下图微信拉你进群,添加后请自觉发捐赠截图
|
||||||
|
|
||||||
本人微信:18237613535,拉你进群
|
<img src="http://yiming_chang.gitee.io/manages/kf.jpg" width="150px" height="195px" />
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
const productPlugins = [];
|
|
||||||
process.env.NODE_ENV === "production" &&
|
|
||||||
productPlugins.push("transform-remove-console");
|
|
||||||
module.exports = {
|
|
||||||
plugins: [...productPlugins]
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,14 @@
|
|||||||
|
// 处理环境变量
|
||||||
const warpperEnv = (envConf: Recordable): ViteEnv => {
|
const warpperEnv = (envConf: Recordable): ViteEnv => {
|
||||||
const ret: any = {};
|
// 此处为默认值,无需修改
|
||||||
|
const ret: ViteEnv = {
|
||||||
|
VITE_PORT: 8848,
|
||||||
|
VITE_PUBLIC_PATH: "",
|
||||||
|
VITE_PROXY_DOMAIN: "",
|
||||||
|
VITE_PROXY_DOMAIN_REAL: "",
|
||||||
|
VITE_ROUTER_HISTORY: "",
|
||||||
|
VITE_LEGACY: false
|
||||||
|
};
|
||||||
|
|
||||||
for (const envName of Object.keys(envConf)) {
|
for (const envName of Object.keys(envConf)) {
|
||||||
let realName = envConf[envName].replace(/\\n/g, "\n");
|
let realName = envConf[envName].replace(/\\n/g, "\n");
|
||||||
@@ -9,13 +18,6 @@ const warpperEnv = (envConf: Recordable): ViteEnv => {
|
|||||||
if (envName === "VITE_PORT") {
|
if (envName === "VITE_PORT") {
|
||||||
realName = Number(realName);
|
realName = Number(realName);
|
||||||
}
|
}
|
||||||
if (envName === "VITE_PROXY" && realName) {
|
|
||||||
try {
|
|
||||||
realName = JSON.parse(realName.replace(/'/g, '"'));
|
|
||||||
} catch (error) {
|
|
||||||
realName = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret[envName] = realName;
|
ret[envName] = realName;
|
||||||
if (typeof realName === "string") {
|
if (typeof realName === "string") {
|
||||||
process.env[envName] = realName;
|
process.env[envName] = realName;
|
||||||
@@ -25,8 +27,15 @@ const warpperEnv = (envConf: Recordable): ViteEnv => {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 跨域代理重写
|
||||||
|
const regExps = (value: string, reg: string): string => {
|
||||||
|
return value.replace(new RegExp(reg, "g"), "");
|
||||||
|
};
|
||||||
|
|
||||||
|
// 环境变量
|
||||||
const loadEnv = (): ViteEnv => {
|
const loadEnv = (): ViteEnv => {
|
||||||
return import.meta.env;
|
return import.meta.env;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { loadEnv, warpperEnv };
|
export { warpperEnv, regExps, loadEnv };
|
||||||
121
build/plugins.ts
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import vue from "@vitejs/plugin-vue";
|
||||||
|
import svgLoader from "vite-svg-loader";
|
||||||
|
import legacy from "@vitejs/plugin-legacy";
|
||||||
|
import vueJsx from "@vitejs/plugin-vue-jsx";
|
||||||
|
import WindiCSS from "vite-plugin-windicss";
|
||||||
|
import { viteMockServe } from "vite-plugin-mock";
|
||||||
|
import liveReload from "vite-plugin-live-reload";
|
||||||
|
import styleImport from "vite-plugin-style-import";
|
||||||
|
import ElementPlus from "unplugin-element-plus/vite";
|
||||||
|
import { visualizer } from "rollup-plugin-visualizer";
|
||||||
|
import removeConsole from "vite-plugin-remove-console";
|
||||||
|
import themePreprocessorPlugin from "@zougt/vite-plugin-theme-preprocessor";
|
||||||
|
|
||||||
|
export function getPluginsList(command, VITE_LEGACY) {
|
||||||
|
const prodMock = true;
|
||||||
|
const lifecycle = process.env.npm_lifecycle_event;
|
||||||
|
return [
|
||||||
|
vue(),
|
||||||
|
// jsx、tsx语法支持
|
||||||
|
vueJsx(),
|
||||||
|
WindiCSS(),
|
||||||
|
// 线上环境删除console
|
||||||
|
removeConsole(),
|
||||||
|
// 修改layout文件夹下的文件时自动重载浏览器 解决 https://github.com/xiaoxian521/vue-pure-admin/issues/170
|
||||||
|
liveReload(["src/layout/**/*", "src/router/**/*"]),
|
||||||
|
// 自定义主题
|
||||||
|
themePreprocessorPlugin({
|
||||||
|
scss: {
|
||||||
|
multipleScopeVars: [
|
||||||
|
{
|
||||||
|
scopeName: "layout-theme-default",
|
||||||
|
path: "src/layout/theme/default-vars.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scopeName: "layout-theme-light",
|
||||||
|
path: "src/layout/theme/light-vars.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scopeName: "layout-theme-dusk",
|
||||||
|
path: "src/layout/theme/dusk-vars.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scopeName: "layout-theme-volcano",
|
||||||
|
path: "src/layout/theme/volcano-vars.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scopeName: "layout-theme-yellow",
|
||||||
|
path: "src/layout/theme/yellow-vars.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scopeName: "layout-theme-mingQing",
|
||||||
|
path: "src/layout/theme/mingQing-vars.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scopeName: "layout-theme-auroraGreen",
|
||||||
|
path: "src/layout/theme/auroraGreen-vars.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scopeName: "layout-theme-pink",
|
||||||
|
path: "src/layout/theme/pink-vars.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scopeName: "layout-theme-saucePurple",
|
||||||
|
path: "src/layout/theme/saucePurple-vars.scss"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// 默认取 multipleScopeVars[0].scopeName
|
||||||
|
defaultScopeName: "",
|
||||||
|
// 在生产模式是否抽取独立的主题css文件,extract为true以下属性有效
|
||||||
|
extract: true,
|
||||||
|
// 独立主题css文件的输出路径,默认取 viteConfig.build.assetsDir 相对于 (viteConfig.build.outDir)
|
||||||
|
outputDir: "",
|
||||||
|
// 会选取defaultScopeName对应的主题css文件在html添加link
|
||||||
|
themeLinkTagId: "head",
|
||||||
|
// "head"||"head-prepend" || "body" ||"body-prepend"
|
||||||
|
themeLinkTagInjectTo: "head",
|
||||||
|
// 是否对抽取的css文件内对应scopeName的权重类名移除
|
||||||
|
removeCssScopeName: false,
|
||||||
|
// 可以自定义css文件名称的函数
|
||||||
|
customThemeCssFileName: scopeName => scopeName
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
// svg组件化支持
|
||||||
|
svgLoader(),
|
||||||
|
// 按需加载vxe-table
|
||||||
|
styleImport({
|
||||||
|
libs: [
|
||||||
|
{
|
||||||
|
libraryName: "vxe-table",
|
||||||
|
esModule: true,
|
||||||
|
ensureStyleFile: true,
|
||||||
|
resolveComponent: name => `vxe-table/es/${name}`,
|
||||||
|
resolveStyle: name => `vxe-table/es/${name}/style.css`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
ElementPlus({}),
|
||||||
|
// mock支持
|
||||||
|
viteMockServe({
|
||||||
|
mockPath: "mock",
|
||||||
|
localEnabled: command === "serve",
|
||||||
|
prodEnabled: command !== "serve" && prodMock,
|
||||||
|
injectCode: `
|
||||||
|
import { setupProdMockServer } from './mockProdServer';
|
||||||
|
setupProdMockServer();
|
||||||
|
`,
|
||||||
|
logger: true
|
||||||
|
}),
|
||||||
|
// 是否为打包后的文件提供传统浏览器兼容性支持
|
||||||
|
VITE_LEGACY
|
||||||
|
? legacy({
|
||||||
|
targets: ["ie >= 11"],
|
||||||
|
additionalLegacyPolyfills: ["regenerator-runtime/runtime"]
|
||||||
|
})
|
||||||
|
: null,
|
||||||
|
// 打包分析
|
||||||
|
lifecycle === "report"
|
||||||
|
? visualizer({ open: true, brotliSize: true, filename: "report.html" })
|
||||||
|
: null
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
type ProxyItem = [string, string];
|
|
||||||
|
|
||||||
type ProxyList = ProxyItem[];
|
|
||||||
|
|
||||||
const regExps = (value: string, reg: string): string => {
|
|
||||||
return value.replace(new RegExp(reg, "g"), "");
|
|
||||||
};
|
|
||||||
|
|
||||||
export function createProxy(list: ProxyList = []) {
|
|
||||||
const ret: any = {};
|
|
||||||
for (const [prefix, target] of list) {
|
|
||||||
ret[prefix] = {
|
|
||||||
target: target,
|
|
||||||
changeOrigin: true,
|
|
||||||
rewrite: (path: string) => regExps(path, prefix)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
123
index.html
@@ -3,7 +3,6 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<link rel="stylesheet" href="/iconfont.css" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>vue-pure-admin</title>
|
<title>vue-pure-admin</title>
|
||||||
<script src="/sortable.min.js"></script>
|
<script src="/sortable.min.js"></script>
|
||||||
@@ -27,94 +26,78 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: #000;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-family: "Reggae One", cursive;
|
font-family: "Reggae One", cursive;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
.loader,
|
||||||
font-size: 8vw;
|
.loader:before,
|
||||||
overflow: hidden;
|
.loader:after {
|
||||||
-webkit-text-stroke: 3px #7272a5;
|
border-radius: 50%;
|
||||||
|
width: 2.5em;
|
||||||
|
height: 2.5em;
|
||||||
|
-webkit-animation-fill-mode: both;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
-webkit-animation: loadAnimation 1.8s infinite ease-in-out;
|
||||||
|
animation: loadAnimation 1.8s infinite ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
.loader {
|
||||||
display: block;
|
color: #406eeb;
|
||||||
font-size: 20px;
|
font-size: 10px;
|
||||||
overflow: hidden;
|
margin: 80px auto;
|
||||||
color: green;
|
position: relative;
|
||||||
text-align: center;
|
text-indent: -9999em;
|
||||||
|
-webkit-transform: translateZ(0);
|
||||||
|
-ms-transform: translateZ(0);
|
||||||
|
transform: translateZ(0);
|
||||||
|
-webkit-animation-delay: -0.16s;
|
||||||
|
animation-delay: -0.16s;
|
||||||
}
|
}
|
||||||
|
|
||||||
p::before {
|
.loader:before,
|
||||||
|
.loader:after {
|
||||||
content: "";
|
content: "";
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
background-image: linear-gradient(45deg, #ff269b, #2ab5f5, #ffbf00);
|
|
||||||
mix-blend-mode: multiply;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p::after {
|
.loader:before {
|
||||||
content: "";
|
left: -3.5em;
|
||||||
background: radial-gradient(circle, #fff, #000 50%);
|
-webkit-animation-delay: -0.32s;
|
||||||
background-size: 25% 25%;
|
animation-delay: -0.32s;
|
||||||
position: absolute;
|
|
||||||
top: -100%;
|
|
||||||
left: -100%;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
mix-blend-mode: color-dodge;
|
|
||||||
animation: mix 2s linear infinite;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes mix {
|
.loader:after {
|
||||||
to {
|
left: 3.5em;
|
||||||
transform: translate(50%, 50%);
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes loadAnimation {
|
||||||
|
0%,
|
||||||
|
80%,
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 2.5em 0 -1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
box-shadow: 0 2.5em 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loadAnimation {
|
||||||
|
0%,
|
||||||
|
80%,
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 2.5em 0 -1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
box-shadow: 0 2.5em 0 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="g-container">
|
<div class="loader">Loading...</div>
|
||||||
<p>Pure-Admin</p>
|
|
||||||
<span class="_develop"></span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
// 此代码仅用于开发环境的友好提示,项目打包前请去掉这段js代码 This code is only used as a friendly reminder of the development environment, please remove this js code before packaging the project
|
|
||||||
window.onload = function () {
|
|
||||||
(function () {
|
|
||||||
const ua = navigator.userAgent.toLowerCase();
|
|
||||||
const re = /(msie|firefox|chrome|opera|version).*?([\d.]+)/;
|
|
||||||
const m = ua.match(re);
|
|
||||||
const Sys = {
|
|
||||||
browser: m[1].replace(/version/, "'safari"),
|
|
||||||
version: m[2]
|
|
||||||
};
|
|
||||||
|
|
||||||
const browser = Array.of("chrome", "firefox").includes(Sys.browser);
|
|
||||||
const version = parseFloat(Sys.version);
|
|
||||||
|
|
||||||
const el = document.querySelector("._develop");
|
|
||||||
|
|
||||||
if (el) {
|
|
||||||
if (browser && version >= 90) {
|
|
||||||
let success =
|
|
||||||
document.createTextNode("当前浏览器版本很适合开发!!! 😃");
|
|
||||||
el.appendChild(success);
|
|
||||||
} else {
|
|
||||||
let warn = document.createTextNode(
|
|
||||||
"当前浏览器版本不适合开发,建议使用最新版本的谷歌或者火狐浏览器!!!😯"
|
|
||||||
);
|
|
||||||
el.appendChild(warn);
|
|
||||||
el.style.color = "red";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Sys;
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -5,28 +5,29 @@ import { MockMethod } from "vite-plugin-mock";
|
|||||||
const systemRouter = {
|
const systemRouter = {
|
||||||
path: "/system",
|
path: "/system",
|
||||||
name: "system",
|
name: "system",
|
||||||
redirect: "/system/user",
|
redirect: "/system/user/index",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "Setting",
|
icon: "setting",
|
||||||
title: "message.hssysManagement",
|
title: "menus.hssysManagement",
|
||||||
showLink: true,
|
i18n: true,
|
||||||
rank: 6
|
rank: 6
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/system/user",
|
path: "/system/user/index",
|
||||||
name: "user",
|
name: "user",
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hsBaseinfo",
|
title: "menus.hsBaseinfo",
|
||||||
showLink: true
|
i18n: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/system/dict",
|
path: "/system/dict/index",
|
||||||
name: "dict",
|
name: "dict",
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hsDict",
|
title: "menus.hsDict",
|
||||||
showLink: true
|
i18n: true,
|
||||||
|
keepAlive: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -35,34 +36,67 @@ const systemRouter = {
|
|||||||
const permissionRouter = {
|
const permissionRouter = {
|
||||||
path: "/permission",
|
path: "/permission",
|
||||||
name: "permission",
|
name: "permission",
|
||||||
redirect: "/permission/page",
|
redirect: "/permission/page/index",
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.permission",
|
title: "menus.permission",
|
||||||
icon: "Lollipop",
|
icon: "lollipop",
|
||||||
showLink: true,
|
i18n: true,
|
||||||
rank: 3
|
rank: 3
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/permission/page",
|
path: "/permission/page/index",
|
||||||
name: "permissionPage",
|
name: "permissionPage",
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.permissionPage",
|
title: "menus.permissionPage",
|
||||||
showLink: true
|
i18n: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/permission/button",
|
path: "/permission/button/index",
|
||||||
name: "permissionButton",
|
name: "permissionButton",
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.permissionButton",
|
title: "menus.permissionButton",
|
||||||
showLink: true,
|
i18n: true,
|
||||||
authority: []
|
authority: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const tabsRouter = {
|
||||||
|
path: "/tabs",
|
||||||
|
name: "reTabs",
|
||||||
|
redirect: "/tabs/index",
|
||||||
|
meta: {
|
||||||
|
icon: "IF-team-icontabs",
|
||||||
|
title: "menus.hstabs",
|
||||||
|
i18n: true,
|
||||||
|
rank: 8
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/tabs/index",
|
||||||
|
name: "reTabs",
|
||||||
|
meta: {
|
||||||
|
title: "menus.hstabs",
|
||||||
|
i18n: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/tabs/detail",
|
||||||
|
name: "tabDetail",
|
||||||
|
meta: {
|
||||||
|
title: "",
|
||||||
|
showLink: false,
|
||||||
|
i18n: false,
|
||||||
|
dynamicLevel: 3,
|
||||||
|
refreshRedirect: "/tabs/index"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
// 添加不同按钮权限到/permission/button页面中
|
// 添加不同按钮权限到/permission/button页面中
|
||||||
function setDifAuthority(authority, routes) {
|
function setDifAuthority(authority, routes) {
|
||||||
routes.children[1].meta.authority = [authority];
|
routes.children[1].meta.authority = [authority];
|
||||||
@@ -77,12 +111,16 @@ export default [
|
|||||||
if (query.name === "admin") {
|
if (query.name === "admin") {
|
||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
info: [systemRouter, setDifAuthority("v-admin", permissionRouter)]
|
info: [
|
||||||
|
tabsRouter,
|
||||||
|
systemRouter,
|
||||||
|
setDifAuthority("v-admin", permissionRouter)
|
||||||
|
]
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
info: [setDifAuthority("v-test", permissionRouter)]
|
info: [tabsRouter, setDifAuthority("v-test", permissionRouter)]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
84
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-pure-admin",
|
"name": "vue-pure-admin",
|
||||||
"version": "2.6.0",
|
"version": "2.9.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16",
|
"node": ">= 16",
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
"dev": "cross-env --max_old_space_size=4096 vite",
|
"dev": "cross-env --max_old_space_size=4096 vite",
|
||||||
"serve": "pnpm dev",
|
"serve": "pnpm dev",
|
||||||
"build": "rimraf dist && cross-env vite build",
|
"build": "rimraf dist && cross-env vite build",
|
||||||
|
"report": "rimraf dist && cross-env vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"preview:build": "pnpm build && vite preview",
|
"preview:build": "pnpm build && vite preview",
|
||||||
"clean:cache": "rm -rf node_modules && rm -rf .eslintcache && pnpm install",
|
"clean:cache": "rm -rf node_modules && rm -rf .eslintcache && pnpm install",
|
||||||
@@ -18,7 +19,7 @@
|
|||||||
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,css,scss,postcss,less}\" --cache --cache-location node_modules/.cache/stylelint/",
|
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,css,scss,postcss,less}\" --cache --cache-location node_modules/.cache/stylelint/",
|
||||||
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
|
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
|
||||||
"lint:pretty": "pretty-quick --staged",
|
"lint:pretty": "pretty-quick --staged",
|
||||||
"lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint && pnpm lint:pretty",
|
"lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"preinstall": "npx only-allow pnpm"
|
"preinstall": "npx only-allow pnpm"
|
||||||
},
|
},
|
||||||
@@ -29,63 +30,67 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||||
"@element-plus/icons": "^0.0.11",
|
"@ctrl/tinycolor": "^3.4.0",
|
||||||
"@logicflow/core": "0.7.1",
|
"@logicflow/core": "0.7.1",
|
||||||
"@logicflow/extension": "0.7.1",
|
"@logicflow/extension": "0.7.1",
|
||||||
"@vueuse/core": "^6.7.1",
|
"@vueuse/core": "^7.5.5",
|
||||||
"@vueuse/motion": "^2.0.0-beta.4",
|
"@vueuse/motion": "^2.0.0-beta.9",
|
||||||
|
"@vueuse/shared": "^7.5.5",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"await-to-js": "^3.0.0",
|
"axios": "^0.25.0",
|
||||||
"axios": "^0.21.1",
|
|
||||||
"cropperjs": "^1.5.11",
|
"cropperjs": "^1.5.11",
|
||||||
|
"css-color-function": "^1.3.3",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
|
"driver.js": "^0.9.8",
|
||||||
"echarts": "^5.2.1",
|
"echarts": "^5.2.1",
|
||||||
"element-plus": "1.2.0-beta.3",
|
"element-plus": "1.3.0-beta.1",
|
||||||
"element-resize-detector": "^1.2.3",
|
"element-resize-detector": "^1.2.3",
|
||||||
"font-awesome": "^4.7.0",
|
"js-cookie": "^3.0.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"lowdb": "^3.0.0",
|
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"path-to-regexp": "^6.2.0",
|
"pinia": "^2.0.11",
|
||||||
"pinia": "^2.0.0-rc.14",
|
"qs": "^6.10.1",
|
||||||
"resize-observer-polyfill": "^1.5.1",
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
"responsive-storage": "^1.0.11",
|
"responsive-storage": "^1.0.11",
|
||||||
"sortablejs": "1.13.0",
|
"rgb-hex": "^4.0.0",
|
||||||
"typescript-cookie": "^1.0.0",
|
|
||||||
"v-contextmenu": "3.0.0",
|
"v-contextmenu": "3.0.0",
|
||||||
"vue": "^3.2.21",
|
"vue": "^3.2.29",
|
||||||
"vue-i18n": "^9.2.0-beta.3",
|
"vue-i18n": "^9.2.0-beta.30",
|
||||||
"vue-json-pretty": "^2.0.2",
|
"vue-json-pretty": "^2.0.2",
|
||||||
"vue-router": "^4.0.11",
|
"vue-router": "^4.0.12",
|
||||||
"vue-types": "^4.1.0",
|
"vue-types": "^4.1.1",
|
||||||
"vuedraggable": "4.1.0",
|
"vuedraggable": "4.1.0",
|
||||||
"vxe-table": "4.0.30",
|
"vxe-table": "^4.1.18",
|
||||||
"wangeditor": "4.7.7",
|
"wangeditor": "^4.7.9",
|
||||||
"xe-ajax": "4.0.5",
|
"xe-utils": "^3.5.2",
|
||||||
"xe-utils": "3.4.0",
|
|
||||||
"xgplayer": "2.28.0"
|
"xgplayer": "2.28.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "13.1.0",
|
"@commitlint/cli": "13.1.0",
|
||||||
"@commitlint/config-conventional": "13.1.0",
|
"@commitlint/config-conventional": "13.1.0",
|
||||||
|
"@iconify-icons/ep": "^1.1.3",
|
||||||
|
"@iconify-icons/fa": "^1.1.1",
|
||||||
|
"@iconify-icons/fa-solid": "^1.1.2",
|
||||||
|
"@iconify-icons/ri": "^1.1.1",
|
||||||
|
"@iconify/vue": "^3.1.3",
|
||||||
"@types/element-resize-detector": "1.1.3",
|
"@types/element-resize-detector": "1.1.3",
|
||||||
|
"@types/js-cookie": "^3.0.1",
|
||||||
"@types/mockjs": "1.0.3",
|
"@types/mockjs": "1.0.3",
|
||||||
"@types/node": "14.14.14",
|
"@types/node": "14.14.14",
|
||||||
"@types/nprogress": "0.2.0",
|
"@types/nprogress": "0.2.0",
|
||||||
|
"@types/qs": "^6.9.7",
|
||||||
"@typescript-eslint/eslint-plugin": "4.31.0",
|
"@typescript-eslint/eslint-plugin": "4.31.0",
|
||||||
"@typescript-eslint/parser": "4.31.0",
|
"@typescript-eslint/parser": "4.31.0",
|
||||||
"@vitejs/plugin-vue": "^1.9.4",
|
"@vitejs/plugin-legacy": "^1.6.4",
|
||||||
"@vitejs/plugin-vue-jsx": "^1.2.0",
|
"@vitejs/plugin-vue": "^2.1.0",
|
||||||
"@vue/compiler-sfc": "^3.2.21",
|
"@vitejs/plugin-vue-jsx": "^1.3.3",
|
||||||
"@vue/eslint-config-prettier": "6.0.0",
|
"@vue/eslint-config-prettier": "6.0.0",
|
||||||
"@vue/eslint-config-typescript": "7.0.0",
|
"@vue/eslint-config-typescript": "7.0.0",
|
||||||
"@zougt/vite-plugin-theme-preprocessor": "^1.3.10",
|
"@zougt/vite-plugin-theme-preprocessor": "^1.4.4",
|
||||||
"autoprefixer": "10.2.4",
|
"autoprefixer": "^10.4.2",
|
||||||
"babel-plugin-transform-remove-console": "6.9.4",
|
|
||||||
"chalk": "2.4.2",
|
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint": "7.30.0",
|
"eslint": "7.30.0",
|
||||||
"eslint-plugin-prettier": "3.4.0",
|
"eslint-plugin-prettier": "3.4.0",
|
||||||
@@ -97,19 +102,24 @@
|
|||||||
"prettier": "2.3.2",
|
"prettier": "2.3.2",
|
||||||
"pretty-quick": "3.1.1",
|
"pretty-quick": "3.1.1",
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"sass": "^1.43.4",
|
"rollup-plugin-visualizer": "^5.5.4",
|
||||||
"sass-loader": "^12.3.0",
|
"sass": "^1.49.7",
|
||||||
|
"sass-loader": "^12.4.0",
|
||||||
"stylelint": "13.13.1",
|
"stylelint": "13.13.1",
|
||||||
"stylelint-config-prettier": "8.0.2",
|
"stylelint-config-prettier": "8.0.2",
|
||||||
"stylelint-config-standard": "22.0.0",
|
"stylelint-config-standard": "22.0.0",
|
||||||
"stylelint-order": "4.1.0",
|
"stylelint-order": "4.1.0",
|
||||||
"typescript": "4.4.2",
|
"typescript": "^4.5.5",
|
||||||
"unplugin-element-plus": "^0.1.0",
|
"unplugin-element-plus": "^0.2.0",
|
||||||
"vite": "latest",
|
"vite": "^2.7.13",
|
||||||
|
"vite-plugin-live-reload": "^2.1.0",
|
||||||
"vite-plugin-mock": "^2.9.6",
|
"vite-plugin-mock": "^2.9.6",
|
||||||
"vite-plugin-style-import": "^1.2.1",
|
"vite-plugin-remove-console": "^0.0.6",
|
||||||
"vite-svg-loader": "^2.2.0",
|
"vite-plugin-style-import": "^1.4.1",
|
||||||
"vue-eslint-parser": "7.10.0"
|
"vite-plugin-windicss": "^1.6.1",
|
||||||
|
"vite-svg-loader": "2.2.0",
|
||||||
|
"vue-eslint-parser": "7.10.0",
|
||||||
|
"windicss": "^3.4.3"
|
||||||
},
|
},
|
||||||
"repository": "git@github.com:xiaoxian521/vue-pure-admin.git",
|
"repository": "git@github.com:xiaoxian521/vue-pure-admin.git",
|
||||||
"author": "xiaoxian521",
|
"author": "xiaoxian521",
|
||||||
|
|||||||
2684
pnpm-lock.yaml
generated
@@ -1,18 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: "iconfont"; /* project id 1098500 */
|
|
||||||
src: url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.eot");
|
|
||||||
src: url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.eot?#iefix")
|
|
||||||
format("embedded-opentype"),
|
|
||||||
url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.woff2") format("woff2"),
|
|
||||||
url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.woff") format("woff"),
|
|
||||||
url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.ttf") format("truetype"),
|
|
||||||
url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.svg#iconfont") format("svg");
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconfont {
|
|
||||||
font-family: "iconfont" !important;
|
|
||||||
font-size: 16px;
|
|
||||||
font-style: normal;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,23 @@
|
|||||||
{
|
{
|
||||||
"Version": "2.6.0",
|
"Version": "2.9.0",
|
||||||
"Title": "PureAdmin",
|
"Title": "PureAdmin",
|
||||||
"FixedHeader": true,
|
"FixedHeader": true,
|
||||||
"HiddenSideBar": false,
|
"HiddenSideBar": false,
|
||||||
|
"MultiTagsCache": false,
|
||||||
"KeepAlive": true,
|
"KeepAlive": true,
|
||||||
"Locale": "zh",
|
"Locale": "zh",
|
||||||
"Layout": "vertical",
|
"Layout": "vertical",
|
||||||
"Theme": "default",
|
"Theme": "default",
|
||||||
|
"DarkMode": false,
|
||||||
"Grey": false,
|
"Grey": false,
|
||||||
"Weak": false,
|
"Weak": false,
|
||||||
"HideTabs": false,
|
"HideTabs": false,
|
||||||
|
"SidebarStatus": true,
|
||||||
|
"EpThemeColor": "#409EFF",
|
||||||
|
"ShowLogo": true,
|
||||||
|
"ShowModel": "smart",
|
||||||
"MapConfigure": {
|
"MapConfigure": {
|
||||||
"amapKey": "97b3248d1553172e81f168cf94ea667e",
|
"amapKey": "97b3248d1553172e81f168cf94ea667e",
|
||||||
"baiduKey": "wTHbkkEweiFqZLKunMIjcrb2RcqNXkhc",
|
|
||||||
"options": {
|
"options": {
|
||||||
"resizeEnable": true,
|
"resizeEnable": true,
|
||||||
"center": [113.6401, 34.72468],
|
"center": [113.6401, 34.72468],
|
||||||
|
|||||||
@@ -5,10 +5,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
import { ElConfigProvider } from "element-plus";
|
import { ElConfigProvider } from "element-plus";
|
||||||
import zhCn from "element-plus/lib/locale/lang/zh-cn";
|
import zhCn from "element-plus/lib/locale/lang/zh-cn";
|
||||||
import en from "element-plus/lib/locale/lang/en";
|
import en from "element-plus/lib/locale/lang/en";
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "app",
|
name: "app",
|
||||||
components: {
|
components: {
|
||||||
[ElConfigProvider.name]: ElConfigProvider
|
[ElConfigProvider.name]: ElConfigProvider
|
||||||
@@ -18,5 +19,5 @@ export default {
|
|||||||
return this.$storage.locale?.locale === "zh" ? zhCn : en;
|
return this.$storage.locale?.locale === "zh" ? zhCn : en;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { http } from "../utils/http";
|
import { http } from "../utils/http";
|
||||||
|
|
||||||
// 地图数据
|
// 地图数据
|
||||||
export const mapJson = (data?: object) => {
|
export const mapJson = (params?: object) => {
|
||||||
return http.request("get", "/getMapInfo", data);
|
return http.request("get", "/getMapInfo", { params });
|
||||||
};
|
};
|
||||||
|
|
||||||
// echarts数据
|
// echarts数据
|
||||||
export const echartsJson = (data?: object) => {
|
export const echartsJson = (params?: object) => {
|
||||||
return http.request("get", "/getEchartsInfo", data);
|
return http.request("get", "/getEchartsInfo", { params });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { http } from "../utils/http";
|
import { http } from "../utils/http";
|
||||||
|
|
||||||
export const getAsyncRoutes = (data?: object) => {
|
export const getAsyncRoutes = (params?: object) => {
|
||||||
return http.request("get", "/getAsyncRoutes", data);
|
return http.request("get", "/getAsyncRoutes", { params });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,5 +13,14 @@ export const getVerify = (): userType => {
|
|||||||
|
|
||||||
// 登录
|
// 登录
|
||||||
export const getLogin = (data: object) => {
|
export const getLogin = (data: object) => {
|
||||||
return http.request("post", "/login", data);
|
return http.request("post", "/login", { data });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 刷新token
|
||||||
|
export const refreshToken = (data: object) => {
|
||||||
|
return http.request("post", "/refreshToken", { data });
|
||||||
|
};
|
||||||
|
|
||||||
|
// export const searchVague = (data: object) => {
|
||||||
|
// return http.request("post", "/searchVague", { data });
|
||||||
|
// };
|
||||||
|
|||||||
BIN
src/assets/avatars.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 52 KiB |
@@ -1,8 +1,8 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 2208059 */
|
font-family: "iconfont"; /* Project id 2208059 */
|
||||||
src: url("iconfont.woff2?t=1636197082361") format("woff2"),
|
src: url("iconfont.woff2?t=1638023560828") format("woff2"),
|
||||||
url("iconfont.woff?t=1636197082361") format("woff"),
|
url("iconfont.woff?t=1638023560828") format("woff"),
|
||||||
url("iconfont.ttf?t=1636197082361") format("truetype");
|
url("iconfont.ttf?t=1638023560828") format("truetype");
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
@@ -13,6 +13,10 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.team-icontabs::before {
|
||||||
|
content: "\e63e";
|
||||||
|
}
|
||||||
|
|
||||||
.team-iconlogo::before {
|
.team-iconlogo::before {
|
||||||
content: "\e620";
|
content: "\e620";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,13 @@
|
|||||||
"css_prefix_text": "team-icon",
|
"css_prefix_text": "team-icon",
|
||||||
"description": "pure-admin",
|
"description": "pure-admin",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"icon_id": "20594647",
|
||||||
|
"name": "标签页",
|
||||||
|
"font_class": "tabs",
|
||||||
|
"unicode": "e63e",
|
||||||
|
"unicode_decimal": 58942
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "22129506",
|
"icon_id": "22129506",
|
||||||
"name": "水能",
|
"name": "水能",
|
||||||
|
|||||||
1
src/assets/svg/dark.svg
Normal 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
@@ -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 |
@@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" class="re-screen" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"><g fill="currentColor"><path d="M3.5 4H1V3h2V1h1v2.5l-.5.5zM13 3V1h-1v2.5l.5.5H15V3h-2zm-1 9.5V15h1v-2h2v-1h-2.5l-.5.5zM1 12v1h2v2h1v-2.5l-.5-.5H1zm11-1.5l-.5.5h-7l-.5-.5v-5l.5-.5h7l.5.5v5zM10 7H6v2h4V7z"></path></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" class="re-screen" color="#00000073" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"><g fill="currentColor"><path d="M3.5 4H1V3h2V1h1v2.5l-.5.5zM13 3V1h-1v2.5l.5.5H15V3h-2zm-1 9.5V15h1v-2h2v-1h-2.5l-.5.5zM1 12v1h2v2h1v-2.5l-.5-.5H1zm11-1.5l-.5.5h-7l-.5-.5v-5l.5-.5h7l.5.5v5zM10 7H6v2h4V7z"></path></g></svg>
|
||||||
|
Before Width: | Height: | Size: 434 B After Width: | Height: | Size: 452 B |
@@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" class="re-screen" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"><g fill="currentColor"><path d="M3 12h10V4H3v8zm2-6h6v4H5V6zM2 6H1V2.5l.5-.5H5v1H2v3zm13-3.5V6h-1V3h-3V2h3.5l.5.5zM14 10h1v3.5l-.5.5H11v-1h3v-3zM2 13h3v1H1.5l-.5-.5V10h1v3z"></path></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" class="re-screen" color="#00000073" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"><g fill="currentColor"><path d="M3 12h10V4H3v8zm2-6h6v4H5V6zM2 6H1V2.5l.5-.5H5v1H2v3zm13-3.5V6h-1V3h-3V2h3.5l.5.5zM14 10h1v3.5l-.5.5H11v-1h3v-3zM2 13h3v1H1.5l-.5-.5V10h1v3z"></path></g></svg>
|
||||||
|
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 421 B |
@@ -12,8 +12,15 @@ import { useEventListener, tryOnUnmounted, useTimeoutFn } from "@vueuse/core";
|
|||||||
|
|
||||||
let echartInstance: ECharts;
|
let echartInstance: ECharts;
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function initechartInstance() {
|
function initechartInstance() {
|
||||||
const echartDom = document.querySelector(".bar");
|
const echartDom = document.querySelector(".bar" + props.index);
|
||||||
if (!echartDom) return;
|
if (!echartDom) return;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
echartInstance = echarts.init(echartDom);
|
echartInstance = echarts.init(echartDom);
|
||||||
@@ -85,12 +92,5 @@ tryOnUnmounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bar"></div>
|
<div :class="'bar' + props.index" style="width: 100%; height: 35vh"></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.bar {
|
|
||||||
width: 100%;
|
|
||||||
height: 35vh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -10,52 +10,48 @@ const lists = ref([
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-descriptions
|
<el-descriptions class="margin-top" direction="vertical" :column="3" border>
|
||||||
class="margin-top"
|
|
||||||
direction="vertical"
|
|
||||||
:column="3"
|
|
||||||
size="medium"
|
|
||||||
border
|
|
||||||
>
|
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<i class="el-icon-user"></i>
|
<el-icon>
|
||||||
|
<IconifyIconOffline icon="user" />
|
||||||
|
</el-icon>
|
||||||
用户名
|
用户名
|
||||||
</template>
|
</template>
|
||||||
xiaoxian
|
xiaoxian
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<i class="el-icon-mobile-phone"></i>
|
<el-icon>
|
||||||
|
<IconifyIconOffline icon="iphone" />
|
||||||
|
</el-icon>
|
||||||
手机号
|
手机号
|
||||||
</template>
|
</template>
|
||||||
123456789
|
123456789
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<i class="el-icon-location-outline"></i>
|
<el-icon>
|
||||||
|
<IconifyIconOffline icon="location" />
|
||||||
|
</el-icon>
|
||||||
居住地
|
居住地
|
||||||
</template>
|
</template>
|
||||||
上海
|
上海
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
<el-descriptions
|
<el-descriptions class="margin-top" direction="vertical" :column="2" border>
|
||||||
class="margin-top"
|
|
||||||
direction="vertical"
|
|
||||||
:column="2"
|
|
||||||
size="medium"
|
|
||||||
border
|
|
||||||
>
|
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<i class="el-icon-tickets"></i>
|
<el-icon>
|
||||||
|
<IconifyIconOffline icon="tickets" />
|
||||||
|
</el-icon>
|
||||||
标签
|
标签
|
||||||
</template>
|
</template>
|
||||||
<el-tag
|
<el-tag
|
||||||
v-for="item in lists"
|
v-for="item in lists"
|
||||||
:key="item.label"
|
:key="item.label"
|
||||||
:type="item.type"
|
:type="item.type"
|
||||||
size="mini"
|
size="small"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
>
|
>
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
@@ -63,22 +59,20 @@ const lists = ref([
|
|||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<i class="el-icon-office-building"></i>
|
<el-icon>
|
||||||
|
<IconifyIconOffline icon="office-building" />
|
||||||
|
</el-icon>
|
||||||
联系地址
|
联系地址
|
||||||
</template>
|
</template>
|
||||||
上海市徐汇区
|
上海市徐汇区
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
<el-descriptions
|
<el-descriptions class="margin-top" direction="vertical" :column="1" border>
|
||||||
class="margin-top"
|
|
||||||
direction="vertical"
|
|
||||||
:column="1"
|
|
||||||
size="medium"
|
|
||||||
border
|
|
||||||
>
|
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<i class="el-icon-notebook-1"></i>
|
<el-icon>
|
||||||
|
<IconifyIconOffline icon="notebook" />
|
||||||
|
</el-icon>
|
||||||
留言
|
留言
|
||||||
</template>
|
</template>
|
||||||
好好学习,天天向上
|
好好学习,天天向上
|
||||||
@@ -87,7 +81,7 @@ const lists = ref([
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.el-tag--mini {
|
.el-tag {
|
||||||
margin-right: 10px !important;
|
margin-right: 10px !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -12,8 +12,15 @@ import { useEventListener, tryOnUnmounted, useTimeoutFn } from "@vueuse/core";
|
|||||||
|
|
||||||
let echartInstance: ECharts;
|
let echartInstance: ECharts;
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function initechartInstance() {
|
function initechartInstance() {
|
||||||
const echartDom = document.querySelector(".line");
|
const echartDom = document.querySelector(".line" + props.index);
|
||||||
if (!echartDom) return;
|
if (!echartDom) return;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
echartInstance = echarts.init(echartDom);
|
echartInstance = echarts.init(echartDom);
|
||||||
@@ -73,12 +80,5 @@ tryOnUnmounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="line"></div>
|
<div :class="'line' + props.index" style="width: 100%; height: 35vh"></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.line {
|
|
||||||
width: 100%;
|
|
||||||
height: 35vh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -12,8 +12,15 @@ import { useEventListener, tryOnUnmounted, useTimeoutFn } from "@vueuse/core";
|
|||||||
|
|
||||||
let echartInstance: ECharts;
|
let echartInstance: ECharts;
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function initechartInstance() {
|
function initechartInstance() {
|
||||||
const echartDom = document.querySelector(".pie");
|
const echartDom = document.querySelector(".pie" + props.index);
|
||||||
if (!echartDom) return;
|
if (!echartDom) return;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
echartInstance = echarts.init(echartDom);
|
echartInstance = echarts.init(echartDom);
|
||||||
@@ -76,12 +83,5 @@ tryOnUnmounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="pie"></div>
|
<div :class="'pie' + props.index" style="width: 100%; height: 35vh"></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.pie {
|
|
||||||
width: 100%;
|
|
||||||
height: 35vh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { App } from "vue";
|
import iconifyIconOffline from "./src/iconifyIconOffline";
|
||||||
import icon from "./src/Icon.vue";
|
import iconifyIconOnline from "./src/iconifyIconOnline";
|
||||||
|
import fontIcon from "./src/iconfont";
|
||||||
|
|
||||||
export const Icon = Object.assign(icon, {
|
export const IconifyIconOffline = iconifyIconOffline;
|
||||||
install(app: App) {
|
export const IconifyIconOnline = iconifyIconOnline;
|
||||||
app.component(icon.name, icon);
|
export const FontIcon = fontIcon;
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Icon
|
IconifyIconOffline,
|
||||||
|
IconifyIconOnline,
|
||||||
|
FontIcon
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
export default {
|
|
||||||
name: "Icon"
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, computed } from "vue";
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
content: {
|
|
||||||
type: String,
|
|
||||||
default: ""
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: Number,
|
|
||||||
default: 18
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
type: Number,
|
|
||||||
default: 20
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
default: 20
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
default: ""
|
|
||||||
},
|
|
||||||
svg: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: "click"): void;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
let text = ref("");
|
|
||||||
|
|
||||||
let className = computed(() => {
|
|
||||||
if (props.content.indexOf("fa-") > -1) {
|
|
||||||
return props.content.indexOf("fa ") === 0
|
|
||||||
? props.content
|
|
||||||
: ["fa", props.content];
|
|
||||||
} else if (props.content.indexOf("el-icon-") > -1) {
|
|
||||||
return props.content;
|
|
||||||
} else if (props.content.indexOf("#") > -1) {
|
|
||||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
|
||||||
text.value = props.content;
|
|
||||||
return "iconfont";
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
|
||||||
text.value = props.content;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let iconStyle = computed(() => {
|
|
||||||
return (
|
|
||||||
"font-size: " +
|
|
||||||
props.size +
|
|
||||||
"px; color: " +
|
|
||||||
props.color +
|
|
||||||
"; width: " +
|
|
||||||
props.width +
|
|
||||||
"px; height: " +
|
|
||||||
props.height +
|
|
||||||
"px; font-style: normal;"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const clickHandle = () => {
|
|
||||||
emit("click");
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<i
|
|
||||||
v-if="!props.svg"
|
|
||||||
:class="className"
|
|
||||||
:style="iconStyle"
|
|
||||||
v-html="text"
|
|
||||||
@click="clickHandle"
|
|
||||||
></i>
|
|
||||||
<svg
|
|
||||||
class="icon-svg"
|
|
||||||
v-if="props.svg"
|
|
||||||
aria-hidden="true"
|
|
||||||
:style="iconStyle"
|
|
||||||
@click="clickHandle"
|
|
||||||
>
|
|
||||||
<use :xlink:href="`#${props.content}`" />
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
||||||
39
src/components/ReIcon/src/hooks.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { h, defineComponent, Component } from "vue";
|
||||||
|
import { IconifyIconOffline, FontIcon } from "../index";
|
||||||
|
|
||||||
|
// 支持fontawesome4、5+、iconfont、remixicon、element-plus的icons、自定义svg
|
||||||
|
export function useRenderIcon(icon: string): Component {
|
||||||
|
// iconfont
|
||||||
|
const ifReg = /^IF-/;
|
||||||
|
// typeof icon === "function" 属于SVG
|
||||||
|
if (ifReg.test(icon)) {
|
||||||
|
// iconfont
|
||||||
|
const name = icon.split(ifReg)[1];
|
||||||
|
const iconName = name.slice(
|
||||||
|
0,
|
||||||
|
name.indexOf(" ") == -1 ? name.length : name.indexOf(" ")
|
||||||
|
);
|
||||||
|
const iconType = name.slice(name.indexOf(" ") + 1, name.length);
|
||||||
|
return defineComponent({
|
||||||
|
name: "FontIcon",
|
||||||
|
render() {
|
||||||
|
return h(FontIcon, {
|
||||||
|
icon: iconName,
|
||||||
|
iconType
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (typeof icon === "function") {
|
||||||
|
// svg
|
||||||
|
return icon;
|
||||||
|
} else {
|
||||||
|
return defineComponent({
|
||||||
|
name: "Icon",
|
||||||
|
render() {
|
||||||
|
return h(IconifyIconOffline, {
|
||||||
|
icon: icon
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/components/ReIcon/src/iconfont.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { h, defineComponent } from "vue";
|
||||||
|
|
||||||
|
// 封装iconfont组件,默认`font-class`引用模式,支持`unicode`引用、`font-class`引用、`symbol`引用 (https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code)
|
||||||
|
export default defineComponent({
|
||||||
|
name: "fontIcon",
|
||||||
|
props: {
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
const attrs = this.$attrs;
|
||||||
|
if (Object.keys(attrs).includes("uni") || attrs?.iconType === "uni") {
|
||||||
|
return h(
|
||||||
|
"i",
|
||||||
|
{
|
||||||
|
class: "iconfont",
|
||||||
|
...attrs
|
||||||
|
},
|
||||||
|
this.icon
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
Object.keys(attrs).includes("svg") ||
|
||||||
|
attrs?.iconType === "svg"
|
||||||
|
) {
|
||||||
|
return h(
|
||||||
|
"svg",
|
||||||
|
{
|
||||||
|
class: "icon-svg",
|
||||||
|
"aria-hidden": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => [
|
||||||
|
h("use", {
|
||||||
|
"xlink:href": `#${this.icon}`
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return h("i", {
|
||||||
|
class: `iconfont ${this.icon}`,
|
||||||
|
...attrs
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
91
src/components/ReIcon/src/iconifyIconOffline.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { h, defineComponent } from "vue";
|
||||||
|
import { Icon as IconifyIcon, addIcon } from "@iconify/vue/dist/offline";
|
||||||
|
|
||||||
|
// element-plus icon
|
||||||
|
import Check from "@iconify-icons/ep/check";
|
||||||
|
import Menu from "@iconify-icons/ep/menu";
|
||||||
|
import HomeFilled from "@iconify-icons/ep/home-filled";
|
||||||
|
import SetUp from "@iconify-icons/ep/set-up";
|
||||||
|
import Edit from "@iconify-icons/ep/edit";
|
||||||
|
import Setting from "@iconify-icons/ep/setting";
|
||||||
|
import Lollipop from "@iconify-icons/ep/lollipop";
|
||||||
|
import Link from "@iconify-icons/ep/link";
|
||||||
|
import Position from "@iconify-icons/ep/position";
|
||||||
|
import Histogram from "@iconify-icons/ep/histogram";
|
||||||
|
import RefreshRight from "@iconify-icons/ep/refresh-right";
|
||||||
|
import ArrowDown from "@iconify-icons/ep/arrow-down";
|
||||||
|
import Close from "@iconify-icons/ep/close";
|
||||||
|
import CloseBold from "@iconify-icons/ep/close-bold";
|
||||||
|
import Bell from "@iconify-icons/ep/bell";
|
||||||
|
import Guide from "@iconify-icons/ep/guide";
|
||||||
|
import User from "@iconify-icons/ep/user";
|
||||||
|
import Iphone from "@iconify-icons/ep/iphone";
|
||||||
|
import Location from "@iconify-icons/ep/location";
|
||||||
|
import Tickets from "@iconify-icons/ep/tickets";
|
||||||
|
import OfficeBuilding from "@iconify-icons/ep/office-building";
|
||||||
|
import Notebook from "@iconify-icons/ep/notebook";
|
||||||
|
addIcon("check", Check);
|
||||||
|
addIcon("menu", Menu);
|
||||||
|
addIcon("home-filled", HomeFilled);
|
||||||
|
addIcon("set-up", SetUp);
|
||||||
|
addIcon("edit", Edit);
|
||||||
|
addIcon("setting", Setting);
|
||||||
|
addIcon("lollipop", Lollipop);
|
||||||
|
addIcon("link", Link);
|
||||||
|
addIcon("position", Position);
|
||||||
|
addIcon("histogram", Histogram);
|
||||||
|
addIcon("refresh-right", RefreshRight);
|
||||||
|
addIcon("arrow-down", ArrowDown);
|
||||||
|
addIcon("close", Close);
|
||||||
|
addIcon("close-bold", CloseBold);
|
||||||
|
addIcon("bell", Bell);
|
||||||
|
addIcon("guide", Guide);
|
||||||
|
addIcon("user", User);
|
||||||
|
addIcon("iphone", Iphone);
|
||||||
|
addIcon("location", Location);
|
||||||
|
addIcon("tickets", Tickets);
|
||||||
|
addIcon("office-building", OfficeBuilding);
|
||||||
|
addIcon("notebook", Notebook);
|
||||||
|
|
||||||
|
// remixicon
|
||||||
|
import arrowRightSLine from "@iconify-icons/ri/arrow-right-s-line";
|
||||||
|
import arrowLeftSLine from "@iconify-icons/ri/arrow-left-s-line";
|
||||||
|
import logoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
||||||
|
import nodeTree from "@iconify-icons/ri/node-tree";
|
||||||
|
addIcon("arrow-right-s-line", arrowRightSLine);
|
||||||
|
addIcon("arrow-left-s-line", arrowLeftSLine);
|
||||||
|
addIcon("logout-circle-r-line", logoutCircleRLine);
|
||||||
|
addIcon("node-tree", nodeTree);
|
||||||
|
|
||||||
|
// Font Awesome 4
|
||||||
|
import faUser from "@iconify-icons/fa/user";
|
||||||
|
import faLock from "@iconify-icons/fa/lock";
|
||||||
|
import faSignOut from "@iconify-icons/fa/sign-out";
|
||||||
|
addIcon("fa-user", faUser);
|
||||||
|
addIcon("fa-lock", faLock);
|
||||||
|
addIcon("fa-sign-out", faSignOut);
|
||||||
|
|
||||||
|
// Iconify Icon在Vue里离线使用(用于内网环境)https://docs.iconify.design/icon-components/vue/offline.html
|
||||||
|
export default defineComponent({
|
||||||
|
name: "IconifyIcon",
|
||||||
|
components: { IconifyIcon },
|
||||||
|
props: {
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
const attrs = this.$attrs;
|
||||||
|
return h(
|
||||||
|
IconifyIcon,
|
||||||
|
{
|
||||||
|
icon: `${this.icon}`,
|
||||||
|
...attrs
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
32
src/components/ReIcon/src/iconifyIconOnline.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { h, defineComponent } from "vue";
|
||||||
|
import { Icon as IconifyIcon } from "@iconify/vue";
|
||||||
|
|
||||||
|
// Iconify Icon在Vue里在线使用(用于外网环境) https://docs.iconify.design/icon-components/vue/offline.html
|
||||||
|
export default defineComponent({
|
||||||
|
name: "IconifyIcon",
|
||||||
|
components: { IconifyIcon },
|
||||||
|
props: {
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
// default element plus icon
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: "ep:"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
const attrs = this.$attrs;
|
||||||
|
return h(
|
||||||
|
IconifyIcon,
|
||||||
|
{
|
||||||
|
icon: `${this.type}${this.icon}`,
|
||||||
|
...attrs
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
import { App } from "vue";
|
import { App } from "vue";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { loadEnv } from "@build/index";
|
||||||
|
|
||||||
let config: object = {};
|
let config: object = {};
|
||||||
|
const { VITE_PUBLIC_PATH } = loadEnv();
|
||||||
|
|
||||||
const setConfig = (cfg?: unknown) => {
|
const setConfig = (cfg?: unknown) => {
|
||||||
config = Object.assign(config, cfg);
|
config = Object.assign(config, cfg);
|
||||||
@@ -30,10 +33,7 @@ export const getServerConfig = async (app: App): Promise<undefined> => {
|
|||||||
return axios({
|
return axios({
|
||||||
baseURL: "",
|
baseURL: "",
|
||||||
method: "get",
|
method: "get",
|
||||||
url:
|
url: `${VITE_PUBLIC_PATH}serverConfig.json`
|
||||||
process.env.NODE_ENV === "production"
|
|
||||||
? "/manages/serverConfig.json"
|
|
||||||
: "/serverConfig.json"
|
|
||||||
})
|
})
|
||||||
.then(({ data: config }) => {
|
.then(({ data: config }) => {
|
||||||
let $config = app.config.globalProperties.$config;
|
let $config = app.config.globalProperties.$config;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const auth: Directive = {
|
|||||||
const authRoles = value;
|
const authRoles = value;
|
||||||
const hasAuth = usePermissionStoreHook().buttonAuth.includes(authRoles);
|
const hasAuth = usePermissionStoreHook().buttonAuth.includes(authRoles);
|
||||||
if (!hasAuth) {
|
if (!hasAuth) {
|
||||||
el.style.display = "none";
|
el.parentNode.removeChild(el);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error("need roles! Like v-auth=\"['admin','test']\"");
|
throw new Error("need roles! Like v-auth=\"['admin','test']\"");
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
getCurrentInstance
|
getCurrentInstance
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import { RouterView } from "vue-router";
|
import { RouterView } from "vue-router";
|
||||||
import backTop from "/@/assets/svg/back_top.svg";
|
import backTop from "/@/assets/svg/back_top.svg?component";
|
||||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -27,12 +27,23 @@ const transitions = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const hideTabs = computed(() => {
|
const hideTabs = computed(() => {
|
||||||
return instance?.sets.hideTabs;
|
return instance?.configure.hideTabs;
|
||||||
});
|
});
|
||||||
|
|
||||||
const layout = computed(() => {
|
const layout = computed(() => {
|
||||||
return instance?.layout.layout === "vertical";
|
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({
|
const transitionMain = defineComponent({
|
||||||
render() {
|
render() {
|
||||||
return h(
|
return h(
|
||||||
@@ -71,12 +82,7 @@ const transitionMain = defineComponent({
|
|||||||
<template>
|
<template>
|
||||||
<section
|
<section
|
||||||
:class="[props.fixedHeader ? 'app-main' : 'app-main-nofixed-header']"
|
:class="[props.fixedHeader ? 'app-main' : 'app-main-nofixed-header']"
|
||||||
:style="[
|
:style="getSectionStyle"
|
||||||
hideTabs && layout ? 'padding-top: 48px;' : '',
|
|
||||||
!hideTabs && layout ? 'padding-top: 85px;' : '',
|
|
||||||
hideTabs && !layout ? 'padding-top: 62px' : '',
|
|
||||||
!hideTabs && !layout ? 'padding-top: 98px;' : ''
|
|
||||||
]"
|
|
||||||
>
|
>
|
||||||
<router-view>
|
<router-view>
|
||||||
<template #default="{ Component, route }">
|
<template #default="{ Component, route }">
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { emitter } from "/@/utils/mitt";
|
import { emitter } from "/@/utils/mitt";
|
||||||
|
import Notice from "./notice/index.vue";
|
||||||
|
import avatars from "/@/assets/avatars.jpg";
|
||||||
|
import { transformI18n } from "/@/plugins/i18n";
|
||||||
import Hamburger from "./sidebar/hamBurger.vue";
|
import Hamburger from "./sidebar/hamBurger.vue";
|
||||||
import { useRouter, useRoute } from "vue-router";
|
import { useRouter, useRoute } from "vue-router";
|
||||||
import { storageSession } from "/@/utils/storage";
|
import { storageSession } from "/@/utils/storage";
|
||||||
@@ -9,7 +13,8 @@ import { useAppStoreHook } from "/@/store/modules/app";
|
|||||||
import { unref, watch, getCurrentInstance } from "vue";
|
import { unref, watch, getCurrentInstance } from "vue";
|
||||||
import { deviceDetection } from "/@/utils/deviceDetection";
|
import { deviceDetection } from "/@/utils/deviceDetection";
|
||||||
import screenfull from "../components/screenfull/index.vue";
|
import screenfull from "../components/screenfull/index.vue";
|
||||||
import globalization from "/@/assets/svg/globalization.svg";
|
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
|
||||||
|
import globalization from "/@/assets/svg/globalization.svg?component";
|
||||||
|
|
||||||
const instance =
|
const instance =
|
||||||
getCurrentInstance().appContext.config.globalProperties.$storage;
|
getCurrentInstance().appContext.config.globalProperties.$storage;
|
||||||
@@ -17,13 +22,26 @@ const pureApp = useAppStoreHook();
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
let usename = storageSession.getItem("info")?.username;
|
let usename = storageSession.getItem("info")?.username;
|
||||||
const { locale, t } = useI18n();
|
const { locale } = useI18n();
|
||||||
|
|
||||||
|
const getDropdownItemStyle = computed(() => {
|
||||||
|
return t => {
|
||||||
|
return {
|
||||||
|
background: locale.value === t ? useEpThemeStoreHook().epThemeColor : "",
|
||||||
|
color: locale.value === t ? "#f4f4f5" : "#000"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => locale.value,
|
() => locale.value,
|
||||||
() => {
|
() => {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
document.title = t(unref(route.meta.title)); // 动态title
|
document.title = transformI18n(
|
||||||
|
//@ts-ignore
|
||||||
|
unref(route.meta.title),
|
||||||
|
unref(route.meta.i18n)
|
||||||
|
); // 动态title
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -65,31 +83,29 @@ function translationEn() {
|
|||||||
<Breadcrumb class="breadcrumb-container" />
|
<Breadcrumb class="breadcrumb-container" />
|
||||||
|
|
||||||
<div class="vertical-header-right">
|
<div class="vertical-header-right">
|
||||||
|
<!-- 通知 -->
|
||||||
|
<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 />
|
<globalization />
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu class="translation">
|
<el-dropdown-menu class="translation">
|
||||||
<el-dropdown-item
|
<el-dropdown-item
|
||||||
:style="{
|
:style="getDropdownItemStyle('zh')"
|
||||||
background: locale === 'zh' ? '#1b2a47' : '',
|
|
||||||
color: locale === 'zh' ? '#f4f4f5' : '#000'
|
|
||||||
}"
|
|
||||||
@click="translationCh"
|
@click="translationCh"
|
||||||
><el-icon class="check-zh" v-show="locale === 'zh'"
|
><IconifyIconOffline
|
||||||
><check /></el-icon
|
class="check-zh"
|
||||||
>简体中文</el-dropdown-item
|
v-show="locale === 'zh'"
|
||||||
|
icon="check"
|
||||||
|
/>简体中文</el-dropdown-item
|
||||||
>
|
>
|
||||||
<el-dropdown-item
|
<el-dropdown-item
|
||||||
:style="{
|
:style="getDropdownItemStyle('en')"
|
||||||
background: locale === 'en' ? '#1b2a47' : '',
|
|
||||||
color: locale === 'en' ? '#f4f4f5' : '#000'
|
|
||||||
}"
|
|
||||||
@click="translationEn"
|
@click="translationEn"
|
||||||
><el-icon class="check-en" v-show="locale === 'en'"
|
><el-icon class="check-en" v-show="locale === 'en'"
|
||||||
><check /></el-icon
|
><IconifyIconOffline icon="check" /></el-icon
|
||||||
>English</el-dropdown-item
|
>English</el-dropdown-item
|
||||||
>
|
>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
@@ -98,25 +114,26 @@ function translationEn() {
|
|||||||
<!-- 退出登陆 -->
|
<!-- 退出登陆 -->
|
||||||
<el-dropdown trigger="click">
|
<el-dropdown trigger="click">
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
<img
|
<img :src="avatars" />
|
||||||
src="https://avatars.githubusercontent.com/u/44761321?s=400&u=30907819abd29bb3779bc247910873e7c7f7c12f&v=4"
|
|
||||||
/>
|
|
||||||
<p>{{ usename }}</p>
|
<p>{{ usename }}</p>
|
||||||
</span>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu class="logout">
|
<el-dropdown-menu class="logout">
|
||||||
<el-dropdown-item icon="el-icon-switch-button" @click="logout">{{
|
<el-dropdown-item @click="logout">
|
||||||
$t("message.hsLoginOut")
|
<IconifyIconOffline
|
||||||
}}</el-dropdown-item>
|
icon="logout-circle-r-line"
|
||||||
|
style="margin: 5px"
|
||||||
|
/>{{ $t("buttons.hsLoginOut") }}</el-dropdown-item
|
||||||
|
>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
<el-icon
|
<el-icon
|
||||||
class="el-icon-setting"
|
class="el-icon-setting"
|
||||||
:title="$t('message.hssystemSet')"
|
:title="$t('buttons.hssystemSet')"
|
||||||
@click="onPanel"
|
@click="onPanel"
|
||||||
>
|
>
|
||||||
<Setting />
|
<IconifyIconOffline icon="setting" />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -137,10 +154,6 @@ function translationEn() {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.3s;
|
transition: background 0.3s;
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(0, 0, 0, 0.025);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vertical-header-right {
|
.vertical-header-right {
|
||||||
@@ -151,6 +164,12 @@ function translationEn() {
|
|||||||
color: #000000d9;
|
color: #000000d9;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
:deep(.dropdown-badge) {
|
||||||
|
&:hover {
|
||||||
|
background: #f6f6f6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.screen-full {
|
.screen-full {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
@@ -216,7 +235,7 @@ function translationEn() {
|
|||||||
|
|
||||||
.translation {
|
.translation {
|
||||||
.el-dropdown-menu__item {
|
.el-dropdown-menu__item {
|
||||||
padding: 0 40px !important;
|
padding: 5px 40px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-dropdown-menu__item:focus,
|
.el-dropdown-menu__item:focus,
|
||||||
@@ -228,19 +247,21 @@ function translationEn() {
|
|||||||
.check-zh {
|
.check-zh {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 20px;
|
left: 20px;
|
||||||
top: 13px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.check-en {
|
.check-en {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 13px;
|
|
||||||
left: 20px;
|
left: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.logout {
|
.logout {
|
||||||
|
max-width: 120px;
|
||||||
|
|
||||||
.el-dropdown-menu__item {
|
.el-dropdown-menu__item {
|
||||||
padding: 0 18px !important;
|
min-width: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-dropdown-menu__item:focus,
|
.el-dropdown-menu__item:focus,
|
||||||
|
|||||||
146
src/layout/components/notice/data.ts
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
export interface ListItem {
|
||||||
|
avatar: string;
|
||||||
|
title: string;
|
||||||
|
datetime: string;
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
status?: "" | "success" | "warning" | "info" | "danger";
|
||||||
|
extra?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TabItem {
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
list: ListItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const noticesData: TabItem[] = [
|
||||||
|
{
|
||||||
|
key: "1",
|
||||||
|
name: "通知",
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
avatar:
|
||||||
|
"https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
|
||||||
|
title: "你收到了 12 份新周报",
|
||||||
|
datetime: "一年前",
|
||||||
|
description: "",
|
||||||
|
type: "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar:
|
||||||
|
"https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png",
|
||||||
|
title: "你推荐的 前端高手 已通过第三轮面试",
|
||||||
|
datetime: "一年前",
|
||||||
|
description: "",
|
||||||
|
type: "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar:
|
||||||
|
"https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png",
|
||||||
|
title: "这种模板可以区分多种通知类型",
|
||||||
|
datetime: "一年前",
|
||||||
|
description: "",
|
||||||
|
type: "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar:
|
||||||
|
"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
|
||||||
|
title:
|
||||||
|
"展示标题内容超过一行后的处理方式,如果内容超过1行将自动截断并支持tooltip显示完整标题。",
|
||||||
|
datetime: "一年前",
|
||||||
|
description: "",
|
||||||
|
type: "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar:
|
||||||
|
"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
|
||||||
|
title: "左侧图标用于区分不同的类型",
|
||||||
|
datetime: "一年前",
|
||||||
|
description: "",
|
||||||
|
type: "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar:
|
||||||
|
"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
|
||||||
|
title: "左侧图标用于区分不同的类型",
|
||||||
|
datetime: "一年前",
|
||||||
|
description: "",
|
||||||
|
type: "1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "2",
|
||||||
|
name: "消息",
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
avatar:
|
||||||
|
"https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg",
|
||||||
|
title: "李白 评论了你",
|
||||||
|
description: "长风破浪会有时,直挂云帆济沧海",
|
||||||
|
datetime: "一年前",
|
||||||
|
type: "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar:
|
||||||
|
"https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg",
|
||||||
|
title: "李白 回复了你",
|
||||||
|
description: "行路难,行路难,多歧路,今安在。",
|
||||||
|
datetime: "一年前",
|
||||||
|
type: "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar:
|
||||||
|
"https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg",
|
||||||
|
title: "标题",
|
||||||
|
description:
|
||||||
|
"请将鼠标移动到此处,以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2,超过2行的描述内容将被省略并且可以通过tooltip查看完整内容",
|
||||||
|
datetime: "一年前",
|
||||||
|
type: "2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "3",
|
||||||
|
name: "代办",
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
avatar: "",
|
||||||
|
title: "任务名称",
|
||||||
|
description: "任务需要在 2022-11-16 20:00 前启动",
|
||||||
|
datetime: "",
|
||||||
|
extra: "未开始",
|
||||||
|
status: "info",
|
||||||
|
type: "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: "",
|
||||||
|
title: "第三方紧急代码变更",
|
||||||
|
description:
|
||||||
|
"一拳提交于 2022-11-16,需在 2022-11-18 前完成代码变更任务",
|
||||||
|
datetime: "",
|
||||||
|
extra: "马上到期",
|
||||||
|
status: "danger",
|
||||||
|
type: "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: "",
|
||||||
|
title: "信息安全考试",
|
||||||
|
description: "指派小仙于 2022-12-12 前完成更新并发布",
|
||||||
|
datetime: "",
|
||||||
|
extra: "已耗时 8 天",
|
||||||
|
status: "warning",
|
||||||
|
type: "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: "",
|
||||||
|
title: "vue-pure-admin 版本发布",
|
||||||
|
description: "vue-pure-admin 版本发布",
|
||||||
|
datetime: "",
|
||||||
|
extra: "进行中",
|
||||||
|
type: "3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
82
src/layout/components/notice/index.vue
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import NoticeList from "./noticeList.vue";
|
||||||
|
import { noticesData } from "./data";
|
||||||
|
|
||||||
|
const activeName = ref(noticesData[0].name);
|
||||||
|
const notices = ref(noticesData);
|
||||||
|
|
||||||
|
let noticesNum = ref(0);
|
||||||
|
notices.value.forEach(notice => {
|
||||||
|
noticesNum.value += notice.list.length;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-dropdown trigger="click" placement="bottom-end">
|
||||||
|
<span class="dropdown-badge">
|
||||||
|
<el-badge :value="noticesNum" :max="99">
|
||||||
|
<el-icon class="header-notice-icon"
|
||||||
|
><IconifyIconOffline icon="bell"
|
||||||
|
/></el-icon>
|
||||||
|
</el-badge>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-tabs v-model="activeName" class="dropdown-tabs">
|
||||||
|
<template v-for="item in notices" :key="item.key">
|
||||||
|
<el-tab-pane
|
||||||
|
:label="`${item.name}(${item.list.length})`"
|
||||||
|
:name="item.name"
|
||||||
|
>
|
||||||
|
<el-scrollbar max-height="330px">
|
||||||
|
<div class="noticeList-container">
|
||||||
|
<NoticeList :list="item.list" />
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</el-tab-pane>
|
||||||
|
</template>
|
||||||
|
</el-tabs>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dropdown-badge {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 48px;
|
||||||
|
width: 60px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.header-notice-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-tabs {
|
||||||
|
width: 336px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
:deep(.el-tabs__header) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__nav-scroll) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__nav-wrap)::after {
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.noticeList-container) {
|
||||||
|
padding: 15px 24px 0 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
168
src/layout/components/notice/noticeItem.vue
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ListItem } from "./data";
|
||||||
|
import { ref, PropType, nextTick } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
noticeItem: {
|
||||||
|
type: Object as PropType<ListItem>,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const titleRef = ref(null);
|
||||||
|
const descriptionRef = ref(null);
|
||||||
|
const titleTooltip = ref(false);
|
||||||
|
const descriptionTooltip = ref(false);
|
||||||
|
|
||||||
|
function hoverTitle() {
|
||||||
|
nextTick(() => {
|
||||||
|
titleRef.value?.scrollWidth > titleRef.value?.clientWidth
|
||||||
|
? (titleTooltip.value = true)
|
||||||
|
: (titleTooltip.value = false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function hoverDescription(event, description) {
|
||||||
|
// currentWidth 为文本在页面中所占的宽度,创建标签,加入到页面,获取currentWidth ,最后在移除
|
||||||
|
let tempTag = document.createElement("span");
|
||||||
|
tempTag.innerText = description;
|
||||||
|
tempTag.className = "getDescriptionWidth";
|
||||||
|
document.querySelector("body").appendChild(tempTag);
|
||||||
|
let currentWidth = (
|
||||||
|
document.querySelector(".getDescriptionWidth") as HTMLSpanElement
|
||||||
|
).offsetWidth;
|
||||||
|
document.querySelector(".getDescriptionWidth").remove();
|
||||||
|
|
||||||
|
// cellWidth为容器的宽度
|
||||||
|
const cellWidth = event.target.offsetWidth;
|
||||||
|
|
||||||
|
// 当文本宽度大于容器宽度两倍时,代表文本显示超过两行
|
||||||
|
currentWidth > 2 * cellWidth
|
||||||
|
? (descriptionTooltip.value = true)
|
||||||
|
: (descriptionTooltip.value = false);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="notice-container">
|
||||||
|
<el-avatar
|
||||||
|
v-if="props.noticeItem.avatar"
|
||||||
|
:size="30"
|
||||||
|
:src="props.noticeItem.avatar"
|
||||||
|
class="notice-container-avatar"
|
||||||
|
></el-avatar>
|
||||||
|
<div class="notice-container-text">
|
||||||
|
<div class="notice-text-title">
|
||||||
|
<el-tooltip
|
||||||
|
popper-class="notice-title-popper"
|
||||||
|
:disabled="!titleTooltip"
|
||||||
|
:content="props.noticeItem.title"
|
||||||
|
placement="top-start"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref="titleRef"
|
||||||
|
class="notice-title-content"
|
||||||
|
@mouseover="hoverTitle"
|
||||||
|
>
|
||||||
|
{{ props.noticeItem.title }}
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tag
|
||||||
|
v-if="props.noticeItem?.extra"
|
||||||
|
:type="props.noticeItem?.status"
|
||||||
|
size="small"
|
||||||
|
class="notice-title-extra"
|
||||||
|
>{{ props.noticeItem?.extra }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-tooltip
|
||||||
|
popper-class="notice-title-popper"
|
||||||
|
:disabled="!descriptionTooltip"
|
||||||
|
:content="props.noticeItem.description"
|
||||||
|
placement="top-start"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref="descriptionRef"
|
||||||
|
class="notice-text-description"
|
||||||
|
@mouseover="hoverDescription($event, props.noticeItem.description)"
|
||||||
|
>
|
||||||
|
{{ props.noticeItem.description }}
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
<div class="notice-text-datetime">
|
||||||
|
{{ props.noticeItem.datetime }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.notice-title-popper {
|
||||||
|
max-width: 238px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.notice-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
.notice-container-avatar {
|
||||||
|
margin-right: 16px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-container-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.notice-text-title {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5715;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.notice-title-content {
|
||||||
|
flex: 1;
|
||||||
|
width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-title-extra {
|
||||||
|
float: right;
|
||||||
|
margin-top: -1.5px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-text-description,
|
||||||
|
.notice-text-datetime {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5715;
|
||||||
|
color: rgba(0, 0, 0, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-text-description {
|
||||||
|
display: -webkit-box;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-text-datetime {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
23
src/layout/components/notice/noticeList.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { PropType } from "vue";
|
||||||
|
import NoticeItem from "./noticeItem.vue";
|
||||||
|
import { ListItem } from "./data";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
list: {
|
||||||
|
type: Array as PropType<Array<ListItem>>,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="props.list.length">
|
||||||
|
<NoticeItem
|
||||||
|
v-for="(item, index) in props.list"
|
||||||
|
:noticeItem="item"
|
||||||
|
:key="index"
|
||||||
|
></NoticeItem>
|
||||||
|
</div>
|
||||||
|
<el-empty v-else description="暂无数据"></el-empty>
|
||||||
|
</template>
|
||||||
@@ -23,7 +23,7 @@ emitter.on("openPanel", () => {
|
|||||||
<div class="project-configuration">
|
<div class="project-configuration">
|
||||||
<h3>项目配置</h3>
|
<h3>项目配置</h3>
|
||||||
<el-icon title="关闭配置" class="el-icon-close" @click="show = !show">
|
<el-icon title="关闭配置" class="el-icon-close" @click="show = !show">
|
||||||
<Close />
|
<IconifyIconOffline icon="close" />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
<div style="border-bottom: 1px solid #dcdfe6"></div>
|
<div style="border-bottom: 1px solid #dcdfe6"></div>
|
||||||
|
|||||||
@@ -5,25 +5,21 @@ const { isFullscreen, toggle } = useFullscreen();
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="screen-full" @click="toggle">
|
<div class="screen-full" @click="toggle">
|
||||||
<i
|
<FontIcon
|
||||||
:title="
|
:title="
|
||||||
isFullscreen
|
isFullscreen
|
||||||
? $t('message.hsexitfullscreen')
|
? $t('buttons.hsexitfullscreen')
|
||||||
: $t('message.hsfullscreen')
|
: $t('buttons.hsfullscreen')
|
||||||
"
|
"
|
||||||
:class="
|
:icon="isFullscreen ? 'team-iconexit-fullscreen' : 'team-iconfullscreen'"
|
||||||
isFullscreen
|
/>
|
||||||
? 'iconfont team-iconexit-fullscreen'
|
|
||||||
: 'iconfont team-iconfullscreen'
|
|
||||||
"
|
|
||||||
></i>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.screen-full {
|
.screen-full {
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 62px;
|
height: 48px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
|
|||||||
@@ -9,18 +9,29 @@ import {
|
|||||||
useCssModule,
|
useCssModule,
|
||||||
getCurrentInstance
|
getCurrentInstance
|
||||||
} from "vue";
|
} 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 { useRouter } from "vue-router";
|
||||||
|
import panel from "../panel/index.vue";
|
||||||
import { emitter } from "/@/utils/mitt";
|
import { emitter } from "/@/utils/mitt";
|
||||||
import { templateRef } from "@vueuse/core";
|
import { templateRef } from "@vueuse/core";
|
||||||
import { debounce } from "/@/utils/debounce";
|
import { debounce } from "/@/utils/debounce";
|
||||||
import { themeColorsType } from "../../types";
|
import { themeColorsType } from "../../types";
|
||||||
import { useAppStoreHook } from "/@/store/modules/app";
|
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 { 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";
|
import { toggleTheme } from "@zougt/vite-plugin-theme-preprocessor/dist/browser-utils";
|
||||||
|
|
||||||
|
import dayIcon from "/@/assets/svg/day.svg?component";
|
||||||
|
import darkIcon from "/@/assets/svg/dark.svg?component";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { isSelect } = useCssModule();
|
const { isSelect } = useCssModule();
|
||||||
|
const body = document.documentElement as HTMLElement;
|
||||||
const instance =
|
const instance =
|
||||||
getCurrentInstance().appContext.app.config.globalProperties.$storage;
|
getCurrentInstance().appContext.app.config.globalProperties.$storage;
|
||||||
|
|
||||||
@@ -28,23 +39,23 @@ const instanceConfig =
|
|||||||
getCurrentInstance().appContext.app.config.globalProperties.$config;
|
getCurrentInstance().appContext.app.config.globalProperties.$config;
|
||||||
|
|
||||||
let themeColors = ref<Array<themeColorsType>>([
|
let themeColors = ref<Array<themeColorsType>>([
|
||||||
// 暗雅(默认)
|
// 道奇蓝(默认)
|
||||||
{ rgb: "27, 42, 71", themeColor: "default" },
|
{ rgb: "27, 42, 71", themeColor: "default" },
|
||||||
// 明亮
|
// 亮白色
|
||||||
{ rgb: "255, 255, 255", themeColor: "light" },
|
{ rgb: "255, 255, 255", themeColor: "light" },
|
||||||
// 薄暮
|
// 猩红色
|
||||||
{ rgb: "245, 34, 45", themeColor: "dusk" },
|
{ rgb: "245, 34, 45", themeColor: "dusk" },
|
||||||
// 火山
|
// 橙红色
|
||||||
{ rgb: "250, 84, 28", themeColor: "volcano" },
|
{ rgb: "250, 84, 28", themeColor: "volcano" },
|
||||||
// 黄色
|
// 金色
|
||||||
{ rgb: "250, 219, 20", themeColor: "yellow" },
|
{ rgb: "250, 219, 20", themeColor: "yellow" },
|
||||||
// 明青
|
// 绿宝石
|
||||||
{ rgb: "19, 194, 194", themeColor: "mingQing" },
|
{ rgb: "19, 194, 194", themeColor: "mingQing" },
|
||||||
// 极光绿
|
// 酸橙绿
|
||||||
{ rgb: "82, 196, 26", themeColor: "auroraGreen" },
|
{ rgb: "82, 196, 26", themeColor: "auroraGreen" },
|
||||||
// 粉红
|
// 深粉色
|
||||||
{ rgb: "235, 47, 150", themeColor: "pink" },
|
{ rgb: "235, 47, 150", themeColor: "pink" },
|
||||||
// 酱紫
|
// 深紫罗兰色
|
||||||
{ rgb: "114, 46, 209", themeColor: "saucePurple" }
|
{ rgb: "114, 46, 209", themeColor: "saucePurple" }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -69,16 +80,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({
|
const settings = reactive({
|
||||||
greyVal: instance.sets.grey,
|
greyVal: instance.configure.grey,
|
||||||
weakVal: instance.sets.weak,
|
weakVal: instance.configure.weak,
|
||||||
tabsVal: instance.sets.hideTabs
|
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) {
|
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
|
||||||
const targetEl = target || document.body;
|
const targetEl = target || document.body;
|
||||||
let { className } = targetEl;
|
let { className } = targetEl;
|
||||||
@@ -89,11 +117,7 @@ function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
|
|||||||
// 灰色模式设置
|
// 灰色模式设置
|
||||||
const greyChange = (value): void => {
|
const greyChange = (value): void => {
|
||||||
toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
|
toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
|
||||||
instance.sets = {
|
changeStorageConfigure("grey", value);
|
||||||
grey: value,
|
|
||||||
weak: instance.sets.weak,
|
|
||||||
hideTabs: instance.sets.hideTabs
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 色弱模式设置
|
// 色弱模式设置
|
||||||
@@ -103,51 +127,57 @@ const weekChange = (value): void => {
|
|||||||
"html-weakness",
|
"html-weakness",
|
||||||
document.querySelector("html")
|
document.querySelector("html")
|
||||||
);
|
);
|
||||||
instance.sets = {
|
changeStorageConfigure("weak", value);
|
||||||
grey: instance.sets.grey,
|
|
||||||
weak: value,
|
|
||||||
hideTabs: instance.sets.hideTabs
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const tagsChange = () => {
|
const tagsChange = () => {
|
||||||
let showVal = settings.tabsVal;
|
let showVal = settings.tabsVal;
|
||||||
instance.sets = {
|
changeStorageConfigure("hideTabs", showVal);
|
||||||
grey: instance.sets.grey,
|
|
||||||
weak: instance.sets.weak,
|
|
||||||
hideTabs: showVal
|
|
||||||
};
|
|
||||||
emitter.emit("tagViewsChange", showVal);
|
emitter.emit("tagViewsChange", showVal);
|
||||||
};
|
};
|
||||||
|
|
||||||
//初始化项目配置
|
const multiTagsCacheChange = () => {
|
||||||
nextTick(() => {
|
let multiTagsCache = settings.multiTagsCache;
|
||||||
settings.greyVal &&
|
changeStorageConfigure("multiTagsCache", multiTagsCache);
|
||||||
document.querySelector("html")?.setAttribute("class", "html-grey");
|
useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
|
||||||
settings.weakVal &&
|
};
|
||||||
document.querySelector("html")?.setAttribute("class", "html-weakness");
|
|
||||||
settings.tabsVal && tagsChange();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 清空缓存并返回登录页
|
// 清空缓存并返回登录页
|
||||||
function onReset() {
|
function onReset() {
|
||||||
|
toggleClass(getConfig().Grey, "html-grey", document.querySelector("html"));
|
||||||
|
toggleClass(
|
||||||
|
getConfig().Weak,
|
||||||
|
"html-weakness",
|
||||||
|
document.querySelector("html")
|
||||||
|
);
|
||||||
|
useMultiTagsStoreHook().handleTags("equal", [
|
||||||
|
{
|
||||||
|
path: "/welcome",
|
||||||
|
parentPath: "/",
|
||||||
|
meta: {
|
||||||
|
title: "menus.hshome",
|
||||||
|
icon: "home-filled",
|
||||||
|
i18n: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
useMultiTagsStoreHook().multiTagsCacheChange(getConfig().MultiTagsCache);
|
||||||
|
useEpThemeStoreHook().setEpThemeColor(getConfig().EpThemeColor);
|
||||||
storageLocal.clear();
|
storageLocal.clear();
|
||||||
storageSession.clear();
|
storageSession.clear();
|
||||||
toggleClass(false, "html-grey", document.querySelector("html"));
|
|
||||||
toggleClass(false, "html-weakness", document.querySelector("html"));
|
|
||||||
router.push("/login");
|
router.push("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange(label) {
|
function onChange(label) {
|
||||||
storageLocal.setItem("showModel", label);
|
changeStorageConfigure("showModel", label);
|
||||||
emitter.emit("tagViewsShowModel", label);
|
emitter.emit("tagViewsShowModel", label);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 侧边栏Logo
|
// 侧边栏Logo
|
||||||
function logoChange() {
|
function logoChange() {
|
||||||
unref(logoVal) === "1"
|
unref(logoVal)
|
||||||
? storageLocal.setItem("logoVal", "1")
|
? changeStorageConfigure("showLogo", true)
|
||||||
: storageLocal.setItem("logoVal", "-1");
|
: changeStorageConfigure("showLogo", false);
|
||||||
emitter.emit("logoChange", unref(logoVal));
|
emitter.emit("logoChange", unref(logoVal));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,23 +223,97 @@ const getThemeColor = computed(() => {
|
|||||||
function setLayoutModel(layout: string) {
|
function setLayoutModel(layout: string) {
|
||||||
layoutTheme.value.layout = layout;
|
layoutTheme.value.layout = layout;
|
||||||
window.document.body.setAttribute("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);
|
useAppStoreHook().setLayout(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 存放夜间主题切换前的导航主题色
|
||||||
|
let tempLayoutThemeColor;
|
||||||
|
|
||||||
// 设置导航主题色
|
// 设置导航主题色
|
||||||
function setLayoutThemeColor(theme: string) {
|
function setLayoutThemeColor(theme: string) {
|
||||||
|
tempLayoutThemeColor = instance.layout.theme;
|
||||||
layoutTheme.value.theme = theme;
|
layoutTheme.value.theme = theme;
|
||||||
toggleTheme({
|
toggleTheme({
|
||||||
scopeName: `layout-theme-${theme}`
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<panel>
|
<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">
|
<ul class="pure-theme">
|
||||||
<el-tooltip class="item" content="左侧菜单模式" placement="bottom">
|
<el-tooltip class="item" content="左侧菜单模式" placement="bottom">
|
||||||
<li
|
<li
|
||||||
@@ -234,12 +338,12 @@ function setLayoutThemeColor(theme: string) {
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<el-divider>主题色</el-divider>
|
<el-divider v-show="!dataTheme">主题色</el-divider>
|
||||||
<ul class="theme-color">
|
<ul class="theme-color" v-show="!dataTheme">
|
||||||
<li
|
<li
|
||||||
v-for="(item, index) in themeColors"
|
v-for="(item, index) in themeColors"
|
||||||
:key="index"
|
:key="index"
|
||||||
:style="{ background: `rgb(${item.rgb})` }"
|
:style="getThemeColorStyle(item.rgb)"
|
||||||
@click="setLayoutThemeColor(item.themeColor)"
|
@click="setLayoutThemeColor(item.themeColor)"
|
||||||
>
|
>
|
||||||
<el-icon
|
<el-icon
|
||||||
@@ -247,14 +351,14 @@ function setLayoutThemeColor(theme: string) {
|
|||||||
:size="17"
|
:size="17"
|
||||||
:color="getThemeColor(item.themeColor)"
|
:color="getThemeColor(item.themeColor)"
|
||||||
>
|
>
|
||||||
<Check />
|
<IconifyIconOffline icon="check" />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<el-divider>界面显示</el-divider>
|
<el-divider>界面显示</el-divider>
|
||||||
<ul class="setting">
|
<ul class="setting">
|
||||||
<li>
|
<li v-show="!dataTheme">
|
||||||
<span>灰色模式</span>
|
<span>灰色模式</span>
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="settings.greyVal"
|
v-model="settings.greyVal"
|
||||||
@@ -266,7 +370,7 @@ function setLayoutThemeColor(theme: string) {
|
|||||||
>
|
>
|
||||||
</el-switch>
|
</el-switch>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li v-show="!dataTheme">
|
||||||
<span>色弱模式</span>
|
<span>色弱模式</span>
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="settings.weakVal"
|
v-model="settings.weakVal"
|
||||||
@@ -295,8 +399,8 @@ function setLayoutThemeColor(theme: string) {
|
|||||||
<el-switch
|
<el-switch
|
||||||
v-model="logoVal"
|
v-model="logoVal"
|
||||||
inline-prompt
|
inline-prompt
|
||||||
active-value="1"
|
:active-value="true"
|
||||||
inactive-value="-1"
|
:inactive-value="false"
|
||||||
inactive-color="#a6a6a6"
|
inactive-color="#a6a6a6"
|
||||||
active-text="开"
|
active-text="开"
|
||||||
inactive-text="关"
|
inactive-text="关"
|
||||||
@@ -304,6 +408,18 @@ function setLayoutThemeColor(theme: string) {
|
|||||||
>
|
>
|
||||||
</el-switch>
|
</el-switch>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<span>标签页持久化</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="settings.multiTagsCache"
|
||||||
|
inline-prompt
|
||||||
|
inactive-color="#a6a6a6"
|
||||||
|
active-text="开"
|
||||||
|
inactive-text="关"
|
||||||
|
@change="multiTagsCacheChange"
|
||||||
|
>
|
||||||
|
</el-switch>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<span>标签风格</span>
|
<span>标签风格</span>
|
||||||
@@ -320,7 +436,12 @@ function setLayoutThemeColor(theme: string) {
|
|||||||
style="width: 90%; margin: 24px 15px"
|
style="width: 90%; margin: 24px 15px"
|
||||||
@click="onReset"
|
@click="onReset"
|
||||||
>
|
>
|
||||||
<i class="fa fa-sign-out"></i>
|
<IconifyIconOffline
|
||||||
|
icon="fa-sign-out"
|
||||||
|
width="15"
|
||||||
|
height="15"
|
||||||
|
style="margin-right: 4px"
|
||||||
|
/>
|
||||||
清空缓存并返回登录页</el-button
|
清空缓存并返回登录页</el-button
|
||||||
>
|
>
|
||||||
</panel>
|
</panel>
|
||||||
@@ -328,7 +449,7 @@ function setLayoutThemeColor(theme: string) {
|
|||||||
|
|
||||||
<style scoped module>
|
<style scoped module>
|
||||||
.isSelect {
|
.isSelect {
|
||||||
border: 2px solid #0960bd;
|
border: 2px solid var(--el-color-primary);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -349,6 +470,14 @@ function setLayoutThemeColor(theme: string) {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pure-datatheme {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
text-align: center;
|
||||||
|
display: block;
|
||||||
|
padding-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
.pure-theme {
|
.pure-theme {
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
|
import { isEqual } from "lodash-es";
|
||||||
|
import { transformI18n } from "/@/plugins/i18n";
|
||||||
|
import { getParentPaths, findRouteByPath } from "/@/router/utils";
|
||||||
|
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
|
||||||
import { useRoute, useRouter, RouteLocationMatched } from "vue-router";
|
import { useRoute, useRouter, RouteLocationMatched } from "vue-router";
|
||||||
|
|
||||||
const levelList = ref([]);
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const levelList = ref([]);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const routes = router.options.routes;
|
||||||
|
const multiTags = useMultiTagsStoreHook().multiTags;
|
||||||
|
|
||||||
const isDashboard = (route: RouteLocationMatched): boolean | string => {
|
const isDashboard = (route: RouteLocationMatched): boolean | string => {
|
||||||
const name = route && (route.name as string);
|
const name = route && (route.name as string);
|
||||||
@@ -15,19 +21,57 @@ const isDashboard = (route: RouteLocationMatched): boolean | string => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getBreadcrumb = (): void => {
|
const getBreadcrumb = (): void => {
|
||||||
let matched = route.matched.filter(item => item.meta && item.meta.title);
|
// 当前路由信息
|
||||||
|
let currentRoute;
|
||||||
|
if (Object.keys(route.query).length > 0) {
|
||||||
|
multiTags.forEach(item => {
|
||||||
|
if (isEqual(route.query, item?.query)) {
|
||||||
|
currentRoute = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
currentRoute = findRouteByPath(router.currentRoute.value.path, multiTags);
|
||||||
|
}
|
||||||
|
// 当前路由的父级路径组成的数组
|
||||||
|
const parentRoutes = getParentPaths(router.currentRoute.value.path, routes);
|
||||||
|
// 存放组成面包屑的数组
|
||||||
|
let matched = [];
|
||||||
|
// 获取每个父级路径对应的路由信息
|
||||||
|
parentRoutes.forEach(path => {
|
||||||
|
if (path !== "/") {
|
||||||
|
matched.push(findRouteByPath(path, routes));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (router.currentRoute.value.meta?.refreshRedirect) {
|
||||||
|
matched.unshift(
|
||||||
|
findRouteByPath(
|
||||||
|
router.currentRoute.value.meta.refreshRedirect as string,
|
||||||
|
routes
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 过滤与子级相同标题的父级路由
|
||||||
|
matched = matched.filter(item => {
|
||||||
|
return !item.redirect || (item.redirect && item.children.length !== 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (currentRoute?.path !== "/welcome") {
|
||||||
|
matched.push(currentRoute);
|
||||||
|
}
|
||||||
|
|
||||||
const first = matched[0];
|
const first = matched[0];
|
||||||
if (!isDashboard(first)) {
|
if (!isDashboard(first)) {
|
||||||
matched = [
|
matched = [
|
||||||
{
|
{
|
||||||
path: "/welcome",
|
path: "/welcome",
|
||||||
parentPath: "/",
|
parentPath: "/",
|
||||||
meta: { title: "message.hshome" }
|
meta: { title: "menus.hshome", i18n: true }
|
||||||
} as unknown as RouteLocationMatched
|
} as unknown as RouteLocationMatched
|
||||||
].concat(matched);
|
].concat(matched);
|
||||||
}
|
}
|
||||||
|
|
||||||
levelList.value = matched.filter(
|
levelList.value = matched.filter(
|
||||||
item => item.meta && item.meta.title && item.meta.breadcrumb !== false
|
item => item?.meta && item?.meta.title !== false
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,6 +82,11 @@ watch(
|
|||||||
() => getBreadcrumb()
|
() => getBreadcrumb()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.query,
|
||||||
|
() => getBreadcrumb()
|
||||||
|
);
|
||||||
|
|
||||||
const handleLink = (item: RouteLocationMatched): any => {
|
const handleLink = (item: RouteLocationMatched): any => {
|
||||||
const { redirect, path } = item;
|
const { redirect, path } = item;
|
||||||
if (redirect) {
|
if (redirect) {
|
||||||
@@ -55,10 +104,10 @@ const handleLink = (item: RouteLocationMatched): any => {
|
|||||||
<span
|
<span
|
||||||
v-if="item.redirect === 'noRedirect' || index == levelList.length - 1"
|
v-if="item.redirect === 'noRedirect' || index == levelList.length - 1"
|
||||||
class="no-redirect"
|
class="no-redirect"
|
||||||
>{{ $t(item.meta.title) }}</span
|
>{{ transformI18n(item.meta.title, item.meta.i18n) }}</span
|
||||||
>
|
>
|
||||||
<a v-else @click.prevent="handleLink(item)">
|
<a v-else @click.prevent="handleLink(item)">
|
||||||
{{ $t(item.meta.title) }}
|
{{ transformI18n(item.meta.title, item.meta.i18n) }}
|
||||||
</a>
|
</a>
|
||||||
</el-breadcrumb-item>
|
</el-breadcrumb-item>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
|
||||||
export interface Props {
|
export interface Props {
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
}
|
}
|
||||||
@@ -7,6 +9,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
isActive: false
|
isActive: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const fillColor = ref<string>("");
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "toggleClick"): void;
|
(e: "toggleClick"): void;
|
||||||
}>();
|
}>();
|
||||||
@@ -21,8 +25,11 @@ const toggleClick = () => {
|
|||||||
:class="classes.container"
|
:class="classes.container"
|
||||||
:title="props.isActive ? '点击折叠' : '点击展开'"
|
:title="props.isActive ? '点击折叠' : '点击展开'"
|
||||||
@click="toggleClick"
|
@click="toggleClick"
|
||||||
|
@mouseenter="fillColor = useEpThemeStoreHook().epThemeColor"
|
||||||
|
@mouseleave="fillColor = ''"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
:fill="fillColor"
|
||||||
:class="['hamburger', props.isActive ? 'is-active' : '']"
|
:class="['hamburger', props.isActive ? 'is-active' : '']"
|
||||||
viewBox="0 0 1024 1024"
|
viewBox="0 0 1024 1024"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|||||||
@@ -9,16 +9,16 @@ import {
|
|||||||
} from "vue";
|
} from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { emitter } from "/@/utils/mitt";
|
import { emitter } from "/@/utils/mitt";
|
||||||
|
import Notice from "../notice/index.vue";
|
||||||
import { templateRef } from "@vueuse/core";
|
import { templateRef } from "@vueuse/core";
|
||||||
import SidebarItem from "./sidebarItem.vue";
|
import SidebarItem from "./sidebarItem.vue";
|
||||||
import { algorithm } from "/@/utils/algorithm";
|
import avatars from "/@/assets/avatars.jpg";
|
||||||
import screenfull from "../screenfull/index.vue";
|
import screenfull from "../screenfull/index.vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { storageSession } from "/@/utils/storage";
|
import { storageSession } from "/@/utils/storage";
|
||||||
import Icon from "/@/components/ReIcon/src/Icon.vue";
|
|
||||||
import { deviceDetection } from "/@/utils/deviceDetection";
|
import { deviceDetection } from "/@/utils/deviceDetection";
|
||||||
import globalization from "/@/assets/svg/globalization.svg";
|
|
||||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
||||||
|
import globalization from "/@/assets/svg/globalization.svg?component";
|
||||||
|
|
||||||
const instance =
|
const instance =
|
||||||
getCurrentInstance().appContext.config.globalProperties.$storage;
|
getCurrentInstance().appContext.config.globalProperties.$storage;
|
||||||
@@ -27,13 +27,21 @@ const title =
|
|||||||
getCurrentInstance().appContext.config.globalProperties.$config?.Title;
|
getCurrentInstance().appContext.config.globalProperties.$config?.Title;
|
||||||
|
|
||||||
const menuRef = templateRef<ElRef | null>("menu", null);
|
const menuRef = templateRef<ElRef | null>("menu", null);
|
||||||
const routeStore = usePermissionStoreHook();
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const routers = useRouter().options.routes;
|
const routers = useRouter().options.routes;
|
||||||
let usename = storageSession.getItem("info")?.username;
|
let usename = storageSession.getItem("info")?.username;
|
||||||
const { locale, t } = useI18n();
|
const { locale, t } = useI18n();
|
||||||
|
|
||||||
|
const getDropdownItemStyle = computed(() => {
|
||||||
|
return t => {
|
||||||
|
return {
|
||||||
|
background: locale.value === t ? "#1b2a47" : "",
|
||||||
|
color: locale.value === t ? "#f4f4f5" : "#000"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => locale.value,
|
() => locale.value,
|
||||||
() => {
|
() => {
|
||||||
@@ -82,7 +90,7 @@ const menuSelect = (indexPath: string): void => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
findCurrentRoute(algorithm.increaseIndexes(routers));
|
findCurrentRoute(routers);
|
||||||
};
|
};
|
||||||
|
|
||||||
function backHome() {
|
function backHome() {
|
||||||
@@ -118,7 +126,11 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<div class="horizontal-header">
|
<div class="horizontal-header">
|
||||||
<div class="horizontal-header-left" @click="backHome">
|
<div class="horizontal-header-left" @click="backHome">
|
||||||
<Icon svg :width="35" :height="35" content="team-iconlogo" />
|
<FontIcon
|
||||||
|
icon="team-iconlogo"
|
||||||
|
svg
|
||||||
|
style="width: 35px; height: 35px"
|
||||||
|
></FontIcon>
|
||||||
<h4>{{ title }}</h4>
|
<h4>{{ title }}</h4>
|
||||||
</div>
|
</div>
|
||||||
<el-menu
|
<el-menu
|
||||||
@@ -131,38 +143,34 @@ onMounted(() => {
|
|||||||
@select="menuSelect"
|
@select="menuSelect"
|
||||||
>
|
>
|
||||||
<sidebar-item
|
<sidebar-item
|
||||||
v-for="route in routeStore.wholeRoutes"
|
v-for="route in usePermissionStoreHook().wholeMenus"
|
||||||
:key="route.path"
|
:key="route.path"
|
||||||
:item="route"
|
:item="route"
|
||||||
:base-path="route.path"
|
:base-path="route.path"
|
||||||
/>
|
/>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
<div class="horizontal-header-right">
|
<div class="horizontal-header-right">
|
||||||
|
<!-- 通知 -->
|
||||||
|
<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 />
|
<globalization />
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu class="translation">
|
<el-dropdown-menu class="translation">
|
||||||
<el-dropdown-item
|
<el-dropdown-item
|
||||||
:style="{
|
:style="getDropdownItemStyle('zh')"
|
||||||
background: locale === 'zh' ? '#1b2a47' : '',
|
|
||||||
color: locale === 'zh' ? '#f4f4f5' : '#000'
|
|
||||||
}"
|
|
||||||
@click="translationCh"
|
@click="translationCh"
|
||||||
><el-icon class="check-zh" v-show="locale === 'zh'"
|
><el-icon class="check-zh" v-show="locale === 'zh'"
|
||||||
><check /></el-icon
|
><IconifyIconOffline icon="check" /></el-icon
|
||||||
>简体中文</el-dropdown-item
|
>简体中文</el-dropdown-item
|
||||||
>
|
>
|
||||||
<el-dropdown-item
|
<el-dropdown-item
|
||||||
:style="{
|
:style="getDropdownItemStyle('en')"
|
||||||
background: locale === 'en' ? '#1b2a47' : '',
|
|
||||||
color: locale === 'en' ? '#f4f4f5' : '#000'
|
|
||||||
}"
|
|
||||||
@click="translationEn"
|
@click="translationEn"
|
||||||
><el-icon class="check-en" v-show="locale === 'en'"
|
><el-icon class="check-en" v-show="locale === 'en'"
|
||||||
><check /></el-icon
|
><IconifyIconOffline icon="check" /></el-icon
|
||||||
>English</el-dropdown-item
|
>English</el-dropdown-item
|
||||||
>
|
>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
@@ -171,25 +179,27 @@ onMounted(() => {
|
|||||||
<!-- 退出登陆 -->
|
<!-- 退出登陆 -->
|
||||||
<el-dropdown trigger="click">
|
<el-dropdown trigger="click">
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
<img
|
<img :src="avatars" />
|
||||||
src="https://avatars.githubusercontent.com/u/44761321?s=400&u=30907819abd29bb3779bc247910873e7c7f7c12f&v=4"
|
|
||||||
/>
|
|
||||||
<p>{{ usename }}</p>
|
<p>{{ usename }}</p>
|
||||||
</span>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu class="logout">
|
<el-dropdown-menu class="logout">
|
||||||
<el-dropdown-item icon="el-icon-switch-button" @click="logout">{{
|
<el-dropdown-item @click="logout">
|
||||||
$t("message.hsLoginOut")
|
<IconifyIconOffline
|
||||||
}}</el-dropdown-item>
|
icon="logout-circle-r-line"
|
||||||
|
style="margin: 5px"
|
||||||
|
/>
|
||||||
|
{{ $t("buttons.hsLoginOut") }}</el-dropdown-item
|
||||||
|
>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
<el-icon
|
<el-icon
|
||||||
class="el-icon-setting"
|
class="el-icon-setting"
|
||||||
:title="$t('message.hssystemSet')"
|
:title="$t('buttons.hssystemSet')"
|
||||||
@click="onPanel"
|
@click="onPanel"
|
||||||
>
|
>
|
||||||
<Setting />
|
<IconifyIconOffline icon="setting" />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -198,7 +208,7 @@ onMounted(() => {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.translation {
|
.translation {
|
||||||
.el-dropdown-menu__item {
|
.el-dropdown-menu__item {
|
||||||
padding: 0 40px !important;
|
padding: 5px 40px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-dropdown-menu__item:focus,
|
.el-dropdown-menu__item:focus,
|
||||||
@@ -210,19 +220,21 @@ onMounted(() => {
|
|||||||
.check-zh {
|
.check-zh {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 20px;
|
left: 20px;
|
||||||
top: 13px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.check-en {
|
.check-en {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 13px;
|
|
||||||
left: 20px;
|
left: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.logout {
|
.logout {
|
||||||
|
max-width: 120px;
|
||||||
|
|
||||||
.el-dropdown-menu__item {
|
.el-dropdown-menu__item {
|
||||||
padding: 0 18px !important;
|
min-width: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-dropdown-menu__item:focus,
|
.el-dropdown-menu__item:focus,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getCurrentInstance } from "vue";
|
import { getCurrentInstance } from "vue";
|
||||||
import Icon from "/@/components/ReIcon/src/Icon.vue";
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
collapse: Boolean
|
collapse: Boolean
|
||||||
});
|
});
|
||||||
@@ -19,7 +18,11 @@ const title =
|
|||||||
class="sidebar-logo-link"
|
class="sidebar-logo-link"
|
||||||
to="/"
|
to="/"
|
||||||
>
|
>
|
||||||
<Icon svg :width="35" :height="35" content="team-iconlogo" />
|
<FontIcon
|
||||||
|
icon="team-iconlogo"
|
||||||
|
svg
|
||||||
|
style="width: 35px; height: 35px"
|
||||||
|
></FontIcon>
|
||||||
<span class="sidebar-title">{{ title }}</span>
|
<span class="sidebar-title">{{ title }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link
|
<router-link
|
||||||
@@ -29,7 +32,11 @@ const title =
|
|||||||
class="sidebar-logo-link"
|
class="sidebar-logo-link"
|
||||||
to="/"
|
to="/"
|
||||||
>
|
>
|
||||||
<Icon svg :width="35" :height="35" content="team-iconlogo" />
|
<FontIcon
|
||||||
|
icon="team-iconlogo"
|
||||||
|
svg
|
||||||
|
style="width: 35px; height: 35px"
|
||||||
|
></FontIcon>
|
||||||
<span class="sidebar-title">{{ title }}</span>
|
<span class="sidebar-title">{{ title }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</transition>
|
</transition>
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
ref,
|
||||||
|
PropType,
|
||||||
|
nextTick,
|
||||||
|
computed,
|
||||||
|
CSSProperties,
|
||||||
|
getCurrentInstance
|
||||||
|
} from "vue";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { PropType, ref, nextTick, getCurrentInstance } from "vue";
|
|
||||||
import { childrenType } from "../../types";
|
import { childrenType } from "../../types";
|
||||||
|
import { transformI18n } from "/@/plugins/i18n";
|
||||||
import { useAppStoreHook } from "/@/store/modules/app";
|
import { useAppStoreHook } from "/@/store/modules/app";
|
||||||
import Icon from "/@/components/ReIcon/src/Icon.vue";
|
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
|
||||||
|
|
||||||
const instance = getCurrentInstance().appContext.app.config.globalProperties;
|
const instance = getCurrentInstance().appContext.app.config.globalProperties;
|
||||||
const menuMode = instance.$storage.layout?.layout === "vertical";
|
const menuMode = instance.$storage.layout?.layout === "vertical";
|
||||||
@@ -23,6 +31,61 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getExtraIconStyle = computed((): CSSProperties => {
|
||||||
|
if (useAppStoreHook().getSidebarStatus) {
|
||||||
|
return {
|
||||||
|
position: "absolute",
|
||||||
|
right: "10px"
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
position: "static"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
const onlyOneChild: childrenType = ref(null);
|
||||||
// 存放菜单是否存在showTooltip属性标识
|
// 存放菜单是否存在showTooltip属性标识
|
||||||
const hoverMenuMap = new WeakMap();
|
const hoverMenuMap = new WeakMap();
|
||||||
@@ -68,8 +131,13 @@ function hasOneShowingChild(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resolvePath(routePath) {
|
function resolvePath(routePath) {
|
||||||
|
const httpReg = /^http(s?):\/\//;
|
||||||
|
if (httpReg.test(routePath)) {
|
||||||
|
return props.basePath + "/" + routePath;
|
||||||
|
} else {
|
||||||
return path.resolve(props.basePath, routePath);
|
return path.resolve(props.basePath, routePath);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -82,50 +150,52 @@ function resolvePath(routePath) {
|
|||||||
<el-menu-item
|
<el-menu-item
|
||||||
:index="resolvePath(onlyOneChild.path)"
|
:index="resolvePath(onlyOneChild.path)"
|
||||||
:class="{ 'submenu-title-noDropdown': !isNest }"
|
:class="{ 'submenu-title-noDropdown': !isNest }"
|
||||||
style="display: flex; align-items: center"
|
:style="getNoDropdownStyle"
|
||||||
>
|
>
|
||||||
<el-icon v-show="props.item.meta.icon">
|
<el-icon v-show="props.item.meta.icon">
|
||||||
<component
|
<component
|
||||||
:is="
|
:is="
|
||||||
onlyOneChild.meta.icon || (props.item.meta && props.item.meta.icon)
|
useRenderIcon(
|
||||||
|
onlyOneChild.meta.icon ||
|
||||||
|
(props.item.meta && props.item.meta.icon)
|
||||||
|
)
|
||||||
"
|
"
|
||||||
></component>
|
></component>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<template #title>
|
<template #title>
|
||||||
<div
|
<div :style="getDivStyle">
|
||||||
:style="{
|
<span v-if="!menuMode">{{
|
||||||
width: pureApp.sidebar.opened ? '' : '100%',
|
transformI18n(onlyOneChild.meta.title, onlyOneChild.meta.i18n)
|
||||||
display: 'flex',
|
}}</span>
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
overflow: 'hidden'
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<span v-if="!menuMode">{{ $t(onlyOneChild.meta.title) }}</span>
|
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-else
|
v-else
|
||||||
placement="top"
|
placement="top"
|
||||||
:offset="-10"
|
:offset="-10"
|
||||||
:disabled="!onlyOneChild.showTooltip"
|
:disabled="!onlyOneChild.showTooltip"
|
||||||
>
|
>
|
||||||
<template #content> {{ $t(onlyOneChild.meta.title) }} </template>
|
<template #content>
|
||||||
|
{{
|
||||||
|
transformI18n(onlyOneChild.meta.title, onlyOneChild.meta.i18n)
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
<span
|
<span
|
||||||
ref="menuTextRef"
|
ref="menuTextRef"
|
||||||
:style="{
|
:style="getMenuTextStyle"
|
||||||
width: pureApp.sidebar.opened ? '125px' : '',
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis'
|
|
||||||
}"
|
|
||||||
@mouseover="hoverMenu(onlyOneChild)"
|
@mouseover="hoverMenu(onlyOneChild)"
|
||||||
>
|
>
|
||||||
{{ $t(onlyOneChild.meta.title) }}
|
{{
|
||||||
|
transformI18n(onlyOneChild.meta.title, onlyOneChild.meta.i18n)
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<Icon
|
<FontIcon
|
||||||
v-if="onlyOneChild.meta.extraIcon"
|
v-if="onlyOneChild.meta.extraIcon"
|
||||||
|
width="30px"
|
||||||
|
height="30px"
|
||||||
|
:style="getExtraIconStyle"
|
||||||
|
:icon="onlyOneChild.meta.extraIcon.name"
|
||||||
:svg="onlyOneChild.meta.extraIcon.svg ? true : false"
|
:svg="onlyOneChild.meta.extraIcon.svg ? true : false"
|
||||||
:content="`${onlyOneChild.meta.extraIcon.name}`"
|
></FontIcon>
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
@@ -139,36 +209,40 @@ function resolvePath(routePath) {
|
|||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<el-icon v-show="props.item.meta.icon" :class="props.item.meta.icon">
|
<el-icon v-show="props.item.meta.icon" :class="props.item.meta.icon">
|
||||||
<component :is="props.item.meta && props.item.meta.icon"></component>
|
<component
|
||||||
|
:is="useRenderIcon(props.item.meta && props.item.meta.icon)"
|
||||||
|
></component>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<span v-if="!menuMode">{{ $t(props.item.meta.title) }}</span>
|
<span v-if="!menuMode">{{
|
||||||
|
transformI18n(props.item.meta.title, props.item.meta.i18n)
|
||||||
|
}}</span>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-else
|
v-else
|
||||||
placement="top"
|
placement="top"
|
||||||
:offset="-10"
|
:offset="-10"
|
||||||
:disabled="!pureApp.sidebar.opened || !props.item.showTooltip"
|
:disabled="!pureApp.sidebar.opened || !props.item.showTooltip"
|
||||||
>
|
>
|
||||||
<template #content> {{ $t(props.item.meta.title) }} </template>
|
<template #content>
|
||||||
|
{{ transformI18n(props.item.meta.title, props.item.meta.i18n) }}
|
||||||
|
</template>
|
||||||
<div
|
<div
|
||||||
ref="menuTextRef"
|
ref="menuTextRef"
|
||||||
:style="{
|
:style="getSubTextStyle"
|
||||||
width: pureApp.sidebar.opened ? '125px' : '',
|
|
||||||
display: 'inline-block',
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis'
|
|
||||||
}"
|
|
||||||
@mouseover="hoverMenu(props.item)"
|
@mouseover="hoverMenu(props.item)"
|
||||||
>
|
>
|
||||||
<span style="overflow: hidden; text-overflow: ellipsis">
|
<span :style="getSpanStyle">
|
||||||
{{ $t(props.item.meta.title) }}
|
{{ transformI18n(props.item.meta.title, props.item.meta.i18n) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<Icon
|
<FontIcon
|
||||||
v-if="props.item.meta.extraIcon"
|
v-if="props.item.meta.extraIcon"
|
||||||
|
width="30px"
|
||||||
|
height="30px"
|
||||||
|
style="position: absolute; right: 10px"
|
||||||
|
:icon="props.item.meta.extraIcon.name"
|
||||||
:svg="props.item.meta.extraIcon.svg ? true : false"
|
:svg="props.item.meta.extraIcon.svg ? true : false"
|
||||||
:content="`${props.item.meta.extraIcon.name}`"
|
></FontIcon>
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<sidebar-item
|
<sidebar-item
|
||||||
v-for="child in props.item.children"
|
v-for="child in props.item.children"
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import Logo from "./logo.vue";
|
import Logo from "./logo.vue";
|
||||||
import { emitter } from "/@/utils/mitt";
|
import { emitter } from "/@/utils/mitt";
|
||||||
import SidebarItem from "./sidebarItem.vue";
|
import SidebarItem from "./sidebarItem.vue";
|
||||||
import { algorithm } from "/@/utils/algorithm";
|
|
||||||
import { storageLocal } from "/@/utils/storage";
|
import { storageLocal } from "/@/utils/storage";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { computed, ref, onBeforeMount } from "vue";
|
import { computed, ref, onBeforeMount } from "vue";
|
||||||
@@ -12,8 +11,9 @@ import { usePermissionStoreHook } from "/@/store/modules/permission";
|
|||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const pureApp = useAppStoreHook();
|
const pureApp = useAppStoreHook();
|
||||||
const router = useRouter().options.routes;
|
const router = useRouter().options.routes;
|
||||||
const routeStore = usePermissionStoreHook();
|
const showLogo = ref(
|
||||||
const showLogo = ref(storageLocal.getItem("logoVal") || "1");
|
storageLocal.getItem("responsive-configure")?.showLogo ?? true
|
||||||
|
);
|
||||||
const isCollapse = computed(() => {
|
const isCollapse = computed(() => {
|
||||||
return !pureApp.getSidebarStatus;
|
return !pureApp.getSidebarStatus;
|
||||||
});
|
});
|
||||||
@@ -47,7 +47,7 @@ const menuSelect = (indexPath: string): void => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
findCurrentRoute(algorithm.increaseIndexes(router));
|
findCurrentRoute(router);
|
||||||
};
|
};
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
@@ -59,7 +59,7 @@ onBeforeMount(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="['sidebar-container', showLogo ? 'has-logo' : '']">
|
<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-scrollbar wrap-class="scrollbar-wrapper">
|
||||||
<el-menu
|
<el-menu
|
||||||
:default-active="activeMenu"
|
:default-active="activeMenu"
|
||||||
@@ -72,7 +72,7 @@ onBeforeMount(() => {
|
|||||||
@select="menuSelect"
|
@select="menuSelect"
|
||||||
>
|
>
|
||||||
<sidebar-item
|
<sidebar-item
|
||||||
v-for="route in routeStore.wholeRoutes"
|
v-for="route in usePermissionStoreHook().wholeMenus"
|
||||||
:key="route.path"
|
:key="route.path"
|
||||||
:item="route"
|
:item="route"
|
||||||
class="outer-most"
|
class="outer-most"
|
||||||
|
|||||||
346
src/layout/components/tag/index.scss
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
@keyframes scheduleInWidth {
|
||||||
|
from {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scheduleOutWidth {
|
||||||
|
from {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes rotate {
|
||||||
|
from {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-moz-keyframes rotate {
|
||||||
|
from {
|
||||||
|
-moz-transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
-moz-transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-o-keyframes rotate {
|
||||||
|
from {
|
||||||
|
-o-transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
-o-transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes close {
|
||||||
|
from {
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags-view {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
background: #fff;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 0 1px #888;
|
||||||
|
|
||||||
|
.scroll-item {
|
||||||
|
border-radius: 3px 3px 0 0;
|
||||||
|
padding: 0 6px 0 6px;
|
||||||
|
box-shadow: 0 0 1px #888;
|
||||||
|
position: relative;
|
||||||
|
margin-right: 4px;
|
||||||
|
height: 28px;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 28px;
|
||||||
|
transition: all 0.4s;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.el-icon-close {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #1890ff;
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
transition: font-size 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-radius: 50%;
|
||||||
|
color: #fff;
|
||||||
|
background: #b4bccc;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-closable:not(:first-child) {
|
||||||
|
&:hover {
|
||||||
|
padding-right: 18px;
|
||||||
|
|
||||||
|
&:not(.is-active) {
|
||||||
|
.el-icon-close {
|
||||||
|
animation: close 200ms ease-in forwards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #666;
|
||||||
|
padding: 0 4px 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-container {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 5px 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
list-style: none;
|
||||||
|
overflow: visible;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: transform 0.5s ease-in-out;
|
||||||
|
|
||||||
|
.scroll-item {
|
||||||
|
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||||
|
|
||||||
|
&:nth-child(1) {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-button {
|
||||||
|
display: flex;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 38px;
|
||||||
|
border-right: 1px solid #ccc;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右键菜单 */
|
||||||
|
.contextmenu {
|
||||||
|
margin: 0;
|
||||||
|
background: #fff;
|
||||||
|
position: absolute;
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 5px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #000000d9;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 13px;
|
||||||
|
white-space: nowrap;
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
|
||||||
|
|
||||||
|
li {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 7px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
display: block;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dropdown-menu {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
display: block;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dropdown-menu__item:not(.is-disabled):hover {
|
||||||
|
color: #606266;
|
||||||
|
background: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dropdown-menu__item) i {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dropdown-menu__item--divided::before {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dropdown-menu__item.is-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-item.is-active {
|
||||||
|
background-color: #eaf4fe;
|
||||||
|
position: relative;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
padding-right: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon-close {
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-left,
|
||||||
|
.arrow-right {
|
||||||
|
width: 40px;
|
||||||
|
height: 38px;
|
||||||
|
color: #00000073;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-left {
|
||||||
|
box-shadow: 5px 0 5px -6px #ccc;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: w-resize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-right {
|
||||||
|
box-shadow: -5px 0 5px -6px #ccc;
|
||||||
|
border-right: 1px solid #ccc;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: e-resize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片模式下鼠标移入显示蓝色边框 */
|
||||||
|
.card-in {
|
||||||
|
color: #1890ff;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片模式下鼠标移出隐藏蓝色边框 */
|
||||||
|
.card-out {
|
||||||
|
border: none;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 灵动模式 */
|
||||||
|
.schedule-active {
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 灵动模式下鼠标移入显示蓝色进度条 */
|
||||||
|
.schedule-in {
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: #1890ff;
|
||||||
|
animation: scheduleInWidth 400ms ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 灵动模式下鼠标移出隐藏蓝色进度条 */
|
||||||
|
.schedule-out {
|
||||||
|
width: 0;
|
||||||
|
height: 2px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: #1890ff;
|
||||||
|
animation: scheduleOutWidth 400ms ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 刷新按钮动画效果 */
|
||||||
|
.refresh-button {
|
||||||
|
-webkit-animation: rotate 600ms linear infinite;
|
||||||
|
-moz-animation: rotate 600ms linear infinite;
|
||||||
|
-o-animation: rotate 600ms linear infinite;
|
||||||
|
animation: rotate 600ms linear infinite;
|
||||||
|
}
|
||||||
@@ -1,13 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
h,
|
h,
|
||||||
ref,
|
|
||||||
unref,
|
|
||||||
reactive,
|
reactive,
|
||||||
computed,
|
computed,
|
||||||
onMounted,
|
onMounted,
|
||||||
watchEffect,
|
|
||||||
onBeforeMount,
|
|
||||||
defineComponent,
|
defineComponent,
|
||||||
getCurrentInstance
|
getCurrentInstance
|
||||||
} from "vue";
|
} from "vue";
|
||||||
@@ -15,13 +11,15 @@ import { setType } from "./types";
|
|||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { routerArrays } from "./types";
|
import { routerArrays } from "./types";
|
||||||
import { emitter } from "/@/utils/mitt";
|
import { emitter } from "/@/utils/mitt";
|
||||||
import { useEventListener } from "@vueuse/core";
|
|
||||||
import backTop from "/@/assets/svg/back_top.svg";
|
|
||||||
import { useAppStoreHook } from "/@/store/modules/app";
|
import { useAppStoreHook } from "/@/store/modules/app";
|
||||||
import fullScreen from "/@/assets/svg/full_screen.svg";
|
import { deviceDetection } from "/@/utils/deviceDetection";
|
||||||
import exitScreen from "/@/assets/svg/exit_screen.svg";
|
import { useMultiTagsStore } from "/@/store/modules/multiTags";
|
||||||
import { useSettingStoreHook } from "/@/store/modules/settings";
|
import { useSettingStoreHook } from "/@/store/modules/settings";
|
||||||
|
|
||||||
|
import backTop from "/@/assets/svg/back_top.svg?component";
|
||||||
|
import fullScreen from "/@/assets/svg/full_screen.svg?component";
|
||||||
|
import exitScreen from "/@/assets/svg/exit_screen.svg?component";
|
||||||
|
|
||||||
import navbar from "./components/navbar.vue";
|
import navbar from "./components/navbar.vue";
|
||||||
import tag from "./components/tag/index.vue";
|
import tag from "./components/tag/index.vue";
|
||||||
import appMain from "./components/appMain.vue";
|
import appMain from "./components/appMain.vue";
|
||||||
@@ -29,19 +27,19 @@ import setting from "./components/setting/index.vue";
|
|||||||
import Vertical from "./components/sidebar/vertical.vue";
|
import Vertical from "./components/sidebar/vertical.vue";
|
||||||
import Horizontal from "./components/sidebar/horizontal.vue";
|
import Horizontal from "./components/sidebar/horizontal.vue";
|
||||||
|
|
||||||
const instance = getCurrentInstance().appContext.app.config.globalProperties;
|
const isMobile = deviceDetection();
|
||||||
const hiddenSideBar = ref(instance.$config?.HiddenSideBar);
|
|
||||||
const pureSetting = useSettingStoreHook();
|
const pureSetting = useSettingStoreHook();
|
||||||
|
const instance = getCurrentInstance().appContext.app.config.globalProperties;
|
||||||
|
|
||||||
// 清空缓存后从serverConfig.json读取默认配置并赋值到storage中
|
// 清空缓存后从serverConfig.json读取默认配置并赋值到storage中
|
||||||
const layout = computed(() => {
|
const layout = computed(() => {
|
||||||
// 路由
|
// 路由
|
||||||
if (
|
if (
|
||||||
!instance.$storage.routesInStorage ||
|
useMultiTagsStore().multiTagsCache &&
|
||||||
instance.$storage.routesInStorage.length === 0
|
(!instance.$storage.tags || instance.$storage.tags.length === 0)
|
||||||
) {
|
) {
|
||||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||||
instance.$storage.routesInStorage = routerArrays;
|
instance.$storage.tags = routerArrays;
|
||||||
}
|
}
|
||||||
// 国际化
|
// 国际化
|
||||||
if (!instance.$storage.locale) {
|
if (!instance.$storage.locale) {
|
||||||
@@ -54,16 +52,22 @@ const layout = computed(() => {
|
|||||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||||
instance.$storage.layout = {
|
instance.$storage.layout = {
|
||||||
layout: instance.$config?.Layout ?? "vertical",
|
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
|
// eslint-disable-next-line
|
||||||
instance.$storage.sets = {
|
instance.$storage.configure = {
|
||||||
grey: instance.$config?.Grey ?? false,
|
grey: instance.$config?.Grey ?? false,
|
||||||
weak: instance.$config?.Weak ?? false,
|
weak: instance.$config?.Weak ?? false,
|
||||||
hideTabs: instance.$config?.HideTabs ?? false
|
hideTabs: instance.$config?.HideTabs ?? false,
|
||||||
|
showLogo: instance.$config?.ShowLogo ?? true,
|
||||||
|
showModel: instance.$config?.ShowModel ?? "smart",
|
||||||
|
multiTagsCache: instance.$config?.MultiTagsCache ?? false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return instance.$storage?.layout.layout;
|
return instance.$storage?.layout.layout;
|
||||||
@@ -92,66 +96,66 @@ const set: setType = reactive({
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
hideTabs: computed(() => {
|
hideTabs: computed(() => {
|
||||||
return instance.$storage?.sets.hideTabs;
|
return instance.$storage?.configure.hideTabs;
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleClickOutside = (params: boolean) => {
|
|
||||||
useAppStoreHook().closeSideBar({ withoutAnimation: params });
|
|
||||||
};
|
|
||||||
|
|
||||||
function setTheme(layoutModel: string) {
|
function setTheme(layoutModel: string) {
|
||||||
window.document.body.setAttribute("layout", layoutModel);
|
window.document.body.setAttribute("layout", layoutModel);
|
||||||
instance.$storage.layout = {
|
instance.$storage.layout = {
|
||||||
layout: `${layoutModel}`,
|
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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggle(device: string, bool: boolean) {
|
||||||
|
useAppStoreHook().toggleDevice(device);
|
||||||
|
useAppStoreHook().toggleSideBar(bool, "resize");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否可自动关闭菜单栏
|
||||||
|
let isAutoCloseSidebar = true;
|
||||||
|
|
||||||
// 监听容器
|
// 监听容器
|
||||||
emitter.on("resize", ({ detail }) => {
|
emitter.on("resize", ({ detail }) => {
|
||||||
|
if (isMobile) return;
|
||||||
let { width } = detail;
|
let { width } = detail;
|
||||||
width <= 670 ? setTheme("vertical") : setTheme(useAppStoreHook().layout);
|
width <= 670 ? setTheme("vertical") : setTheme(useAppStoreHook().layout);
|
||||||
});
|
/** width app-wrapper类容器宽度
|
||||||
|
* 0 < width <= 760 隐藏侧边栏
|
||||||
watchEffect(() => {
|
* 760 < width <= 990 折叠侧边栏
|
||||||
if (set.device === "mobile" && !set.sidebar.opened) {
|
* width > 990 展开侧边栏
|
||||||
handleClickOutside(false);
|
*/
|
||||||
|
if (width > 0 && width <= 760) {
|
||||||
|
toggle("mobile", false);
|
||||||
|
isAutoCloseSidebar = true;
|
||||||
|
} else if (width > 760 && width <= 990) {
|
||||||
|
if (isAutoCloseSidebar) {
|
||||||
|
toggle("desktop", false);
|
||||||
|
isAutoCloseSidebar = false;
|
||||||
|
}
|
||||||
|
} else if (width > 990) {
|
||||||
|
if (!set.sidebar.isClickHamburger) {
|
||||||
|
toggle("desktop", true);
|
||||||
|
isAutoCloseSidebar = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const $_isMobile = () => {
|
|
||||||
const rect = document.body.getBoundingClientRect();
|
|
||||||
return rect.width - 1 < 992;
|
|
||||||
};
|
|
||||||
|
|
||||||
const $_resizeHandler = () => {
|
|
||||||
if (!document.hidden) {
|
|
||||||
const isMobile = $_isMobile();
|
|
||||||
useAppStoreHook().toggleDevice(isMobile ? "mobile" : "desktop");
|
|
||||||
if (isMobile) {
|
|
||||||
handleClickOutside(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function onFullScreen() {
|
|
||||||
unref(hiddenSideBar)
|
|
||||||
? (hiddenSideBar.value = false)
|
|
||||||
: (hiddenSideBar.value = true);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const isMobile = $_isMobile();
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
useAppStoreHook().toggleDevice("mobile");
|
toggle("mobile", false);
|
||||||
handleClickOutside(true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeMount(() => {
|
function onFullScreen() {
|
||||||
useEventListener("resize", $_resizeHandler);
|
pureSetting.hiddenSideBar
|
||||||
});
|
? pureSetting.changeSetting({ key: "hiddenSideBar", value: false })
|
||||||
|
: pureSetting.changeSetting({ key: "hiddenSideBar", value: true });
|
||||||
|
}
|
||||||
|
|
||||||
const layoutHeader = defineComponent({
|
const layoutHeader = defineComponent({
|
||||||
render() {
|
render() {
|
||||||
@@ -167,10 +171,10 @@ const layoutHeader = defineComponent({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
default: () => [
|
default: () => [
|
||||||
!hiddenSideBar.value && layout.value.includes("vertical")
|
!pureSetting.hiddenSideBar && layout.value.includes("vertical")
|
||||||
? h(navbar)
|
? h(navbar)
|
||||||
: h("div"),
|
: h("div"),
|
||||||
!hiddenSideBar.value && layout.value.includes("horizontal")
|
!pureSetting.hiddenSideBar && layout.value.includes("horizontal")
|
||||||
? h(Horizontal)
|
? h(Horizontal)
|
||||||
: h("div"),
|
: h("div"),
|
||||||
h(
|
h(
|
||||||
@@ -183,7 +187,7 @@ const layoutHeader = defineComponent({
|
|||||||
{ onClick: onFullScreen },
|
{ onClick: onFullScreen },
|
||||||
{
|
{
|
||||||
default: () => [
|
default: () => [
|
||||||
!hiddenSideBar.value ? h(fullScreen) : h(exitScreen)
|
!pureSetting.hiddenSideBar ? h(fullScreen) : h(exitScreen)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -205,11 +209,18 @@ const layoutHeader = defineComponent({
|
|||||||
set.sidebar.opened &&
|
set.sidebar.opened &&
|
||||||
layout.includes('vertical')
|
layout.includes('vertical')
|
||||||
"
|
"
|
||||||
class="drawer-bg"
|
class="app-mask"
|
||||||
@click="handleClickOutside(false)"
|
@click="useAppStoreHook().toggleSideBar()"
|
||||||
/>
|
/>
|
||||||
<Vertical v-show="!hiddenSideBar && layout.includes('vertical')" />
|
<Vertical
|
||||||
<div :class="['main-container', hiddenSideBar ? 'main-hidden' : '']">
|
v-show="!pureSetting.hiddenSideBar && layout.includes('vertical')"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'main-container',
|
||||||
|
pureSetting.hiddenSideBar ? 'main-hidden' : ''
|
||||||
|
]"
|
||||||
|
>
|
||||||
<div v-if="set.fixedHeader">
|
<div v-if="set.fixedHeader">
|
||||||
<layout-header />
|
<layout-header />
|
||||||
<!-- 主体内容 -->
|
<!-- 主体内容 -->
|
||||||
@@ -257,7 +268,7 @@ const layoutHeader = defineComponent({
|
|||||||
margin-left: 0 !important;
|
margin-left: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer-bg {
|
.app-mask {
|
||||||
background: #000;
|
background: #000;
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
15
src/layout/routerView/parent.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<router-view>
|
||||||
|
<template #default="{ Component, route }">
|
||||||
|
<transition appear name="fade-transform" mode="out-in">
|
||||||
|
<component :is="Component" :key="route.fullPath" />
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
</router-view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "layoutParentView"
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
// 极光绿
|
/* 酸橙绿 */
|
||||||
|
|
||||||
$subMenuActiveText: #fff;
|
$subMenuActiveText: #fff;
|
||||||
$menuBg: #0b1e15;
|
$menuBg: #0b1e15;
|
||||||
$menuHover: #60ac80;
|
$menuHover: #60ac80;
|
||||||
|
|||||||
@@ -1,24 +1,29 @@
|
|||||||
/**
|
/**
|
||||||
|
* 道奇蓝(默认)
|
||||||
* 此scss变量文件作为multipleScopeVars去编译时,会自动移除!default以达到变量提升
|
* 此scss变量文件作为multipleScopeVars去编译时,会自动移除!default以达到变量提升
|
||||||
* 同时此scss变量文件作为默认主题变量文件,被其他.scss通过 @import 时,必需 !default
|
* 同时此scss变量文件作为默认主题变量文件,被其他.scss通过 @import 时,必需 !default
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 暗雅(默认)
|
/* 菜单选中后字体样式 */
|
||||||
|
|
||||||
// 菜单选中后字体样式
|
|
||||||
$subMenuActiveText: #fff !default;
|
$subMenuActiveText: #fff !default;
|
||||||
//菜单背景
|
|
||||||
|
/* 菜单背景 */
|
||||||
$menuBg: #001529 !default;
|
$menuBg: #001529 !default;
|
||||||
// 鼠标覆盖到菜单时的背景
|
|
||||||
|
/* 鼠标覆盖到菜单时的背景 */
|
||||||
$menuHover: #4091f7 !default;
|
$menuHover: #4091f7 !default;
|
||||||
// 子菜单背景
|
|
||||||
|
/* 子菜单背景 */
|
||||||
$subMenuBg: #0f0303 !default;
|
$subMenuBg: #0f0303 !default;
|
||||||
// 有无子集的激活菜单背景
|
|
||||||
|
/* 有无子集的激活菜单背景 */
|
||||||
$subMenuActiveBg: #4091f7 !default;
|
$subMenuActiveBg: #4091f7 !default;
|
||||||
$navTextColor: #fff !default;
|
$navTextColor: #fff !default;
|
||||||
$menuText: rgba(254, 254, 254, 0.65) !default;
|
$menuText: rgba(254, 254, 254, 0.65) !default;
|
||||||
// logo背景颜色
|
|
||||||
|
/* logo背景颜色 */
|
||||||
$sidebarLogo: #002140 !default;
|
$sidebarLogo: #002140 !default;
|
||||||
// 鼠标覆盖到菜单时的字体颜色
|
|
||||||
|
/* 鼠标覆盖到菜单时的字体颜色 */
|
||||||
$menuTitleHover: #fff !default;
|
$menuTitleHover: #fff !default;
|
||||||
$menuActiveBefore: #4091f7 !default;
|
$menuActiveBefore: #4091f7 !default;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// 薄暮
|
/* 猩红色 */
|
||||||
|
|
||||||
$subMenuActiveText: #fff;
|
$subMenuActiveText: #fff;
|
||||||
$menuBg: #2a0608;
|
$menuBg: #2a0608;
|
||||||
$menuHover: #e13c39;
|
$menuHover: #e13c39;
|
||||||
|
|||||||
80
src/layout/theme/element-plus.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/* 动态改变element-plus主题色 */
|
||||||
|
import rgbHex from "rgb-hex";
|
||||||
|
import { convert } 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(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();
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// 明亮
|
/* 亮白色 */
|
||||||
$subMenuActiveText: #409eff;
|
$subMenuActiveText: #409eff;
|
||||||
$menuBg: #fff;
|
$menuBg: #fff;
|
||||||
$menuHover: #e0ebf6;
|
$menuHover: #e0ebf6;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// 明青
|
/* 绿宝石 */
|
||||||
|
|
||||||
$subMenuActiveText: #fff;
|
$subMenuActiveText: #fff;
|
||||||
$menuBg: #032121;
|
$menuBg: #032121;
|
||||||
$menuHover: #59bfc1;
|
$menuHover: #59bfc1;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// 粉红
|
/* 深粉色 */
|
||||||
|
|
||||||
$subMenuActiveText: #fff;
|
$subMenuActiveText: #fff;
|
||||||
$menuBg: #28081a;
|
$menuBg: #28081a;
|
||||||
$menuHover: #d84493;
|
$menuHover: #d84493;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// 酱紫
|
/* 深紫罗兰色 */
|
||||||
|
|
||||||
$subMenuActiveText: #fff;
|
$subMenuActiveText: #fff;
|
||||||
$menuBg: #130824;
|
$menuBg: #130824;
|
||||||
$menuHover: #693ac9;
|
$menuHover: #693ac9;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// 火山
|
/* 橙红色 */
|
||||||
|
|
||||||
$subMenuActiveText: #fff;
|
$subMenuActiveText: #fff;
|
||||||
$menuBg: #2b0e05;
|
$menuBg: #2b0e05;
|
||||||
$menuHover: #e85f33;
|
$menuHover: #e85f33;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// 黄色
|
/* 金色 */
|
||||||
|
|
||||||
$subMenuActiveText: #d25f00;
|
$subMenuActiveText: #d25f00;
|
||||||
$menuBg: #2b2503;
|
$menuBg: #2b2503;
|
||||||
$menuHover: #f6da4d;
|
$menuHover: #f6da4d;
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
|
import { Component } from "vue";
|
||||||
export const routerArrays: Array<RouteConfigs> = [
|
export const routerArrays: Array<RouteConfigs> = [
|
||||||
{
|
{
|
||||||
path: "/welcome",
|
path: "/welcome",
|
||||||
parentPath: "/",
|
parentPath: "/",
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hshome",
|
title: "menus.hshome",
|
||||||
icon: "el-icon-s-home",
|
i18n: true,
|
||||||
showLink: true
|
icon: "home-filled"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -13,21 +14,25 @@ export const routerArrays: Array<RouteConfigs> = [
|
|||||||
export type RouteConfigs = {
|
export type RouteConfigs = {
|
||||||
path?: string;
|
path?: string;
|
||||||
parentPath?: string;
|
parentPath?: string;
|
||||||
|
query?: object;
|
||||||
meta?: {
|
meta?: {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
i18n?: boolean;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
showLink?: boolean;
|
showLink?: boolean;
|
||||||
savedPosition?: boolean;
|
savedPosition?: boolean;
|
||||||
|
authority?: Array<string>;
|
||||||
};
|
};
|
||||||
|
children?: RouteConfigs[];
|
||||||
name?: string;
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type relativeStorageType = {
|
export type multiTagsType = {
|
||||||
routesInStorage: Array<RouteConfigs>;
|
tags: Array<RouteConfigs>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type tagsViewsType = {
|
export type tagsViewsType = {
|
||||||
icon: string;
|
icon: Component;
|
||||||
text: string;
|
text: string;
|
||||||
divided: boolean;
|
divided: boolean;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
@@ -38,6 +43,7 @@ export interface setType {
|
|||||||
sidebar: {
|
sidebar: {
|
||||||
opened: boolean;
|
opened: boolean;
|
||||||
withoutAnimation: boolean;
|
withoutAnimation: boolean;
|
||||||
|
isClickHamburger: boolean;
|
||||||
};
|
};
|
||||||
device: string;
|
device: string;
|
||||||
fixedHeader: boolean;
|
fixedHeader: boolean;
|
||||||
@@ -58,6 +64,7 @@ export type childrenType = {
|
|||||||
meta?: {
|
meta?: {
|
||||||
icon?: string;
|
icon?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
i18n?: boolean;
|
||||||
extraIcon?: {
|
extraIcon?: {
|
||||||
svg?: boolean;
|
svg?: boolean;
|
||||||
name?: string;
|
name?: string;
|
||||||
@@ -70,3 +77,9 @@ export type themeColorsType = {
|
|||||||
rgb: string;
|
rgb: string;
|
||||||
themeColor: string;
|
themeColor: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface scrollbarDomType extends HTMLElement {
|
||||||
|
wrap?: {
|
||||||
|
offsetWidth: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
11
src/main.ts
@@ -10,6 +10,7 @@ import { useElementPlus } from "../src/plugins/element-plus";
|
|||||||
import { injectResponsiveStorage } from "/@/utils/storage/responsive";
|
import { injectResponsiveStorage } from "/@/utils/storage/responsive";
|
||||||
|
|
||||||
import "animate.css";
|
import "animate.css";
|
||||||
|
import "virtual:windi.css";
|
||||||
// 导入公共样式
|
// 导入公共样式
|
||||||
import "./style/index.scss";
|
import "./style/index.scss";
|
||||||
// 导入字体图标
|
// 导入字体图标
|
||||||
@@ -25,6 +26,16 @@ Object.keys(directives).forEach(key => {
|
|||||||
app.directive(key, (directives as { [key: string]: Directive })[key]);
|
app.directive(key, (directives as { [key: string]: Directive })[key]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 全局注册`@iconify/vue`图标库
|
||||||
|
import {
|
||||||
|
IconifyIconOffline,
|
||||||
|
IconifyIconOnline,
|
||||||
|
FontIcon
|
||||||
|
} from "./components/ReIcon";
|
||||||
|
app.component("IconifyIconOffline", IconifyIconOffline);
|
||||||
|
app.component("IconifyIconOnline", IconifyIconOnline);
|
||||||
|
app.component("FontIcon", FontIcon);
|
||||||
|
|
||||||
getServerConfig(app).then(async config => {
|
getServerConfig(app).then(async config => {
|
||||||
injectResponsiveStorage(app, config);
|
injectResponsiveStorage(app, config);
|
||||||
setupStore(app);
|
setupStore(app);
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import {
|
|||||||
ElInput,
|
ElInput,
|
||||||
ElForm,
|
ElForm,
|
||||||
ElFormItem,
|
ElFormItem,
|
||||||
ElLoading,
|
|
||||||
ElPopover,
|
ElPopover,
|
||||||
ElPopper,
|
ElPopper,
|
||||||
ElTooltip,
|
ElTooltip,
|
||||||
@@ -36,26 +35,22 @@ import {
|
|||||||
ElDescriptions,
|
ElDescriptions,
|
||||||
ElDescriptionsItem,
|
ElDescriptionsItem,
|
||||||
ElBacktop,
|
ElBacktop,
|
||||||
ElSwitch
|
ElSwitch,
|
||||||
|
ElBadge,
|
||||||
|
ElTabs,
|
||||||
|
ElTabPane,
|
||||||
|
ElAvatar,
|
||||||
|
ElEmpty,
|
||||||
|
ElCollapse,
|
||||||
|
ElCollapseItem,
|
||||||
|
ElTreeV2,
|
||||||
|
// 指令
|
||||||
|
ElLoading,
|
||||||
|
ElInfiniteScroll
|
||||||
} from "element-plus";
|
} from "element-plus";
|
||||||
|
|
||||||
// https://element-plus.org/zh-CN/component/icon.html
|
// Directives
|
||||||
import {
|
const plugins = [ElLoading, ElInfiniteScroll];
|
||||||
Check,
|
|
||||||
Menu,
|
|
||||||
HomeFilled,
|
|
||||||
SetUp,
|
|
||||||
Edit,
|
|
||||||
Setting,
|
|
||||||
Lollipop,
|
|
||||||
Link,
|
|
||||||
Position,
|
|
||||||
Histogram,
|
|
||||||
RefreshRight,
|
|
||||||
ArrowDown,
|
|
||||||
Close,
|
|
||||||
CloseBold
|
|
||||||
} from "@element-plus/icons";
|
|
||||||
|
|
||||||
const components = [
|
const components = [
|
||||||
ElTag,
|
ElTag,
|
||||||
@@ -94,31 +89,24 @@ const components = [
|
|||||||
ElDescriptionsItem,
|
ElDescriptionsItem,
|
||||||
ElBacktop,
|
ElBacktop,
|
||||||
ElSwitch,
|
ElSwitch,
|
||||||
|
ElBadge,
|
||||||
// icon
|
ElTabs,
|
||||||
Check,
|
ElTabPane,
|
||||||
Menu,
|
ElAvatar,
|
||||||
HomeFilled,
|
ElEmpty,
|
||||||
SetUp,
|
ElCollapse,
|
||||||
Edit,
|
ElCollapseItem,
|
||||||
Setting,
|
ElTreeV2
|
||||||
Lollipop,
|
|
||||||
Link,
|
|
||||||
Position,
|
|
||||||
Histogram,
|
|
||||||
RefreshRight,
|
|
||||||
ArrowDown,
|
|
||||||
Close,
|
|
||||||
CloseBold
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const plugins = [ElLoading];
|
|
||||||
|
|
||||||
export function useElementPlus(app: App) {
|
export function useElementPlus(app: App) {
|
||||||
|
// 注册组件
|
||||||
components.forEach((component: Component) => {
|
components.forEach((component: Component) => {
|
||||||
app.component(component.name, component);
|
app.component(component.name, component);
|
||||||
});
|
});
|
||||||
|
// 注册指令
|
||||||
plugins.forEach(plugin => {
|
plugins.forEach(plugin => {
|
||||||
|
// @ts-ignore
|
||||||
app.use(plugin);
|
app.use(plugin);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// 菜单国际化配置
|
import { siphonI18n } from "./index";
|
||||||
// vxe-table组件国际化
|
// vxe-table组件国际化
|
||||||
import zhVxeTable from "vxe-table/lib/locale/lang/zh-CN";
|
import zhVxeTable from "vxe-table/lib/locale/lang/zh-CN";
|
||||||
import enVxeTable from "vxe-table/lib/locale/lang/en-US";
|
import enVxeTable from "vxe-table/lib/locale/lang/en-US";
|
||||||
@@ -7,152 +7,18 @@ import enVxeTable from "vxe-table/lib/locale/lang/en-US";
|
|||||||
import enLocale from "element-plus/lib/locale/lang/en";
|
import enLocale from "element-plus/lib/locale/lang/en";
|
||||||
import zhLocale from "element-plus/lib/locale/lang/zh-cn";
|
import zhLocale from "element-plus/lib/locale/lang/zh-cn";
|
||||||
|
|
||||||
// 导航菜单配置
|
// 项目内自定义国际化
|
||||||
export const menusConfig = {
|
const zhModules = import.meta.globEager("./zh-CN/**/*.ts");
|
||||||
zh: {
|
const enModules = import.meta.globEager("./en/**/*.ts");
|
||||||
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: "按钮权限",
|
|
||||||
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",
|
|
||||||
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];
|
|
||||||
|
|
||||||
export const localesConfigs = {
|
export const localesConfigs = {
|
||||||
zh: {
|
zh: {
|
||||||
message: Object.assign({}, ...localesList.map(v => v.zh.message)),
|
...siphonI18n(zhModules, "zh-CN"),
|
||||||
...zhVxeTable,
|
...zhVxeTable,
|
||||||
...zhLocale
|
...zhLocale
|
||||||
},
|
},
|
||||||
en: {
|
en: {
|
||||||
message: Object.assign({}, ...localesList.map(v => v.en.message)),
|
...siphonI18n(enModules, "en"),
|
||||||
...enVxeTable,
|
...enVxeTable,
|
||||||
...enLocale
|
...enLocale
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/plugins/i18n/en/buttons.ts
Normal 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"
|
||||||
|
};
|
||||||
38
src/plugins/i18n/en/menus.ts
Normal 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"
|
||||||
|
};
|
||||||
@@ -1,9 +1,68 @@
|
|||||||
// 多组件库的国际化和本地项目国际化兼容
|
// 多组件库的国际化和本地项目国际化兼容
|
||||||
import { App } from "vue";
|
import { App } from "vue";
|
||||||
|
import { set } from "lodash-es";
|
||||||
import { createI18n } from "vue-i18n";
|
import { createI18n } from "vue-i18n";
|
||||||
import { localesConfigs } from "./config";
|
import { localesConfigs } from "./config";
|
||||||
import { storageLocal } from "/@/utils/storage";
|
import { storageLocal } from "/@/utils/storage";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际化转换工具函数
|
||||||
|
* @param message message
|
||||||
|
* @param isI18n 如果true,获取对应的消息,否则返回本身
|
||||||
|
* @returns message
|
||||||
|
*/
|
||||||
|
export function transformI18n(message: string | object = "", isI18n = false) {
|
||||||
|
if (!message) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理存储动态路由的title,格式 {zh:"",en:""}
|
||||||
|
if (typeof message === "object") {
|
||||||
|
return message[i18n.global?.locale];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isI18n) {
|
||||||
|
//@ts-ignore
|
||||||
|
return i18n.global.tc.call(i18n.global, message);
|
||||||
|
} else {
|
||||||
|
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({
|
export const i18n = createI18n({
|
||||||
locale: storageLocal.getItem("responsive-locale")?.locale ?? "zh",
|
locale: storageLocal.getItem("responsive-locale")?.locale ?? "zh",
|
||||||
fallbackLocale: "en",
|
fallbackLocale: "en",
|
||||||
|
|||||||
21
src/plugins/i18n/zh-CN/buttons.ts
Normal 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: "关闭全部标签页"
|
||||||
|
};
|
||||||
38
src/plugins/i18n/zh-CN/menus.ts
Normal 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: "外链"
|
||||||
|
};
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import "xe-utils";
|
import "xe-utils";
|
||||||
import { App } from "vue";
|
import { App } from "vue";
|
||||||
import { i18n } from "../i18n/index";
|
import { i18n } from "../i18n/index";
|
||||||
import "font-awesome/css/font-awesome.css";
|
|
||||||
import {
|
import {
|
||||||
// 核心
|
// 核心
|
||||||
VXETable,
|
VXETable,
|
||||||
@@ -66,8 +65,8 @@ VXETable.setup({
|
|||||||
i18n: (key, args) => i18n.global.t(key, args),
|
i18n: (key, args) => i18n.global.t(key, args),
|
||||||
// 可选,对参数中的列头、校验提示..等进行自动翻译(只对支持国际化的有效)
|
// 可选,对参数中的列头、校验提示..等进行自动翻译(只对支持国际化的有效)
|
||||||
translate(key, args) {
|
translate(key, args) {
|
||||||
// 例如,只翻译 "message." 开头的键值
|
// 例如,只翻译 "buttons." 开头的键值
|
||||||
if (key && key.indexOf("message.") > -1) {
|
if (key && key.indexOf("buttons.") > -1) {
|
||||||
return i18n.global.t(key, args);
|
return i18n.global.t(key, args);
|
||||||
}
|
}
|
||||||
if (key && key.indexOf("el.") > -1) {
|
if (key && key.indexOf("el.") > -1) {
|
||||||
|
|||||||
@@ -1,140 +1,27 @@
|
|||||||
import {
|
import { toRouteType } from "./types";
|
||||||
Router,
|
|
||||||
createRouter,
|
|
||||||
RouteComponent,
|
|
||||||
createWebHashHistory,
|
|
||||||
RouteRecordNormalized
|
|
||||||
} from "vue-router";
|
|
||||||
import { RouteConfigs } from "/@/layout/types";
|
|
||||||
import { split, uniqBy } from "lodash-es";
|
|
||||||
import { i18n } from "/@/plugins/i18n";
|
|
||||||
import { openLink } from "/@/utils/link";
|
import { openLink } from "/@/utils/link";
|
||||||
import NProgress from "/@/utils/progress";
|
import NProgress from "/@/utils/progress";
|
||||||
import { useTimeoutFn } from "@vueuse/core";
|
import { constantRoutes } from "./modules";
|
||||||
import { storageSession, storageLocal } from "/@/utils/storage";
|
import { split, findIndex } from "lodash-es";
|
||||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
import { transformI18n } from "/@/plugins/i18n";
|
||||||
|
|
||||||
// 静态路由
|
|
||||||
import homeRouter from "./modules/home";
|
|
||||||
import Layout from "/@/layout/index.vue";
|
|
||||||
import errorRouter from "./modules/error";
|
|
||||||
import editorRouter from "./modules/editor";
|
|
||||||
import nestedRouter from "./modules/nested";
|
|
||||||
import externalLink from "./modules/externalLink";
|
|
||||||
import remainingRouter from "./modules/remaining";
|
import remainingRouter from "./modules/remaining";
|
||||||
import flowChartRouter from "./modules/flowchart";
|
import { storageSession } from "/@/utils/storage";
|
||||||
import componentsRouter from "./modules/components";
|
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
|
||||||
// 动态路由
|
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
||||||
import { getAsyncRoutes } from "/@/api/routes";
|
import { Router, RouteMeta, createRouter, RouteRecordName } from "vue-router";
|
||||||
|
import {
|
||||||
// https://cn.vitejs.dev/guide/features.html#glob-import
|
initRouter,
|
||||||
const modulesRoutes = import.meta.glob("/src/views/*/*/*.vue");
|
getHistoryMode,
|
||||||
|
getParentPaths,
|
||||||
const constantRoutes: Array<RouteComponent> = [
|
findRouteByPath,
|
||||||
homeRouter,
|
handleAliveRoute
|
||||||
flowChartRouter,
|
} from "./utils";
|
||||||
editorRouter,
|
|
||||||
componentsRouter,
|
|
||||||
nestedRouter,
|
|
||||||
externalLink,
|
|
||||||
errorRouter
|
|
||||||
];
|
|
||||||
|
|
||||||
// 按照路由中meta下的rank等级升序来排序路由
|
|
||||||
export const ascending = arr => {
|
|
||||||
return arr.sort((a: any, b: any) => {
|
|
||||||
return a?.meta?.rank - b?.meta?.rank;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 将所有静态路由导出
|
|
||||||
export const constantRoutesArr: Array<RouteComponent> = ascending(
|
|
||||||
constantRoutes
|
|
||||||
).concat(...remainingRouter);
|
|
||||||
|
|
||||||
// 过滤meta中showLink为false的路由
|
|
||||||
export const filterTree = data => {
|
|
||||||
const newTree = data.filter(v => v.meta.showLink);
|
|
||||||
newTree.forEach(v => v.children && (v.children = filterTree(v.children)));
|
|
||||||
return newTree;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 从路由中提取keepAlive为true的name组成数组(此处本项目中并没有用到,只是暴露个方法)
|
|
||||||
export const getAliveRoute = () => {
|
|
||||||
const alivePageList = [];
|
|
||||||
const recursiveSearch = treeLists => {
|
|
||||||
if (!treeLists || !treeLists.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < treeLists.length; i++) {
|
|
||||||
if (treeLists[i]?.meta?.keepAlive) alivePageList.push(treeLists[i].name);
|
|
||||||
recursiveSearch(treeLists[i].children);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
recursiveSearch(router.options.routes);
|
|
||||||
return alivePageList;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 批量删除缓存路由
|
|
||||||
export const delAliveRoutes = (delAliveRouteList: Array<RouteConfigs>) => {
|
|
||||||
delAliveRouteList.forEach(route => {
|
|
||||||
usePermissionStoreHook().cacheOperate({
|
|
||||||
mode: "delete",
|
|
||||||
name: route?.name
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理缓存路由(添加、删除、刷新)
|
|
||||||
export const handleAliveRoute = (
|
|
||||||
matched: RouteRecordNormalized[],
|
|
||||||
mode?: string
|
|
||||||
) => {
|
|
||||||
switch (mode) {
|
|
||||||
case "add":
|
|
||||||
matched.forEach(v => {
|
|
||||||
usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
usePermissionStoreHook().cacheOperate({
|
|
||||||
mode: "delete",
|
|
||||||
name: matched[matched.length - 1].name
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usePermissionStoreHook().cacheOperate({
|
|
||||||
mode: "delete",
|
|
||||||
name: matched[matched.length - 1].name
|
|
||||||
});
|
|
||||||
useTimeoutFn(() => {
|
|
||||||
matched.forEach(v => {
|
|
||||||
usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 过滤后端传来的动态路由 重新生成规范路由
|
|
||||||
export const addAsyncRoutes = (arrRoutes: Array<RouteComponent>) => {
|
|
||||||
if (!arrRoutes || !arrRoutes.length) return;
|
|
||||||
arrRoutes.forEach((v: any) => {
|
|
||||||
if (v.redirect) {
|
|
||||||
v.component = Layout;
|
|
||||||
} else {
|
|
||||||
v.component = modulesRoutes[`/src/views${v.path}/index.vue`];
|
|
||||||
}
|
|
||||||
if (v.children) {
|
|
||||||
addAsyncRoutes(v.children);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return arrRoutes;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 创建路由实例
|
// 创建路由实例
|
||||||
export const router: Router = createRouter({
|
export const router: Router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: getHistoryMode(),
|
||||||
routes: filterTree(ascending(constantRoutes)).concat(...remainingRouter),
|
routes: constantRoutes.concat(...remainingRouter),
|
||||||
|
strict: true,
|
||||||
scrollBehavior(to, from, savedPosition) {
|
scrollBehavior(to, from, savedPosition) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (savedPosition) {
|
if (savedPosition) {
|
||||||
@@ -150,53 +37,10 @@ export const router: Router = createRouter({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 初始化路由
|
|
||||||
export const initRouter = name => {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
getAsyncRoutes({ name }).then(({ info }) => {
|
|
||||||
if (info.length === 0) {
|
|
||||||
usePermissionStoreHook().changeSetting(info);
|
|
||||||
} else {
|
|
||||||
addAsyncRoutes(info).map((v: any) => {
|
|
||||||
// 防止重复添加路由
|
|
||||||
if (
|
|
||||||
router.options.routes.findIndex(value => value.path === v.path) !==
|
|
||||||
-1
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// 切记将路由push到routes后还需要使用addRoute,这样路由才能正常跳转
|
|
||||||
router.options.routes.push(v);
|
|
||||||
// 最终路由进行升序
|
|
||||||
ascending(router.options.routes);
|
|
||||||
router.addRoute(v.name, v);
|
|
||||||
usePermissionStoreHook().changeSetting(info);
|
|
||||||
}
|
|
||||||
resolve(router);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
router.addRoute({
|
|
||||||
path: "/:pathMatch(.*)",
|
|
||||||
redirect: "/error/404"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 重置路由
|
|
||||||
export function resetRouter() {
|
|
||||||
router.getRoutes().forEach(route => {
|
|
||||||
const { name } = route;
|
|
||||||
if (name) {
|
|
||||||
router.hasRoute(name) && router.removeRoute(name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 路由白名单
|
// 路由白名单
|
||||||
const whiteList = ["/login"];
|
const whiteList = ["/login"];
|
||||||
|
|
||||||
router.beforeEach((to, _from, next) => {
|
router.beforeEach((to: toRouteType, _from, next) => {
|
||||||
if (to.meta?.keepAlive) {
|
if (to.meta?.keepAlive) {
|
||||||
const newMatched = to.matched;
|
const newMatched = to.matched;
|
||||||
handleAliveRoute(newMatched, "add");
|
handleAliveRoute(newMatched, "add");
|
||||||
@@ -208,10 +52,15 @@ router.beforeEach((to, _from, next) => {
|
|||||||
const name = storageSession.getItem("info");
|
const name = storageSession.getItem("info");
|
||||||
NProgress.start();
|
NProgress.start();
|
||||||
const externalLink = to?.redirectedFrom?.fullPath;
|
const externalLink = to?.redirectedFrom?.fullPath;
|
||||||
// @ts-ignore
|
if (!externalLink)
|
||||||
const { t } = i18n.global;
|
to.matched.some(item => {
|
||||||
// @ts-ignore
|
item.meta.title
|
||||||
if (!externalLink) to.meta.title ? (document.title = t(to.meta.title)) : "";
|
? (document.title = transformI18n(
|
||||||
|
item.meta.title as string,
|
||||||
|
item.meta?.i18n as boolean
|
||||||
|
))
|
||||||
|
: "";
|
||||||
|
});
|
||||||
if (name) {
|
if (name) {
|
||||||
if (_from?.name) {
|
if (_from?.name) {
|
||||||
// 如果路由包含http 则是超链接 反之是普通路由
|
// 如果路由包含http 则是超链接 反之是普通路由
|
||||||
@@ -223,26 +72,75 @@ router.beforeEach((to, _from, next) => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 刷新
|
// 刷新
|
||||||
if (usePermissionStoreHook().wholeRoutes.length === 0)
|
if (usePermissionStoreHook().wholeMenus.length === 0)
|
||||||
initRouter(name.username).then((router: Router) => {
|
initRouter(name.username).then((router: Router) => {
|
||||||
router.push(to.path);
|
if (!useMultiTagsStoreHook().getMultiTagsCache) {
|
||||||
// 刷新页面更新标签栏与页面路由匹配
|
const handTag = (
|
||||||
const localRoutes = storageLocal.getItem(
|
path: string,
|
||||||
"responsive-routesInStorage"
|
parentPath: string,
|
||||||
|
name: RouteRecordName,
|
||||||
|
meta: RouteMeta
|
||||||
|
): void => {
|
||||||
|
useMultiTagsStoreHook().handleTags("push", {
|
||||||
|
path,
|
||||||
|
parentPath,
|
||||||
|
name,
|
||||||
|
meta
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// 未开启标签页缓存,刷新页面重定向到顶级路由(参考标签页操作例子,只针对静态路由)
|
||||||
|
if (to.meta?.refreshRedirect) {
|
||||||
|
const routes = router.options.routes;
|
||||||
|
const { refreshRedirect } = to.meta;
|
||||||
|
const { name, meta } = findRouteByPath(refreshRedirect, routes);
|
||||||
|
handTag(
|
||||||
|
refreshRedirect,
|
||||||
|
getParentPaths(refreshRedirect, routes)[1],
|
||||||
|
name,
|
||||||
|
meta
|
||||||
);
|
);
|
||||||
const optionsRoutes = router.options?.routes;
|
return router.push(refreshRedirect);
|
||||||
const newLocalRoutes = [];
|
} else {
|
||||||
optionsRoutes.forEach(ors => {
|
const { path } = to;
|
||||||
localRoutes.forEach(lrs => {
|
const index = findIndex(remainingRouter, v => {
|
||||||
if (ors.path === lrs.parentPath) {
|
return v.path == path;
|
||||||
newLocalRoutes.push(lrs);
|
});
|
||||||
|
const routes =
|
||||||
|
index === -1
|
||||||
|
? router.options.routes[0].children
|
||||||
|
: router.options.routes;
|
||||||
|
const route = findRouteByPath(path, routes);
|
||||||
|
const routePartent = getParentPaths(path, routes);
|
||||||
|
// 未开启标签页缓存,刷新页面重定向到顶级路由(参考标签页操作例子,只针对动态路由)
|
||||||
|
if (
|
||||||
|
path !== routes[0].path &&
|
||||||
|
route?.meta?.rank !== 0 &&
|
||||||
|
routePartent.length === 0
|
||||||
|
) {
|
||||||
|
if (!route?.meta?.refreshRedirect) return;
|
||||||
|
const { name, meta } = findRouteByPath(
|
||||||
|
route.meta.refreshRedirect,
|
||||||
|
routes
|
||||||
|
);
|
||||||
|
handTag(
|
||||||
|
route.meta?.refreshRedirect,
|
||||||
|
getParentPaths(route.meta?.refreshRedirect, routes)[0],
|
||||||
|
name,
|
||||||
|
meta
|
||||||
|
);
|
||||||
|
return router.push(route.meta?.refreshRedirect);
|
||||||
|
} else {
|
||||||
|
handTag(
|
||||||
|
route.path,
|
||||||
|
routePartent[routePartent.length - 1],
|
||||||
|
route.name,
|
||||||
|
route.meta
|
||||||
|
);
|
||||||
|
return router.push(path);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
}
|
||||||
storageLocal.setItem(
|
router.push(to.fullPath);
|
||||||
"responsive-routesInStorage",
|
|
||||||
uniqBy(newLocalRoutes, "path")
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
import Layout from "/@/layout/index.vue";
|
||||||
|
|
||||||
const componentsRouter = {
|
const componentsRouter = {
|
||||||
@@ -6,9 +7,9 @@ const componentsRouter = {
|
|||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: "/components/video",
|
redirect: "/components/video",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "Menu",
|
icon: "menu",
|
||||||
title: "message.hscomponents",
|
title: $t("menus.hscomponents"),
|
||||||
showLink: true,
|
i18n: true,
|
||||||
rank: 4
|
rank: 4
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
@@ -17,8 +18,8 @@ const componentsRouter = {
|
|||||||
name: "video",
|
name: "video",
|
||||||
component: () => import("/@/views/components/video/index.vue"),
|
component: () => import("/@/views/components/video/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hsvideo",
|
title: $t("menus.hsvideo"),
|
||||||
showLink: true
|
i18n: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -26,9 +27,9 @@ const componentsRouter = {
|
|||||||
name: "map",
|
name: "map",
|
||||||
component: () => import("/@/views/components/map/index.vue"),
|
component: () => import("/@/views/components/map/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hsmap",
|
title: $t("menus.hsmap"),
|
||||||
showLink: true,
|
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
|
i18n: true,
|
||||||
transition: {
|
transition: {
|
||||||
name: "fade"
|
name: "fade"
|
||||||
}
|
}
|
||||||
@@ -39,8 +40,8 @@ const componentsRouter = {
|
|||||||
name: "draggable",
|
name: "draggable",
|
||||||
component: () => import("/@/views/components/draggable/index.vue"),
|
component: () => import("/@/views/components/draggable/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hsdraggable",
|
title: $t("menus.hsdraggable"),
|
||||||
showLink: true,
|
i18n: true,
|
||||||
transition: {
|
transition: {
|
||||||
enterTransition: "animate__zoomIn",
|
enterTransition: "animate__zoomIn",
|
||||||
leaveTransition: "animate__zoomOut"
|
leaveTransition: "animate__zoomOut"
|
||||||
@@ -53,8 +54,8 @@ const componentsRouter = {
|
|||||||
name: "splitPane",
|
name: "splitPane",
|
||||||
component: () => import("/@/views/components/split-pane/index.vue"),
|
component: () => import("/@/views/components/split-pane/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hssplitPane",
|
title: $t("menus.hssplitPane"),
|
||||||
showLink: true,
|
i18n: true,
|
||||||
extraIcon: {
|
extraIcon: {
|
||||||
svg: true,
|
svg: true,
|
||||||
name: "team-iconxinpinrenqiwang"
|
name: "team-iconxinpinrenqiwang"
|
||||||
@@ -66,8 +67,8 @@ const componentsRouter = {
|
|||||||
name: "button",
|
name: "button",
|
||||||
component: () => import("/@/views/components/button/index.vue"),
|
component: () => import("/@/views/components/button/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hsbutton",
|
title: $t("menus.hsbutton"),
|
||||||
showLink: true
|
i18n: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -75,8 +76,8 @@ const componentsRouter = {
|
|||||||
name: "cropping",
|
name: "cropping",
|
||||||
component: () => import("/@/views/components/cropping/index.vue"),
|
component: () => import("/@/views/components/cropping/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hscropping",
|
title: $t("menus.hscropping"),
|
||||||
showLink: true
|
i18n: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -84,8 +85,8 @@ const componentsRouter = {
|
|||||||
name: "countTo",
|
name: "countTo",
|
||||||
component: () => import("/@/views/components/count-to/index.vue"),
|
component: () => import("/@/views/components/count-to/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hscountTo",
|
title: $t("menus.hscountTo"),
|
||||||
showLink: true
|
i18n: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -93,8 +94,8 @@ const componentsRouter = {
|
|||||||
name: "selector",
|
name: "selector",
|
||||||
component: () => import("/@/views/components/selector/index.vue"),
|
component: () => import("/@/views/components/selector/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hsselector",
|
title: $t("menus.hsselector"),
|
||||||
showLink: true
|
i18n: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -102,8 +103,8 @@ const componentsRouter = {
|
|||||||
name: "seamlessScroll",
|
name: "seamlessScroll",
|
||||||
component: () => import("/@/views/components/seamless-scroll/index.vue"),
|
component: () => import("/@/views/components/seamless-scroll/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hsseamless",
|
title: $t("menus.hsseamless"),
|
||||||
showLink: true
|
i18n: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -111,8 +112,8 @@ const componentsRouter = {
|
|||||||
name: "contextmenu",
|
name: "contextmenu",
|
||||||
component: () => import("/@/views/components/contextmenu/index.vue"),
|
component: () => import("/@/views/components/contextmenu/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hscontextmenu",
|
title: $t("menus.hscontextmenu"),
|
||||||
showLink: true
|
i18n: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
import Layout from "/@/layout/index.vue";
|
||||||
|
|
||||||
const editorRouter = {
|
const editorRouter = {
|
||||||
@@ -6,9 +7,9 @@ const editorRouter = {
|
|||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: "/editor/index",
|
redirect: "/editor/index",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "Edit",
|
icon: "edit",
|
||||||
title: "message.hseditor",
|
title: $t("menus.hseditor"),
|
||||||
showLink: true,
|
i18n: true,
|
||||||
rank: 2
|
rank: 2
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
@@ -17,8 +18,8 @@ const editorRouter = {
|
|||||||
name: "reEditor",
|
name: "reEditor",
|
||||||
component: () => import("/@/views/editor/index.vue"),
|
component: () => import("/@/views/editor/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hseditor",
|
title: $t("menus.hseditor"),
|
||||||
showLink: true,
|
i18n: true,
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
extraIcon: {
|
extraIcon: {
|
||||||
svg: true,
|
svg: true,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
import Layout from "/@/layout/index.vue";
|
||||||
|
|
||||||
const errorRouter = {
|
const errorRouter = {
|
||||||
@@ -6,9 +7,9 @@ const errorRouter = {
|
|||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: "/error/401",
|
redirect: "/error/401",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "Position",
|
icon: "position",
|
||||||
title: "message.hserror",
|
title: $t("menus.hserror"),
|
||||||
showLink: true,
|
i18n: true,
|
||||||
rank: 7
|
rank: 7
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
@@ -17,8 +18,8 @@ const errorRouter = {
|
|||||||
name: "401",
|
name: "401",
|
||||||
component: () => import("/@/views/error/401.vue"),
|
component: () => import("/@/views/error/401.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hsfourZeroOne",
|
title: $t("menus.hsfourZeroOne"),
|
||||||
showLink: true
|
i18n: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -26,8 +27,8 @@ const errorRouter = {
|
|||||||
name: "404",
|
name: "404",
|
||||||
component: () => import("/@/views/error/404.vue"),
|
component: () => import("/@/views/error/404.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hsfourZeroFour",
|
title: $t("menus.hsfourZeroFour"),
|
||||||
showLink: true
|
i18n: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
import Layout from "/@/layout/index.vue";
|
||||||
|
|
||||||
const externalLink = {
|
const externalLink = {
|
||||||
@@ -5,17 +6,17 @@ const externalLink = {
|
|||||||
name: "external",
|
name: "external",
|
||||||
component: Layout,
|
component: Layout,
|
||||||
meta: {
|
meta: {
|
||||||
icon: "Link",
|
icon: "link",
|
||||||
title: "message.externalLink",
|
title: $t("menus.externalLink"),
|
||||||
showLink: true,
|
i18n: true,
|
||||||
rank: 190
|
rank: 190
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "https://github.com/xiaoxian521/vue-pure-admin",
|
path: "https://github.com/xiaoxian521/vue-pure-admin",
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.externalLink",
|
title: $t("menus.externalLink"),
|
||||||
showLink: true,
|
i18n: true,
|
||||||
rank: 191
|
rank: 191
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { $t } from "/@/plugins/i18n";
|
||||||
import Layout from "/@/layout/index.vue";
|
import Layout from "/@/layout/index.vue";
|
||||||
|
|
||||||
const flowChartRouter = {
|
const flowChartRouter = {
|
||||||
@@ -6,9 +7,9 @@ const flowChartRouter = {
|
|||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: "/flowChart/index",
|
redirect: "/flowChart/index",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "SetUp",
|
icon: "set-up",
|
||||||
title: "message.hsflowChart",
|
title: $t("menus.hsflowChart"),
|
||||||
showLink: true,
|
i18n: true,
|
||||||
rank: 1
|
rank: 1
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
@@ -17,8 +18,8 @@ const flowChartRouter = {
|
|||||||
name: "flowChart",
|
name: "flowChart",
|
||||||
component: () => import("/@/views/flow-chart/index.vue"),
|
component: () => import("/@/views/flow-chart/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: "message.hsflowChart",
|
title: $t("menus.hsflowChart"),
|
||||||
showLink: true
|
i18n: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||