feat(projects): ceshitijiao

This commit is contained in:
shaoyou 2025-07-16 15:46:12 +08:00
parent e89b86ce56
commit e55a822add
18 changed files with 302 additions and 257 deletions

44
.env
View File

@ -1,60 +1,60 @@
# the base url of the application, the default is "/" # 应用的基础路径,默认为 "/"
# if use a sub directory, it must be end with "/", like "/admin/" but not "/admin" # 如果使用子目录,必须以 "/" 结尾,如 "/admin/" 而不是 "/admin"
VITE_BASE_URL=/ VITE_BASE_URL=/
VITE_APP_TITLE=SoybeanAdmin VITE_APP_TITLE=SoybeanAdmin
VITE_APP_DESC=SoybeanAdmin is a fresh and elegant admin template VITE_APP_DESC=SoybeanAdmin 是一个清新优雅的管理后台模板
# the prefix of the icon name # 图标名称的前缀
VITE_ICON_PREFIX=icon VITE_ICON_PREFIX=icon
# the prefix of the local svg icon component, must include VITE_ICON_PREFIX # 本地 svg 图标组件的前缀,必须包含 VITE_ICON_PREFIX
# format {VITE_ICON_PREFIX}-{local icon name} # 格式为 {VITE_ICON_PREFIX}-{本地图标名}
VITE_ICON_LOCAL_PREFIX=icon-local VITE_ICON_LOCAL_PREFIX=icon-local
# auth route mode: static dynamic # 权限路由模式:static dynamic
VITE_AUTH_ROUTE_MODE=static VITE_AUTH_ROUTE_MODE=static
# static auth route home # static 权限路由的首页
VITE_ROUTE_HOME=home VITE_ROUTE_HOME=home
# default menu icon # 默认菜单图标
VITE_MENU_ICON=mdi:menu VITE_MENU_ICON=mdi:menu
# whether to enable http proxy when is dev mode # 是否在开发模式下启用 http 代理
VITE_HTTP_PROXY=Y VITE_HTTP_PROXY=Y
# vue-router mode: hash | history | memory # vue-router 模式:hash | history | memory
VITE_ROUTER_HISTORY_MODE=history VITE_ROUTER_HISTORY_MODE=history
# success code of backend service, when the code is received, the request is successful # 后端服务的成功状态码,收到该状态码时请求成功
VITE_SERVICE_SUCCESS_CODE=0000 VITE_SERVICE_SUCCESS_CODE=0000
# logout codes of backend service, when the code is received, the user will be logged out and redirected to login page # 后端服务登出状态码,收到该状态码时用户会被登出并跳转到登录页
VITE_SERVICE_LOGOUT_CODES=8888,8889 VITE_SERVICE_LOGOUT_CODES=8888,8889
# modal logout codes of backend service, when the code is received, the user will be logged out by displaying a modal # 后端服务弹窗登出状态码,收到该状态码时会弹窗提示并登出
VITE_SERVICE_MODAL_LOGOUT_CODES=7777,7778 VITE_SERVICE_MODAL_LOGOUT_CODES=7777,7778
# token expired codes of backend service, when the code is received, it will refresh the token and resend the request # 后端服务 token 过期状态码,收到该状态码时会刷新 token 并重新发送请求
VITE_SERVICE_EXPIRED_TOKEN_CODES=9999,9998,3333 VITE_SERVICE_EXPIRED_TOKEN_CODES=9999,9998,3333
# when the route mode is static, the defined super role # 路由模式为 static 时定义的超级角色
VITE_STATIC_SUPER_ROLE=R_SUPER VITE_STATIC_SUPER_ROLE=R_SUPER
# sourcemap # 是否生成 sourcemap
VITE_SOURCE_MAP=N VITE_SOURCE_MAP=N
# Used to differentiate storage across different domains # 用于区分不同域名下的存储
VITE_STORAGE_PREFIX=SOY_ VITE_STORAGE_PREFIX=SOY_
# used to control whether the program automatically detects updates # 控制程序打包后是否自动检测更新
VITE_AUTOMATICALLY_DETECT_UPDATE=Y VITE_AUTOMATICALLY_DETECT_UPDATE=Y
# show proxy url log in terminal # 是否在终端显示代理地址日志
VITE_PROXY_LOG=Y VITE_PROXY_LOG=Y
# used to control whether to launch editor # 控制是否启动编辑器
# by the way, this plugin is only available in dev mode, not in build mode # 此插件仅在开发模式下可用,构建模式下不可用
VITE_DEVTOOLS_LAUNCH_EDITOR=code VITE_DEVTOOLS_LAUNCH_EDITOR=code

View File

@ -1,7 +1,7 @@
# backend service base url, prod environment # # 后端服务基础地址,生产环境
VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
# other backend service base url, prod environment # 其他后端服务基础地址,生产环境
VITE_OTHER_SERVICE_BASE_URL= `{ VITE_OTHER_SERVICE_BASE_URL= `{
"demo": "http://localhost:9529" "demo": "http://localhost:9529"
}` }`

View File

@ -1,7 +1,7 @@
# backend service base url, test environment # 后端服务基础地址,测试环境
VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
# other backend service base url, test environment # 其他后端服务基础地址,测试环境
VITE_OTHER_SERVICE_BASE_URL= `{ VITE_OTHER_SERVICE_BASE_URL= `{
"demo": "http://localhost:9528" "demo": "http://localhost:9528"
}` }`

View File

