mirror of
https://github.com/pure-admin/pure-admin-thin.git
synced 2025-12-14 14:20:31 +08:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2543cf9d3 | ||
|
|
13a36c0acb | ||
|
|
85e5074665 | ||
|
|
a8a1f8d900 | ||
|
|
c0f3dcbbb7 | ||
|
|
c6b0aa2467 | ||
|
|
057fef9147 | ||
|
|
036459cb02 | ||
|
|
7640f97a36 | ||
|
|
0a57ad7920 | ||
|
|
a4f857124f | ||
|
|
0d8f6b314b | ||
|
|
923fdd9ff1 | ||
|
|
e29340422a | ||
|
|
e25f4bcf39 | ||
|
|
270df1b17a | ||
|
|
fda66ee37c | ||
|
|
dcd687fe86 | ||
|
|
1f2116c6b9 | ||
|
|
23db7512d0 | ||
|
|
03d68a24d9 | ||
|
|
51a4a6d246 | ||
|
|
f64cd466eb | ||
|
|
e87134a635 | ||
|
|
ef184a8636 | ||
|
|
afc9ad0273 | ||
|
|
1121c5a2b5 | ||
|
|
ff27074ebd | ||
|
|
1f27d6cd9e | ||
|
|
4b435d0e0f | ||
|
|
872e0bbd5b | ||
|
|
b6859d7920 | ||
|
|
6e02ae14a0 | ||
|
|
c28066fb1f | ||
|
|
d9ab1b1198 | ||
|
|
4e95672cb4 | ||
|
|
9c0872fa6a | ||
|
|
7678aa64e2 | ||
|
|
1e30b31be2 | ||
|
|
2c887ee1d9 | ||
|
|
635da1dc52 | ||
|
|
6f65e67872 | ||
|
|
c79862a6c8 | ||
|
|
799643a283 | ||
|
|
707200c71d | ||
|
|
bc548d500c | ||
|
|
4d57c9e3d7 | ||
|
|
1bcf391513 | ||
|
|
30af2b78fe | ||
|
|
0b1bd19179 | ||
|
|
4ccf200059 | ||
|
|
27056e7560 | ||
|
|
132fbbade3 | ||
|
|
c6e25d6933 | ||
|
|
cabf1f85ef | ||
|
|
cb3d7cd552 | ||
|
|
80453ec4b1 | ||
|
|
8ca8bbcee0 | ||
|
|
2b67efe771 | ||
|
|
bbe23ce0a2 | ||
|
|
68492ec362 | ||
|
|
bc3b199860 | ||
|
|
4407dd5d10 | ||
|
|
b3cbdd6e87 | ||
|
|
b5f0ca52ce | ||
|
|
6110be29a1 | ||
|
|
d53496e495 | ||
|
|
1bafbeaab7 | ||
|
|
9a802296c7 | ||
|
|
e183ea75a0 | ||
|
|
969775c7cf | ||
|
|
841c5bd53a | ||
|
|
761b0e5ec2 |
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
@@ -9,7 +9,6 @@
|
|||||||
"bradlc.vscode-tailwindcss",
|
"bradlc.vscode-tailwindcss",
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"esbenp.prettier-vscode",
|
"esbenp.prettier-vscode",
|
||||||
"lokalise.i18n-ally",
|
|
||||||
"redhat.vscode-yaml",
|
"redhat.vscode-yaml",
|
||||||
"csstools.postcss",
|
"csstools.postcss",
|
||||||
"mikestead.dotenv",
|
"mikestead.dotenv",
|
||||||
|
|||||||
14
.vscode/settings.json
vendored
14
.vscode/settings.json
vendored
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"tailwindCSS.experimental.configFile": "src/style/tailwind.css",
|
|
||||||
"editor.formatOnType": true,
|
"editor.formatOnType": true,
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"[vue]": {
|
"[vue]": {
|
||||||
@@ -28,19 +27,6 @@
|
|||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": "explicit"
|
"source.fixAll.eslint": "explicit"
|
||||||
},
|
},
|
||||||
"i18n-ally.localesPaths": "locales",
|
|
||||||
"i18n-ally.keystyle": "nested",
|
|
||||||
"i18n-ally.sortKeys": true,
|
|
||||||
"i18n-ally.namespace": true,
|
|
||||||
"i18n-ally.enabledParsers": [
|
|
||||||
"yaml",
|
|
||||||
"js"
|
|
||||||
],
|
|
||||||
"i18n-ally.sourceLanguage": "en",
|
|
||||||
"i18n-ally.displayLanguage": "zh-CN",
|
|
||||||
"i18n-ally.enabledFrameworks": [
|
|
||||||
"vue"
|
|
||||||
],
|
|
||||||
"iconify.excludes": [
|
"iconify.excludes": [
|
||||||
"el"
|
"el"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<h1>vue-pure-admin Lite Edition(i18n version)</h1>
|
<h1>vue-pure-admin Lite Edition(no i18n version)</h1>
|
||||||
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
@@ -8,6 +8,14 @@
|
|||||||
|
|
||||||
The simplified version is based on the shelf extracted from [vue-pure-admin](https://github.com/pure-admin/vue-pure-admin), which contains main functions and is more suitable for actual project development. The packaged size is introduced globally [element-plus](https://element-plus.org) is still below `2.3MB`, and the full version of the code will be permanently synchronized. After enabling `brotli` compression and `cdn` to replace the local library mode, the package size is less than `350kb`
|
The simplified version is based on the shelf extracted from [vue-pure-admin](https://github.com/pure-admin/vue-pure-admin), which contains main functions and is more suitable for actual project development. The packaged size is introduced globally [element-plus](https://element-plus.org) is still below `2.3MB`, and the full version of the code will be permanently synchronized. After enabling `brotli` compression and `cdn` to replace the local library mode, the package size is less than `350kb`
|
||||||
|
|
||||||
|
## `js` version
|
||||||
|
|
||||||
|
[Click me to view js version](https://pure-admin.cn/pages/js/)
|
||||||
|
|
||||||
|
## `max` version
|
||||||
|
|
||||||
|
[Click me to view the max version](https://pure-admin.cn/pages/max/)
|
||||||
|
|
||||||
## Supporting video
|
## Supporting video
|
||||||
|
|
||||||
[Click me to view UI design](https://www.bilibili.com/video/BV17g411T7rq)
|
[Click me to view UI design](https://www.bilibili.com/video/BV17g411T7rq)
|
||||||
@@ -18,7 +26,7 @@ The simplified version is based on the shelf extracted from [vue-pure-admin](htt
|
|||||||
[Click me to view vue-pure-admin documentation](https://pure-admin.cn/)
|
[Click me to view vue-pure-admin documentation](https://pure-admin.cn/)
|
||||||
[Click me to view @pureadmin/utils documentation](https://pure-admin-utils.netlify.app)
|
[Click me to view @pureadmin/utils documentation](https://pure-admin-utils.netlify.app)
|
||||||
|
|
||||||
## Premium service
|
## Quality service, software outsourcing, sponsorship support
|
||||||
|
|
||||||
[Click me to view details](https://pure-admin.cn/pages/service/)
|
[Click me to view details](https://pure-admin.cn/pages/service/)
|
||||||
|
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -1,4 +1,4 @@
|
|||||||
<h1>vue-pure-admin精简版(国际化版本)</h1>
|
<h1>vue-pure-admin精简版(非国际化版本)</h1>
|
||||||
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
@@ -10,7 +10,15 @@
|
|||||||
|
|
||||||
## 版本选择
|
## 版本选择
|
||||||
|
|
||||||
当前是国际化版本,如果您需要非国际化版本 [请点击](https://github.com/pure-admin/pure-admin-thin)
|
当前是非国际化版本,如果您需要国际化版本 [请点击](https://github.com/pure-admin/pure-admin-thin/tree/i18n)
|
||||||
|
|
||||||
|
## `js` 版本
|
||||||
|
|
||||||
|
[点我查看 js 版本](https://pure-admin.cn/pages/js/)
|
||||||
|
|
||||||
|
## `max` 版本
|
||||||
|
|
||||||
|
[点我查看 max 版本](https://pure-admin.cn/pages/max/)
|
||||||
|
|
||||||
## 配套视频
|
## 配套视频
|
||||||
|
|
||||||
@@ -22,7 +30,7 @@
|
|||||||
[点我查看 vue-pure-admin 文档](https://pure-admin.cn/)
|
[点我查看 vue-pure-admin 文档](https://pure-admin.cn/)
|
||||||
[点我查看 @pureadmin/utils 文档](https://pure-admin-utils.netlify.app)
|
[点我查看 @pureadmin/utils 文档](https://pure-admin-utils.netlify.app)
|
||||||
|
|
||||||
## 高级服务
|
## 优质服务、软件外包、赞助支持
|
||||||
|
|
||||||
[点我查看详情](https://pure-admin.cn/pages/service/)
|
[点我查看详情](https://pure-admin.cn/pages/service/)
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,6 @@ export const cdn = importToCDN({
|
|||||||
var: "VueRouter",
|
var: "VueRouter",
|
||||||
path: "vue-router.global.min.js"
|
path: "vue-router.global.min.js"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "vue-i18n",
|
|
||||||
var: "VueI18n",
|
|
||||||
path: "vue-i18n.runtime.global.prod.min.js"
|
|
||||||
},
|
|
||||||
// 项目中没有直接安装vue-demi,但是pinia用到了,所以需要在引入pinia前引入vue-demi(https://github.com/vuejs/pinia/blob/v2/packages/pinia/package.json#L77)
|
// 项目中没有直接安装vue-demi,但是pinia用到了,所以需要在引入pinia前引入vue-demi(https://github.com/vuejs/pinia/blob/v2/packages/pinia/package.json#L77)
|
||||||
{
|
{
|
||||||
name: "vue-demi",
|
name: "vue-demi",
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ const include = [
|
|||||||
"dayjs",
|
"dayjs",
|
||||||
"axios",
|
"axios",
|
||||||
"pinia",
|
"pinia",
|
||||||
"vue-i18n",
|
|
||||||
"vue-types",
|
"vue-types",
|
||||||
"js-cookie",
|
"js-cookie",
|
||||||
"vue-tippy",
|
"vue-tippy",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { cdn } from "./cdn";
|
import { cdn } from "./cdn";
|
||||||
import vue from "@vitejs/plugin-vue";
|
import vue from "@vitejs/plugin-vue";
|
||||||
import { pathResolve } from "./utils";
|
|
||||||
import { viteBuildInfo } from "./info";
|
import { viteBuildInfo } from "./info";
|
||||||
import svgLoader from "vite-svg-loader";
|
import svgLoader from "vite-svg-loader";
|
||||||
import Icons from "unplugin-icons/vite";
|
import Icons from "unplugin-icons/vite";
|
||||||
@@ -11,7 +10,6 @@ import { configCompressPlugin } from "./compress";
|
|||||||
import removeNoMatch from "vite-plugin-router-warn";
|
import removeNoMatch from "vite-plugin-router-warn";
|
||||||
import { visualizer } from "rollup-plugin-visualizer";
|
import { visualizer } from "rollup-plugin-visualizer";
|
||||||
import removeConsole from "vite-plugin-remove-console";
|
import removeConsole from "vite-plugin-remove-console";
|
||||||
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";
|
|
||||||
import { codeInspectorPlugin } from "code-inspector-plugin";
|
import { codeInspectorPlugin } from "code-inspector-plugin";
|
||||||
import { vitePluginFakeServer } from "vite-plugin-fake-server";
|
import { vitePluginFakeServer } from "vite-plugin-fake-server";
|
||||||
|
|
||||||
@@ -25,9 +23,6 @@ export function getPluginsList(
|
|||||||
vue(),
|
vue(),
|
||||||
// jsx、tsx语法支持
|
// jsx、tsx语法支持
|
||||||
vueJsx(),
|
vueJsx(),
|
||||||
VueI18nPlugin({
|
|
||||||
include: [pathResolve("../locales/**")]
|
|
||||||
}),
|
|
||||||
/**
|
/**
|
||||||
* 在页面上按住组合键时,鼠标在页面移动即会在 DOM 上出现遮罩层并显示相关信息,点击一下将自动打开 IDE 并将光标定位到元素对应的代码位置
|
* 在页面上按住组合键时,鼠标在页面移动即会在 DOM 上出现遮罩层并显示相关信息,点击一下将自动打开 IDE 并将光标定位到元素对应的代码位置
|
||||||
* Mac 默认组合键 Option + Shift
|
* Mac 默认组合键 Option + Shift
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
buttons:
|
|
||||||
pureLoginOut: LoginOut
|
|
||||||
pureLogin: Login
|
|
||||||
pureOpenSystemSet: Open System Configs
|
|
||||||
pureReload: Reload
|
|
||||||
pureCloseCurrentTab: Close CurrentTab
|
|
||||||
pureCloseLeftTabs: Close LeftTabs
|
|
||||||
pureCloseRightTabs: Close RightTabs
|
|
||||||
pureCloseOtherTabs: Close OtherTabs
|
|
||||||
pureCloseAllTabs: Close AllTabs
|
|
||||||
pureContentFullScreen: Content FullScreen
|
|
||||||
pureContentExitFullScreen: Content ExitFullScreen
|
|
||||||
pureClickCollapse: Collapse
|
|
||||||
pureClickExpand: Expand
|
|
||||||
pureConfirm: Confirm
|
|
||||||
pureSwitch: Switch
|
|
||||||
pureClose: Close
|
|
||||||
pureBackTop: BackTop
|
|
||||||
pureOpenText: Open
|
|
||||||
pureCloseText: Close
|
|
||||||
search:
|
|
||||||
pureTotal: Total
|
|
||||||
pureHistory: History
|
|
||||||
pureCollect: Collect
|
|
||||||
pureDragSort: (Drag Sort)
|
|
||||||
pureEmpty: Empty
|
|
||||||
purePlaceholder: Search Menu
|
|
||||||
panel:
|
|
||||||
pureSystemSet: System Configs
|
|
||||||
pureCloseSystemSet: Close System Configs
|
|
||||||
pureClearCacheAndToLogin: Clear cache and return to login page
|
|
||||||
pureClearCache: Clear Cache
|
|
||||||
pureOverallStyle: Overall Style
|
|
||||||
pureOverallStyleLight: Light
|
|
||||||
pureOverallStyleLightTip: Set sail freshly and light up the comfortable work interface
|
|
||||||
pureOverallStyleDark: Dark
|
|
||||||
pureOverallStyleDarkTip: Moonlight Overture, indulge in the tranquility and elegance of the night
|
|
||||||
pureOverallStyleSystem: Auto
|
|
||||||
pureOverallStyleSystemTip: Synchronize time, the interface naturally responds to morning and dusk
|
|
||||||
pureThemeColor: Theme Color
|
|
||||||
pureLayoutModel: Layout Model
|
|
||||||
pureVerticalTip: The menu on the left is familiar and friendly
|
|
||||||
pureHorizontalTip: Top menu, concise overview
|
|
||||||
pureMixTip: Mixed menu, flexible
|
|
||||||
pureStretch: Stretch Page
|
|
||||||
pureStretchFixed: Fixed
|
|
||||||
pureStretchFixedTip: Compact pages make it easy to find the information you need
|
|
||||||
pureStretchCustom: Custom
|
|
||||||
pureStretchCustomTip: Minimum 1280, maximum 1600
|
|
||||||
pureTagsStyle: Tags Style
|
|
||||||
pureTagsStyleSmart: Smart
|
|
||||||
pureTagsStyleSmartTip: Smart tags add fun and brilliance
|
|
||||||
pureTagsStyleCard: Card
|
|
||||||
pureTagsStyleCardTip: Card tags for efficient browsing
|
|
||||||
pureTagsStyleChrome: Chrome
|
|
||||||
pureTagsStyleChromeTip: Chrome style is classic and elegant
|
|
||||||
pureInterfaceDisplay: Interface Display
|
|
||||||
pureGreyModel: Grey Model
|
|
||||||
pureWeakModel: Weak Model
|
|
||||||
pureHiddenTags: Hidden Tags
|
|
||||||
pureHiddenFooter: Hidden Footer
|
|
||||||
pureMultiTagsCache: MultiTags Cache
|
|
||||||
menus:
|
|
||||||
pureHome: Home
|
|
||||||
pureLogin: Login
|
|
||||||
pureAbnormal: Abnormal Page
|
|
||||||
purePageNotFound: "404"
|
|
||||||
pureAccessDenied: "403"
|
|
||||||
pureServerError: "500"
|
|
||||||
purePermission: Permission Manage
|
|
||||||
purePermissionPage: Page Permission
|
|
||||||
purePermissionButton: Button Permission
|
|
||||||
purePermissionButtonRouter: Route return button permission
|
|
||||||
purePermissionButtonLogin: Login interface return button permission
|
|
||||||
status:
|
|
||||||
pureLoad: Loading...
|
|
||||||
pureMessage: Message
|
|
||||||
pureNotify: Notify
|
|
||||||
pureTodo: Todo
|
|
||||||
pureNoMessage: No Message
|
|
||||||
pureNoNotify: No Notify
|
|
||||||
pureNoTodo: No Todo
|
|
||||||
login:
|
|
||||||
pureUsername: Username
|
|
||||||
purePassword: Password
|
|
||||||
pureLogin: Login
|
|
||||||
pureLoginSuccess: Login Success
|
|
||||||
pureLoginFail: Login Fail
|
|
||||||
pureUsernameReg: Please enter username
|
|
||||||
purePassWordReg: Please enter password
|
|
||||||
purePassWordRuleReg: The password format should be any combination of 8-18 digits
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
buttons:
|
|
||||||
pureLoginOut: 退出系统
|
|
||||||
pureLogin: 登录
|
|
||||||
pureOpenSystemSet: 打开系统配置
|
|
||||||
pureReload: 重新加载
|
|
||||||
pureCloseCurrentTab: 关闭当前标签页
|
|
||||||
pureCloseLeftTabs: 关闭左侧标签页
|
|
||||||
pureCloseRightTabs: 关闭右侧标签页
|
|
||||||
pureCloseOtherTabs: 关闭其他标签页
|
|
||||||
pureCloseAllTabs: 关闭全部标签页
|
|
||||||
pureContentFullScreen: 内容区全屏
|
|
||||||
pureContentExitFullScreen: 内容区退出全屏
|
|
||||||
pureClickCollapse: 点击折叠
|
|
||||||
pureClickExpand: 点击展开
|
|
||||||
pureConfirm: 确认
|
|
||||||
pureSwitch: 切换
|
|
||||||
pureClose: 关闭
|
|
||||||
pureBackTop: 回到顶部
|
|
||||||
pureOpenText: 开
|
|
||||||
pureCloseText: 关
|
|
||||||
search:
|
|
||||||
pureTotal: 共
|
|
||||||
pureHistory: 搜索历史
|
|
||||||
pureCollect: 收藏
|
|
||||||
pureDragSort: (可拖拽排序)
|
|
||||||
pureEmpty: 暂无搜索结果
|
|
||||||
purePlaceholder: 搜索菜单(支持拼音搜索)
|
|
||||||
panel:
|
|
||||||
pureSystemSet: 系统配置
|
|
||||||
pureCloseSystemSet: 关闭配置
|
|
||||||
pureClearCacheAndToLogin: 清空缓存并返回登录页
|
|
||||||
pureClearCache: 清空缓存
|
|
||||||
pureOverallStyle: 整体风格
|
|
||||||
pureOverallStyleLight: 浅色
|
|
||||||
pureOverallStyleLightTip: 清新启航,点亮舒适的工作界面
|
|
||||||
pureOverallStyleDark: 深色
|
|
||||||
pureOverallStyleDarkTip: 月光序曲,沉醉于夜的静谧雅致
|
|
||||||
pureOverallStyleSystem: 自动
|
|
||||||
pureOverallStyleSystemTip: 同步时光,界面随晨昏自然呼应
|
|
||||||
pureThemeColor: 主题色
|
|
||||||
pureLayoutModel: 导航模式
|
|
||||||
pureVerticalTip: 左侧菜单,亲切熟悉
|
|
||||||
pureHorizontalTip: 顶部菜单,简洁概览
|
|
||||||
pureMixTip: 混合菜单,灵活多变
|
|
||||||
pureStretch: 页宽
|
|
||||||
pureStretchFixed: 固定
|
|
||||||
pureStretchFixedTip: 紧凑页面,轻松找到所需信息
|
|
||||||
pureStretchCustom: 自定义
|
|
||||||
pureStretchCustomTip: 最小1280、最大1600
|
|
||||||
pureTagsStyle: 页签风格
|
|
||||||
pureTagsStyleSmart: 灵动
|
|
||||||
pureTagsStyleSmartTip: 灵动标签,添趣生辉
|
|
||||||
pureTagsStyleCard: 卡片
|
|
||||||
pureTagsStyleCardTip: 卡片标签,高效浏览
|
|
||||||
pureTagsStyleChrome: 谷歌
|
|
||||||
pureTagsStyleChromeTip: 谷歌风格,经典美观
|
|
||||||
pureInterfaceDisplay: 界面显示
|
|
||||||
pureGreyModel: 灰色模式
|
|
||||||
pureWeakModel: 色弱模式
|
|
||||||
pureHiddenTags: 隐藏标签页
|
|
||||||
pureHiddenFooter: 隐藏页脚
|
|
||||||
pureMultiTagsCache: 页签持久化
|
|
||||||
menus:
|
|
||||||
pureHome: 首页
|
|
||||||
pureLogin: 登录
|
|
||||||
pureAbnormal: 异常页面
|
|
||||||
purePageNotFound: "404"
|
|
||||||
pureAccessDenied: "403"
|
|
||||||
pureServerError: "500"
|
|
||||||
purePermission: 权限管理
|
|
||||||
purePermissionPage: 页面权限
|
|
||||||
purePermissionButton: 按钮权限
|
|
||||||
purePermissionButtonRouter: 路由返回按钮权限
|
|
||||||
purePermissionButtonLogin: 登录接口返回按钮权限
|
|
||||||
status:
|
|
||||||
pureLoad: 加载中...
|
|
||||||
pureMessage: 消息
|
|
||||||
pureNotify: 通知
|
|
||||||
pureTodo: 待办
|
|
||||||
pureNoMessage: 暂无消息
|
|
||||||
pureNoNotify: 暂无通知
|
|
||||||
pureNoTodo: 暂无待办
|
|
||||||
login:
|
|
||||||
pureUsername: 账号
|
|
||||||
purePassword: 密码
|
|
||||||
pureLogin: 登录
|
|
||||||
pureLoginSuccess: 登录成功
|
|
||||||
pureLoginFail: 登录失败
|
|
||||||
pureUsernameReg: 请输入账号
|
|
||||||
purePassWordReg: 请输入密码
|
|
||||||
purePassWordRuleReg: 密码格式应为8-18位数字、字母、符号的任意两种组合
|
|
||||||
@@ -9,7 +9,7 @@ import { defineFakeRoute } from "vite-plugin-fake-server/client";
|
|||||||
const permissionRouter = {
|
const permissionRouter = {
|
||||||
path: "/permission",
|
path: "/permission",
|
||||||
meta: {
|
meta: {
|
||||||
title: "menus.purePermission",
|
title: "权限管理",
|
||||||
icon: "ep:lollipop",
|
icon: "ep:lollipop",
|
||||||
rank: 10
|
rank: 10
|
||||||
},
|
},
|
||||||
@@ -18,14 +18,14 @@ const permissionRouter = {
|
|||||||
path: "/permission/page/index",
|
path: "/permission/page/index",
|
||||||
name: "PermissionPage",
|
name: "PermissionPage",
|
||||||
meta: {
|
meta: {
|
||||||
title: "menus.purePermissionPage",
|
title: "页面权限",
|
||||||
roles: ["admin", "common"]
|
roles: ["admin", "common"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/permission/button",
|
path: "/permission/button",
|
||||||
meta: {
|
meta: {
|
||||||
title: "menus.purePermissionButton",
|
title: "按钮权限",
|
||||||
roles: ["admin", "common"]
|
roles: ["admin", "common"]
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
@@ -34,7 +34,7 @@ const permissionRouter = {
|
|||||||
component: "permission/button/index",
|
component: "permission/button/index",
|
||||||
name: "PermissionButtonRouter",
|
name: "PermissionButtonRouter",
|
||||||
meta: {
|
meta: {
|
||||||
title: "menus.purePermissionButtonRouter",
|
title: "路由返回按钮权限",
|
||||||
auths: [
|
auths: [
|
||||||
"permission:btn:add",
|
"permission:btn:add",
|
||||||
"permission:btn:edit",
|
"permission:btn:edit",
|
||||||
@@ -47,7 +47,7 @@ const permissionRouter = {
|
|||||||
component: "permission/button/perms",
|
component: "permission/button/perms",
|
||||||
name: "PermissionButtonLogin",
|
name: "PermissionButtonLogin",
|
||||||
meta: {
|
meta: {
|
||||||
title: "menus.purePermissionButtonLogin"
|
title: "登录接口返回按钮权限"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
103
package.json
103
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pure-admin-thin",
|
"name": "pure-admin-thin",
|
||||||
"version": "6.2.0",
|
"version": "6.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"vite",
|
"vite",
|
||||||
"esm"
|
"esm"
|
||||||
],
|
],
|
||||||
"homepage": "https://github.com/pure-admin/pure-admin-thin/tree/i18n",
|
"homepage": "https://github.com/pure-admin/pure-admin-thin",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/pure-admin/pure-admin-thin.git"
|
"url": "git+https://github.com/pure-admin/pure-admin-thin.git"
|
||||||
@@ -49,89 +49,87 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pureadmin/descriptions": "^1.2.1",
|
"@pureadmin/descriptions": "^1.2.1",
|
||||||
"@pureadmin/table": "^3.3.0",
|
"@pureadmin/table": "^3.2.1",
|
||||||
"@pureadmin/utils": "^2.6.2",
|
"@pureadmin/utils": "^2.6.0",
|
||||||
"@vueuse/core": "^14.0.0",
|
"@vueuse/core": "^13.1.0",
|
||||||
"@vueuse/motion": "^3.0.3",
|
"@vueuse/motion": "^3.0.3",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"axios": "^1.12.2",
|
"axios": "^1.9.0",
|
||||||
"dayjs": "^1.11.18",
|
"dayjs": "^1.11.13",
|
||||||
"echarts": "^6.0.0",
|
"echarts": "^5.6.0",
|
||||||
"element-plus": "^2.11.5",
|
"element-plus": "^2.9.8",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.2",
|
||||||
"pinyin-pro": "^3.27.0",
|
"pinyin-pro": "^3.26.0",
|
||||||
"qs": "^6.14.0",
|
"qs": "^6.14.0",
|
||||||
"responsive-storage": "^2.2.0",
|
"responsive-storage": "^2.2.0",
|
||||||
"sortablejs": "^1.15.6",
|
"sortablejs": "^1.15.6",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.13",
|
||||||
"vue-i18n": "^11.1.12",
|
"vue-router": "^4.5.0",
|
||||||
"vue-router": "^4.6.3",
|
"vue-tippy": "^6.7.0",
|
||||||
"vue-tippy": "^6.7.1",
|
|
||||||
"vue-types": "^6.0.0"
|
"vue-types": "^6.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^20.1.0",
|
"@commitlint/cli": "^19.8.0",
|
||||||
"@commitlint/config-conventional": "^20.0.0",
|
"@commitlint/config-conventional": "^19.8.0",
|
||||||
"@commitlint/types": "^20.0.0",
|
"@commitlint/types": "^19.8.0",
|
||||||
"@eslint/js": "^9.38.0",
|
"@eslint/js": "^9.25.1",
|
||||||
"@faker-js/faker": "^10.1.0",
|
"@faker-js/faker": "^9.7.0",
|
||||||
"@iconify/json": "^2.2.400",
|
"@iconify/json": "^2.2.331",
|
||||||
"@iconify/vue": "4.2.0",
|
"@iconify/vue": "4.2.0",
|
||||||
"@intlify/unplugin-vue-i18n": "^11.0.1",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
"@tailwindcss/vite": "^4.1.16",
|
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/node": "^20.19.23",
|
"@types/node": "^20.17.30",
|
||||||
"@types/nprogress": "^0.2.3",
|
"@types/nprogress": "^0.2.3",
|
||||||
"@types/path-browserify": "^1.0.3",
|
"@types/path-browserify": "^1.0.3",
|
||||||
"@types/qs": "^6.14.0",
|
"@types/qs": "^6.9.18",
|
||||||
"@types/sortablejs": "^1.15.9",
|
"@types/sortablejs": "^1.15.8",
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^5.2.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^5.1.1",
|
"@vitejs/plugin-vue-jsx": "^4.1.2",
|
||||||
"boxen": "^8.0.1",
|
"boxen": "^8.0.1",
|
||||||
"code-inspector-plugin": "^1.2.10",
|
"code-inspector-plugin": "^0.20.10",
|
||||||
"cssnano": "^7.1.1",
|
"cssnano": "^7.0.6",
|
||||||
"eslint": "^9.38.0",
|
"eslint": "^9.25.1",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.2",
|
||||||
"eslint-plugin-prettier": "^5.5.4",
|
"eslint-plugin-prettier": "^5.2.6",
|
||||||
"eslint-plugin-vue": "^10.5.1",
|
"eslint-plugin-vue": "^10.0.0",
|
||||||
"gradient-string": "^3.0.0",
|
"gradient-string": "^3.0.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"lint-staged": "^16.2.6",
|
"lint-staged": "^15.5.1",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.3",
|
||||||
"postcss-html": "^1.8.0",
|
"postcss-html": "^1.8.0",
|
||||||
"postcss-load-config": "^6.0.1",
|
"postcss-load-config": "^6.0.1",
|
||||||
"postcss-scss": "^4.0.9",
|
"postcss-scss": "^4.0.9",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.5.3",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"rollup-plugin-visualizer": "^6.0.5",
|
"rollup-plugin-visualizer": "^5.14.0",
|
||||||
"sass": "^1.93.2",
|
"sass": "^1.87.0",
|
||||||
"stylelint": "^16.25.0",
|
"stylelint": "^16.19.0",
|
||||||
"stylelint-config-recess-order": "^7.4.0",
|
"stylelint-config-recess-order": "^6.0.0",
|
||||||
"stylelint-config-recommended-vue": "^1.6.1",
|
"stylelint-config-recommended-vue": "^1.6.0",
|
||||||
"stylelint-config-standard-scss": "^14.0.0",
|
"stylelint-config-standard-scss": "^14.0.0",
|
||||||
"stylelint-prettier": "^5.0.3",
|
"stylelint-prettier": "^5.0.3",
|
||||||
"svgo": "^4.0.0",
|
"svgo": "^3.3.2",
|
||||||
"tailwindcss": "^4.1.16",
|
"tailwindcss": "^4.1.4",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.46.2",
|
"typescript-eslint": "^8.31.0",
|
||||||
"unplugin-icons": "^22.5.0",
|
"unplugin-icons": "^22.1.0",
|
||||||
"vite": "^7.1.12",
|
"vite": "^6.3.3",
|
||||||
"vite-plugin-cdn-import": "^1.0.1",
|
"vite-plugin-cdn-import": "^1.0.1",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-fake-server": "^2.2.0",
|
"vite-plugin-fake-server": "^2.2.0",
|
||||||
"vite-plugin-remove-console": "^2.2.0",
|
"vite-plugin-remove-console": "^2.2.0",
|
||||||
"vite-plugin-router-warn": "^1.0.0",
|
"vite-plugin-router-warn": "^1.0.0",
|
||||||
"vite-svg-loader": "^5.1.0",
|
"vite-svg-loader": "^5.1.0",
|
||||||
"vue-eslint-parser": "^10.2.0",
|
"vue-eslint-parser": "^10.1.3",
|
||||||
"vue-tsc": "^3.1.2"
|
"vue-tsc": "^2.2.10"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || >=22.13.0",
|
"node": "^18.18.0 || ^20.9.0 || >=22.0.0",
|
||||||
"pnpm": ">=9"
|
"pnpm": ">=9"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
@@ -156,9 +154,6 @@
|
|||||||
"esbuild",
|
"esbuild",
|
||||||
"typeit",
|
"typeit",
|
||||||
"vue-demi"
|
"vue-demi"
|
||||||
],
|
|
||||||
"ignoredBuiltDependencies": [
|
|
||||||
"@tailwindcss/oxide"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4544
pnpm-lock.yaml
generated
4544
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,10 @@
|
|||||||
{
|
{
|
||||||
"Version": "6.2.0",
|
"Version": "6.0.0",
|
||||||
"Title": "PureAdmin",
|
"Title": "PureAdmin",
|
||||||
"FixedHeader": true,
|
"FixedHeader": true,
|
||||||
"HiddenSideBar": false,
|
"HiddenSideBar": false,
|
||||||
"MultiTagsCache": false,
|
"MultiTagsCache": false,
|
||||||
"KeepAlive": true,
|
"KeepAlive": true,
|
||||||
"Locale": "zh",
|
|
||||||
"Layout": "vertical",
|
"Layout": "vertical",
|
||||||
"Theme": "light",
|
"Theme": "light",
|
||||||
"DarkMode": false,
|
"DarkMode": false,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { ElConfigProvider } from "element-plus";
|
import { ElConfigProvider } from "element-plus";
|
||||||
import { ReDialog } from "@/components/ReDialog";
|
import { ReDialog } from "@/components/ReDialog";
|
||||||
import en from "element-plus/es/locale/lang/en";
|
|
||||||
import zhCn from "element-plus/es/locale/lang/zh-cn";
|
import zhCn from "element-plus/es/locale/lang/zh-cn";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -20,7 +19,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
currentLocale() {
|
currentLocale() {
|
||||||
return this.$storage.locale?.locale === "zh" ? zhCn : en;
|
return zhCn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" class="globalization" viewBox="0 0 512 512"><path fill="currentColor" d="m478.33 433.6-90-218a22 22 0 0 0-40.67 0l-90 218a22 22 0 1 0 40.67 16.79L316.66 406h102.67l18.33 44.39A22 22 0 0 0 458 464a22 22 0 0 0 20.32-30.4zM334.83 362 368 281.65 401.17 362zm-66.99-19.08a22 22 0 0 0-4.89-30.7c-.2-.15-15-11.13-36.49-34.73 39.65-53.68 62.11-114.75 71.27-143.49H330a22 22 0 0 0 0-44H214V70a22 22 0 0 0-44 0v20H54a22 22 0 0 0 0 44h197.25c-9.52 26.95-27.05 69.5-53.79 108.36-31.41-41.68-43.08-68.65-43.17-68.87a22 22 0 0 0-40.58 17c.58 1.38 14.55 34.23 52.86 83.93.92 1.19 1.83 2.35 2.74 3.51-39.24 44.35-77.74 71.86-93.85 80.74a22 22 0 1 0 21.07 38.63c2.16-1.18 48.6-26.89 101.63-85.59 22.52 24.08 38 35.44 38.93 36.1a22 22 0 0 0 30.75-4.9z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 807 B |
@@ -1,5 +1,4 @@
|
|||||||
import Sortable from "sortablejs";
|
import Sortable from "sortablejs";
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
||||||
import {
|
import {
|
||||||
type PropType,
|
type PropType,
|
||||||
@@ -148,9 +147,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleCheckColumnListChange(val: boolean, label: string) {
|
function handleCheckColumnListChange(val: boolean, label: string) {
|
||||||
dynamicColumns.value.filter(
|
dynamicColumns.value.filter(item => item.label === label)[0].hide = !val;
|
||||||
item => transformI18n(item.label) === transformI18n(label)
|
|
||||||
)[0].hide = !val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onReset() {
|
async function onReset() {
|
||||||
@@ -223,9 +220,7 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isFixedColumn = (label: string) => {
|
const isFixedColumn = (label: string) => {
|
||||||
return dynamicColumns.value.filter(
|
return dynamicColumns.value.filter(item => item.label === label)[0].fixed
|
||||||
item => transformI18n(item.label) === transformI18n(label)
|
|
||||||
)[0].fixed
|
|
||||||
? true
|
? true
|
||||||
: false;
|
: false;
|
||||||
};
|
};
|
||||||
@@ -363,10 +358,10 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
title={transformI18n(item)}
|
title={item}
|
||||||
class="inline-block w-[120px] truncate hover:text-text_color_primary"
|
class="inline-block w-[120px] truncate hover:text-text_color_primary"
|
||||||
>
|
>
|
||||||
{transformI18n(item)}
|
{item}
|
||||||
</span>
|
</span>
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ $ripple-animation-visible-opacity: 0.25 !default;
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
contain: strict;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
|
contain: strict;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__animation {
|
&__animation {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import LayFrame from "../lay-frame/index.vue";
|
import LayFrame from "../lay-frame/index.vue";
|
||||||
import LayFooter from "../lay-footer/index.vue";
|
import LayFooter from "../lay-footer/index.vue";
|
||||||
import { useTags } from "@/layout/hooks/useTag";
|
import { useTags } from "@/layout/hooks/useTag";
|
||||||
@@ -12,7 +11,6 @@ const props = defineProps({
|
|||||||
fixedHeader: Boolean
|
fixedHeader: Boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { showModel } = useTags();
|
const { showModel } = useTags();
|
||||||
const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
|
const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
|
||||||
|
|
||||||
@@ -133,7 +131,7 @@ const transitionMain = defineComponent({
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<el-backtop
|
<el-backtop
|
||||||
:title="t('buttons.pureBackTop')"
|
title="回到顶部"
|
||||||
target=".app-main .el-scrollbar__wrap"
|
target=".app-main .el-scrollbar__wrap"
|
||||||
>
|
>
|
||||||
<BackTopIcon />
|
<BackTopIcon />
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const TITLE = getConfig("Title");
|
|||||||
>
|
>
|
||||||
Copyright © 2020-present
|
Copyright © 2020-present
|
||||||
<a
|
<a
|
||||||
class="hover:text-primary!"
|
class="hover:text-primary"
|
||||||
href="https://github.com/pure-admin"
|
href="https://github.com/pure-admin"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -3,15 +3,12 @@ import { useNav } from "@/layout/hooks/useNav";
|
|||||||
import LaySearch from "../lay-search/index.vue";
|
import LaySearch from "../lay-search/index.vue";
|
||||||
import LayNotice from "../lay-notice/index.vue";
|
import LayNotice from "../lay-notice/index.vue";
|
||||||
import LayNavMix from "../lay-sidebar/NavMix.vue";
|
import LayNavMix from "../lay-sidebar/NavMix.vue";
|
||||||
import { useTranslationLang } from "@/layout/hooks/useTranslationLang";
|
|
||||||
import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue";
|
import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue";
|
||||||
import LaySidebarBreadCrumb from "../lay-sidebar/components/SidebarBreadCrumb.vue";
|
import LaySidebarBreadCrumb from "../lay-sidebar/components/SidebarBreadCrumb.vue";
|
||||||
import LaySidebarTopCollapse from "../lay-sidebar/components/SidebarTopCollapse.vue";
|
import LaySidebarTopCollapse from "../lay-sidebar/components/SidebarTopCollapse.vue";
|
||||||
|
|
||||||
import GlobalizationIcon from "@/assets/svg/globalization.svg?component";
|
|
||||||
import LogoutCircleRLine from "~icons/ri/logout-circle-r-line";
|
import LogoutCircleRLine from "~icons/ri/logout-circle-r-line";
|
||||||
import Setting from "~icons/ri/settings-3-line";
|
import Setting from "~icons/ri/settings-3-line";
|
||||||
import Check from "~icons/ep/check";
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
layout,
|
layout,
|
||||||
@@ -22,12 +19,8 @@ const {
|
|||||||
username,
|
username,
|
||||||
userAvatar,
|
userAvatar,
|
||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
toggleSideBar,
|
toggleSideBar
|
||||||
getDropdownItemStyle,
|
|
||||||
getDropdownItemClass
|
|
||||||
} = useNav();
|
} = useNav();
|
||||||
|
|
||||||
const { t, locale, translationCh, translationEn } = useTranslationLang();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -49,38 +42,6 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
|
|||||||
<div v-if="layout === 'vertical'" class="vertical-header-right">
|
<div v-if="layout === 'vertical'" class="vertical-header-right">
|
||||||
<!-- 菜单搜索 -->
|
<!-- 菜单搜索 -->
|
||||||
<LaySearch id="header-search" />
|
<LaySearch id="header-search" />
|
||||||
<!-- 国际化 -->
|
|
||||||
<el-dropdown id="header-translation" trigger="click">
|
|
||||||
<GlobalizationIcon
|
|
||||||
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-hidden"
|
|
||||||
/>
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu class="translation">
|
|
||||||
<el-dropdown-item
|
|
||||||
:style="getDropdownItemStyle(locale, 'zh')"
|
|
||||||
:class="['dark:text-white!', getDropdownItemClass(locale, 'zh')]"
|
|
||||||
@click="translationCh"
|
|
||||||
>
|
|
||||||
<IconifyIconOffline
|
|
||||||
v-show="locale === 'zh'"
|
|
||||||
class="check-zh"
|
|
||||||
:icon="Check"
|
|
||||||
/>
|
|
||||||
简体中文
|
|
||||||
</el-dropdown-item>
|
|
||||||
<el-dropdown-item
|
|
||||||
:style="getDropdownItemStyle(locale, 'en')"
|
|
||||||
:class="['dark:text-white!', getDropdownItemClass(locale, 'en')]"
|
|
||||||
@click="translationEn"
|
|
||||||
>
|
|
||||||
<span v-show="locale === 'en'" class="check-en">
|
|
||||||
<IconifyIconOffline :icon="Check" />
|
|
||||||
</span>
|
|
||||||
English
|
|
||||||
</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
<!-- 全屏 -->
|
<!-- 全屏 -->
|
||||||
<LaySidebarFullScreen id="full-screen" />
|
<LaySidebarFullScreen id="full-screen" />
|
||||||
<!-- 消息通知 -->
|
<!-- 消息通知 -->
|
||||||
@@ -98,14 +59,14 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
|
|||||||
:icon="LogoutCircleRLine"
|
:icon="LogoutCircleRLine"
|
||||||
style="margin: 5px"
|
style="margin: 5px"
|
||||||
/>
|
/>
|
||||||
{{ t("buttons.pureLoginOut") }}
|
退出系统
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
<span
|
<span
|
||||||
class="set-icon navbar-bg-hover"
|
class="set-icon navbar-bg-hover"
|
||||||
:title="t('buttons.pureOpenSystemSet')"
|
title="打开系统配置"
|
||||||
@click="onPanel"
|
@click="onPanel"
|
||||||
>
|
>
|
||||||
<IconifyIconOffline :icon="Setting" />
|
<IconifyIconOffline :icon="Setting" />
|
||||||
@@ -162,22 +123,6 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.translation {
|
|
||||||
::v-deep(.el-dropdown-menu__item) {
|
|
||||||
padding: 5px 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.check-zh {
|
|
||||||
position: absolute;
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.check-en {
|
|
||||||
position: absolute;
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.logout {
|
.logout {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import { PropType } from "vue";
|
import { PropType } from "vue";
|
||||||
import { ListItem } from "../data";
|
import { ListItem } from "../data";
|
||||||
import NoticeItem from "./NoticeItem.vue";
|
import NoticeItem from "./NoticeItem.vue";
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
list: {
|
list: {
|
||||||
@@ -20,5 +19,5 @@ defineProps({
|
|||||||
<div v-if="list.length">
|
<div v-if="list.length">
|
||||||
<NoticeItem v-for="(item, index) in list" :key="index" :noticeItem="item" />
|
<NoticeItem v-for="(item, index) in list" :key="index" :noticeItem="item" />
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-else :description="transformI18n(emptyText)" />
|
<el-empty v-else :description="emptyText" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { $t } from "@/plugins/i18n";
|
|
||||||
|
|
||||||
export interface ListItem {
|
export interface ListItem {
|
||||||
avatar: string;
|
avatar: string;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -20,13 +18,13 @@ export interface TabItem {
|
|||||||
export const noticesData: TabItem[] = [
|
export const noticesData: TabItem[] = [
|
||||||
{
|
{
|
||||||
key: "1",
|
key: "1",
|
||||||
name: $t("status.pureNotify"),
|
name: "通知",
|
||||||
list: [],
|
list: [],
|
||||||
emptyText: $t("status.pureNoNotify")
|
emptyText: "暂无通知"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "2",
|
key: "2",
|
||||||
name: $t("status.pureMessage"),
|
name: "消息",
|
||||||
list: [
|
list: [
|
||||||
{
|
{
|
||||||
avatar: "https://xiaoxian521.github.io/hyperlink/svg/smile1.svg",
|
avatar: "https://xiaoxian521.github.io/hyperlink/svg/smile1.svg",
|
||||||
@@ -51,11 +49,11 @@ export const noticesData: TabItem[] = [
|
|||||||
type: "2"
|
type: "2"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
emptyText: $t("status.pureNoMessage")
|
emptyText: "暂无消息"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "3",
|
key: "3",
|
||||||
name: $t("status.pureTodo"),
|
name: "待办",
|
||||||
list: [
|
list: [
|
||||||
{
|
{
|
||||||
avatar: "",
|
avatar: "",
|
||||||
@@ -94,6 +92,6 @@ export const noticesData: TabItem[] = [
|
|||||||
type: "3"
|
type: "3"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
emptyText: $t("status.pureNoTodo")
|
emptyText: "暂无待办"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import { noticesData } from "./data";
|
import { noticesData } from "./data";
|
||||||
import NoticeList from "./components/NoticeList.vue";
|
import NoticeList from "./components/NoticeList.vue";
|
||||||
import BellIcon from "~icons/ep/bell";
|
import BellIcon from "~icons/ep/bell";
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const noticesNum = ref(0);
|
const noticesNum = ref(0);
|
||||||
const notices = ref(noticesData);
|
const notices = ref(noticesData);
|
||||||
const activeKey = ref(noticesData[0]?.key);
|
const activeKey = ref(noticesData[0]?.key);
|
||||||
@@ -14,7 +12,7 @@ notices.value.map(v => (noticesNum.value += v.list.length));
|
|||||||
|
|
||||||
const getLabel = computed(
|
const getLabel = computed(
|
||||||
() => item =>
|
() => item =>
|
||||||
t(item.name) + (item.list.length > 0 ? `(${item.list.length})` : "")
|
item.name + (item.list.length > 0 ? `(${item.list.length})` : "")
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -44,7 +42,7 @@ const getLabel = computed(
|
|||||||
>
|
>
|
||||||
<el-empty
|
<el-empty
|
||||||
v-if="notices.length === 0"
|
v-if="notices.length === 0"
|
||||||
:description="t('status.pureNoMessage')"
|
description="暂无消息"
|
||||||
:image-size="60"
|
:image-size="60"
|
||||||
/>
|
/>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from "@/utils/mitt";
|
||||||
import { onClickOutside } from "@vueuse/core";
|
import { onClickOutside } from "@vueuse/core";
|
||||||
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
|
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
|
||||||
@@ -26,7 +25,6 @@ const iconClass = computed(() => {
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { onReset } = useDataThemeChange();
|
const { onReset } = useDataThemeChange();
|
||||||
|
|
||||||
onClickOutside(target, (event: any) => {
|
onClickOutside(target, (event: any) => {
|
||||||
@@ -53,12 +51,10 @@ onBeforeUnmount(() => {
|
|||||||
<div
|
<div
|
||||||
class="project-configuration border-0 border-b-[1px] border-solid border-[var(--pure-border-color)]"
|
class="project-configuration border-0 border-b-[1px] border-solid border-[var(--pure-border-color)]"
|
||||||
>
|
>
|
||||||
<h4 class="dark:text-white">
|
<h4 class="dark:text-white">系统配置</h4>
|
||||||
{{ t("panel.pureSystemSet") }}
|
|
||||||
</h4>
|
|
||||||
<span
|
<span
|
||||||
v-tippy="{
|
v-tippy="{
|
||||||
content: t('panel.pureCloseSystemSet'),
|
content: '关闭配置',
|
||||||
placement: 'bottom-start',
|
placement: 'bottom-start',
|
||||||
zIndex: 41000
|
zIndex: 41000
|
||||||
}"
|
}"
|
||||||
@@ -82,7 +78,7 @@ onBeforeUnmount(() => {
|
|||||||
>
|
>
|
||||||
<el-button
|
<el-button
|
||||||
v-tippy="{
|
v-tippy="{
|
||||||
content: t('panel.pureClearCacheAndToLogin'),
|
content: '清空缓存并返回登录页',
|
||||||
placement: 'left-start',
|
placement: 'left-start',
|
||||||
zIndex: 41000
|
zIndex: 41000
|
||||||
}"
|
}"
|
||||||
@@ -91,7 +87,7 @@ onBeforeUnmount(() => {
|
|||||||
bg
|
bg
|
||||||
@click="onReset"
|
@click="onReset"
|
||||||
>
|
>
|
||||||
{{ t("panel.pureClearCache") }}
|
清空缓存
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from "@/layout/hooks/useNav";
|
||||||
import MdiKeyboardEsc from "@/assets/svg/keyboard_esc.svg?component";
|
import MdiKeyboardEsc from "@/assets/svg/keyboard_esc.svg?component";
|
||||||
import EnterOutlined from "@/assets/svg/enter_outlined.svg?component";
|
import EnterOutlined from "@/assets/svg/enter_outlined.svg?component";
|
||||||
@@ -10,7 +9,6 @@ withDefaults(defineProps<{ total?: number }>(), {
|
|||||||
total: 0
|
total: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { device } = useNav();
|
const { device } = useNav();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -18,19 +16,19 @@ const { device } = useNav();
|
|||||||
<div class="search-footer text-[#333] dark:text-white">
|
<div class="search-footer text-[#333] dark:text-white">
|
||||||
<span class="search-footer-item">
|
<span class="search-footer-item">
|
||||||
<EnterOutlined class="icon" />
|
<EnterOutlined class="icon" />
|
||||||
{{ t("buttons.pureConfirm") }}
|
确认
|
||||||
</span>
|
</span>
|
||||||
<span class="search-footer-item">
|
<span class="search-footer-item">
|
||||||
<IconifyIconOffline :icon="ArrowUpLine" class="icon" />
|
<IconifyIconOffline :icon="ArrowUpLine" class="icon" />
|
||||||
<IconifyIconOffline :icon="ArrowDownLine" class="icon" />
|
<IconifyIconOffline :icon="ArrowDownLine" class="icon" />
|
||||||
{{ t("buttons.pureSwitch") }}
|
切换
|
||||||
</span>
|
</span>
|
||||||
<span class="search-footer-item">
|
<span class="search-footer-item">
|
||||||
<MdiKeyboardEsc class="icon" />
|
<MdiKeyboardEsc class="icon" />
|
||||||
{{ t("buttons.pureClose") }}
|
关闭
|
||||||
</span>
|
</span>
|
||||||
<p v-if="device !== 'mobile' && total > 0" class="search-footer-total">
|
<p v-if="device !== 'mobile' && total > 0" class="search-footer-total">
|
||||||
{{ `${t("search.pureTotal")} ${total}` }}
|
{{ `共 ${total} 项` }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Sortable from "sortablejs";
|
import Sortable from "sortablejs";
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import SearchHistoryItem from "./SearchHistoryItem.vue";
|
import SearchHistoryItem from "./SearchHistoryItem.vue";
|
||||||
import type { optionsItem, dragItem, Props } from "../types";
|
import type { optionsItem, dragItem, Props } from "../types";
|
||||||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
||||||
@@ -20,7 +19,6 @@ const innerHeight = ref();
|
|||||||
/** 判断是否停止鼠标移入事件处理 */
|
/** 判断是否停止鼠标移入事件处理 */
|
||||||
const stopMouseEvent = ref(false);
|
const stopMouseEvent = ref(false);
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
const instance = getCurrentInstance()!;
|
const instance = getCurrentInstance()!;
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {});
|
||||||
@@ -143,9 +141,7 @@ defineExpose({ handleScroll });
|
|||||||
<template>
|
<template>
|
||||||
<div ref="historyRef" class="history">
|
<div ref="historyRef" class="history">
|
||||||
<template v-if="historyList.length">
|
<template v-if="historyList.length">
|
||||||
<div :style="titleStyle">
|
<div :style="titleStyle">搜索历史</div>
|
||||||
{{ t("search.pureHistory") }}
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in historyList"
|
v-for="(item, index) in historyList"
|
||||||
:key="item.path"
|
:key="item.path"
|
||||||
@@ -164,9 +160,7 @@ defineExpose({ handleScroll });
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="collectList.length">
|
<template v-if="collectList.length">
|
||||||
<div :style="titleStyle">
|
<div :style="titleStyle">
|
||||||
{{
|
{{ `收藏${collectList.length > 1 ? "(可拖拽排序)" : ""}` }}
|
||||||
`${t("search.pureCollect")}${collectList.length > 1 ? t("search.pureDragSort") : ""}`
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="collect-container">
|
<div class="collect-container">
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { optionsItem } from "../types";
|
import type { optionsItem } from "../types";
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
import StarIcon from "~icons/ep/star";
|
import StarIcon from "~icons/ep/star";
|
||||||
import CloseIcon from "~icons/ep/close";
|
import CloseIcon from "~icons/ep/close";
|
||||||
@@ -29,7 +28,7 @@ function handleDelete(item) {
|
|||||||
<template>
|
<template>
|
||||||
<component :is="useRenderIcon(item.meta?.icon)" />
|
<component :is="useRenderIcon(item.meta?.icon)" />
|
||||||
<span class="history-item-title">
|
<span class="history-item-title">
|
||||||
{{ transformI18n(item.meta?.title) }}
|
{{ item.meta?.title }}
|
||||||
</span>
|
</span>
|
||||||
<IconifyIconOffline
|
<IconifyIconOffline
|
||||||
v-show="item.type === 'history'"
|
v-show="item.type === 'history'"
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { match } from "pinyin-pro";
|
import { match } from "pinyin-pro";
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from "@/config";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import SearchResult from "./SearchResult.vue";
|
import SearchResult from "./SearchResult.vue";
|
||||||
import SearchFooter from "./SearchFooter.vue";
|
import SearchFooter from "./SearchFooter.vue";
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from "@/layout/hooks/useNav";
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import SearchHistory from "./SearchHistory.vue";
|
import SearchHistory from "./SearchHistory.vue";
|
||||||
import type { optionsItem, dragItem } from "../types";
|
import type { optionsItem, dragItem } from "../types";
|
||||||
import { ref, computed, shallowRef, watch } from "vue";
|
import { ref, computed, shallowRef, watch } from "vue";
|
||||||
@@ -29,7 +27,6 @@ const emit = defineEmits<Emits>();
|
|||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t, locale } = useI18n();
|
|
||||||
|
|
||||||
const HISTORY_TYPE = "history";
|
const HISTORY_TYPE = "history";
|
||||||
const COLLECT_TYPE = "collect";
|
const COLLECT_TYPE = "collect";
|
||||||
@@ -110,16 +107,15 @@ function search() {
|
|||||||
const flatMenusData = flatTree(menusData.value);
|
const flatMenusData = flatTree(menusData.value);
|
||||||
resultOptions.value = flatMenusData.filter(menu =>
|
resultOptions.value = flatMenusData.filter(menu =>
|
||||||
keyword.value
|
keyword.value
|
||||||
? transformI18n(menu.meta?.title)
|
? menu.meta?.title
|
||||||
.toLocaleLowerCase()
|
.toLocaleLowerCase()
|
||||||
.includes(keyword.value.toLocaleLowerCase().trim()) ||
|
.includes(keyword.value.toLocaleLowerCase().trim()) ||
|
||||||
(locale.value === "zh" &&
|
!isAllEmpty(
|
||||||
!isAllEmpty(
|
match(
|
||||||
match(
|
menu.meta?.title.toLocaleLowerCase(),
|
||||||
transformI18n(menu.meta?.title).toLocaleLowerCase(),
|
keyword.value.toLocaleLowerCase().trim()
|
||||||
keyword.value.toLocaleLowerCase().trim()
|
)
|
||||||
)
|
)
|
||||||
))
|
|
||||||
: false
|
: false
|
||||||
);
|
);
|
||||||
activePath.value =
|
activePath.value =
|
||||||
@@ -293,7 +289,7 @@ onKeyStroke("ArrowDown", handleDown);
|
|||||||
v-model="keyword"
|
v-model="keyword"
|
||||||
size="large"
|
size="large"
|
||||||
clearable
|
clearable
|
||||||
:placeholder="t('search.purePlaceholder')"
|
placeholder="搜索菜单(支持拼音搜索)"
|
||||||
@input="handleSearch"
|
@input="handleSearch"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
@@ -305,7 +301,7 @@ onKeyStroke("ArrowDown", handleDown);
|
|||||||
</el-input>
|
</el-input>
|
||||||
<div class="search-content">
|
<div class="search-content">
|
||||||
<el-scrollbar ref="scrollbarRef" max-height="calc(90vh - 140px)">
|
<el-scrollbar ref="scrollbarRef" max-height="calc(90vh - 140px)">
|
||||||
<el-empty v-if="showEmpty" :description="t('search.pureEmpty')" />
|
<el-empty v-if="showEmpty" description="暂无搜索结果" />
|
||||||
<SearchHistory
|
<SearchHistory
|
||||||
v-if="showSearchHistory"
|
v-if="showSearchHistory"
|
||||||
ref="historyRef"
|
ref="historyRef"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Props } from "../types";
|
import type { Props } from "../types";
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import { useResizeObserver } from "@pureadmin/utils";
|
import { useResizeObserver } from "@pureadmin/utils";
|
||||||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
@@ -82,7 +81,7 @@ defineExpose({ handleScroll });
|
|||||||
>
|
>
|
||||||
<component :is="useRenderIcon(item.meta?.icon)" />
|
<component :is="useRenderIcon(item.meta?.icon)" />
|
||||||
<span class="result-item-title">
|
<span class="result-item-title">
|
||||||
{{ transformI18n(item.meta?.title) }}
|
{{ item.meta?.title }}
|
||||||
</span>
|
</span>
|
||||||
<EnterOutlined />
|
<EnterOutlined />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
onUnmounted,
|
onUnmounted,
|
||||||
onBeforeMount
|
onBeforeMount
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from "@/utils/mitt";
|
||||||
import LayPanel from "../lay-panel/index.vue";
|
import LayPanel from "../lay-panel/index.vue";
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from "@/layout/hooks/useNav";
|
||||||
@@ -26,7 +25,6 @@ import DayIcon from "@/assets/svg/day.svg?component";
|
|||||||
import DarkIcon from "@/assets/svg/dark.svg?component";
|
import DarkIcon from "@/assets/svg/dark.svg?component";
|
||||||
import SystemIcon from "@/assets/svg/system.svg?component";
|
import SystemIcon from "@/assets/svg/system.svg?component";
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { device } = useNav();
|
const { device } = useNav();
|
||||||
const { isDark } = useDark();
|
const { isDark } = useDark();
|
||||||
const { $storage } = useGlobal<GlobalPropertiesApi>();
|
const { $storage } = useGlobal<GlobalPropertiesApi>();
|
||||||
@@ -147,13 +145,13 @@ function setFalse(Doms): any {
|
|||||||
const stretchTypeOptions = computed<Array<OptionsType>>(() => {
|
const stretchTypeOptions = computed<Array<OptionsType>>(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: t("panel.pureStretchFixed"),
|
label: "固定",
|
||||||
tip: t("panel.pureStretchFixedTip"),
|
tip: "紧凑页面,轻松找到所需信息",
|
||||||
value: "fixed"
|
value: "fixed"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t("panel.pureStretchCustom"),
|
label: "自定义",
|
||||||
tip: t("panel.pureStretchCustomTip"),
|
tip: "最小1280、最大1600",
|
||||||
value: "custom"
|
value: "custom"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -195,24 +193,24 @@ const pClass = computed(() => {
|
|||||||
const themeOptions = computed<Array<OptionsType>>(() => {
|
const themeOptions = computed<Array<OptionsType>>(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: t("panel.pureOverallStyleLight"),
|
label: "浅色",
|
||||||
icon: DayIcon,
|
icon: DayIcon,
|
||||||
theme: "light",
|
theme: "light",
|
||||||
tip: t("panel.pureOverallStyleLightTip"),
|
tip: "清新启航,点亮舒适的工作界面",
|
||||||
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
|
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t("panel.pureOverallStyleDark"),
|
label: "深色",
|
||||||
icon: DarkIcon,
|
icon: DarkIcon,
|
||||||
theme: "dark",
|
theme: "dark",
|
||||||
tip: t("panel.pureOverallStyleDarkTip"),
|
tip: "月光序曲,沉醉于夜的静谧雅致",
|
||||||
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
|
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t("panel.pureOverallStyleSystem"),
|
label: "自动",
|
||||||
icon: SystemIcon,
|
icon: SystemIcon,
|
||||||
theme: "system",
|
theme: "system",
|
||||||
tip: t("panel.pureOverallStyleSystemTip"),
|
tip: "同步时光,界面随晨昏自然呼应",
|
||||||
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
|
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -221,18 +219,18 @@ const themeOptions = computed<Array<OptionsType>>(() => {
|
|||||||
const markOptions = computed<Array<OptionsType>>(() => {
|
const markOptions = computed<Array<OptionsType>>(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: t("panel.pureTagsStyleSmart"),
|
label: "灵动",
|
||||||
tip: t("panel.pureTagsStyleSmartTip"),
|
tip: "灵动标签,添趣生辉",
|
||||||
value: "smart"
|
value: "smart"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t("panel.pureTagsStyleCard"),
|
label: "卡片",
|
||||||
tip: t("panel.pureTagsStyleCardTip"),
|
tip: "卡片标签,高效浏览",
|
||||||
value: "card"
|
value: "card"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t("panel.pureTagsStyleChrome"),
|
label: "谷歌",
|
||||||
tip: t("panel.pureTagsStyleChromeTip"),
|
tip: "谷歌风格,经典美观",
|
||||||
value: "chrome"
|
value: "chrome"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -317,7 +315,7 @@ onUnmounted(() => removeMatchMedia);
|
|||||||
<template>
|
<template>
|
||||||
<LayPanel>
|
<LayPanel>
|
||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
<p :class="pClass">{{ t("panel.pureOverallStyle") }}</p>
|
<p :class="pClass">整体风格</p>
|
||||||
<Segmented
|
<Segmented
|
||||||
resize
|
resize
|
||||||
class="select-none"
|
class="select-none"
|
||||||
@@ -335,7 +333,7 @@ onUnmounted(() => removeMatchMedia);
|
|||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p :class="['mt-5!', pClass]">{{ t("panel.pureThemeColor") }}</p>
|
<p :class="['mt-5!', pClass]">主题色</p>
|
||||||
<ul class="theme-color">
|
<ul class="theme-color">
|
||||||
<li
|
<li
|
||||||
v-for="(item, index) in themeColors"
|
v-for="(item, index) in themeColors"
|
||||||
@@ -354,12 +352,12 @@ onUnmounted(() => removeMatchMedia);
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p :class="['mt-5!', pClass]">{{ t("panel.pureLayoutModel") }}</p>
|
<p :class="['mt-5!', pClass]">导航模式</p>
|
||||||
<ul class="pure-theme">
|
<ul class="pure-theme">
|
||||||
<li
|
<li
|
||||||
ref="verticalRef"
|
ref="verticalRef"
|
||||||
v-tippy="{
|
v-tippy="{
|
||||||
content: t('panel.pureVerticalTip'),
|
content: '左侧菜单,亲切熟悉',
|
||||||
zIndex: 41000
|
zIndex: 41000
|
||||||
}"
|
}"
|
||||||
:class="layoutTheme.layout === 'vertical' ? 'is-select' : ''"
|
:class="layoutTheme.layout === 'vertical' ? 'is-select' : ''"
|
||||||
@@ -372,7 +370,7 @@ onUnmounted(() => removeMatchMedia);
|
|||||||
v-if="device !== 'mobile'"
|
v-if="device !== 'mobile'"
|
||||||
ref="horizontalRef"
|
ref="horizontalRef"
|
||||||
v-tippy="{
|
v-tippy="{
|
||||||
content: t('panel.pureHorizontalTip'),
|
content: '顶部菜单,简洁概览',
|
||||||
zIndex: 41000
|
zIndex: 41000
|
||||||
}"
|
}"
|
||||||
:class="layoutTheme.layout === 'horizontal' ? 'is-select' : ''"
|
:class="layoutTheme.layout === 'horizontal' ? 'is-select' : ''"
|
||||||
@@ -385,7 +383,7 @@ onUnmounted(() => removeMatchMedia);
|
|||||||
v-if="device !== 'mobile'"
|
v-if="device !== 'mobile'"
|
||||||
ref="mixRef"
|
ref="mixRef"
|
||||||
v-tippy="{
|
v-tippy="{
|
||||||
content: t('panel.pureMixTip'),
|
content: '混合菜单,灵活多变',
|
||||||
zIndex: 41000
|
zIndex: 41000
|
||||||
}"
|
}"
|
||||||
:class="layoutTheme.layout === 'mix' ? 'is-select' : ''"
|
:class="layoutTheme.layout === 'mix' ? 'is-select' : ''"
|
||||||
@@ -397,7 +395,7 @@ onUnmounted(() => removeMatchMedia);
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<span v-if="useAppStoreHook().getViewportWidth > 1280">
|
<span v-if="useAppStoreHook().getViewportWidth > 1280">
|
||||||
<p :class="['mt-5!', pClass]">{{ t("panel.pureStretch") }}</p>
|
<p :class="['mt-5!', pClass]">页宽</p>
|
||||||
<Segmented
|
<Segmented
|
||||||
resize
|
resize
|
||||||
class="mb-2 select-none"
|
class="mb-2 select-none"
|
||||||
@@ -438,7 +436,7 @@ onUnmounted(() => removeMatchMedia);
|
|||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<p :class="['mt-4!', pClass]">{{ t("panel.pureTagsStyle") }}</p>
|
<p :class="['mt-4!', pClass]">页签风格</p>
|
||||||
<Segmented
|
<Segmented
|
||||||
resize
|
resize
|
||||||
class="select-none"
|
class="select-none"
|
||||||
@@ -447,47 +445,45 @@ onUnmounted(() => removeMatchMedia);
|
|||||||
@change="onChange"
|
@change="onChange"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p class="mt-5! font-medium text-sm dark:text-white">
|
<p class="mt-5! font-medium text-sm dark:text-white">界面显示</p>
|
||||||
{{ t("panel.pureInterfaceDisplay") }}
|
|
||||||
</p>
|
|
||||||
<ul class="setting">
|
<ul class="setting">
|
||||||
<li>
|
<li>
|
||||||
<span class="dark:text-white">{{ t("panel.pureGreyModel") }}</span>
|
<span class="dark:text-white">灰色模式</span>
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="settings.greyVal"
|
v-model="settings.greyVal"
|
||||||
inline-prompt
|
inline-prompt
|
||||||
:active-text="t('buttons.pureOpenText')"
|
active-text="开"
|
||||||
:inactive-text="t('buttons.pureCloseText')"
|
inactive-text="关"
|
||||||
@change="greyChange"
|
@change="greyChange"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span class="dark:text-white">{{ t("panel.pureWeakModel") }}</span>
|
<span class="dark:text-white">色弱模式</span>
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="settings.weakVal"
|
v-model="settings.weakVal"
|
||||||
inline-prompt
|
inline-prompt
|
||||||
:active-text="t('buttons.pureOpenText')"
|
active-text="开"
|
||||||
:inactive-text="t('buttons.pureCloseText')"
|
inactive-text="关"
|
||||||
@change="weekChange"
|
@change="weekChange"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span class="dark:text-white">{{ t("panel.pureHiddenTags") }}</span>
|
<span class="dark:text-white">隐藏标签页</span>
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="settings.tabsVal"
|
v-model="settings.tabsVal"
|
||||||
inline-prompt
|
inline-prompt
|
||||||
:active-text="t('buttons.pureOpenText')"
|
active-text="开"
|
||||||
:inactive-text="t('buttons.pureCloseText')"
|
inactive-text="关"
|
||||||
@change="tagsChange"
|
@change="tagsChange"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span class="dark:text-white">{{ t("panel.pureHiddenFooter") }}</span>
|
<span class="dark:text-white">隐藏页脚</span>
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="settings.hideFooter"
|
v-model="settings.hideFooter"
|
||||||
inline-prompt
|
inline-prompt
|
||||||
:active-text="t('buttons.pureOpenText')"
|
active-text="开"
|
||||||
:inactive-text="t('buttons.pureCloseText')"
|
inactive-text="关"
|
||||||
@change="hideFooterChange"
|
@change="hideFooterChange"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
@@ -498,20 +494,18 @@ onUnmounted(() => removeMatchMedia);
|
|||||||
inline-prompt
|
inline-prompt
|
||||||
:active-value="true"
|
:active-value="true"
|
||||||
:inactive-value="false"
|
:inactive-value="false"
|
||||||
:active-text="t('buttons.pureOpenText')"
|
active-text="开"
|
||||||
:inactive-text="t('buttons.pureCloseText')"
|
inactive-text="关"
|
||||||
@change="logoChange"
|
@change="logoChange"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span class="dark:text-white">
|
<span class="dark:text-white">页签持久化</span>
|
||||||
{{ t("panel.pureMultiTagsCache") }}
|
|
||||||
</span>
|
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="settings.multiTagsCache"
|
v-model="settings.multiTagsCache"
|
||||||
inline-prompt
|
inline-prompt
|
||||||
:active-text="t('buttons.pureOpenText')"
|
active-text="开"
|
||||||
:inactive-text="t('buttons.pureCloseText')"
|
inactive-text="关"
|
||||||
@change="multiTagsCacheChange"
|
@change="multiTagsCacheChange"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -6,15 +6,12 @@ import LayNotice from "../lay-notice/index.vue";
|
|||||||
import { responsiveStorageNameSpace } from "@/config";
|
import { responsiveStorageNameSpace } from "@/config";
|
||||||
import { ref, nextTick, computed, onMounted } from "vue";
|
import { ref, nextTick, computed, onMounted } from "vue";
|
||||||
import { storageLocal, isAllEmpty } from "@pureadmin/utils";
|
import { storageLocal, isAllEmpty } from "@pureadmin/utils";
|
||||||
import { useTranslationLang } from "../../hooks/useTranslationLang";
|
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||||
import LaySidebarItem from "../lay-sidebar/components/SidebarItem.vue";
|
import LaySidebarItem from "../lay-sidebar/components/SidebarItem.vue";
|
||||||
import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue";
|
import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue";
|
||||||
|
|
||||||
import GlobalizationIcon from "@/assets/svg/globalization.svg?component";
|
|
||||||
import LogoutCircleRLine from "~icons/ri/logout-circle-r-line";
|
import LogoutCircleRLine from "~icons/ri/logout-circle-r-line";
|
||||||
import Setting from "~icons/ri/settings-3-line";
|
import Setting from "~icons/ri/settings-3-line";
|
||||||
import Check from "~icons/ep/check";
|
|
||||||
|
|
||||||
const menuRef = ref();
|
const menuRef = ref();
|
||||||
const showLogo = ref(
|
const showLogo = ref(
|
||||||
@@ -23,9 +20,8 @@ const showLogo = ref(
|
|||||||
)?.showLogo ?? true
|
)?.showLogo ?? true
|
||||||
);
|
);
|
||||||
|
|
||||||
const { t, route, locale, translationCh, translationEn } =
|
|
||||||
useTranslationLang(menuRef);
|
|
||||||
const {
|
const {
|
||||||
|
route,
|
||||||
title,
|
title,
|
||||||
logout,
|
logout,
|
||||||
onPanel,
|
onPanel,
|
||||||
@@ -33,9 +29,7 @@ const {
|
|||||||
username,
|
username,
|
||||||
userAvatar,
|
userAvatar,
|
||||||
backTopMenu,
|
backTopMenu,
|
||||||
avatarsStyle,
|
avatarsStyle
|
||||||
getDropdownItemStyle,
|
|
||||||
getDropdownItemClass
|
|
||||||
} = useNav();
|
} = useNav();
|
||||||
|
|
||||||
const defaultActive = computed(() =>
|
const defaultActive = computed(() =>
|
||||||
@@ -79,36 +73,6 @@ onMounted(() => {
|
|||||||
<div class="horizontal-header-right">
|
<div class="horizontal-header-right">
|
||||||
<!-- 菜单搜索 -->
|
<!-- 菜单搜索 -->
|
||||||
<LaySearch id="header-search" />
|
<LaySearch id="header-search" />
|
||||||
<!-- 国际化 -->
|
|
||||||
<el-dropdown id="header-translation" trigger="click">
|
|
||||||
<GlobalizationIcon
|
|
||||||
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-hidden"
|
|
||||||
/>
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu class="translation">
|
|
||||||
<el-dropdown-item
|
|
||||||
:style="getDropdownItemStyle(locale, 'zh')"
|
|
||||||
:class="['dark:text-white!', getDropdownItemClass(locale, 'zh')]"
|
|
||||||
@click="translationCh"
|
|
||||||
>
|
|
||||||
<span v-show="locale === 'zh'" class="check-zh">
|
|
||||||
<IconifyIconOffline :icon="Check" />
|
|
||||||
</span>
|
|
||||||
简体中文
|
|
||||||
</el-dropdown-item>
|
|
||||||
<el-dropdown-item
|
|
||||||
:style="getDropdownItemStyle(locale, 'en')"
|
|
||||||
:class="['dark:text-white!', getDropdownItemClass(locale, 'en')]"
|
|
||||||
@click="translationEn"
|
|
||||||
>
|
|
||||||
<span v-show="locale === 'en'" class="check-en">
|
|
||||||
<IconifyIconOffline :icon="Check" />
|
|
||||||
</span>
|
|
||||||
English
|
|
||||||
</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
<!-- 全屏 -->
|
<!-- 全屏 -->
|
||||||
<LaySidebarFullScreen id="full-screen" />
|
<LaySidebarFullScreen id="full-screen" />
|
||||||
<!-- 消息通知 -->
|
<!-- 消息通知 -->
|
||||||
@@ -126,14 +90,14 @@ onMounted(() => {
|
|||||||
:icon="LogoutCircleRLine"
|
:icon="LogoutCircleRLine"
|
||||||
style="margin: 5px"
|
style="margin: 5px"
|
||||||
/>
|
/>
|
||||||
{{ t("buttons.pureLoginOut") }}
|
退出系统
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
<span
|
<span
|
||||||
class="set-icon navbar-bg-hover"
|
class="set-icon navbar-bg-hover"
|
||||||
:title="t('buttons.pureOpenSystemSet')"
|
title="打开系统配置"
|
||||||
@click="onPanel"
|
@click="onPanel"
|
||||||
>
|
>
|
||||||
<IconifyIconOffline :icon="Setting" />
|
<IconifyIconOffline :icon="Setting" />
|
||||||
@@ -147,22 +111,6 @@ onMounted(() => {
|
|||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
}
|
}
|
||||||
|
|
||||||
.translation {
|
|
||||||
::v-deep(.el-dropdown-menu__item) {
|
|
||||||
padding: 5px 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.check-zh {
|
|
||||||
position: absolute;
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.check-en {
|
|
||||||
position: absolute;
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.logout {
|
.logout {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,23 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { isAllEmpty } from "@pureadmin/utils";
|
import { isAllEmpty } from "@pureadmin/utils";
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from "@/layout/hooks/useNav";
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import LaySearch from "../lay-search/index.vue";
|
import LaySearch from "../lay-search/index.vue";
|
||||||
import LayNotice from "../lay-notice/index.vue";
|
import LayNotice from "../lay-notice/index.vue";
|
||||||
import { ref, toRaw, watch, onMounted, nextTick } from "vue";
|
import { ref, toRaw, watch, onMounted, nextTick } from "vue";
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
import { getParentPaths, findRouteByPath } from "@/router/utils";
|
import { getParentPaths, findRouteByPath } from "@/router/utils";
|
||||||
import { useTranslationLang } from "../../hooks/useTranslationLang";
|
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||||
import LaySidebarExtraIcon from "../lay-sidebar/components/SidebarExtraIcon.vue";
|
import LaySidebarExtraIcon from "../lay-sidebar/components/SidebarExtraIcon.vue";
|
||||||
import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue";
|
import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue";
|
||||||
|
|
||||||
import GlobalizationIcon from "@/assets/svg/globalization.svg?component";
|
|
||||||
import LogoutCircleRLine from "~icons/ri/logout-circle-r-line";
|
import LogoutCircleRLine from "~icons/ri/logout-circle-r-line";
|
||||||
import Setting from "~icons/ri/settings-3-line";
|
import Setting from "~icons/ri/settings-3-line";
|
||||||
import Check from "~icons/ep/check";
|
|
||||||
|
|
||||||
const menuRef = ref();
|
const menuRef = ref();
|
||||||
const defaultActive = ref(null);
|
const defaultActive = ref(null);
|
||||||
|
|
||||||
const { t, route, locale, translationCh, translationEn } =
|
|
||||||
useTranslationLang(menuRef);
|
|
||||||
const {
|
const {
|
||||||
|
route,
|
||||||
device,
|
device,
|
||||||
logout,
|
logout,
|
||||||
onPanel,
|
onPanel,
|
||||||
@@ -30,9 +25,7 @@ const {
|
|||||||
username,
|
username,
|
||||||
userAvatar,
|
userAvatar,
|
||||||
getDivStyle,
|
getDivStyle,
|
||||||
avatarsStyle,
|
avatarsStyle
|
||||||
getDropdownItemStyle,
|
|
||||||
getDropdownItemClass
|
|
||||||
} = useNav();
|
} = useNav();
|
||||||
|
|
||||||
function getDefaultActive(routePath) {
|
function getDefaultActive(routePath) {
|
||||||
@@ -90,7 +83,7 @@ watch(
|
|||||||
</div>
|
</div>
|
||||||
<div :style="getDivStyle">
|
<div :style="getDivStyle">
|
||||||
<span class="select-none">
|
<span class="select-none">
|
||||||
{{ transformI18n(route.meta.title) }}
|
{{ route.meta.title }}
|
||||||
</span>
|
</span>
|
||||||
<LaySidebarExtraIcon :extraIcon="route.meta.extraIcon" />
|
<LaySidebarExtraIcon :extraIcon="route.meta.extraIcon" />
|
||||||
</div>
|
</div>
|
||||||
@@ -100,36 +93,6 @@ watch(
|
|||||||
<div class="horizontal-header-right">
|
<div class="horizontal-header-right">
|
||||||
<!-- 菜单搜索 -->
|
<!-- 菜单搜索 -->
|
||||||
<LaySearch id="header-search" />
|
<LaySearch id="header-search" />
|
||||||
<!-- 国际化 -->
|
|
||||||
<el-dropdown id="header-translation" trigger="click">
|
|
||||||
<GlobalizationIcon
|
|
||||||
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-hidden"
|
|
||||||
/>
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu class="translation">
|
|
||||||
<el-dropdown-item
|
|
||||||
:style="getDropdownItemStyle(locale, 'zh')"
|
|
||||||
:class="['dark:text-white!', getDropdownItemClass(locale, 'zh')]"
|
|
||||||
@click="translationCh"
|
|
||||||
>
|
|
||||||
<span v-show="locale === 'zh'" class="check-zh">
|
|
||||||
<IconifyIconOffline :icon="Check" />
|
|
||||||
</span>
|
|
||||||
简体中文
|
|
||||||
</el-dropdown-item>
|
|
||||||
<el-dropdown-item
|
|
||||||
:style="getDropdownItemStyle(locale, 'en')"
|
|
||||||
:class="['dark:text-white!', getDropdownItemClass(locale, 'en')]"
|
|
||||||
@click="translationEn"
|
|
||||||
>
|
|
||||||
<span v-show="locale === 'en'" class="check-en">
|
|
||||||
<IconifyIconOffline :icon="Check" />
|
|
||||||
</span>
|
|
||||||
English
|
|
||||||
</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
<!-- 全屏 -->
|
<!-- 全屏 -->
|
||||||
<LaySidebarFullScreen id="full-screen" />
|
<LaySidebarFullScreen id="full-screen" />
|
||||||
<!-- 消息通知 -->
|
<!-- 消息通知 -->
|
||||||
@@ -147,14 +110,14 @@ watch(
|
|||||||
:icon="LogoutCircleRLine"
|
:icon="LogoutCircleRLine"
|
||||||
style="margin: 5px"
|
style="margin: 5px"
|
||||||
/>
|
/>
|
||||||
{{ t("buttons.pureLoginOut") }}
|
退出系统
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
<span
|
<span
|
||||||
class="set-icon navbar-bg-hover"
|
class="set-icon navbar-bg-hover"
|
||||||
:title="t('buttons.pureOpenSystemSet')"
|
title="打开系统配置"
|
||||||
@click="onPanel"
|
@click="onPanel"
|
||||||
>
|
>
|
||||||
<IconifyIconOffline :icon="Setting" />
|
<IconifyIconOffline :icon="Setting" />
|
||||||
@@ -168,22 +131,6 @@ watch(
|
|||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
}
|
}
|
||||||
|
|
||||||
.translation {
|
|
||||||
::v-deep(.el-dropdown-menu__item) {
|
|
||||||
padding: 5px 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.check-zh {
|
|
||||||
position: absolute;
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.check-en {
|
|
||||||
position: absolute;
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.logout {
|
.logout {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { isEqual } from "@pureadmin/utils";
|
import { isEqual } from "@pureadmin/utils";
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { ref, watch, onMounted, toRaw } from "vue";
|
import { ref, watch, onMounted, toRaw } from "vue";
|
||||||
import { getParentPaths, findRouteByPath } from "@/router/utils";
|
import { getParentPaths, findRouteByPath } from "@/router/utils";
|
||||||
@@ -113,7 +112,7 @@ watch(
|
|||||||
class="inline! items-stretch!"
|
class="inline! items-stretch!"
|
||||||
>
|
>
|
||||||
<a @click.prevent="handleLink(item)">
|
<a @click.prevent="handleLink(item)">
|
||||||
{{ transformI18n(item.meta.title) }}
|
{{ item.meta.title }}
|
||||||
</a>
|
</a>
|
||||||
</el-breadcrumb-item>
|
</el-breadcrumb-item>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { useGlobal } from "@pureadmin/utils";
|
import { useGlobal } from "@pureadmin/utils";
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from "@/layout/hooks/useNav";
|
||||||
|
|
||||||
@@ -14,7 +13,6 @@ withDefaults(defineProps<Props>(), {
|
|||||||
isActive: false
|
isActive: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { tooltipEffect } = useNav();
|
const { tooltipEffect } = useNav();
|
||||||
|
|
||||||
const iconClass = computed(() => {
|
const iconClass = computed(() => {
|
||||||
@@ -36,9 +34,7 @@ const toggleClick = () => {
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-tippy="{
|
v-tippy="{
|
||||||
content: isActive
|
content: isActive ? '点击折叠' : '点击展开',
|
||||||
? t('buttons.pureClickCollapse')
|
|
||||||
: t('buttons.pureClickExpand'),
|
|
||||||
theme: tooltipEffect,
|
theme: tooltipEffect,
|
||||||
hideOnClick: 'toggle',
|
hideOnClick: 'toggle',
|
||||||
placement: 'right'
|
placement: 'right'
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { posix } from "path-browserify";
|
|||||||
import { menuType } from "@/layout/types";
|
import { menuType } from "@/layout/types";
|
||||||
import { ReText } from "@/components/ReText";
|
import { ReText } from "@/components/ReText";
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from "@/layout/hooks/useNav";
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import SidebarLinkItem from "./SidebarLinkItem.vue";
|
import SidebarLinkItem from "./SidebarLinkItem.vue";
|
||||||
import SidebarExtraIcon from "./SidebarExtraIcon.vue";
|
import SidebarExtraIcon from "./SidebarExtraIcon.vue";
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
@@ -161,7 +160,7 @@ function resolvePath(routePath) {
|
|||||||
truncated
|
truncated
|
||||||
class="w-full! px-3! min-w-[54px]! text-center! text-inherit!"
|
class="w-full! px-3! min-w-[54px]! text-center! text-inherit!"
|
||||||
>
|
>
|
||||||
{{ transformI18n(onlyOneChild.meta.title) }}
|
{{ onlyOneChild.meta.title }}
|
||||||
</el-text>
|
</el-text>
|
||||||
|
|
||||||
<template #title>
|
<template #title>
|
||||||
@@ -173,7 +172,7 @@ function resolvePath(routePath) {
|
|||||||
}"
|
}"
|
||||||
class="w-full! text-inherit!"
|
class="w-full! text-inherit!"
|
||||||
>
|
>
|
||||||
{{ transformI18n(onlyOneChild.meta.title) }}
|
{{ onlyOneChild.meta.title }}
|
||||||
</ReText>
|
</ReText>
|
||||||
<SidebarExtraIcon :extraIcon="onlyOneChild.meta.extraIcon" />
|
<SidebarExtraIcon :extraIcon="onlyOneChild.meta.extraIcon" />
|
||||||
</div>
|
</div>
|
||||||
@@ -212,7 +211,7 @@ function resolvePath(routePath) {
|
|||||||
}"
|
}"
|
||||||
:class="textClass"
|
:class="textClass"
|
||||||
>
|
>
|
||||||
{{ transformI18n(item.meta.title) }}
|
{{ item.meta.title }}
|
||||||
</ReText>
|
</ReText>
|
||||||
<SidebarExtraIcon v-if="!isCollapse" :extraIcon="item.meta.extraIcon" />
|
<SidebarExtraIcon v-if="!isCollapse" :extraIcon="item.meta.extraIcon" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { useGlobal } from "@pureadmin/utils";
|
import { useGlobal } from "@pureadmin/utils";
|
||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from "@/layout/hooks/useNav";
|
||||||
|
|
||||||
@@ -14,7 +13,6 @@ withDefaults(defineProps<Props>(), {
|
|||||||
isActive: false
|
isActive: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { tooltipEffect } = useNav();
|
const { tooltipEffect } = useNav();
|
||||||
|
|
||||||
const iconClass = computed(() => {
|
const iconClass = computed(() => {
|
||||||
@@ -46,9 +44,7 @@ const toggleClick = () => {
|
|||||||
<div class="left-collapse">
|
<div class="left-collapse">
|
||||||
<IconifyIconOffline
|
<IconifyIconOffline
|
||||||
v-tippy="{
|
v-tippy="{
|
||||||
content: isActive
|
content: isActive ? '点击折叠' : '点击展开',
|
||||||
? t('buttons.pureClickCollapse')
|
|
||||||
: t('buttons.pureClickExpand'),
|
|
||||||
theme: tooltipEffect,
|
theme: tooltipEffect,
|
||||||
hideOnClick: 'toggle',
|
hideOnClick: 'toggle',
|
||||||
placement: 'right'
|
placement: 'right'
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import MenuFold from "~icons/ri/menu-fold-fill";
|
import MenuFold from "~icons/ri/menu-fold-fill";
|
||||||
import MenuUnfold from "~icons/ri/menu-unfold-fill";
|
import MenuUnfold from "~icons/ri/menu-unfold-fill";
|
||||||
|
|
||||||
@@ -11,8 +10,6 @@ withDefaults(defineProps<Props>(), {
|
|||||||
isActive: false
|
isActive: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "toggleClick"): void;
|
(e: "toggleClick"): void;
|
||||||
}>();
|
}>();
|
||||||
@@ -25,9 +22,7 @@ const toggleClick = () => {
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="px-3 mr-1 navbar-bg-hover"
|
class="px-3 mr-1 navbar-bg-hover"
|
||||||
:title="
|
:title="isActive ? '点击折叠' : '点击展开'"
|
||||||
isActive ? t('buttons.pureClickCollapse') : t('buttons.pureClickExpand')
|
|
||||||
"
|
|
||||||
@click="toggleClick"
|
@click="toggleClick"
|
||||||
>
|
>
|
||||||
<IconifyIconOffline
|
<IconifyIconOffline
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { $t } from "@/plugins/i18n";
|
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from "@/utils/mitt";
|
||||||
import NProgress from "@/utils/progress";
|
|
||||||
import { RouteConfigs } from "../../types";
|
import { RouteConfigs } from "../../types";
|
||||||
import { useTags } from "../../hooks/useTag";
|
import { useTags } from "../../hooks/useTag";
|
||||||
import { routerArrays } from "@/layout/types";
|
import { routerArrays } from "@/layout/types";
|
||||||
@@ -52,7 +50,6 @@ const {
|
|||||||
onMounted,
|
onMounted,
|
||||||
onMouseenter,
|
onMouseenter,
|
||||||
onMouseleave,
|
onMouseleave,
|
||||||
transformI18n,
|
|
||||||
onContentFullScreen
|
onContentFullScreen
|
||||||
} = useTags();
|
} = useTags();
|
||||||
|
|
||||||
@@ -207,14 +204,12 @@ function dynamicRouteTag(value: string): void {
|
|||||||
|
|
||||||
/** 刷新路由 */
|
/** 刷新路由 */
|
||||||
function onFresh() {
|
function onFresh() {
|
||||||
NProgress.start();
|
|
||||||
const { fullPath, query } = unref(route);
|
const { fullPath, query } = unref(route);
|
||||||
router.replace({
|
router.replace({
|
||||||
path: "/redirect" + fullPath,
|
path: "/redirect" + fullPath,
|
||||||
query
|
query
|
||||||
});
|
});
|
||||||
handleAliveRoute(route as ToRouteType, "refresh");
|
handleAliveRoute(route as ToRouteType, "refresh");
|
||||||
NProgress.done();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
||||||
@@ -257,7 +252,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
|||||||
if (tag === "other") {
|
if (tag === "other") {
|
||||||
spliceRoute(1, 1, true);
|
spliceRoute(1, 1, true);
|
||||||
} else if (tag === "left") {
|
} else if (tag === "left") {
|
||||||
spliceRoute(fixedTags.length, valueIndex - fixedTags.length);
|
spliceRoute(fixedTags.length, valueIndex - 1, true);
|
||||||
} else if (tag === "right") {
|
} else if (tag === "right") {
|
||||||
spliceRoute(valueIndex + 1, multiTags.value.length);
|
spliceRoute(valueIndex + 1, multiTags.value.length);
|
||||||
} else {
|
} else {
|
||||||
@@ -347,16 +342,16 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (pureSetting.hiddenSideBar) {
|
if (pureSetting.hiddenSideBar) {
|
||||||
tagsViews[6].icon = ExitFullscreen;
|
tagsViews[6].icon = ExitFullscreen;
|
||||||
tagsViews[6].text = $t("buttons.pureContentExitFullScreen");
|
tagsViews[6].text = "内容区退出全屏";
|
||||||
} else {
|
} else {
|
||||||
tagsViews[6].icon = Fullscreen;
|
tagsViews[6].icon = Fullscreen;
|
||||||
tagsViews[6].text = $t("buttons.pureContentFullScreen");
|
tagsViews[6].text = "内容区全屏";
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showMenuModel(route.fullPath, route.query, route.params);
|
showMenuModel(route.fullPath, route.query);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,18 +386,15 @@ function disabledMenus(value: boolean, fixedTag = false) {
|
|||||||
function showMenuModel(
|
function showMenuModel(
|
||||||
currentPath: string,
|
currentPath: string,
|
||||||
query: object = {},
|
query: object = {},
|
||||||
params: object = {},
|
|
||||||
refresh = false
|
refresh = false
|
||||||
) {
|
) {
|
||||||
const allRoute = multiTags.value;
|
const allRoute = multiTags.value;
|
||||||
const routeLength = multiTags.value.length;
|
const routeLength = multiTags.value.length;
|
||||||
let currentIndex = -1;
|
let currentIndex = -1;
|
||||||
if (!isAllEmpty(params)) {
|
if (isAllEmpty(query)) {
|
||||||
currentIndex = allRoute.findIndex(v => isEqual(v.params, params));
|
|
||||||
} else if (!isAllEmpty(query)) {
|
|
||||||
currentIndex = allRoute.findIndex(v => isEqual(v.query, query));
|
|
||||||
} else {
|
|
||||||
currentIndex = allRoute.findIndex(v => v.path === currentPath);
|
currentIndex = allRoute.findIndex(v => v.path === currentPath);
|
||||||
|
} else {
|
||||||
|
currentIndex = allRoute.findIndex(v => isEqual(v.query, query));
|
||||||
}
|
}
|
||||||
function fixedTagDisabled() {
|
function fixedTagDisabled() {
|
||||||
if (allRoute[currentIndex]?.meta?.fixedTag) {
|
if (allRoute[currentIndex]?.meta?.fixedTag) {
|
||||||
@@ -468,14 +460,14 @@ function openMenu(tag, e) {
|
|||||||
} else if (route.path !== tag.path && route.name !== tag.name) {
|
} else if (route.path !== tag.path && route.name !== tag.name) {
|
||||||
// 右键菜单不匹配当前路由,隐藏刷新
|
// 右键菜单不匹配当前路由,隐藏刷新
|
||||||
tagsViews[0].show = false;
|
tagsViews[0].show = false;
|
||||||
showMenuModel(tag.path, tag.query, tag.params);
|
showMenuModel(tag.path, tag.query);
|
||||||
} else if (multiTags.value.length === 2 && route.path !== tag.path) {
|
} else if (multiTags.value.length === 2 && route.path !== tag.path) {
|
||||||
showMenus(true);
|
showMenus(true);
|
||||||
// 只有两个标签时不显示关闭其他标签页
|
// 只有两个标签时不显示关闭其他标签页
|
||||||
tagsViews[4].show = false;
|
tagsViews[4].show = false;
|
||||||
showMenuModel(tag.path, tag.query, tag.params);
|
} else if (route.path === tag.path) {
|
||||||
} else {
|
// 右键当前激活的菜单
|
||||||
showMenuModel(tag.path, tag.query, tag.params, true);
|
showMenuModel(tag.path, tag.query, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSelect.value = tag;
|
currentSelect.value = tag;
|
||||||
@@ -597,7 +589,7 @@ onBeforeUnmount(() => {
|
|||||||
<span
|
<span
|
||||||
class="tag-title dark:text-text_color_primary! dark:hover:text-primary!"
|
class="tag-title dark:text-text_color_primary! dark:hover:text-primary!"
|
||||||
>
|
>
|
||||||
{{ transformI18n(item.meta.title) }}
|
{{ item.meta.title }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="
|
v-if="
|
||||||
@@ -622,7 +614,7 @@ onBeforeUnmount(() => {
|
|||||||
<TagChrome />
|
<TagChrome />
|
||||||
</div>
|
</div>
|
||||||
<span class="tag-title">
|
<span class="tag-title">
|
||||||
{{ transformI18n(item.meta.title) }}
|
{{ item.meta.title }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="isFixedTag(item) ? false : index !== 0"
|
v-if="isFixedTag(item) ? false : index !== 0"
|
||||||
@@ -655,7 +647,7 @@ onBeforeUnmount(() => {
|
|||||||
>
|
>
|
||||||
<li v-if="item.show" @click="selectTag(key, item)">
|
<li v-if="item.show" @click="selectTag(key, item)">
|
||||||
<IconifyIconOffline :icon="item.icon" />
|
<IconifyIconOffline :icon="item.icon" />
|
||||||
{{ transformI18n(item.text) }}
|
{{ item.text }}
|
||||||
</li>
|
</li>
|
||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -679,7 +671,7 @@ onBeforeUnmount(() => {
|
|||||||
:disabled="item.disabled"
|
:disabled="item.disabled"
|
||||||
>
|
>
|
||||||
<IconifyIconOffline :icon="item.icon" />
|
<IconifyIconOffline :icon="item.icon" />
|
||||||
{{ transformI18n(item.text) }}
|
{{ item.text }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import { ref, unref, watch, onMounted, nextTick } from "vue";
|
import { ref, unref, watch, onMounted, nextTick } from "vue";
|
||||||
|
|
||||||
@@ -14,27 +13,17 @@ const props = defineProps<{
|
|||||||
};
|
};
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const currentRoute = useRoute();
|
const currentRoute = useRoute();
|
||||||
const frameSrc = ref<string>("");
|
const frameSrc = ref<string>("");
|
||||||
const frameRef = ref<HTMLElement | null>(null);
|
const frameRef = ref<HTMLElement | null>(null);
|
||||||
const fallbackTimer = ref<number | null>(null);
|
|
||||||
|
|
||||||
if (unref(currentRoute.meta)?.frameSrc) {
|
if (unref(currentRoute.meta)?.frameSrc) {
|
||||||
frameSrc.value = unref(currentRoute.meta)?.frameSrc as string;
|
frameSrc.value = unref(currentRoute.meta)?.frameSrc as string;
|
||||||
}
|
}
|
||||||
|
unref(currentRoute.meta)?.frameLoading === false && hideLoading();
|
||||||
function clearFallbackTimer() {
|
|
||||||
if (fallbackTimer.value !== null) {
|
|
||||||
clearTimeout(fallbackTimer.value);
|
|
||||||
fallbackTimer.value = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideLoading() {
|
function hideLoading() {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
clearFallbackTimer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
@@ -43,42 +32,32 @@ function init() {
|
|||||||
if (!iframe) return;
|
if (!iframe) return;
|
||||||
const _frame = iframe as any;
|
const _frame = iframe as any;
|
||||||
if (_frame.attachEvent) {
|
if (_frame.attachEvent) {
|
||||||
_frame.attachEvent("onload", hideLoading);
|
_frame.attachEvent("onload", () => {
|
||||||
|
hideLoading();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
iframe.onload = hideLoading;
|
iframe.onload = () => {
|
||||||
|
hideLoading();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let isRedirect = false;
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => currentRoute.fullPath,
|
() => currentRoute.fullPath,
|
||||||
path => {
|
path => {
|
||||||
if (
|
if (
|
||||||
currentRoute.name === "Redirect" &&
|
currentRoute.name === "Redirect" &&
|
||||||
props.frameInfo?.fullPath &&
|
path.includes(props.frameInfo?.fullPath)
|
||||||
path.includes(props.frameInfo.fullPath)
|
|
||||||
) {
|
) {
|
||||||
isRedirect = true;
|
frameSrc.value = path; // redirect时,置换成任意值,待重定向后 重新赋值
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (props.frameInfo?.fullPath === path && isRedirect) {
|
// 重新赋值
|
||||||
loading.value = true;
|
if (props.frameInfo?.fullPath === path) {
|
||||||
clearFallbackTimer();
|
frameSrc.value = props.frameInfo?.frameSrc;
|
||||||
const url = new URL(props.frameInfo.frameSrc, window.location.origin);
|
|
||||||
const joinChar = url.search ? "&" : "?";
|
|
||||||
frameSrc.value = `${props.frameInfo.frameSrc}${joinChar}t=${Date.now()}`;
|
|
||||||
fallbackTimer.value = window.setTimeout(() => {
|
|
||||||
if (loading.value) {
|
|
||||||
hideLoading();
|
|
||||||
}
|
|
||||||
}, 1500);
|
|
||||||
isRedirect = false;
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
{ immediate: true }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -87,11 +66,7 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div v-loading="loading" class="frame" element-loading-text="加载中...">
|
||||||
v-loading="loading"
|
|
||||||
class="frame"
|
|
||||||
:element-loading-text="t('status.pureLoad')"
|
|
||||||
>
|
|
||||||
<iframe ref="frameRef" :src="frameSrc" class="frame-iframe" />
|
<iframe ref="frameRef" :src="frameSrc" class="frame-iframe" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { routerArrays } from "../types";
|
import { routerArrays } from "../types";
|
||||||
import { useGlobal } from "@pureadmin/utils";
|
import { useGlobal } from "@pureadmin/utils";
|
||||||
import { useMultiTagsStore } from "@/store/modules/multiTags";
|
import { useMultiTagsStore } from "@/store/modules/multiTags";
|
||||||
@@ -15,11 +14,6 @@ export function useLayout() {
|
|||||||
) {
|
) {
|
||||||
$storage.tags = routerArrays;
|
$storage.tags = routerArrays;
|
||||||
}
|
}
|
||||||
/** 国际化 */
|
|
||||||
if (!$storage.locale) {
|
|
||||||
$storage.locale = { locale: $config?.Locale ?? "zh" };
|
|
||||||
useI18n().locale.value = $config?.Locale ?? "zh";
|
|
||||||
}
|
|
||||||
/** 导航 */
|
/** 导航 */
|
||||||
if (!$storage.layout) {
|
if (!$storage.layout) {
|
||||||
$storage.layout = {
|
$storage.layout = {
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from "@/config";
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from "@/utils/mitt";
|
||||||
import Avatar from "@/assets/user.jpg";
|
import Avatar from "@/assets/user.jpg";
|
||||||
import { getTopMenu } from "@/router/utils";
|
import { getTopMenu } from "@/router/utils";
|
||||||
import { useFullscreen } from "@vueuse/core";
|
import { useFullscreen } from "@vueuse/core";
|
||||||
import type { routeMetaType } from "../types";
|
import type { routeMetaType } from "../types";
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
import { useRouter, useRoute } from "vue-router";
|
||||||
import { router, remainingPaths } from "@/router";
|
import { router, remainingPaths } from "@/router";
|
||||||
import { computed, type CSSProperties } from "vue";
|
import { computed, type CSSProperties } from "vue";
|
||||||
import { useAppStoreHook } from "@/store/modules/app";
|
import { useAppStoreHook } from "@/store/modules/app";
|
||||||
import { useUserStoreHook } from "@/store/modules/user";
|
import { useUserStoreHook } from "@/store/modules/user";
|
||||||
import { useGlobal, isAllEmpty } from "@pureadmin/utils";
|
import { useGlobal, isAllEmpty } from "@pureadmin/utils";
|
||||||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||||
import ExitFullscreen from "~icons/ri/fullscreen-exit-fill";
|
import ExitFullscreen from "~icons/ri/fullscreen-exit-fill";
|
||||||
import Fullscreen from "~icons/ri/fullscreen-fill";
|
import Fullscreen from "~icons/ri/fullscreen-fill";
|
||||||
@@ -21,6 +19,7 @@ const errorInfo =
|
|||||||
"The current routing configuration is incorrect, please check the configuration";
|
"The current routing configuration is incorrect, please check the configuration";
|
||||||
|
|
||||||
export function useNav() {
|
export function useNav() {
|
||||||
|
const route = useRoute();
|
||||||
const pureApp = useAppStoreHook();
|
const pureApp = useAppStoreHook();
|
||||||
const routers = useRouter().options.routes;
|
const routers = useRouter().options.routes;
|
||||||
const { isFullscreen, toggle } = useFullscreen();
|
const { isFullscreen, toggle } = useFullscreen();
|
||||||
@@ -52,22 +51,6 @@ export function useNav() {
|
|||||||
: useUserStoreHook()?.nickname;
|
: useUserStoreHook()?.nickname;
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 设置国际化选中后的样式 */
|
|
||||||
const getDropdownItemStyle = computed(() => {
|
|
||||||
return (locale, t) => {
|
|
||||||
return {
|
|
||||||
background: locale === t ? useEpThemeStoreHook().epThemeColor : "",
|
|
||||||
color: locale === t ? "#f4f4f5" : "#000"
|
|
||||||
};
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const getDropdownItemClass = computed(() => {
|
|
||||||
return (locale, t) => {
|
|
||||||
return locale === t ? "" : "dark:hover:text-primary!";
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const avatarsStyle = computed(() => {
|
const avatarsStyle = computed(() => {
|
||||||
return username.value ? { marginRight: "10px" } : "";
|
return username.value ? { marginRight: "10px" } : "";
|
||||||
});
|
});
|
||||||
@@ -92,8 +75,8 @@ export function useNav() {
|
|||||||
/** 动态title */
|
/** 动态title */
|
||||||
function changeTitle(meta: routeMetaType) {
|
function changeTitle(meta: routeMetaType) {
|
||||||
const Title = getConfig().Title;
|
const Title = getConfig().Title;
|
||||||
if (Title) document.title = `${transformI18n(meta.title)} | ${Title}`;
|
if (Title) document.title = `${meta.title} | ${Title}`;
|
||||||
else document.title = transformI18n(meta.title);
|
else document.title = meta.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 退出登录 */
|
/** 退出登录 */
|
||||||
@@ -144,6 +127,7 @@ export function useNav() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
route,
|
||||||
title,
|
title,
|
||||||
device,
|
device,
|
||||||
layout,
|
layout,
|
||||||
@@ -168,8 +152,6 @@ export function useNav() {
|
|||||||
username,
|
username,
|
||||||
userAvatar,
|
userAvatar,
|
||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
tooltipEffect,
|
tooltipEffect
|
||||||
getDropdownItemStyle,
|
|
||||||
getDropdownItemClass
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
} from "vue";
|
} from "vue";
|
||||||
import type { tagsViewsType } from "../types";
|
import type { tagsViewsType } from "../types";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { transformI18n, $t } from "@/plugins/i18n";
|
|
||||||
import { responsiveStorageNameSpace } from "@/config";
|
import { responsiveStorageNameSpace } from "@/config";
|
||||||
import { useSettingStoreHook } from "@/store/modules/settings";
|
import { useSettingStoreHook } from "@/store/modules/settings";
|
||||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
||||||
@@ -64,49 +63,49 @@ export function useTags() {
|
|||||||
const tagsViews = reactive<Array<tagsViewsType>>([
|
const tagsViews = reactive<Array<tagsViewsType>>([
|
||||||
{
|
{
|
||||||
icon: RefreshRight,
|
icon: RefreshRight,
|
||||||
text: $t("buttons.pureReload"),
|
text: "重新加载",
|
||||||
divided: false,
|
divided: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: Close,
|
icon: Close,
|
||||||
text: $t("buttons.pureCloseCurrentTab"),
|
text: "关闭当前标签页",
|
||||||
divided: false,
|
divided: false,
|
||||||
disabled: multiTags.value.length > 1 ? false : true,
|
disabled: multiTags.value.length > 1 ? false : true,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: CloseLeftTags,
|
icon: CloseLeftTags,
|
||||||
text: $t("buttons.pureCloseLeftTabs"),
|
text: "关闭左侧标签页",
|
||||||
divided: true,
|
divided: true,
|
||||||
disabled: multiTags.value.length > 1 ? false : true,
|
disabled: multiTags.value.length > 1 ? false : true,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: CloseRightTags,
|
icon: CloseRightTags,
|
||||||
text: $t("buttons.pureCloseRightTabs"),
|
text: "关闭右侧标签页",
|
||||||
divided: false,
|
divided: false,
|
||||||
disabled: multiTags.value.length > 1 ? false : true,
|
disabled: multiTags.value.length > 1 ? false : true,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: CloseOtherTags,
|
icon: CloseOtherTags,
|
||||||
text: $t("buttons.pureCloseOtherTabs"),
|
text: "关闭其他标签页",
|
||||||
divided: true,
|
divided: true,
|
||||||
disabled: multiTags.value.length > 2 ? false : true,
|
disabled: multiTags.value.length > 2 ? false : true,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: CloseAllTags,
|
icon: CloseAllTags,
|
||||||
text: $t("buttons.pureCloseAllTabs"),
|
text: "关闭全部标签页",
|
||||||
divided: false,
|
divided: false,
|
||||||
disabled: multiTags.value.length > 1 ? false : true,
|
disabled: multiTags.value.length > 1 ? false : true,
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: Fullscreen,
|
icon: Fullscreen,
|
||||||
text: $t("buttons.pureContentFullScreen"),
|
text: "内容区全屏",
|
||||||
divided: true,
|
divided: true,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
show: true
|
show: true
|
||||||
@@ -114,21 +113,14 @@ export function useTags() {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
function conditionHandle(item, previous, next) {
|
function conditionHandle(item, previous, next) {
|
||||||
const currentName = route.name || "";
|
|
||||||
const itemName = item.name || "";
|
|
||||||
|
|
||||||
if (isBoolean(route?.meta?.showLink) && route?.meta?.showLink === false) {
|
if (isBoolean(route?.meta?.showLink) && route?.meta?.showLink === false) {
|
||||||
if (Object.keys(route.query).length > 0) {
|
if (Object.keys(route.query).length > 0) {
|
||||||
return currentName === itemName && isEqual(route.query, item.query)
|
return isEqual(route.query, item.query) ? previous : next;
|
||||||
? previous
|
|
||||||
: next;
|
|
||||||
} else {
|
} else {
|
||||||
return currentName === itemName && isEqual(route.params, item.params)
|
return isEqual(route.params, item.params) ? previous : next;
|
||||||
? previous
|
|
||||||
: next;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return currentName === itemName ? previous : next;
|
return route.path === item.path ? previous : next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,12 +236,10 @@ export function useTags() {
|
|||||||
currentSelect,
|
currentSelect,
|
||||||
scheduleIsActive,
|
scheduleIsActive,
|
||||||
getContextMenuStyle,
|
getContextMenuStyle,
|
||||||
$t,
|
|
||||||
closeMenu,
|
closeMenu,
|
||||||
onMounted,
|
onMounted,
|
||||||
onMouseenter,
|
onMouseenter,
|
||||||
onMouseleave,
|
onMouseleave,
|
||||||
transformI18n,
|
|
||||||
onContentFullScreen
|
onContentFullScreen
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
import { useNav } from "./useNav";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import { watch, onBeforeMount, type Ref } from "vue";
|
|
||||||
|
|
||||||
export function useTranslationLang(ref?: Ref) {
|
|
||||||
const { $storage, changeTitle, handleResize } = useNav();
|
|
||||||
const { locale, t } = useI18n();
|
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
function translationCh() {
|
|
||||||
$storage.locale = { locale: "zh" };
|
|
||||||
locale.value = "zh";
|
|
||||||
ref && handleResize(ref.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function translationEn() {
|
|
||||||
$storage.locale = { locale: "en" };
|
|
||||||
locale.value = "en";
|
|
||||||
ref && handleResize(ref.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => locale.value,
|
|
||||||
() => {
|
|
||||||
changeTitle(route.meta);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
locale.value = $storage.locale?.locale ?? "zh";
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
t,
|
|
||||||
route,
|
|
||||||
locale,
|
|
||||||
translationCh,
|
|
||||||
translationEn
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ import "animate.css";
|
|||||||
// 引入 src/components/ReIcon/src/offlineIcon.ts 文件中所有使用addIcon添加过的本地图标
|
// 引入 src/components/ReIcon/src/offlineIcon.ts 文件中所有使用addIcon添加过的本地图标
|
||||||
import "@/components/ReIcon/src/offlineIcon";
|
import "@/components/ReIcon/src/offlineIcon";
|
||||||
import { setType } from "./types";
|
import { setType } from "./types";
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { useLayout } from "./hooks/useLayout";
|
import { useLayout } from "./hooks/useLayout";
|
||||||
import { useAppStoreHook } from "@/store/modules/app";
|
import { useAppStoreHook } from "@/store/modules/app";
|
||||||
import { useSettingStoreHook } from "@/store/modules/settings";
|
import { useSettingStoreHook } from "@/store/modules/settings";
|
||||||
@@ -32,7 +31,6 @@ import NavVertical from "./components/lay-sidebar/NavVertical.vue";
|
|||||||
import NavHorizontal from "./components/lay-sidebar/NavHorizontal.vue";
|
import NavHorizontal from "./components/lay-sidebar/NavHorizontal.vue";
|
||||||
import BackTopIcon from "@/assets/svg/back_top.svg?component";
|
import BackTopIcon from "@/assets/svg/back_top.svg?component";
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const appWrapperRef = ref();
|
const appWrapperRef = ref();
|
||||||
const { isDark } = useDark();
|
const { isDark } = useDark();
|
||||||
const { layout } = useLayout();
|
const { layout } = useLayout();
|
||||||
@@ -188,7 +186,7 @@ const LayHeader = defineComponent({
|
|||||||
</div>
|
</div>
|
||||||
<el-scrollbar v-else>
|
<el-scrollbar v-else>
|
||||||
<el-backtop
|
<el-backtop
|
||||||
:title="t('buttons.pureBackTop')"
|
title="回到顶部"
|
||||||
target=".main-container .el-scrollbar__wrap"
|
target=".main-container .el-scrollbar__wrap"
|
||||||
>
|
>
|
||||||
<BackTopIcon />
|
<BackTopIcon />
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ export const routerArrays: Array<RouteConfigs> =
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
path: "/welcome",
|
path: "/welcome",
|
||||||
name: "Welcome",
|
|
||||||
meta: {
|
meta: {
|
||||||
title: "menus.pureHome",
|
title: "首页",
|
||||||
icon: "ep/home-filled"
|
icon: "ep/home-filled"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
import { setupStore } from "@/store";
|
import { setupStore } from "@/store";
|
||||||
import { useI18n } from "@/plugins/i18n";
|
|
||||||
import { getPlatformConfig } from "./config";
|
import { getPlatformConfig } from "./config";
|
||||||
import { MotionPlugin } from "@vueuse/motion";
|
import { MotionPlugin } from "@vueuse/motion";
|
||||||
// import { useEcharts } from "@/plugins/echarts";
|
// import { useEcharts } from "@/plugins/echarts";
|
||||||
@@ -58,7 +57,7 @@ getPlatformConfig(app).then(async config => {
|
|||||||
app.use(router);
|
app.use(router);
|
||||||
await router.isReady();
|
await router.isReady();
|
||||||
injectResponsiveStorage(app, config);
|
injectResponsiveStorage(app, config);
|
||||||
app.use(MotionPlugin).use(useI18n).use(useElementPlus).use(Table);
|
app.use(MotionPlugin).use(useElementPlus).use(Table);
|
||||||
// .use(PureDescriptions)
|
// .use(PureDescriptions)
|
||||||
// .use(useEcharts);
|
// .use(useEcharts);
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
// 多组件库的国际化和本地项目国际化兼容
|
|
||||||
import { type I18n, createI18n } from "vue-i18n";
|
|
||||||
import type { App, WritableComputedRef } from "vue";
|
|
||||||
import { responsiveStorageNameSpace } from "@/config";
|
|
||||||
import { storageLocal, isObject } from "@pureadmin/utils";
|
|
||||||
|
|
||||||
// element-plus国际化
|
|
||||||
import enLocale from "element-plus/es/locale/lang/en";
|
|
||||||
import zhLocale from "element-plus/es/locale/lang/zh-cn";
|
|
||||||
|
|
||||||
const siphonI18n = (function () {
|
|
||||||
// 仅初始化一次国际化配置
|
|
||||||
const cache = Object.fromEntries(
|
|
||||||
Object.entries(
|
|
||||||
import.meta.glob("../../locales/*.y(a)?ml", { eager: true })
|
|
||||||
).map(([key, value]: any) => {
|
|
||||||
const matched = key.match(/([A-Za-z0-9-_]+)\./i)[1];
|
|
||||||
return [matched, value.default];
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return (prefix = "zh-CN") => {
|
|
||||||
return cache[prefix];
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
export const localesConfigs = {
|
|
||||||
zh: {
|
|
||||||
...siphonI18n("zh-CN"),
|
|
||||||
...zhLocale
|
|
||||||
},
|
|
||||||
en: {
|
|
||||||
...siphonI18n("en"),
|
|
||||||
...enLocale
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 获取对象中所有嵌套对象的key键,并将它们用点号分割组成字符串 */
|
|
||||||
function getObjectKeys(obj) {
|
|
||||||
const stack = [];
|
|
||||||
const keys: Set<string> = new Set();
|
|
||||||
|
|
||||||
stack.push({ obj, key: "" });
|
|
||||||
|
|
||||||
while (stack.length > 0) {
|
|
||||||
const { obj, key } = stack.pop();
|
|
||||||
|
|
||||||
for (const k in obj) {
|
|
||||||
const newKey = key ? `${key}.${k}` : k;
|
|
||||||
|
|
||||||
if (obj[k] && isObject(obj[k])) {
|
|
||||||
stack.push({ obj: obj[k], key: newKey });
|
|
||||||
} else {
|
|
||||||
keys.add(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 将展开的key缓存 */
|
|
||||||
const keysCache: Map<string, Set<string>> = new Map();
|
|
||||||
const flatI18n = (prefix = "zh-CN") => {
|
|
||||||
let cache = keysCache.get(prefix);
|
|
||||||
if (!cache) {
|
|
||||||
cache = getObjectKeys(siphonI18n(prefix));
|
|
||||||
keysCache.set(prefix, cache);
|
|
||||||
}
|
|
||||||
return cache;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 国际化转换工具函数(自动读取根目录locales文件夹下文件进行国际化匹配)
|
|
||||||
* @param message message
|
|
||||||
* @returns 转化后的message
|
|
||||||
*/
|
|
||||||
export function transformI18n(message: any = "") {
|
|
||||||
if (!message) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理存储动态路由的title,格式 {zh:"",en:""}
|
|
||||||
if (typeof message === "object") {
|
|
||||||
const locale: string | WritableComputedRef<string> | any =
|
|
||||||
i18n.global.locale;
|
|
||||||
return message[locale?.value];
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = message.match(/(\S*)\./)?.input;
|
|
||||||
|
|
||||||
if (key && flatI18n("zh-CN").has(key)) {
|
|
||||||
return i18n.global.t.call(i18n.global.locale, message);
|
|
||||||
} else if (!key && Object.hasOwn(siphonI18n("zh-CN"), message)) {
|
|
||||||
// 兼容非嵌套形式的国际化写法
|
|
||||||
return i18n.global.t.call(i18n.global.locale, message);
|
|
||||||
} else {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 此函数只是配合i18n Ally插件来进行国际化智能提示,并无实际意义(只对提示起作用),如果不需要国际化可删除 */
|
|
||||||
export const $t = (key: string) => key;
|
|
||||||
|
|
||||||
export const i18n: I18n = createI18n({
|
|
||||||
legacy: false,
|
|
||||||
locale:
|
|
||||||
storageLocal().getItem<StorageConfigs>(
|
|
||||||
`${responsiveStorageNameSpace()}locale`
|
|
||||||
)?.locale ?? "zh",
|
|
||||||
fallbackLocale: "en",
|
|
||||||
messages: localesConfigs
|
|
||||||
});
|
|
||||||
|
|
||||||
export function useI18n(app: App) {
|
|
||||||
app.use(i18n);
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from "@/config";
|
||||||
import NProgress from "@/utils/progress";
|
import NProgress from "@/utils/progress";
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import { buildHierarchyTree } from "@/utils/tree";
|
import { buildHierarchyTree } from "@/utils/tree";
|
||||||
import remainingRouter from "./modules/remaining";
|
import remainingRouter from "./modules/remaining";
|
||||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
||||||
@@ -94,14 +93,6 @@ export const router: Router = createRouter({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 记录已经加载的页面路径 */
|
|
||||||
const loadedPaths = new Set<string>();
|
|
||||||
|
|
||||||
/** 重置已加载页面记录 */
|
|
||||||
export function resetLoadedPaths() {
|
|
||||||
loadedPaths.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置路由 */
|
/** 重置路由 */
|
||||||
export function resetRouter() {
|
export function resetRouter() {
|
||||||
router.clearRoutes();
|
router.clearRoutes();
|
||||||
@@ -112,7 +103,6 @@ export function resetRouter() {
|
|||||||
formatFlatteningRoutes(buildHierarchyTree(ascending(routes.flat(Infinity))))
|
formatFlatteningRoutes(buildHierarchyTree(ascending(routes.flat(Infinity))))
|
||||||
);
|
);
|
||||||
usePermissionStoreHook().clearAllCachePage();
|
usePermissionStoreHook().clearAllCachePage();
|
||||||
resetLoadedPaths();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 路由白名单 */
|
/** 路由白名单 */
|
||||||
@@ -121,12 +111,6 @@ const whiteList = ["/login"];
|
|||||||
const { VITE_HIDE_HOME } = import.meta.env;
|
const { VITE_HIDE_HOME } = import.meta.env;
|
||||||
|
|
||||||
router.beforeEach((to: ToRouteType, _from, next) => {
|
router.beforeEach((to: ToRouteType, _from, next) => {
|
||||||
to.meta.loaded = loadedPaths.has(to.path);
|
|
||||||
|
|
||||||
if (!to.meta.loaded) {
|
|
||||||
NProgress.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (to.meta?.keepAlive) {
|
if (to.meta?.keepAlive) {
|
||||||
handleAliveRoute(to, "add");
|
handleAliveRoute(to, "add");
|
||||||
// 页面整体刷新和点击标签页刷新
|
// 页面整体刷新和点击标签页刷新
|
||||||
@@ -135,14 +119,14 @@ router.beforeEach((to: ToRouteType, _from, next) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const userInfo = storageLocal().getItem<DataInfo<number>>(userKey);
|
const userInfo = storageLocal().getItem<DataInfo<number>>(userKey);
|
||||||
|
NProgress.start();
|
||||||
const externalLink = isUrl(to?.name as string);
|
const externalLink = isUrl(to?.name as string);
|
||||||
if (!externalLink) {
|
if (!externalLink) {
|
||||||
to.matched.some(item => {
|
to.matched.some(item => {
|
||||||
if (!item.meta.title) return "";
|
if (!item.meta.title) return "";
|
||||||
const Title = getConfig().Title;
|
const Title = getConfig().Title;
|
||||||
if (Title)
|
if (Title) document.title = `${item.meta.title} | ${Title}`;
|
||||||
document.title = `${transformI18n(item.meta.title)} | ${Title}`;
|
else document.title = item.meta.title as string;
|
||||||
else document.title = transformI18n(item.meta.title);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/** 如果已经登录并存在登录信息后不能跳转到路由白名单,而是继续保持在当前页面 */
|
/** 如果已经登录并存在登录信息后不能跳转到路由白名单,而是继续保持在当前页面 */
|
||||||
@@ -220,8 +204,7 @@ router.beforeEach((to: ToRouteType, _from, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.afterEach(to => {
|
router.afterEach(() => {
|
||||||
loadedPaths.add(to.path);
|
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { $t } from "@/plugins/i18n";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
path: "/error",
|
path: "/error",
|
||||||
redirect: "/error/403",
|
redirect: "/error/403",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "ri/information-line",
|
icon: "ri/information-line",
|
||||||
// showLink: false,
|
// showLink: false,
|
||||||
title: $t("menus.pureAbnormal"),
|
title: "异常页面",
|
||||||
rank: 9
|
rank: 9
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
@@ -15,7 +13,7 @@ export default {
|
|||||||
name: "403",
|
name: "403",
|
||||||
component: () => import("@/views/error/403.vue"),
|
component: () => import("@/views/error/403.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t("menus.pureAccessDenied")
|
title: "403"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -23,7 +21,7 @@ export default {
|
|||||||
name: "404",
|
name: "404",
|
||||||
component: () => import("@/views/error/404.vue"),
|
component: () => import("@/views/error/404.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t("menus.purePageNotFound")
|
title: "404"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -31,7 +29,7 @@ export default {
|
|||||||
name: "500",
|
name: "500",
|
||||||
component: () => import("@/views/error/500.vue"),
|
component: () => import("@/views/error/500.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t("menus.pureServerError")
|
title: "500"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { $t } from "@/plugins/i18n";
|
|
||||||
const { VITE_HIDE_HOME } = import.meta.env;
|
const { VITE_HIDE_HOME } = import.meta.env;
|
||||||
const Layout = () => import("@/layout/index.vue");
|
const Layout = () => import("@/layout/index.vue");
|
||||||
|
|
||||||
@@ -9,7 +8,7 @@ export default {
|
|||||||
redirect: "/welcome",
|
redirect: "/welcome",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "ep/home-filled",
|
icon: "ep/home-filled",
|
||||||
title: $t("menus.pureHome"),
|
title: "首页",
|
||||||
rank: 0
|
rank: 0
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
@@ -18,7 +17,7 @@ export default {
|
|||||||
name: "Welcome",
|
name: "Welcome",
|
||||||
component: () => import("@/views/welcome/index.vue"),
|
component: () => import("@/views/welcome/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t("menus.pureHome"),
|
title: "首页",
|
||||||
showLink: VITE_HIDE_HOME === "true" ? false : true
|
showLink: VITE_HIDE_HOME === "true" ? false : true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { $t } from "@/plugins/i18n";
|
|
||||||
const Layout = () => import("@/layout/index.vue");
|
const Layout = () => import("@/layout/index.vue");
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
@@ -7,36 +6,18 @@ export default [
|
|||||||
name: "Login",
|
name: "Login",
|
||||||
component: () => import("@/views/login/index.vue"),
|
component: () => import("@/views/login/index.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t("menus.pureLogin"),
|
title: "登录",
|
||||||
showLink: false
|
showLink: false,
|
||||||
}
|
rank: 101
|
||||||
},
|
|
||||||
// 全屏403(无权访问)页面
|
|
||||||
{
|
|
||||||
path: "/access-denied",
|
|
||||||
name: "AccessDenied",
|
|
||||||
component: () => import("@/views/error/403.vue"),
|
|
||||||
meta: {
|
|
||||||
title: $t("menus.pureAccessDenied"),
|
|
||||||
showLink: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 全屏500(服务器出错)页面
|
|
||||||
{
|
|
||||||
path: "/server-error",
|
|
||||||
name: "ServerError",
|
|
||||||
component: () => import("@/views/error/500.vue"),
|
|
||||||
meta: {
|
|
||||||
title: $t("menus.pureServerError"),
|
|
||||||
showLink: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/redirect",
|
path: "/redirect",
|
||||||
component: Layout,
|
component: Layout,
|
||||||
meta: {
|
meta: {
|
||||||
title: $t("status.pureLoad"),
|
title: "加载中...",
|
||||||
showLink: false
|
showLink: false,
|
||||||
|
rank: 102
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -139,17 +139,12 @@ function findRouteByPath(path: string, routes: RouteRecordRaw[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 动态路由注册完成后,再添加全屏404(页面不存在)页面,避免刷新动态路由页面时误跳转到404页面 */
|
|
||||||
function addPathMatch() {
|
function addPathMatch() {
|
||||||
if (!router.hasRoute("pathMatch")) {
|
if (!router.hasRoute("pathMatch")) {
|
||||||
router.addRoute({
|
router.addRoute({
|
||||||
path: "/:pathMatch(.*)*",
|
path: "/:pathMatch(.*)",
|
||||||
name: "PageNotFound",
|
name: "pathMatch",
|
||||||
component: () => import("@/views/error/404.vue"),
|
redirect: "/error/404"
|
||||||
meta: {
|
|
||||||
title: "menus.purePageNotFound",
|
|
||||||
showLink: false
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,15 +80,22 @@ export const useMultiTagsStore = defineStore("pure-multiTags", {
|
|||||||
if (isBoolean(tagVal?.meta?.showLink) && !tagVal?.meta?.showLink)
|
if (isBoolean(tagVal?.meta?.showLink) && !tagVal?.meta?.showLink)
|
||||||
return;
|
return;
|
||||||
const tagPath = tagVal.path;
|
const tagPath = tagVal.path;
|
||||||
|
// 判断tag是否已存在
|
||||||
const tagHasExits = this.multiTags.some(tag => {
|
const tagHasExits = this.multiTags.some(tag => {
|
||||||
return (
|
return tag.path === tagPath;
|
||||||
tag.path === tagPath &&
|
|
||||||
isEqual(tag?.query, tagVal?.query) &&
|
|
||||||
isEqual(tag?.params, tagVal?.params)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (tagHasExits) return;
|
// 判断tag中的query键值是否相等
|
||||||
|
const tagQueryHasExits = this.multiTags.some(tag => {
|
||||||
|
return isEqual(tag?.query, tagVal?.query);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 判断tag中的params键值是否相等
|
||||||
|
const tagParamsHasExits = this.multiTags.some(tag => {
|
||||||
|
return isEqual(tag?.params, tagVal?.params);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tagHasExits && tagQueryHasExits && tagParamsHasExits) return;
|
||||||
|
|
||||||
// 动态路由可打开的最大数量
|
// 动态路由可打开的最大数量
|
||||||
const dynamicLevel = tagVal?.meta?.dynamicLevel ?? -1;
|
const dynamicLevel = tagVal?.meta?.dynamicLevel ?? -1;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { defineStore } from "pinia";
|
|||||||
import {
|
import {
|
||||||
type cacheType,
|
type cacheType,
|
||||||
store,
|
store,
|
||||||
|
debounce,
|
||||||
ascending,
|
ascending,
|
||||||
getKeyList,
|
getKeyList,
|
||||||
filterTree,
|
filterTree,
|
||||||
@@ -32,35 +33,33 @@ export const usePermissionStore = defineStore("pure-permission", {
|
|||||||
this.constantMenus.concat(routes) as any
|
this.constantMenus.concat(routes) as any
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
/** 监听缓存页面是否存在于标签页,不存在则删除 */
|
|
||||||
clearCache() {
|
|
||||||
let cacheLength = this.cachePageList.length;
|
|
||||||
const nameList = getKeyList(useMultiTagsStoreHook().multiTags, "name");
|
|
||||||
while (cacheLength > 0) {
|
|
||||||
nameList.findIndex(v => v === this.cachePageList[cacheLength - 1]) ===
|
|
||||||
-1 &&
|
|
||||||
this.cachePageList.splice(
|
|
||||||
this.cachePageList.indexOf(this.cachePageList[cacheLength - 1]),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
cacheLength--;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cacheOperate({ mode, name }: cacheType) {
|
cacheOperate({ mode, name }: cacheType) {
|
||||||
const delIndex = this.cachePageList.findIndex(v => v === name);
|
const delIndex = this.cachePageList.findIndex(v => v === name);
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "refresh":
|
case "refresh":
|
||||||
this.cachePageList = this.cachePageList.filter(v => v !== name);
|
this.cachePageList = this.cachePageList.filter(v => v !== name);
|
||||||
this.clearCache();
|
|
||||||
break;
|
break;
|
||||||
case "add":
|
case "add":
|
||||||
this.cachePageList.push(name);
|
this.cachePageList.push(name);
|
||||||
break;
|
break;
|
||||||
case "delete":
|
case "delete":
|
||||||
delIndex !== -1 && this.cachePageList.splice(delIndex, 1);
|
delIndex !== -1 && this.cachePageList.splice(delIndex, 1);
|
||||||
this.clearCache();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/** 监听缓存页面是否存在于标签页,不存在则删除 */
|
||||||
|
debounce(() => {
|
||||||
|
let cacheLength = this.cachePageList.length;
|
||||||
|
const nameList = getKeyList(useMultiTagsStoreHook().multiTags, "name");
|
||||||
|
while (cacheLength > 0) {
|
||||||
|
nameList.findIndex(v => v === this.cachePageList[cacheLength - 1]) ===
|
||||||
|
-1 &&
|
||||||
|
this.cachePageList.splice(
|
||||||
|
this.cachePageList.indexOf(this.cachePageList[cacheLength - 1]),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
cacheLength--;
|
||||||
|
}
|
||||||
|
})();
|
||||||
},
|
},
|
||||||
/** 清空缓存页面 */
|
/** 清空缓存页面 */
|
||||||
clearAllCachePage() {
|
clearAllCachePage() {
|
||||||
|
|||||||
@@ -172,6 +172,10 @@
|
|||||||
.is-active > .el-sub-menu__title,
|
.is-active > .el-sub-menu__title,
|
||||||
.is-active.submenu-title-noDropdown {
|
.is-active.submenu-title-noDropdown {
|
||||||
color: var(--pure-theme-sub-menu-active-text) !important;
|
color: var(--pure-theme-sub-menu-active-text) !important;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: var(--pure-theme-sub-menu-active-text) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-active {
|
.is-active {
|
||||||
@@ -266,6 +270,10 @@
|
|||||||
.is-active > .el-sub-menu__title,
|
.is-active > .el-sub-menu__title,
|
||||||
.is-active.submenu-title-noDropdown {
|
.is-active.submenu-title-noDropdown {
|
||||||
color: var(--pure-theme-sub-menu-active-text) !important;
|
color: var(--pure-theme-sub-menu-active-text) !important;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: var(--pure-theme-sub-menu-active-text) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 子菜单中还有子菜单 */
|
/* 子菜单中还有子菜单 */
|
||||||
@@ -366,6 +374,10 @@
|
|||||||
.is-active > .el-sub-menu__title,
|
.is-active > .el-sub-menu__title,
|
||||||
.is-active.submenu-title-noDropdown {
|
.is-active.submenu-title-noDropdown {
|
||||||
color: var(--pure-theme-sub-menu-active-text) !important;
|
color: var(--pure-theme-sub-menu-active-text) !important;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: var(--pure-theme-sub-menu-active-text) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nest-menu .el-sub-menu > .el-sub-menu__title,
|
.nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||||
@@ -448,8 +460,6 @@
|
|||||||
|
|
||||||
/* 搜索 */
|
/* 搜索 */
|
||||||
.search-container,
|
.search-container,
|
||||||
/* 国际化 */
|
|
||||||
.globalization,
|
|
||||||
/* 全屏 */
|
/* 全屏 */
|
||||||
.fullscreen-icon,
|
.fullscreen-icon,
|
||||||
/* 消息通知 */
|
/* 消息通知 */
|
||||||
@@ -468,15 +478,6 @@
|
|||||||
color: var(--pure-theme-sub-menu-active-text);
|
color: var(--pure-theme-sub-menu-active-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.globalization {
|
|
||||||
width: 40px;
|
|
||||||
height: 48px;
|
|
||||||
padding: 11px;
|
|
||||||
color: var(--pure-theme-sub-menu-active-text);
|
|
||||||
cursor: pointer;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-dropdown-link {
|
.el-dropdown-link {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -529,6 +530,10 @@
|
|||||||
.is-active > .el-sub-menu__title,
|
.is-active > .el-sub-menu__title,
|
||||||
.is-active.submenu-title-noDropdown {
|
.is-active.submenu-title-noDropdown {
|
||||||
color: var(--pure-theme-sub-menu-active-text) !important;
|
color: var(--pure-theme-sub-menu-active-text) !important;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: var(--pure-theme-sub-menu-active-text) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-active {
|
.is-active {
|
||||||
@@ -629,8 +634,6 @@ body[layout="vertical"] {
|
|||||||
|
|
||||||
/* 搜索 */
|
/* 搜索 */
|
||||||
.search-container,
|
.search-container,
|
||||||
/* 国际化 */
|
|
||||||
.globalization,
|
|
||||||
/* 全屏 */
|
/* 全屏 */
|
||||||
.fullscreen-icon,
|
.fullscreen-icon,
|
||||||
/* 消息通知 */
|
/* 消息通知 */
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import type {
|
|||||||
PureHttpRequestConfig
|
PureHttpRequestConfig
|
||||||
} from "./types.d";
|
} from "./types.d";
|
||||||
import { stringify } from "qs";
|
import { stringify } from "qs";
|
||||||
|
import NProgress from "../progress";
|
||||||
import { getToken, formatToken } from "@/utils/auth";
|
import { getToken, formatToken } from "@/utils/auth";
|
||||||
import { useUserStoreHook } from "@/store/modules/user";
|
import { useUserStoreHook } from "@/store/modules/user";
|
||||||
|
|
||||||
@@ -60,6 +61,8 @@ class PureHttp {
|
|||||||
private httpInterceptorsRequest(): void {
|
private httpInterceptorsRequest(): void {
|
||||||
PureHttp.axiosInstance.interceptors.request.use(
|
PureHttp.axiosInstance.interceptors.request.use(
|
||||||
async (config: PureHttpRequestConfig): Promise<any> => {
|
async (config: PureHttpRequestConfig): Promise<any> => {
|
||||||
|
// 开启进度条动画
|
||||||
|
NProgress.start();
|
||||||
// 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
|
// 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
|
||||||
if (typeof config.beforeRequestCallback === "function") {
|
if (typeof config.beforeRequestCallback === "function") {
|
||||||
config.beforeRequestCallback(config);
|
config.beforeRequestCallback(config);
|
||||||
@@ -118,6 +121,8 @@ class PureHttp {
|
|||||||
instance.interceptors.response.use(
|
instance.interceptors.response.use(
|
||||||
(response: PureHttpResponse) => {
|
(response: PureHttpResponse) => {
|
||||||
const $config = response.config;
|
const $config = response.config;
|
||||||
|
// 关闭进度条动画
|
||||||
|
NProgress.done();
|
||||||
// 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
|
// 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调
|
||||||
if (typeof $config.beforeResponseCallback === "function") {
|
if (typeof $config.beforeResponseCallback === "function") {
|
||||||
$config.beforeResponseCallback(response);
|
$config.beforeResponseCallback(response);
|
||||||
@@ -132,6 +137,8 @@ class PureHttp {
|
|||||||
(error: PureHttpError) => {
|
(error: PureHttpError) => {
|
||||||
const $error = error;
|
const $error = error;
|
||||||
$error.isCancelRequest = Axios.isCancel($error);
|
$error.isCancelRequest = Axios.isCancel($error);
|
||||||
|
// 关闭进度条动画
|
||||||
|
NProgress.done();
|
||||||
// 所有的响应异常 区分来源为取消请求/非取消请求
|
// 所有的响应异常 区分来源为取消请求/非取消请求
|
||||||
return Promise.reject($error);
|
return Promise.reject($error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,6 @@ export const injectResponsiveStorage = (app: App, config: PlatformConfigs) => {
|
|||||||
const nameSpace = responsiveStorageNameSpace();
|
const nameSpace = responsiveStorageNameSpace();
|
||||||
const configObj = Object.assign(
|
const configObj = Object.assign(
|
||||||
{
|
{
|
||||||
// 国际化 默认中文zh
|
|
||||||
locale: Storage.getData("locale", nameSpace) ?? {
|
|
||||||
locale: config.Locale ?? "zh"
|
|
||||||
},
|
|
||||||
// layout模式以及主题
|
// layout模式以及主题
|
||||||
layout: Storage.getData("layout", nameSpace) ?? {
|
layout: Storage.getData("layout", nameSpace) ?? {
|
||||||
layout: config.Layout ?? "vertical",
|
layout: config.Layout ?? "vertical",
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ const router = useRouter();
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="flex justify-center items-center h-[640px]">
|
||||||
class="flex flex-col md:flex-row justify-center items-center min-h-full w-full p-4 md:p-0"
|
|
||||||
>
|
|
||||||
<noAccess />
|
<noAccess />
|
||||||
<div class="mt-8 md:ml-12 md:mt-0 text-center md:text-left">
|
<div class="ml-12">
|
||||||
<p
|
<p
|
||||||
v-motion
|
v-motion
|
||||||
class="font-medium text-4xl mb-4! dark:text-white"
|
class="font-medium text-4xl mb-4! dark:text-white"
|
||||||
@@ -34,7 +32,7 @@ const router = useRouter();
|
|||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
v-motion
|
v-motion
|
||||||
class="text-xl mb-4! text-gray-500"
|
class="mb-4! text-gray-500"
|
||||||
:initial="{
|
:initial="{
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
y: 100
|
y: 100
|
||||||
@@ -52,7 +50,6 @@ const router = useRouter();
|
|||||||
<el-button
|
<el-button
|
||||||
v-motion
|
v-motion
|
||||||
type="primary"
|
type="primary"
|
||||||
class="block mx-auto md:inline-block md:mx-0"
|
|
||||||
:initial="{
|
:initial="{
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
y: 100
|
y: 100
|
||||||
@@ -71,9 +68,3 @@ const router = useRouter();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.main-content {
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ const router = useRouter();
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="flex justify-center items-center h-[640px]">
|
||||||
class="flex flex-col md:flex-row justify-center items-center min-h-full w-full p-4 md:p-0"
|
|
||||||
>
|
|
||||||
<noExist />
|
<noExist />
|
||||||
<div class="mt-8 md:ml-12 md:mt-0 text-center md:text-left">
|
<div class="ml-12">
|
||||||
<p
|
<p
|
||||||
v-motion
|
v-motion
|
||||||
class="font-medium text-4xl mb-4! dark:text-white"
|
class="font-medium text-4xl mb-4! dark:text-white"
|
||||||
@@ -34,7 +32,7 @@ const router = useRouter();
|
|||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
v-motion
|
v-motion
|
||||||
class="text-xl mb-4! text-gray-500"
|
class="mb-4! text-gray-500"
|
||||||
:initial="{
|
:initial="{
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
y: 100
|
y: 100
|
||||||
@@ -52,7 +50,6 @@ const router = useRouter();
|
|||||||
<el-button
|
<el-button
|
||||||
v-motion
|
v-motion
|
||||||
type="primary"
|
type="primary"
|
||||||
class="block mx-auto md:inline-block md:mx-0"
|
|
||||||
:initial="{
|
:initial="{
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
y: 100
|
y: 100
|
||||||
@@ -71,9 +68,3 @@ const router = useRouter();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.main-content {
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ const router = useRouter();
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="flex justify-center items-center h-[640px]">
|
||||||
class="flex flex-col md:flex-row justify-center items-center min-h-full w-full p-4 md:p-0"
|
|
||||||
>
|
|
||||||
<noServer />
|
<noServer />
|
||||||
<div class="mt-8 md:ml-12 md:mt-0 text-center md:text-left">
|
<div class="ml-12">
|
||||||
<p
|
<p
|
||||||
v-motion
|
v-motion
|
||||||
class="font-medium text-4xl mb-4! dark:text-white"
|
class="font-medium text-4xl mb-4! dark:text-white"
|
||||||
@@ -34,7 +32,7 @@ const router = useRouter();
|
|||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
v-motion
|
v-motion
|
||||||
class="text-xl mb-4! text-gray-500"
|
class="mb-4! text-gray-500"
|
||||||
:initial="{
|
:initial="{
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
y: 100
|
y: 100
|
||||||
@@ -52,7 +50,6 @@ const router = useRouter();
|
|||||||
<el-button
|
<el-button
|
||||||
v-motion
|
v-motion
|
||||||
type="primary"
|
type="primary"
|
||||||
class="block mx-auto md:inline-block md:mx-0"
|
|
||||||
:initial="{
|
:initial="{
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
y: 100
|
y: 100
|
||||||
@@ -71,9 +68,3 @@ const router = useRouter();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.main-content {
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import Motion from "./utils/motion";
|
import Motion from "./utils/motion";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { message } from "@/utils/message";
|
import { message } from "@/utils/message";
|
||||||
@@ -9,20 +8,16 @@ import { debounce } from "@pureadmin/utils";
|
|||||||
import { useNav } from "@/layout/hooks/useNav";
|
import { useNav } from "@/layout/hooks/useNav";
|
||||||
import { useEventListener } from "@vueuse/core";
|
import { useEventListener } from "@vueuse/core";
|
||||||
import type { FormInstance } from "element-plus";
|
import type { FormInstance } from "element-plus";
|
||||||
import { $t, transformI18n } from "@/plugins/i18n";
|
|
||||||
import { useLayout } from "@/layout/hooks/useLayout";
|
import { useLayout } from "@/layout/hooks/useLayout";
|
||||||
import { useUserStoreHook } from "@/store/modules/user";
|
import { useUserStoreHook } from "@/store/modules/user";
|
||||||
import { initRouter, getTopMenu } from "@/router/utils";
|
import { initRouter, getTopMenu } from "@/router/utils";
|
||||||
import { bg, avatar, illustration } from "./utils/static";
|
import { bg, avatar, illustration } from "./utils/static";
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
import { useTranslationLang } from "@/layout/hooks/useTranslationLang";
|
|
||||||
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
|
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
|
||||||
|
|
||||||
import dayIcon from "@/assets/svg/day.svg?component";
|
import dayIcon from "@/assets/svg/day.svg?component";
|
||||||
import darkIcon from "@/assets/svg/dark.svg?component";
|
import darkIcon from "@/assets/svg/dark.svg?component";
|
||||||
import globalization from "@/assets/svg/globalization.svg?component";
|
|
||||||
import Lock from "~icons/ri/lock-fill";
|
import Lock from "~icons/ri/lock-fill";
|
||||||
import Check from "~icons/ep/check";
|
|
||||||
import User from "~icons/ri/user-3-fill";
|
import User from "~icons/ri/user-3-fill";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@@ -37,11 +32,9 @@ const ruleFormRef = ref<FormInstance>();
|
|||||||
const { initStorage } = useLayout();
|
const { initStorage } = useLayout();
|
||||||
initStorage();
|
initStorage();
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { dataTheme, overallStyle, dataThemeChange } = useDataThemeChange();
|
const { dataTheme, overallStyle, dataThemeChange } = useDataThemeChange();
|
||||||
dataThemeChange(overallStyle.value);
|
dataThemeChange(overallStyle.value);
|
||||||
const { title, getDropdownItemStyle, getDropdownItemClass } = useNav();
|
const { title } = useNav();
|
||||||
const { locale, translationCh, translationEn } = useTranslationLang();
|
|
||||||
|
|
||||||
const ruleForm = reactive({
|
const ruleForm = reactive({
|
||||||
username: "admin",
|
username: "admin",
|
||||||
@@ -66,12 +59,12 @@ const onLogin = async (formEl: FormInstance | undefined) => {
|
|||||||
router
|
router
|
||||||
.push(getTopMenu(true).path)
|
.push(getTopMenu(true).path)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
message(t("login.pureLoginSuccess"), { type: "success" });
|
message("登录成功", { type: "success" });
|
||||||
})
|
})
|
||||||
.finally(() => (disabled.value = false));
|
.finally(() => (disabled.value = false));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
message(t("login.pureLoginFail"), { type: "error" });
|
message("登录失败", { type: "error" });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => (loading.value = false));
|
.finally(() => (loading.value = false));
|
||||||
@@ -107,38 +100,6 @@ useEventListener(document, "keydown", ({ code }) => {
|
|||||||
:inactive-icon="darkIcon"
|
:inactive-icon="darkIcon"
|
||||||
@change="dataThemeChange"
|
@change="dataThemeChange"
|
||||||
/>
|
/>
|
||||||
<!-- 国际化 -->
|
|
||||||
<el-dropdown trigger="click">
|
|
||||||
<globalization
|
|
||||||
class="hover:text-primary hover:bg-[transparent]! w-[20px] h-[20px] ml-1.5 cursor-pointer outline-hidden duration-300"
|
|
||||||
/>
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu class="translation">
|
|
||||||
<el-dropdown-item
|
|
||||||
:style="getDropdownItemStyle(locale, 'zh')"
|
|
||||||
:class="['dark:text-white!', getDropdownItemClass(locale, 'zh')]"
|
|
||||||
@click="translationCh"
|
|
||||||
>
|
|
||||||
<IconifyIconOffline
|
|
||||||
v-show="locale === 'zh'"
|
|
||||||
class="check-zh"
|
|
||||||
:icon="Check"
|
|
||||||
/>
|
|
||||||
简体中文
|
|
||||||
</el-dropdown-item>
|
|
||||||
<el-dropdown-item
|
|
||||||
:style="getDropdownItemStyle(locale, 'en')"
|
|
||||||
:class="['dark:text-white!', getDropdownItemClass(locale, 'en')]"
|
|
||||||
@click="translationEn"
|
|
||||||
>
|
|
||||||
<span v-show="locale === 'en'" class="check-en">
|
|
||||||
<IconifyIconOffline :icon="Check" />
|
|
||||||
</span>
|
|
||||||
English
|
|
||||||
</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<div class="img">
|
<div class="img">
|
||||||
@@ -162,7 +123,7 @@ useEventListener(document, "keydown", ({ code }) => {
|
|||||||
:rules="[
|
:rules="[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: transformI18n($t('login.pureUsernameReg')),
|
message: '请输入账号',
|
||||||
trigger: 'blur'
|
trigger: 'blur'
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
@@ -171,7 +132,7 @@ useEventListener(document, "keydown", ({ code }) => {
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="ruleForm.username"
|
v-model="ruleForm.username"
|
||||||
clearable
|
clearable
|
||||||
:placeholder="t('login.pureUsername')"
|
placeholder="账号"
|
||||||
:prefix-icon="useRenderIcon(User)"
|
:prefix-icon="useRenderIcon(User)"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -183,7 +144,7 @@ useEventListener(document, "keydown", ({ code }) => {
|
|||||||
v-model="ruleForm.password"
|
v-model="ruleForm.password"
|
||||||
clearable
|
clearable
|
||||||
show-password
|
show-password
|
||||||
:placeholder="t('login.purePassword')"
|
placeholder="密码"
|
||||||
:prefix-icon="useRenderIcon(Lock)"
|
:prefix-icon="useRenderIcon(Lock)"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -198,7 +159,7 @@ useEventListener(document, "keydown", ({ code }) => {
|
|||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@click="onLogin(ruleFormRef)"
|
@click="onLogin(ruleFormRef)"
|
||||||
>
|
>
|
||||||
{{ t("login.pureLogin") }}
|
登录
|
||||||
</el-button>
|
</el-button>
|
||||||
</Motion>
|
</Motion>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -216,20 +177,4 @@ useEventListener(document, "keydown", ({ code }) => {
|
|||||||
:deep(.el-input-group__append, .el-input-group__prepend) {
|
:deep(.el-input-group__append, .el-input-group__prepend) {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.translation {
|
|
||||||
::v-deep(.el-dropdown-menu__item) {
|
|
||||||
padding: 5px 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.check-zh {
|
|
||||||
position: absolute;
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.check-en {
|
|
||||||
position: absolute;
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { reactive } from "vue";
|
import { reactive } from "vue";
|
||||||
import type { FormRules } from "element-plus";
|
import type { FormRules } from "element-plus";
|
||||||
import { $t, transformI18n } from "@/plugins/i18n";
|
|
||||||
|
|
||||||
/** 密码正则(密码格式应为8-18位数字、字母、符号的任意两种组合) */
|
/** 密码正则(密码格式应为8-18位数字、字母、符号的任意两种组合) */
|
||||||
export const REGEXP_PWD =
|
export const REGEXP_PWD =
|
||||||
@@ -12,9 +11,11 @@ const loginRules = reactive<FormRules>({
|
|||||||
{
|
{
|
||||||
validator: (rule, value, callback) => {
|
validator: (rule, value, callback) => {
|
||||||
if (value === "") {
|
if (value === "") {
|
||||||
callback(new Error(transformI18n($t("login.purePassWordReg"))));
|
callback(new Error("请输入密码"));
|
||||||
} else if (!REGEXP_PWD.test(value)) {
|
} else if (!REGEXP_PWD.test(value)) {
|
||||||
callback(new Error(transformI18n($t("login.purePassWordRuleReg"))));
|
callback(
|
||||||
|
new Error("密码格式应为8-18位数字、字母、符号的任意两种组合")
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ defineOptions({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h1>Pure-Admin-Thin(国际化版本)</h1>
|
<h1>Pure-Admin-Thin(非国际化版本)</h1>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
2
types/router.d.ts
vendored
2
types/router.d.ts
vendored
@@ -56,8 +56,6 @@ declare global {
|
|||||||
* 而通过设置`activePath`指定激活菜单即可获得高亮,`activePath`为指定激活菜单的`path`)
|
* 而通过设置`activePath`指定激活菜单即可获得高亮,`activePath`为指定激活菜单的`path`)
|
||||||
*/
|
*/
|
||||||
activePath?: string;
|
activePath?: string;
|
||||||
/** 当前页面是否已经加载过 */
|
|
||||||
loaded?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user