mirror of
https://github.com/chansee97/nova-admin.git
synced 2025-10-05 00:15:56 +08:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8417432343 | ||
|
f2e82e725f | ||
|
ac9d7e84e5 | ||
|
050ab3e7ed | ||
|
79501a53f0 | ||
|
501b64e884 | ||
|
03a7891ed4 | ||
|
e66e4fb17c | ||
|
ade869ea30 | ||
|
a1085e1922 | ||
|
2535252310 | ||
|
228ccaee5b | ||
|
3144acbc39 | ||
|
1b4639d5d8 |
2
.env
2
.env
@ -20,7 +20,7 @@ VITE_STORAGE_PREFIX =
|
|||||||
VITE_COPYRIGHT_INFO = Copyright © 2024 chansee97
|
VITE_COPYRIGHT_INFO = Copyright © 2024 chansee97
|
||||||
|
|
||||||
# 自动刷新token
|
# 自动刷新token
|
||||||
VITE_AUTO_REFRESH_TOKEN = Y
|
VITE_AUTO_REFRESH_TOKEN = N
|
||||||
|
|
||||||
# 默认多语言 enUS | zhCN
|
# 默认多语言 enUS | zhCN
|
||||||
VITE_DEFAULT_LANG = enUS
|
VITE_DEFAULT_LANG = enUS
|
||||||
|
2
.env.dev
2
.env.dev
@ -1,2 +1,2 @@
|
|||||||
# 是否开启服务接口代理 Y | N
|
# 是否开启服务接口代理 Y | N
|
||||||
VITE_HTTP_PROXY=Y
|
VITE_HTTP_PROXY=N
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
# 是否开启压缩资源
|
|
||||||
VITE_BUILD_COMPRESS=N
|
|
||||||
|
|
||||||
# 压缩算法 gzip | brotliCompress | deflate | deflateRaw
|
|
||||||
VITE_COMPRESS_TYPE=gzip
|
|
||||||
|
|
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@ -10,6 +10,7 @@
|
|||||||
"antfu.iconify",
|
"antfu.iconify",
|
||||||
"kisstkondoros.vscode-gutter-preview",
|
"kisstkondoros.vscode-gutter-preview",
|
||||||
"antfu.unocss",
|
"antfu.unocss",
|
||||||
"vue.volar"
|
"vue.volar",
|
||||||
|
"tu6ge.naive-ui-intelligence"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -34,11 +34,9 @@ export interface ServiceProxyPluginOptions {
|
|||||||
serviceConfig: FullServiceConfig
|
serviceConfig: FullServiceConfig
|
||||||
/** 代理路径前缀(可选,默认为 'proxy-') */
|
/** 代理路径前缀(可选,默认为 'proxy-') */
|
||||||
proxyPrefix?: string
|
proxyPrefix?: string
|
||||||
/** 开发环境名称(可选,默认为 'development') */
|
|
||||||
devEnvName?: ServiceEnvType
|
|
||||||
/** 是否启用代理配置 */
|
/** 是否启用代理配置 */
|
||||||
enableProxy?: boolean
|
enableProxy?: boolean
|
||||||
/** 环境变量名(可选,默认为 '__PROXY_MAPPING__') */
|
/** 环境变量名(可选,默认为 '__URL_MAP__') */
|
||||||
envName?: string
|
envName?: string
|
||||||
/** d.ts 类型文件生成路径(可选,如果传入路径则在该路径生成 d.ts 类型文件) */
|
/** d.ts 类型文件生成路径(可选,如果传入路径则在该路径生成 d.ts 类型文件) */
|
||||||
dts?: string
|
dts?: string
|
||||||
@ -47,16 +45,15 @@ export interface ServiceProxyPluginOptions {
|
|||||||
export default function createServiceProxyPlugin(options: ServiceProxyPluginOptions) {
|
export default function createServiceProxyPlugin(options: ServiceProxyPluginOptions) {
|
||||||
const {
|
const {
|
||||||
serviceConfig,
|
serviceConfig,
|
||||||
devEnvName = 'development',
|
|
||||||
proxyPrefix = 'proxy-',
|
proxyPrefix = 'proxy-',
|
||||||
enableProxy = true,
|
enableProxy = true,
|
||||||
envName = '__PROXY_MAPPING__',
|
envName = '__URL_MAP__',
|
||||||
dts,
|
dts,
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'vite-auto-proxy',
|
name: 'vite-auto-proxy',
|
||||||
config(config: UserConfig, { command }: { mode: string, command: 'build' | 'serve' }) {
|
config(config: UserConfig, { mode, command }: { mode: string, command: 'build' | 'serve' }) {
|
||||||
// 只在开发环境(serve命令)时生成代理配置
|
// 只在开发环境(serve命令)时生成代理配置
|
||||||
const isDev = command === 'serve'
|
const isDev = command === 'serve'
|
||||||
|
|
||||||
@ -66,9 +63,9 @@ export default function createServiceProxyPlugin(options: ServiceProxyPluginOpti
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!enableProxy || !isDev) {
|
if (!enableProxy || !isDev) {
|
||||||
// 在非开发环境下,生成原始地址映射(path 和 rawPath 都是原始地址)
|
|
||||||
const rawMapping: ProxyMapping = {}
|
const rawMapping: ProxyMapping = {}
|
||||||
const envConfig = serviceConfig[devEnvName]
|
const envConfig = serviceConfig[mode]
|
||||||
|
|
||||||
if (envConfig) {
|
if (envConfig) {
|
||||||
Object.entries(envConfig).forEach(([serviceName, serviceUrl]) => {
|
Object.entries(envConfig).forEach(([serviceName, serviceUrl]) => {
|
||||||
rawMapping[serviceName] = {
|
rawMapping[serviceName] = {
|
||||||
@ -76,7 +73,12 @@ export default function createServiceProxyPlugin(options: ServiceProxyPluginOpti
|
|||||||
rawPath: serviceUrl,
|
rawPath: serviceUrl,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
console.warn(`[auto-proxy] 已加载 ${Object.keys(envConfig).length} 个服务地址`)
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
console.warn(`[auto-proxy] 未找到环境 "${mode}" 的配置`)
|
||||||
|
}
|
||||||
|
|
||||||
config.define[envName] = JSON.stringify(rawMapping)
|
config.define[envName] = JSON.stringify(rawMapping)
|
||||||
|
|
||||||
// 生成 d.ts 类型文件(如果指定了路径)
|
// 生成 d.ts 类型文件(如果指定了路径)
|
||||||
@ -86,9 +88,9 @@ export default function createServiceProxyPlugin(options: ServiceProxyPluginOpti
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.warn(`[auto-proxy] 已加载${devEnvName}模式 ${Object.keys(serviceConfig[devEnvName]).length} 个服务地址`)
|
console.warn(`[auto-proxy] 已加载${mode}模式 ${Object.keys(serviceConfig[mode]).length} 个服务地址`)
|
||||||
|
|
||||||
const { proxyConfig, proxyMapping } = generateProxyFromServiceConfig(serviceConfig, devEnvName, proxyPrefix)
|
const { proxyConfig, proxyMapping } = generateProxyFromServiceConfig(serviceConfig, mode, proxyPrefix)
|
||||||
|
|
||||||
Object.entries(proxyMapping).forEach(([serviceName, proxyItem]) => {
|
Object.entries(proxyMapping).forEach(([serviceName, proxyItem]) => {
|
||||||
console.warn(`[auto-proxy] 服务: ${serviceName} | 代理地址: ${proxyItem.path} | 实际地址: ${proxyItem.rawPath}`)
|
console.warn(`[auto-proxy] 服务: ${serviceName} | 代理地址: ${proxyItem.path} | 实际地址: ${proxyItem.rawPath}`)
|
||||||
|
@ -83,7 +83,6 @@ export function createVitePlugins(env: ImportMetaEnv) {
|
|||||||
AutoProxy({
|
AutoProxy({
|
||||||
enableProxy: env.VITE_HTTP_PROXY === 'Y',
|
enableProxy: env.VITE_HTTP_PROXY === 'Y',
|
||||||
serviceConfig,
|
serviceConfig,
|
||||||
devEnvName: 'dev',
|
|
||||||
dts: 'src/typings/auto-proxy.d.ts',
|
dts: 'src/typings/auto-proxy.d.ts',
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="appLoading"></div>
|
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -108,9 +108,6 @@
|
|||||||
"QRCode": "QR code",
|
"QRCode": "QR code",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"clipboard": "Clipboard",
|
"clipboard": "Clipboard",
|
||||||
"demo403": "403",
|
|
||||||
"demo404": "404",
|
|
||||||
"demo500": "500",
|
|
||||||
"dictionarySetting": "Dictionary settings",
|
"dictionarySetting": "Dictionary settings",
|
||||||
"documents": "Document",
|
"documents": "Document",
|
||||||
"documentsVite": "Vite",
|
"documentsVite": "Vite",
|
||||||
@ -122,7 +119,6 @@
|
|||||||
"editor": "Editor",
|
"editor": "Editor",
|
||||||
"editorMd": "MarkDown editor",
|
"editorMd": "MarkDown editor",
|
||||||
"editorRich": "Rich text editor",
|
"editorRich": "Rich text editor",
|
||||||
"error": "Exception page",
|
|
||||||
"icons": "Icon",
|
"icons": "Icon",
|
||||||
"justSuper": "Supervisible",
|
"justSuper": "Supervisible",
|
||||||
"map": "Map",
|
"map": "Map",
|
||||||
|
@ -151,10 +151,6 @@
|
|||||||
"permission": "权限",
|
"permission": "权限",
|
||||||
"permissionDemo": "权限示例",
|
"permissionDemo": "权限示例",
|
||||||
"justSuper": "super可见",
|
"justSuper": "super可见",
|
||||||
"error": "异常页",
|
|
||||||
"demo403": "403",
|
|
||||||
"demo404": "404",
|
|
||||||
"demo500": "500",
|
|
||||||
"setting": "系统设置",
|
"setting": "系统设置",
|
||||||
"accountSetting": "用户设置",
|
"accountSetting": "用户设置",
|
||||||
"dictionarySetting": "字典设置",
|
"dictionarySetting": "字典设置",
|
||||||
|
17
netlify.toml
17
netlify.toml
@ -1,17 +0,0 @@
|
|||||||
[build]
|
|
||||||
publish = "dist"
|
|
||||||
command = "vite build --mode prod"
|
|
||||||
|
|
||||||
[build.environment]
|
|
||||||
NODE_VERSION = "20"
|
|
||||||
|
|
||||||
[[redirects]]
|
|
||||||
from = "/*"
|
|
||||||
to = "/index.html"
|
|
||||||
status = 200
|
|
||||||
|
|
||||||
[[headers]]
|
|
||||||
for = "/manifest.webmanifest"
|
|
||||||
|
|
||||||
[headers.values]
|
|
||||||
Content-Type = "application/manifest+json"
|
|
48
package.json
48
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "nova-admin",
|
"name": "nova-admin",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.9.15",
|
"version": "0.9.18",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "a clean and concise back-end management template based on Vue3, Vite5, Typescript, and Naive UI.",
|
"description": "a clean and concise back-end management template based on Vue3, Vite5, Typescript, and Naive UI.",
|
||||||
"author": {
|
"author": {
|
||||||
@ -38,11 +38,9 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --mode dev --port 9980",
|
"dev": "vite --mode dev --port 9980",
|
||||||
"dev:test": "vite --mode test",
|
"dev:prod": "vite --mode production",
|
||||||
"dev:prod": "vite --mode prod",
|
"build": "vite build",
|
||||||
"build": "vite build --mode prod",
|
|
||||||
"build:dev": "vite build --mode dev",
|
"build:dev": "vite build --mode dev",
|
||||||
"build:test": "vite build --mode test",
|
|
||||||
"preview": "vite preview --port 9981",
|
"preview": "vite preview --port 9981",
|
||||||
"lint": "eslint . && vue-tsc --noEmit",
|
"lint": "eslint . && vue-tsc --noEmit",
|
||||||
"lint:fix": "eslint . --fix",
|
"lint:fix": "eslint . --fix",
|
||||||
@ -50,43 +48,43 @@
|
|||||||
"sizecheck": "npx vite-bundle-visualizer"
|
"sizecheck": "npx vite-bundle-visualizer"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vueuse/core": "^13.3.0",
|
"@vueuse/core": "^13.6.0",
|
||||||
"alova": "^3.3.2",
|
"alova": "^3.3.4",
|
||||||
"colord": "^2.9.3",
|
"colord": "^2.9.3",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"md-editor-v3": "^5.6.1",
|
"md-editor-v3": "^5.6.1",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"pinia-plugin-persistedstate": "^4.3.0",
|
"pinia-plugin-persistedstate": "^4.4.1",
|
||||||
"pro-naive-ui": "^2.4.3",
|
"pro-naive-ui": "^2.4.3",
|
||||||
"quill": "^2.0.3",
|
"quill": "^2.0.3",
|
||||||
"radash": "^12.1.0",
|
"radash": "^12.1.1",
|
||||||
"vue": "^3.5.16",
|
"vue": "^3.5.18",
|
||||||
"vue-draggable-plus": "^0.6.0",
|
"vue-draggable-plus": "^0.6.0",
|
||||||
"vue-i18n": "^11.1.5",
|
"vue-i18n": "^11.1.11",
|
||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^4.14.1",
|
"@antfu/eslint-config": "^5.0.0",
|
||||||
"@iconify-json/icon-park-outline": "^1.2.2",
|
"@iconify-json/icon-park-outline": "^1.2.2",
|
||||||
"@iconify/vue": "^4.3.0",
|
"@iconify/vue": "^5.0.0",
|
||||||
"@types/node": "^24.0.1",
|
"@types/node": "^24.1.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.4",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
"@vitejs/plugin-vue-jsx": "^4.2.0",
|
"@vitejs/plugin-vue-jsx": "^5.0.1",
|
||||||
"eslint": "^9.29.0",
|
"eslint": "^9.29.0",
|
||||||
"lint-staged": "^16.1.2",
|
"lint-staged": "^16.1.2",
|
||||||
"naive-ui": "^2.41.1",
|
"naive-ui": "^2.42.0",
|
||||||
"sass": "^1.86.3",
|
"sass": "^1.89.2",
|
||||||
"simple-git-hooks": "^2.13.0",
|
"simple-git-hooks": "^2.13.1",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"unocss": "^66.2.0",
|
"unocss": "^66.3.3",
|
||||||
"unplugin-auto-import": "^19.3.0",
|
"unplugin-auto-import": "^19.3.0",
|
||||||
"unplugin-icons": "^22.1.0",
|
"unplugin-icons": "^22.2.0",
|
||||||
"unplugin-vue-components": "^28.7.0",
|
"unplugin-vue-components": "^28.8.0",
|
||||||
"vite": "^6.3.5",
|
"vite": "^7.0.6",
|
||||||
"vite-bundle-visualizer": "^1.2.1",
|
"vite-bundle-visualizer": "^1.2.1",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-vue-devtools": "7.7.6",
|
"vite-plugin-vue-devtools": "8.0.0",
|
||||||
"vue-tsc": "^2.2.10"
|
"vue-tsc": "^3.0.5"
|
||||||
},
|
},
|
||||||
"simple-git-hooks": {
|
"simple-git-hooks": {
|
||||||
"pre-commit": "pnpm lint-staged"
|
"pre-commit": "pnpm lint-staged"
|
||||||
|
6
pnpm-workspace.yaml
Normal file
6
pnpm-workspace.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
ignoredBuiltDependencies:
|
||||||
|
- '@parcel/watcher'
|
||||||
|
- esbuild
|
||||||
|
- simple-git-hooks
|
||||||
|
- vue-demi
|
||||||
|
- unrs-resolver
|
@ -1,12 +1,9 @@
|
|||||||
/** 不同请求服务的环境配置 */
|
/** 不同请求服务的环境配置 */
|
||||||
export const serviceConfig: Record<ServiceEnvType, Record<string, string>> = {
|
export const serviceConfig: Record<ServiceEnvType, Record<string, string>> = {
|
||||||
dev: {
|
dev: {
|
||||||
url: 'https://mock.apifox.cn/m1/4071143-0-default',
|
url: 'http://localhost:3000',
|
||||||
},
|
},
|
||||||
test: {
|
production: {
|
||||||
url: 'https://mock.apifox.cn/m1/4071143-0-default',
|
|
||||||
},
|
|
||||||
prod: {
|
|
||||||
url: 'https://mock.apifox.cn/m1/4071143-0-default',
|
url: 'https://mock.apifox.cn/m1/4071143-0-default',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
30
src/App.vue
30
src/App.vue
@ -1,24 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { naiveI18nOptions } from '@/utils'
|
import AppMain from './AppMain.vue'
|
||||||
import { darkTheme } from 'naive-ui'
|
import AppLoading from './components/common/AppLoading.vue'
|
||||||
import { useAppStore } from './store'
|
|
||||||
|
|
||||||
const appStore = useAppStore()
|
// 使用 Suspense 处理异步组件加载
|
||||||
|
|
||||||
const naiveLocale = computed(() => {
|
|
||||||
return naiveI18nOptions[appStore.lang] ? naiveI18nOptions[appStore.lang] : naiveI18nOptions.enUS
|
|
||||||
},
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-config-provider
|
<Suspense>
|
||||||
class="wh-full" inline-theme-disabled :theme="appStore.colorMode === 'dark' ? darkTheme : null"
|
<!-- 异步组件 -->
|
||||||
:locale="naiveLocale.locale" :date-locale="naiveLocale.dateLocale" :theme-overrides="appStore.theme"
|
<AppMain />
|
||||||
>
|
|
||||||
<naive-provider>
|
<!-- 加载状态 -->
|
||||||
<router-view />
|
<template #fallback>
|
||||||
<Watermark :show-watermark="appStore.showWatermark" />
|
<AppLoading />
|
||||||
</naive-provider>
|
</template>
|
||||||
</n-config-provider>
|
</Suspense>
|
||||||
</template>
|
</template>
|
||||||
|
57
src/AppMain.vue
Normal file
57
src/AppMain.vue
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { App } from 'vue'
|
||||||
|
import { installRouter } from '@/router'
|
||||||
|
import { installPinia } from '@/store'
|
||||||
|
import { naiveI18nOptions } from '@/utils'
|
||||||
|
import { darkTheme } from 'naive-ui'
|
||||||
|
import { useAppStore } from './store'
|
||||||
|
|
||||||
|
// 创建异步初始化 Promise - 这会让组件变成异步组件
|
||||||
|
const initializationPromise = (async () => {
|
||||||
|
// 获取当前应用实例
|
||||||
|
const app = getCurrentInstance()?.appContext.app
|
||||||
|
if (!app) {
|
||||||
|
throw new Error('Failed to get app instance')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册模块 Pinia
|
||||||
|
await installPinia(app)
|
||||||
|
|
||||||
|
// 注册模块 Vue-router
|
||||||
|
await installRouter(app)
|
||||||
|
|
||||||
|
// 注册模块 指令/静态资源
|
||||||
|
const modules = import.meta.glob<{ install: (app: App) => void }>('./modules/*.ts', {
|
||||||
|
eager: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.values(modules).forEach(module => app.use(module))
|
||||||
|
|
||||||
|
return true
|
||||||
|
})()
|
||||||
|
|
||||||
|
// 等待初始化完成 - 这使得整个 setup 函数变成异步的
|
||||||
|
await initializationPromise
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
const naiveLocale = computed(() => {
|
||||||
|
return naiveI18nOptions[appStore.lang] ? naiveI18nOptions[appStore.lang] : naiveI18nOptions.enUS
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-config-provider
|
||||||
|
class="wh-full"
|
||||||
|
inline-theme-disabled
|
||||||
|
:theme="appStore.colorMode === 'dark' ? darkTheme : null"
|
||||||
|
:locale="naiveLocale.locale"
|
||||||
|
:date-locale="naiveLocale.dateLocale"
|
||||||
|
:theme-overrides="appStore.theme"
|
||||||
|
>
|
||||||
|
<naive-provider>
|
||||||
|
<router-view />
|
||||||
|
<Watermark :show-watermark="appStore.showWatermark" />
|
||||||
|
</naive-provider>
|
||||||
|
</n-config-provider>
|
||||||
|
</template>
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 60 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
@ -1,36 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
defineProps<{
|
|
||||||
/** 异常类型 403 404 500 */
|
|
||||||
type: '403' | '404' | '500'
|
|
||||||
}>()
|
|
||||||
const router = useRouter()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="flex-col-center h-full">
|
|
||||||
<img
|
|
||||||
v-if="type === '403'"
|
|
||||||
src="@/assets/svg/error-403.svg"
|
|
||||||
alt=""
|
|
||||||
class="w-1/3"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
v-if="type === '404'"
|
|
||||||
src="@/assets/svg/error-404.svg"
|
|
||||||
alt=""
|
|
||||||
class="w-1/3"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
v-if="type === '500'"
|
|
||||||
src="@/assets/svg/error-500.svg"
|
|
||||||
alt=""
|
|
||||||
class="w-1/3"
|
|
||||||
>
|
|
||||||
<n-button
|
|
||||||
type="primary"
|
|
||||||
@click="router.push('/')"
|
|
||||||
>
|
|
||||||
{{ $t('app.backHome') }}
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
@ -6,11 +6,15 @@ const routeStore = useRouteStore()
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-el>
|
<n-el
|
||||||
|
class="h-full"
|
||||||
|
:class="[
|
||||||
|
appStore.layoutMode === 'full-content' ? 'p-0' : 'p-16px',
|
||||||
|
]"
|
||||||
|
style="background-color: var(--action-color);"
|
||||||
|
>
|
||||||
<router-view
|
<router-view
|
||||||
v-slot="{ Component, route }"
|
v-slot="{ Component, route }"
|
||||||
class="flex-1 p-16px"
|
|
||||||
style="background-color: var(--action-color);"
|
|
||||||
>
|
>
|
||||||
<transition :name="appStore.transitionAnimation" mode="out-in">
|
<transition :name="appStore.transitionAnimation" mode="out-in">
|
||||||
<keep-alive :include="routeStore.cacheRoutes">
|
<keep-alive :include="routeStore.cacheRoutes">
|
||||||
|
38
src/layouts/components/common/MobileDrawer.vue
Normal file
38
src/layouts/components/common/MobileDrawer.vue
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Search from '../header/Search.vue'
|
||||||
|
import Notices from '../header/Notices.vue'
|
||||||
|
import UserCenter from '../header/UserCenter.vue'
|
||||||
|
import Setting from './Setting.vue'
|
||||||
|
|
||||||
|
const showDrawer = defineModel<boolean>('show', { default: false })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-drawer
|
||||||
|
v-model:show="showDrawer"
|
||||||
|
:width="280"
|
||||||
|
placement="right"
|
||||||
|
:mask-closable="true"
|
||||||
|
:close-on-esc="true"
|
||||||
|
>
|
||||||
|
<n-drawer-content :native-scrollbar="false" :body-content-style="{ padding: '0' }">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<UserCenter />
|
||||||
|
<div class="ml-auto" />
|
||||||
|
<Search />
|
||||||
|
<Notices />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<slot />
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<DarkModeSwitch />
|
||||||
|
<LangsSwitch />
|
||||||
|
<div class="ml-auto" />
|
||||||
|
<Setting />
|
||||||
|
</template>
|
||||||
|
</n-drawer-content>
|
||||||
|
</n-drawer>
|
||||||
|
</template>
|
@ -1,7 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useBoolean } from '@/hooks'
|
import { useBoolean } from '@/hooks'
|
||||||
import { useRouteStore } from '@/store'
|
import { useAppStore, useRouteStore } from '@/store'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
const routeStore = useRouteStore()
|
const routeStore = useRouteStore()
|
||||||
|
|
||||||
// 搜索值
|
// 搜索值
|
||||||
@ -143,13 +144,14 @@ function handleMouseEnter(index: number) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<CommonWrapper @click="openModal">
|
<CommonWrapper @click="openModal">
|
||||||
<icon-park-outline-search /><n-tag round size="small" class="font-mono cursor-pointer">
|
<icon-park-outline-search />
|
||||||
|
<n-tag v-if="!appStore.isMobile" round size="small" class="font-mono cursor-pointer">
|
||||||
CtrlK
|
CtrlK
|
||||||
</n-tag>
|
</n-tag>
|
||||||
</CommonWrapper>
|
</CommonWrapper>
|
||||||
<n-modal
|
<n-modal
|
||||||
v-model:show="showModal"
|
v-model:show="showModal"
|
||||||
class="w-560px fixed top-60px inset-x-0"
|
class="w-560px fixed top-60px inset-x-0 max-w-full"
|
||||||
size="small"
|
size="small"
|
||||||
preset="card"
|
preset="card"
|
||||||
:segmented="{
|
:segmented="{
|
||||||
|
@ -61,7 +61,7 @@ function handleSelect(key: string | number) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (key === 'userCenter')
|
if (key === 'userCenter')
|
||||||
router.push('/userCenter')
|
router.push('/user-center')
|
||||||
|
|
||||||
if (key === 'guthub')
|
if (key === 'guthub')
|
||||||
window.open('https://github.com/chansee97/nova-admin')
|
window.open('https://github.com/chansee97/nova-admin')
|
||||||
|
@ -2,6 +2,7 @@ import BackTop from './common/BackTop.vue'
|
|||||||
import Setting from './common/Setting.vue'
|
import Setting from './common/Setting.vue'
|
||||||
import SettingDrawer from './common/SettingDrawer.vue'
|
import SettingDrawer from './common/SettingDrawer.vue'
|
||||||
import Logo from './common/Logo.vue'
|
import Logo from './common/Logo.vue'
|
||||||
|
import MobileDrawer from './common/MobileDrawer.vue'
|
||||||
|
|
||||||
import Breadcrumb from './header/Breadcrumb.vue'
|
import Breadcrumb from './header/Breadcrumb.vue'
|
||||||
import CollapaseButton from './header/CollapaseButton.vue'
|
import CollapaseButton from './header/CollapaseButton.vue'
|
||||||
@ -18,6 +19,7 @@ export {
|
|||||||
CollapaseButton,
|
CollapaseButton,
|
||||||
FullScreen,
|
FullScreen,
|
||||||
Logo,
|
Logo,
|
||||||
|
MobileDrawer,
|
||||||
Notices,
|
Notices,
|
||||||
Search,
|
Search,
|
||||||
Setting,
|
Setting,
|
||||||
|
@ -2,16 +2,49 @@
|
|||||||
import { useAppStore } from '@/store'
|
import { useAppStore } from '@/store'
|
||||||
|
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
let previousLayoutMode = appStore.layoutMode
|
||||||
|
|
||||||
|
function enterFullContent() {
|
||||||
|
previousLayoutMode = appStore.layoutMode
|
||||||
|
appStore.layoutMode = 'full-content'
|
||||||
|
}
|
||||||
|
|
||||||
|
function exitFullContent() {
|
||||||
|
// 如果是全屏或者数据不存在,则恢复为默认的vertical
|
||||||
|
if (previousLayoutMode === 'full-content' || !previousLayoutMode) {
|
||||||
|
previousLayoutMode = 'vertical'
|
||||||
|
}
|
||||||
|
appStore.layoutMode = previousLayoutMode
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-tooltip placement="bottom" trigger="hover">
|
<n-tooltip v-if="!appStore.isMobile" placement="bottom" trigger="hover">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<CommonWrapper @click="appStore.contentFullScreen = !appStore.contentFullScreen">
|
<CommonWrapper @click="enterFullContent">
|
||||||
<icon-park-outline-off-screen-one v-if="appStore.contentFullScreen" />
|
<icon-park-outline-full-screen-one />
|
||||||
<icon-park-outline-full-screen-one v-else />
|
|
||||||
</CommonWrapper>
|
</CommonWrapper>
|
||||||
</template>
|
</template>
|
||||||
<span>{{ $t('app.togglContentFullScreen') }}</span>
|
{{ $t('app.togglContentFullScreen') }}
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
|
|
||||||
|
<Teleport to="body">
|
||||||
|
<div
|
||||||
|
v-if="appStore.layoutMode === 'full-content'"
|
||||||
|
class="fixed top-4 right-0 z-[9999]"
|
||||||
|
>
|
||||||
|
<n-tooltip placement="left" trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<n-el
|
||||||
|
class="bg-[var(--primary-color)] c-[var(--base-color)] rounded-l-lg shadow-lg p-2 cursor-pointer"
|
||||||
|
@click="exitFullContent"
|
||||||
|
>
|
||||||
|
<icon-park-outline-off-screen-one />
|
||||||
|
</n-el>
|
||||||
|
</template>
|
||||||
|
{{ $t('app.togglContentFullScreen') }}
|
||||||
|
</n-tooltip>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
CollapaseButton,
|
CollapaseButton,
|
||||||
FullScreen,
|
FullScreen,
|
||||||
Logo,
|
Logo,
|
||||||
|
MobileDrawer,
|
||||||
Notices,
|
Notices,
|
||||||
Search,
|
Search,
|
||||||
Setting,
|
Setting,
|
||||||
@ -14,12 +15,12 @@ import {
|
|||||||
UserCenter,
|
UserCenter,
|
||||||
} from './components'
|
} from './components'
|
||||||
import Content from './Content.vue'
|
import Content from './Content.vue'
|
||||||
|
|
||||||
import { ProLayout, useLayoutMenu } from 'pro-naive-ui'
|
import { ProLayout, useLayoutMenu } from 'pro-naive-ui'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const routeStore = useRouteStore()
|
const routeStore = useRouteStore()
|
||||||
|
|
||||||
const { layoutMode } = storeToRefs(useAppStore())
|
const { layoutMode } = storeToRefs(useAppStore())
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -31,16 +32,19 @@ const {
|
|||||||
menus: routeStore.menus,
|
menus: routeStore.menus,
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => route.path, (value) => {
|
watch(() => route.path, () => {
|
||||||
activeKey.value = value
|
activeKey.value = routeStore.activeMenu
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
|
// 移动端抽屉控制
|
||||||
|
const showMobileDrawer = ref(false)
|
||||||
|
|
||||||
const sidebarWidth = ref(240)
|
const sidebarWidth = ref(240)
|
||||||
const sidebarCollapsedWidth = ref(64)
|
const sidebarCollapsedWidth = ref(64)
|
||||||
|
|
||||||
const hasHorizontalMenu = computed(() => ['horizontal', 'mixed-two-column', 'mixed-sidebar'].includes(layoutMode.value))
|
const hasHorizontalMenu = computed(() => ['horizontal', 'mixed-two-column', 'mixed-sidebar'].includes(layoutMode.value))
|
||||||
|
|
||||||
const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.value))
|
const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.value) || appStore.isMobile)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -48,7 +52,8 @@ const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.v
|
|||||||
<ProLayout
|
<ProLayout
|
||||||
v-model:collapsed="appStore.collapsed"
|
v-model:collapsed="appStore.collapsed"
|
||||||
:mode="layoutMode"
|
:mode="layoutMode"
|
||||||
:show-logo="appStore.showLogo"
|
:is-mobile="appStore.isMobile"
|
||||||
|
:show-logo="appStore.showLogo && !appStore.isMobile"
|
||||||
:show-footer="appStore.showFooter"
|
:show-footer="appStore.showFooter"
|
||||||
:show-tabbar="appStore.showTabs"
|
:show-tabbar="appStore.showTabs"
|
||||||
nav-fixed
|
nav-fixed
|
||||||
@ -65,11 +70,17 @@ const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.v
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #nav-left>
|
<template #nav-left>
|
||||||
|
<template v-if="appStore.isMobile">
|
||||||
|
<Logo />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
<div v-if="!hasHorizontalMenu || !hidenCollapaseButton" class="h-full flex-y-center gap-1 p-x-sm">
|
<div v-if="!hasHorizontalMenu || !hidenCollapaseButton" class="h-full flex-y-center gap-1 p-x-sm">
|
||||||
<CollapaseButton v-if="!hidenCollapaseButton" />
|
<CollapaseButton v-if="!hidenCollapaseButton" />
|
||||||
<Breadcrumb v-if="!hasHorizontalMenu" />
|
<Breadcrumb v-if="!hasHorizontalMenu" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #nav-center>
|
<template #nav-center>
|
||||||
<div class="h-full flex-y-center gap-1">
|
<div class="h-full flex-y-center gap-1">
|
||||||
@ -79,6 +90,22 @@ const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.v
|
|||||||
|
|
||||||
<template #nav-right>
|
<template #nav-right>
|
||||||
<div class="h-full flex-y-center gap-1 p-x-xl">
|
<div class="h-full flex-y-center gap-1 p-x-xl">
|
||||||
|
<!-- 移动端:只显示菜单按钮 -->
|
||||||
|
<template v-if="appStore.isMobile">
|
||||||
|
<n-button
|
||||||
|
quaternary
|
||||||
|
@click="showMobileDrawer = true"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<n-icon size="18">
|
||||||
|
<icon-park-outline-hamburger-button />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
</n-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 桌面端:显示完整功能组件 -->
|
||||||
|
<template v-else>
|
||||||
<Search />
|
<Search />
|
||||||
<Notices />
|
<Notices />
|
||||||
<FullScreen />
|
<FullScreen />
|
||||||
@ -86,6 +113,7 @@ const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.v
|
|||||||
<LangsSwitch />
|
<LangsSwitch />
|
||||||
<Setting />
|
<Setting />
|
||||||
<UserCenter />
|
<UserCenter />
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -111,5 +139,10 @@ const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.v
|
|||||||
<Content />
|
<Content />
|
||||||
<BackTop />
|
<BackTop />
|
||||||
<SettingDrawer />
|
<SettingDrawer />
|
||||||
|
|
||||||
|
<!-- 移动端功能抽屉 -->
|
||||||
|
<MobileDrawer v-model:show="showMobileDrawer">
|
||||||
|
<n-menu v-bind="layout.verticalMenuProps" />
|
||||||
|
</MobileDrawer>
|
||||||
</ProLayout>
|
</ProLayout>
|
||||||
</template>
|
</template>
|
||||||
|
36
src/main.ts
36
src/main.ts
@ -1,35 +1,5 @@
|
|||||||
import type { App } from 'vue'
|
import App from './App.vue'
|
||||||
import { installRouter } from '@/router'
|
|
||||||
import { installPinia } from '@/store'
|
|
||||||
import AppVue from './App.vue'
|
|
||||||
import AppLoading from './components/common/AppLoading.vue'
|
|
||||||
|
|
||||||
async function setupApp() {
|
// 创建应用实例并挂载
|
||||||
// 载入全局loading加载状态
|
const app = createApp(App)
|
||||||
const appLoading = createApp(AppLoading)
|
|
||||||
appLoading.mount('#appLoading')
|
|
||||||
|
|
||||||
// 创建vue实例
|
|
||||||
const app = createApp(AppVue)
|
|
||||||
|
|
||||||
// 注册模块Pinia
|
|
||||||
await installPinia(app)
|
|
||||||
|
|
||||||
// 注册模块 Vue-router
|
|
||||||
await installRouter(app)
|
|
||||||
|
|
||||||
/* 注册模块 指令/静态资源 */
|
|
||||||
Object.values(
|
|
||||||
import.meta.glob<{ install: (app: App) => void }>('./modules/*.ts', {
|
|
||||||
eager: true,
|
|
||||||
}),
|
|
||||||
).map(i => app.use(i))
|
|
||||||
|
|
||||||
// 卸载载入动画
|
|
||||||
appLoading.unmount()
|
|
||||||
|
|
||||||
// 挂载
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
}
|
|
||||||
|
|
||||||
setupApp()
|
|
||||||
|
@ -22,6 +22,19 @@ export function setupRouterGuard(router: Router) {
|
|||||||
// 判断有无TOKEN,登录鉴权
|
// 判断有无TOKEN,登录鉴权
|
||||||
const isLogin = Boolean(local.get('accessToken'))
|
const isLogin = Boolean(local.get('accessToken'))
|
||||||
|
|
||||||
|
// 处理根路由重定向
|
||||||
|
if (to.name === 'root') {
|
||||||
|
if (isLogin) {
|
||||||
|
// 已登录,重定向到首页
|
||||||
|
next({ path: import.meta.env.VITE_HOME_PATH, replace: true })
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 未登录,重定向到登录页
|
||||||
|
next({ path: '/login', replace: true })
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 如果是login路由,直接放行
|
// 如果是login路由,直接放行
|
||||||
if (to.name === 'login') {
|
if (to.name === 'login') {
|
||||||
// login页面不需要任何认证检查,直接放行
|
// login页面不需要任何认证检查,直接放行
|
||||||
@ -34,16 +47,17 @@ export function setupRouterGuard(router: Router) {
|
|||||||
}
|
}
|
||||||
// 如果路由设置了requiresAuth为true,且用户未登录,重定向到登录页
|
// 如果路由设置了requiresAuth为true,且用户未登录,重定向到登录页
|
||||||
else if (to.meta.requiresAuth === true && !isLogin) {
|
else if (to.meta.requiresAuth === true && !isLogin) {
|
||||||
const redirect = to.name === '404' ? undefined : to.fullPath
|
const redirect = to.name === 'not-found' ? undefined : to.fullPath
|
||||||
next({ path: '/login', query: { redirect } })
|
next({ path: '/login', query: { redirect } })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断路由有无进行初始化
|
// 判断路由有无进行初始化
|
||||||
if (!routeStore.isInitAuthRoute) {
|
if (!routeStore.isInitAuthRoute && to.name !== 'login') {
|
||||||
|
try {
|
||||||
await routeStore.initAuthRoute()
|
await routeStore.initAuthRoute()
|
||||||
// 动态路由加载完回到根路由
|
// 动态路由加载完回到根路由
|
||||||
if (to.name === '404') {
|
if (to.name === 'not-found') {
|
||||||
// 等待权限路由加载好了,回到之前的路由,否则404
|
// 等待权限路由加载好了,回到之前的路由,否则404
|
||||||
next({
|
next({
|
||||||
path: to.fullPath,
|
path: to.fullPath,
|
||||||
@ -54,6 +68,13 @@ export function setupRouterGuard(router: Router) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
// 如果路由初始化失败(比如 401 错误),重定向到登录页
|
||||||
|
const redirect = to.fullPath !== '/' ? to.fullPath : undefined
|
||||||
|
next({ path: '/login', query: redirect ? { redirect } : undefined })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 如果用户已登录且访问login页面,重定向到首页
|
// 如果用户已登录且访问login页面,重定向到首页
|
||||||
if (to.name === 'login' && isLogin) {
|
if (to.name === 'login' && isLogin) {
|
||||||
|
@ -5,14 +5,13 @@ export const routes: RouteRecordRaw[] = [
|
|||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'root',
|
name: 'root',
|
||||||
redirect: '/appRoot',
|
|
||||||
children: [
|
children: [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'login',
|
name: 'login',
|
||||||
component: () => import('@/views/login/index.vue'), // 注意这里要带上 文件后缀.vue
|
component: () => import('@/views/build-in/login/index.vue'), // 注意这里要带上 文件后缀.vue
|
||||||
meta: {
|
meta: {
|
||||||
title: '登录',
|
title: '登录',
|
||||||
withoutTab: true,
|
withoutTab: true,
|
||||||
@ -21,7 +20,7 @@ export const routes: RouteRecordRaw[] = [
|
|||||||
{
|
{
|
||||||
path: '/public',
|
path: '/public',
|
||||||
name: 'publicAccess',
|
name: 'publicAccess',
|
||||||
component: () => import('@/views/demo/publicAccess/index.vue'),
|
component: () => import('@/views/build-in/public-access/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '公共访问示例',
|
title: '公共访问示例',
|
||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
@ -29,38 +28,19 @@ export const routes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/403',
|
path: '/not-found',
|
||||||
name: '403',
|
name: 'not-found',
|
||||||
component: () => import('@/views/error/403/index.vue'),
|
component: () => import('@/views/build-in/not-found/index.vue'),
|
||||||
meta: {
|
|
||||||
title: '用户无权限',
|
|
||||||
withoutTab: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/404',
|
|
||||||
name: '404',
|
|
||||||
component: () => import('@/views/error/404/index.vue'),
|
|
||||||
meta: {
|
meta: {
|
||||||
title: '找不到页面',
|
title: '找不到页面',
|
||||||
icon: 'icon-park-outline:ghost',
|
icon: 'icon-park-outline:ghost',
|
||||||
withoutTab: true,
|
withoutTab: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/500',
|
|
||||||
name: '500',
|
|
||||||
component: () => import('@/views/error/500/index.vue'),
|
|
||||||
meta: {
|
|
||||||
title: '服务器错误',
|
|
||||||
icon: 'icon-park-outline:close-wifi',
|
|
||||||
withoutTab: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
component: () => import('@/views/error/404/index.vue'),
|
component: () => import('@/views/build-in/not-found/index.vue'),
|
||||||
name: '404',
|
name: 'not-found',
|
||||||
meta: {
|
meta: {
|
||||||
title: '找不到页面',
|
title: '找不到页面',
|
||||||
icon: 'icon-park-outline:ghost',
|
icon: 'icon-park-outline:ghost',
|
||||||
|
@ -19,7 +19,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
pinTab: true,
|
pinTab: true,
|
||||||
menuType: 'page',
|
menuType: 'page',
|
||||||
componentPath: '/dashboard/workbench/index.vue',
|
componentPath: '/dashboard/workbench/index.vue',
|
||||||
id: 2,
|
id: 101,
|
||||||
pid: 1,
|
pid: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -30,7 +30,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
icon: 'icon-park-outline:anchor',
|
icon: 'icon-park-outline:anchor',
|
||||||
menuType: 'page',
|
menuType: 'page',
|
||||||
componentPath: '/dashboard/monitor/index.vue',
|
componentPath: '/dashboard/monitor/index.vue',
|
||||||
id: 3,
|
id: 102,
|
||||||
pid: 1,
|
pid: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -41,53 +41,53 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
icon: 'icon-park-outline:list',
|
icon: 'icon-park-outline:list',
|
||||||
menuType: 'dir',
|
menuType: 'dir',
|
||||||
componentPath: null,
|
componentPath: null,
|
||||||
id: 4,
|
id: 2,
|
||||||
pid: null,
|
pid: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'multi2',
|
name: 'multi2',
|
||||||
path: '/multi/multi2',
|
path: '/multi/multi-2',
|
||||||
title: '多级菜单子页',
|
title: '多级菜单子页',
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'icon-park-outline:list',
|
icon: 'icon-park-outline:list',
|
||||||
menuType: 'page',
|
menuType: 'page',
|
||||||
componentPath: '/demo/multi/multi2/index.vue',
|
componentPath: '/demo/multi/multi-2/index.vue',
|
||||||
id: 6,
|
id: 201,
|
||||||
pid: 4,
|
pid: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'multi2Detail',
|
name: 'multi2-detail',
|
||||||
path: '/multi/multi2/detail',
|
path: '/multi/multi-2/detail',
|
||||||
title: '菜单详情页',
|
title: '菜单详情页',
|
||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
icon: 'icon-park-outline:list',
|
icon: 'icon-park-outline:list',
|
||||||
hide: true,
|
hide: true,
|
||||||
activeMenu: '/multi/multi2',
|
activeMenu: '/multi/multi-2',
|
||||||
menuType: 'page',
|
menuType: 'page',
|
||||||
componentPath: '/demo/multi/multi2/detail/index.vue',
|
componentPath: '/demo/multi/multi-2/detail/index.vue',
|
||||||
id: 7,
|
id: 20101,
|
||||||
pid: 4,
|
pid: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'multi3',
|
name: 'multi3',
|
||||||
path: '/multi/multi3',
|
path: '/multi/multi-3',
|
||||||
title: '多级菜单',
|
title: '多级菜单',
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'icon-park-outline:list',
|
icon: 'icon-park-outline:list',
|
||||||
menuType: 'dir',
|
menuType: 'dir',
|
||||||
componentPath: null,
|
componentPath: null,
|
||||||
id: 8,
|
id: 202,
|
||||||
pid: 4,
|
pid: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'multi4',
|
name: 'multi4',
|
||||||
path: '/multi/multi3/multi4',
|
path: '/multi/multi-3/multi-4',
|
||||||
title: '多级菜单3-1',
|
title: '多级菜单3-1',
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'icon-park-outline:list',
|
icon: 'icon-park-outline:list',
|
||||||
componentPath: '/demo/multi/multi3/multi4/index.vue',
|
componentPath: '/demo/multi/multi-3/multi-4/index.vue',
|
||||||
id: 9,
|
id: 20201,
|
||||||
pid: 8,
|
pid: 202,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'list',
|
name: 'list',
|
||||||
@ -97,28 +97,38 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
icon: 'icon-park-outline:list-two',
|
icon: 'icon-park-outline:list-two',
|
||||||
menuType: 'dir',
|
menuType: 'dir',
|
||||||
componentPath: null,
|
componentPath: null,
|
||||||
id: 10,
|
id: 3,
|
||||||
pid: null,
|
pid: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'commonList',
|
name: 'commonList',
|
||||||
path: '/list/commonList',
|
path: '/list/common-list',
|
||||||
title: '常用列表',
|
title: '常用列表',
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'icon-park-outline:list-view',
|
icon: 'icon-park-outline:list-view',
|
||||||
componentPath: '/demo/list/commonList/index.vue',
|
componentPath: '/demo/list/common-list/index.vue',
|
||||||
id: 11,
|
id: 301,
|
||||||
pid: 10,
|
pid: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cardList',
|
name: 'cardList',
|
||||||
path: '/list/cardList',
|
path: '/list/card-list',
|
||||||
title: '卡片列表',
|
title: '卡片列表',
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'icon-park-outline:view-grid-list',
|
icon: 'icon-park-outline:view-grid-list',
|
||||||
componentPath: '/demo/list/cardList/index.vue',
|
componentPath: '/demo/list/card-list/index.vue',
|
||||||
id: 12,
|
id: 302,
|
||||||
pid: 10,
|
pid: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'draggableList',
|
||||||
|
path: '/list/draggable-list',
|
||||||
|
title: '拖拽列表',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'icon-park-outline:menu-fold',
|
||||||
|
componentPath: '/demo/list/draggable-list/index.vue',
|
||||||
|
id: 303,
|
||||||
|
pid: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'demo',
|
name: 'demo',
|
||||||
@ -128,7 +138,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
icon: 'icon-park-outline:application-one',
|
icon: 'icon-park-outline:application-one',
|
||||||
menuType: 'dir',
|
menuType: 'dir',
|
||||||
componentPath: null,
|
componentPath: null,
|
||||||
id: 13,
|
id: 4,
|
||||||
pid: null,
|
pid: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -138,8 +148,8 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'icon-park-outline:international',
|
icon: 'icon-park-outline:international',
|
||||||
componentPath: '/demo/fetch/index.vue',
|
componentPath: '/demo/fetch/index.vue',
|
||||||
id: 5,
|
id: 401,
|
||||||
pid: 13,
|
pid: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'echarts',
|
name: 'echarts',
|
||||||
@ -148,8 +158,8 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'icon-park-outline:chart-proportion',
|
icon: 'icon-park-outline:chart-proportion',
|
||||||
componentPath: '/demo/echarts/index.vue',
|
componentPath: '/demo/echarts/index.vue',
|
||||||
id: 15,
|
id: 402,
|
||||||
pid: 13,
|
pid: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'map',
|
name: 'map',
|
||||||
@ -159,8 +169,8 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
icon: 'carbon:map',
|
icon: 'carbon:map',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
componentPath: '/demo/map/index.vue',
|
componentPath: '/demo/map/index.vue',
|
||||||
id: 17,
|
id: 403,
|
||||||
pid: 13,
|
pid: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'editor',
|
name: 'editor',
|
||||||
@ -170,8 +180,8 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
icon: 'icon-park-outline:editor',
|
icon: 'icon-park-outline:editor',
|
||||||
menuType: 'dir',
|
menuType: 'dir',
|
||||||
componentPath: null,
|
componentPath: null,
|
||||||
id: 18,
|
id: 404,
|
||||||
pid: 13,
|
pid: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'editorMd',
|
name: 'editorMd',
|
||||||
@ -180,8 +190,8 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'ri:markdown-line',
|
icon: 'ri:markdown-line',
|
||||||
componentPath: '/demo/editor/md/index.vue',
|
componentPath: '/demo/editor/md/index.vue',
|
||||||
id: 19,
|
id: 40401,
|
||||||
pid: 18,
|
pid: 404,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'editorRich',
|
name: 'editorRich',
|
||||||
@ -190,8 +200,8 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'icon-park-outline:edit-one',
|
icon: 'icon-park-outline:edit-one',
|
||||||
componentPath: '/demo/editor/rich/index.vue',
|
componentPath: '/demo/editor/rich/index.vue',
|
||||||
id: 20,
|
id: 40402,
|
||||||
pid: 18,
|
pid: 404,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'clipboard',
|
name: 'clipboard',
|
||||||
@ -200,8 +210,8 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'icon-park-outline:clipboard',
|
icon: 'icon-park-outline:clipboard',
|
||||||
componentPath: '/demo/clipboard/index.vue',
|
componentPath: '/demo/clipboard/index.vue',
|
||||||
id: 21,
|
id: 405,
|
||||||
pid: 13,
|
pid: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'icons',
|
name: 'icons',
|
||||||
@ -210,18 +220,38 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'local:cool',
|
icon: 'local:cool',
|
||||||
componentPath: '/demo/icons/index.vue',
|
componentPath: '/demo/icons/index.vue',
|
||||||
id: 22,
|
id: 406,
|
||||||
pid: 13,
|
pid: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'QRCode',
|
name: 'QRCode',
|
||||||
path: '/demo/QRCode',
|
path: '/demo/qr-code',
|
||||||
title: '二维码',
|
title: '二维码',
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'icon-park-outline:two-dimensional-code',
|
icon: 'icon-park-outline:two-dimensional-code',
|
||||||
componentPath: '/demo/QRCode/index.vue',
|
componentPath: '/demo/qr-code/index.vue',
|
||||||
id: 23,
|
id: 407,
|
||||||
pid: 13,
|
pid: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cascader',
|
||||||
|
path: '/demo/cascader',
|
||||||
|
title: '省市区联动',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'icon-park-outline:add-subset',
|
||||||
|
componentPath: '/demo/cascader/index.vue',
|
||||||
|
id: 408,
|
||||||
|
pid: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dict',
|
||||||
|
path: '/demo/dict',
|
||||||
|
title: '字典示例',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'icon-park-outline:book-one',
|
||||||
|
componentPath: '/demo/dict/index.vue',
|
||||||
|
id: 409,
|
||||||
|
pid: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'documents',
|
name: 'documents',
|
||||||
@ -231,7 +261,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
icon: 'icon-park-outline:file-doc',
|
icon: 'icon-park-outline:file-doc',
|
||||||
menuType: 'dir',
|
menuType: 'dir',
|
||||||
componentPath: null,
|
componentPath: null,
|
||||||
id: 24,
|
id: 5,
|
||||||
pid: null,
|
pid: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -241,8 +271,8 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'logos:vue',
|
icon: 'logos:vue',
|
||||||
componentPath: '/demo/documents/vue/index.vue',
|
componentPath: '/demo/documents/vue/index.vue',
|
||||||
id: 25,
|
id: 501,
|
||||||
pid: 24,
|
pid: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'documentsVite',
|
name: 'documentsVite',
|
||||||
@ -251,169 +281,21 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'logos:vitejs',
|
icon: 'logos:vitejs',
|
||||||
componentPath: '/demo/documents/vite/index.vue',
|
componentPath: '/demo/documents/vite/index.vue',
|
||||||
id: 26,
|
id: 502,
|
||||||
pid: 24,
|
pid: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'documentsVueuse',
|
name: 'documentsVueuse',
|
||||||
path: '/documents/vueuse',
|
path: '/documents/vue-use',
|
||||||
title: 'VueUse(外链)',
|
title: 'VueUse(外链)',
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
icon: 'logos:vueuse',
|
icon: 'logos:vueuse',
|
||||||
href: 'https://vueuse.org/guide/',
|
href: 'https://vueuse.org/guide/',
|
||||||
componentPath: 'null',
|
componentPath: 'null',
|
||||||
id: 27,
|
id: 503,
|
||||||
pid: 24,
|
pid: 5,
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'permission',
|
|
||||||
path: '/permission',
|
|
||||||
title: '权限',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'icon-park-outline:people-safe',
|
|
||||||
menuType: 'dir',
|
|
||||||
componentPath: null,
|
|
||||||
id: 28,
|
|
||||||
pid: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'permissionDemo',
|
|
||||||
path: '/permission/permission',
|
|
||||||
title: '权限示例',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'icon-park-outline:right-user',
|
|
||||||
componentPath: '/demo/permission/permission/index.vue',
|
|
||||||
id: 29,
|
|
||||||
pid: 28,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'justSuper',
|
|
||||||
path: '/permission/justSuper',
|
|
||||||
title: 'super可见',
|
|
||||||
requiresAuth: true,
|
|
||||||
roles: [
|
|
||||||
'super',
|
|
||||||
],
|
|
||||||
icon: 'icon-park-outline:wrong-user',
|
|
||||||
componentPath: '/demo/permission/justSuper/index.vue',
|
|
||||||
id: 30,
|
|
||||||
pid: 28,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'error',
|
|
||||||
path: '/error',
|
|
||||||
title: '异常页',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'icon-park-outline:error-computer',
|
|
||||||
menuType: 'dir',
|
|
||||||
componentPath: null,
|
|
||||||
id: 31,
|
|
||||||
pid: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'demo403',
|
|
||||||
path: '/error/403',
|
|
||||||
title: '403',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'carbon:error',
|
|
||||||
order: 3,
|
|
||||||
componentPath: '/error/403/index.vue',
|
|
||||||
id: 32,
|
|
||||||
pid: 31,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'demo404',
|
|
||||||
path: '/error/404',
|
|
||||||
title: '404',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'icon-park-outline:error',
|
|
||||||
order: 2,
|
|
||||||
componentPath: '/error/404/index.vue',
|
|
||||||
id: 33,
|
|
||||||
pid: 31,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'demo500',
|
|
||||||
path: '/error/500',
|
|
||||||
title: '500',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'carbon:data-error',
|
|
||||||
order: 1,
|
|
||||||
componentPath: '/error/500/index.vue',
|
|
||||||
id: 34,
|
|
||||||
pid: 31,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'setting',
|
|
||||||
path: '/setting',
|
|
||||||
title: '系统设置',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'icon-park-outline:setting',
|
|
||||||
menuType: 'dir',
|
|
||||||
componentPath: null,
|
|
||||||
id: 35,
|
|
||||||
pid: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'accountSetting',
|
|
||||||
path: '/setting/account',
|
|
||||||
title: '用户设置',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'icon-park-outline:every-user',
|
|
||||||
componentPath: '/setting/account/index.vue',
|
|
||||||
id: 36,
|
|
||||||
pid: 35,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'dictionarySetting',
|
|
||||||
path: '/setting/dictionary',
|
|
||||||
title: '字典设置',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'icon-park-outline:book-one',
|
|
||||||
componentPath: '/setting/dictionary/index.vue',
|
|
||||||
id: 37,
|
|
||||||
pid: 35,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'menuSetting',
|
|
||||||
path: '/setting/menu',
|
|
||||||
title: '菜单设置',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'icon-park-outline:application-menu',
|
|
||||||
componentPath: '/setting/menu/index.vue',
|
|
||||||
id: 38,
|
|
||||||
pid: 35,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'userCenter',
|
|
||||||
path: '/userCenter',
|
|
||||||
title: '个人中心',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'carbon:user-avatar-filled-alt',
|
|
||||||
componentPath: '/demo/userCenter/index.vue',
|
|
||||||
id: 39,
|
|
||||||
pid: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'about',
|
|
||||||
path: '/about',
|
|
||||||
title: '关于',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'icon-park-outline:info',
|
|
||||||
componentPath: '/demo/about/index.vue',
|
|
||||||
id: 40,
|
|
||||||
pid: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'cascader',
|
|
||||||
path: '/demo/cascader',
|
|
||||||
title: '省市区联动',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'icon-park-outline:add-subset',
|
|
||||||
componentPath: '/demo/cascader/index.vue',
|
|
||||||
id: 41,
|
|
||||||
pid: 13,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'documentsNova',
|
name: 'documentsNova',
|
||||||
path: '/documents/nova',
|
path: '/documents/nova',
|
||||||
@ -422,28 +304,8 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
icon: 'local:logo',
|
icon: 'local:logo',
|
||||||
href: 'https://nova-admin-docs.netlify.app/',
|
href: 'https://nova-admin-docs.netlify.app/',
|
||||||
componentPath: '2333333',
|
componentPath: '2333333',
|
||||||
id: 42,
|
id: 504,
|
||||||
pid: 24,
|
pid: 5,
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'dict',
|
|
||||||
path: '/demo/dict',
|
|
||||||
title: '字典示例',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'icon-park-outline:book-one',
|
|
||||||
componentPath: '/demo/dict/index.vue',
|
|
||||||
id: 43,
|
|
||||||
pid: 13,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'draggableList',
|
|
||||||
path: '/list/draggableList',
|
|
||||||
title: '拖拽列表',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'icon-park-outline:menu-fold',
|
|
||||||
componentPath: '/demo/list/draggableList/index.vue',
|
|
||||||
id: 44,
|
|
||||||
pid: 10,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'documentsPublic',
|
name: 'documentsPublic',
|
||||||
@ -453,7 +315,104 @@ export const staticRoutes: AppRoute.RowRoute[] = [
|
|||||||
icon: 'local:logo',
|
icon: 'local:logo',
|
||||||
href: '/public',
|
href: '/public',
|
||||||
componentPath: 'null',
|
componentPath: 'null',
|
||||||
id: 45,
|
id: 505,
|
||||||
pid: 24,
|
pid: 5,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'permission',
|
||||||
|
path: '/permission',
|
||||||
|
title: '权限',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'icon-park-outline:people-safe',
|
||||||
|
menuType: 'dir',
|
||||||
|
componentPath: null,
|
||||||
|
id: 6,
|
||||||
|
pid: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'permissionDemo',
|
||||||
|
path: '/permission/permission',
|
||||||
|
title: '权限示例',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'icon-park-outline:right-user',
|
||||||
|
componentPath: '/demo/permission/permission/index.vue',
|
||||||
|
id: 601,
|
||||||
|
pid: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'justSuper',
|
||||||
|
path: '/permission/just-super',
|
||||||
|
title: 'super可见',
|
||||||
|
requiresAuth: true,
|
||||||
|
roles: [
|
||||||
|
'super',
|
||||||
|
],
|
||||||
|
icon: 'icon-park-outline:wrong-user',
|
||||||
|
componentPath: '/demo/permission/just-super/index.vue',
|
||||||
|
id: 602,
|
||||||
|
pid: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'setting',
|
||||||
|
path: '/setting',
|
||||||
|
title: '系统设置',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'icon-park-outline:setting',
|
||||||
|
menuType: 'dir',
|
||||||
|
componentPath: null,
|
||||||
|
id: 7,
|
||||||
|
pid: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accountSetting',
|
||||||
|
path: '/setting/account',
|
||||||
|
title: '用户设置',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'icon-park-outline:every-user',
|
||||||
|
componentPath: '/setting/account/index.vue',
|
||||||
|
id: 701,
|
||||||
|
pid: 7,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dictionarySetting',
|
||||||
|
path: '/setting/dictionary',
|
||||||
|
title: '字典设置',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'icon-park-outline:book-one',
|
||||||
|
componentPath: '/setting/dictionary/index.vue',
|
||||||
|
id: 702,
|
||||||
|
pid: 7,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'menuSetting',
|
||||||
|
path: '/setting/menu',
|
||||||
|
title: '菜单设置',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'icon-park-outline:application-menu',
|
||||||
|
componentPath: '/setting/menu/index.vue',
|
||||||
|
id: 703,
|
||||||
|
pid: 7,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'about',
|
||||||
|
path: '/about',
|
||||||
|
title: '关于',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'icon-park-outline:info',
|
||||||
|
componentPath: '/demo/about/index.vue',
|
||||||
|
id: 8,
|
||||||
|
pid: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'userCenter',
|
||||||
|
path: '/user-center',
|
||||||
|
title: '个人中心',
|
||||||
|
requiresAuth: true,
|
||||||
|
hide: true,
|
||||||
|
icon: 'carbon:user-avatar-filled-alt',
|
||||||
|
componentPath: '/build-in/user-center/index.vue',
|
||||||
|
id: 999,
|
||||||
|
pid: null,
|
||||||
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -19,9 +19,11 @@ const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthenticati
|
|||||||
// 服务端判定token过期
|
// 服务端判定token过期
|
||||||
refreshTokenOnSuccess: {
|
refreshTokenOnSuccess: {
|
||||||
// 当服务端返回401时,表示token过期
|
// 当服务端返回401时,表示token过期
|
||||||
isExpired: (response, method) => {
|
isExpired: async (response, method) => {
|
||||||
|
const res = await response.clone().json()
|
||||||
|
|
||||||
const isExpired = method.meta && method.meta.isExpired
|
const isExpired = method.meta && method.meta.isExpired
|
||||||
return response.status === 401 && !isExpired
|
return (response.status === 401 || res.code === 401) && !isExpired
|
||||||
},
|
},
|
||||||
|
|
||||||
// 当token过期时触发,在此函数中触发刷新token
|
// 当token过期时触发,在此函数中触发刷新token
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { createAlovaInstance } from './alova'
|
import { createAlovaInstance } from './alova'
|
||||||
|
|
||||||
export const request = createAlovaInstance({
|
export const request = createAlovaInstance({
|
||||||
baseURL: __PROXY_MAPPING__.url.path,
|
baseURL: __URL_MAP__.url.path,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const blankInstance = createAlovaInstance({
|
export const blankInstance = createAlovaInstance({
|
||||||
|
@ -17,6 +17,8 @@ const { system, store } = useColorMode({
|
|||||||
emitAuto: true,
|
emitAuto: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const isMobile = useMediaQuery('(max-width: 700px)')
|
||||||
|
|
||||||
export const useAppStore = defineStore('app-store', {
|
export const useAppStore = defineStore('app-store', {
|
||||||
state: () => {
|
state: () => {
|
||||||
return {
|
return {
|
||||||
@ -38,7 +40,6 @@ export const useAppStore = defineStore('app-store', {
|
|||||||
showSetting: false,
|
showSetting: false,
|
||||||
transitionAnimation: 'fade-slide' as TransitionAnimation,
|
transitionAnimation: 'fade-slide' as TransitionAnimation,
|
||||||
layoutMode: 'vertical' as ProLayoutMode,
|
layoutMode: 'vertical' as ProLayoutMode,
|
||||||
contentFullScreen: false,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
@ -51,6 +52,9 @@ export const useAppStore = defineStore('app-store', {
|
|||||||
fullScreen() {
|
fullScreen() {
|
||||||
return isFullscreen.value
|
return isFullscreen.value
|
||||||
},
|
},
|
||||||
|
isMobile() {
|
||||||
|
return isMobile.value
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
// 重置所有设置
|
// 重置所有设置
|
||||||
@ -68,8 +72,7 @@ export const useAppStore = defineStore('app-store', {
|
|||||||
this.showBreadcrumbIcon = true
|
this.showBreadcrumbIcon = true
|
||||||
this.showWatermark = false
|
this.showWatermark = false
|
||||||
this.transitionAnimation = 'fade-slide'
|
this.transitionAnimation = 'fade-slide'
|
||||||
this.layoutMode = 'leftMenu'
|
this.layoutMode = 'vertical'
|
||||||
this.contentFullScreen = false
|
|
||||||
|
|
||||||
// 重置所有配色
|
// 重置所有配色
|
||||||
this.setPrimaryColor(this.primaryColor)
|
this.setPrimaryColor(this.primaryColor)
|
||||||
|
@ -39,23 +39,22 @@ export const useRouteStore = defineStore('route-store', {
|
|||||||
|
|
||||||
async initRouteInfo() {
|
async initRouteInfo() {
|
||||||
if (import.meta.env.VITE_ROUTE_LOAD_MODE === 'dynamic') {
|
if (import.meta.env.VITE_ROUTE_LOAD_MODE === 'dynamic') {
|
||||||
const userInfo = local.get('userInfo')
|
try {
|
||||||
|
|
||||||
if (!userInfo || !userInfo.id) {
|
|
||||||
const authStore = useAuthStore()
|
|
||||||
authStore.logout()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user's route
|
// Get user's route
|
||||||
const { data } = await fetchUserRoutes({
|
const result = await fetchUserRoutes({
|
||||||
id: userInfo.id,
|
id: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!data)
|
if (!result.isSuccess || !result.data) {
|
||||||
return
|
throw new Error('Failed to fetch user routes')
|
||||||
|
}
|
||||||
|
|
||||||
return data
|
return result.data
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('Failed to initialize route info:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.rowRoutes = staticRoutes
|
this.rowRoutes = staticRoutes
|
||||||
@ -65,11 +64,13 @@ export const useRouteStore = defineStore('route-store', {
|
|||||||
async initAuthRoute() {
|
async initAuthRoute() {
|
||||||
this.isInitAuthRoute = false
|
this.isInitAuthRoute = false
|
||||||
|
|
||||||
|
try {
|
||||||
// Initialize route information
|
// Initialize route information
|
||||||
const rowRoutes = await this.initRouteInfo()
|
const rowRoutes = await this.initRouteInfo()
|
||||||
if (!rowRoutes) {
|
if (!rowRoutes) {
|
||||||
|
const error = new Error('Failed to get route information')
|
||||||
window.$message.error($t(`app.getRouteError`))
|
window.$message.error($t(`app.getRouteError`))
|
||||||
return
|
throw error
|
||||||
}
|
}
|
||||||
this.rowRoutes = rowRoutes
|
this.rowRoutes = rowRoutes
|
||||||
|
|
||||||
@ -84,6 +85,12 @@ export const useRouteStore = defineStore('route-store', {
|
|||||||
this.cacheRoutes = generateCacheRoutes(rowRoutes)
|
this.cacheRoutes = generateCacheRoutes(rowRoutes)
|
||||||
|
|
||||||
this.isInitAuthRoute = true
|
this.isInitAuthRoute = true
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
// 重置状态并重新抛出错误
|
||||||
|
this.isInitAuthRoute = false
|
||||||
|
throw error
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
3
src/typings/env.d.ts
vendored
3
src/typings/env.d.ts
vendored
@ -1,10 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
*后台服务的环境类型
|
*后台服务的环境类型
|
||||||
* - dev: 后台开发环境
|
* - dev: 后台开发环境
|
||||||
* - test: 后台测试环境
|
|
||||||
* - prod: 后台生产环境
|
* - prod: 后台生产环境
|
||||||
*/
|
*/
|
||||||
type ServiceEnvType = 'dev' | 'test' | 'prod'
|
type ServiceEnvType = 'dev' | 'production'
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
/** 项目基本地址 */
|
/** 项目基本地址 */
|
||||||
|
@ -18,7 +18,7 @@ const appName = import.meta.env.VITE_APP_NAME
|
|||||||
<DarkModeSwitch />
|
<DarkModeSwitch />
|
||||||
<LangsSwitch />
|
<LangsSwitch />
|
||||||
</div>
|
</div>
|
||||||
<n-el
|
<div
|
||||||
class="p-4xl h-full w-full sm:w-450px sm:h-unset"
|
class="p-4xl h-full w-full sm:w-450px sm:h-unset"
|
||||||
style="background: var(--card-color);box-shadow: var(--box-shadow-1);"
|
style="background: var(--card-color);box-shadow: var(--box-shadow-1);"
|
||||||
>
|
>
|
||||||
@ -36,7 +36,7 @@ const appName = import.meta.env.VITE_APP_NAME
|
|||||||
/>
|
/>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</n-el>
|
</div>
|
||||||
|
|
||||||
<div />
|
<div />
|
||||||
</n-el>
|
</n-el>
|
19
src/views/build-in/not-found/index.vue
Normal file
19
src/views/build-in/not-found/index.vue
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const router = useRouter()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex-col-center h-full">
|
||||||
|
<img
|
||||||
|
src="@/assets/svg/error-not-found.svg"
|
||||||
|
alt=""
|
||||||
|
class="w-1/3"
|
||||||
|
>
|
||||||
|
<n-button
|
||||||
|
type="primary"
|
||||||
|
@click="router.push('/')"
|
||||||
|
>
|
||||||
|
{{ $t('app.backHome') }}
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -1,8 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useAppStore } from '@/store'
|
||||||
import Chart from './components/chart.vue'
|
import Chart from './components/chart.vue'
|
||||||
import Chart2 from './components/chart2.vue'
|
import Chart2 from './components/chart2.vue'
|
||||||
import Chart3 from './components/chart3.vue'
|
import Chart3 from './components/chart3.vue'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const tableData = [
|
const tableData = [
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
@ -32,12 +35,15 @@ const tableData = [
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
|
||||||
<n-grid
|
<n-grid
|
||||||
:x-gap="16"
|
:x-gap="16"
|
||||||
:y-gap="16"
|
:y-gap="16"
|
||||||
|
:cols="12"
|
||||||
|
item-responsive
|
||||||
|
responsive="screen"
|
||||||
>
|
>
|
||||||
<n-gi :span="6">
|
<!-- 统计卡片 - 移动端每行2个,桌面端每行4个 -->
|
||||||
|
<n-gi span="6 m:3">
|
||||||
<n-card>
|
<n-card>
|
||||||
<n-space
|
<n-space
|
||||||
justify="space-between"
|
justify="space-between"
|
||||||
@ -69,7 +75,7 @@ const tableData = [
|
|||||||
</template>
|
</template>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="6">
|
<n-gi span="6 m:3">
|
||||||
<n-card>
|
<n-card>
|
||||||
<n-space
|
<n-space
|
||||||
justify="space-between"
|
justify="space-between"
|
||||||
@ -101,7 +107,7 @@ const tableData = [
|
|||||||
</template>
|
</template>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="6">
|
<n-gi span="6 m:3">
|
||||||
<n-card>
|
<n-card>
|
||||||
<n-space
|
<n-space
|
||||||
justify="space-between"
|
justify="space-between"
|
||||||
@ -133,7 +139,7 @@ const tableData = [
|
|||||||
</template>
|
</template>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="6">
|
<n-gi span="6 m:3">
|
||||||
<n-card>
|
<n-card>
|
||||||
<n-space
|
<n-space
|
||||||
justify="space-between"
|
justify="space-between"
|
||||||
@ -165,7 +171,8 @@ const tableData = [
|
|||||||
</template>
|
</template>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="24">
|
<!-- 图表区域 - 全宽显示 -->
|
||||||
|
<n-gi :span="12">
|
||||||
<n-card content-style="padding: 0;">
|
<n-card content-style="padding: 0;">
|
||||||
<n-tabs
|
<n-tabs
|
||||||
type="line"
|
type="line"
|
||||||
@ -182,7 +189,9 @@ const tableData = [
|
|||||||
</n-tabs>
|
</n-tabs>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="8">
|
|
||||||
|
<!-- 访问来源 - 移动端全宽,桌面端1/3宽 -->
|
||||||
|
<n-gi span="12 m:4">
|
||||||
<n-card
|
<n-card
|
||||||
title="访问来源"
|
title="访问来源"
|
||||||
:segmented="{
|
:segmented="{
|
||||||
@ -192,7 +201,9 @@ const tableData = [
|
|||||||
<Chart3 />
|
<Chart3 />
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="16">
|
|
||||||
|
<!-- 成交记录 - 移动端全宽,桌面端2/3宽 -->
|
||||||
|
<n-gi span="12 m:8">
|
||||||
<n-card
|
<n-card
|
||||||
title="成交记录"
|
title="成交记录"
|
||||||
:segmented="{
|
:segmented="{
|
||||||
@ -210,6 +221,7 @@ const tableData = [
|
|||||||
<n-table
|
<n-table
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
:single-line="false"
|
:single-line="false"
|
||||||
|
:scroll-x="appStore.isMobile ? 600 : undefined"
|
||||||
>
|
>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -243,7 +255,4 @@ const tableData = [
|
|||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
</n-grid>
|
</n-grid>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
@ -9,21 +9,30 @@ const { userInfo } = useAuthStore()
|
|||||||
<n-grid
|
<n-grid
|
||||||
:x-gap="16"
|
:x-gap="16"
|
||||||
:y-gap="16"
|
:y-gap="16"
|
||||||
|
:cols="3"
|
||||||
|
item-responsive
|
||||||
|
responsive="screen"
|
||||||
>
|
>
|
||||||
<n-gi :span="16">
|
<!-- 左侧主要内容区 - 移动端全宽,桌面端2/3宽 -->
|
||||||
|
<n-gi span="3 m:2">
|
||||||
<n-space
|
<n-space
|
||||||
vertical
|
vertical
|
||||||
:size="16"
|
:size="16"
|
||||||
>
|
>
|
||||||
|
<!-- 图表区域 -->
|
||||||
<n-card style="--n-padding-left: 0;">
|
<n-card style="--n-padding-left: 0;">
|
||||||
<Chart />
|
<Chart />
|
||||||
</n-card>
|
</n-card>
|
||||||
<n-card>
|
|
||||||
|
<!-- 统计卡片区域 -->
|
||||||
<n-grid
|
<n-grid
|
||||||
:x-gap="8"
|
:x-gap="16"
|
||||||
:y-gap="8"
|
:y-gap="16"
|
||||||
|
:cols="4"
|
||||||
|
item-responsive
|
||||||
|
responsive="screen"
|
||||||
>
|
>
|
||||||
<n-gi :span="6">
|
<n-gi span="2 l:1">
|
||||||
<n-card>
|
<n-card>
|
||||||
<n-thing>
|
<n-thing>
|
||||||
<template #avatar>
|
<template #avatar>
|
||||||
@ -41,7 +50,7 @@ const { userInfo } = useAuthStore()
|
|||||||
</n-thing>
|
</n-thing>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="6">
|
<n-gi span="2 l:1">
|
||||||
<n-card>
|
<n-card>
|
||||||
<n-thing>
|
<n-thing>
|
||||||
<template #avatar>
|
<template #avatar>
|
||||||
@ -59,7 +68,7 @@ const { userInfo } = useAuthStore()
|
|||||||
</n-thing>
|
</n-thing>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="6">
|
<n-gi span="2 l:1">
|
||||||
<n-card>
|
<n-card>
|
||||||
<n-thing>
|
<n-thing>
|
||||||
<template #avatar>
|
<template #avatar>
|
||||||
@ -77,7 +86,7 @@ const { userInfo } = useAuthStore()
|
|||||||
</n-thing>
|
</n-thing>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="6">
|
<n-gi span="2 l:1">
|
||||||
<n-card>
|
<n-card>
|
||||||
<n-thing>
|
<n-thing>
|
||||||
<template #avatar>
|
<template #avatar>
|
||||||
@ -96,7 +105,6 @@ const { userInfo } = useAuthStore()
|
|||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
</n-grid>
|
</n-grid>
|
||||||
</n-card>
|
|
||||||
<n-card title="动态">
|
<n-card title="动态">
|
||||||
<template #header-extra>
|
<template #header-extra>
|
||||||
<n-button
|
<n-button
|
||||||
@ -167,7 +175,9 @@ const { userInfo } = useAuthStore()
|
|||||||
</n-card>
|
</n-card>
|
||||||
</n-space>
|
</n-space>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="8">
|
|
||||||
|
<!-- 右侧边栏 - 移动端全宽,桌面端1/3宽 -->
|
||||||
|
<n-gi span="3 m:1">
|
||||||
<n-space
|
<n-space
|
||||||
vertical
|
vertical
|
||||||
:size="16"
|
:size="16"
|
||||||
@ -226,11 +236,14 @@ const { userInfo } = useAuthStore()
|
|||||||
</n-list-item>
|
</n-list-item>
|
||||||
</n-list>
|
</n-list>
|
||||||
</n-card>
|
</n-card>
|
||||||
|
<!-- 订单和待办统计 -->
|
||||||
<n-grid
|
<n-grid
|
||||||
:x-gap="8"
|
:x-gap="16"
|
||||||
:y-gap="8"
|
:y-gap="16"
|
||||||
|
:cols="2"
|
||||||
>
|
>
|
||||||
<n-gi :span="12">
|
<!-- 移动端和桌面端都是每行2个 -->
|
||||||
|
<n-gi :span="1">
|
||||||
<n-card>
|
<n-card>
|
||||||
<n-flex vertical align="center">
|
<n-flex vertical align="center">
|
||||||
<n-text depth="3">
|
<n-text depth="3">
|
||||||
@ -245,7 +258,7 @@ const { userInfo } = useAuthStore()
|
|||||||
</n-flex>
|
</n-flex>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi :span="12">
|
<n-gi :span="1">
|
||||||
<n-card>
|
<n-card>
|
||||||
<n-flex vertical align="center">
|
<n-flex vertical align="center">
|
||||||
<n-text depth="3">
|
<n-text depth="3">
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
<script setup lang="ts"></script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ErrorTip type="403" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
@ -1,7 +0,0 @@
|
|||||||
<script setup lang="ts"></script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ErrorTip type="404" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
@ -1,7 +0,0 @@
|
|||||||
<script setup lang="ts"></script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ErrorTip type="500" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
@ -1,10 +1,10 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import type { DataTableColumns } from 'naive-ui'
|
import type { DataTableColumns } from 'naive-ui'
|
||||||
import CopyText from '@/components/custom/CopyText.vue'
|
|
||||||
import { useBoolean } from '@/hooks'
|
import { useBoolean } from '@/hooks'
|
||||||
import { fetchAllRoutes } from '@/service'
|
import { fetchAllRoutes } from '@/service'
|
||||||
import { arrayToTree, createIcon } from '@/utils'
|
import { arrayToTree, createIcon } from '@/utils'
|
||||||
import { NButton, NPopconfirm, NSpace, NTag } from 'naive-ui'
|
import { NButton, NPopconfirm, NSpace, NTag } from 'naive-ui'
|
||||||
|
import { renderProCopyableText } from 'pro-naive-ui'
|
||||||
import TableModal from './components/TableModal.vue'
|
import TableModal from './components/TableModal.vue'
|
||||||
|
|
||||||
const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(false)
|
const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(false)
|
||||||
@ -45,11 +45,7 @@ const columns: DataTableColumns<AppRoute.RowRoute> = [
|
|||||||
{
|
{
|
||||||
title: '路径',
|
title: '路径',
|
||||||
key: 'path',
|
key: 'path',
|
||||||
render: (row) => {
|
render: row => renderProCopyableText(row.path),
|
||||||
return (
|
|
||||||
<CopyText value={row.path} />
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '组件路径',
|
title: '组件路径',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user