@ -4,10 +4,10 @@ import { consola } from 'consola';
import { createServiceConfig } from '../../src/utils/service'; import { createServiceConfig } from '../../src/utils/service';
/** /**
* Set http proxy * http
* *
* @param env - The current env * @param env -
* @param enable - If enable http proxy * @param enable - http
*/ */
export function createViteProxy(env: Env.ImportMeta, enable: boolean) { export function createViteProxy(env: Env.ImportMeta, enable: boolean) {
const isEnableHttpProxy = enable && env.VITE_HTTP_PROXY === 'Y'; const isEnableHttpProxy = enable && env.VITE_HTTP_PROXY === 'Y';
@ -27,6 +27,12 @@ export function createViteProxy(env: Env.ImportMeta, enable: boolean) {
return proxy; return proxy;
} }
/**
*
*
* @param item
* @param enableLog
*/
function createProxyItem(item: App.Service.ServiceConfigItem, enableLog: boolean) { function createProxyItem(item: App.Service.ServiceConfigItem, enableLog: boolean) {
const proxy: Record<string, ProxyOptions> = {}; const proxy: Record<string, ProxyOptions> = {};

View File

@ -12,22 +12,22 @@ type CommandAction<A extends object> = (args?: A) => Promise<void> | void;
type CommandWithAction<A extends object = object> = Record<Command, { desc: string; action: CommandAction<A> }>; type CommandWithAction<A extends object = object> = Record<Command, { desc: string; action: CommandAction<A> }>;
interface CommandArg { interface CommandArg {
/** Execute additional command after bumping and before git commit. Defaults to 'pnpm sa changelog' */ /** bump 版本并 git commit 之前执行的额外命令,默认为 'pnpm sa changelog' */
execute?: string; execute?: string;
/** Indicates whether to push the git commit and tag. Defaults to true */ /** 是否推送 git commit 和 tag默认为 true */
push?: boolean; push?: boolean;
/** Generate changelog by total tags */ /** 是否根据所有 tag 生成 changelog */
total?: boolean; total?: boolean;
/** /**
* The glob pattern of dirs to clean up * glob
* *
* If not set, it will use the default value * 使
* *
* Multiple values use "," to separate them * ","
*/ */
cleanupDir?: string; cleanupDir?: string;
/** /**
* display lang of cli * CLI
* *
* @default 'en-us' * @default 'en-us'
*/ */
@ -41,58 +41,52 @@ export async function setupCli() {
cli cli
.version(lightGreen(version)) .version(lightGreen(version))
.option( .option('-e, --execute [command]', "bump 版本并 git commit 之前执行的额外命令,默认为 'npx soy changelog'")
'-e, --execute [command]', .option('-p, --push', '是否推送 git commit 和 tag')
"Execute additional command after bumping and before git commit. Defaults to 'npx soy changelog'" .option('-t, --total', '是否根据所有 tag 生成 changelog')
) .option('-c, --cleanupDir <dir>', '需要清理的目录的 glob 模式,未设置则使用默认值,多个值用 "," 分隔')
.option('-p, --push', 'Indicates whether to push the git commit and tag') .option('-l, --lang <lang>', 'CLI 显示语言', { default: 'en-us', type: [String] })
.option('-t, --total', 'Generate changelog by total tags')
.option(
'-c, --cleanupDir <dir>',
'The glob pattern of dirs to cleanup, If not set, it will use the default value, Multiple values use "," to separate them'
)
.option('-l, --lang <lang>', 'display lang of cli', { default: 'en-us', type: [String] })
.help(); .help();
const commands: CommandWithAction<CommandArg> = { const commands: CommandWithAction<CommandArg> = {
cleanup: { cleanup: {
desc: 'delete dirs: node_modules, dist, etc.', desc: '删除 node_modules、dist 等目录',
action: async () => { action: async () => {
await cleanup(cliOptions.cleanupDirs); await cleanup(cliOptions.cleanupDirs);
} }
}, },
'update-pkg': { 'update-pkg': {
desc: 'update package.json dependencies versions', desc: '更新 package.json 依赖版本',
action: async () => { action: async () => {
await updatePkg(cliOptions.ncuCommandArgs); await updatePkg(cliOptions.ncuCommandArgs);
} }
}, },
'git-commit': { 'git-commit': {
desc: 'git commit, generate commit message which match Conventional Commits standard', desc: 'git commit,生成符合 Conventional Commits 规范的提交信息',
action: async args => { action: async args => {
await gitCommit(args?.lang); await gitCommit(args?.lang);
} }
}, },
'git-commit-verify': { 'git-commit-verify': {
desc: 'verify git commit message, make sure it match Conventional Commits standard', desc: '校验 git commit 信息,确保符合 Conventional Commits 规范',
action: async args => { action: async args => {
await gitCommitVerify(args?.lang, cliOptions.gitCommitVerifyIgnores); await gitCommitVerify(args?.lang, cliOptions.gitCommitVerifyIgnores);
} }
}, },
changelog: { changelog: {
desc: 'generate changelog', desc: '生成 changelog',
action: async args => { action: async args => {
await genChangelog(cliOptions.changelogOptions, args?.total); await genChangelog(cliOptions.changelogOptions, args?.total);
} }
}, },
release: { release: {
desc: 'release: update version, generate changelog, commit code', desc: '发布:更新版本、生成 changelog、提交代码',
action: async args => { action: async args => {
await release(args?.execute, args?.push); await release(args?.execute, args?.push);
} }
}, },
'gen-route': { 'gen-route': {
desc: 'generate route', desc: '生成路由',
action: async () => { action: async () => {
await generateRoute(); await generateRoute();
} }

View File

@ -166,7 +166,8 @@ const local: App.I18n.Schema = {
404: 'Page Not Found', 404: 'Page Not Found',
500: 'Server Error', 500: 'Server Error',
'iframe-page': 'Iframe', 'iframe-page': 'Iframe',
home: 'Home' home: 'Home',
cesium: 'Cesium'
}, },
page: { page: {
login: { login: {

View File

@ -166,7 +166,8 @@ const local: App.I18n.Schema = {
404: '页面不存在', 404: '页面不存在',
500: '服务器错误', 500: '服务器错误',
'iframe-page': '外链页面', 'iframe-page': '外链页面',
home: '首页' home: '首页',
cesium: '三维地球'
}, },
page: { page: {
login: { login: {

View File

@ -20,5 +20,6 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
500: () => import("@/views/_builtin/500/index.vue"), 500: () => import("@/views/_builtin/500/index.vue"),
"iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"), "iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"),
login: () => import("@/views/_builtin/login/index.vue"), login: () => import("@/views/_builtin/login/index.vue"),
cesium: () => import("@/views/cesium/index.vue"),
home: () => import("@/views/home/index.vue"), home: () => import("@/views/home/index.vue"),
}; };

View File

@ -39,6 +39,16 @@ export const generatedRoutes: GeneratedRoute[] = [
hideInMenu: true hideInMenu: true
} }
}, },
{
name: 'cesium',
path: '/cesium',
component: 'layout.base$view.cesium',
meta: {
title: 'cesium',
i18nKey: 'route.cesium',
order: 1
}
},
{ {
name: 'home', name: 'home',
path: '/home', path: '/home',
@ -47,7 +57,7 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'home', title: 'home',
i18nKey: 'route.home', i18nKey: 'route.home',
icon: 'mdi:monitor-dashboard', icon: 'mdi:monitor-dashboard',
order: 1 order: 0
} }
}, },
{ {

View File

@ -166,6 +166,7 @@ const routeMap: RouteMap = {
"403": "/403", "403": "/403",
"404": "/404", "404": "/404",
"500": "/500", "500": "/500",
"cesium": "/cesium",
"home": "/home", "home": "/home",
"iframe-page": "/iframe-page/:url", "iframe-page": "/iframe-page/: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)?"

View File

@ -12,9 +12,9 @@ import { localStg } from '@/utils/storage';
import { getRouteName } from '@/router/elegant/transform'; import { getRouteName } from '@/router/elegant/transform';
/** /**
* create route guard *
* *
* @param router router instance * @param router
*/ */
export function createRouteGuard(router: Router) { export function createRouteGuard(router: Router) {
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
@ -38,39 +38,39 @@ export function createRouteGuard(router: Router) {
const hasRole = authStore.userInfo.roles.some(role => routeRoles.includes(role)); const hasRole = authStore.userInfo.roles.some(role => routeRoles.includes(role));
const hasAuth = authStore.isStaticSuper || !routeRoles.length || hasRole; const hasAuth = authStore.isStaticSuper || !routeRoles.length || hasRole;
// if it is login route when logged in, then switch to the root page // 已登录时访问登录页,跳转到首页
if (to.name === loginRoute && isLogin) { if (to.name === loginRoute && isLogin) {
next({ name: rootRoute }); next({ name: rootRoute });
return; return;
} }
// if the route does not need login, then it is allowed to access directly // 路由不需要登录,直接放行
if (!needLogin) { if (!needLogin) {
handleRouteSwitch(to, from, next); handleRouteSwitch(to, from, next);
return; return;
} }
// the route need login but the user is not logged in, then switch to the login page // 路由需要登录但用户未登录,跳转到登录页
if (!isLogin) { if (!isLogin) {
next({ name: loginRoute, query: { redirect: to.fullPath } }); next({ name: loginRoute, query: { redirect: to.fullPath } });
return; return;
} }
// if the user is logged in but does not have authorization, then switch to the 403 page // 已登录但无权限,跳转到 403 页面
if (!hasAuth) { if (!hasAuth) {
next({ name: noAuthorizationRoute }); next({ name: noAuthorizationRoute });
return; return;
} }
// switch route normally // 正常切换路由
handleRouteSwitch(to, from, next); handleRouteSwitch(to, from, next);
}); });
} }
/** /**
* initialize route *
* *
* @param to to route * @param to
*/ */
async function initRoute(to: RouteLocationNormalized): Promise<RouteLocationRaw | null> { async function initRoute(to: RouteLocationNormalized): Promise<RouteLocationRaw | null> {
const routeStore = useRouteStore(); const routeStore = useRouteStore();
@ -78,12 +78,12 @@ async function initRoute(to: RouteLocationNormalized): Promise<RouteLocationRaw
const notFoundRoute: RouteKey = 'not-found'; const notFoundRoute: RouteKey = 'not-found';
const isNotFoundRoute = to.name === notFoundRoute; const isNotFoundRoute = to.name === notFoundRoute;
// if the constant route is not initialized, then initialize the constant route // 如果常量路由未初始化,则初始化常量路由
if (!routeStore.isInitConstantRoute) { if (!routeStore.isInitConstantRoute) {
await routeStore.initConstantRoute(); await routeStore.initConstantRoute();
// the route is captured by the "not-found" route because the constant route is not initialized // 因为常量路由未初始化,被 not-found 路由捕获
// after the constant route is initialized, redirect to the original route // 常量路由初始化后,重定向到原始路由
const path = to.fullPath; const path = to.fullPath;
const location: RouteLocationRaw = { const location: RouteLocationRaw = {
path, path,
@ -98,14 +98,14 @@ async function initRoute(to: RouteLocationNormalized): Promise<RouteLocationRaw
const isLogin = Boolean(localStg.get('token')); const isLogin = Boolean(localStg.get('token'));
if (!isLogin) { if (!isLogin) {
// if the user is not logged in and the route is a constant route but not the "not-found" route, then it is allowed to access. // 未登录且为常量路由但不是 not-found 路由,允许访问
if (to.meta.constant && !isNotFoundRoute) { if (to.meta.constant && !isNotFoundRoute) {
routeStore.onRouteSwitchWhenNotLoggedIn(); routeStore.onRouteSwitchWhenNotLoggedIn();
return null; return null;
} }
// if the user is not logged in, then switch to the login page // 未登录,跳转到登录页
const loginRoute: RouteKey = 'login'; const loginRoute: RouteKey = 'login';
const query = getRouteQueryOfLoginRoute(to, routeStore.routeHome); const query = getRouteQueryOfLoginRoute(to, routeStore.routeHome);
@ -118,11 +118,11 @@ async function initRoute(to: RouteLocationNormalized): Promise<RouteLocationRaw
} }
if (!routeStore.isInitAuthRoute) { if (!routeStore.isInitAuthRoute) {
// initialize the auth route // 初始化权限路由
await routeStore.initAuthRoute(); await routeStore.initAuthRoute();
// the route is captured by the "not-found" route because the auth route is not initialized // 因为权限路由未初始化,被 not-found 路由捕获
// after the auth route is initialized, redirect to the original route // 权限路由初始化后,重定向到原始路由
if (isNotFoundRoute) { if (isNotFoundRoute) {
const rootRoute: RouteKey = 'root'; const rootRoute: RouteKey = 'root';
const path = to.redirectedFrom?.name === rootRoute ? '/' : to.fullPath; const path = to.redirectedFrom?.name === rootRoute ? '/' : to.fullPath;
@ -140,13 +140,12 @@ async function initRoute(to: RouteLocationNormalized): Promise<RouteLocationRaw
routeStore.onRouteSwitchWhenLoggedIn(); routeStore.onRouteSwitchWhenLoggedIn();
// the auth route is initialized // 权限路由已初始化,且不是 not-found 路由,允许访问
// it is not the "not-found" route, then it is allowed to access
if (!isNotFoundRoute) { if (!isNotFoundRoute) {
return null; return null;
} }
// it is captured by the "not-found" route, then check whether the route exists // 被 not-found 路由捕获,判断路由是否存在
const exist = await routeStore.getIsAuthRouteExist(to.path as RoutePath); const exist = await routeStore.getIsAuthRouteExist(to.path as RoutePath);
const noPermissionRoute: RouteKey = '403'; const noPermissionRoute: RouteKey = '403';
@ -161,8 +160,11 @@ async function initRoute(to: RouteLocationNormalized): Promise<RouteLocationRaw
return null; return null;
} }
/**
*
*/
function handleRouteSwitch(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) { function handleRouteSwitch(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) {
// route with href // 带 href 的路由
if (to.meta.href) { if (to.meta.href) {
window.open(to.meta.href, '_blank'); window.open(to.meta.href, '_blank');
@ -174,6 +176,9 @@ function handleRouteSwitch(to: RouteLocationNormalized, from: RouteLocationNorma
next(); next();
} }
/**
* query
*/
function getRouteQueryOfLoginRoute(to: RouteLocationNormalized, routeHome: RouteKey) { function getRouteQueryOfLoginRoute(to: RouteLocationNormalized, routeHome: RouteKey) {
const loginRoute: RouteKey = 'login'; const loginRoute: RouteKey = 'login';
const redirect = to.fullPath; const redirect = to.fullPath;

View File

@ -1,19 +1,19 @@
import { request } from '../request'; import { request } from '../request';
/** get constant routes */ /** 获取常量路由 */
export function fetchGetConstantRoutes() { export function fetchGetConstantRoutes() {
return request<Api.Route.MenuRoute[]>({ url: '/route/getConstantRoutes' }); return request<Api.Route.MenuRoute[]>({ url: '/route/getConstantRoutes' });
} }
/** get user routes */ /** 获取用户路由 */
export function fetchGetUserRoutes() { export function fetchGetUserRoutes() {
return request<Api.Route.UserRoute>({ url: '/route/getUserRoutes' }); return request<Api.Route.UserRoute>({ url: '/route/getUserRoutes' });
} }
/** /**
* whether the route is exist *
* *
* @param routeName route name * @param routeName
*/ */
export function fetchIsRouteExist(routeName: string) { export function fetchIsRouteExist(routeName: string) {
return request<boolean>({ url: '/route/isRouteExist', params: { routeName } }); return request<boolean>({ url: '/route/isRouteExist', params: { routeName } });

49
src/typings/api.d.ts vendored
View File

@ -1,64 +1,67 @@
/** /**
* Namespace Api * Api
* *
* All backend api type * API
*/ */
declare namespace Api { declare namespace Api {
namespace Common { namespace Common {
/** common params of paginating */ /** 分页通用参数 */
interface PaginatingCommonParams { interface PaginatingCommonParams {
/** current page number */ /** 当前页码 */
current: number; current: number;
/** page size */ /** 每页数量 */
size: number; size: number;
/** total count */ /** 总条数 */
total: number; total: number;
} }
/** common params of paginating query list data */ /** 分页查询列表数据的通用参数 */
interface PaginatingQueryRecord<T = any> extends PaginatingCommonParams { interface PaginatingQueryRecord<T = any> extends PaginatingCommonParams {
/** 数据列表 */
records: T[]; records: T[];
} }
/** common search params of table */ /** 表格通用搜索参数 */
type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'current' | 'size'>; type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'current' | 'size'>;
/** /**
* enable status *
* *
* - "1": enabled * - "1":
* - "2": disabled * - "2":
*/ */
type EnableStatus = '1' | '2'; type EnableStatus = '1' | '2';
/** common record */ /** 通用记录 */
type CommonRecord<T = any> = { type CommonRecord<T = any> = {
/** record id */ /** 记录ID */
id: number; id: number;
/** record creator */ /** 创建人 */
createBy: string; createBy: string;
/** record create time */ /** 创建时间 */
createTime: string; createTime: string;
/** record updater */ /** 更新人 */
updateBy: string; updateBy: string;
/** record update time */ /** 更新时间 */
updateTime: string; updateTime: string;
/** record status */ /** 状态 */
status: EnableStatus | null; status: EnableStatus | null;
} & T; } & T;
} }
/** /**
* namespace Auth * Auth
* *
* backend api module: "auth" * API "auth"
*/ */
namespace Auth { namespace Auth {
/** 登录令牌 */
interface LoginToken { interface LoginToken {
token: string; token: string;
refreshToken: string; refreshToken: string;
} }
/** 用户信息 */
interface UserInfo { interface UserInfo {
userId: string; userId: string;
userName: string; userName: string;
@ -68,17 +71,19 @@ declare namespace Api {
} }
/** /**
* namespace Route * Route
* *
* backend api module: "route" * API "route"
*/ */
namespace Route { namespace Route {
type ElegantConstRoute = import('@elegant-router/types').ElegantConstRoute; type ElegantConstRoute = import('@elegant-router/types').ElegantConstRoute;
/** 菜单路由 */
interface MenuRoute extends ElegantConstRoute { interface MenuRoute extends ElegantConstRoute {
id: string; id: string;
} }
/** 用户路由 */
interface UserRoute { interface UserRoute {
routes: MenuRoute[]; routes: MenuRoute[];
home: import('@elegant-router/types').LastLevelRouteKey; home: import('@elegant-router/types').LastLevelRouteKey;

204
src/typings/app.d.ts vendored
View File

@ -1,121 +1,121 @@
/** The global namespace for the app */ /** 应用全局命名空间 */
declare namespace App { declare namespace App {
/** Theme namespace */ /** 主题命名空间 */
namespace Theme { namespace Theme {
type ColorPaletteNumber = import('@sa/color').ColorPaletteNumber; type ColorPaletteNumber = import('@sa/color').ColorPaletteNumber;
/** Theme setting */ /** 主题设置 */
interface ThemeSetting { interface ThemeSetting {
/** Theme scheme */ /** 主题方案 */
themeScheme: UnionKey.ThemeScheme; themeScheme: UnionKey.ThemeScheme;
/** grayscale mode */ /** 灰度模式 */
grayscale: boolean; grayscale: boolean;
/** colour weakness mode */ /** 色弱模式 */
colourWeakness: boolean; colourWeakness: boolean;
/** Whether to recommend color */ /** 是否推荐配色 */
recommendColor: boolean; recommendColor: boolean;
/** Theme color */ /** 主题色 */
themeColor: string; themeColor: string;
/** Other color */ /** 其他颜色 */
otherColor: OtherColor; otherColor: OtherColor;
/** Whether info color is followed by the primary color */ /** info颜色是否跟随主色 */
isInfoFollowPrimary: boolean; isInfoFollowPrimary: boolean;
/** Reset cache strategy */ /** 重置缓存策略 */
resetCacheStrategy: UnionKey.ResetCacheStrategy; resetCacheStrategy: UnionKey.ResetCacheStrategy;
/** Layout */ /** 布局 */
layout: { layout: {
/** Layout mode */ /** 布局模式 */
mode: UnionKey.ThemeLayoutMode; mode: UnionKey.ThemeLayoutMode;
/** Scroll mode */ /** 滚动模式 */
scrollMode: UnionKey.ThemeScrollMode; scrollMode: UnionKey.ThemeScrollMode;
/** /**
* Whether to reverse the horizontal mix *
* *
* if true, the vertical child level menus in left and horizontal first level menus in top * true
*/ */
reverseHorizontalMix: boolean; reverseHorizontalMix: boolean;
}; };
/** Page */ /** 页面 */
page: { page: {
/** Whether to show the page transition */ /** 是否显示页面切换动画 */
animate: boolean; animate: boolean;
/** Page animate mode */ /** 页面动画模式 */
animateMode: UnionKey.ThemePageAnimateMode; animateMode: UnionKey.ThemePageAnimateMode;
}; };
/** Header */ /** 顶栏 */
header: { header: {
/** Header height */ /** 顶栏高度 */
height: number; height: number;
/** Header breadcrumb */ /** 顶栏面包屑 */
breadcrumb: { breadcrumb: {
/** Whether to show the breadcrumb */ /** 是否显示面包屑 */
visible: boolean; visible: boolean;
/** Whether to show the breadcrumb icon */ /** 是否显示面包屑图标 */
showIcon: boolean; showIcon: boolean;
}; };
/** Multilingual */ /** 多语言 */
multilingual: { multilingual: {
/** Whether to show the multilingual */ /** 是否显示多语言 */
visible: boolean; visible: boolean;
}; };
globalSearch: { globalSearch: {
/** Whether to show the GlobalSearch */ /** 是否显示全局搜索 */
visible: boolean; visible: boolean;
}; };
}; };
/** Tab */ /** 标签栏 */
tab: { tab: {
/** Whether to show the tab */ /** 是否显示标签栏 */
visible: boolean; visible: boolean;
/** /**
* Whether to cache the tab *
* *
* If cache, the tabs will get from the local storage when the page is refreshed *
*/ */
cache: boolean; cache: boolean;
/** Tab height */ /** 标签栏高度 */
height: number; height: number;
/** Tab mode */ /** 标签栏模式 */
mode: UnionKey.ThemeTabMode; mode: UnionKey.ThemeTabMode;
}; };
/** Fixed header and tab */ /** 固定头部和标签栏 */
fixedHeaderAndTab: boolean; fixedHeaderAndTab: boolean;
/** Sider */ /** 侧边栏 */
sider: { sider: {
/** Inverted sider */ /** 侧边栏反色 */
inverted: boolean; inverted: boolean;
/** Sider width */ /** 侧边栏宽度 */
width: number; width: number;
/** Collapsed sider width */ /** 侧边栏收起宽度 */
collapsedWidth: number; collapsedWidth: number;
/** Sider width when the layout is 'vertical-mix' or 'horizontal-mix' */ /** 混合布局下侧边栏宽度 */
mixWidth: number; mixWidth: number;
/** Collapsed sider width when the layout is 'vertical-mix' or 'horizontal-mix' */ /** 混合布局下收起宽度 */
mixCollapsedWidth: number; mixCollapsedWidth: number;
/** Child menu width when the layout is 'vertical-mix' or 'horizontal-mix' */ /** 混合布局下子菜单宽度 */
mixChildMenuWidth: number; mixChildMenuWidth: number;
}; };
/** Footer */ /** 页脚 */
footer: { footer: {
/** Whether to show the footer */ /** 是否显示页脚 */
visible: boolean; visible: boolean;
/** Whether fixed the footer */ /** 是否固定页脚 */
fixed: boolean; fixed: boolean;
/** Footer height */ /** 页脚高度 */
height: number; height: number;
/** Whether float the footer to the right when the layout is 'horizontal-mix' */ /** 混合布局下页脚是否右浮动 */
right: boolean; right: boolean;
}; };
/** Watermark */ /** 水印 */
watermark: { watermark: {
/** Whether to show the watermark */ /** 是否显示水印 */
visible: boolean; visible: boolean;
/** Watermark text */ /** 水印文本 */
text: string; text: string;
/** Whether to use user name as watermark text */ /** 是否使用用户名作为水印文本 */
enableUserName: boolean; enableUserName: boolean;
}; };
/** define some theme settings tokens, will transform to css variables */ /** 定义部分主题 token会转换为 css 变量 */
tokens: { tokens: {
light: ThemeSettingToken; light: ThemeSettingToken;
dark?: { dark?: {
@ -144,7 +144,7 @@ declare namespace App {
type BaseToken = Record<string, Record<string, string>>; type BaseToken = Record<string, Record<string, string>>;
interface ThemeSettingTokenColor { interface ThemeSettingTokenColor {
/** the progress bar color, if not set, will use the primary color */ /** 进度条颜色,未设置时使用主色 */
nprogress?: string; nprogress?: string;
container: string; container: string;
layout: string; layout: string;
@ -165,14 +165,14 @@ declare namespace App {
type ThemeTokenColor = ThemePaletteColor & ThemeSettingTokenColor; type ThemeTokenColor = ThemePaletteColor & ThemeSettingTokenColor;
/** Theme token CSS variables */ /** 主题 token CSS 变量 */
type ThemeTokenCSSVars = { type ThemeTokenCSSVars = {
colors: ThemeTokenColor & { [key: string]: string }; colors: ThemeTokenColor & { [key: string]: string };
boxShadow: ThemeSettingTokenBoxShadow & { [key: string]: string }; boxShadow: ThemeSettingTokenBoxShadow & { [key: string]: string };
}; };
} }
/** Global namespace */ /** 全局命名空间 */
namespace Global { namespace Global {
type VNode = import('vue').VNode; type VNode = import('vue').VNode;
type RouteLocationNormalizedLoaded = import('vue-router').RouteLocationNormalizedLoaded; type RouteLocationNormalizedLoaded = import('vue-router').RouteLocationNormalizedLoaded;
@ -181,41 +181,41 @@ declare namespace App {
type RoutePath = import('@elegant-router/types').RoutePath; type RoutePath = import('@elegant-router/types').RoutePath;
type LastLevelRouteKey = import('@elegant-router/types').LastLevelRouteKey; type LastLevelRouteKey = import('@elegant-router/types').LastLevelRouteKey;
/** The router push options */ /** 路由跳转参数 */
type RouterPushOptions = { type RouterPushOptions = {
query?: Record<string, string>; query?: Record<string, string>;
params?: Record<string, string>; params?: Record<string, string>;
}; };
/** The global header props */ /** 全局头部属性 */
interface HeaderProps { interface HeaderProps {
/** Whether to show the logo */ /** 是否显示 logo */
showLogo?: boolean; showLogo?: boolean;
/** Whether to show the menu toggler */ /** 是否显示菜单切换按钮 */
showMenuToggler?: boolean; showMenuToggler?: boolean;
/** Whether to show the menu */ /** 是否显示菜单 */
showMenu?: boolean; showMenu?: boolean;
} }
/** The global menu */ /** 全局菜单 */
type Menu = { type Menu = {
/** /**
* The menu key * key
* *
* Equal to the route key * key
*/ */
key: string; key: string;
/** The menu label */ /** 菜单名称 */
label: string; label: string;
/** The menu i18n key */ /** 菜单 i18n key */
i18nKey?: I18n.I18nKey | null; i18nKey?: I18n.I18nKey | null;
/** The route key */ /** 路由 key */
routeKey: RouteKey; routeKey: RouteKey;
/** The route path */ /** 路由 path */
routePath: RoutePath; routePath: RoutePath;
/** The menu icon */ /** 菜单图标 */
icon?: () => VNode; icon?: () => VNode;
/** The menu children */ /** 子菜单 */
children?: Menu[]; children?: Menu[];
}; };
@ -223,63 +223,63 @@ declare namespace App {
options?: Breadcrumb[]; options?: Breadcrumb[];
}; };
/** Tab route */ /** 标签页路由 */
type TabRoute = Pick<RouteLocationNormalizedLoaded, 'name' | 'path' | 'meta'> & type TabRoute = Pick<RouteLocationNormalizedLoaded, 'name' | 'path' | 'meta'> &
Partial<Pick<RouteLocationNormalizedLoaded, 'fullPath' | 'query' | 'matched'>>; Partial<Pick<RouteLocationNormalizedLoaded, 'fullPath' | 'query' | 'matched'>>;
/** The global tab */ /** 全局标签页 */
type Tab = { type Tab = {
/** The tab id */ /** 标签页 id */
id: string; id: string;
/** The tab label */ /** 标签页名称 */
label: string; label: string;
/** /**
* The new tab label *
* *
* If set, the tab label will be replaced by this value *
*/ */
newLabel?: string; newLabel?: string;
/** /**
* The old tab label *
* *
* when reset the tab label, the tab label will be replaced by this value *
*/ */
oldLabel?: string; oldLabel?: string;
/** The tab route key */ /** 标签页路由 key */
routeKey: LastLevelRouteKey; routeKey: LastLevelRouteKey;
/** The tab route path */ /** 标签页路由 path */
routePath: RouteMap[LastLevelRouteKey]; routePath: RouteMap[LastLevelRouteKey];
/** The tab route full path */ /** 标签页完整路径 */
fullPath: string; fullPath: string;
/** The tab fixed index */ /** 固定标签页索引 */
fixedIndex?: number | null; fixedIndex?: number | null;
/** /**
* Tab icon *
* *
* Iconify icon * Iconify
*/ */
icon?: string; icon?: string;
/** /**
* Tab local icon *
* *
* Local icon *
*/ */
localIcon?: string; localIcon?: string;
/** I18n key */ /** I18n key */
i18nKey?: I18n.I18nKey | null; i18nKey?: I18n.I18nKey | null;
}; };
/** Form rule */ /** 表单校验规则 */
type FormRule = import('naive-ui').FormItemRule; type FormRule = import('naive-ui').FormItemRule;
/** The global dropdown key */ /** 全局下拉菜单 key */
type DropdownKey = 'closeCurrent' | 'closeOther' | 'closeLeft' | 'closeRight' | 'closeAll'; type DropdownKey = 'closeCurrent' | 'closeOther' | 'closeLeft' | 'closeRight' | 'closeAll';
} }
/** /**
* I18n namespace * I18n
* *
* Locales type *
*/ */
namespace I18n { namespace I18n {
type RouteKey = import('@elegant-router/types').RouteKey; type RouteKey = import('@elegant-router/types').RouteKey;
@ -549,15 +549,15 @@ declare namespace App {
} }
} }
/** Service namespace */ /** 服务命名空间 */
namespace Service { namespace Service {
/** Other baseURL key */ /** 其他 baseURL key */
type OtherBaseURLKey = 'demo'; type OtherBaseURLKey = 'demo';
interface ServiceConfigItem { interface ServiceConfigItem {
/** The backend service base url */ /** 后端服务基础地址 */
baseURL: string; baseURL: string;
/** The proxy pattern of the backend service base url */ /** 后端服务基础地址的代理前缀 */
proxyPattern: string; proxyPattern: string;
} }
@ -565,9 +565,9 @@ declare namespace App {
key: OtherBaseURLKey; key: OtherBaseURLKey;
} }
/** The backend service config */ /** 后端服务配置 */
interface ServiceConfig extends ServiceConfigItem { interface ServiceConfig extends ServiceConfigItem {
/** Other backend service config */ /** 其他后端服务配置 */
other: OtherServiceConfigItem[]; other: OtherServiceConfigItem[];
} }
@ -575,23 +575,23 @@ declare namespace App {
other: Record<OtherBaseURLKey, string>; other: Record<OtherBaseURLKey, string>;
} }
/** The backend service response data */ /** 后端服务响应数据 */
type Response<T = unknown> = { type Response<T = unknown> = {
/** The backend service response code */ /** 响应码 */
code: string; code: string;
/** The backend service response message */ /** 响应信息 */
msg: string; msg: string;
/** The backend service response data */ /** 响应数据 */
data: T; data: T;
}; };
/** The demo backend service response data */ /** demo 后端服务响应数据 */
type DemoResponse<T = unknown> = { type DemoResponse<T = unknown> = {
/** The backend service response code */ /** 响应码 */
status: string; status: string;
/** The backend service response message */ /** 响应信息 */
message: string; message: string;
/** The backend service response data */ /** 响应数据 */
result: T; result: T;
}; };
} }

View File

@ -20,6 +20,7 @@ declare module "@elegant-router/types" {
"403": "/403"; "403": "/403";
"404": "/404"; "404": "/404";
"500": "/500"; "500": "/500";
"cesium": "/cesium";
"home": "/home"; "home": "/home";
"iframe-page": "/iframe-page/:url"; "iframe-page": "/iframe-page/: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)?";
@ -57,6 +58,7 @@ declare module "@elegant-router/types" {
| "403" | "403"
| "404" | "404"
| "500" | "500"
| "cesium"
| "home" | "home"
| "iframe-page" | "iframe-page"
| "login" | "login"
@ -81,6 +83,7 @@ declare module "@elegant-router/types" {
| "500" | "500"
| "iframe-page" | "iframe-page"
| "login" | "login"
| "cesium"
| "home" | "home"
>; >;

View File

@ -1,114 +1,113 @@
/** /**
* Namespace Env * Env
* *
* It is used to declare the type of the import.meta object * import.meta
*/ */
declare namespace Env { declare namespace Env {
/** The router history mode */ /** 路由历史模式 */
type RouterHistoryMode = 'hash' | 'history' | 'memory'; type RouterHistoryMode = 'hash' | 'history' | 'memory';
/** Interface for import.meta */ /** import.meta 的接口 */
// eslint-disable-next-line @typescript-eslint/no-shadow // eslint-disable-next-line @typescript-eslint/no-shadow
interface ImportMeta extends ImportMetaEnv { interface ImportMeta extends ImportMetaEnv {
/** The base url of the application */ /** 应用的基础路径 */
readonly VITE_BASE_URL: string; readonly VITE_BASE_URL: string;
/** The title of the application */ /** 应用标题 */
readonly VITE_APP_TITLE: string; readonly VITE_APP_TITLE: string;
/** The description of the application */ /** 应用描述 */
readonly VITE_APP_DESC: string; readonly VITE_APP_DESC: string;
/** The router history mode */ /** 路由历史模式 */
readonly VITE_ROUTER_HISTORY_MODE?: RouterHistoryMode; readonly VITE_ROUTER_HISTORY_MODE?: RouterHistoryMode;
/** The prefix of the iconify icon */ /** iconify 图标前缀 */
readonly VITE_ICON_PREFIX: 'icon'; readonly VITE_ICON_PREFIX: 'icon';
/** /**
* The prefix of the local icon *
* *
* This prefix is start with the icon prefix * icon
*/ */
readonly VITE_ICON_LOCAL_PREFIX: 'icon-local'; readonly VITE_ICON_LOCAL_PREFIX: 'icon-local';
/** backend service base url */ /** 后端服务基础地址 */
readonly VITE_SERVICE_BASE_URL: string; readonly VITE_SERVICE_BASE_URL: string;
/** /**
* success code of backend service *
* *
* when the code is received, the request is successful *
*/ */
readonly VITE_SERVICE_SUCCESS_CODE: string; readonly VITE_SERVICE_SUCCESS_CODE: string;
/** /**
* logout codes of backend service *
* *
* when the code is received, the user will be logged out and redirected to login page *
* *
* use "," to separate multiple codes * ","
*/ */
readonly VITE_SERVICE_LOGOUT_CODES: string; readonly VITE_SERVICE_LOGOUT_CODES: string;
/** /**
* modal logout codes of backend service *
* *
* when the code is received, the user will be logged out by displaying a modal *
* *
* use "," to separate multiple codes * ","
*/ */
readonly VITE_SERVICE_MODAL_LOGOUT_CODES: string; readonly VITE_SERVICE_MODAL_LOGOUT_CODES: string;
/** /**
* token expired codes of backend service * token
* *
* when the code is received, it will refresh the token and resend the request * token
* *
* use "," to separate multiple codes * ","
*/ */
readonly VITE_SERVICE_EXPIRED_TOKEN_CODES: string; readonly VITE_SERVICE_EXPIRED_TOKEN_CODES: string;
/** when the route mode is static, the defined super role */ /** 路由模式为 static 时定义的超级角色 */
readonly VITE_STATIC_SUPER_ROLE: string; readonly VITE_STATIC_SUPER_ROLE: string;
/** /**
* other backend service base url *
* *
* the value is a json * json
*/ */
readonly VITE_OTHER_SERVICE_BASE_URL: string; readonly VITE_OTHER_SERVICE_BASE_URL: string;
/** /**
* Whether to enable the http proxy * http
* *
* Only valid in the development environment *
*/ */
readonly VITE_HTTP_PROXY?: CommonType.YesOrNo; readonly VITE_HTTP_PROXY?: CommonType.YesOrNo;
/** /**
* The auth route mode *
* *
* - Static: the auth routes is generated in front-end * - static
* - Dynamic: the auth routes is generated in back-end * - dynamic
*/ */
readonly VITE_AUTH_ROUTE_MODE: 'static' | 'dynamic'; readonly VITE_AUTH_ROUTE_MODE: 'static' | 'dynamic';
/** /**
* The home route key * key
* *
* It only has effect when the auth route mode is static, if the route mode is dynamic, the home route key is * static dynamic
* defined in the back-end
*/ */
readonly VITE_ROUTE_HOME: import('@elegant-router/types').LastLevelRouteKey; readonly VITE_ROUTE_HOME: import('@elegant-router/types').LastLevelRouteKey;
/** /**
* Default menu icon if menu icon is not set * 使
* *
* Iconify icon name * Iconify
*/ */
readonly VITE_MENU_ICON: string; readonly VITE_MENU_ICON: string;
/** Whether to build with sourcemap */ /** 是否构建 sourcemap */
readonly VITE_SOURCE_MAP?: CommonType.YesOrNo; readonly VITE_SOURCE_MAP?: CommonType.YesOrNo;
/** /**
* Iconify api provider url * Iconify API
* *
* If the project is deployed in intranet, you can set the api provider url to the local iconify server * iconify
* *
* @link https://docs.iconify.design/api/providers.html * @link https://docs.iconify.design/api/providers.html
*/ */
readonly VITE_ICONIFY_URL?: string; readonly VITE_ICONIFY_URL?: string;
/** Used to differentiate storage across different domains */ /** 用于区分不同域名下的存储 */
readonly VITE_STORAGE_PREFIX?: string; readonly VITE_STORAGE_PREFIX?: string;
/** Whether to automatically detect updates after configuring application packaging */ /** 配置应用打包后是否自动检测更新 */
readonly VITE_AUTOMATICALLY_DETECT_UPDATE?: CommonType.YesOrNo; readonly VITE_AUTOMATICALLY_DETECT_UPDATE?: CommonType.YesOrNo;
/** show proxy url log in terminal */ /** 是否在终端显示代理地址日志 */
readonly VITE_PROXY_LOG?: CommonType.YesOrNo; readonly VITE_PROXY_LOG?: CommonType.YesOrNo;
/** The launch editor */ /** 启动的编辑器 */
readonly VITE_DEVTOOLS_LAUNCH_EDITOR?: import('vite-plugin-vue-devtools').VitePluginVueDevToolsOptions['launchEditor']; readonly VITE_DEVTOOLS_LAUNCH_EDITOR?: import('vite-plugin-vue-devtools').VitePluginVueDevToolsOptions['launchEditor'];
} }
} }

View File

@ -1,9 +1,9 @@
import json5 from 'json5'; import json5 from 'json5';
/** /**
* Create service config by current env *
* *
* @param env The current env * @param env
*/ */
export function createServiceConfig(env: Env.ImportMeta) { export function createServiceConfig(env: Env.ImportMeta) {
const { VITE_SERVICE_BASE_URL, VITE_OTHER_SERVICE_BASE_URL } = env; const { VITE_SERVICE_BASE_URL, VITE_OTHER_SERVICE_BASE_URL } = env;
@ -13,7 +13,7 @@ export function createServiceConfig(env: Env.ImportMeta) {
other = json5.parse(VITE_OTHER_SERVICE_BASE_URL); other = json5.parse(VITE_OTHER_SERVICE_BASE_URL);
} catch { } catch {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error('VITE_OTHER_SERVICE_BASE_URL is not a valid json5 string'); console.error('VITE_OTHER_SERVICE_BASE_URL 不是有效的 json5 字符串');
} }
const httpConfig: App.Service.SimpleServiceConfig = { const httpConfig: App.Service.SimpleServiceConfig = {
@ -41,10 +41,10 @@ export function createServiceConfig(env: Env.ImportMeta) {
} }
/** /**
* get backend service base url *
* *
* @param env - the current env * @param env
* @param isProxy - if use proxy * @param isProxy 使
*/ */
export function getServiceBaseURL(env: Env.ImportMeta, isProxy: boolean) { export function getServiceBaseURL(env: Env.ImportMeta, isProxy: boolean) {
const { baseURL, other } = createServiceConfig(env); const { baseURL, other } = createServiceConfig(env);
@ -62,9 +62,9 @@ export function getServiceBaseURL(env: Env.ImportMeta, isProxy: boolean) {
} }
/** /**
* Get proxy pattern of backend service base url *
* *
* @param key If not set, will use the default key * @param key 使 key
*/ */
function createProxyPattern(key?: App.Service.OtherBaseURLKey) { function createProxyPattern(key?: App.Service.OtherBaseURLKey) {
if (!key) { if (!key) {

View File

@ -0,0 +1,19 @@
<script setup lang="ts"></script>
<template>
<div>
这是cesium页面
<div class="cesium-container">
<div id="cesiumContainer"></div>
</div>
<p>请在控制台查看Cesium相关信息</p>
</div>
</template>
<style scoped>
#cesiumContainer {
width: 100%;
height: 100vh;
background-color: red;
}
</style>