mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-11-24 17:06:47 +08:00
Compare commits
20 Commits
358e129765
...
v2.0-route
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
858d7625a4 | ||
|
|
d5a3a25d3d | ||
|
|
d567c057a8 | ||
|
|
91fae50260 | ||
|
|
61fa4b7f3b | ||
|
|
ef7acc626f | ||
|
|
2a0c9f1b41 | ||
|
|
5be864a80b | ||
|
|
b041fdd864 | ||
|
|
3d72f954ed | ||
|
|
1213531bef | ||
|
|
805c338141 | ||
|
|
3c0a52825d | ||
|
|
100e0ea55d | ||
|
|
257f1183fc | ||
|
|
d73111116a | ||
|
|
29a2a5c66a | ||
|
|
4005763c00 | ||
|
|
a55b4dc073 | ||
|
|
be8f915a0c |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -33,3 +33,5 @@ package-lock.json
|
|||||||
yarn.lock
|
yarn.lock
|
||||||
|
|
||||||
.VSCodeCounter
|
.VSCodeCounter
|
||||||
|
|
||||||
|
.temp
|
||||||
13
.vscode/settings.json
vendored
13
.vscode/settings.json
vendored
@@ -4,7 +4,18 @@
|
|||||||
"source.organizeImports": "never"
|
"source.organizeImports": "never"
|
||||||
},
|
},
|
||||||
"editor.formatOnSave": false,
|
"editor.formatOnSave": false,
|
||||||
"eslint.validate": ["html", "css", "scss", "json", "jsonc"],
|
"eslint.validate": [
|
||||||
|
"html",
|
||||||
|
"css",
|
||||||
|
"scss",
|
||||||
|
"json",
|
||||||
|
"jsonc",
|
||||||
|
"javascript",
|
||||||
|
"javascriptreact",
|
||||||
|
"typescript",
|
||||||
|
"typescriptreact",
|
||||||
|
"vue"
|
||||||
|
],
|
||||||
"i18n-ally.displayLanguage": "zh-cn",
|
"i18n-ally.displayLanguage": "zh-cn",
|
||||||
"i18n-ally.enabledParsers": ["ts"],
|
"i18n-ally.enabledParsers": ["ts"],
|
||||||
"i18n-ally.enabledFrameworks": ["vue"],
|
"i18n-ally.enabledFrameworks": ["vue"],
|
||||||
|
|||||||
@@ -13,8 +13,10 @@
|
|||||||
[](https://gitcode.com/soybeanjs/soybean-admin)
|
[](https://gitcode.com/soybeanjs/soybean-admin)
|
||||||
[](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
|
[](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
|
||||||
|
|
||||||
|
<div style="display: flex; gap: 12px; align-items: center;">
|
||||||
|
<a href="https://trendshift.io/repositories/7963" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7963" alt="soybeanjs%2Fsoybean-admin | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
<a href="https://hellogithub.com/repository/1298f27d5fe54959a16cf9686516ddb3" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=1298f27d5fe54959a16cf9686516ddb3&claim_uid=IiDXWmP4TEntjbV" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
<a href="https://hellogithub.com/repository/1298f27d5fe54959a16cf9686516ddb3" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=1298f27d5fe54959a16cf9686516ddb3&claim_uid=IiDXWmP4TEntjbV" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> If you think `SoybeanAdmin` is helpful to you, or you like our project, please give us a ⭐️ on GitHub. Your support is the driving force for us to continue to improve and add new features! Thank you for your support!
|
> If you think `SoybeanAdmin` is helpful to you, or you like our project, please give us a ⭐️ on GitHub. Your support is the driving force for us to continue to improve and add new features! Thank you for your support!
|
||||||
|
|||||||
@@ -13,7 +13,10 @@
|
|||||||
[](https://gitcode.com/soybeanjs/soybean-admin)
|
[](https://gitcode.com/soybeanjs/soybean-admin)
|
||||||
[](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
|
[](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
|
||||||
|
|
||||||
|
<div style="display: flex; gap: 12px; align-items: center;">
|
||||||
|
<a href="https://trendshift.io/repositories/7963" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7963" alt="soybeanjs%2Fsoybean-admin | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
<a href="https://hellogithub.com/repository/1298f27d5fe54959a16cf9686516ddb3" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=1298f27d5fe54959a16cf9686516ddb3&claim_uid=IiDXWmP4TEntjbV" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
<a href="https://hellogithub.com/repository/1298f27d5fe54959a16cf9686516ddb3" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=1298f27d5fe54959a16cf9686516ddb3&claim_uid=IiDXWmP4TEntjbV" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> 如果您觉得 `SoybeanAdmin`对您有所帮助,或者您喜欢我们的项目,请在 GitHub 上给我们一个 ⭐️。您的支持是我们持续改进和增加新功能的动力!感谢您的支持!
|
> 如果您觉得 `SoybeanAdmin`对您有所帮助,或者您喜欢我们的项目,请在 GitHub 上给我们一个 ⭐️。您的支持是我们持续改进和增加新功能的动力!感谢您的支持!
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { HttpProxy, ProxyOptions } from 'vite';
|
import type { ProxyOptions } from 'vite';
|
||||||
import { bgRed, bgYellow, green, lightBlue } from 'kolorist';
|
import { bgRed, bgYellow, green, lightBlue } from 'kolorist';
|
||||||
import { consola } from 'consola';
|
import { consola } from 'consola';
|
||||||
import { createServiceConfig } from '../../src/utils/service';
|
import { createServiceConfig } from '../../src/utils/service';
|
||||||
@@ -33,7 +33,7 @@ function createProxyItem(item: App.Service.ServiceConfigItem, enableLog: boolean
|
|||||||
proxy[item.proxyPattern] = {
|
proxy[item.proxyPattern] = {
|
||||||
target: item.baseURL,
|
target: item.baseURL,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
configure: (_proxy: HttpProxy.Server, options: ProxyOptions) => {
|
configure: (_proxy, options) => {
|
||||||
_proxy.on('proxyReq', (_proxyReq, req, _res) => {
|
_proxy.on('proxyReq', (_proxyReq, req, _res) => {
|
||||||
if (!enableLog) return;
|
if (!enableLog) return;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { PluginOption } from 'vite';
|
|||||||
import vue from '@vitejs/plugin-vue';
|
import vue from '@vitejs/plugin-vue';
|
||||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||||
import progress from 'vite-plugin-progress';
|
import progress from 'vite-plugin-progress';
|
||||||
import { setupElegantRouter } from './router';
|
import elegantRouter from 'elegant-router/vite';
|
||||||
import { setupUnocss } from './unocss';
|
import { setupUnocss } from './unocss';
|
||||||
import { setupUnplugin } from './unplugin';
|
import { setupUnplugin } from './unplugin';
|
||||||
import { setupHtmlPlugin } from './html';
|
import { setupHtmlPlugin } from './html';
|
||||||
@@ -13,7 +13,7 @@ export function setupVitePlugins(viteEnv: Env.ImportMeta, buildTime: string) {
|
|||||||
vue(),
|
vue(),
|
||||||
vueJsx(),
|
vueJsx(),
|
||||||
setupDevtoolsPlugin(viteEnv),
|
setupDevtoolsPlugin(viteEnv),
|
||||||
setupElegantRouter(),
|
elegantRouter(),
|
||||||
setupUnocss(viteEnv),
|
setupUnocss(viteEnv),
|
||||||
...setupUnplugin(viteEnv),
|
...setupUnplugin(viteEnv),
|
||||||
progress(),
|
progress(),
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
import type { RouteMeta } from 'vue-router';
|
|
||||||
import ElegantVueRouter from '@elegant-router/vue/vite';
|
|
||||||
import type { RouteKey } from '@elegant-router/types';
|
|
||||||
|
|
||||||
export function setupElegantRouter() {
|
|
||||||
return ElegantVueRouter({
|
|
||||||
layouts: {
|
|
||||||
base: 'src/layouts/base-layout/index.vue',
|
|
||||||
blank: 'src/layouts/blank-layout/index.vue'
|
|
||||||
},
|
|
||||||
routePathTransformer(routeName, routePath) {
|
|
||||||
const key = routeName as RouteKey;
|
|
||||||
|
|
||||||
if (key === 'login') {
|
|
||||||
const modules: UnionKey.LoginModule[] = ['pwd-login', 'code-login', 'register', 'reset-pwd', 'bind-wechat'];
|
|
||||||
|
|
||||||
const moduleReg = modules.join('|');
|
|
||||||
|
|
||||||
return `/login/:module(${moduleReg})?`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return routePath;
|
|
||||||
},
|
|
||||||
onRouteMetaGen(routeName) {
|
|
||||||
const key = routeName as RouteKey;
|
|
||||||
|
|
||||||
const constantRoutes: RouteKey[] = ['login', '403', '404', '500'];
|
|
||||||
|
|
||||||
const meta: Partial<RouteMeta> = {
|
|
||||||
title: key,
|
|
||||||
i18nKey: `route.${key}` as App.I18n.I18nKey
|
|
||||||
};
|
|
||||||
|
|
||||||
if (constantRoutes.includes(key)) {
|
|
||||||
meta.constant = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return meta;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
37
er.config.ts
Normal file
37
er.config.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import type { RouteMeta } from 'vue-router';
|
||||||
|
import { defineConfig } from 'elegant-router';
|
||||||
|
import type { RouteKey } from '@elegant-router/types';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
pageDir: ['src/views'],
|
||||||
|
layouts: {
|
||||||
|
base: 'src/layouts/base-layout/index.vue',
|
||||||
|
blank: 'src/layouts/blank-layout/index.vue'
|
||||||
|
},
|
||||||
|
getRoutePath: node => {
|
||||||
|
if (node.name === 'Login') {
|
||||||
|
const modules: UnionKey.LoginModule[] = ['pwd-login', 'code-login', 'register', 'reset-pwd', 'bind-wechat'];
|
||||||
|
|
||||||
|
const moduleReg = modules.join('|');
|
||||||
|
|
||||||
|
return `/login/:module(${moduleReg})?`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.path;
|
||||||
|
},
|
||||||
|
getRouteMeta: node => {
|
||||||
|
const constantRoutes: RouteKey[] = ['Login', '403', '404', '500'];
|
||||||
|
|
||||||
|
const name = node.name as RouteKey;
|
||||||
|
|
||||||
|
const meta: Partial<RouteMeta> = {
|
||||||
|
title: name
|
||||||
|
};
|
||||||
|
|
||||||
|
if (constantRoutes.includes(name)) {
|
||||||
|
meta.constant = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
});
|
||||||
55
package.json
55
package.json
@@ -54,53 +54,54 @@
|
|||||||
"@sa/hooks": "workspace:*",
|
"@sa/hooks": "workspace:*",
|
||||||
"@sa/materials": "workspace:*",
|
"@sa/materials": "workspace:*",
|
||||||
"@sa/utils": "workspace:*",
|
"@sa/utils": "workspace:*",
|
||||||
"@vueuse/core": "13.5.0",
|
"@vueuse/core": "13.9.0",
|
||||||
"clipboard": "2.0.11",
|
"clipboard": "2.0.11",
|
||||||
"dayjs": "1.11.13",
|
"dayjs": "1.11.18",
|
||||||
"defu": "6.1.4",
|
"defu": "6.1.4",
|
||||||
"echarts": "5.6.0",
|
"echarts": "6.0.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"naive-ui": "2.42.0",
|
"naive-ui": "2.43.1",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"pinia": "3.0.3",
|
"pinia": "3.0.3",
|
||||||
"tailwind-merge": "3.3.1",
|
"tailwind-merge": "3.3.1",
|
||||||
"vue": "3.5.17",
|
"vue": "3.5.22",
|
||||||
"vue-draggable-plus": "0.6.0",
|
"vue-draggable-plus": "0.6.0",
|
||||||
"vue-i18n": "11.1.10",
|
"vue-i18n": "11.1.12",
|
||||||
"vue-router": "4.5.1"
|
"vue-router": "4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@elegant-router/vue": "0.3.8",
|
"@elegant-router/vue": "0.3.8",
|
||||||
"@iconify/json": "2.2.359",
|
"@iconify/json": "2.2.395",
|
||||||
"@sa/scripts": "workspace:*",
|
"@sa/scripts": "workspace:*",
|
||||||
"@sa/uno-preset": "workspace:*",
|
"@sa/uno-preset": "workspace:*",
|
||||||
"@soybeanjs/eslint-config": "1.7.1",
|
"@soybeanjs/eslint-config": "1.7.1",
|
||||||
"@types/node": "24.0.15",
|
"@types/node": "24.7.2",
|
||||||
"@types/nprogress": "0.2.3",
|
"@types/nprogress": "0.2.3",
|
||||||
"@unocss/eslint-config": "66.3.3",
|
"@unocss/eslint-config": "66.5.3",
|
||||||
"@unocss/preset-icons": "66.3.3",
|
"@unocss/preset-icons": "66.5.3",
|
||||||
"@unocss/preset-uno": "66.3.3",
|
"@unocss/preset-uno": "66.5.3",
|
||||||
"@unocss/transformer-directives": "66.3.3",
|
"@unocss/transformer-directives": "66.5.3",
|
||||||
"@unocss/transformer-variant-group": "66.3.3",
|
"@unocss/transformer-variant-group": "66.5.3",
|
||||||
"@unocss/vite": "66.3.3",
|
"@unocss/vite": "66.5.3",
|
||||||
"@vitejs/plugin-vue": "6.0.0",
|
"@vitejs/plugin-vue": "6.0.1",
|
||||||
"@vitejs/plugin-vue-jsx": "5.0.1",
|
"@vitejs/plugin-vue-jsx": "5.1.1",
|
||||||
"consola": "3.4.2",
|
"consola": "3.4.2",
|
||||||
"eslint": "9.31.0",
|
"elegant-router": "1.0.4",
|
||||||
"eslint-plugin-vue": "10.3.0",
|
"eslint": "9.37.0",
|
||||||
|
"eslint-plugin-vue": "10.5.0",
|
||||||
"kolorist": "1.8.0",
|
"kolorist": "1.8.0",
|
||||||
"sass": "1.89.2",
|
"sass": "1.93.2",
|
||||||
"simple-git-hooks": "2.13.0",
|
"simple-git-hooks": "2.13.1",
|
||||||
"tsx": "4.20.3",
|
"tsx": "4.20.6",
|
||||||
"typescript": "5.8.3",
|
"typescript": "5.9.3",
|
||||||
"unplugin-icons": "22.1.0",
|
"unplugin-icons": "22.4.2",
|
||||||
"unplugin-vue-components": "28.8.0",
|
"unplugin-vue-components": "29.1.0",
|
||||||
"vite": "7.0.5",
|
"vite": "7.1.9",
|
||||||
"vite-plugin-progress": "0.0.7",
|
"vite-plugin-progress": "0.0.7",
|
||||||
"vite-plugin-svg-icons": "2.0.1",
|
"vite-plugin-svg-icons": "2.0.1",
|
||||||
"vite-plugin-vue-devtools": "7.7.7",
|
"vite-plugin-vue-devtools": "8.0.2",
|
||||||
"vue-eslint-parser": "10.2.0",
|
"vue-eslint-parser": "10.2.0",
|
||||||
"vue-tsc": "3.0.3"
|
"vue-tsc": "3.1.1"
|
||||||
},
|
},
|
||||||
"simple-git-hooks": {
|
"simple-git-hooks": {
|
||||||
"commit-msg": "pnpm sa git-commit-verify",
|
"commit-msg": "pnpm sa git-commit-verify",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sa/utils": "workspace:*",
|
"@sa/utils": "workspace:*",
|
||||||
"axios": "1.10.0",
|
"axios": "1.12.2",
|
||||||
"axios-retry": "4.5.0",
|
"axios-retry": "4.5.0",
|
||||||
"qs": "6.14.0"
|
"qs": "6.14.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { AxiosResponse, CreateAxiosDefaults, InternalAxiosRequestConfig } f
|
|||||||
import axiosRetry from 'axios-retry';
|
import axiosRetry from 'axios-retry';
|
||||||
import { nanoid } from '@sa/utils';
|
import { nanoid } from '@sa/utils';
|
||||||
import { createAxiosConfig, createDefaultOptions, createRetryOptions } from './options';
|
import { createAxiosConfig, createDefaultOptions, createRetryOptions } from './options';
|
||||||
|
import { transformResponse } from './shared';
|
||||||
import { BACKEND_ERROR_CODE, REQUEST_ID_KEY } from './constant';
|
import { BACKEND_ERROR_CODE, REQUEST_ID_KEY } from './constant';
|
||||||
import type {
|
import type {
|
||||||
CustomAxiosRequestConfig,
|
CustomAxiosRequestConfig,
|
||||||
@@ -53,6 +54,8 @@ function createCommonRequest<
|
|||||||
async response => {
|
async response => {
|
||||||
const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json';
|
const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json';
|
||||||
|
|
||||||
|
await transformResponse(response);
|
||||||
|
|
||||||
if (responseType !== 'json' || opts.isBackendSuccess(response)) {
|
if (responseType !== 'json' || opts.isBackendSuccess(response)) {
|
||||||
return Promise.resolve(response);
|
return Promise.resolve(response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { AxiosHeaderValue, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
import type { AxiosHeaderValue, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
||||||
|
import type { ResponseType } from './type';
|
||||||
|
|
||||||
export function getContentType(config: InternalAxiosRequestConfig) {
|
export function getContentType(config: InternalAxiosRequestConfig) {
|
||||||
const contentType: AxiosHeaderValue = config.headers?.['Content-Type'] || 'application/json';
|
const contentType: AxiosHeaderValue = config.headers?.['Content-Type'] || 'application/json';
|
||||||
@@ -26,3 +27,53 @@ export function isResponseJson(response: AxiosResponse) {
|
|||||||
|
|
||||||
return responseType === 'json' || responseType === undefined;
|
return responseType === 'json' || responseType === undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function transformResponse(response: AxiosResponse) {
|
||||||
|
const responseType: ResponseType = (response.config?.responseType as ResponseType) || 'json';
|
||||||
|
if (responseType === 'json') return;
|
||||||
|
|
||||||
|
const isJson = response.headers['content-type']?.includes('application/json');
|
||||||
|
if (!isJson) return;
|
||||||
|
|
||||||
|
if (responseType === 'blob') {
|
||||||
|
await transformBlobToJson(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responseType === 'arrayBuffer') {
|
||||||
|
await transformArrayBufferToJson(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function transformBlobToJson(response: AxiosResponse) {
|
||||||
|
try {
|
||||||
|
let data = response.data;
|
||||||
|
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.prototype.toString.call(data) === '[object Blob]') {
|
||||||
|
const json = await data.text();
|
||||||
|
data = JSON.parse(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
response.data = data;
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function transformArrayBufferToJson(response: AxiosResponse) {
|
||||||
|
try {
|
||||||
|
let data = response.data;
|
||||||
|
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.prototype.toString.call(data) === '[object ArrayBuffer]') {
|
||||||
|
const json = new TextDecoder().decode(data);
|
||||||
|
data = JSON.parse(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
response.data = data;
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|||||||
@@ -95,3 +95,27 @@
|
|||||||
.chrome-tab_dark .chrome-tab-divider {
|
.chrome-tab_dark .chrome-tab-divider {
|
||||||
background-color: rgba(255, 255, 255, 0.9);
|
background-color: rgba(255, 255, 255, 0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.slider-tab {
|
||||||
|
background-color: transparent;
|
||||||
|
height: 100%;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-tab_dark {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-tab:hover {
|
||||||
|
color: var(--soy-primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-tab_active {
|
||||||
|
color: var(--soy-primary-color);
|
||||||
|
background-color: var(--soy-primary-color-opacity1);
|
||||||
|
border-bottom-color: var(--soy-primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-tab_active_dark {
|
||||||
|
background-color: var(--soy-primary-color-opacity2);
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ declare const styles: {
|
|||||||
readonly 'chrome-tab_dark': string;
|
readonly 'chrome-tab_dark': string;
|
||||||
readonly 'chrome-tab-divider': string;
|
readonly 'chrome-tab-divider': string;
|
||||||
readonly 'svg-close': string;
|
readonly 'svg-close': string;
|
||||||
|
readonly 'slider-tab': string;
|
||||||
|
readonly 'slider-tab_active': string;
|
||||||
|
readonly 'slider-tab_active_dark': string;
|
||||||
|
readonly 'slider-tab_dark': string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default styles;
|
export default styles;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { PageTabMode, PageTabProps } from '../../types';
|
|||||||
import { ACTIVE_COLOR, createTabCssVars } from './shared';
|
import { ACTIVE_COLOR, createTabCssVars } from './shared';
|
||||||
import ChromeTab from './chrome-tab.vue';
|
import ChromeTab from './chrome-tab.vue';
|
||||||
import ButtonTab from './button-tab.vue';
|
import ButtonTab from './button-tab.vue';
|
||||||
|
import SliderTab from './slider-tab.vue';
|
||||||
import SvgClose from './svg-close.vue';
|
import SvgClose from './svg-close.vue';
|
||||||
import style from './index.module.css';
|
import style from './index.module.css';
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ interface Emits {
|
|||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
const activeTabComponent = computed(() => {
|
const activeTabComponent = computed(() => {
|
||||||
const { mode, chromeClass, buttonClass } = props;
|
const { mode, chromeClass, buttonClass, sliderClass } = props;
|
||||||
|
|
||||||
const tabComponentMap = {
|
const tabComponentMap = {
|
||||||
chrome: {
|
chrome: {
|
||||||
@@ -36,6 +37,10 @@ const activeTabComponent = computed(() => {
|
|||||||
button: {
|
button: {
|
||||||
component: ButtonTab,
|
component: ButtonTab,
|
||||||
class: buttonClass
|
class: buttonClass
|
||||||
|
},
|
||||||
|
slider: {
|
||||||
|
component: SliderTab,
|
||||||
|
class: sliderClass
|
||||||
}
|
}
|
||||||
} satisfies Record<PageTabMode, { component: Component; class?: string }>;
|
} satisfies Record<PageTabMode, { component: Component; class?: string }>;
|
||||||
|
|
||||||
@@ -45,7 +50,7 @@ const activeTabComponent = computed(() => {
|
|||||||
const cssVars = computed(() => createTabCssVars(props.activeColor));
|
const cssVars = computed(() => createTabCssVars(props.activeColor));
|
||||||
|
|
||||||
const bindProps = computed(() => {
|
const bindProps = computed(() => {
|
||||||
const { chromeClass: _chromeCls, buttonClass: _btnCls, ...rest } = props;
|
const { chromeClass: _chromeCls, buttonClass: _btnCls, sliderClass: _sliderCls, ...rest } = props;
|
||||||
|
|
||||||
return rest;
|
return rest;
|
||||||
});
|
});
|
||||||
|
|||||||
53
packages/materials/src/libs/page-tab/slider-tab.vue
Normal file
53
packages/materials/src/libs/page-tab/slider-tab.vue
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PageTabProps } from '../../types';
|
||||||
|
import style from './index.module.css';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'SliderTab'
|
||||||
|
});
|
||||||
|
|
||||||
|
defineProps<PageTabProps>();
|
||||||
|
|
||||||
|
type SlotFn = (props?: Record<string, unknown>) => any;
|
||||||
|
|
||||||
|
type Slots = {
|
||||||
|
/**
|
||||||
|
* Slot
|
||||||
|
*
|
||||||
|
* The center content of the tab
|
||||||
|
*/
|
||||||
|
default?: SlotFn;
|
||||||
|
/**
|
||||||
|
* Slot
|
||||||
|
*
|
||||||
|
* The left content of the tab
|
||||||
|
*/
|
||||||
|
prefix?: SlotFn;
|
||||||
|
/**
|
||||||
|
* Slot
|
||||||
|
*
|
||||||
|
* The right content of the tab
|
||||||
|
*/
|
||||||
|
suffix?: SlotFn;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineSlots<Slots>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class=":soy: relative inline-flex cursor-pointer items-center justify-center gap-6px whitespace-nowrap px-12px py-4px"
|
||||||
|
:class="[
|
||||||
|
style['slider-tab'],
|
||||||
|
{ [style['slider-tab_dark']]: darkMode },
|
||||||
|
{ [style['slider-tab_active']]: active },
|
||||||
|
{ [style['slider-tab_active_dark']]: active && darkMode }
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<slot name="prefix"></slot>
|
||||||
|
<slot></slot>
|
||||||
|
<slot name="suffix"></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -239,7 +239,7 @@ export type LayoutCssVars = {
|
|||||||
*
|
*
|
||||||
* @default chrome
|
* @default chrome
|
||||||
*/
|
*/
|
||||||
export type PageTabMode = 'button' | 'chrome';
|
export type PageTabMode = 'button' | 'chrome' | 'slider';
|
||||||
|
|
||||||
export interface PageTabProps {
|
export interface PageTabProps {
|
||||||
/** Whether is dark mode */
|
/** Whether is dark mode */
|
||||||
@@ -262,6 +262,8 @@ export interface PageTabProps {
|
|||||||
buttonClass?: string;
|
buttonClass?: string;
|
||||||
/** The class of the chrome tab */
|
/** The class of the chrome tab */
|
||||||
chromeClass?: string;
|
chromeClass?: string;
|
||||||
|
/** The class of the title tab */
|
||||||
|
sliderClass?: string;
|
||||||
/** Whether the tab is active */
|
/** Whether the tab is active */
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
/** The color of the active tab */
|
/** The color of the active tab */
|
||||||
|
|||||||
@@ -13,15 +13,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@soybeanjs/changelog": "0.3.24",
|
"@soybeanjs/changelog": "0.3.25",
|
||||||
"bumpp": "10.2.0",
|
"bumpp": "10.3.1",
|
||||||
"c12": "3.1.0",
|
"c12": "3.3.0",
|
||||||
"cac": "6.7.14",
|
"cac": "6.7.14",
|
||||||
"consola": "3.4.2",
|
"consola": "3.4.2",
|
||||||
"enquirer": "2.4.1",
|
"enquirer": "2.4.1",
|
||||||
"execa": "9.6.0",
|
"execa": "9.6.0",
|
||||||
"kolorist": "1.8.0",
|
"kolorist": "1.8.0",
|
||||||
"npm-check-updates": "18.0.1",
|
"npm-check-updates": "19.0.0",
|
||||||
|
"picomatch": "4.0.3",
|
||||||
"rimraf": "6.0.1"
|
"rimraf": "6.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
"klona": "2.0.6",
|
"klona": "2.0.6",
|
||||||
"localforage": "1.10.0",
|
"localforage": "1.10.0",
|
||||||
"nanoid": "5.1.5"
|
"nanoid": "5.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/crypto-js": "4.2.2"
|
"@types/crypto-js": "4.2.2"
|
||||||
|
|||||||
2650
pnpm-lock.yaml
generated
2650
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
42
src/components/common/icon-tooltip.vue
Normal file
42
src/components/common/icon-tooltip.vue
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, useSlots } from 'vue';
|
||||||
|
import type { PopoverPlacement } from 'naive-ui';
|
||||||
|
|
||||||
|
defineOptions({ name: 'IconTooltip' });
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
icon?: string;
|
||||||
|
localIcon?: string;
|
||||||
|
desc?: string;
|
||||||
|
placement?: PopoverPlacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
icon: 'mdi-help-circle',
|
||||||
|
localIcon: '',
|
||||||
|
desc: '',
|
||||||
|
placement: 'top'
|
||||||
|
});
|
||||||
|
|
||||||
|
const slots = useSlots();
|
||||||
|
const hasCustomTrigger = computed(() => Boolean(slots.trigger));
|
||||||
|
|
||||||
|
if (!hasCustomTrigger.value && !props.icon && !props.localIcon) {
|
||||||
|
throw new Error('icon or localIcon is required when no custom trigger slot is provided');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NTooltip :placement="placement">
|
||||||
|
<template #trigger>
|
||||||
|
<slot name="trigger">
|
||||||
|
<div class="cursor-pointer">
|
||||||
|
<SvgIcon :icon="icon" :local-icon="localIcon" />
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
<slot>
|
||||||
|
<span>{{ desc }}</span>
|
||||||
|
</slot>
|
||||||
|
</NTooltip>
|
||||||
|
</template>
|
||||||
@@ -40,7 +40,8 @@ export const themeScrollModeOptions = transformRecordToOption(themeScrollModeRec
|
|||||||
|
|
||||||
export const themeTabModeRecord: Record<UnionKey.ThemeTabMode, App.I18n.I18nKey> = {
|
export const themeTabModeRecord: Record<UnionKey.ThemeTabMode, App.I18n.I18nKey> = {
|
||||||
chrome: 'theme.layout.tab.mode.chrome',
|
chrome: 'theme.layout.tab.mode.chrome',
|
||||||
button: 'theme.layout.tab.mode.button'
|
button: 'theme.layout.tab.mode.button',
|
||||||
|
slider: 'theme.layout.tab.mode.slider'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const themeTabModeOptions = transformRecordToOption(themeTabModeRecord);
|
export const themeTabModeOptions = transformRecordToOption(themeTabModeRecord);
|
||||||
@@ -57,13 +58,6 @@ export const themePageAnimationModeRecord: Record<UnionKey.ThemePageAnimateMode,
|
|||||||
|
|
||||||
export const themePageAnimationModeOptions = transformRecordToOption(themePageAnimationModeRecord);
|
export const themePageAnimationModeOptions = transformRecordToOption(themePageAnimationModeRecord);
|
||||||
|
|
||||||
export const resetCacheStrategyRecord: Record<UnionKey.ResetCacheStrategy, App.I18n.I18nKey> = {
|
|
||||||
close: 'theme.layout.resetCacheStrategy.close',
|
|
||||||
refresh: 'theme.layout.resetCacheStrategy.refresh'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const resetCacheStrategyOptions = transformRecordToOption(resetCacheStrategyRecord);
|
|
||||||
|
|
||||||
export const DARK_CLASS = 'dark';
|
export const DARK_CLASS = 'dark';
|
||||||
|
|
||||||
export const watermarkTimeFormatOptions = [
|
export const watermarkTimeFormatOptions = [
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const { selectedKey } = useMenu();
|
|||||||
/>
|
/>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<Teleport :to="`#${GLOBAL_SIDER_MENU_ID}`">
|
<Teleport :to="`#${GLOBAL_SIDER_MENU_ID}`">
|
||||||
|
<div class="h-full pt-2">
|
||||||
<FirstLevelMenu
|
<FirstLevelMenu
|
||||||
:menus="firstLevelMenus"
|
:menus="firstLevelMenus"
|
||||||
:active-menu-key="activeFirstLevelMenuKey"
|
:active-menu-key="activeFirstLevelMenuKey"
|
||||||
@@ -39,6 +40,7 @@ const { selectedKey } = useMenu();
|
|||||||
@select="handleSelectFirstLevelMenu"
|
@select="handleSelectFirstLevelMenu"
|
||||||
@toggle-sider-collapse="appStore.toggleSiderCollapse"
|
@toggle-sider-collapse="appStore.toggleSiderCollapse"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,9 @@ init();
|
|||||||
<div
|
<div
|
||||||
ref="tabRef"
|
ref="tabRef"
|
||||||
class="h-full flex pr-18px"
|
class="h-full flex pr-18px"
|
||||||
:class="[themeStore.tab.mode === 'chrome' ? 'items-end' : 'items-center gap-12px']"
|
:class="[
|
||||||
|
themeStore.tab.mode === 'chrome' || themeStore.tab.mode === 'slider' ? 'items-end' : 'items-center gap-12px'
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<PageTab
|
<PageTab
|
||||||
v-for="tab in tabStore.tabs"
|
v-for="tab in tabStore.tabs"
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ function handleChangeMode(mode: UnionKey.ThemeLayoutMode) {
|
|||||||
class="flex-col-center cursor-pointer"
|
class="flex-col-center cursor-pointer"
|
||||||
@click="handleChangeMode(key)"
|
@click="handleChangeMode(key)"
|
||||||
>
|
>
|
||||||
<NTooltip :placement="item.placement">
|
<IconTooltip :placement="item.placement">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<div
|
<div
|
||||||
class="h-64px w-96px gap-6px rd-4px p-6px shadow ring-2 ring-transparent transition-all hover:ring-primary"
|
class="h-64px w-96px gap-6px rd-4px p-6px shadow ring-2 ring-transparent transition-all hover:ring-primary"
|
||||||
@@ -92,7 +92,7 @@ function handleChangeMode(mode: UnionKey.ThemeLayoutMode) {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
{{ $t(`theme.layout.layoutMode.${key}_detail`) }}
|
{{ $t(`theme.layout.layoutMode.${key}_detail`) }}
|
||||||
</NTooltip>
|
</IconTooltip>
|
||||||
<p class="mt-8px text-12px">{{ $t(themeLayoutModeRecord[key]) }}</p>
|
<p class="mt-8px text-12px">{{ $t(themeLayoutModeRecord[key]) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ defineProps<Props>();
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full flex-y-center justify-between">
|
<div class="w-full flex-y-center justify-between">
|
||||||
<div>
|
<div class="flex-y-center">
|
||||||
<span class="pr-8px text-base-text">{{ label }}</span>
|
<span class="pr-8px text-base-text">{{ label }}</span>
|
||||||
<slot name="suffix"></slot>
|
<slot name="suffix"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import AppearanceSettings from './modules/appearance/index.vue';
|
|||||||
import LayoutSettings from './modules/layout/index.vue';
|
import LayoutSettings from './modules/layout/index.vue';
|
||||||
import GeneralSettings from './modules/general/index.vue';
|
import GeneralSettings from './modules/general/index.vue';
|
||||||
import ConfigOperation from './modules/config-operation.vue';
|
import ConfigOperation from './modules/config-operation.vue';
|
||||||
|
import PresetSettings from './modules/preset/index.vue';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'ThemeDrawer'
|
name: 'ThemeDrawer'
|
||||||
@@ -33,6 +34,7 @@ const drawerWidth = computed(() => {
|
|||||||
<NTab name="appearance" :tab="$t('theme.tabs.appearance')"></NTab>
|
<NTab name="appearance" :tab="$t('theme.tabs.appearance')"></NTab>
|
||||||
<NTab name="layout" :tab="$t('theme.tabs.layout')"></NTab>
|
<NTab name="layout" :tab="$t('theme.tabs.layout')"></NTab>
|
||||||
<NTab name="general" :tab="$t('theme.tabs.general')"></NTab>
|
<NTab name="general" :tab="$t('theme.tabs.general')"></NTab>
|
||||||
|
<NTab name="preset" :tab="$t('theme.tabs.preset')"></NTab>
|
||||||
</NTabs>
|
</NTabs>
|
||||||
|
|
||||||
<div class="min-h-400px">
|
<div class="min-h-400px">
|
||||||
@@ -40,6 +42,7 @@ const drawerWidth = computed(() => {
|
|||||||
<AppearanceSettings v-if="activeTab === 'appearance'" />
|
<AppearanceSettings v-if="activeTab === 'appearance'" />
|
||||||
<LayoutSettings v-else-if="activeTab === 'layout'" />
|
<LayoutSettings v-else-if="activeTab === 'layout'" />
|
||||||
<GeneralSettings v-else-if="activeTab === 'general'" />
|
<GeneralSettings v-else-if="activeTab === 'general'" />
|
||||||
|
<PresetSettings v-else-if="activeTab === 'preset'" />
|
||||||
</KeepAlive>
|
</KeepAlive>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -36,12 +36,9 @@ const swatches: string[] = [
|
|||||||
<template>
|
<template>
|
||||||
<NDivider>{{ $t('theme.appearance.themeColor.title') }}</NDivider>
|
<NDivider>{{ $t('theme.appearance.themeColor.title') }}</NDivider>
|
||||||
<div class="flex-col-stretch gap-12px">
|
<div class="flex-col-stretch gap-12px">
|
||||||
<NTooltip placement="top-start">
|
|
||||||
<template #trigger>
|
|
||||||
<SettingItem key="recommend-color" :label="$t('theme.appearance.recommendColor')">
|
<SettingItem key="recommend-color" :label="$t('theme.appearance.recommendColor')">
|
||||||
<NSwitch v-model:value="themeStore.recommendColor" />
|
<template #suffix>
|
||||||
</SettingItem>
|
<IconTooltip>
|
||||||
</template>
|
|
||||||
<p>
|
<p>
|
||||||
<span class="pr-12px">{{ $t('theme.appearance.recommendColorDesc') }}</span>
|
<span class="pr-12px">{{ $t('theme.appearance.recommendColorDesc') }}</span>
|
||||||
<br />
|
<br />
|
||||||
@@ -56,7 +53,11 @@ const swatches: string[] = [
|
|||||||
https://uicolors.app/create
|
https://uicolors.app/create
|
||||||
</NButton>
|
</NButton>
|
||||||
</p>
|
</p>
|
||||||
</NTooltip>
|
</IconTooltip>
|
||||||
|
</template>
|
||||||
|
<NSwitch v-model:value="themeStore.recommendColor" />
|
||||||
|
</SettingItem>
|
||||||
|
|
||||||
<SettingItem
|
<SettingItem
|
||||||
v-for="(_, key) in themeStore.themeColors"
|
v-for="(_, key) in themeStore.themeColors"
|
||||||
:key="key"
|
:key="key"
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ const isWrapperScrollMode = computed(() => themeStore.layout.scrollMode === 'wra
|
|||||||
<NDivider>{{ $t('theme.layout.content.title') }}</NDivider>
|
<NDivider>{{ $t('theme.layout.content.title') }}</NDivider>
|
||||||
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
||||||
<SettingItem key="1" :label="$t('theme.layout.content.scrollMode.title')">
|
<SettingItem key="1" :label="$t('theme.layout.content.scrollMode.title')">
|
||||||
|
<template #suffix>
|
||||||
|
<IconTooltip :desc="$t('theme.layout.content.scrollMode.tip')" />
|
||||||
|
</template>
|
||||||
<NSelect
|
<NSelect
|
||||||
v-model:value="themeStore.layout.scrollMode"
|
v-model:value="themeStore.layout.scrollMode"
|
||||||
:options="translateOptions(themeScrollModeOptions)"
|
:options="translateOptions(themeScrollModeOptions)"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { resetCacheStrategyOptions, themeTabModeOptions } from '@/constants/app';
|
import { themeTabModeOptions } from '@/constants/app';
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { translateOptions } from '@/utils/common';
|
import { translateOptions } from '@/utils/common';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
@@ -15,18 +15,13 @@ const themeStore = useThemeStore();
|
|||||||
<template>
|
<template>
|
||||||
<NDivider>{{ $t('theme.layout.tab.title') }}</NDivider>
|
<NDivider>{{ $t('theme.layout.tab.title') }}</NDivider>
|
||||||
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
<TransitionGroup tag="div" name="setting-list" class="flex-col-stretch gap-12px">
|
||||||
<SettingItem key="0" :label="$t('theme.layout.resetCacheStrategy.title')">
|
|
||||||
<NSelect
|
|
||||||
v-model:value="themeStore.resetCacheStrategy"
|
|
||||||
:options="translateOptions(resetCacheStrategyOptions)"
|
|
||||||
size="small"
|
|
||||||
class="w-120px"
|
|
||||||
/>
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem key="1" :label="$t('theme.layout.tab.visible')">
|
<SettingItem key="1" :label="$t('theme.layout.tab.visible')">
|
||||||
<NSwitch v-model:value="themeStore.tab.visible" />
|
<NSwitch v-model:value="themeStore.tab.visible" />
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
<SettingItem v-if="themeStore.tab.visible" key="2" :label="$t('theme.layout.tab.cache')">
|
<SettingItem v-if="themeStore.tab.visible" key="2" :label="$t('theme.layout.tab.cache')">
|
||||||
|
<template #suffix>
|
||||||
|
<IconTooltip :desc="$t('theme.layout.tab.cacheTip')" />
|
||||||
|
</template>
|
||||||
<NSwitch v-model:value="themeStore.tab.cache" />
|
<NSwitch v-model:value="themeStore.tab.cache" />
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
<SettingItem v-if="themeStore.tab.visible" key="3" :label="$t('theme.layout.tab.height')">
|
<SettingItem v-if="themeStore.tab.visible" key="3" :label="$t('theme.layout.tab.height')">
|
||||||
|
|||||||
15
src/layouts/modules/theme-drawer/modules/preset/index.vue
Normal file
15
src/layouts/modules/theme-drawer/modules/preset/index.vue
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import ThemePreset from './modules/theme-preset.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'PresetSettings'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex-col-stretch gap-16px">
|
||||||
|
<ThemePreset />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'ThemePreset'
|
||||||
|
});
|
||||||
|
|
||||||
|
type ThemePreset = Pick<
|
||||||
|
App.Theme.ThemeSetting,
|
||||||
|
| 'themeScheme'
|
||||||
|
| 'grayscale'
|
||||||
|
| 'colourWeakness'
|
||||||
|
| 'recommendColor'
|
||||||
|
| 'themeColor'
|
||||||
|
| 'otherColor'
|
||||||
|
| 'isInfoFollowPrimary'
|
||||||
|
| 'layout'
|
||||||
|
| 'page'
|
||||||
|
| 'header'
|
||||||
|
| 'tab'
|
||||||
|
| 'fixedHeaderAndTab'
|
||||||
|
| 'sider'
|
||||||
|
| 'footer'
|
||||||
|
| 'watermark'
|
||||||
|
| 'tokens'
|
||||||
|
> & {
|
||||||
|
name: string;
|
||||||
|
desc: string;
|
||||||
|
i18nkey?: string;
|
||||||
|
version: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const presetModules = import.meta.glob('@/theme/preset/*.json', { eager: true, import: 'default' });
|
||||||
|
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
|
||||||
|
// Extract preset data
|
||||||
|
const presets = computed(() =>
|
||||||
|
Object.entries(presetModules)
|
||||||
|
.map(([path, presetData]) => {
|
||||||
|
const fileName = path.split('/').pop()?.replace('.json', '') || '';
|
||||||
|
return {
|
||||||
|
id: fileName,
|
||||||
|
...(presetData as ThemePreset)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (a.name === 'default') return -1;
|
||||||
|
if (b.name === 'default') return 1;
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const getPresetName = (preset: ThemePreset): string => {
|
||||||
|
if (!preset.i18nkey) return preset.name;
|
||||||
|
try {
|
||||||
|
const key = `${preset.i18nkey}.name` as App.I18n.I18nKey;
|
||||||
|
const translated = $t(key);
|
||||||
|
return translated !== key ? translated : preset.name;
|
||||||
|
} catch {
|
||||||
|
return preset.name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPresetDesc = (preset: ThemePreset): string => {
|
||||||
|
if (!preset.i18nkey) return preset.desc;
|
||||||
|
try {
|
||||||
|
const key = `${preset.i18nkey}.desc` as App.I18n.I18nKey;
|
||||||
|
const translated = $t(key);
|
||||||
|
return translated !== key ? translated : preset.desc;
|
||||||
|
} catch {
|
||||||
|
return preset.desc;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyPreset = ({ themeScheme, grayscale, colourWeakness, layout, watermark, ...rest }: ThemePreset): void => {
|
||||||
|
themeStore.setThemeScheme(themeScheme);
|
||||||
|
themeStore.setGrayscale(grayscale);
|
||||||
|
themeStore.setColourWeakness(colourWeakness);
|
||||||
|
themeStore.setThemeLayout(layout.mode);
|
||||||
|
themeStore.setWatermarkEnableUserName(watermark.enableUserName);
|
||||||
|
themeStore.setWatermarkEnableTime(watermark.enableTime);
|
||||||
|
|
||||||
|
Object.assign(themeStore, {
|
||||||
|
...rest,
|
||||||
|
layout: { ...themeStore.layout, scrollMode: layout.scrollMode },
|
||||||
|
page: { ...rest.page },
|
||||||
|
header: { ...rest.header },
|
||||||
|
tab: { ...rest.tab },
|
||||||
|
sider: { ...rest.sider },
|
||||||
|
footer: { ...rest.footer },
|
||||||
|
watermark: { ...watermark },
|
||||||
|
tokens: { ...rest.tokens }
|
||||||
|
});
|
||||||
|
|
||||||
|
window.$message?.success($t('theme.appearance.preset.applySuccess'));
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDivider>{{ $t('theme.appearance.preset.title') }}</NDivider>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
|
<div
|
||||||
|
v-for="preset in presets"
|
||||||
|
:key="preset.id"
|
||||||
|
class="border border-primary/10 rounded-lg border-solid bg-white/5 p-3 backdrop-blur-10 transition-all duration-300 hover:(shadow-md -translate-y-0.5)"
|
||||||
|
>
|
||||||
|
<div class="mb-2 flex items-center justify-between">
|
||||||
|
<div class="min-w-0 w-full flex flex-1 items-center justify-between gap-2">
|
||||||
|
<h5 class="m-0 truncate text-sm text-primary font-600">
|
||||||
|
{{ getPresetName(preset) }}
|
||||||
|
</h5>
|
||||||
|
<NBadge :value="`v${preset.version}`" type="info" size="small" class="flex-shrink-0 opacity-80" />
|
||||||
|
</div>
|
||||||
|
<NButton type="primary" size="tiny" ghost round class="ml-2 flex-shrink-0" @click="applyPreset(preset)">
|
||||||
|
{{ $t('theme.appearance.preset.apply') }}
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="line-clamp-2 mb-3 text-xs text-gray-500 leading-4">{{ getPresetDesc(preset) }}</p>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<div
|
||||||
|
v-for="(color, key) in { primary: preset.themeColor, ...preset.otherColor }"
|
||||||
|
:key="key"
|
||||||
|
class="h-3 w-3 cursor-pointer border border-white/30 rounded-full transition-transform hover:scale-110"
|
||||||
|
:style="{ backgroundColor: color }"
|
||||||
|
:class="{ 'ring-1 ring-primary/50': key === 'primary' }"
|
||||||
|
:title="key"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<div class="text-lg">
|
||||||
|
{{ preset.themeScheme === 'dark' ? '🌙' : '☀️' }}
|
||||||
|
</div>
|
||||||
|
<div class="text-lg">
|
||||||
|
{{ preset.grayscale ? '🎨' : '' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -62,7 +62,8 @@ const local: App.I18n.Schema = {
|
|||||||
tabs: {
|
tabs: {
|
||||||
appearance: 'Appearance',
|
appearance: 'Appearance',
|
||||||
layout: 'Layout',
|
layout: 'Layout',
|
||||||
general: 'General'
|
general: 'General',
|
||||||
|
preset: 'Preset'
|
||||||
},
|
},
|
||||||
appearance: {
|
appearance: {
|
||||||
themeSchema: {
|
themeSchema: {
|
||||||
@@ -83,7 +84,28 @@ const local: App.I18n.Schema = {
|
|||||||
followPrimary: 'Follow Primary'
|
followPrimary: 'Follow Primary'
|
||||||
},
|
},
|
||||||
recommendColor: 'Apply Recommended Color Algorithm',
|
recommendColor: 'Apply Recommended Color Algorithm',
|
||||||
recommendColorDesc: 'The recommended color algorithm refers to'
|
recommendColorDesc: 'The recommended color algorithm refers to',
|
||||||
|
preset: {
|
||||||
|
title: 'Theme Presets',
|
||||||
|
apply: 'Apply',
|
||||||
|
applySuccess: 'Preset applied successfully',
|
||||||
|
default: {
|
||||||
|
name: 'Default Preset',
|
||||||
|
desc: 'Default theme preset with balanced settings'
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
name: 'Dark Preset',
|
||||||
|
desc: 'Dark theme preset for night time usage'
|
||||||
|
},
|
||||||
|
compact: {
|
||||||
|
name: 'Compact Preset',
|
||||||
|
desc: 'Compact layout preset for small screens'
|
||||||
|
},
|
||||||
|
azir: {
|
||||||
|
name: "Azir's Preset",
|
||||||
|
desc: 'It is a cold and elegant preset that Azir likes'
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
layout: {
|
layout: {
|
||||||
layoutMode: {
|
layoutMode: {
|
||||||
@@ -109,9 +131,11 @@ const local: App.I18n.Schema = {
|
|||||||
title: 'Tab Settings',
|
title: 'Tab Settings',
|
||||||
visible: 'Tab Visible',
|
visible: 'Tab Visible',
|
||||||
cache: 'Tag Bar Info Cache',
|
cache: 'Tag Bar Info Cache',
|
||||||
|
cacheTip: 'One-click to open/close global keepalive',
|
||||||
height: 'Tab Height',
|
height: 'Tab Height',
|
||||||
mode: {
|
mode: {
|
||||||
title: 'Tab Mode',
|
title: 'Tab Mode',
|
||||||
|
slider: 'Slider',
|
||||||
chrome: 'Chrome',
|
chrome: 'Chrome',
|
||||||
button: 'Button'
|
button: 'Button'
|
||||||
}
|
}
|
||||||
@@ -144,6 +168,7 @@ const local: App.I18n.Schema = {
|
|||||||
title: 'Content Area Settings',
|
title: 'Content Area Settings',
|
||||||
scrollMode: {
|
scrollMode: {
|
||||||
title: 'Scroll Mode',
|
title: 'Scroll Mode',
|
||||||
|
tip: 'The theme scroll only scrolls the main part, the outer scroll can carry the header and footer together',
|
||||||
wrapper: 'Wrapper',
|
wrapper: 'Wrapper',
|
||||||
content: 'Content'
|
content: 'Content'
|
||||||
},
|
},
|
||||||
@@ -161,11 +186,6 @@ const local: App.I18n.Schema = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
fixedHeaderAndTab: 'Fixed Header And Tab'
|
fixedHeaderAndTab: 'Fixed Header And Tab'
|
||||||
},
|
|
||||||
resetCacheStrategy: {
|
|
||||||
title: 'Reset Cache Strategy',
|
|
||||||
close: 'Close Page',
|
|
||||||
refresh: 'Refresh Page'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ const local: App.I18n.Schema = {
|
|||||||
tabs: {
|
tabs: {
|
||||||
appearance: '外观',
|
appearance: '外观',
|
||||||
layout: '布局',
|
layout: '布局',
|
||||||
general: '通用'
|
general: '通用',
|
||||||
|
preset: '预设'
|
||||||
},
|
},
|
||||||
appearance: {
|
appearance: {
|
||||||
themeSchema: {
|
themeSchema: {
|
||||||
@@ -83,7 +84,28 @@ const local: App.I18n.Schema = {
|
|||||||
followPrimary: '跟随主色'
|
followPrimary: '跟随主色'
|
||||||
},
|
},
|
||||||
recommendColor: '应用推荐算法的颜色',
|
recommendColor: '应用推荐算法的颜色',
|
||||||
recommendColorDesc: '推荐颜色的算法参照'
|
recommendColorDesc: '推荐颜色的算法参照',
|
||||||
|
preset: {
|
||||||
|
title: '主题预设',
|
||||||
|
apply: '应用',
|
||||||
|
applySuccess: '预设应用成功',
|
||||||
|
default: {
|
||||||
|
name: '默认预设',
|
||||||
|
desc: 'Soybean 默认主题预设'
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
name: '暗色预设',
|
||||||
|
desc: '适用于夜间使用的暗色主题预设'
|
||||||
|
},
|
||||||
|
compact: {
|
||||||
|
name: '紧凑型',
|
||||||
|
desc: '适用于小屏幕的紧凑布局预设'
|
||||||
|
},
|
||||||
|
azir: {
|
||||||
|
name: 'Azir的预设',
|
||||||
|
desc: '是 Azir 比较喜欢的莫兰迪色系冷淡风'
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
layout: {
|
layout: {
|
||||||
layoutMode: {
|
layoutMode: {
|
||||||
@@ -106,9 +128,11 @@ const local: App.I18n.Schema = {
|
|||||||
title: '标签栏设置',
|
title: '标签栏设置',
|
||||||
visible: '显示标签栏',
|
visible: '显示标签栏',
|
||||||
cache: '标签栏信息缓存',
|
cache: '标签栏信息缓存',
|
||||||
|
cacheTip: '一键开启/关闭全局 keepalive',
|
||||||
height: '标签栏高度',
|
height: '标签栏高度',
|
||||||
mode: {
|
mode: {
|
||||||
title: '标签栏风格',
|
title: '标签栏风格',
|
||||||
|
slider: '滑块风格',
|
||||||
chrome: '谷歌风格',
|
chrome: '谷歌风格',
|
||||||
button: '按钮风格'
|
button: '按钮风格'
|
||||||
}
|
}
|
||||||
@@ -135,12 +159,13 @@ const local: App.I18n.Schema = {
|
|||||||
visible: '显示底部',
|
visible: '显示底部',
|
||||||
fixed: '固定底部',
|
fixed: '固定底部',
|
||||||
height: '底部高度',
|
height: '底部高度',
|
||||||
right: '底部局右'
|
right: '底部居右'
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
title: '内容区域设置',
|
title: '内容区域设置',
|
||||||
scrollMode: {
|
scrollMode: {
|
||||||
title: '滚动模式',
|
title: '滚动模式',
|
||||||
|
tip: '主题滚动仅 main 部分滚动,外层滚动可携带头部底部一起滚动',
|
||||||
wrapper: '外层滚动',
|
wrapper: '外层滚动',
|
||||||
content: '主体滚动'
|
content: '主体滚动'
|
||||||
},
|
},
|
||||||
@@ -158,11 +183,6 @@ const local: App.I18n.Schema = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
fixedHeaderAndTab: '固定头部和标签栏'
|
fixedHeaderAndTab: '固定头部和标签栏'
|
||||||
},
|
|
||||||
resetCacheStrategy: {
|
|
||||||
title: '重置缓存策略',
|
|
||||||
close: '关闭页面',
|
|
||||||
refresh: '刷新页面'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
|||||||
27
src/router/_generated/imports.ts
Normal file
27
src/router/_generated/imports.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
/* oxlint-disable */
|
||||||
|
// biome-ignore lint: disable
|
||||||
|
// Generated by elegant-router
|
||||||
|
// Read more: https://github.com/soybeanjs/elegant-router
|
||||||
|
|
||||||
|
import type { RouteFileKey, RouteLayoutKey, RawRouteComponent } from "@elegant-router/types";
|
||||||
|
|
||||||
|
export const layouts: Record<RouteLayoutKey, RawRouteComponent> = {
|
||||||
|
base: () => import("@/layouts/base-layout/index.vue"),
|
||||||
|
blank: () => import("@/layouts/blank-layout/index.vue"),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const views: Record<RouteFileKey, RawRouteComponent> = {
|
||||||
|
403: () => import("@/views/(builtin)/403/index.vue"),
|
||||||
|
404: () => import("@/views/(builtin)/404/index.vue"),
|
||||||
|
500: () => import("@/views/(builtin)/500/index.vue"),
|
||||||
|
Home: () => import("@/views/home/index.vue"),
|
||||||
|
IframeUrl: () => import("@/views/(builtin)/iframe/[url].vue"),
|
||||||
|
Login: () => import("@/views/(builtin)/login/index.vue"),
|
||||||
|
ManageMenu: () => import("@/views/manage/menu/index.vue"),
|
||||||
|
ManageRole: () => import("@/views/manage/role/index.vue"),
|
||||||
|
ManageRoute: () => import("@/views/manage/route/index.vue"),
|
||||||
|
ManageUser: () => import("@/views/manage/user/index.vue"),
|
||||||
|
Wip: () => import("@/views/(builtin)/wip/index.vue"),
|
||||||
|
};
|
||||||
130
src/router/_generated/routes.ts
Normal file
130
src/router/_generated/routes.ts
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// biome-ignore lint: disable
|
||||||
|
// Generated by elegant-router
|
||||||
|
// Read more: https://github.com/soybeanjs/elegant-router
|
||||||
|
|
||||||
|
import type { AutoRouterRoute } from '@elegant-router/types';
|
||||||
|
|
||||||
|
export const routes: AutoRouterRoute[] = [
|
||||||
|
{
|
||||||
|
name: 'Root',
|
||||||
|
path: '/',
|
||||||
|
redirect: '/home',
|
||||||
|
meta: {
|
||||||
|
title: "Root"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NotFound',
|
||||||
|
path: '/:pathMatch(.*)*',
|
||||||
|
layout: 'base',
|
||||||
|
component: '404',
|
||||||
|
meta: {
|
||||||
|
title: "NotFound"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '403',
|
||||||
|
path: '/403',
|
||||||
|
layout: 'base',
|
||||||
|
component: '403',
|
||||||
|
meta: {
|
||||||
|
title: "403",
|
||||||
|
constant: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '404',
|
||||||
|
path: '/404',
|
||||||
|
layout: 'base',
|
||||||
|
component: '404',
|
||||||
|
meta: {
|
||||||
|
title: "404",
|
||||||
|
constant: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '500',
|
||||||
|
path: '/500',
|
||||||
|
layout: 'base',
|
||||||
|
component: '500',
|
||||||
|
meta: {
|
||||||
|
title: "500",
|
||||||
|
constant: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Home',
|
||||||
|
path: '/home',
|
||||||
|
layout: 'base',
|
||||||
|
component: 'Home',
|
||||||
|
meta: {
|
||||||
|
title: "Home"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'IframeUrl',
|
||||||
|
path: '/iframe/:url',
|
||||||
|
layout: 'base',
|
||||||
|
component: 'IframeUrl',
|
||||||
|
meta: {
|
||||||
|
title: "IframeUrl"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Login',
|
||||||
|
path: '/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?',
|
||||||
|
layout: 'base',
|
||||||
|
component: 'Login',
|
||||||
|
meta: {
|
||||||
|
title: "Login",
|
||||||
|
constant: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ManageMenu',
|
||||||
|
path: '/manage/menu',
|
||||||
|
layout: 'base',
|
||||||
|
component: 'ManageMenu',
|
||||||
|
meta: {
|
||||||
|
title: "ManageMenu"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ManageRole',
|
||||||
|
path: '/manage/role',
|
||||||
|
layout: 'base',
|
||||||
|
component: 'ManageRole',
|
||||||
|
meta: {
|
||||||
|
title: "ManageRole"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ManageRoute',
|
||||||
|
path: '/manage/route',
|
||||||
|
layout: 'base',
|
||||||
|
component: 'ManageRoute',
|
||||||
|
meta: {
|
||||||
|
title: "ManageRoute"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ManageUser',
|
||||||
|
path: '/manage/user',
|
||||||
|
layout: 'base',
|
||||||
|
component: 'ManageUser',
|
||||||
|
meta: {
|
||||||
|
title: "ManageUser"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Wip',
|
||||||
|
path: '/wip',
|
||||||
|
layout: 'base',
|
||||||
|
component: 'Wip',
|
||||||
|
meta: {
|
||||||
|
title: "Wip"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
28
src/router/_generated/shared.ts
Normal file
28
src/router/_generated/shared.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
/* oxlint-disable */
|
||||||
|
// biome-ignore lint: disable
|
||||||
|
// Generated by elegant-router
|
||||||
|
// Read more: https://github.com/soybeanjs/elegant-router
|
||||||
|
|
||||||
|
import type { RouteKey, RoutePathMap } from '@elegant-router/types';
|
||||||
|
|
||||||
|
const routePathMap: RoutePathMap = {
|
||||||
|
"Root": "/",
|
||||||
|
"NotFound": "/:pathMatch(.*)*",
|
||||||
|
"403": "/403",
|
||||||
|
"404": "/404",
|
||||||
|
"500": "/500",
|
||||||
|
"Home": "/home",
|
||||||
|
"IframeUrl": "/iframe/:url",
|
||||||
|
"Login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
|
||||||
|
"ManageMenu": "/manage/menu",
|
||||||
|
"ManageRole": "/manage/role",
|
||||||
|
"ManageRoute": "/manage/route",
|
||||||
|
"ManageUser": "/manage/user",
|
||||||
|
"Wip": "/wip",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getRoutePath(key: RouteKey) {
|
||||||
|
return routePathMap[key];
|
||||||
|
}
|
||||||
70
src/router/_generated/transformer.ts
Normal file
70
src/router/_generated/transformer.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
/* oxlint-disable */
|
||||||
|
// biome-ignore lint: disable
|
||||||
|
// Generated by elegant-router
|
||||||
|
// Read more: https://github.com/soybeanjs/elegant-router
|
||||||
|
|
||||||
|
import type { RouteRecordRaw } from 'vue-router';
|
||||||
|
import type {
|
||||||
|
AutoRouterRedirect,
|
||||||
|
AutoRouterRoute,
|
||||||
|
AutoRouterSingleView,
|
||||||
|
RawRouteComponent,
|
||||||
|
RouteFileKey,
|
||||||
|
RouteLayoutKey
|
||||||
|
} from '@elegant-router/types';
|
||||||
|
|
||||||
|
export function transformToVueRoutes(
|
||||||
|
routes: AutoRouterRoute[],
|
||||||
|
layouts: Record<RouteLayoutKey, RawRouteComponent>,
|
||||||
|
views: Record<RouteFileKey, RawRouteComponent>
|
||||||
|
) {
|
||||||
|
const { redirects, groupedRoutes } = getFormattedRoutes(routes);
|
||||||
|
|
||||||
|
const vueRoutes: RouteRecordRaw[] = [...redirects];
|
||||||
|
|
||||||
|
groupedRoutes.forEach((items, layout) => {
|
||||||
|
const layoutRoute: RouteRecordRaw = {
|
||||||
|
path: `/${layout}-layout`,
|
||||||
|
component: layouts[layout],
|
||||||
|
children: items.map(item => {
|
||||||
|
const { layout: _, component, ...rest } = item;
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: views[component],
|
||||||
|
...rest
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
vueRoutes.push(layoutRoute);
|
||||||
|
});
|
||||||
|
|
||||||
|
return vueRoutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFormattedRoutes(routes: AutoRouterRoute[]) {
|
||||||
|
const groupedRoutes = new Map<RouteLayoutKey, AutoRouterSingleView[]>();
|
||||||
|
const redirects: AutoRouterRedirect[] = [];
|
||||||
|
|
||||||
|
routes.forEach(route => {
|
||||||
|
if (isAutoRouterRedirect(route)) {
|
||||||
|
redirects.push(route);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = groupedRoutes.get(route.layout) || [];
|
||||||
|
items.push(route);
|
||||||
|
groupedRoutes.set(route.layout, items);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
redirects,
|
||||||
|
groupedRoutes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAutoRouterRedirect(route: AutoRouterRoute): route is AutoRouterRedirect {
|
||||||
|
return 'redirect' in route;
|
||||||
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/* prettier-ignore */
|
|
||||||
// Generated by elegant-router
|
|
||||||
// Read more: https://github.com/soybeanjs/elegant-router
|
|
||||||
|
|
||||||
import type { RouteComponent } from "vue-router";
|
|
||||||
import type { LastLevelRouteKey, RouteLayout } from "@elegant-router/types";
|
|
||||||
|
|
||||||
import BaseLayout from "@/layouts/base-layout/index.vue";
|
|
||||||
import BlankLayout from "@/layouts/blank-layout/index.vue";
|
|
||||||
|
|
||||||
export const layouts: Record<RouteLayout, RouteComponent | (() => Promise<RouteComponent>)> = {
|
|
||||||
base: BaseLayout,
|
|
||||||
blank: BlankLayout,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<RouteComponent>)> = {
|
|
||||||
403: () => import("@/views/_builtin/403/index.vue"),
|
|
||||||
404: () => import("@/views/_builtin/404/index.vue"),
|
|
||||||
500: () => import("@/views/_builtin/500/index.vue"),
|
|
||||||
"iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"),
|
|
||||||
login: () => import("@/views/_builtin/login/index.vue"),
|
|
||||||
home: () => import("@/views/home/index.vue"),
|
|
||||||
};
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/* prettier-ignore */
|
|
||||||
// Generated by elegant-router
|
|
||||||
// Read more: https://github.com/soybeanjs/elegant-router
|
|
||||||
|
|
||||||
import type { GeneratedRoute } from '@elegant-router/types';
|
|
||||||
|
|
||||||
export const generatedRoutes: GeneratedRoute[] = [
|
|
||||||
{
|
|
||||||
name: '403',
|
|
||||||
path: '/403',
|
|
||||||
component: 'layout.blank$view.403',
|
|
||||||
meta: {
|
|
||||||
title: '403',
|
|
||||||
i18nKey: 'route.403',
|
|
||||||
constant: true,
|
|
||||||
hideInMenu: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '404',
|
|
||||||
path: '/404',
|
|
||||||
component: 'layout.blank$view.404',
|
|
||||||
meta: {
|
|
||||||
title: '404',
|
|
||||||
i18nKey: 'route.404',
|
|
||||||
constant: true,
|
|
||||||
hideInMenu: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '500',
|
|
||||||
path: '/500',
|
|
||||||
component: 'layout.blank$view.500',
|
|
||||||
meta: {
|
|
||||||
title: '500',
|
|
||||||
i18nKey: 'route.500',
|
|
||||||
constant: true,
|
|
||||||
hideInMenu: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'home',
|
|
||||||
path: '/home',
|
|
||||||
component: 'layout.base$view.home',
|
|
||||||
meta: {
|
|
||||||
title: 'home',
|
|
||||||
i18nKey: 'route.home',
|
|
||||||
icon: 'mdi:monitor-dashboard',
|
|
||||||
order: 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'iframe-page',
|
|
||||||
path: '/iframe-page/:url',
|
|
||||||
component: 'layout.base$view.iframe-page',
|
|
||||||
props: true,
|
|
||||||
meta: {
|
|
||||||
title: 'iframe-page',
|
|
||||||
i18nKey: 'route.iframe-page',
|
|
||||||
constant: true,
|
|
||||||
hideInMenu: true,
|
|
||||||
keepAlive: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'login',
|
|
||||||
path: '/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?',
|
|
||||||
component: 'layout.blank$view.login',
|
|
||||||
props: true,
|
|
||||||
meta: {
|
|
||||||
title: 'login',
|
|
||||||
i18nKey: 'route.login',
|
|
||||||
constant: true,
|
|
||||||
hideInMenu: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/* prettier-ignore */
|
|
||||||
// Generated by elegant-router
|
|
||||||
// Read more: https://github.com/soybeanjs/elegant-router
|
|
||||||
|
|
||||||
import type { RouteRecordRaw, RouteComponent } from 'vue-router';
|
|
||||||
import type { ElegantConstRoute } from '@elegant-router/vue';
|
|
||||||
import type { RouteMap, RouteKey, RoutePath } from '@elegant-router/types';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* transform elegant const routes to vue routes
|
|
||||||
* @param routes elegant const routes
|
|
||||||
* @param layouts layout components
|
|
||||||
* @param views view components
|
|
||||||
*/
|
|
||||||
export function transformElegantRoutesToVueRoutes(
|
|
||||||
routes: ElegantConstRoute[],
|
|
||||||
layouts: Record<string, RouteComponent | (() => Promise<RouteComponent>)>,
|
|
||||||
views: Record<string, RouteComponent | (() => Promise<RouteComponent>)>
|
|
||||||
) {
|
|
||||||
return routes.flatMap(route => transformElegantRouteToVueRoute(route, layouts, views));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* transform elegant route to vue route
|
|
||||||
* @param route elegant const route
|
|
||||||
* @param layouts layout components
|
|
||||||
* @param views view components
|
|
||||||
*/
|
|
||||||
function transformElegantRouteToVueRoute(
|
|
||||||
route: ElegantConstRoute,
|
|
||||||
layouts: Record<string, RouteComponent | (() => Promise<RouteComponent>)>,
|
|
||||||
views: Record<string, RouteComponent | (() => Promise<RouteComponent>)>
|
|
||||||
) {
|
|
||||||
const LAYOUT_PREFIX = 'layout.';
|
|
||||||
const VIEW_PREFIX = 'view.';
|
|
||||||
const ROUTE_DEGREE_SPLITTER = '_';
|
|
||||||
const FIRST_LEVEL_ROUTE_COMPONENT_SPLIT = '$';
|
|
||||||
|
|
||||||
function isLayout(component: string) {
|
|
||||||
return component.startsWith(LAYOUT_PREFIX);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLayoutName(component: string) {
|
|
||||||
const layout = component.replace(LAYOUT_PREFIX, '');
|
|
||||||
|
|
||||||
if(!layouts[layout]) {
|
|
||||||
throw new Error(`Layout component "${layout}" not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isView(component: string) {
|
|
||||||
return component.startsWith(VIEW_PREFIX);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getViewName(component: string) {
|
|
||||||
const view = component.replace(VIEW_PREFIX, '');
|
|
||||||
|
|
||||||
if(!views[view]) {
|
|
||||||
throw new Error(`View component "${view}" not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isFirstLevelRoute(item: ElegantConstRoute) {
|
|
||||||
return !item.name.includes(ROUTE_DEGREE_SPLITTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSingleLevelRoute(item: ElegantConstRoute) {
|
|
||||||
return isFirstLevelRoute(item) && !item.children?.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSingleLevelRouteComponent(component: string) {
|
|
||||||
const [layout, view] = component.split(FIRST_LEVEL_ROUTE_COMPONENT_SPLIT);
|
|
||||||
|
|
||||||
return {
|
|
||||||
layout: getLayoutName(layout),
|
|
||||||
view: getViewName(view)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const vueRoutes: RouteRecordRaw[] = [];
|
|
||||||
|
|
||||||
// add props: true to route
|
|
||||||
if (route.path.includes(':') && !route.props) {
|
|
||||||
route.props = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { name, path, component, children, ...rest } = route;
|
|
||||||
|
|
||||||
const vueRoute = { name, path, ...rest } as RouteRecordRaw;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (component) {
|
|
||||||
if (isSingleLevelRoute(route)) {
|
|
||||||
const { layout, view } = getSingleLevelRouteComponent(component);
|
|
||||||
|
|
||||||
const singleLevelRoute: RouteRecordRaw = {
|
|
||||||
path,
|
|
||||||
component: layouts[layout],
|
|
||||||
meta: {
|
|
||||||
title: route.meta?.title || ''
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
path: '',
|
|
||||||
component: views[view],
|
|
||||||
...rest
|
|
||||||
} as RouteRecordRaw
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
return [singleLevelRoute];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLayout(component)) {
|
|
||||||
const layoutName = getLayoutName(component);
|
|
||||||
|
|
||||||
vueRoute.component = layouts[layoutName];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isView(component)) {
|
|
||||||
const viewName = getViewName(component);
|
|
||||||
|
|
||||||
vueRoute.component = views[viewName];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error(`Error transforming route "${route.name}": ${error.toString()}`);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// add redirect to child
|
|
||||||
if (children?.length && !vueRoute.redirect) {
|
|
||||||
vueRoute.redirect = {
|
|
||||||
name: children[0].name
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (children?.length) {
|
|
||||||
const childRoutes = children.flatMap(child => transformElegantRouteToVueRoute(child, layouts, views));
|
|
||||||
|
|
||||||
if(isFirstLevelRoute(route)) {
|
|
||||||
vueRoute.children = childRoutes;
|
|
||||||
} else {
|
|
||||||
vueRoutes.push(...childRoutes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vueRoutes.unshift(vueRoute);
|
|
||||||
|
|
||||||
return vueRoutes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* map of route name and route path
|
|
||||||
*/
|
|
||||||
const routeMap: RouteMap = {
|
|
||||||
"root": "/",
|
|
||||||
"not-found": "/:pathMatch(.*)*",
|
|
||||||
"403": "/403",
|
|
||||||
"404": "/404",
|
|
||||||
"500": "/500",
|
|
||||||
"home": "/home",
|
|
||||||
"iframe-page": "/iframe-page/:url",
|
|
||||||
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?"
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get route path by route name
|
|
||||||
* @param name route name
|
|
||||||
*/
|
|
||||||
export function getRoutePath<T extends RouteKey>(name: T) {
|
|
||||||
return routeMap[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get route name by route path
|
|
||||||
* @param path route path
|
|
||||||
*/
|
|
||||||
export function getRouteName(path: RoutePath) {
|
|
||||||
const routeEntries = Object.entries(routeMap) as [RouteKey, RoutePath][];
|
|
||||||
|
|
||||||
const routeName: RouteKey | null = routeEntries.find(([, routePath]) => routePath === path)?.[0] || null;
|
|
||||||
|
|
||||||
return routeName;
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
import type { App } from 'vue';
|
import type { App } from 'vue';
|
||||||
import {
|
import { createMemoryHistory, createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
|
||||||
type RouterHistory,
|
import type { RouterHistory } from 'vue-router';
|
||||||
createMemoryHistory,
|
|
||||||
createRouter,
|
|
||||||
createWebHashHistory,
|
|
||||||
createWebHistory
|
|
||||||
} from 'vue-router';
|
|
||||||
import { createBuiltinVueRoutes } from './routes/builtin';
|
|
||||||
import { createRouterGuard } from './guard';
|
import { createRouterGuard } from './guard';
|
||||||
|
|
||||||
const { VITE_ROUTER_HISTORY_MODE = 'history', VITE_BASE_URL } = import.meta.env;
|
const { VITE_ROUTER_HISTORY_MODE = 'history', VITE_BASE_URL } = import.meta.env;
|
||||||
@@ -19,7 +13,7 @@ const historyCreatorMap: Record<Env.RouterHistoryMode, (base?: string) => Router
|
|||||||
|
|
||||||
export const router = createRouter({
|
export const router = createRouter({
|
||||||
history: historyCreatorMap[VITE_ROUTER_HISTORY_MODE](VITE_BASE_URL),
|
history: historyCreatorMap[VITE_ROUTER_HISTORY_MODE](VITE_BASE_URL),
|
||||||
routes: createBuiltinVueRoutes()
|
routes: []
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Setup Vue Router */
|
/** Setup Vue Router */
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import type { CustomRoute } from '@elegant-router/types';
|
|
||||||
import { layouts, views } from '../elegant/imports';
|
|
||||||
import { getRoutePath, transformElegantRoutesToVueRoutes } from '../elegant/transform';
|
|
||||||
|
|
||||||
export const ROOT_ROUTE: CustomRoute = {
|
|
||||||
name: 'root',
|
|
||||||
path: '/',
|
|
||||||
redirect: getRoutePath(import.meta.env.VITE_ROUTE_HOME) || '/home',
|
|
||||||
meta: {
|
|
||||||
title: 'root',
|
|
||||||
constant: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const NOT_FOUND_ROUTE: CustomRoute = {
|
|
||||||
name: 'not-found',
|
|
||||||
path: '/:pathMatch(.*)*',
|
|
||||||
component: 'layout.blank$view.404',
|
|
||||||
meta: {
|
|
||||||
title: 'not-found',
|
|
||||||
constant: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** builtin routes, it must be constant and setup in vue-router */
|
|
||||||
const builtinRoutes: CustomRoute[] = [ROOT_ROUTE, NOT_FOUND_ROUTE];
|
|
||||||
|
|
||||||
/** create builtin vue routes */
|
|
||||||
export function createBuiltinVueRoutes() {
|
|
||||||
return transformElegantRoutesToVueRoutes(builtinRoutes, layouts, views);
|
|
||||||
}
|
|
||||||
@@ -1,40 +1,39 @@
|
|||||||
import type { CustomRoute, ElegantConstRoute, ElegantRoute } from '@elegant-router/types';
|
import type { AutoRouterRedirect, AutoRouterRoute } from '@elegant-router/types';
|
||||||
import { generatedRoutes } from '../elegant/routes';
|
import { routes } from '../_generated/routes';
|
||||||
import { layouts, views } from '../elegant/imports';
|
import { layouts, views } from '../_generated/imports';
|
||||||
import { transformElegantRoutesToVueRoutes } from '../elegant/transform';
|
import { transformToVueRoutes } from '../_generated/transformer';
|
||||||
|
|
||||||
/**
|
|
||||||
* custom routes
|
|
||||||
*
|
|
||||||
* @link https://github.com/soybeanjs/elegant-router?tab=readme-ov-file#custom-route
|
|
||||||
*/
|
|
||||||
const customRoutes: CustomRoute[] = [];
|
|
||||||
|
|
||||||
/** create routes when the auth route mode is static */
|
/** create routes when the auth route mode is static */
|
||||||
export function createStaticRoutes() {
|
export function createStaticRoutes() {
|
||||||
const constantRoutes: ElegantRoute[] = [];
|
const constantRoutes: AutoRouterRoute[] = [];
|
||||||
|
const authRoutes: AutoRouterRoute[] = [];
|
||||||
|
|
||||||
const authRoutes: ElegantRoute[] = [];
|
let rootRoute: AutoRouterRedirect | undefined;
|
||||||
|
|
||||||
[...customRoutes, ...generatedRoutes].forEach(item => {
|
routes.forEach(item => {
|
||||||
if (item.meta?.constant) {
|
if (item.meta?.constant) {
|
||||||
constantRoutes.push(item);
|
constantRoutes.push(item);
|
||||||
} else {
|
} else {
|
||||||
authRoutes.push(item);
|
authRoutes.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.name === 'Root') {
|
||||||
|
rootRoute = item as AutoRouterRedirect;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
constantRoutes,
|
constantRoutes,
|
||||||
authRoutes
|
authRoutes,
|
||||||
|
rootRoute: rootRoute as AutoRouterRedirect
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get auth vue routes
|
* Get auth vue routes
|
||||||
*
|
*
|
||||||
* @param routes Elegant routes
|
* @param authRoutes Elegant routes
|
||||||
*/
|
*/
|
||||||
export function getAuthVueRoutes(routes: ElegantConstRoute[]) {
|
export function getAuthVueRoutes(authRoutes: AutoRouterRoute[]) {
|
||||||
return transformElegantRoutesToVueRoutes(routes, layouts, views);
|
return transformToVueRoutes(authRoutes, layouts, views);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,11 +46,8 @@ export const useAppStore = defineStore(SetupStoreId.App, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setReloadFlag(true);
|
setReloadFlag(true);
|
||||||
|
|
||||||
if (themeStore.resetCacheStrategy === 'refresh') {
|
|
||||||
routeStore.resetRouteCache();
|
routeStore.resetRouteCache();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const locale = ref<App.I18n.LangType>(localStg.get('lang') || 'zh-CN');
|
const locale = ref<App.I18n.LangType>(localStg.get('lang') || 'zh-CN');
|
||||||
|
|
||||||
|
|||||||
@@ -112,11 +112,9 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
|
|||||||
await switchRouteByTab(nextTab);
|
await switchRouteByTab(nextTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset route cache if cache strategy is close
|
// reset route cache
|
||||||
if (themeStore.resetCacheStrategy === 'close') {
|
|
||||||
routeStore.resetRouteCache(removedTabRouteKey);
|
routeStore.resetRouteCache(removedTabRouteKey);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** remove active tab */
|
/** remove active tab */
|
||||||
async function removeActiveTab() {
|
async function removeActiveTab() {
|
||||||
@@ -147,11 +145,9 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
|
|||||||
const tabsToRemove = tabs.value.filter(tab => !remainTabIds.includes(tab.id));
|
const tabsToRemove = tabs.value.filter(tab => !remainTabIds.includes(tab.id));
|
||||||
const routeKeysToReset: RouteKey[] = [];
|
const routeKeysToReset: RouteKey[] = [];
|
||||||
|
|
||||||
if (themeStore.resetCacheStrategy === 'close') {
|
|
||||||
for (const tab of tabsToRemove) {
|
for (const tab of tabsToRemove) {
|
||||||
routeKeysToReset.push(tab.routeKey);
|
routeKeysToReset.push(tab.routeKey);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const removedTabsIds = tabsToRemove.map(tab => tab.id);
|
const removedTabsIds = tabsToRemove.map(tab => tab.id);
|
||||||
|
|
||||||
|
|||||||
89
src/theme/preset/azir.json
Normal file
89
src/theme/preset/azir.json
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"name": "Azir's Preset",
|
||||||
|
"desc": "It is a cold and elegant preset that Azir likes",
|
||||||
|
"i18nkey": "theme.appearance.preset.azir",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"themeScheme": "light",
|
||||||
|
"grayscale": false,
|
||||||
|
"colourWeakness": false,
|
||||||
|
"recommendColor": true,
|
||||||
|
"themeColor": "#78a878",
|
||||||
|
"otherColor": {
|
||||||
|
"info": "#89b989",
|
||||||
|
"success": "#99c299",
|
||||||
|
"warning": "#d4bb9d",
|
||||||
|
"error": "#c49a9a"
|
||||||
|
},
|
||||||
|
"isInfoFollowPrimary": true,
|
||||||
|
"layout": {
|
||||||
|
"mode": "vertical-mix",
|
||||||
|
"scrollMode": "wrapper"
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"animate": true,
|
||||||
|
"animateMode": "zoom-fade"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"height": 64,
|
||||||
|
"breadcrumb": {
|
||||||
|
"visible": true,
|
||||||
|
"showIcon": true
|
||||||
|
},
|
||||||
|
"multilingual": {
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
"globalSearch": {
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tab": {
|
||||||
|
"visible": true,
|
||||||
|
"cache": true,
|
||||||
|
"height": 48,
|
||||||
|
"mode": "chrome"
|
||||||
|
},
|
||||||
|
"fixedHeaderAndTab": true,
|
||||||
|
"sider": {
|
||||||
|
"inverted": false,
|
||||||
|
"width": 220,
|
||||||
|
"collapsedWidth": 64,
|
||||||
|
"mixWidth": 90,
|
||||||
|
"mixCollapsedWidth": 64,
|
||||||
|
"mixChildMenuWidth": 200
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"visible": true,
|
||||||
|
"fixed": true,
|
||||||
|
"height": 56,
|
||||||
|
"right": true
|
||||||
|
},
|
||||||
|
"watermark": {
|
||||||
|
"visible": false,
|
||||||
|
"text": "SoybeanAdmin",
|
||||||
|
"enableUserName": false,
|
||||||
|
"enableTime": true,
|
||||||
|
"timeFormat": "YYYY-MM-DD HH:mm:ss"
|
||||||
|
},
|
||||||
|
"tokens": {
|
||||||
|
"light": {
|
||||||
|
"colors": {
|
||||||
|
"container": "rgb(255, 255, 255)",
|
||||||
|
"layout": "rgb(247, 250, 252)",
|
||||||
|
"inverted": "rgb(0, 20, 40)",
|
||||||
|
"base-text": "rgb(31, 31, 31)"
|
||||||
|
},
|
||||||
|
"boxShadow": {
|
||||||
|
"header": "0 1px 2px rgb(0, 21, 41, 0.08)",
|
||||||
|
"sider": "2px 0 8px 0 rgb(29, 35, 41, 0.05)",
|
||||||
|
"tab": "0 1px 2px rgb(0, 21, 41, 0.08)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"colors": {
|
||||||
|
"container": "rgb(28, 28, 28)",
|
||||||
|
"layout": "rgb(18, 18, 18)",
|
||||||
|
"base-text": "rgb(224, 224, 224)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/theme/preset/compact.json
Normal file
89
src/theme/preset/compact.json
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"name": "Compact Preset",
|
||||||
|
"desc": "Compact layout preset for small screens",
|
||||||
|
"i18nkey": "theme.appearance.preset.compact",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"themeScheme": "light",
|
||||||
|
"grayscale": false,
|
||||||
|
"colourWeakness": false,
|
||||||
|
"recommendColor": false,
|
||||||
|
"themeColor": "#646cff",
|
||||||
|
"otherColor": {
|
||||||
|
"info": "#2080f0",
|
||||||
|
"success": "#52c41a",
|
||||||
|
"warning": "#faad14",
|
||||||
|
"error": "#f5222d"
|
||||||
|
},
|
||||||
|
"isInfoFollowPrimary": true,
|
||||||
|
"layout": {
|
||||||
|
"mode": "vertical",
|
||||||
|
"scrollMode": "content"
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"animate": true,
|
||||||
|
"animateMode": "fade-slide"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"height": 48,
|
||||||
|
"breadcrumb": {
|
||||||
|
"visible": true,
|
||||||
|
"showIcon": true
|
||||||
|
},
|
||||||
|
"multilingual": {
|
||||||
|
"visible": false
|
||||||
|
},
|
||||||
|
"globalSearch": {
|
||||||
|
"visible": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tab": {
|
||||||
|
"visible": true,
|
||||||
|
"cache": true,
|
||||||
|
"height": 36,
|
||||||
|
"mode": "button"
|
||||||
|
},
|
||||||
|
"fixedHeaderAndTab": true,
|
||||||
|
"sider": {
|
||||||
|
"inverted": false,
|
||||||
|
"width": 180,
|
||||||
|
"collapsedWidth": 48,
|
||||||
|
"mixWidth": 80,
|
||||||
|
"mixCollapsedWidth": 48,
|
||||||
|
"mixChildMenuWidth": 180
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"visible": false,
|
||||||
|
"fixed": false,
|
||||||
|
"height": 40,
|
||||||
|
"right": true
|
||||||
|
},
|
||||||
|
"watermark": {
|
||||||
|
"visible": false,
|
||||||
|
"text": "SoybeanAdmin",
|
||||||
|
"enableUserName": false,
|
||||||
|
"enableTime": false,
|
||||||
|
"timeFormat": "YYYY-MM-DD HH:mm"
|
||||||
|
},
|
||||||
|
"tokens": {
|
||||||
|
"light": {
|
||||||
|
"colors": {
|
||||||
|
"container": "rgb(255, 255, 255)",
|
||||||
|
"layout": "rgb(247, 250, 252)",
|
||||||
|
"inverted": "rgb(0, 20, 40)",
|
||||||
|
"base-text": "rgb(31, 31, 31)"
|
||||||
|
},
|
||||||
|
"boxShadow": {
|
||||||
|
"header": "0 1px 2px rgb(0, 21, 41, 0.08)",
|
||||||
|
"sider": "2px 0 8px 0 rgb(29, 35, 41, 0.05)",
|
||||||
|
"tab": "0 1px 2px rgb(0, 21, 41, 0.08)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"colors": {
|
||||||
|
"container": "rgb(28, 28, 28)",
|
||||||
|
"layout": "rgb(18, 18, 18)",
|
||||||
|
"base-text": "rgb(224, 224, 224)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/theme/preset/dark.json
Normal file
89
src/theme/preset/dark.json
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"name": "Dark Preset",
|
||||||
|
"desc": "Dark theme preset for night time usage",
|
||||||
|
"i18nkey": "theme.appearance.preset.dark",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"themeScheme": "dark",
|
||||||
|
"grayscale": false,
|
||||||
|
"colourWeakness": false,
|
||||||
|
"recommendColor": false,
|
||||||
|
"themeColor": "#409eff",
|
||||||
|
"otherColor": {
|
||||||
|
"info": "#2080f0",
|
||||||
|
"success": "#52c41a",
|
||||||
|
"warning": "#faad14",
|
||||||
|
"error": "#f5222d"
|
||||||
|
},
|
||||||
|
"isInfoFollowPrimary": true,
|
||||||
|
"layout": {
|
||||||
|
"mode": "vertical",
|
||||||
|
"scrollMode": "content"
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"animate": true,
|
||||||
|
"animateMode": "fade-slide"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"height": 56,
|
||||||
|
"breadcrumb": {
|
||||||
|
"visible": true,
|
||||||
|
"showIcon": true
|
||||||
|
},
|
||||||
|
"multilingual": {
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
"globalSearch": {
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tab": {
|
||||||
|
"visible": true,
|
||||||
|
"cache": true,
|
||||||
|
"height": 44,
|
||||||
|
"mode": "chrome"
|
||||||
|
},
|
||||||
|
"fixedHeaderAndTab": true,
|
||||||
|
"sider": {
|
||||||
|
"inverted": true,
|
||||||
|
"width": 220,
|
||||||
|
"collapsedWidth": 64,
|
||||||
|
"mixWidth": 90,
|
||||||
|
"mixCollapsedWidth": 64,
|
||||||
|
"mixChildMenuWidth": 200
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"visible": true,
|
||||||
|
"fixed": false,
|
||||||
|
"height": 48,
|
||||||
|
"right": true
|
||||||
|
},
|
||||||
|
"watermark": {
|
||||||
|
"visible": false,
|
||||||
|
"text": "SoybeanAdmin",
|
||||||
|
"enableUserName": false,
|
||||||
|
"enableTime": false,
|
||||||
|
"timeFormat": "YYYY-MM-DD HH:mm"
|
||||||
|
},
|
||||||
|
"tokens": {
|
||||||
|
"light": {
|
||||||
|
"colors": {
|
||||||
|
"container": "rgb(255, 255, 255)",
|
||||||
|
"layout": "rgb(247, 250, 252)",
|
||||||
|
"inverted": "rgb(0, 20, 40)",
|
||||||
|
"base-text": "rgb(31, 31, 31)"
|
||||||
|
},
|
||||||
|
"boxShadow": {
|
||||||
|
"header": "0 1px 2px rgb(0, 21, 41, 0.08)",
|
||||||
|
"sider": "2px 0 8px 0 rgb(29, 35, 41, 0.05)",
|
||||||
|
"tab": "0 1px 2px rgb(0, 21, 41, 0.08)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"colors": {
|
||||||
|
"container": "rgb(28, 28, 28)",
|
||||||
|
"layout": "rgb(18, 18, 18)",
|
||||||
|
"base-text": "rgb(224, 224, 224)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/theme/preset/default.json
Normal file
89
src/theme/preset/default.json
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"desc": "Default theme preset with balanced settings",
|
||||||
|
"i18nkey": "theme.appearance.preset.default",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"themeScheme": "light",
|
||||||
|
"grayscale": false,
|
||||||
|
"colourWeakness": false,
|
||||||
|
"recommendColor": false,
|
||||||
|
"themeColor": "#646cff",
|
||||||
|
"otherColor": {
|
||||||
|
"info": "#2080f0",
|
||||||
|
"success": "#52c41a",
|
||||||
|
"warning": "#faad14",
|
||||||
|
"error": "#f5222d"
|
||||||
|
},
|
||||||
|
"isInfoFollowPrimary": true,
|
||||||
|
"layout": {
|
||||||
|
"mode": "vertical",
|
||||||
|
"scrollMode": "content"
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"animate": true,
|
||||||
|
"animateMode": "fade-slide"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"height": 56,
|
||||||
|
"breadcrumb": {
|
||||||
|
"visible": true,
|
||||||
|
"showIcon": true
|
||||||
|
},
|
||||||
|
"multilingual": {
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
"globalSearch": {
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tab": {
|
||||||
|
"visible": true,
|
||||||
|
"cache": true,
|
||||||
|
"height": 44,
|
||||||
|
"mode": "chrome"
|
||||||
|
},
|
||||||
|
"fixedHeaderAndTab": true,
|
||||||
|
"sider": {
|
||||||
|
"inverted": false,
|
||||||
|
"width": 220,
|
||||||
|
"collapsedWidth": 64,
|
||||||
|
"mixWidth": 90,
|
||||||
|
"mixCollapsedWidth": 64,
|
||||||
|
"mixChildMenuWidth": 200
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"visible": true,
|
||||||
|
"fixed": false,
|
||||||
|
"height": 48,
|
||||||
|
"right": true
|
||||||
|
},
|
||||||
|
"watermark": {
|
||||||
|
"visible": false,
|
||||||
|
"text": "SoybeanAdmin",
|
||||||
|
"enableUserName": false,
|
||||||
|
"enableTime": false,
|
||||||
|
"timeFormat": "YYYY-MM-DD HH:mm"
|
||||||
|
},
|
||||||
|
"tokens": {
|
||||||
|
"light": {
|
||||||
|
"colors": {
|
||||||
|
"container": "rgb(255, 255, 255)",
|
||||||
|
"layout": "rgb(247, 250, 252)",
|
||||||
|
"inverted": "rgb(0, 20, 40)",
|
||||||
|
"base-text": "rgb(31, 31, 31)"
|
||||||
|
},
|
||||||
|
"boxShadow": {
|
||||||
|
"header": "0 1px 2px rgb(0, 21, 41, 0.08)",
|
||||||
|
"sider": "2px 0 8px 0 rgb(29, 35, 41, 0.05)",
|
||||||
|
"tab": "0 1px 2px rgb(0, 21, 41, 0.08)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"colors": {
|
||||||
|
"container": "rgb(28, 28, 28)",
|
||||||
|
"layout": "rgb(18, 18, 18)",
|
||||||
|
"base-text": "rgb(224, 224, 224)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,6 @@ export const themeSettings: App.Theme.ThemeSetting = {
|
|||||||
error: '#f5222d'
|
error: '#f5222d'
|
||||||
},
|
},
|
||||||
isInfoFollowPrimary: true,
|
isInfoFollowPrimary: true,
|
||||||
resetCacheStrategy: 'close',
|
|
||||||
layout: {
|
layout: {
|
||||||
mode: 'vertical',
|
mode: 'vertical',
|
||||||
scrollMode: 'content'
|
scrollMode: 'content'
|
||||||
|
|||||||
70
src/typings/api/common.d.ts
vendored
70
src/typings/api/common.d.ts
vendored
@@ -5,46 +5,38 @@
|
|||||||
*/
|
*/
|
||||||
declare namespace Api {
|
declare namespace Api {
|
||||||
namespace Common {
|
namespace Common {
|
||||||
/** common params of paginating */
|
interface CreatedUpdatedDTO {
|
||||||
interface PaginatingCommonParams {
|
/** 创建人 */
|
||||||
/** current page number */
|
createdBy: string;
|
||||||
current: number;
|
/** 创建时间 */
|
||||||
/** page size */
|
createdTime: string;
|
||||||
size: number;
|
/** 更新人 */
|
||||||
/** total count */
|
updatedBy: string;
|
||||||
|
/** 更新时间 */
|
||||||
|
updatedTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CommonRecordDTO extends CreatedUpdatedDTO {
|
||||||
|
/** 主键 */
|
||||||
|
id: number;
|
||||||
|
/**
|
||||||
|
* 启用状态 Enable status
|
||||||
|
*
|
||||||
|
* - "1": 启用
|
||||||
|
* - "2": 禁用
|
||||||
|
*/
|
||||||
|
status: Union.EnableStatus | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PaginationQueryDTO {
|
||||||
|
page: number;
|
||||||
|
pageSize: number;
|
||||||
|
sort: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PaginationDTO extends Pick<PaginationQueryDTO, 'page' | 'pageSize'> {
|
||||||
|
/** 总条数 */
|
||||||
total: number;
|
total: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** common params of paginating query list data */
|
|
||||||
interface PaginatingQueryRecord<T = any> extends PaginatingCommonParams {
|
|
||||||
records: T[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** common search params of table */
|
|
||||||
type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'current' | 'size'>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* enable status
|
|
||||||
*
|
|
||||||
* - "1": enabled
|
|
||||||
* - "2": disabled
|
|
||||||
*/
|
|
||||||
type EnableStatus = '1' | '2';
|
|
||||||
|
|
||||||
/** common record */
|
|
||||||
type CommonRecord<T = any> = {
|
|
||||||
/** record id */
|
|
||||||
id: number;
|
|
||||||
/** record creator */
|
|
||||||
createBy: string;
|
|
||||||
/** record create time */
|
|
||||||
createTime: string;
|
|
||||||
/** record updater */
|
|
||||||
updateBy: string;
|
|
||||||
/** record update time */
|
|
||||||
updateTime: string;
|
|
||||||
/** record status */
|
|
||||||
status: EnableStatus | null;
|
|
||||||
} & T;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
src/typings/api/menu.d.ts
vendored
Normal file
42
src/typings/api/menu.d.ts
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
declare namespace Api {
|
||||||
|
namespace Menu {
|
||||||
|
type RouteKey = import('@elegant-router/types').RouteKey;
|
||||||
|
|
||||||
|
interface Menu extends Common.CommonRecordDTO {
|
||||||
|
/** 菜单名称 */
|
||||||
|
name: string;
|
||||||
|
/** 菜单类型 */
|
||||||
|
menuType: Union.MenuType;
|
||||||
|
/** 菜单描述 */
|
||||||
|
description?: string | null;
|
||||||
|
/** 菜单图标 */
|
||||||
|
icon?: string | null;
|
||||||
|
/** 菜单图标类型 */
|
||||||
|
iconType?: string | null;
|
||||||
|
/** 父级菜单ID */
|
||||||
|
parentId?: number | null;
|
||||||
|
/** 菜单排序 */
|
||||||
|
order?: number | null;
|
||||||
|
/** 是否隐藏 */
|
||||||
|
isHidden?: boolean | null;
|
||||||
|
/** iframe 页面URL */
|
||||||
|
iframeUrl?: string | null;
|
||||||
|
/** 链接 */
|
||||||
|
href?: string | null;
|
||||||
|
/** 路由名称 */
|
||||||
|
routeName?: RouteKey | null;
|
||||||
|
/** 路由查询参数 */
|
||||||
|
routeQueries?: Record<string, string | number | boolean> | null;
|
||||||
|
/** 路由路径参数 */
|
||||||
|
routeParams?: Record<string, string | number | boolean> | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MenuListDTO extends Common.PaginationDTO {
|
||||||
|
list: Menu[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MenuTreeDTO extends Menu {
|
||||||
|
children?: MenuTreeDTO[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/typings/api/route.d.ts
vendored
39
src/typings/api/route.d.ts
vendored
@@ -1,19 +1,34 @@
|
|||||||
declare namespace Api {
|
declare namespace Api {
|
||||||
/**
|
|
||||||
* namespace Route
|
|
||||||
*
|
|
||||||
* backend api module: "route"
|
|
||||||
*/
|
|
||||||
namespace Route {
|
namespace Route {
|
||||||
type ElegantConstRoute = import('@elegant-router/types').ElegantConstRoute;
|
interface RouteDTO extends Common.CommonRecordDTO {
|
||||||
|
/** 路由路径 */
|
||||||
interface MenuRoute extends ElegantConstRoute {
|
path: string;
|
||||||
id: string;
|
/** 路由名称 */
|
||||||
|
name: string;
|
||||||
|
/** 路由布局 */
|
||||||
|
layout: string;
|
||||||
|
/** 路由组件 */
|
||||||
|
component: string;
|
||||||
|
/** 重定向路径 */
|
||||||
|
redirect?: string | null;
|
||||||
|
/** 路由图标 */
|
||||||
|
icon?: string | null;
|
||||||
|
/** 路由图标类型 */
|
||||||
|
iconType?: string | null;
|
||||||
|
/** 是否缓存 */
|
||||||
|
isCache?: boolean | null;
|
||||||
|
/** 是否支持多开 */
|
||||||
|
isMultiple?: boolean | null;
|
||||||
|
/** 固定索引 */
|
||||||
|
fixedIndex?: number | null;
|
||||||
|
/** 是否需要权限 */
|
||||||
|
requireAuth?: boolean | null;
|
||||||
|
/** 路由属性 */
|
||||||
|
props?: Record<string, string | number | boolean> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserRoute {
|
interface RouteListDTO extends Common.PaginationDTO {
|
||||||
routes: MenuRoute[];
|
list: RouteDTO[];
|
||||||
home: import('@elegant-router/types').LastLevelRouteKey;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
src/typings/api/union.d.ts
vendored
Normal file
31
src/typings/api/union.d.ts
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
declare namespace Api {
|
||||||
|
namespace Union {
|
||||||
|
/**
|
||||||
|
* 是否 Yes or No
|
||||||
|
*
|
||||||
|
* - "Y": yes
|
||||||
|
* - "N": no
|
||||||
|
*/
|
||||||
|
type YesOrNo = 'Y' | 'N';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用状态 Enable status
|
||||||
|
*
|
||||||
|
* - "1": 启用
|
||||||
|
* - "2": 禁用
|
||||||
|
*/
|
||||||
|
type EnableStatus = '1' | '2';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单类型
|
||||||
|
*
|
||||||
|
* - "directory": 目录
|
||||||
|
* - "menu": 菜单
|
||||||
|
* - "iframe": iframe 页面(iframe 嵌入页面)
|
||||||
|
* - "link": 链接(跳转外部链接)
|
||||||
|
* - "page": 页面(不展示在菜单中,可用于作为菜单类型的子菜单展示在面包屑中)
|
||||||
|
* - "other": 其他(作用自行定义)
|
||||||
|
*/
|
||||||
|
type MenuType = 'page' | 'directory' | 'menu' | 'iframe' | 'link' | 'other';
|
||||||
|
}
|
||||||
|
}
|
||||||
112
src/typings/app.d.ts
vendored
112
src/typings/app.d.ts
vendored
@@ -20,8 +20,6 @@ declare namespace App {
|
|||||||
otherColor: OtherColor;
|
otherColor: OtherColor;
|
||||||
/** Whether info color is followed by the primary color */
|
/** Whether info color is followed by the primary color */
|
||||||
isInfoFollowPrimary: boolean;
|
isInfoFollowPrimary: boolean;
|
||||||
/** Reset cache strategy */
|
|
||||||
resetCacheStrategy: UnionKey.ResetCacheStrategy;
|
|
||||||
/** Layout */
|
/** Layout */
|
||||||
layout: {
|
layout: {
|
||||||
/** Layout mode */
|
/** Layout mode */
|
||||||
@@ -181,9 +179,9 @@ declare namespace App {
|
|||||||
type VNode = import('vue').VNode;
|
type VNode = import('vue').VNode;
|
||||||
type RouteLocationNormalizedLoaded = import('vue-router').RouteLocationNormalizedLoaded;
|
type RouteLocationNormalizedLoaded = import('vue-router').RouteLocationNormalizedLoaded;
|
||||||
type RouteKey = import('@elegant-router/types').RouteKey;
|
type RouteKey = import('@elegant-router/types').RouteKey;
|
||||||
type RouteMap = import('@elegant-router/types').RouteMap;
|
type RoutePathMap = import('@elegant-router/types').RoutePathMap;
|
||||||
type RoutePath = import('@elegant-router/types').RoutePath;
|
type RoutePath = import('@elegant-router/types').RoutePath;
|
||||||
type LastLevelRouteKey = import('@elegant-router/types').LastLevelRouteKey;
|
type RouteFileKey = import('@elegant-router/types').RouteFileKey;
|
||||||
|
|
||||||
/** The router push options */
|
/** The router push options */
|
||||||
type RouterPushOptions = {
|
type RouterPushOptions = {
|
||||||
@@ -201,77 +199,25 @@ declare namespace App {
|
|||||||
showMenu?: boolean;
|
showMenu?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MenuType = 'page' | 'directory' | 'menu' | 'iframe' | 'link' | 'button' | 'other';
|
||||||
|
|
||||||
/** The global menu */
|
/** The global menu */
|
||||||
type Menu = {
|
interface Menu {
|
||||||
/**
|
id: number;
|
||||||
* The menu key
|
name: string;
|
||||||
*
|
menuType: MenuType;
|
||||||
* Equal to the route key
|
description?: string | null;
|
||||||
*/
|
icon?: string | null;
|
||||||
key: string;
|
iconType?: string | null;
|
||||||
/** The menu label */
|
parentId?: number | null;
|
||||||
label: string;
|
order?: number | null;
|
||||||
/** The menu i18n key */
|
isHidden?: boolean | null;
|
||||||
i18nKey?: I18n.I18nKey | null;
|
iframeUrl?: string | null;
|
||||||
/** The route key */
|
href?: string | null;
|
||||||
routeKey: RouteKey;
|
routeName?: RouteKey | null;
|
||||||
/** The route path */
|
routeQueries?: Record<string, string | number | boolean> | null;
|
||||||
routePath: RoutePath;
|
routeParams?: Record<string, string | number | boolean> | null;
|
||||||
/** The menu icon */
|
}
|
||||||
icon?: () => VNode;
|
|
||||||
/** The menu children */
|
|
||||||
children?: Menu[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type Breadcrumb = Omit<Menu, 'children'> & {
|
|
||||||
options?: Breadcrumb[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Tab route */
|
|
||||||
type TabRoute = Pick<RouteLocationNormalizedLoaded, 'name' | 'path' | 'meta'> &
|
|
||||||
Partial<Pick<RouteLocationNormalizedLoaded, 'fullPath' | 'query' | 'matched'>>;
|
|
||||||
|
|
||||||
/** The global tab */
|
|
||||||
type Tab = {
|
|
||||||
/** The tab id */
|
|
||||||
id: string;
|
|
||||||
/** The tab label */
|
|
||||||
label: string;
|
|
||||||
/**
|
|
||||||
* The new tab label
|
|
||||||
*
|
|
||||||
* If set, the tab label will be replaced by this value
|
|
||||||
*/
|
|
||||||
newLabel?: string;
|
|
||||||
/**
|
|
||||||
* The old tab label
|
|
||||||
*
|
|
||||||
* when reset the tab label, the tab label will be replaced by this value
|
|
||||||
*/
|
|
||||||
oldLabel?: string;
|
|
||||||
/** The tab route key */
|
|
||||||
routeKey: LastLevelRouteKey;
|
|
||||||
/** The tab route path */
|
|
||||||
routePath: RouteMap[LastLevelRouteKey];
|
|
||||||
/** The tab route full path */
|
|
||||||
fullPath: string;
|
|
||||||
/** The tab fixed index */
|
|
||||||
fixedIndex?: number | null;
|
|
||||||
/**
|
|
||||||
* Tab icon
|
|
||||||
*
|
|
||||||
* Iconify icon
|
|
||||||
*/
|
|
||||||
icon?: string;
|
|
||||||
/**
|
|
||||||
* Tab local icon
|
|
||||||
*
|
|
||||||
* Local icon
|
|
||||||
*/
|
|
||||||
localIcon?: string;
|
|
||||||
/** I18n key */
|
|
||||||
i18nKey?: I18n.I18nKey | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Form rule */
|
/** Form rule */
|
||||||
type FormRule = import('naive-ui').FormItemRule;
|
type FormRule = import('naive-ui').FormItemRule;
|
||||||
@@ -367,6 +313,7 @@ declare namespace App {
|
|||||||
appearance: string;
|
appearance: string;
|
||||||
layout: string;
|
layout: string;
|
||||||
general: string;
|
general: string;
|
||||||
|
preset: string;
|
||||||
};
|
};
|
||||||
appearance: {
|
appearance: {
|
||||||
themeSchema: { title: string } & Record<UnionKey.ThemeScheme, string>;
|
themeSchema: { title: string } & Record<UnionKey.ThemeScheme, string>;
|
||||||
@@ -375,9 +322,20 @@ declare namespace App {
|
|||||||
themeColor: {
|
themeColor: {
|
||||||
title: string;
|
title: string;
|
||||||
followPrimary: string;
|
followPrimary: string;
|
||||||
} & Theme.ThemeColor;
|
} & Record<Theme.ThemeColorKey, string>;
|
||||||
recommendColor: string;
|
recommendColor: string;
|
||||||
recommendColorDesc: string;
|
recommendColorDesc: string;
|
||||||
|
preset: {
|
||||||
|
title: string;
|
||||||
|
apply: string;
|
||||||
|
applySuccess: string;
|
||||||
|
[key: string]:
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
desc: string;
|
||||||
|
}
|
||||||
|
| string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
layout: {
|
layout: {
|
||||||
layoutMode: { title: string } & Record<UnionKey.ThemeLayoutMode, string> & {
|
layoutMode: { title: string } & Record<UnionKey.ThemeLayoutMode, string> & {
|
||||||
@@ -387,6 +345,7 @@ declare namespace App {
|
|||||||
title: string;
|
title: string;
|
||||||
visible: string;
|
visible: string;
|
||||||
cache: string;
|
cache: string;
|
||||||
|
cacheTip: string;
|
||||||
height: string;
|
height: string;
|
||||||
mode: { title: string } & Record<UnionKey.ThemeTabMode, string>;
|
mode: { title: string } & Record<UnionKey.ThemeTabMode, string>;
|
||||||
};
|
};
|
||||||
@@ -416,14 +375,13 @@ declare namespace App {
|
|||||||
};
|
};
|
||||||
content: {
|
content: {
|
||||||
title: string;
|
title: string;
|
||||||
scrollMode: { title: string } & Record<UnionKey.ThemeScrollMode, string>;
|
scrollMode: { title: string; tip: string } & Record<UnionKey.ThemeScrollMode, string>;
|
||||||
page: {
|
page: {
|
||||||
animate: string;
|
animate: string;
|
||||||
mode: { title: string } & Record<UnionKey.ThemePageAnimateMode, string>;
|
mode: { title: string } & Record<UnionKey.ThemePageAnimateMode, string>;
|
||||||
};
|
};
|
||||||
fixedHeaderAndTab: string;
|
fixedHeaderAndTab: string;
|
||||||
};
|
};
|
||||||
resetCacheStrategy: { title: string } & Record<UnionKey.ResetCacheStrategy, string>;
|
|
||||||
};
|
};
|
||||||
general: {
|
general: {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
4
src/typings/components.d.ts
vendored
4
src/typings/components.d.ts
vendored
@@ -25,11 +25,13 @@ declare module 'vue' {
|
|||||||
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
||||||
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
|
||||||
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
|
||||||
|
IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default']
|
||||||
IconUilSearch: typeof import('~icons/uil/search')['default']
|
IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||||
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||||
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
||||||
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
|
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
|
||||||
NAlert: typeof import('naive-ui')['NAlert']
|
NAlert: typeof import('naive-ui')['NAlert']
|
||||||
|
NBadge: typeof import('naive-ui')['NBadge']
|
||||||
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
|
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
|
||||||
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
|
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
|
||||||
NButton: typeof import('naive-ui')['NButton']
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
@@ -56,6 +58,7 @@ declare module 'vue' {
|
|||||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||||
NModal: typeof import('naive-ui')['NModal']
|
NModal: typeof import('naive-ui')['NModal']
|
||||||
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
||||||
|
NPopover: typeof import('naive-ui')['NPopover']
|
||||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||||
NSelect: typeof import('naive-ui')['NSelect']
|
NSelect: typeof import('naive-ui')['NSelect']
|
||||||
NSpace: typeof import('naive-ui')['NSpace']
|
NSpace: typeof import('naive-ui')['NSpace']
|
||||||
@@ -63,6 +66,7 @@ declare module 'vue' {
|
|||||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||||
NTab: typeof import('naive-ui')['NTab']
|
NTab: typeof import('naive-ui')['NTab']
|
||||||
NTabs: typeof import('naive-ui')['NTabs']
|
NTabs: typeof import('naive-ui')['NTabs']
|
||||||
|
NTag: typeof import('naive-ui')['NTag']
|
||||||
NThing: typeof import('naive-ui')['NThing']
|
NThing: typeof import('naive-ui')['NThing']
|
||||||
NTooltip: typeof import('naive-ui')['NTooltip']
|
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||||
NWatermark: typeof import('naive-ui')['NWatermark']
|
NWatermark: typeof import('naive-ui')['NWatermark']
|
||||||
|
|||||||
236
src/typings/elegant-router.d.ts
vendored
236
src/typings/elegant-router.d.ts
vendored
@@ -1,240 +1,100 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
|
/* oxlint-disable */
|
||||||
|
// biome-ignore lint: disable
|
||||||
// Generated by elegant-router
|
// Generated by elegant-router
|
||||||
// Read more: https://github.com/soybeanjs/elegant-router
|
// Read more: https://github.com/soybeanjs/elegant-router
|
||||||
|
|
||||||
declare module "@elegant-router/types" {
|
declare module "@elegant-router/types" {
|
||||||
type ElegantConstRoute = import('@elegant-router/vue').ElegantConstRoute;
|
type RouteRecordSingleView = import("vue-router").RouteRecordSingleView;
|
||||||
|
type RouteRecordRedirect = import("vue-router").RouteRecordRedirect;
|
||||||
|
type RouteComponent = import("vue-router").RouteComponent;
|
||||||
|
|
||||||
|
type Lazy<T> = () => Promise<T>;
|
||||||
|
|
||||||
|
export type RawRouteComponent = RouteComponent | Lazy<RouteComponent>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* route layout
|
* route layout key
|
||||||
*/
|
*/
|
||||||
export type RouteLayout = "base" | "blank";
|
export type RouteLayoutKey = "base" | "blank";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* route map
|
* route path map
|
||||||
*/
|
*/
|
||||||
export type RouteMap = {
|
export type RoutePathMap = {
|
||||||
"root": "/";
|
"Root": "/";
|
||||||
"not-found": "/:pathMatch(.*)*";
|
"NotFound": "/:pathMatch(.*)*";
|
||||||
"403": "/403";
|
"403": "/403";
|
||||||
"404": "/404";
|
"404": "/404";
|
||||||
"500": "/500";
|
"500": "/500";
|
||||||
"home": "/home";
|
"Home": "/home";
|
||||||
"iframe-page": "/iframe-page/:url";
|
"IframeUrl": "/iframe/:url";
|
||||||
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
|
"Login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
|
||||||
|
"ManageMenu": "/manage/menu";
|
||||||
|
"ManageRole": "/manage/role";
|
||||||
|
"ManageRoute": "/manage/route";
|
||||||
|
"ManageUser": "/manage/user";
|
||||||
|
"Wip": "/wip";
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* route key
|
* route key
|
||||||
*/
|
*/
|
||||||
export type RouteKey = keyof RouteMap;
|
export type RouteKey = keyof RoutePathMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* route path
|
* route path
|
||||||
*/
|
*/
|
||||||
export type RoutePath = RouteMap[RouteKey];
|
export type RoutePath = RoutePathMap[RouteKey];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* custom route key
|
* root route key
|
||||||
*/
|
*/
|
||||||
export type CustomRouteKey = Extract<
|
export type RootRouteKey = 'Root';
|
||||||
RouteKey,
|
|
||||||
| "root"
|
|
||||||
| "not-found"
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the generated route key
|
* not found route key
|
||||||
*/
|
*/
|
||||||
export type GeneratedRouteKey = Exclude<RouteKey, CustomRouteKey>;
|
export type NotFoundRouteKey = 'NotFound';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the first level route key, which contain the layout of the route
|
* builtin route key
|
||||||
*/
|
*/
|
||||||
export type FirstLevelRouteKey = Extract<
|
export type BuiltinRouteKey = RootRouteKey | NotFoundRouteKey;
|
||||||
RouteKey,
|
|
||||||
| "403"
|
|
||||||
| "404"
|
|
||||||
| "500"
|
|
||||||
| "home"
|
|
||||||
| "iframe-page"
|
|
||||||
| "login"
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the custom first level route key
|
* reuse route key
|
||||||
*/
|
*/
|
||||||
export type CustomFirstLevelRouteKey = Extract<
|
export type ReuseRouteKey = never;
|
||||||
CustomRouteKey,
|
|
||||||
| "root"
|
|
||||||
| "not-found"
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the last level route key, which has the page file
|
* the route file key, which has it's own file
|
||||||
*/
|
*/
|
||||||
export type LastLevelRouteKey = Extract<
|
export type RouteFileKey = Exclude<RouteKey, BuiltinRouteKey | ReuseRouteKey>;
|
||||||
RouteKey,
|
|
||||||
| "403"
|
|
||||||
| "404"
|
|
||||||
| "500"
|
|
||||||
| "iframe-page"
|
|
||||||
| "login"
|
|
||||||
| "home"
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the custom last level route key
|
* mapped name and path
|
||||||
*/
|
*/
|
||||||
export type CustomLastLevelRouteKey = Extract<
|
type MappedNamePath = {
|
||||||
CustomRouteKey,
|
[K in RouteKey]: { name: K; path: RoutePathMap[K] };
|
||||||
| "root"
|
}[RouteKey];
|
||||||
| "not-found"
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the single level route key
|
* auto router single view
|
||||||
*/
|
*/
|
||||||
export type SingleLevelRouteKey = FirstLevelRouteKey & LastLevelRouteKey;
|
export type AutoRouterSingleView = Omit<RouteRecordSingleView, 'component' | 'name' | 'path'> & {
|
||||||
|
component: RouteFileKey;
|
||||||
|
layout: RouteLayoutKey;
|
||||||
|
} & MappedNamePath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the custom single level route key
|
* auto router redirect
|
||||||
*/
|
*/
|
||||||
export type CustomSingleLevelRouteKey = CustomFirstLevelRouteKey & CustomLastLevelRouteKey;
|
export type AutoRouterRedirect = Omit<RouteRecordRedirect, 'children' | 'name' | 'path'> & MappedNamePath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the first level route key, but not the single level
|
* auto router route
|
||||||
*/
|
*/
|
||||||
export type FirstLevelRouteNotSingleKey = Exclude<FirstLevelRouteKey, SingleLevelRouteKey>;
|
export type AutoRouterRoute = AutoRouterSingleView | AutoRouterRedirect;
|
||||||
|
|
||||||
/**
|
|
||||||
* the custom first level route key, but not the single level
|
|
||||||
*/
|
|
||||||
export type CustomFirstLevelRouteNotSingleKey = Exclude<CustomFirstLevelRouteKey, CustomSingleLevelRouteKey>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the center level route key
|
|
||||||
*/
|
|
||||||
export type CenterLevelRouteKey = Exclude<GeneratedRouteKey, FirstLevelRouteKey | LastLevelRouteKey>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the custom center level route key
|
|
||||||
*/
|
|
||||||
export type CustomCenterLevelRouteKey = Exclude<CustomRouteKey, CustomFirstLevelRouteKey | CustomLastLevelRouteKey>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the center level route key
|
|
||||||
*/
|
|
||||||
type GetChildRouteKey<K extends RouteKey, T extends RouteKey = RouteKey> = T extends `${K}_${infer R}`
|
|
||||||
? R extends `${string}_${string}`
|
|
||||||
? never
|
|
||||||
: T
|
|
||||||
: never;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the single level route
|
|
||||||
*/
|
|
||||||
type SingleLevelRoute<K extends SingleLevelRouteKey = SingleLevelRouteKey> = K extends string
|
|
||||||
? Omit<ElegantConstRoute, 'children'> & {
|
|
||||||
name: K;
|
|
||||||
path: RouteMap[K];
|
|
||||||
component: `layout.${RouteLayout}$view.${K}`;
|
|
||||||
}
|
|
||||||
: never;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the last level route
|
|
||||||
*/
|
|
||||||
type LastLevelRoute<K extends GeneratedRouteKey> = K extends LastLevelRouteKey
|
|
||||||
? Omit<ElegantConstRoute, 'children'> & {
|
|
||||||
name: K;
|
|
||||||
path: RouteMap[K];
|
|
||||||
component: `view.${K}`;
|
|
||||||
}
|
|
||||||
: never;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the center level route
|
|
||||||
*/
|
|
||||||
type CenterLevelRoute<K extends GeneratedRouteKey> = K extends CenterLevelRouteKey
|
|
||||||
? Omit<ElegantConstRoute, 'component'> & {
|
|
||||||
name: K;
|
|
||||||
path: RouteMap[K];
|
|
||||||
children: (CenterLevelRoute<GetChildRouteKey<K>> | LastLevelRoute<GetChildRouteKey<K>>)[];
|
|
||||||
}
|
|
||||||
: never;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the multi level route
|
|
||||||
*/
|
|
||||||
type MultiLevelRoute<K extends FirstLevelRouteNotSingleKey = FirstLevelRouteNotSingleKey> = K extends string
|
|
||||||
? ElegantConstRoute & {
|
|
||||||
name: K;
|
|
||||||
path: RouteMap[K];
|
|
||||||
component: `layout.${RouteLayout}`;
|
|
||||||
children: (CenterLevelRoute<GetChildRouteKey<K>> | LastLevelRoute<GetChildRouteKey<K>>)[];
|
|
||||||
}
|
|
||||||
: never;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the custom first level route
|
|
||||||
*/
|
|
||||||
type CustomSingleLevelRoute<K extends CustomFirstLevelRouteKey = CustomFirstLevelRouteKey> = K extends string
|
|
||||||
? Omit<ElegantConstRoute, 'children'> & {
|
|
||||||
name: K;
|
|
||||||
path: RouteMap[K];
|
|
||||||
component?: `layout.${RouteLayout}$view.${LastLevelRouteKey}`;
|
|
||||||
}
|
|
||||||
: never;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the custom last level route
|
|
||||||
*/
|
|
||||||
type CustomLastLevelRoute<K extends CustomRouteKey> = K extends CustomLastLevelRouteKey
|
|
||||||
? Omit<ElegantConstRoute, 'children'> & {
|
|
||||||
name: K;
|
|
||||||
path: RouteMap[K];
|
|
||||||
component?: `view.${LastLevelRouteKey}`;
|
|
||||||
}
|
|
||||||
: never;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the custom center level route
|
|
||||||
*/
|
|
||||||
type CustomCenterLevelRoute<K extends CustomRouteKey> = K extends CustomCenterLevelRouteKey
|
|
||||||
? Omit<ElegantConstRoute, 'component'> & {
|
|
||||||
name: K;
|
|
||||||
path: RouteMap[K];
|
|
||||||
children: (CustomCenterLevelRoute<GetChildRouteKey<K>> | CustomLastLevelRoute<GetChildRouteKey<K>>)[];
|
|
||||||
}
|
|
||||||
: never;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the custom multi level route
|
|
||||||
*/
|
|
||||||
type CustomMultiLevelRoute<K extends CustomFirstLevelRouteNotSingleKey = CustomFirstLevelRouteNotSingleKey> =
|
|
||||||
K extends string
|
|
||||||
? ElegantConstRoute & {
|
|
||||||
name: K;
|
|
||||||
path: RouteMap[K];
|
|
||||||
component: `layout.${RouteLayout}`;
|
|
||||||
children: (CustomCenterLevelRoute<GetChildRouteKey<K>> | CustomLastLevelRoute<GetChildRouteKey<K>>)[];
|
|
||||||
}
|
|
||||||
: never;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the custom route
|
|
||||||
*/
|
|
||||||
type CustomRoute = CustomSingleLevelRoute | CustomMultiLevelRoute;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the generated route
|
|
||||||
*/
|
|
||||||
type GeneratedRoute = SingleLevelRoute | MultiLevelRoute;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the elegant route
|
|
||||||
*/
|
|
||||||
type ElegantRoute = GeneratedRoute | CustomRoute;
|
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/typings/router.d.ts
vendored
4
src/typings/router.d.ts
vendored
@@ -1,7 +1,7 @@
|
|||||||
import 'vue-router';
|
export {};
|
||||||
|
|
||||||
declare module 'vue-router' {
|
declare module 'vue-router' {
|
||||||
interface RouteMeta {
|
export interface RouteMeta {
|
||||||
/**
|
/**
|
||||||
* Title of the route
|
* Title of the route
|
||||||
*
|
*
|
||||||
|
|||||||
40
src/typings/typed-router.d.ts
vendored
Normal file
40
src/typings/typed-router.d.ts
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
/* prettier-ignore */
|
||||||
|
/* oxlint-disable */
|
||||||
|
// biome-ignore lint: disable
|
||||||
|
// Generated by elegant-router
|
||||||
|
// Read more: https://github.com/soybeanjs/elegant-router
|
||||||
|
|
||||||
|
export {}
|
||||||
|
|
||||||
|
declare module "vue-router" {
|
||||||
|
type RouteNamedMap = import("vue-router/auto-routes").RouteNamedMap;
|
||||||
|
|
||||||
|
export interface TypesConfig {
|
||||||
|
RouteNamedMap: RouteNamedMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "vue-router/auto-routes" {
|
||||||
|
import type { RouteParamsRawGeneric, RouteParamsGeneric, RouteMeta, RouteRecordInfo, ParamValue, ParamValueZeroOrOne } from "vue-router";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* route named map
|
||||||
|
*/
|
||||||
|
export interface RouteNamedMap {
|
||||||
|
"Root": RouteRecordInfo<"Root", "/", Record<never, never>, Record<never, never>>;
|
||||||
|
"NotFound": RouteRecordInfo<"NotFound", "/:pathMatch(.*)*", Record<never, never>, Record<never, never>>;
|
||||||
|
"403": RouteRecordInfo<"403", "/403", Record<never, never>, Record<never, never>>;
|
||||||
|
"404": RouteRecordInfo<"404", "/404", Record<never, never>, Record<never, never>>;
|
||||||
|
"500": RouteRecordInfo<"500", "/500", Record<never, never>, Record<never, never>>;
|
||||||
|
"Home": RouteRecordInfo<"Home", "/home", Record<never, never>, Record<never, never>>;
|
||||||
|
"IframeUrl": RouteRecordInfo<"IframeUrl", "/iframe/:url", { url: ParamValue<true> }, { url: ParamValue<false> }>;
|
||||||
|
"Login": RouteRecordInfo<"Login", "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?", { module?: ParamValueZeroOrOne<true> }, { module?: ParamValueZeroOrOne<false> }>;
|
||||||
|
"ManageMenu": RouteRecordInfo<"ManageMenu", "/manage/menu", Record<never, never>, Record<never, never>>;
|
||||||
|
"ManageRole": RouteRecordInfo<"ManageRole", "/manage/role", Record<never, never>, Record<never, never>>;
|
||||||
|
"ManageRoute": RouteRecordInfo<"ManageRoute", "/manage/route", Record<never, never>, Record<never, never>>;
|
||||||
|
"ManageUser": RouteRecordInfo<"ManageUser", "/manage/user", Record<never, never>, Record<never, never>>;
|
||||||
|
"Wip": RouteRecordInfo<"Wip", "/wip", Record<never, never>, Record<never, never>>
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/typings/union-key.d.ts
vendored
8
src/typings/union-key.d.ts
vendored
@@ -14,14 +14,6 @@ declare namespace UnionKey {
|
|||||||
/** Theme scheme */
|
/** Theme scheme */
|
||||||
type ThemeScheme = 'light' | 'dark' | 'auto';
|
type ThemeScheme = 'light' | 'dark' | 'auto';
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset cache strategy
|
|
||||||
*
|
|
||||||
* - close: re-cache when close page
|
|
||||||
* - refresh: re-cache when refresh page
|
|
||||||
*/
|
|
||||||
type ResetCacheStrategy = 'close' | 'refresh';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The layout mode
|
* The layout mode
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const appStore = useAppStore();
|
|||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
|
|
||||||
interface LoginModule {
|
interface LoginModule {
|
||||||
label: string;
|
label: App.I18n.I18nKey;
|
||||||
component: Component;
|
component: Component;
|
||||||
}
|
}
|
||||||
|
|
||||||
5
src/views/(builtin)/wip/index.vue
Normal file
5
src/views/(builtin)/wip/index.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<LookForward />
|
||||||
|
</template>
|
||||||
5
src/views/manage/menu/index.vue
Normal file
5
src/views/manage/menu/index.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>ManageMenu</div>
|
||||||
|
</template>
|
||||||
5
src/views/manage/role/index.vue
Normal file
5
src/views/manage/role/index.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>ManageRole</div>
|
||||||
|
</template>
|
||||||
5
src/views/manage/route/index.vue
Normal file
5
src/views/manage/route/index.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>ManageRoute</div>
|
||||||
|
</template>
|
||||||
5
src/views/manage/user/index.vue
Normal file
5
src/views/manage/user/index.vue
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>ManageUser</div>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user