feat(projects): new i18n function $t & login page and setting drawer config i18n

This commit is contained in:
Soybean 2023-07-23 20:19:47 +08:00
parent 458e387b68
commit 854d0bcf20
49 changed files with 1176 additions and 543 deletions

29
.vscode/settings.json vendored
View File

@ -1,11 +1,32 @@
{
"cSpell.ignorePaths": [
"package.json",
"package-lock.json",
"yarn.lock",
"pnpm-lock.yaml",
"node_modules",
"vscode-extension",
".git/objects",
".vscode",
".vscode-insiders",
"CHANGELOG.md",
"dist",
"public",
"styles"
],
"cSpell.words": [
"AMAP",
"antv",
"bmapgl",
"colord",
"echarts",
"gitee",
"gridicons",
"iconify",
"jsapi",
"naiveui",
"Sider",
"tauri",
"unocss",
"unplugin",
"vditor",
@ -29,7 +50,13 @@
"*.svg": "html"
},
"files.eol": "\n",
"i18n-ally.localesPaths": ["src/locales", "src/locales/lang"],
"i18n-ally.displayLanguage": "zh-CN",
"i18n-ally.enabledParsers": ["ts"],
"i18n-ally.enabledFrameworks": ["vue"],
"i18n-ally.editor.preferEditor": true,
"i18n-ally.keystyle": "nested",
"i18n-ally.localesPaths": ["src/locales/lang"],
"material-icon-theme.activeIconPack": "vue",
"[html][css][less][scss][sass][markdown][yaml][yml][jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true

View File

@ -13,7 +13,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
title: '分析页',
requiresAuth: true,
icon: 'icon-park-outline:analysis',
i18nTitle: 'message.routes.dashboard.analysis'
i18nTitle: 'routes.dashboard.analysis'
}
},
{
@ -24,7 +24,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
title: '工作台',
requiresAuth: true,
icon: 'icon-park-outline:workbench',
i18nTitle: 'message.routes.dashboard.workbench'
i18nTitle: 'routes.dashboard.workbench'
}
}
],
@ -32,7 +32,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
title: '仪表盘',
icon: 'mdi:monitor-dashboard',
order: 1,
i18nTitle: 'message.routes.dashboard._value'
i18nTitle: 'routes.dashboard._value'
}
},
{
@ -46,7 +46,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'vue文档',
i18nTitle: 'message.routes.document.vue',
i18nTitle: 'routes.document.vue',
requiresAuth: true,
icon: 'logos:vue'
}
@ -57,7 +57,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'vite文档',
i18nTitle: 'message.routes.document.vite',
i18nTitle: 'routes.document.vite',
requiresAuth: true,
icon: 'logos:vitejs'
}
@ -68,7 +68,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'naive文档',
i18nTitle: 'message.routes.document.naive',
i18nTitle: 'routes.document.naive',
requiresAuth: true,
icon: 'logos:naiveui'
}
@ -79,7 +79,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '项目文档',
i18nTitle: 'message.routes.document.project',
i18nTitle: 'routes.document.project',
requiresAuth: true,
localIcon: 'logo'
}
@ -89,7 +89,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
path: '/document/project-link',
meta: {
title: '项目文档(外链)',
i18nTitle: 'message.routes.document.project-link',
i18nTitle: 'routes.document.project-link',
requiresAuth: true,
localIcon: 'logo',
href: 'https://docs.soybean.pro/'
@ -98,7 +98,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '文档',
i18nTitle: 'message.routes.document._value',
i18nTitle: 'routes.document._value',
icon: 'mdi:file-document-multiple-outline',
order: 2
}
@ -114,7 +114,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '按钮',
i18nTitle: 'message.routes.component.button',
i18nTitle: 'routes.component.button',
requiresAuth: true,
icon: 'mdi:button-cursor'
}
@ -125,7 +125,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '卡片',
i18nTitle: 'message.routes.component.card',
i18nTitle: 'routes.component.card',
requiresAuth: true,
icon: 'mdi:card-outline'
}
@ -136,7 +136,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '表格',
i18nTitle: 'message.routes.component.table',
i18nTitle: 'routes.component.table',
requiresAuth: true,
icon: 'mdi:table-large'
}
@ -144,7 +144,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '组件示例',
i18nTitle: 'message.routes.component._value',
i18nTitle: 'routes.component._value',
icon: 'cib:app-store',
order: 3
}
@ -165,7 +165,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'ECharts',
i18nTitle: 'message.routes.plugin.charts.echarts',
i18nTitle: 'routes.plugin.charts.echarts',
requiresAuth: true,
icon: 'simple-icons:apacheecharts'
}
@ -176,7 +176,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'AntV',
i18nTitle: 'message.routes.plugin.charts.antv',
i18nTitle: 'routes.plugin.charts.antv',
requiresAuth: true,
icon: 'simple-icons:antdesign'
}
@ -184,7 +184,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '图表',
i18nTitle: 'message.routes.plugin.charts._value',
i18nTitle: 'routes.plugin.charts._value',
icon: 'mdi:chart-areaspline'
}
},
@ -194,7 +194,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '地图',
i18nTitle: 'message.routes.plugin.map',
i18nTitle: 'routes.plugin.map',
requiresAuth: true,
icon: 'mdi:map'
}
@ -205,7 +205,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '视频',
i18nTitle: 'message.routes.plugin.video',
i18nTitle: 'routes.plugin.video',
requiresAuth: true,
icon: 'mdi:video'
}
@ -221,7 +221,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '富文本编辑器',
i18nTitle: 'message.routes.plugin.editor.quill',
i18nTitle: 'routes.plugin.editor.quill',
requiresAuth: true,
icon: 'mdi:file-document-edit-outline'
}
@ -232,7 +232,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'markdown编辑器',
i18nTitle: 'message.routes.plugin.editor.markdown',
i18nTitle: 'routes.plugin.editor.markdown',
requiresAuth: true,
icon: 'ri:markdown-line'
}
@ -240,7 +240,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '编辑器',
i18nTitle: 'message.routes.plugin.editor._value',
i18nTitle: 'routes.plugin.editor._value',
icon: 'icon-park-outline:editor'
}
},
@ -250,7 +250,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'Swiper插件',
i18nTitle: 'message.routes.plugin.swiper',
i18nTitle: 'routes.plugin.swiper',
requiresAuth: true,
icon: 'simple-icons:swiper'
}
@ -261,7 +261,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '剪贴板',
i18nTitle: 'message.routes.plugin.copy',
i18nTitle: 'routes.plugin.copy',
requiresAuth: true,
icon: 'mdi:clipboard-outline'
}
@ -272,7 +272,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '图标',
i18nTitle: 'message.routes.plugin.icon',
i18nTitle: 'routes.plugin.icon',
requiresAuth: true,
localIcon: 'custom-icon'
}
@ -283,7 +283,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '打印',
i18nTitle: 'message.routes.plugin.print',
i18nTitle: 'routes.plugin.print',
requiresAuth: true,
icon: 'mdi:printer'
}
@ -291,7 +291,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '插件示例',
i18nTitle: 'message.routes.plugin._value',
i18nTitle: 'routes.plugin._value',
icon: 'clarity:plugin-line',
order: 4
}
@ -307,7 +307,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '权限切换',
i18nTitle: 'message.routes.auth-demo.permission',
i18nTitle: 'routes.auth-demo.permission',
requiresAuth: true,
icon: 'ic:round-construction'
}
@ -318,7 +318,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '超级管理员可见',
i18nTitle: 'message.routes.auth-demo.super',
i18nTitle: 'routes.auth-demo.super',
requiresAuth: true,
icon: 'ic:round-supervisor-account'
}
@ -326,7 +326,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '权限示例',
i18nTitle: 'message.routes.auth-demo._value',
i18nTitle: 'routes.auth-demo._value',
icon: 'ic:baseline-security',
order: 5
}
@ -342,7 +342,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'Tab',
i18nTitle: 'message.routes.function.tab',
i18nTitle: 'routes.function.tab',
requiresAuth: true,
icon: 'ic:round-tab'
}
@ -375,7 +375,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '功能',
i18nTitle: 'message.routes.function._value',
i18nTitle: 'routes.function._value',
icon: 'icon-park-outline:all-application',
order: 6
}
@ -391,7 +391,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '异常页403',
i18nTitle: 'message.routes.exception.403',
i18nTitle: 'routes.exception.403',
requiresAuth: true,
icon: 'ic:baseline-block'
}
@ -402,7 +402,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '异常页404',
i18nTitle: 'message.routes.exception.404',
i18nTitle: 'routes.exception.404',
requiresAuth: true,
icon: 'ic:baseline-web-asset-off'
}
@ -413,14 +413,14 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '异常页500',
i18nTitle: 'message.routes.exception.500',
i18nTitle: 'routes.exception.500',
requiresAuth: true,
icon: 'ic:baseline-wifi-off'
}
}
],
meta: {
i18nTitle: 'message.routes.exception._value',
i18nTitle: 'routes.exception._value',
title: '异常页',
icon: 'ant-design:exception-outlined',
order: 7
@ -442,7 +442,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '二级菜单',
i18nTitle: 'message.routes.multi-menu.first.second',
i18nTitle: 'routes.multi-menu.first.second',
requiresAuth: true,
icon: 'mdi:menu'
}
@ -458,7 +458,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '三级菜单',
i18nTitle: 'message.routes.multi-menu.first.second-new.third',
i18nTitle: 'routes.multi-menu.first.second-new.third',
requiresAuth: true,
icon: 'mdi:menu'
}
@ -466,21 +466,21 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '二级菜单(有子菜单)',
i18nTitle: 'message.routes.multi-menu.first.second-new._value',
i18nTitle: 'routes.multi-menu.first.second-new._value',
icon: 'mdi:menu'
}
}
],
meta: {
title: '一级菜单',
i18nTitle: 'message.routes.multi-menu.first._value',
i18nTitle: 'routes.multi-menu.first._value',
icon: 'mdi:menu'
}
}
],
meta: {
title: '多级菜单',
i18nTitle: 'message.routes.multi-menu._value',
i18nTitle: 'routes.multi-menu._value',
icon: 'carbon:menu',
order: 8
}
@ -496,7 +496,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '权限管理',
i18nTitle: 'message.routes.management.auth',
i18nTitle: 'routes.management.auth',
requiresAuth: true,
icon: 'ic:baseline-security'
}
@ -507,7 +507,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '角色管理',
i18nTitle: 'message.routes.management.role',
i18nTitle: 'routes.management.role',
requiresAuth: true,
icon: 'carbon:user-role'
}
@ -518,7 +518,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '用户管理',
i18nTitle: 'message.routes.management.user',
i18nTitle: 'routes.management.user',
requiresAuth: true,
icon: 'ic:round-manage-accounts'
}
@ -529,7 +529,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '路由管理',
i18nTitle: 'message.routes.management.route',
i18nTitle: 'routes.management.route',
requiresAuth: true,
icon: 'material-symbols:route'
}
@ -537,7 +537,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '系统管理',
i18nTitle: 'message.routes.management._value',
i18nTitle: 'routes.management._value',
icon: 'carbon:cloud-service-management',
order: 9
}
@ -548,7 +548,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '关于',
i18nTitle: 'message.routes.about',
i18nTitle: 'routes.about',
requiresAuth: true,
keepAlive: true,
singleLayout: 'basic',
@ -571,7 +571,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
title: '分析页',
requiresAuth: true,
icon: 'icon-park-outline:analysis',
i18nTitle: 'message.routes.dashboard.analysis'
i18nTitle: 'routes.dashboard.analysis'
}
},
{
@ -582,7 +582,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
title: '工作台',
requiresAuth: true,
icon: 'icon-park-outline:workbench',
i18nTitle: 'message.routes.dashboard.workbench'
i18nTitle: 'routes.dashboard.workbench'
}
}
],
@ -590,7 +590,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
title: '仪表盘',
icon: 'mdi:monitor-dashboard',
order: 1,
i18nTitle: 'message.routes.dashboard._value'
i18nTitle: 'routes.dashboard._value'
}
},
{
@ -604,7 +604,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'vue文档',
i18nTitle: 'message.routes.document.vue',
i18nTitle: 'routes.document.vue',
requiresAuth: true,
icon: 'logos:vue'
}
@ -615,7 +615,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'vite文档',
i18nTitle: 'message.routes.document.vite',
i18nTitle: 'routes.document.vite',
requiresAuth: true,
icon: 'logos:vitejs'
}
@ -626,7 +626,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'naive文档',
i18nTitle: 'message.routes.document.naive',
i18nTitle: 'routes.document.naive',
requiresAuth: true,
icon: 'logos:naiveui'
}
@ -637,7 +637,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '项目文档',
i18nTitle: 'message.routes.document.project',
i18nTitle: 'routes.document.project',
requiresAuth: true,
localIcon: 'logo'
}
@ -647,7 +647,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
path: '/document/project-link',
meta: {
title: '项目文档(外链)',
i18nTitle: 'message.routes.document.project-link',
i18nTitle: 'routes.document.project-link',
requiresAuth: true,
localIcon: 'logo',
href: 'https://docs.soybean.pro/'
@ -656,7 +656,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '文档',
i18nTitle: 'message.routes.document._value',
i18nTitle: 'routes.document._value',
icon: 'mdi:file-document-multiple-outline',
order: 2
}
@ -672,7 +672,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '按钮',
i18nTitle: 'message.routes.component.button',
i18nTitle: 'routes.component.button',
requiresAuth: true,
icon: 'mdi:button-cursor'
}
@ -683,7 +683,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '卡片',
i18nTitle: 'message.routes.component.card',
i18nTitle: 'routes.component.card',
requiresAuth: true,
icon: 'mdi:card-outline'
}
@ -694,7 +694,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '表格',
i18nTitle: 'message.routes.component.table',
i18nTitle: 'routes.component.table',
requiresAuth: true,
icon: 'mdi:table-large'
}
@ -702,7 +702,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '组件示例',
i18nTitle: 'message.routes.component._value',
i18nTitle: 'routes.component._value',
icon: 'cib:app-store',
order: 3
}
@ -723,7 +723,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'ECharts',
i18nTitle: 'message.routes.plugin.charts.echarts',
i18nTitle: 'routes.plugin.charts.echarts',
requiresAuth: true,
icon: 'simple-icons:apacheecharts'
}
@ -734,7 +734,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'AntV',
i18nTitle: 'message.routes.plugin.charts.antv',
i18nTitle: 'routes.plugin.charts.antv',
requiresAuth: true,
icon: 'simple-icons:antdesign'
}
@ -742,7 +742,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '图表',
i18nTitle: 'message.routes.plugin.charts._value',
i18nTitle: 'routes.plugin.charts._value',
icon: 'mdi:chart-areaspline'
}
},
@ -752,7 +752,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '地图',
i18nTitle: 'message.routes.plugin.map',
i18nTitle: 'routes.plugin.map',
requiresAuth: true,
icon: 'mdi:map'
}
@ -763,7 +763,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '视频',
i18nTitle: 'message.routes.plugin.video',
i18nTitle: 'routes.plugin.video',
requiresAuth: true,
icon: 'mdi:video'
}
@ -779,7 +779,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '富文本编辑器',
i18nTitle: 'message.routes.plugin.editor.quill',
i18nTitle: 'routes.plugin.editor.quill',
requiresAuth: true,
icon: 'mdi:file-document-edit-outline'
}
@ -790,7 +790,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'markdown编辑器',
i18nTitle: 'message.routes.plugin.editor.markdown',
i18nTitle: 'routes.plugin.editor.markdown',
requiresAuth: true,
icon: 'ri:markdown-line'
}
@ -798,7 +798,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '编辑器',
i18nTitle: 'message.routes.plugin.editor._value',
i18nTitle: 'routes.plugin.editor._value',
icon: 'icon-park-outline:editor'
}
},
@ -808,7 +808,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'Swiper插件',
i18nTitle: 'message.routes.plugin.swiper',
i18nTitle: 'routes.plugin.swiper',
requiresAuth: true,
icon: 'simple-icons:swiper'
}
@ -819,7 +819,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '剪贴板',
i18nTitle: 'message.routes.plugin.copy',
i18nTitle: 'routes.plugin.copy',
requiresAuth: true,
icon: 'mdi:clipboard-outline'
}
@ -830,7 +830,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '图标',
i18nTitle: 'message.routes.plugin.icon',
i18nTitle: 'routes.plugin.icon',
requiresAuth: true,
localIcon: 'custom-icon'
}
@ -841,7 +841,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '打印',
i18nTitle: 'message.routes.plugin.print',
i18nTitle: 'routes.plugin.print',
requiresAuth: true,
icon: 'mdi:printer'
}
@ -849,7 +849,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '插件示例',
i18nTitle: 'message.routes.plugin._value',
i18nTitle: 'routes.plugin._value',
icon: 'clarity:plugin-line',
order: 4
}
@ -865,7 +865,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '权限切换',
i18nTitle: 'message.routes.auth-demo.permission',
i18nTitle: 'routes.auth-demo.permission',
requiresAuth: true,
icon: 'ic:round-construction'
}
@ -876,7 +876,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '超级管理员可见',
i18nTitle: 'message.routes.auth-demo.super',
i18nTitle: 'routes.auth-demo.super',
requiresAuth: true,
icon: 'ic:round-supervisor-account'
}
@ -884,7 +884,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '权限示例',
i18nTitle: 'message.routes.auth-demo._value',
i18nTitle: 'routes.auth-demo._value',
icon: 'ic:baseline-security',
order: 5
}
@ -900,7 +900,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: 'Tab',
i18nTitle: 'message.routes.function.tab',
i18nTitle: 'routes.function.tab',
requiresAuth: true,
icon: 'ic:round-tab'
}
@ -933,7 +933,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '功能',
i18nTitle: 'message.routes.function._value',
i18nTitle: 'routes.function._value',
icon: 'icon-park-outline:all-application',
order: 6
}
@ -949,7 +949,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '异常页403',
i18nTitle: 'message.routes.exception.403',
i18nTitle: 'routes.exception.403',
requiresAuth: true,
icon: 'ic:baseline-block'
}
@ -960,7 +960,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '异常页404',
i18nTitle: 'message.routes.exception.404',
i18nTitle: 'routes.exception.404',
requiresAuth: true,
icon: 'ic:baseline-web-asset-off'
}
@ -971,14 +971,14 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '异常页500',
i18nTitle: 'message.routes.exception.500',
i18nTitle: 'routes.exception.500',
requiresAuth: true,
icon: 'ic:baseline-wifi-off'
}
}
],
meta: {
i18nTitle: 'message.routes.exception._value',
i18nTitle: 'routes.exception._value',
title: '异常页',
icon: 'ant-design:exception-outlined',
order: 7
@ -1000,7 +1000,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '二级菜单',
i18nTitle: 'message.routes.multi-menu.first.second',
i18nTitle: 'routes.multi-menu.first.second',
requiresAuth: true,
icon: 'mdi:menu'
}
@ -1016,7 +1016,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '三级菜单',
i18nTitle: 'message.routes.multi-menu.first.second-new.third',
i18nTitle: 'routes.multi-menu.first.second-new.third',
requiresAuth: true,
icon: 'mdi:menu'
}
@ -1024,21 +1024,21 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '二级菜单(有子菜单)',
i18nTitle: 'message.routes.multi-menu.first.second-new._value',
i18nTitle: 'routes.multi-menu.first.second-new._value',
icon: 'mdi:menu'
}
}
],
meta: {
title: '一级菜单',
i18nTitle: 'message.routes.multi-menu.first._value',
i18nTitle: 'routes.multi-menu.first._value',
icon: 'mdi:menu'
}
}
],
meta: {
title: '多级菜单',
i18nTitle: 'message.routes.multi-menu._value',
i18nTitle: 'routes.multi-menu._value',
icon: 'carbon:menu',
order: 8
}
@ -1054,7 +1054,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '权限管理',
i18nTitle: 'message.routes.management.auth',
i18nTitle: 'routes.management.auth',
requiresAuth: true,
icon: 'ic:baseline-security'
}
@ -1065,7 +1065,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '角色管理',
i18nTitle: 'message.routes.management.role',
i18nTitle: 'routes.management.role',
requiresAuth: true,
icon: 'carbon:user-role'
}
@ -1076,7 +1076,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '用户管理',
i18nTitle: 'message.routes.management.user',
i18nTitle: 'routes.management.user',
requiresAuth: true,
icon: 'ic:round-manage-accounts'
}
@ -1087,7 +1087,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '路由管理',
i18nTitle: 'message.routes.management.route',
i18nTitle: 'routes.management.route',
requiresAuth: true,
icon: 'material-symbols:route'
}
@ -1095,7 +1095,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '系统管理',
i18nTitle: 'message.routes.management._value',
i18nTitle: 'routes.management._value',
icon: 'carbon:cloud-service-management',
order: 9
}
@ -1106,7 +1106,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '关于',
i18nTitle: 'message.routes.about',
i18nTitle: 'routes.about',
requiresAuth: true,
keepAlive: true,
singleLayout: 'basic',
@ -1129,7 +1129,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
title: '分析页',
requiresAuth: true,
icon: 'icon-park-outline:analysis',
i18nTitle: 'message.routes.dashboard.analysis'
i18nTitle: 'routes.dashboard.analysis'
}
},
{
@ -1140,7 +1140,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
title: '工作台',
requiresAuth: true,
icon: 'icon-park-outline:workbench',
i18nTitle: 'message.routes.dashboard.workbench'
i18nTitle: 'routes.dashboard.workbench'
}
}
],
@ -1148,7 +1148,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
title: '仪表盘',
icon: 'mdi:monitor-dashboard',
order: 1,
i18nTitle: 'message.routes.dashboard._value'
i18nTitle: 'routes.dashboard._value'
}
},
{
@ -1162,7 +1162,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '权限切换',
i18nTitle: 'message.routes.auth-demo.permission',
i18nTitle: 'routes.auth-demo.permission',
requiresAuth: true,
icon: 'ic:round-construction'
}
@ -1173,7 +1173,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '超级管理员可见',
i18nTitle: 'message.routes.auth-demo.super',
i18nTitle: 'routes.auth-demo.super',
requiresAuth: true,
icon: 'ic:round-supervisor-account'
}
@ -1181,7 +1181,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '权限示例',
i18nTitle: 'message.routes.auth-demo._value',
i18nTitle: 'routes.auth-demo._value',
icon: 'ic:baseline-security',
order: 5
}
@ -1202,7 +1202,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '二级菜单',
i18nTitle: 'message.routes.multi-menu.first.second',
i18nTitle: 'routes.multi-menu.first.second',
requiresAuth: true,
icon: 'mdi:menu'
}
@ -1218,7 +1218,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '三级菜单',
i18nTitle: 'message.routes.multi-menu.first.second-new.third',
i18nTitle: 'routes.multi-menu.first.second-new.third',
requiresAuth: true,
icon: 'mdi:menu'
}
@ -1226,21 +1226,21 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
],
meta: {
title: '二级菜单(有子菜单)',
i18nTitle: 'message.routes.multi-menu.first.second-new._value',
i18nTitle: 'routes.multi-menu.first.second-new._value',
icon: 'mdi:menu'
}
}
],
meta: {
title: '一级菜单',
i18nTitle: 'message.routes.multi-menu.first._value',
i18nTitle: 'routes.multi-menu.first._value',
icon: 'mdi:menu'
}
}
],
meta: {
title: '多级菜单',
i18nTitle: 'message.routes.multi-menu._value',
i18nTitle: 'routes.multi-menu._value',
icon: 'carbon:menu',
order: 8
}
@ -1251,7 +1251,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
component: 'self',
meta: {
title: '关于',
i18nTitle: 'message.routes.about',
i18nTitle: 'routes.about',
requiresAuth: true,
keepAlive: true,
singleLayout: 'basic',

View File

@ -11,17 +11,15 @@
></div>
</div>
</div>
<h2 class="text-28px font-500 text-#646464">{{ title }}</h2>
<h2 class="text-28px font-500 text-#646464">{{ $t('system.title') }}</h2>
</div>
</template>
<script setup lang="ts">
import { useAppInfo } from '@/composables';
import { sessionStg, getRgbOfColor } from '@/utils';
import { $t } from '@/locales';
import themeSettings from '@/settings/theme.json';
const { title } = useAppInfo();
const lodingClasses = [
'left-0 top-0',
'left-0 bottom-0 animate-delay-500',

View File

@ -2,26 +2,6 @@ import UAParser from 'ua-parser-js';
import { useAuthStore } from '@/store';
import { isArray, isString } from '@/utils';
interface AppInfo {
/** 项目名称 */
name: string;
/** 项目标题 */
title: string;
/** 项目描述 */
desc: string;
}
/** 项目信息 */
export function useAppInfo(): AppInfo {
const { VITE_APP_NAME: name, VITE_APP_TITLE: title, VITE_APP_DESC: desc } = import.meta.env;
return {
name,
title,
desc
};
}
/** 获取设备信息 */
export function useDeviceInfo() {
const parser = new UAParser();

View File

@ -1,17 +1,18 @@
import { $t } from '@/locales';
import { transformObjectToOption } from './_shared';
export const loginModuleLabels: Record<UnionKey.LoginModule, string> = {
'pwd-login': '账密登录',
'code-login': '手机验证码登录',
register: '注册',
'reset-pwd': '重置密码',
'bind-wechat': '微信绑定'
'pwd-login': $t('page.login.pwdLogin.title'),
'code-login': $t('page.login.codeLogin.title'),
register: $t('page.login.register.title'),
'reset-pwd': $t('page.login.resetPwd.title'),
'bind-wechat': $t('page.login.bindWeChat.title')
};
export const userRoleLabels: Record<Auth.RoleType, string> = {
super: '超级管理员',
admin: '管理员',
user: '普通用户'
super: $t('page.login.pwdLogin.superAdmin'),
admin: $t('page.login.pwdLogin.admin'),
user: $t('page.login.pwdLogin.user')
};
export const userRoleOptions = transformObjectToOption(userRoleLabels);

View File

@ -35,7 +35,7 @@ import { routePath } from '@/router';
import { useRouteStore, useThemeStore } from '@/store';
import { useRouterPush } from '@/composables';
import { getBreadcrumbByRouteKey } from '@/utils';
import { t } from '@/locales';
import { $t } from '@/locales';
defineOptions({ name: 'GlobalBreadcrumb' });
@ -48,8 +48,8 @@ const breadcrumbs = computed(() =>
getBreadcrumbByRouteKey(route.name as string, routeStore.menus as App.GlobalMenuOption[], routePath('root')).map(
item => ({
...item,
label: item.i18nTitle ? t(item.i18nTitle) : item.label,
options: item.options?.map(oItem => ({ ...oItem, label: oItem.i18nTitle ? t(oItem.i18nTitle) : oItem.label }))
label: item.i18nTitle ? $t(item.i18nTitle) : item.label,
options: item.options?.map(oItem => ({ ...oItem, label: oItem.i18nTitle ? $t(oItem.i18nTitle) : oItem.label }))
})
)
);

View File

@ -15,7 +15,7 @@ import { localStg } from '@/utils';
const theme = useThemeStore();
const { locale } = useI18n();
const language = ref<I18nType.langType>(localStg.get('lang') || 'zh-CN');
const language = ref<I18nType.LangType>(localStg.get('lang') || 'zh-CN');
const options = [
{
label: '中文',
@ -31,9 +31,9 @@ const options = [
}
];
const handleSelect = (key: string) => {
language.value = key as I18nType.langType;
language.value = key as I18nType.LangType;
locale.value = key;
localStg.set('lang', key as I18nType.langType);
localStg.set('lang', key as I18nType.LangType);
};
</script>
<style scoped></style>

View File

@ -2,14 +2,14 @@
<router-link :to="routeHomePath" class="flex-center w-full nowrap-hidden">
<system-logo class="text-32px text-primary" />
<h2 v-show="showTitle" class="pl-8px text-16px font-bold text-primary transition duration-300 ease-in-out">
{{ t('message.system.title') }}
{{ $t('system.title') }}
</h2>
</router-link>
</template>
<script setup lang="ts">
import { routePath } from '@/router';
import { t } from '@/locales';
import { $t } from '@/locales';
defineOptions({ name: 'GlobalLogo' });

View File

@ -9,7 +9,7 @@
:style="{ width: showDrawer ? theme.sider.mixChildMenuWidth + 'px' : '0px' }"
>
<header class="header-height flex-y-center justify-between" :style="{ height: theme.header.height + 'px' }">
<h2 class="text-primary pl-8px text-16px font-bold">{{ title }}</h2>
<h2 class="text-primary pl-8px text-16px font-bold">{{ $t('system.title') }}</h2>
<div class="px-8px text-16px text-gray-600 cursor-pointer" @click="app.toggleMixSiderFixed">
<icon-mdi-pin-off v-if="app.mixSiderFixed" />
<icon-mdi-pin v-else />
@ -35,8 +35,9 @@ import { computed, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import type { MenuOption } from 'naive-ui';
import { useAppStore, useThemeStore } from '@/store';
import { useAppInfo, useRouterPush } from '@/composables';
import { useRouterPush } from '@/composables';
import { getActiveKeyPathsOfMenus } from '@/utils';
import { $t } from '@/locales';
defineOptions({ name: 'MixMenuDrawer' });
@ -53,7 +54,6 @@ const route = useRoute();
const app = useAppStore();
const theme = useThemeStore();
const { routerPush } = useRouterPush();
const { title } = useAppInfo();
const showDrawer = computed(() => (props.visible && props.menus.length) || app.mixSiderFixed);

View File

@ -28,7 +28,7 @@ import { useRouterPush } from '@/composables';
import { useBoolean } from '@/hooks';
import { translateMenuLabel } from '@/utils';
import { GlobalLogo } from '@/layouts/common';
import { t } from '@/locales';
import { $t } from '@/locales';
import { MixMenuCollapse, MixMenuDetail, MixMenuDrawer } from './components';
defineOptions({ name: 'VerticalMixSider' });
@ -53,7 +53,7 @@ const firstDegreeMenus = computed(() =>
return {
routeName,
label: i18nTitle ? t(i18nTitle) : label,
label: i18nTitle ? $t(i18nTitle) : label,
icon,
hasChildren
};

View File

@ -19,7 +19,7 @@
class="inline-block align-text-bottom text-16px"
/>
</template>
{{ item.meta.i18nTitle ? t(item.meta.i18nTitle) : item.meta.title }}
{{ item.meta.i18nTitle ? $t(item.meta.i18nTitle) : item.meta.title }}
</PageTab>
</div>
<context-menu
@ -36,7 +36,7 @@
import { computed, nextTick, reactive, ref, watch } from 'vue';
import { PageTab } from '@soybeanjs/vue-materials';
import { useTabStore, useThemeStore } from '@/store';
import { t } from '@/locales';
import { $t } from '@/locales';
import { ContextMenu } from './components';
defineOptions({ name: 'TabDetail' });

View File

@ -1,7 +1,7 @@
<template>
<n-divider title-placement="center">主题模式</n-divider>
<n-divider title-placement="center">{{ $t('layout.settingDrawer.themeModeTitle') }}</n-divider>
<n-space vertical size="large">
<setting-menu label="深色主题">
<setting-menu :label="$t('layout.settingDrawer.darkMode')">
<n-switch :value="theme.darkMode" @update:value="theme.setDarkMode">
<template #checked>
<icon-mdi-white-balance-sunny class="text-14px text-white" />
@ -11,7 +11,7 @@
</template>
</n-switch>
</setting-menu>
<setting-menu label="跟随系统">
<setting-menu :label="$t('layout.settingDrawer.followSystemTheme')">
<n-switch :value="theme.followSystemTheme" @update:value="theme.setFollowSystemTheme">
<template #checked>
<icon-ic-baseline-do-not-disturb class="text-14px text-white" />
@ -21,7 +21,7 @@
</template>
</n-switch>
</setting-menu>
<setting-menu label="自定义暗黑主题动画过渡">
<setting-menu :label="$t('layout.settingDrawer.isCustomizeDarkModeTransition')">
<n-switch :value="theme.isCustomizeDarkModeTransition" @update:value="theme.setIsCustomizeDarkModeTransition">
<template #checked>
<icon-ic-baseline-do-not-disturb class="text-14px text-white" />
@ -31,13 +31,13 @@
</template>
</n-switch>
</setting-menu>
<setting-menu label="侧边栏深色">
<setting-menu :label="$t('layout.settingDrawer.sider.inverted')">
<n-switch :value="theme.sider.inverted" @update:value="theme.setSiderInverted" />
</setting-menu>
<setting-menu label="头部深色">
<setting-menu :label="$t('layout.settingDrawer.header.inverted')">
<n-switch :value="theme.header.inverted" @update:value="theme.setHeaderInverted" />
</setting-menu>
<setting-menu label="底部深色">
<setting-menu :label="$t('layout.settingDrawer.footer.inverted')">
<n-switch :value="theme.footer.inverted" @update:value="theme.setFooterInverted" />
</setting-menu>
</n-space>
@ -45,6 +45,7 @@
<script lang="ts" setup>
import { useThemeStore } from '@/store';
import { $t } from '@/locales';
import SettingMenu from '../setting-menu/index.vue';
defineOptions({ name: 'DarkMode' });

View File

@ -1,5 +1,5 @@
<template>
<n-divider title-placement="center">布局模式</n-divider>
<n-divider title-placement="center">{{ $t('layout.settingDrawer.layoutModelTitle') }}</n-divider>
<n-space justify="space-around" :wrap="true" :size="24" class="px-12px">
<layout-card
v-for="item in theme.layout.modeList"
@ -43,6 +43,7 @@
<script setup lang="ts">
import { useThemeStore } from '@/store';
import { $t } from '@/locales';
import { LayoutCard } from './components';
defineOptions({ name: 'LayoutMode' });

View File

@ -1,7 +1,7 @@
<template>
<n-divider title-placement="center">界面功能</n-divider>
<n-divider title-placement="center">{{ $t('layout.settingDrawer.pageFunctionsTitle') }}</n-divider>
<n-space vertical size="large">
<setting-menu label="滚动模式">
<setting-menu :label="$t('layout.settingDrawer.scrollMode')">
<n-select
class="w-120px"
size="small"
@ -10,10 +10,10 @@
@update:value="theme.setScrollMode"
/>
</setting-menu>
<setting-menu label="固定头部和多页签">
<setting-menu :label="$t('layout.settingDrawer.fixedHeaderAndTab')">
<n-switch :value="theme.fixedHeaderAndTab" @update:value="theme.setIsFixedHeaderAndTab" />
</setting-menu>
<setting-menu label="顶部菜单位置">
<setting-menu :label="$t('layout.settingDrawer.menu.horizontalPosition')">
<n-select
class="w-120px"
size="small"
@ -22,7 +22,7 @@
@update:value="theme.setHorizontalMenuPosition"
/>
</setting-menu>
<setting-menu label="头部高度">
<setting-menu :label="$t('layout.settingDrawer.header.height')">
<n-input-number
class="w-120px"
size="small"
@ -31,7 +31,7 @@
@update:value="theme.setHeaderHeight"
/>
</setting-menu>
<setting-menu label="多页签高度">
<setting-menu :label="$t('layout.settingDrawer.tab.height')">
<n-input-number
class="w-120px"
size="small"
@ -40,10 +40,10 @@
@update:value="theme.setTabHeight"
/>
</setting-menu>
<setting-menu label="多页签缓存">
<setting-menu :label="$t('layout.settingDrawer.tab.isCache')">
<n-switch :value="theme.tab.isCache" @update:value="theme.setTabIsCache" />
</setting-menu>
<setting-menu label="侧边栏展开宽度">
<setting-menu :label="$t('layout.settingDrawer.sider.width')">
<n-input-number
class="w-120px"
size="small"
@ -52,7 +52,7 @@
@update:value="theme.setSiderWidth"
/>
</setting-menu>
<setting-menu label="左侧混合侧边栏展开宽度">
<setting-menu :label="$t('layout.settingDrawer.sider.mixWidth')">
<n-input-number
class="w-120px"
size="small"
@ -61,13 +61,13 @@
@update:value="theme.setMixSiderWidth"
/>
</setting-menu>
<setting-menu label="显示底部">
<setting-menu :label="$t('layout.settingDrawer.footer.visible')">
<n-switch :value="theme.footer.visible" @update:value="theme.setFooterVisible" />
</setting-menu>
<setting-menu label="固定底部">
<setting-menu :label="$t('layout.settingDrawer.footer.fixed')">
<n-switch :value="theme.footer.fixed" @update:value="theme.setFooterIsFixed" />
</setting-menu>
<setting-menu label="底部居右">
<setting-menu :label="$t('layout.settingDrawer.footer.right')">
<n-switch :value="theme.footer.right" @update:value="theme.setFooterIsRight" />
</setting-menu>
</n-space>
@ -75,6 +75,7 @@
<script lang="ts" setup>
import { useThemeStore } from '@/store';
import { $t } from '@/locales';
import SettingMenu from '../setting-menu/index.vue';
defineOptions({ name: 'PageFunc' });

View File

@ -1,16 +1,16 @@
<template>
<n-divider title-placement="center">界面显示</n-divider>
<n-divider title-placement="center">{{ $t('layout.settingDrawer.pageViewTitle') }}</n-divider>
<n-space vertical size="large">
<setting-menu label="面包屑">
<setting-menu :label="$t('layout.settingDrawer.header.crumb.visible')">
<n-switch :value="theme.header.crumb.visible" @update:value="theme.setHeaderCrumbVisible" />
</setting-menu>
<setting-menu label="面包屑图标">
<setting-menu :label="$t('layout.settingDrawer.header.crumb.icon')">
<n-switch :value="theme.header.crumb.showIcon" @update:value="theme.setHeaderCrumbIconVisible" />
</setting-menu>
<setting-menu label="多页签">
<setting-menu :label="$t('layout.settingDrawer.tab.visible')">
<n-switch :value="theme.tab.visible" @update:value="theme.setTabVisible" />
</setting-menu>
<setting-menu label="多页签风格">
<setting-menu :label="$t('layout.settingDrawer.tab.modeList.mode')">
<n-select
class="w-120px"
size="small"
@ -19,10 +19,10 @@
@update:value="theme.setTabMode"
/>
</setting-menu>
<setting-menu label="页面切换动画">
<setting-menu :label="$t('layout.settingDrawer.page.animate')">
<n-switch :value="theme.page.animate" @update:value="theme.setPageIsAnimate" />
</setting-menu>
<setting-menu label="页面切换动画类型">
<setting-menu :label="$t('layout.settingDrawer.page.animateMode')">
<n-select
class="w-120px"
size="small"
@ -36,6 +36,7 @@
<script lang="ts" setup>
import { useThemeStore } from '@/store';
import { $t } from '@/locales';
import SettingMenu from '../setting-menu/index.vue';
defineOptions({ name: 'PageView' });

View File

@ -1,5 +1,5 @@
<template>
<n-divider title-placement="center">系统主题</n-divider>
<n-divider title-placement="center">{{ $t('layout.settingDrawer.systemThemeTitle') }}</n-divider>
<n-grid :cols="8" :x-gap="8" :y-gap="12">
<n-grid-item v-for="color in theme.themeColorList" :key="color" class="flex-x-center">
<color-checkbox :color="color" :checked="color === theme.themeColor" @click="theme.setThemeColor(color)" />
@ -7,7 +7,9 @@
</n-grid>
<n-space :vertical="true" class="pt-12px">
<n-color-picker :value="theme.themeColor" :show-alpha="false" @update-value="theme.setThemeColor" />
<n-button :block="true" :type="otherColorBtnType" @click="openModal">更多颜色</n-button>
<n-button :block="true" :type="otherColorBtnType" @click="openModal">
{{ $t('layout.settingDrawer.systemTheme.moreColors') }}
</n-button>
</n-space>
<color-modal :visible="visible" @close="closeModal" />
</template>
@ -17,6 +19,7 @@ import { computed } from 'vue';
import { isInTraditionColors } from '@/settings';
import { useThemeStore } from '@/store';
import { useBoolean } from '@/hooks';
import { $t } from '@/locales';
import { ColorCheckbox, ColorModal } from './components';
defineOptions({ name: 'ThemeColorSelect' });

View File

@ -1,11 +1,13 @@
<template>
<n-divider title-placement="center">主题配置</n-divider>
<n-divider title-placement="center">{{ $t('layout.settingDrawer.themeConfiguration.title') }}</n-divider>
<textarea id="themeConfigCopyTarget" v-model="dataClipboardText" class="absolute opacity-0" />
<n-space vertical>
<div ref="copyRef" data-clipboard-target="#themeConfigCopyTarget">
<n-button type="primary" :block="true">拷贝当前配置</n-button>
<n-button type="primary" :block="true">{{ $t('layout.settingDrawer.themeConfiguration.copy') }}</n-button>
</div>
<n-button type="warning" :block="true" @click="handleResetConfig">重置当前配置</n-button>
<n-button type="warning" :block="true" @click="handleResetConfig">
{{ $t('layout.settingDrawer.themeConfiguration.reset') }}
</n-button>
</n-space>
</template>
@ -13,6 +15,7 @@
import { onMounted, onUnmounted, ref, watch } from 'vue';
import Clipboard from 'clipboard';
import { useThemeStore } from '@/store';
import { $t } from '@/locales';
defineOptions({ name: 'ThemeConfig' });
@ -28,7 +31,7 @@ function getClipboardText() {
function handleResetConfig() {
theme.resetThemeStore();
window.$message?.success('已重置配置,请重新拷贝!');
window.$message?.success($t('layout.settingDrawer.themeConfiguration.resetSuccess'));
}
function clipboardEventListener() {
@ -36,9 +39,9 @@ function clipboardEventListener() {
const copy = new Clipboard(copyRef.value);
copy.on('success', () => {
window.$dialog?.success({
title: '操作成功',
content: '复制成功,请替换 src/settings/theme.json的内容',
positiveText: '确定'
title: $t('layout.settingDrawer.themeConfiguration.operateSuccess'),
content: $t('layout.settingDrawer.themeConfiguration.copySuccess'),
positiveText: $t('layout.settingDrawer.themeConfiguration.confirmCopy')
});
});
}

View File

@ -1,6 +1,6 @@
<template>
<n-drawer :show="app.settingDrawerVisible" display-directive="show" :width="330" @mask-click="app.closeSettingDrawer">
<n-drawer-content title="主题配置" :native-scrollbar="false">
<n-drawer-content :title="$t('layout.settingDrawer.title')" :native-scrollbar="false">
<dark-mode />
<layout-mode />
<theme-color-select />
@ -14,6 +14,7 @@
<script setup lang="ts">
import { useAppStore } from '@/store';
import { $t } from '@/locales';
import { DarkMode, DrawerButton, LayoutMode, PageFunc, PageView, ThemeColorSelect, ThemeConfig } from './components';
defineOptions({ name: 'SettingDrawer' });

View File

@ -1,8 +1,8 @@
import type { App } from 'vue';
import { createI18n } from 'vue-i18n';
import { localStg } from '@/utils';
import messages from './lang';
import type { LocaleKey } from './lang';
import type { TranslateOptions } from 'vue-i18n';
import { localStg } from '@/utils/storage';
import messages from './locale';
const i18n = createI18n({
locale: localStg.get('lang') || 'zh-CN',
@ -15,10 +15,20 @@ export function setupI18n(app: App) {
app.use(i18n);
}
export function t(key: string) {
return i18n.global.t(key);
interface T {
(key: I18nType.I18nKey): string;
(key: I18nType.I18nKey, plural: number, options?: TranslateOptions<I18nType.LangType>): string;
(key: I18nType.I18nKey, defaultMsg: string, options?: TranslateOptions<I18nType.I18nKey>): string;
(key: I18nType.I18nKey, list: unknown[], options?: TranslateOptions<I18nType.I18nKey>): string;
(key: I18nType.I18nKey, list: unknown[], plural: number): string;
(key: I18nType.I18nKey, list: unknown[], defaultMsg: string): string;
(key: I18nType.I18nKey, named: Record<string, unknown>, options?: TranslateOptions<I18nType.LangType>): string;
(key: I18nType.I18nKey, named: Record<string, unknown>, plural: number): string;
(key: I18nType.I18nKey, named: Record<string, unknown>, defaultMsg: string): string;
}
export function setLocale(locale: LocaleKey) {
export const $t = i18n.global.t as T;
export function setLocale(locale: I18nType.LangType) {
i18n.global.locale.value = locale;
}

View File

@ -1,10 +1,20 @@
import type { LocaleMessages } from 'vue-i18n';
const locale: LocaleMessages<I18nType.Schema> = {
message: {
const locale: I18nType.Schema = {
system: {
title: 'SoybeanAdmin'
},
common: {
add: 'Add',
addSuccess: 'Add Success',
edit: 'Edit',
editSuccess: 'Edit Success',
delete: 'Delete',
deleteSuccess: 'Delete Success',
batchDelete: 'Batch Delete',
confirm: 'Confirm',
cancel: 'Cancel',
pleaseCheckValue: 'Please check the value is valid',
action: 'Action'
},
routes: {
dashboard: {
_value: 'Dashboard',
@ -78,6 +88,130 @@ const locale: LocaleMessages<I18nType.Schema> = {
user: 'User'
},
about: 'About'
},
layout: {
settingDrawer: {
title: 'Theme configuration',
themeModeTitle: 'Theme mode',
darkMode: 'Dark mode',
layoutModelTitle: 'Layout mode',
systemThemeTitle: 'System theme',
pageFunctionsTitle: 'Page functions',
pageViewTitle: 'Page view',
followSystemTheme: 'Follow the system',
isCustomizeDarkModeTransition: 'Custom dark theme animation transition',
scrollMode: 'scrollMode',
scrollModeList: {
wrapper: 'Outer layer scroll',
content: 'Main body scroll'
},
fixedHeaderAndTab: 'Fixed header and multiple tabs',
header: {
inverted: 'darkHead',
height: 'Head Height',
crumb: {
visible: 'Crumb',
icon: 'Crumb icon'
}
},
tab: {
visible: 'Multi-page tab',
height: 'Multiple tab height',
modeList: {
mode: 'Multi-tab style',
chrome: 'Google style',
button: 'Button style'
},
isCache: 'Multiple tab caching'
},
sider: {
inverted: 'Dark sidebar',
width: 'Sidebar expanded width',
mixWidth: 'Left hybrid sidebar expanded width'
},
menu: {
horizontalPosition: 'Top menu position',
horizontalPositionList: {
flexStart: 'Right',
center: 'center',
flexEnd: 'Left'
}
},
footer: {
inverted: 'Dark bottom',
visible: 'Show bottom',
fixed: 'Fixed bottom',
right: 'Bottom to the right'
},
page: {
animate: 'switch animation',
animateMode: 'switch animation type',
animateModeList: {
zoomFade: 'Gradual change',
zoomOut: 'Flash',
fadeSlide: 'Slide',
fade: 'Fade away',
fadeBottom: 'Bottom fade',
fadeScale: 'Resizing fade away'
}
},
systemTheme: {
moreColors: 'More colors'
},
themeConfiguration: {
title: 'Theme configuration',
copy: 'Copy the current configuration',
reset: 'Reset the current configuration',
resetSuccess: 'The configuration has been reset, please copy it again!',
operateSuccess: 'Successful operation',
copySuccess: 'Copy success, please replace the content of src/settings/theme.json!',
confirmCopy: 'Confirm'
}
}
},
page: {
login: {
common: {
userNamePlaceholder: 'Please enter user name',
phonePlaceholder: 'Please enter phone number',
codePlaceholder: 'Please enter verification code',
passwordPlaceholder: 'Please enter password',
confirmPasswordPlaceholder: 'Please enter password again',
codeLogin: 'Verification code login',
confirm: 'Confirm',
back: 'Back',
validateSuccess: 'Verification passed',
loginSuccess: 'Login success',
welcomeBack: 'Welcome back, {userName}!'
},
pwdLogin: {
title: 'Password Login',
rememberMe: 'Remember me',
forgetPassword: 'Forget password?',
register: 'Register account',
otherAccountLogin: 'Other Account Login',
otherLoginMode: 'Other Login Mode',
superAdmin: 'Super Administrator',
admin: 'Administrator',
user: 'Ordinary User'
},
codeLogin: {
title: 'Verification Code Login',
getCode: 'Get verification code',
imageCodePlaceholder: 'Please enter image verification code'
},
register: {
title: 'Register Account',
agreement: 'I have read and agree to',
protocol: '《User Agreement》',
policy: '《Privacy Policy》'
},
resetPwd: {
title: 'Reset Password'
},
bindWeChat: {
title: 'Bind WeChat'
}
}
}
};

View File

@ -1,13 +0,0 @@
import zhCN from './zh-cn';
import en from './en';
import kmKH from './km-KH';
const locales = {
'zh-CN': zhCN,
en,
'km-KH': kmKH
};
export type LocaleKey = keyof typeof locales;
export default locales;

View File

@ -1,10 +1,20 @@
import type { LocaleMessages } from 'vue-i18n';
const locale: LocaleMessages<I18nType.Schema> = {
message: {
const locale: I18nType.Schema = {
system: {
title: 'ប្រព័ន្ធគ្រប់គ្រង'
},
common: {
add: 'បន្ថែម',
addSuccess: 'បន្ថែមជោគជ័យ',
edit: 'កែប្រែ',
editSuccess: 'កែប្រែជោគជ័យ',
delete: 'លុប',
deleteSuccess: 'លុបជោគជ័យ',
batchDelete: 'លុបច្រើន',
confirm: 'យល់ព្រម',
cancel: 'បោះបង់',
pleaseCheckValue: 'សូមពិនិត្យមើលតម្លៃដែលបានបញ្ចូលដើម្បីបញ្ជាក់ថាត្រូវប្រើប្រាស់បាន',
action: 'សកម្មភាព'
},
routes: {
dashboard: {
_value: 'ផ្ទាំងទិន្នន័យ',
@ -78,6 +88,130 @@ const locale: LocaleMessages<I18nType.Schema> = {
user: 'អ្នកប្រើប្រាស់'
},
about: 'អំពីប្រព័ន្ធ'
},
layout: {
settingDrawer: {
title: 'ការកំណត់ស្បែក',
themeModeTitle: 'ស្បែករបស់របស់អ្នក',
darkMode: 'របៀបងារស្បែកងងឹត',
layoutModelTitle: 'របៀបប្រើប្រាស់របស់អ្នក',
systemThemeTitle: 'ស្បែករបស់ប្រព័ន្ធគ្រប់គ្រង',
pageFunctionsTitle: 'មុខងារទំនាក់ទំនងរបស់ទំព័រ',
pageViewTitle: 'ទំព័រទស្សន៍ទាយ',
followSystemTheme: 'តាមដានស្បែកប្រព័ន្ធគ្រប់គ្រង',
isCustomizeDarkModeTransition: 'ប្រើប្រាស់របៀបងារស្បែកងងឹតផ្ទាល់ខ្លួន',
scrollMode: 'របៀបរុករក',
scrollModeList: {
wrapper: 'រុករកជាក់លាក់',
content: 'រុករកមានមុខងារ'
},
fixedHeaderAndTab: 'បិទការរុករកជាក់លាក់និងរុករកមានមុខងារ',
header: {
inverted: 'បង្កើតការរុករកជាក់លាក់',
height: 'កម្ពស់',
crumb: {
visible: 'បង្ហាញរុករកជាក់លាក់',
icon: 'រុករកជាក់លាក់រូបតំណាង'
}
},
tab: {
visible: 'បង្ហាញរុករកជាក់លាក់',
height: 'កម្ពស់',
modeList: {
mode: 'របៀប',
chrome: 'ក្រុមហ៊ុន',
button: 'ប៊ូតុង'
},
isCache: 'រក្សាទុកការរុករកជាក់លាក់'
},
sider: {
inverted: 'បង្កើតការរុករកជាក់លាក់',
width: 'ទទឹង',
mixWidth: 'ទទឹងបញ្ចូល'
},
menu: {
horizontalPosition: 'ទីតាំងផ្ដេក',
horizontalPositionList: {
flexStart: 'ចាប់ផ្ដើមឈុត',
center: 'កណ្តាល',
flexEnd: 'ចាប់ផ្ដើមចុងក្រោយ'
}
},
footer: {
inverted: 'បង្កើតការរុករកជាក់លាក់',
visible: 'បង្ហាញការរុករកជាក់លាក់',
fixed: 'ការរុករកជាក់លាក់',
right: 'ត្រឡប់ទៅស្តាំ'
},
page: {
animate: 'ការផ្លាស់ប្តូរ',
animateMode: 'របៀបផ្លាស់ប្តូរ',
animateModeList: {
zoomFade: 'ពង្រីកបង្ហាញនិងលាស់ប្តូរ',
zoomOut: 'ពង្រីកបង្ហាញនិងលាស់ប្តូរ',
fadeSlide: 'ពង្រីកបង្ហាញនិងលាស់ប្តូរ',
fade: 'ពង្រីកបង្ហាញនិងលាស់ប្តូរ',
fadeBottom: 'ពង្រីកបង្ហាញនិងលាស់ប្តូរ',
fadeScale: 'ពង្រីកបង្ហាញនិងលាស់ប្តូរ'
}
},
systemTheme: {
moreColors: 'ពន្លឺច្រើនទៀត'
},
themeConfiguration: {
title: 'ការកំណត់ស្បែក',
copy: 'ចម្លង',
reset: 'កំណត់ឡើងវិញ',
resetSuccess: 'កំណត់ឡើងវិញជោគជ័យ, សូមចម្លងឯកសារស្បែកឡើងវិញ!',
operateSuccess: 'សម្រាប់ការប្រើប្រាស់ជោគជ័យ',
copySuccess: 'ចម្លងជោគជ័យ, សូមជោគជ័យឯកសារ src/settings/theme.json!',
confirmCopy: 'យល់ព្រម'
}
}
},
page: {
login: {
common: {
userNamePlaceholder: 'ឈ្មោះអ្នកប្រើប្រាស់',
phonePlaceholder: 'លេខទូរស័ព្ទ',
codePlaceholder: 'លេខកូដ',
passwordPlaceholder: 'លេខសម្ងាត់',
confirmPasswordPlaceholder: 'បញ្ជាក់លេខសម្ងាត់',
codeLogin: 'ចូលតាមលេខកូដ',
confirm: 'យល់ព្រម',
back: 'ត្រឡប់ក្រោយ',
validateSuccess: 'បញ្ជាក់ជោគជ័យ',
loginSuccess: 'ចូលជោគជ័យ',
welcomeBack: 'សូមស្វាគមន៍ម្តងទៀត, {userName}!'
},
pwdLogin: {
title: 'ចូលគណនី',
rememberMe: 'ចងចាំខ្ញុំ',
forgetPassword: 'ភ្លេចលេខសម្ងាត់',
register: 'ចុះឈ្មោះ',
otherAccountLogin: 'ចូលតាមគណនីផ្សេងទៀត',
otherLoginMode: 'របៀបចូលគណនីផ្សេងទៀត',
superAdmin: 'អ្នកគ្រប់គ្រងសុវត្ថិភាព',
admin: 'អ្នកគ្រប់គ្រង',
user: 'អ្នកប្រើប្រាស់'
},
codeLogin: {
title: 'ចូលតាមលេខកូដ',
getCode: 'ទទួលលេខកូដ',
imageCodePlaceholder: 'លេខកូដរូបភាព'
},
register: {
title: 'ចុះឈ្មោះ',
agreement: 'យល់ព្រមនឹង',
protocol: 'សម្រាប់ការប្រើប្រាស់',
policy: 'គោលការណ៍ផ្សេងៗ'
},
resetPwd: {
title: 'កំណត់លេខសម្ងាត់ថ្មី'
},
bindWeChat: {
title: 'ភ្ជាប់គណនីរបស់អ្នកជាមួយគណនីរបស់អ្នក'
}
}
}
};

View File

@ -1,85 +0,0 @@
import type { LocaleMessages } from 'vue-i18n';
const locale: LocaleMessages<I18nType.Schema> = {
message: {
system: {
title: 'Soybean管理系统'
},
routes: {
dashboard: {
_value: '仪表盘',
analysis: '分析页',
workbench: '工作台'
},
document: {
_value: '文档',
vue: 'Vue文档',
vite: 'Vite文档',
naive: 'NaiveUI文档',
project: '项目文档',
'project-link': '项目文档(外链)'
},
component: {
_value: '组件示例',
button: '按钮',
card: '卡片',
table: '表格'
},
plugin: {
_value: '插件示例',
charts: {
_value: '图表',
echarts: 'ECharts',
antv: 'AntV'
},
copy: '剪贴板',
editor: {
_value: '编辑器',
quill: '富文本',
markdown: 'Markdown'
},
icon: '图标',
map: '地图',
print: '打印',
swiper: 'Swiper',
video: '视频'
},
'auth-demo': {
_value: '权限示例',
permission: '切换权限',
super: '超级管理员可见'
},
function: {
_value: '功能',
tab: 'Tab页签'
},
exception: {
_value: '异常页',
403: '403',
404: '404',
500: '500'
},
'multi-menu': {
_value: '多级菜单',
first: {
_value: '一级菜单',
second: '二级菜单',
'second-new': {
_value: '二级菜单(有子菜单)',
third: '三级菜单'
}
}
},
management: {
_value: '系统管理',
auth: '权限管理',
role: '角色管理',
route: '路由管理',
user: '用户管理'
},
about: '关于'
}
}
};
export default locale;

219
src/locales/lang/zhCN.ts Normal file
View File

@ -0,0 +1,219 @@
const locale: I18nType.Schema = {
system: {
title: 'Soybean管理系统'
},
common: {
add: '添加',
addSuccess: '添加成功',
edit: '修改',
editSuccess: '修改成功',
delete: '删除',
deleteSuccess: '删除成功',
batchDelete: '批量删除',
confirm: '确认',
cancel: '取消',
pleaseCheckValue: '请检查输入的值是否合法',
action: '操作'
},
routes: {
dashboard: {
_value: '仪表盘',
analysis: '分析页',
workbench: '工作台'
},
document: {
_value: '文档',
vue: 'Vue文档',
vite: 'Vite文档',
naive: 'NaiveUI文档',
project: '项目文档',
'project-link': '项目文档(外链)'
},
component: {
_value: '组件示例',
button: '按钮',
card: '卡片',
table: '表格'
},
plugin: {
_value: '插件示例',
charts: {
_value: '图表',
echarts: 'ECharts',
antv: 'AntV'
},
copy: '剪贴板',
editor: {
_value: '编辑器',
quill: '富文本',
markdown: 'Markdown'
},
icon: '图标',
map: '地图',
print: '打印',
swiper: 'Swiper',
video: '视频'
},
'auth-demo': {
_value: '权限示例',
permission: '切换权限',
super: '超级管理员可见'
},
function: {
_value: '功能',
tab: 'Tab页签'
},
exception: {
_value: '异常页',
403: '403',
404: '404',
500: '500'
},
'multi-menu': {
_value: '多级菜单',
first: {
_value: '一级菜单',
second: '二级菜单',
'second-new': {
_value: '二级菜单(有子菜单)',
third: '三级菜单'
}
}
},
management: {
_value: '系统管理',
auth: '权限管理',
role: '角色管理',
route: '路由管理',
user: '用户管理'
},
about: '关于'
},
layout: {
settingDrawer: {
title: '主题配置',
themeModeTitle: '主题模式',
darkMode: '深色主题',
layoutModelTitle: '布局模式',
systemThemeTitle: '系统主题',
pageFunctionsTitle: '界面功能',
pageViewTitle: '界面显示',
followSystemTheme: '跟随系统',
isCustomizeDarkModeTransition: '自定义暗黑主题动画过渡',
scrollMode: '滚动模式',
scrollModeList: {
wrapper: '外层滚动',
content: '主体滚动'
},
fixedHeaderAndTab: '固定头部和多页签',
header: {
inverted: '头部深色',
height: '头部高度',
crumb: {
visible: '面包屑',
icon: '面包屑图标'
}
},
tab: {
visible: '多页签',
height: '多页签高度',
modeList: {
mode: '多页签风格',
chrome: '谷歌风格',
button: '按钮风格'
},
isCache: '多页签缓存'
},
sider: {
inverted: '侧边栏深色',
width: '侧边栏展开宽度',
mixWidth: '左侧混合侧边栏展开宽度'
},
menu: {
horizontalPosition: '顶部菜单位置',
horizontalPositionList: {
flexStart: '居左',
center: '居中',
flexEnd: '居右'
}
},
footer: {
inverted: '底部深色',
visible: '显示底部',
fixed: '固定底部',
right: '底部居右'
},
page: {
animate: '页面切换动画',
animateMode: '页面切换动画类型',
animateModeList: {
zoomFade: '渐变',
zoomOut: '闪现',
fadeSlide: '滑动',
fade: '消退',
fadeBottom: '底部消退',
fadeScale: '缩放消退'
}
},
systemTheme: {
moreColors: '更多颜色'
},
themeConfiguration: {
title: '主题配置',
copy: '拷贝当前配置',
reset: '重置当前配置',
resetSuccess: '已重置配置,请重新拷贝!',
operateSuccess: '操作成功',
copySuccess: '复制成功,请替换 src/settings/theme.json的内容',
confirmCopy: '确认'
}
}
},
page: {
login: {
common: {
userNamePlaceholder: '请输入用户名',
phonePlaceholder: '请输入手机号',
codePlaceholder: '请输入验证码',
passwordPlaceholder: '请输入密码',
confirmPasswordPlaceholder: '请再次输入密码',
codeLogin: '验证码登录',
confirm: '确定',
back: '返回',
validateSuccess: '验证成功',
loginSuccess: '登录成功',
welcomeBack: '欢迎回来,{userName}!'
},
pwdLogin: {
title: '密码登录',
rememberMe: '记住我',
forgetPassword: '忘记密码?',
register: '注册账号',
otherAccountLogin: '其他账号登录',
otherLoginMode: '其他登录方式',
superAdmin: '超级管理员',
admin: '管理员',
user: '普通用户'
},
codeLogin: {
title: '验证码登录',
getCode: '获取验证码',
imageCodePlaceholder: '请输入图片验证码'
},
register: {
title: '注册账号',
agreement: '我已经仔细阅读并接受',
protocol: '《用户协议》',
policy: '《隐私权政策》'
},
resetPwd: {
title: '重置密码'
},
bindWeChat: {
title: '绑定微信'
}
}
}
};
export default locale;

11
src/locales/locale.ts Normal file
View File

@ -0,0 +1,11 @@
import zhCN from './lang/zhCN';
import en from './lang/en';
import kmKH from './lang/km-KH';
const locales: Record<I18nType.LangType, I18nType.Schema> = {
'zh-CN': zhCN,
en,
'km-KH': kmKH
};
export default locales;

View File

@ -1,6 +1,6 @@
import type { Router } from 'vue-router';
import { useTitle } from '@vueuse/core';
import { t } from '@/locales';
import { $t } from '@/locales';
import { createPermissionGuard } from './permission';
/**
@ -16,7 +16,7 @@ export function createRouterGuard(router: Router) {
});
router.afterEach(to => {
// 设置document title
useTitle(to.meta.i18nTitle ? t(to.meta.i18nTitle) : to.meta.title);
useTitle(to.meta.i18nTitle ? $t(to.meta.i18nTitle) : to.meta.title);
// 结束 loadingBar
window.$loadingBar?.finish();
});

View File

@ -1,10 +1,10 @@
const about1: AuthRoute.Route = {
const about: AuthRoute.Route = {
name: 'about',
path: '/about',
component: 'self',
meta: {
title: '关于',
i18nTitle: 'message.routes.about',
i18nTitle: 'routes.about',
requiresAuth: true,
keepAlive: true,
singleLayout: 'basic',
@ -14,4 +14,4 @@ const about1: AuthRoute.Route = {
}
};
export default about1;
export default about;

View File

@ -9,7 +9,7 @@ const authDemo: AuthRoute.Route = {
component: 'self',
meta: {
title: '权限切换',
i18nTitle: 'message.routes.auth-demo.permission',
i18nTitle: 'routes.auth-demo.permission',
requiresAuth: true,
icon: 'ic:round-construction'
}
@ -20,7 +20,7 @@ const authDemo: AuthRoute.Route = {
component: 'self',
meta: {
title: '超级管理员可见',
i18nTitle: 'message.routes.auth-demo.super',
i18nTitle: 'routes.auth-demo.super',
requiresAuth: true,
permissions: ['super'],
icon: 'ic:round-supervisor-account'
@ -29,7 +29,7 @@ const authDemo: AuthRoute.Route = {
],
meta: {
title: '权限示例',
i18nTitle: 'message.routes.auth-demo._value',
i18nTitle: 'routes.auth-demo._value',
icon: 'ic:baseline-security',
order: 5
}

View File

@ -9,7 +9,7 @@ const component: AuthRoute.Route = {
component: 'self',
meta: {
title: '按钮',
i18nTitle: 'message.routes.component.button',
i18nTitle: 'routes.component.button',
requiresAuth: true,
icon: 'mdi:button-cursor'
}
@ -20,7 +20,7 @@ const component: AuthRoute.Route = {
component: 'self',
meta: {
title: '卡片',
i18nTitle: 'message.routes.component.card',
i18nTitle: 'routes.component.card',
requiresAuth: true,
icon: 'mdi:card-outline'
}
@ -31,7 +31,7 @@ const component: AuthRoute.Route = {
component: 'self',
meta: {
title: '表格',
i18nTitle: 'message.routes.component.table',
i18nTitle: 'routes.component.table',
requiresAuth: true,
icon: 'mdi:table-large'
}
@ -39,7 +39,7 @@ const component: AuthRoute.Route = {
],
meta: {
title: '组件示例',
i18nTitle: 'message.routes.component._value',
i18nTitle: 'routes.component._value',
icon: 'cib:app-store',
order: 3
}

View File

@ -11,7 +11,7 @@ const dashboard: AuthRoute.Route = {
title: '分析页',
requiresAuth: true,
icon: 'icon-park-outline:analysis',
i18nTitle: 'message.routes.dashboard.analysis'
i18nTitle: 'routes.dashboard.analysis'
}
},
{
@ -22,7 +22,7 @@ const dashboard: AuthRoute.Route = {
title: '工作台',
requiresAuth: true,
icon: 'icon-park-outline:workbench',
i18nTitle: 'message.routes.dashboard.workbench'
i18nTitle: 'routes.dashboard.workbench'
}
}
],
@ -30,7 +30,7 @@ const dashboard: AuthRoute.Route = {
title: '仪表盘',
icon: 'mdi:monitor-dashboard',
order: 1,
i18nTitle: 'message.routes.dashboard._value'
i18nTitle: 'routes.dashboard._value'
}
};

View File

@ -9,7 +9,7 @@ const document: AuthRoute.Route = {
component: 'self',
meta: {
title: 'vue文档',
i18nTitle: 'message.routes.document.vue',
i18nTitle: 'routes.document.vue',
requiresAuth: true,
icon: 'logos:vue'
}
@ -20,7 +20,7 @@ const document: AuthRoute.Route = {
component: 'self',
meta: {
title: 'vite文档',
i18nTitle: 'message.routes.document.vite',
i18nTitle: 'routes.document.vite',
requiresAuth: true,
icon: 'logos:vitejs'
}
@ -31,7 +31,7 @@ const document: AuthRoute.Route = {
component: 'self',
meta: {
title: 'naive文档',
i18nTitle: 'message.routes.document.naive',
i18nTitle: 'routes.document.naive',
requiresAuth: true,
icon: 'logos:naiveui'
}
@ -42,7 +42,7 @@ const document: AuthRoute.Route = {
component: 'self',
meta: {
title: '项目文档',
i18nTitle: 'message.routes.document.project',
i18nTitle: 'routes.document.project',
requiresAuth: true,
localIcon: 'logo'
}
@ -52,7 +52,7 @@ const document: AuthRoute.Route = {
path: '/document/project-link',
meta: {
title: '项目文档(外链)',
i18nTitle: 'message.routes.document.project-link',
i18nTitle: 'routes.document.project-link',
requiresAuth: true,
localIcon: 'logo',
href: 'https://docs.soybean.pro/'
@ -61,7 +61,7 @@ const document: AuthRoute.Route = {
],
meta: {
title: '文档',
i18nTitle: 'message.routes.document._value',
i18nTitle: 'routes.document._value',
icon: 'mdi:file-document-multiple-outline',
order: 2
}

View File

@ -9,7 +9,7 @@ const exception: AuthRoute.Route = {
component: 'self',
meta: {
title: '异常页403',
i18nTitle: 'message.routes.exception.403',
i18nTitle: 'routes.exception.403',
requiresAuth: true,
icon: 'ic:baseline-block'
}
@ -20,7 +20,7 @@ const exception: AuthRoute.Route = {
component: 'self',
meta: {
title: '异常页404',
i18nTitle: 'message.routes.exception.404',
i18nTitle: 'routes.exception.404',
requiresAuth: true,
icon: 'ic:baseline-web-asset-off'
}
@ -31,14 +31,14 @@ const exception: AuthRoute.Route = {
component: 'self',
meta: {
title: '异常页500',
i18nTitle: 'message.routes.exception.500',
i18nTitle: 'routes.exception.500',
requiresAuth: true,
icon: 'ic:baseline-wifi-off'
}
}
],
meta: {
i18nTitle: 'message.routes.exception._value',
i18nTitle: 'routes.exception._value',
title: '异常页',
icon: 'ant-design:exception-outlined',
order: 7

View File

@ -9,7 +9,7 @@ const functionRoute: AuthRoute.Route = {
component: 'self',
meta: {
title: 'Tab',
i18nTitle: 'message.routes.function.tab',
i18nTitle: 'routes.function.tab',
requiresAuth: true,
icon: 'ic:round-tab'
}
@ -42,7 +42,7 @@ const functionRoute: AuthRoute.Route = {
],
meta: {
title: '功能',
i18nTitle: 'message.routes.function._value',
i18nTitle: 'routes.function._value',
icon: 'icon-park-outline:all-application',
order: 6
}

View File

@ -9,7 +9,7 @@ const management: AuthRoute.Route = {
component: 'self',
meta: {
title: '权限管理',
i18nTitle: 'message.routes.management.auth',
i18nTitle: 'routes.management.auth',
requiresAuth: true,
icon: 'ic:baseline-security'
}
@ -20,7 +20,7 @@ const management: AuthRoute.Route = {
component: 'self',
meta: {
title: '角色管理',
i18nTitle: 'message.routes.management.role',
i18nTitle: 'routes.management.role',
requiresAuth: true,
icon: 'carbon:user-role'
}
@ -31,7 +31,7 @@ const management: AuthRoute.Route = {
component: 'self',
meta: {
title: '用户管理',
i18nTitle: 'message.routes.management.user',
i18nTitle: 'routes.management.user',
requiresAuth: true,
icon: 'ic:round-manage-accounts'
}
@ -42,7 +42,7 @@ const management: AuthRoute.Route = {
component: 'self',
meta: {
title: '路由管理',
i18nTitle: 'message.routes.management.route',
i18nTitle: 'routes.management.route',
requiresAuth: true,
icon: 'material-symbols:route'
}
@ -50,7 +50,7 @@ const management: AuthRoute.Route = {
],
meta: {
title: '系统管理',
i18nTitle: 'message.routes.management._value',
i18nTitle: 'routes.management._value',
icon: 'carbon:cloud-service-management',
order: 9
}

View File

@ -14,7 +14,7 @@ const multiMenu: AuthRoute.Route = {
component: 'self',
meta: {
title: '二级菜单',
i18nTitle: 'message.routes.multi-menu.first.second',
i18nTitle: 'routes.multi-menu.first.second',
requiresAuth: true,
icon: 'mdi:menu'
}
@ -30,7 +30,7 @@ const multiMenu: AuthRoute.Route = {
component: 'self',
meta: {
title: '三级菜单',
i18nTitle: 'message.routes.multi-menu.first.second-new.third',
i18nTitle: 'routes.multi-menu.first.second-new.third',
requiresAuth: true,
icon: 'mdi:menu'
}
@ -38,21 +38,21 @@ const multiMenu: AuthRoute.Route = {
],
meta: {
title: '二级菜单(有子菜单)',
i18nTitle: 'message.routes.multi-menu.first.second-new._value',
i18nTitle: 'routes.multi-menu.first.second-new._value',
icon: 'mdi:menu'
}
}
],
meta: {
title: '一级菜单',
i18nTitle: 'message.routes.multi-menu.first._value',
i18nTitle: 'routes.multi-menu.first._value',
icon: 'mdi:menu'
}
}
],
meta: {
title: '多级菜单',
i18nTitle: 'message.routes.multi-menu._value',
i18nTitle: 'routes.multi-menu._value',
icon: 'carbon:menu',
order: 8
}

View File

@ -14,7 +14,7 @@ const plugin: AuthRoute.Route = {
component: 'self',
meta: {
title: 'ECharts',
i18nTitle: 'message.routes.plugin.charts.echarts',
i18nTitle: 'routes.plugin.charts.echarts',
requiresAuth: true,
icon: 'simple-icons:apacheecharts'
}
@ -25,7 +25,7 @@ const plugin: AuthRoute.Route = {
component: 'self',
meta: {
title: 'AntV',
i18nTitle: 'message.routes.plugin.charts.antv',
i18nTitle: 'routes.plugin.charts.antv',
requiresAuth: true,
icon: 'simple-icons:antdesign'
}
@ -33,7 +33,7 @@ const plugin: AuthRoute.Route = {
],
meta: {
title: '图表',
i18nTitle: 'message.routes.plugin.charts._value',
i18nTitle: 'routes.plugin.charts._value',
icon: 'mdi:chart-areaspline'
}
},
@ -43,7 +43,7 @@ const plugin: AuthRoute.Route = {
component: 'self',
meta: {
title: '地图',
i18nTitle: 'message.routes.plugin.map',
i18nTitle: 'routes.plugin.map',
requiresAuth: true,
icon: 'mdi:map'
}
@ -54,7 +54,7 @@ const plugin: AuthRoute.Route = {
component: 'self',
meta: {
title: '视频',
i18nTitle: 'message.routes.plugin.video',
i18nTitle: 'routes.plugin.video',
requiresAuth: true,
icon: 'mdi:video'
}
@ -70,7 +70,7 @@ const plugin: AuthRoute.Route = {
component: 'self',
meta: {
title: '富文本编辑器',
i18nTitle: 'message.routes.plugin.editor.quill',
i18nTitle: 'routes.plugin.editor.quill',
requiresAuth: true,
icon: 'mdi:file-document-edit-outline'
}
@ -81,7 +81,7 @@ const plugin: AuthRoute.Route = {
component: 'self',
meta: {
title: 'markdown编辑器',
i18nTitle: 'message.routes.plugin.editor.markdown',
i18nTitle: 'routes.plugin.editor.markdown',
requiresAuth: true,
icon: 'ri:markdown-line'
}
@ -89,7 +89,7 @@ const plugin: AuthRoute.Route = {
],
meta: {
title: '编辑器',
i18nTitle: 'message.routes.plugin.editor._value',
i18nTitle: 'routes.plugin.editor._value',
icon: 'icon-park-outline:editor'
}
},
@ -99,7 +99,7 @@ const plugin: AuthRoute.Route = {
component: 'self',
meta: {
title: 'Swiper插件',
i18nTitle: 'message.routes.plugin.swiper',
i18nTitle: 'routes.plugin.swiper',
requiresAuth: true,
icon: 'simple-icons:swiper'
}
@ -110,7 +110,7 @@ const plugin: AuthRoute.Route = {
component: 'self',
meta: {
title: '剪贴板',
i18nTitle: 'message.routes.plugin.copy',
i18nTitle: 'routes.plugin.copy',
requiresAuth: true,
icon: 'mdi:clipboard-outline'
}
@ -121,7 +121,7 @@ const plugin: AuthRoute.Route = {
component: 'self',
meta: {
title: '图标',
i18nTitle: 'message.routes.plugin.icon',
i18nTitle: 'routes.plugin.icon',
requiresAuth: true,
localIcon: 'custom-icon'
}
@ -132,7 +132,7 @@ const plugin: AuthRoute.Route = {
component: 'self',
meta: {
title: '打印',
i18nTitle: 'message.routes.plugin.print',
i18nTitle: 'routes.plugin.print',
requiresAuth: true,
icon: 'mdi:printer'
}
@ -140,7 +140,7 @@ const plugin: AuthRoute.Route = {
],
meta: {
title: '插件示例',
i18nTitle: 'message.routes.plugin._value',
i18nTitle: 'routes.plugin._value',
icon: 'clarity:plugin-line',
order: 4
}

View File

@ -4,6 +4,7 @@ import { router } from '@/router';
import { fetchLogin, fetchUserInfo } from '@/service';
import { useRouterPush } from '@/composables';
import { localStg } from '@/utils';
import { $t } from '@/locales';
import { useTabStore } from '../tab';
import { useRouteStore } from '../route';
import { getToken, getUserInfo, clearAuthStorage } from './helpers';
@ -68,8 +69,8 @@ export const useAuthStore = defineStore('auth-store', {
// 登录成功弹出欢迎提示
if (route.isInitAuthRoute) {
window.$notification?.success({
title: '登录成功!',
content: `欢迎回来,${this.userInfo.userName}!`,
title: $t('page.login.common.loginSuccess'),
content: $t('page.login.common.welcomeBack', { userName: this.userInfo.userName }),
duration: 3000
});
}

View File

@ -69,7 +69,7 @@ export const useTabStore = defineStore('tab-store', {
const item = this.tabs.find(tab => tab.fullPath === this.activeTab);
if (item) {
if (item.meta.i18nTitle) {
item.meta.i18nTitle = title;
item.meta.i18nTitle = title as I18nType.I18nKey;
} else {
item.meta.title = title;
}

View File

@ -32,7 +32,7 @@ declare namespace AuthRoute {
/** 路由标题(可用来作document.title或者菜单的名称) */
title: string;
/** 用来支持多国语言 如果i18nTitle和title同时存在优先使用i18nTitle */
i18nTitle?: string;
i18nTitle?: I18nType.I18nKey;
/** 路由的动态路径(需要动态路径的页面需要将path添加进范型参数) */
dynamicPath?: AuthRouteUtils.GetDynamicPath<K>;
/** 作为单级路由的父级路由布局组件 */

View File

@ -244,7 +244,7 @@ declare namespace App {
routePath: string;
icon?: () => import('vue').VNodeChild;
children?: GlobalMenuOption[];
i18nTitle?: string;
i18nTitle?: I18nType.I18nKey;
};
/** 面包屑 */
@ -255,8 +255,8 @@ declare namespace App {
routeName: string;
hasChildren: boolean;
icon?: import('vue').Component;
i18nTitle?: string;
options?: (import('naive-ui/es/dropdown/src/interface').DropdownMixedOption & { i18nTitle?: string })[];
i18nTitle?: I18nType.I18nKey;
options?: (import('naive-ui/es/dropdown/src/interface').DropdownMixedOption & { i18nTitle?: I18nType.I18nKey })[];
};
/** 多页签Tab的路由 */
@ -304,12 +304,25 @@ declare namespace App {
}
declare namespace I18nType {
type langType = 'en' | 'zh-CN' | 'km-KH';
type LangType = 'en' | 'zh-CN' | 'km-KH';
interface Schema {
type Schema = {
system: {
title: string;
};
common: {
add: string;
addSuccess: string;
edit: string;
editSuccess: string;
delete: string;
deleteSuccess: string;
batchDelete: string;
confirm: string;
cancel: string;
pleaseCheckValue: string;
action: string;
};
routes: {
dashboard: {
_value: string;
@ -360,9 +373,9 @@ declare namespace I18nType {
};
exception: {
_value: string;
403: string;
404: string;
500: string;
'403': string;
'404': string;
'500': string;
};
'multi-menu': {
_value: string;
@ -384,5 +397,138 @@ declare namespace I18nType {
};
about: string;
};
}
layout: {
settingDrawer: {
title: string;
themeModeTitle: string;
darkMode: string;
layoutModelTitle: string;
systemThemeTitle: string;
pageFunctionsTitle: string;
pageViewTitle: string;
followSystemTheme: string;
isCustomizeDarkModeTransition: string;
scrollMode: string;
scrollModeList: {
wrapper: string;
content: string;
};
fixedHeaderAndTab: string;
header: {
inverted: string;
height: string;
crumb: {
visible: string;
icon: string;
};
};
tab: {
visible: string;
height: string;
modeList: {
mode: string;
chrome: string;
button: string;
};
isCache: string;
};
sider: {
inverted: string;
width: string;
mixWidth: string;
};
menu: {
horizontalPosition: string;
horizontalPositionList: {
flexStart: string;
center: string;
flexEnd: string;
};
};
footer: {
inverted: string;
visible: string;
fixed: string;
right: string;
};
page: {
animate: string;
animateMode: string;
animateModeList: {
zoomFade: string;
zoomOut: string;
fadeSlide: string;
fade: string;
fadeBottom: string;
fadeScale: string;
};
};
systemTheme: {
moreColors: string;
};
themeConfiguration: {
title: string;
copy: string;
reset: string;
resetSuccess: string;
operateSuccess: string;
copySuccess: string;
confirmCopy: string;
};
};
};
page: {
login: {
common: {
userNamePlaceholder: string;
phonePlaceholder: string;
codePlaceholder: string;
passwordPlaceholder: string;
confirmPasswordPlaceholder: string;
codeLogin: string;
confirm: string;
back: string;
validateSuccess: string;
loginSuccess: string;
welcomeBack: string;
};
pwdLogin: {
title: string;
rememberMe: string;
forgetPassword: string;
register: string;
otherAccountLogin: string;
otherLoginMode: string;
superAdmin: string;
admin: string;
user: string;
};
codeLogin: {
title: string;
getCode: string;
imageCodePlaceholder: string;
};
register: {
title: string;
agreement: string;
protocol: string;
policy: string;
};
resetPwd: {
title: string;
};
bindWeChat: {
title: string;
};
};
};
};
type GetI18nKey<T extends Record<string, unknown>, K extends keyof T = keyof T> = K extends string
? T[K] extends Record<string, unknown>
? `${K}.${GetI18nKey<T[K]>}`
: K
: never;
type I18nKey = GetI18nKey<Schema>;
}

View File

@ -1,5 +1,5 @@
import { useIconRender } from '@/composables';
import { t } from '@/locales';
import { $t } from '@/locales';
/**
*
@ -50,7 +50,7 @@ export function translateMenuLabel(menus: App.GlobalMenuOption[]): App.GlobalMen
const menuItem: App.GlobalMenuOption = {
...menu,
children: menuChildren,
label: menu.i18nTitle ? t(menu.i18nTitle) : menu.label
label: menu.i18nTitle ? $t(menu.i18nTitle) : menu.label
};
globalMenu.push(menuItem);
});

View File

@ -1,11 +1,11 @@
<template>
<n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
<n-form-item path="phone">
<n-input v-model:value="model.phone" placeholder="手机号码" />
<n-input v-model:value="model.phone" :placeholder="$t('page.login.common.phonePlaceholder')" />
</n-form-item>
<n-form-item path="code">
<div class="flex-y-center w-full">
<n-input v-model:value="model.code" placeholder="验证码" />
<n-input v-model:value="model.code" :placeholder="$t('page.login.common.codePlaceholder')" />
<div class="w-18px"></div>
<n-button size="large" :disabled="isCounting" :loading="smsLoading" @click="handleSmsCode">
{{ label }}
@ -13,8 +13,12 @@
</div>
</n-form-item>
<n-space :vertical="true" size="large">
<n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">确定</n-button>
<n-button size="large" :block="true" :round="true" @click="toLoginModule('pwd-login')">返回</n-button>
<n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">
{{ $t('page.login.common.confirm') }}
</n-button>
<n-button size="large" :block="true" :round="true" @click="toLoginModule('pwd-login')">
{{ $t('page.login.common.back') }}
</n-button>
</n-space>
</n-form>
</template>
@ -25,6 +29,7 @@ import type { FormInst } from 'naive-ui';
import { useRouterPush } from '@/composables';
import { useSmsCode } from '@/hooks';
import { formRules } from '@/utils';
import { $t } from '@/locales';
const { toLoginModule } = useRouterPush();
const { label, isCounting, loading: smsLoading, getSmsCode } = useSmsCode();
@ -48,7 +53,7 @@ function handleSmsCode() {
async function handleSubmit() {
await formRef.value?.validate();
window.$message?.success('验证成功!');
window.$message?.success($t('page.login.common.validateSuccess'));
}
</script>

View File

@ -1,11 +1,11 @@
<template>
<n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
<n-form-item path="phone">
<n-input v-model:value="model.phone" placeholder="手机号码" />
<n-input v-model:value="model.phone" :placeholder="$t('page.login.common.phonePlaceholder')" />
</n-form-item>
<n-form-item path="code">
<div class="flex-y-center w-full">
<n-input v-model:value="model.code" placeholder="验证码" />
<n-input v-model:value="model.code" :placeholder="$t('page.login.common.codePlaceholder')" />
<div class="w-18px"></div>
<n-button size="large" :disabled="isCounting" :loading="smsLoading" @click="handleSmsCode">
{{ label }}
@ -13,7 +13,7 @@
</div>
</n-form-item>
<n-form-item path="imgCode">
<n-input v-model:value="model.imgCode" placeholder="验证码,点击图片刷新" />
<n-input v-model:value="model.imgCode" :placeholder="$t('page.login.codeLogin.imageCodePlaceholder')" />
<div class="pl-8px">
<image-verify v-model:code="imgCode" />
</div>
@ -27,9 +27,11 @@
:loading="auth.loginLoading"
@click="handleSubmit"
>
确定
{{ $t('page.login.common.confirm') }}
</n-button>
<n-button size="large" :block="true" :round="true" @click="toLoginModule('pwd-login')">
{{ $t('page.login.common.back') }}
</n-button>
<n-button size="large" :block="true" :round="true" @click="toLoginModule('pwd-login')">返回</n-button>
</n-space>
</n-form>
</template>
@ -41,6 +43,7 @@ import { useAuthStore } from '@/store';
import { useRouterPush } from '@/composables';
import { useSmsCode } from '@/hooks';
import { formRules, getImgCodeRule } from '@/utils';
import { $t } from '@/locales';
const auth = useAuthStore();
const { toLoginModule } = useRouterPush();
@ -68,7 +71,7 @@ function handleSmsCode() {
async function handleSubmit() {
await formRef.value?.validate();
window.$message?.success('验证成功!');
window.$message?.success($t('page.login.common.validateSuccess'));
}
</script>

View File

@ -1,6 +1,6 @@
<template>
<n-space :vertical="true">
<n-divider class="!mb-0 text-14px text-#666">其他账户登录</n-divider>
<n-divider class="!mb-0 text-14px text-#666">{{ $t('page.login.pwdLogin.otherAccountLogin') }}</n-divider>
<n-space justify="center">
<n-button
v-for="item in accounts"
@ -15,25 +15,38 @@
</template>
<script lang="ts" setup>
import { userRoleLabels } from '@/constants';
import { $t } from '@/locales';
interface Emits {
(e: 'login', param: { userName: string; password: string }): void;
}
const emit = defineEmits<Emits>();
const accounts = [
interface Account {
key: Auth.RoleType;
label: string;
userName: string;
password: string;
}
const accounts: Account[] = [
{
label: '超级管理员',
key: 'super',
label: userRoleLabels.super,
userName: 'Super',
password: 'super123'
},
{
label: '管理员',
key: 'admin',
label: userRoleLabels.admin,
userName: 'Admin',
password: 'admin123'
},
{
label: '普通用户',
key: 'user',
label: userRoleLabels.user,
userName: 'User01',
password: 'user01123'
}

View File

@ -1,6 +1,6 @@
<template>
<n-space :vertical="true">
<n-divider class="!mb-0 text-14px text-#666">其他登录方式</n-divider>
<n-divider class="!mb-0 text-14px text-#666">{{ $t('page.login.pwdLogin.otherLoginMode') }}</n-divider>
<div class="flex-center">
<n-button :text="true">
<icon-mdi-wechat class="text-22px text-#888 hover:text-#52BF5E" />
@ -9,6 +9,8 @@
</n-space>
</template>
<script lang="ts" setup></script>
<script lang="ts" setup>
import { $t } from '@/locales';
</script>
<style scoped></style>

View File

@ -1,15 +1,22 @@
<template>
<n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
<n-form-item path="userName">
<n-input v-model:value="model.userName" placeholder="请输入用户名" />
<n-input v-model:value="model.userName" :placeholder="$t('page.login.common.userNamePlaceholder')" />
</n-form-item>
<n-form-item path="password">
<n-input v-model:value="model.password" type="password" show-password-on="click" placeholder="请输入密码" />
<n-input
v-model:value="model.password"
type="password"
show-password-on="click"
:placeholder="$t('page.login.common.passwordPlaceholder')"
/>
</n-form-item>
<n-space :vertical="true" :size="24">
<div class="flex-y-center justify-between">
<n-checkbox v-model:checked="rememberMe">记住我</n-checkbox>
<n-button :text="true" @click="toLoginModule('reset-pwd')">忘记密码</n-button>
<n-checkbox v-model:checked="rememberMe">{{ $t('page.login.pwdLogin.rememberMe') }}</n-checkbox>
<n-button :text="true" @click="toLoginModule('reset-pwd')">
{{ $t('page.login.pwdLogin.forgetPassword') }}
</n-button>
</div>
<n-button
type="primary"
@ -19,7 +26,7 @@
:loading="auth.loginLoading"
@click="handleSubmit"
>
确定
{{ $t('page.login.common.confirm') }}
</n-button>
<div class="flex-y-center justify-between">
<n-button class="flex-1" :block="true" @click="toLoginModule('code-login')">

View File

@ -1,11 +1,11 @@
<template>
<n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
<n-form-item path="phone">
<n-input v-model:value="model.phone" placeholder="手机号码" />
<n-input v-model:value="model.phone" :placeholder="$t('page.login.common.phonePlaceholder')" />
</n-form-item>
<n-form-item path="code">
<div class="flex-y-center w-full">
<n-input v-model:value="model.code" placeholder="验证码" />
<n-input v-model:value="model.code" :placeholder="$t('page.login.common.codePlaceholder')" />
<div class="w-18px"></div>
<n-button size="large" :disabled="isCounting" :loading="smsLoading" @click="handleSmsCode">
{{ label }}
@ -13,15 +13,29 @@
</div>
</n-form-item>
<n-form-item path="pwd">
<n-input v-model:value="model.pwd" type="password" show-password-on="click" placeholder="密码" />
<n-input
v-model:value="model.pwd"
type="password"
show-password-on="click"
:placeholder="$t('page.login.common.passwordPlaceholder')"
/>
</n-form-item>
<n-form-item path="confirmPwd">
<n-input v-model:value="model.confirmPwd" type="password" show-password-on="click" placeholder="确认密码" />
<n-input
v-model:value="model.confirmPwd"
type="password"
show-password-on="click"
:placeholder="$t('page.login.common.confirmPasswordPlaceholder')"
/>
</n-form-item>
<n-space :vertical="true" :size="18">
<login-agreement v-model:value="agreement" />
<n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">确定</n-button>
<n-button size="large" :block="true" :round="true" @click="toLoginModule('pwd-login')">返回</n-button>
<n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">
{{ $t('page.login.common.confirm') }}
</n-button>
<n-button size="large" :block="true" :round="true" @click="toLoginModule('pwd-login')">
{{ $t('page.login.common.back') }}
</n-button>
</n-space>
</n-form>
</template>
@ -32,6 +46,7 @@ import type { FormInst, FormRules } from 'naive-ui';
import { useRouterPush } from '@/composables';
import { useSmsCode } from '@/hooks';
import { formRules, getConfirmPwdRule } from '@/utils';
import { $t } from '@/locales';
const { toLoginModule } = useRouterPush();
const { label, isCounting, loading: smsLoading, start } = useSmsCode();
@ -60,7 +75,7 @@ function handleSmsCode() {
async function handleSubmit() {
await formRef.value?.validate();
window.$message?.success('验证成功!');
window.$message?.success($t('page.login.common.validateSuccess'));
}
</script>

View File

@ -1,11 +1,11 @@
<template>
<n-form ref="formRef" :model="model" :rules="rules" size="large" :show-label="false">
<n-form-item path="phone">
<n-input v-model:value="model.phone" placeholder="手机号码" />
<n-input v-model:value="model.phone" :placeholder="$t('page.login.common.phonePlaceholder')" />
</n-form-item>
<n-form-item path="code">
<div class="flex-y-center w-full">
<n-input v-model:value="model.code" placeholder="验证码" />
<n-input v-model:value="model.code" :placeholder="$t('page.login.common.codePlaceholder')" />
<div class="w-18px"></div>
<n-button size="large" :disabled="isCounting" :loading="smsLoading" @click="handleSmsCode">
{{ label }}
@ -13,14 +13,28 @@
</div>
</n-form-item>
<n-form-item path="pwd">
<n-input v-model:value="model.pwd" type="password" show-password-on="click" placeholder="密码" />
<n-input
v-model:value="model.pwd"
type="password"
show-password-on="click"
:placeholder="$t('page.login.common.passwordPlaceholder')"
/>
</n-form-item>
<n-form-item path="confirmPwd">
<n-input v-model:value="model.confirmPwd" type="password" show-password-on="click" placeholder="确认密码" />
<n-input
v-model:value="model.confirmPwd"
type="password"
show-password-on="click"
:placeholder="$t('page.login.common.confirmPasswordPlaceholder')"
/>
</n-form-item>
<n-space :vertical="true" size="large">
<n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">确定</n-button>
<n-button size="large" :block="true" :round="true" @click="toLoginModule('pwd-login')">返回</n-button>
<n-button type="primary" size="large" :block="true" :round="true" @click="handleSubmit">
{{ $t('page.login.common.confirm') }}
</n-button>
<n-button size="large" :block="true" :round="true" @click="toLoginModule('pwd-login')">
{{ $t('page.login.common.back') }}
</n-button>
</n-space>
</n-form>
</template>
@ -31,6 +45,7 @@ import type { FormInst, FormRules } from 'naive-ui';
import { useRouterPush } from '@/composables';
import { useSmsCode } from '@/hooks';
import { formRules, getConfirmPwdRule } from '@/utils';
import { $t } from '@/locales';
const { toLoginModule } = useRouterPush();
const { label, isCounting, loading: smsLoading, start } = useSmsCode();
@ -57,7 +72,7 @@ function handleSmsCode() {
async function handleSubmit() {
await formRef.value?.validate();
window.$message?.success('验证成功!');
window.$message?.success($t('page.login.common.validateSuccess'));
}
</script>

View File

@ -9,7 +9,7 @@
<div class="w-300px sm:w-360px">
<header class="flex-y-center justify-between">
<system-logo class="text-64px text-primary" />
<n-gradient-text type="primary" :size="28">{{ title }}</n-gradient-text>
<n-gradient-text type="primary" :size="28">{{ $t('system.title') }}</n-gradient-text>
</header>
<main class="pt-24px">
<h3 class="text-18px text-primary font-medium">{{ activeModule.label }}</h3>
@ -30,8 +30,8 @@ import { computed } from 'vue';
import type { Component } from 'vue';
import { loginModuleLabels } from '@/constants';
import { useThemeStore } from '@/store';
import { useAppInfo } from '@/composables';
import { getColorPalette, mixColor } from '@/utils';
import { $t } from '@/locales';
import { BindWechat, CodeLogin, LoginBg, PwdLogin, Register, ResetPwd } from './components';
interface Props {
@ -42,7 +42,6 @@ interface Props {
const props = defineProps<Props>();
const theme = useThemeStore();
const { title } = useAppInfo();
interface LoginModule {
key: UnionKey.LoginModule;