feat:新增pure-admin前端
This commit is contained in:
55
Yi.Pure.Vue3/src/router/enums.ts
Normal file
55
Yi.Pure.Vue3/src/router/enums.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
// 完整版菜单比较多,将 rank 抽离出来,在此方便维护
|
||||
|
||||
const home = 0, // 平台规定只有 home 路由的 rank 才能为 0 ,所以后端在返回 rank 的时候需要从非 0 开始
|
||||
vueflow = 1,
|
||||
ganttastic = 2,
|
||||
components = 3,
|
||||
able = 4,
|
||||
table = 5,
|
||||
form = 6,
|
||||
list = 7,
|
||||
result = 8,
|
||||
error = 9,
|
||||
frame = 10,
|
||||
nested = 11,
|
||||
permission = 12,
|
||||
system = 13,
|
||||
monitor = 14,
|
||||
tabs = 15,
|
||||
about = 16,
|
||||
editor = 17,
|
||||
flowchart = 18,
|
||||
formdesign = 19,
|
||||
board = 20,
|
||||
ppt = 21,
|
||||
mind = 22,
|
||||
guide = 23,
|
||||
menuoverflow = 24;
|
||||
|
||||
export {
|
||||
home,
|
||||
vueflow,
|
||||
ganttastic,
|
||||
components,
|
||||
able,
|
||||
table,
|
||||
form,
|
||||
list,
|
||||
result,
|
||||
error,
|
||||
frame,
|
||||
nested,
|
||||
permission,
|
||||
system,
|
||||
monitor,
|
||||
tabs,
|
||||
about,
|
||||
editor,
|
||||
flowchart,
|
||||
formdesign,
|
||||
board,
|
||||
ppt,
|
||||
mind,
|
||||
guide,
|
||||
menuoverflow
|
||||
};
|
||||
208
Yi.Pure.Vue3/src/router/index.ts
Normal file
208
Yi.Pure.Vue3/src/router/index.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
import "@/utils/sso";
|
||||
import Cookies from "js-cookie";
|
||||
import { getConfig } from "@/config";
|
||||
import NProgress from "@/utils/progress";
|
||||
import { transformI18n } from "@/plugins/i18n";
|
||||
import { buildHierarchyTree } from "@/utils/tree";
|
||||
import remainingRouter from "./modules/remaining";
|
||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||
import { isUrl, openLink, storageLocal, isAllEmpty } from "@pureadmin/utils";
|
||||
import {
|
||||
ascending,
|
||||
getTopMenu,
|
||||
initRouter,
|
||||
isOneOfArray,
|
||||
getHistoryMode,
|
||||
findRouteByPath,
|
||||
handleAliveRoute,
|
||||
formatTwoStageRoutes,
|
||||
formatFlatteningRoutes
|
||||
} from "./utils";
|
||||
import {
|
||||
type Router,
|
||||
createRouter,
|
||||
type RouteRecordRaw,
|
||||
type RouteComponent
|
||||
} from "vue-router";
|
||||
import {
|
||||
type DataInfo,
|
||||
userKey,
|
||||
removeToken,
|
||||
multipleTabsKey
|
||||
} from "@/utils/auth";
|
||||
|
||||
/** 自动导入全部静态路由,无需再手动引入!匹配 src/router/modules 目录(任何嵌套级别)中具有 .ts 扩展名的所有文件,除了 remaining.ts 文件
|
||||
* 如何匹配所有文件请看:https://github.com/mrmlnc/fast-glob#basic-syntax
|
||||
* 如何排除文件请看:https://cn.vitejs.dev/guide/features.html#negative-patterns
|
||||
*/
|
||||
const modules: Record<string, any> = import.meta.glob(
|
||||
["./modules/**/*.ts", "!./modules/**/remaining.ts"],
|
||||
{
|
||||
eager: true
|
||||
}
|
||||
);
|
||||
|
||||
/** 原始静态路由(未做任何处理) */
|
||||
const routes = [];
|
||||
|
||||
Object.keys(modules).forEach(key => {
|
||||
routes.push(modules[key].default);
|
||||
});
|
||||
|
||||
/** 导出处理后的静态路由(三级及以上的路由全部拍成二级) */
|
||||
export const constantRoutes: Array<RouteRecordRaw> = formatTwoStageRoutes(
|
||||
formatFlatteningRoutes(buildHierarchyTree(ascending(routes.flat(Infinity))))
|
||||
);
|
||||
|
||||
/** 用于渲染菜单,保持原始层级 */
|
||||
export const constantMenus: Array<RouteComponent> = ascending(
|
||||
routes.flat(Infinity)
|
||||
).concat(...remainingRouter);
|
||||
|
||||
/** 不参与菜单的路由 */
|
||||
export const remainingPaths = Object.keys(remainingRouter).map(v => {
|
||||
return remainingRouter[v].path;
|
||||
});
|
||||
|
||||
/** 创建路由实例 */
|
||||
export const router: Router = createRouter({
|
||||
history: getHistoryMode(import.meta.env.VITE_ROUTER_HISTORY),
|
||||
routes: constantRoutes.concat(...(remainingRouter as any)),
|
||||
strict: true,
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
return new Promise(resolve => {
|
||||
if (savedPosition) {
|
||||
return savedPosition;
|
||||
} else {
|
||||
if (from.meta.saveSrollTop) {
|
||||
const top: number =
|
||||
document.documentElement.scrollTop || document.body.scrollTop;
|
||||
resolve({ left: 0, top });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/** 重置路由 */
|
||||
export function resetRouter() {
|
||||
router.getRoutes().forEach(route => {
|
||||
const { name, meta } = route;
|
||||
if (name && router.hasRoute(name) && meta?.backstage) {
|
||||
router.removeRoute(name);
|
||||
router.options.routes = formatTwoStageRoutes(
|
||||
formatFlatteningRoutes(
|
||||
buildHierarchyTree(ascending(routes.flat(Infinity)))
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
usePermissionStoreHook().clearAllCachePage();
|
||||
}
|
||||
|
||||
/** 路由白名单 */
|
||||
const whiteList = ["/login"];
|
||||
|
||||
const { VITE_HIDE_HOME } = import.meta.env;
|
||||
|
||||
router.beforeEach((to: ToRouteType, _from, next) => {
|
||||
if (to.meta?.keepAlive) {
|
||||
handleAliveRoute(to, "add");
|
||||
// 页面整体刷新和点击标签页刷新
|
||||
if (_from.name === undefined || _from.name === "Redirect") {
|
||||
handleAliveRoute(to);
|
||||
}
|
||||
}
|
||||
const userInfo = storageLocal().getItem<DataInfo<number>>(userKey);
|
||||
NProgress.start();
|
||||
const externalLink = isUrl(to?.name as string);
|
||||
if (!externalLink) {
|
||||
to.matched.some(item => {
|
||||
if (!item.meta.title) return "";
|
||||
const Title = getConfig().Title;
|
||||
if (Title)
|
||||
document.title = `${transformI18n(item.meta.title)} | ${Title}`;
|
||||
else document.title = transformI18n(item.meta.title);
|
||||
});
|
||||
}
|
||||
/** 如果已经登录并存在登录信息后不能跳转到路由白名单,而是继续保持在当前页面 */
|
||||
function toCorrectRoute() {
|
||||
whiteList.includes(to.fullPath) ? next(_from.fullPath) : next();
|
||||
}
|
||||
if (Cookies.get(multipleTabsKey) && userInfo) {
|
||||
// 无权限跳转403页面
|
||||
if (to.meta?.roles && !isOneOfArray(to.meta?.roles, userInfo?.roles)) {
|
||||
next({ path: "/error/403" });
|
||||
}
|
||||
// 开启隐藏首页后在浏览器地址栏手动输入首页welcome路由则跳转到404页面
|
||||
if (VITE_HIDE_HOME === "true" && to.fullPath === "/welcome") {
|
||||
next({ path: "/error/404" });
|
||||
}
|
||||
if (_from?.name) {
|
||||
// name为超链接
|
||||
if (externalLink) {
|
||||
openLink(to?.name as string);
|
||||
NProgress.done();
|
||||
} else {
|
||||
toCorrectRoute();
|
||||
}
|
||||
} else {
|
||||
// 刷新
|
||||
if (
|
||||
usePermissionStoreHook().wholeMenus.length === 0 &&
|
||||
to.path !== "/login"
|
||||
) {
|
||||
initRouter().then((router: Router) => {
|
||||
if (!useMultiTagsStoreHook().getMultiTagsCache) {
|
||||
const { path } = to;
|
||||
const route = findRouteByPath(
|
||||
path,
|
||||
router.options.routes[0].children
|
||||
);
|
||||
getTopMenu(true);
|
||||
// query、params模式路由传参数的标签页不在此处处理
|
||||
if (route && route.meta?.title) {
|
||||
if (isAllEmpty(route.parentId) && route.meta?.backstage) {
|
||||
// 此处为动态顶级路由(目录)
|
||||
const { path, name, meta } = route.children[0];
|
||||
useMultiTagsStoreHook().handleTags("push", {
|
||||
path,
|
||||
name,
|
||||
meta
|
||||
});
|
||||
} else {
|
||||
const { path, name, meta } = route;
|
||||
useMultiTagsStoreHook().handleTags("push", {
|
||||
path,
|
||||
name,
|
||||
meta
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// 确保动态路由完全加入路由列表并且不影响静态路由(注意:动态路由刷新时router.beforeEach可能会触发两次,第一次触发动态路由还未完全添加,第二次动态路由才完全添加到路由列表,如果需要在router.beforeEach做一些判断可以在to.name存在的条件下去判断,这样就只会触发一次)
|
||||
if (isAllEmpty(to.name)) router.push(to.fullPath);
|
||||
});
|
||||
}
|
||||
toCorrectRoute();
|
||||
}
|
||||
} else {
|
||||
if (to.path !== "/login") {
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
next();
|
||||
} else {
|
||||
removeToken();
|
||||
next({ path: "/login" });
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
router.afterEach(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
|
||||
export default router;
|
||||
215
Yi.Pure.Vue3/src/router/modules/able.ts
Normal file
215
Yi.Pure.Vue3/src/router/modules/able.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { able } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/able",
|
||||
redirect: "/able/watermark",
|
||||
meta: {
|
||||
icon: "ri:ubuntu-fill",
|
||||
title: $t("menus.pureAble"),
|
||||
rank: able
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/able/mqtt-client",
|
||||
name: "MqttClient",
|
||||
component: () => import("@/views/able/mqtt-client.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureMqtt"),
|
||||
extraIcon: "IF-pure-iconfont-new svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/verify",
|
||||
name: "Verify",
|
||||
component: () => import("@/views/able/verify.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureVerify")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/watermark",
|
||||
name: "WaterMark",
|
||||
component: () => import("@/views/able/watermark.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureWatermark")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/print",
|
||||
name: "Print",
|
||||
component: () => import("@/views/able/print/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.purePrint")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/download",
|
||||
name: "Download",
|
||||
component: () => import("@/views/able/download.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureDownload")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/excel",
|
||||
name: "Excel",
|
||||
component: () => import("@/views/able/excel.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureExcel")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/ripple",
|
||||
name: "Ripple",
|
||||
component: () => import("@/views/able/ripple.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureRipple")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/debounce",
|
||||
name: "Debounce",
|
||||
component: () => import("@/views/able/debounce.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureDebounce")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/directives",
|
||||
name: "Directives",
|
||||
component: () => import("@/views/able/directives.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureOptimize")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/draggable",
|
||||
name: "Draggable",
|
||||
component: () => import("@/views/able/draggable.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureDraggable"),
|
||||
transition: {
|
||||
enterTransition: "animate__zoomIn",
|
||||
leaveTransition: "animate__zoomOut"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/pdf",
|
||||
name: "Pdf",
|
||||
component: () => import("@/views/able/pdf.vue"),
|
||||
meta: {
|
||||
title: $t("menus.purePdf")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/barcode",
|
||||
name: "BarCode",
|
||||
component: () => import("@/views/able/barcode.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureBarcode")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/qrcode",
|
||||
name: "QrCode",
|
||||
component: () => import("@/views/able/qrcode.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureQrcode")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/map",
|
||||
name: "MapPage",
|
||||
component: () => import("@/views/able/map.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureMap"),
|
||||
keepAlive: true,
|
||||
transition: {
|
||||
name: "fade"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/wavesurfer",
|
||||
name: "Wavesurfer",
|
||||
component: () => import("@/views/able/wavesurfer/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureWavesurfer")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/video",
|
||||
name: "VideoPage",
|
||||
component: () => import("@/views/able/video.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureVideo")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/video-frame",
|
||||
name: "VideoFrame",
|
||||
component: () => import("@/views/able/video-frame/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureVideoFrame")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/danmaku",
|
||||
name: "Danmaku",
|
||||
component: () => import("@/views/able/danmaku/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureDanmaku")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/infinite-scroll",
|
||||
name: "InfiniteScroll",
|
||||
component: () => import("@/views/able/infinite-scroll.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureInfiniteScroll")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/menu-tree",
|
||||
name: "MenuTree",
|
||||
component: () => import("@/views/able/menu-tree.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureMenuTree")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/line-tree",
|
||||
name: "LineTree",
|
||||
component: () => import("@/views/able/line-tree.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureLineTree")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/typeit",
|
||||
name: "Typeit",
|
||||
component: () => import("@/views/able/typeit.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureTypeit")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/sensitive",
|
||||
name: "Sensitive",
|
||||
component: () => import("@/views/able/sensitive.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureSensitive")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/able/pinyin",
|
||||
name: "Pinyin",
|
||||
component: () => import("@/views/able/pinyin.vue"),
|
||||
meta: {
|
||||
title: $t("menus.purePinyin")
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
22
Yi.Pure.Vue3/src/router/modules/about.ts
Normal file
22
Yi.Pure.Vue3/src/router/modules/about.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { about } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/about",
|
||||
redirect: "/about/index",
|
||||
meta: {
|
||||
icon: "ri:file-info-line",
|
||||
title: $t("menus.pureAbout"),
|
||||
rank: about
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/about/index",
|
||||
name: "About",
|
||||
component: () => import("@/views/about/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureAbout")
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
25
Yi.Pure.Vue3/src/router/modules/board.ts
Normal file
25
Yi.Pure.Vue3/src/router/modules/board.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { board } from "@/router/enums";
|
||||
const IFrame = () => import("@/layout/frame.vue");
|
||||
|
||||
export default {
|
||||
path: "/board",
|
||||
redirect: "/board/index",
|
||||
meta: {
|
||||
icon: "ri:artboard-line",
|
||||
title: $t("menus.pureBoard"),
|
||||
rank: board
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/board/index",
|
||||
name: "FrameBoard",
|
||||
component: IFrame,
|
||||
meta: {
|
||||
title: $t("menus.pureBoard"),
|
||||
keepAlive: true,
|
||||
frameSrc: "https://songlh.top/paint-board/"
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
265
Yi.Pure.Vue3/src/router/modules/components.ts
Normal file
265
Yi.Pure.Vue3/src/router/modules/components.ts
Normal file
@@ -0,0 +1,265 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { components } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/components",
|
||||
redirect: "/components/dialog",
|
||||
meta: {
|
||||
icon: "ep:menu",
|
||||
title: $t("menus.pureComponents"),
|
||||
rank: components
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/components/dialog",
|
||||
name: "DialogPage",
|
||||
component: () => import("@/views/components/dialog/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureDialog")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/message",
|
||||
name: "Message",
|
||||
component: () => import("@/views/components/message.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureMessage")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/upload",
|
||||
name: "PureUpload",
|
||||
component: () => import("@/views/components/upload/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureUpload")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/check-card",
|
||||
name: "CheckCard",
|
||||
component: () => import("@/views/components/check-card.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureCheckCard"),
|
||||
extraIcon: "IF-pure-iconfont-new svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/date-picker",
|
||||
name: "DatePicker",
|
||||
component: () => import("@/views/components/date-picker.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureDatePicker")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/datetime-picker",
|
||||
name: "DateTimePicker",
|
||||
component: () => import("@/views/components/datetime-picker.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureDateTimePicker")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/time-picker",
|
||||
name: "TimePicker",
|
||||
component: () => import("@/views/components/time-picker.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureTimePicker")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/icon-select",
|
||||
name: "IconSelect",
|
||||
component: () => import("@/views/components/icon-select.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureIconSelect")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/animatecss",
|
||||
name: "AnimateCss",
|
||||
component: () => import("@/views/components/animatecss.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureAnimatecss")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/cropping",
|
||||
name: "Cropping",
|
||||
component: () => import("@/views/components/cropping/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureCropping")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/segmented",
|
||||
name: "Segmented",
|
||||
component: () => import("@/views/components/segmented.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureSegmented")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/text",
|
||||
name: "PureText",
|
||||
component: () => import("@/views/components/text.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureText"),
|
||||
extraIcon: "IF-pure-iconfont-new svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/el-button",
|
||||
name: "PureButton",
|
||||
component: () => import("@/views/components/el-button.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureElButton")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/check-button",
|
||||
name: "CheckButton",
|
||||
component: () => import("@/views/components/check-button.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureCheckButton"),
|
||||
extraIcon: "IF-pure-iconfont-new svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/button",
|
||||
name: "ButtonPage",
|
||||
component: () => import("@/views/components/button.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureButton")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/progress",
|
||||
name: "PureProgress",
|
||||
component: () => import("@/views/components/progress.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureProgress")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/tag",
|
||||
name: "PureTag",
|
||||
component: () => import("@/views/components/tag.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureTag")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/statistic",
|
||||
name: "Statistic",
|
||||
component: () => import("@/views/components/statistic.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureStatistic")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/collapse",
|
||||
name: "Collapse",
|
||||
component: () => import("@/views/components/collapse.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureCollapse")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/cascader",
|
||||
name: "Cascader",
|
||||
component: () => import("@/views/components/cascader.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureCascader")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/color-picker",
|
||||
name: "ColorPicker",
|
||||
component: () => import("@/views/components/color-picker.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureColorPicker")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/selector",
|
||||
name: "Selector",
|
||||
component: () => import("@/views/components/selector.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureSelector")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/waterfall",
|
||||
name: "Waterfall",
|
||||
component: () => import("@/views/components/waterfall/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureWaterfall")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/split-pane",
|
||||
name: "SplitPane",
|
||||
component: () => import("@/views/components/split-pane.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureSplitPane")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/swiper",
|
||||
name: "Swiper",
|
||||
component: () => import("@/views/components/swiper.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureSwiper")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/timeline",
|
||||
name: "TimeLine",
|
||||
component: () => import("@/views/components/timeline.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureTimeline")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/count-to",
|
||||
name: "CountTo",
|
||||
component: () => import("@/views/components/count-to.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureCountTo")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/contextmenu",
|
||||
name: "ContextMenu",
|
||||
component: () => import("@/views/components/contextmenu/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureContextmenu")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/json-editor",
|
||||
name: "JsonEditor",
|
||||
component: () => import("@/views/components/json-editor.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureJsonEditor")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/seamless-scroll",
|
||||
name: "SeamlessScroll",
|
||||
component: () => import("@/views/components/seamless-scroll.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureSeamless")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/components/virtual-list",
|
||||
name: "VirtualList",
|
||||
component: () => import("@/views/components/virtual-list/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureVirtualList")
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
23
Yi.Pure.Vue3/src/router/modules/editor.ts
Normal file
23
Yi.Pure.Vue3/src/router/modules/editor.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { editor } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/editor",
|
||||
redirect: "/editor/index",
|
||||
meta: {
|
||||
icon: "ep:edit",
|
||||
title: $t("menus.pureEditor"),
|
||||
rank: editor
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/editor/index",
|
||||
name: "Editor",
|
||||
component: () => import("@/views/editor/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureEditor"),
|
||||
keepAlive: true
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
39
Yi.Pure.Vue3/src/router/modules/error.ts
Normal file
39
Yi.Pure.Vue3/src/router/modules/error.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { error } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/error",
|
||||
redirect: "/error/403",
|
||||
meta: {
|
||||
icon: "ri:information-line",
|
||||
// showLink: false,
|
||||
title: $t("menus.pureAbnormal"),
|
||||
rank: error
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/error/403",
|
||||
name: "403",
|
||||
component: () => import("@/views/error/403.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureFourZeroOne")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/error/404",
|
||||
name: "404",
|
||||
component: () => import("@/views/error/404.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureFourZeroFour")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/error/500",
|
||||
name: "500",
|
||||
component: () => import("@/views/error/500.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureFive")
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
22
Yi.Pure.Vue3/src/router/modules/flowchart.ts
Normal file
22
Yi.Pure.Vue3/src/router/modules/flowchart.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { flowchart } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/flow-chart",
|
||||
redirect: "/flow-chart/index",
|
||||
meta: {
|
||||
icon: "ep:set-up",
|
||||
title: $t("menus.pureFlowChart"),
|
||||
rank: flowchart
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/flow-chart/index",
|
||||
name: "FlowChart",
|
||||
component: () => import("@/views/flow-chart/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureFlowChart")
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
23
Yi.Pure.Vue3/src/router/modules/form.ts
Normal file
23
Yi.Pure.Vue3/src/router/modules/form.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { form } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/form",
|
||||
redirect: "/form/index",
|
||||
meta: {
|
||||
icon: "ri:edit-box-line",
|
||||
title: $t("menus.pureSchemaForm"),
|
||||
rank: form
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/form/index",
|
||||
name: "SchemaForm",
|
||||
component: () => import("@/views/schema-form/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureSchemaForm"),
|
||||
extraIcon: "IF-pure-iconfont-new svg"
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
27
Yi.Pure.Vue3/src/router/modules/formdesign.ts
Normal file
27
Yi.Pure.Vue3/src/router/modules/formdesign.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { formdesign } from "@/router/enums";
|
||||
const IFrame = () => import("@/layout/frame.vue");
|
||||
|
||||
export default {
|
||||
path: "/form-design",
|
||||
redirect: "/form-design/index",
|
||||
meta: {
|
||||
icon: "ri:terminal-window-line",
|
||||
title: $t("menus.pureFormDesign"),
|
||||
rank: formdesign
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/form-design/index",
|
||||
name: "FormDesign",
|
||||
component: IFrame,
|
||||
meta: {
|
||||
title: $t("menus.pureFormDesign"),
|
||||
keepAlive: true,
|
||||
frameSrc:
|
||||
"https://haixin-fang.github.io/vue-form-design/playground/index.html",
|
||||
frameLoading: false
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
23
Yi.Pure.Vue3/src/router/modules/ganttastic.ts
Normal file
23
Yi.Pure.Vue3/src/router/modules/ganttastic.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { ganttastic } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/ganttastic",
|
||||
redirect: "/ganttastic/index",
|
||||
meta: {
|
||||
icon: "ri:bar-chart-horizontal-line",
|
||||
title: $t("menus.pureGanttastic"),
|
||||
rank: ganttastic
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/ganttastic/index",
|
||||
name: "Ganttastic",
|
||||
component: () => import("@/views/ganttastic/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureGanttastic"),
|
||||
extraIcon: "IF-pure-iconfont-new svg"
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
22
Yi.Pure.Vue3/src/router/modules/guide.ts
Normal file
22
Yi.Pure.Vue3/src/router/modules/guide.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { guide } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/guide",
|
||||
redirect: "/guide/index",
|
||||
meta: {
|
||||
icon: "ep:guide",
|
||||
title: $t("menus.pureGuide"),
|
||||
rank: guide
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/guide/index",
|
||||
name: "Guide",
|
||||
component: () => import("@/views/guide/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureGuide")
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
27
Yi.Pure.Vue3/src/router/modules/home.ts
Normal file
27
Yi.Pure.Vue3/src/router/modules/home.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { home } from "@/router/enums";
|
||||
const { VITE_HIDE_HOME } = import.meta.env;
|
||||
const Layout = () => import("@/layout/index.vue");
|
||||
|
||||
export default {
|
||||
path: "/",
|
||||
name: "Home",
|
||||
component: Layout,
|
||||
redirect: "/welcome",
|
||||
meta: {
|
||||
icon: "ep:home-filled",
|
||||
title: $t("menus.pureHome"),
|
||||
rank: home
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/welcome",
|
||||
name: "Welcome",
|
||||
component: () => import("@/views/welcome/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureHome"),
|
||||
showLink: VITE_HIDE_HOME === "true" ? false : true
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
24
Yi.Pure.Vue3/src/router/modules/list.ts
Normal file
24
Yi.Pure.Vue3/src/router/modules/list.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { list } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/list",
|
||||
redirect: "/list/card",
|
||||
meta: {
|
||||
icon: "ri:list-check",
|
||||
title: $t("menus.pureList"),
|
||||
rank: list
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/list/card",
|
||||
name: "CardList",
|
||||
component: () => import("@/views/list/card/index.vue"),
|
||||
meta: {
|
||||
icon: "ri:bank-card-line",
|
||||
title: $t("menus.pureCardList"),
|
||||
showParent: true
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
22
Yi.Pure.Vue3/src/router/modules/menuoverflow.ts
Normal file
22
Yi.Pure.Vue3/src/router/modules/menuoverflow.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { menuoverflow } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/menuoverflow",
|
||||
redirect: "/menuoverflow/index",
|
||||
meta: {
|
||||
title: $t("menus.pureMenuOverflow"),
|
||||
rank: menuoverflow
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/menuoverflow/index",
|
||||
name: "MenuOverflow",
|
||||
component: () => import("@/views/menuoverflow/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureChildMenuOverflow"),
|
||||
showParent: true
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
25
Yi.Pure.Vue3/src/router/modules/mind.ts
Normal file
25
Yi.Pure.Vue3/src/router/modules/mind.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { mind } from "@/router/enums";
|
||||
const IFrame = () => import("@/layout/frame.vue");
|
||||
|
||||
export default {
|
||||
path: "/mind-map",
|
||||
redirect: "/mind-map/index",
|
||||
meta: {
|
||||
icon: "ri:mind-map",
|
||||
title: $t("menus.pureMindMap"),
|
||||
rank: mind
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/mind-map/index",
|
||||
name: "FrameMindMap",
|
||||
component: IFrame,
|
||||
meta: {
|
||||
title: $t("menus.pureMindMap"),
|
||||
keepAlive: true,
|
||||
frameSrc: "https://wanglin2.github.io/mind-map/#/"
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
81
Yi.Pure.Vue3/src/router/modules/nested.ts
Normal file
81
Yi.Pure.Vue3/src/router/modules/nested.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { nested } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/nested",
|
||||
redirect: "/nested/menu1/menu1-1",
|
||||
meta: {
|
||||
title: $t("menus.pureMenus"),
|
||||
icon: "ep:histogram",
|
||||
rank: nested
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/nested/menu1",
|
||||
meta: {
|
||||
title: $t("menus.pureMenu1"),
|
||||
keepAlive: true
|
||||
},
|
||||
redirect: "/nested/menu1/menu1-1",
|
||||
children: [
|
||||
{
|
||||
path: "/nested/menu1/menu1-1",
|
||||
component: () => import("@/views/nested/menu1/menu1-1/index.vue"),
|
||||
name: "Menu1-1",
|
||||
meta: {
|
||||
title: $t("menus.pureMenu1-1"),
|
||||
keepAlive: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/nested/menu1/menu1-2",
|
||||
redirect: "/nested/menu1/menu1-2/menu1-2-1",
|
||||
meta: {
|
||||
title: $t("menus.pureMenu1-2"),
|
||||
keepAlive: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/nested/menu1/menu1-2/menu1-2-1",
|
||||
component: () =>
|
||||
import("@/views/nested/menu1/menu1-2/menu1-2-1/index.vue"),
|
||||
name: "Menu1-2-1",
|
||||
meta: {
|
||||
title: $t("menus.pureMenu1-2-1"),
|
||||
keepAlive: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/nested/menu1/menu1-2/menu1-2-2",
|
||||
component: () =>
|
||||
import("@/views/nested/menu1/menu1-2/menu1-2-2/index.vue"),
|
||||
name: "Menu1-2-2",
|
||||
meta: {
|
||||
title: $t("menus.pureMenu1-2-2"),
|
||||
keepAlive: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/nested/menu1/menu1-3",
|
||||
component: () => import("@/views/nested/menu1/menu1-3/index.vue"),
|
||||
name: "Menu1-3",
|
||||
meta: {
|
||||
title: $t("menus.pureMenu1-3"),
|
||||
keepAlive: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/nested/menu2",
|
||||
name: "Menu2",
|
||||
component: () => import("@/views/nested/menu2/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureMenu2"),
|
||||
keepAlive: true
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
25
Yi.Pure.Vue3/src/router/modules/ppt.ts
Normal file
25
Yi.Pure.Vue3/src/router/modules/ppt.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { ppt } from "@/router/enums";
|
||||
const IFrame = () => import("@/layout/frame.vue");
|
||||
|
||||
export default {
|
||||
path: "/ppt",
|
||||
redirect: "/ppt/index",
|
||||
meta: {
|
||||
icon: "ri:file-ppt-2-line",
|
||||
title: "PPT",
|
||||
rank: ppt
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/ppt/index",
|
||||
name: "FramePpt",
|
||||
component: IFrame,
|
||||
meta: {
|
||||
title: "PPT",
|
||||
keepAlive: true,
|
||||
frameSrc: "https://pipipi-pikachu.github.io/PPTist/",
|
||||
frameLoading: false
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
52
Yi.Pure.Vue3/src/router/modules/remaining.ts
Normal file
52
Yi.Pure.Vue3/src/router/modules/remaining.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
const Layout = () => import("@/layout/index.vue");
|
||||
|
||||
export default [
|
||||
{
|
||||
path: "/login",
|
||||
name: "Login",
|
||||
component: () => import("@/views/login/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureLogin"),
|
||||
showLink: false,
|
||||
rank: 101
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/redirect",
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: $t("status.pureLoad"),
|
||||
showLink: false,
|
||||
rank: 102
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/redirect/:path(.*)",
|
||||
name: "Redirect",
|
||||
component: () => import("@/layout/redirect.vue")
|
||||
}
|
||||
]
|
||||
},
|
||||
// 下面是一个无layout菜单的例子(一个全屏空白页面),因为这种情况极少发生,所以只需要在前端配置即可(配置路径:src/router/modules/remaining.ts)
|
||||
{
|
||||
path: "/empty",
|
||||
name: "Empty",
|
||||
component: () => import("@/views/empty/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureEmpty"),
|
||||
showLink: false,
|
||||
rank: 103
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/account-settings",
|
||||
name: "AccountSettings",
|
||||
component: () => import("@/views/account-settings/index.vue"),
|
||||
meta: {
|
||||
title: $t("buttons.pureAccountSettings"),
|
||||
showLink: false,
|
||||
rank: 104
|
||||
}
|
||||
}
|
||||
] satisfies Array<RouteConfigsTable>;
|
||||
30
Yi.Pure.Vue3/src/router/modules/result.ts
Normal file
30
Yi.Pure.Vue3/src/router/modules/result.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { result } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/result",
|
||||
redirect: "/result/success",
|
||||
meta: {
|
||||
icon: "ri:checkbox-circle-line",
|
||||
title: $t("menus.pureResult"),
|
||||
rank: result
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/result/success",
|
||||
name: "Success",
|
||||
component: () => import("@/views/result/success.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureSuccess")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/result/fail",
|
||||
name: "Fail",
|
||||
component: () => import("@/views/result/fail.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureFail")
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
48
Yi.Pure.Vue3/src/router/modules/table.ts
Normal file
48
Yi.Pure.Vue3/src/router/modules/table.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
import { table } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/table",
|
||||
redirect: "/table/index",
|
||||
meta: {
|
||||
icon: "ri:table-line",
|
||||
title: $t("menus.pureTable"),
|
||||
rank: table
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/table/index",
|
||||
name: "PureTable",
|
||||
component: () => import("@/views/table/index.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureTableBase")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/table/high",
|
||||
name: "PureTableHigh",
|
||||
component: () => import("@/views/table/high.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureTableHigh")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/table/edit",
|
||||
name: "PureTableEdit",
|
||||
component: () => import("@/views/table/edit.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureTableEdit"),
|
||||
extraIcon: "IF-pure-iconfont-new svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/table/virtual",
|
||||
name: "VxeTable",
|
||||
component: () => import("@/views/table/virtual.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureVxeTable"),
|
||||
extraIcon: "IF-pure-iconfont-new svg"
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
22
Yi.Pure.Vue3/src/router/modules/vueflow.ts
Normal file
22
Yi.Pure.Vue3/src/router/modules/vueflow.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { vueflow } from "@/router/enums";
|
||||
|
||||
export default {
|
||||
path: "/vue-flow",
|
||||
redirect: "/vue-flow/index",
|
||||
meta: {
|
||||
icon: "ep:set-up",
|
||||
title: "vue-flow",
|
||||
rank: vueflow
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/vue-flow/index",
|
||||
name: "VueFlow",
|
||||
component: () => import("@/views/vue-flow/layouting/index.vue"),
|
||||
meta: {
|
||||
title: "vue-flow",
|
||||
extraIcon: "IF-pure-iconfont-new svg"
|
||||
}
|
||||
}
|
||||
]
|
||||
} satisfies RouteConfigsTable;
|
||||
408
Yi.Pure.Vue3/src/router/utils.ts
Normal file
408
Yi.Pure.Vue3/src/router/utils.ts
Normal file
@@ -0,0 +1,408 @@
|
||||
import {
|
||||
type RouterHistory,
|
||||
type RouteRecordRaw,
|
||||
type RouteComponent,
|
||||
createWebHistory,
|
||||
createWebHashHistory
|
||||
} from "vue-router";
|
||||
import { router } from "./index";
|
||||
import { isProxy, toRaw } from "vue";
|
||||
import { useTimeoutFn } from "@vueuse/core";
|
||||
import {
|
||||
isString,
|
||||
cloneDeep,
|
||||
isAllEmpty,
|
||||
intersection,
|
||||
storageLocal,
|
||||
isIncludeAllChildren
|
||||
} from "@pureadmin/utils";
|
||||
import { getConfig } from "@/config";
|
||||
import { buildHierarchyTree } from "@/utils/tree";
|
||||
import { userKey, type DataInfo } from "@/utils/auth";
|
||||
import { type menuType, routerArrays } from "@/layout/types";
|
||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||
const IFrame = () => import("@/layout/frame.vue");
|
||||
// https://cn.vitejs.dev/guide/features.html#glob-import
|
||||
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
|
||||
|
||||
// 动态路由
|
||||
import { getAsyncRoutes } from "@/api/routes";
|
||||
|
||||
function handRank(routeInfo: any) {
|
||||
const { name, path, parentId, meta } = routeInfo;
|
||||
return isAllEmpty(parentId)
|
||||
? isAllEmpty(meta?.rank) ||
|
||||
(meta?.rank === 0 && name !== "Home" && path !== "/")
|
||||
? true
|
||||
: false
|
||||
: false;
|
||||
}
|
||||
|
||||
/** 按照路由中meta下的rank等级升序来排序路由 */
|
||||
function ascending(arr: any[]) {
|
||||
arr.forEach((v, index) => {
|
||||
// 当rank不存在时,根据顺序自动创建,首页路由永远在第一位
|
||||
if (handRank(v)) v.meta.rank = index + 2;
|
||||
});
|
||||
return arr.sort(
|
||||
(a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
|
||||
return a?.meta.rank - b?.meta.rank;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/** 过滤meta中showLink为false的菜单 */
|
||||
function filterTree(data: RouteComponent[]) {
|
||||
const newTree = cloneDeep(data).filter(
|
||||
(v: { meta: { showLink: boolean } }) => v.meta?.showLink !== false
|
||||
);
|
||||
newTree.forEach(
|
||||
(v: { children }) => v.children && (v.children = filterTree(v.children))
|
||||
);
|
||||
return newTree;
|
||||
}
|
||||
|
||||
/** 过滤children长度为0的的目录,当目录下没有菜单时,会过滤此目录,目录没有赋予roles权限,当目录下只要有一个菜单有显示权限,那么此目录就会显示 */
|
||||
function filterChildrenTree(data: RouteComponent[]) {
|
||||
const newTree = cloneDeep(data).filter((v: any) => v?.children?.length !== 0);
|
||||
newTree.forEach(
|
||||
(v: { children }) => v.children && (v.children = filterTree(v.children))
|
||||
);
|
||||
return newTree;
|
||||
}
|
||||
|
||||
/** 判断两个数组彼此是否存在相同值 */
|
||||
function isOneOfArray(a: Array<string>, b: Array<string>) {
|
||||
return Array.isArray(a) && Array.isArray(b)
|
||||
? intersection(a, b).length > 0
|
||||
? true
|
||||
: false
|
||||
: true;
|
||||
}
|
||||
|
||||
/** 从localStorage里取出当前登录用户的角色roles,过滤无权限的菜单 */
|
||||
function filterNoPermissionTree(data: RouteComponent[]) {
|
||||
const currentRoles =
|
||||
storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [];
|
||||
const newTree = cloneDeep(data).filter((v: any) =>
|
||||
isOneOfArray(v.meta?.roles, currentRoles)
|
||||
);
|
||||
newTree.forEach(
|
||||
(v: any) => v.children && (v.children = filterNoPermissionTree(v.children))
|
||||
);
|
||||
return filterChildrenTree(newTree);
|
||||
}
|
||||
|
||||
/** 通过指定 `key` 获取父级路径集合,默认 `key` 为 `path` */
|
||||
function getParentPaths(value: string, routes: RouteRecordRaw[], key = "path") {
|
||||
// 深度遍历查找
|
||||
function dfs(routes: RouteRecordRaw[], value: string, parents: string[]) {
|
||||
for (let i = 0; i < routes.length; i++) {
|
||||
const item = routes[i];
|
||||
// 返回父级path
|
||||
if (item[key] === value) return parents;
|
||||
// children不存在或为空则不递归
|
||||
if (!item.children || !item.children.length) continue;
|
||||
// 往下查找时将当前path入栈
|
||||
parents.push(item.path);
|
||||
|
||||
if (dfs(item.children, value, parents).length) return parents;
|
||||
// 深度遍历查找未找到时当前path 出栈
|
||||
parents.pop();
|
||||
}
|
||||
// 未找到时返回空数组
|
||||
return [];
|
||||
}
|
||||
|
||||
return dfs(routes, value, []);
|
||||
}
|
||||
|
||||
/** 查找对应 `path` 的路由信息 */
|
||||
function findRouteByPath(path: string, routes: RouteRecordRaw[]) {
|
||||
let res = routes.find((item: { path: string }) => item.path == path);
|
||||
if (res) {
|
||||
return isProxy(res) ? toRaw(res) : res;
|
||||
} else {
|
||||
for (let i = 0; i < routes.length; i++) {
|
||||
if (
|
||||
routes[i].children instanceof Array &&
|
||||
routes[i].children.length > 0
|
||||
) {
|
||||
res = findRouteByPath(path, routes[i].children);
|
||||
if (res) {
|
||||
return isProxy(res) ? toRaw(res) : res;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function addPathMatch() {
|
||||
if (!router.hasRoute("pathMatch")) {
|
||||
router.addRoute({
|
||||
path: "/:pathMatch(.*)",
|
||||
name: "pathMatch",
|
||||
redirect: "/error/404"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理动态路由(后端返回的路由) */
|
||||
function handleAsyncRoutes(routeList) {
|
||||
if (routeList.length === 0) {
|
||||
usePermissionStoreHook().handleWholeMenus(routeList);
|
||||
} else {
|
||||
formatFlatteningRoutes(addAsyncRoutes(routeList)).map(
|
||||
(v: RouteRecordRaw) => {
|
||||
// 防止重复添加路由
|
||||
if (
|
||||
router.options.routes[0].children.findIndex(
|
||||
value => value.path === v.path
|
||||
) !== -1
|
||||
) {
|
||||
return;
|
||||
} else {
|
||||
// 切记将路由push到routes后还需要使用addRoute,这样路由才能正常跳转
|
||||
router.options.routes[0].children.push(v);
|
||||
// 最终路由进行升序
|
||||
ascending(router.options.routes[0].children);
|
||||
if (!router.hasRoute(v?.name)) router.addRoute(v);
|
||||
const flattenRouters: any = router
|
||||
.getRoutes()
|
||||
.find(n => n.path === "/");
|
||||
router.addRoute(flattenRouters);
|
||||
}
|
||||
}
|
||||
);
|
||||
usePermissionStoreHook().handleWholeMenus(routeList);
|
||||
}
|
||||
if (!useMultiTagsStoreHook().getMultiTagsCache) {
|
||||
useMultiTagsStoreHook().handleTags("equal", [
|
||||
...routerArrays,
|
||||
...usePermissionStoreHook().flatteningRoutes.filter(
|
||||
v => v?.meta?.fixedTag
|
||||
)
|
||||
]);
|
||||
}
|
||||
addPathMatch();
|
||||
}
|
||||
|
||||
/** 初始化路由(`new Promise` 写法防止在异步请求中造成无限循环)*/
|
||||
function initRouter() {
|
||||
if (getConfig()?.CachingAsyncRoutes) {
|
||||
// 开启动态路由缓存本地localStorage
|
||||
const key = "async-routes";
|
||||
const asyncRouteList = storageLocal().getItem(key) as any;
|
||||
if (asyncRouteList && asyncRouteList?.length > 0) {
|
||||
return new Promise(resolve => {
|
||||
handleAsyncRoutes(asyncRouteList);
|
||||
resolve(router);
|
||||
});
|
||||
} else {
|
||||
return new Promise(resolve => {
|
||||
getAsyncRoutes().then(({ data }) => {
|
||||
handleAsyncRoutes(cloneDeep(data));
|
||||
storageLocal().setItem(key, data);
|
||||
resolve(router);
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return new Promise(resolve => {
|
||||
getAsyncRoutes().then(({ data }) => {
|
||||
handleAsyncRoutes(cloneDeep(data));
|
||||
resolve(router);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将多级嵌套路由处理成一维数组
|
||||
* @param routesList 传入路由
|
||||
* @returns 返回处理后的一维路由
|
||||
*/
|
||||
function formatFlatteningRoutes(routesList: RouteRecordRaw[]) {
|
||||
if (routesList.length === 0) return routesList;
|
||||
let hierarchyList = buildHierarchyTree(routesList);
|
||||
for (let i = 0; i < hierarchyList.length; i++) {
|
||||
if (hierarchyList[i].children) {
|
||||
hierarchyList = hierarchyList
|
||||
.slice(0, i + 1)
|
||||
.concat(hierarchyList[i].children, hierarchyList.slice(i + 1));
|
||||
}
|
||||
}
|
||||
return hierarchyList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 一维数组处理成多级嵌套数组(三级及以上的路由全部拍成二级,keep-alive 只支持到二级缓存)
|
||||
* https://github.com/pure-admin/vue-pure-admin/issues/67
|
||||
* @param routesList 处理后的一维路由菜单数组
|
||||
* @returns 返回将一维数组重新处理成规定路由的格式
|
||||
*/
|
||||
function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
|
||||
if (routesList.length === 0) return routesList;
|
||||
const newRoutesList: RouteRecordRaw[] = [];
|
||||
routesList.forEach((v: RouteRecordRaw) => {
|
||||
if (v.path === "/") {
|
||||
newRoutesList.push({
|
||||
component: v.component,
|
||||
name: v.name,
|
||||
path: v.path,
|
||||
redirect: v.redirect,
|
||||
meta: v.meta,
|
||||
children: []
|
||||
});
|
||||
} else {
|
||||
newRoutesList[0]?.children.push({ ...v });
|
||||
}
|
||||
});
|
||||
return newRoutesList;
|
||||
}
|
||||
|
||||
/** 处理缓存路由(添加、删除、刷新) */
|
||||
function handleAliveRoute({ name }: ToRouteType, mode?: string) {
|
||||
switch (mode) {
|
||||
case "add":
|
||||
usePermissionStoreHook().cacheOperate({
|
||||
mode: "add",
|
||||
name
|
||||
});
|
||||
break;
|
||||
case "delete":
|
||||
usePermissionStoreHook().cacheOperate({
|
||||
mode: "delete",
|
||||
name
|
||||
});
|
||||
break;
|
||||
case "refresh":
|
||||
usePermissionStoreHook().cacheOperate({
|
||||
mode: "refresh",
|
||||
name
|
||||
});
|
||||
break;
|
||||
default:
|
||||
usePermissionStoreHook().cacheOperate({
|
||||
mode: "delete",
|
||||
name
|
||||
});
|
||||
useTimeoutFn(() => {
|
||||
usePermissionStoreHook().cacheOperate({
|
||||
mode: "add",
|
||||
name
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
/** 过滤后端传来的动态路由 重新生成规范路由 */
|
||||
function addAsyncRoutes(arrRoutes: Array<RouteRecordRaw>) {
|
||||
if (!arrRoutes || !arrRoutes.length) return;
|
||||
const modulesRoutesKeys = Object.keys(modulesRoutes);
|
||||
arrRoutes.forEach((v: RouteRecordRaw) => {
|
||||
// 将backstage属性加入meta,标识此路由为后端返回路由
|
||||
v.meta.backstage = true;
|
||||
// 父级的redirect属性取值:如果子级存在且父级的redirect属性不存在,默认取第一个子级的path;如果子级存在且父级的redirect属性存在,取存在的redirect属性,会覆盖默认值
|
||||
if (v?.children && v.children.length && !v.redirect)
|
||||
v.redirect = v.children[0].path;
|
||||
// 父级的name属性取值:如果子级存在且父级的name属性不存在,默认取第一个子级的name;如果子级存在且父级的name属性存在,取存在的name属性,会覆盖默认值(注意:测试中发现父级的name不能和子级name重复,如果重复会造成重定向无效(跳转404),所以这里给父级的name起名的时候后面会自动加上`Parent`,避免重复)
|
||||
if (v?.children && v.children.length && !v.name)
|
||||
v.name = (v.children[0].name as string) + "Parent";
|
||||
if (v.meta?.frameSrc) {
|
||||
v.component = IFrame;
|
||||
} else {
|
||||
// 对后端传component组件路径和不传做兼容(如果后端传component组件路径,那么path可以随便写,如果不传,component组件路径会跟path保持一致)
|
||||
const index = v?.component
|
||||
? modulesRoutesKeys.findIndex(ev => ev.includes(v.component as any))
|
||||
: modulesRoutesKeys.findIndex(ev => ev.includes(v.path));
|
||||
v.component = modulesRoutes[modulesRoutesKeys[index]];
|
||||
}
|
||||
if (v?.children && v.children.length) {
|
||||
addAsyncRoutes(v.children);
|
||||
}
|
||||
});
|
||||
return arrRoutes;
|
||||
}
|
||||
|
||||
/** 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html */
|
||||
function getHistoryMode(routerHistory): RouterHistory {
|
||||
// len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1
|
||||
const historyMode = routerHistory.split(",");
|
||||
const leftMode = historyMode[0];
|
||||
const rightMode = historyMode[1];
|
||||
// no param
|
||||
if (historyMode.length === 1) {
|
||||
if (leftMode === "hash") {
|
||||
return createWebHashHistory("");
|
||||
} else if (leftMode === "h5") {
|
||||
return createWebHistory("");
|
||||
}
|
||||
} //has param
|
||||
else if (historyMode.length === 2) {
|
||||
if (leftMode === "hash") {
|
||||
return createWebHashHistory(rightMode);
|
||||
} else if (leftMode === "h5") {
|
||||
return createWebHistory(rightMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取当前页面按钮级别的权限 */
|
||||
function getAuths(): Array<string> {
|
||||
return router.currentRoute.value.meta.auths as Array<string>;
|
||||
}
|
||||
|
||||
/** 是否有按钮级别的权限(根据路由`meta`中的`auths`字段进行判断)*/
|
||||
function hasAuth(value: string | Array<string>): boolean {
|
||||
if (!value) return false;
|
||||
/** 从当前路由的`meta`字段里获取按钮级别的所有自定义`code`值 */
|
||||
const metaAuths = getAuths();
|
||||
if (!metaAuths) return false;
|
||||
const isAuths = isString(value)
|
||||
? metaAuths.includes(value)
|
||||
: isIncludeAllChildren(value, metaAuths);
|
||||
return isAuths ? true : false;
|
||||
}
|
||||
|
||||
function handleTopMenu(route) {
|
||||
if (route?.children && route.children.length > 1) {
|
||||
if (route.redirect) {
|
||||
return route.children.filter(cur => cur.path === route.redirect)[0];
|
||||
} else {
|
||||
return route.children[0];
|
||||
}
|
||||
} else {
|
||||
return route;
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取所有菜单中的第一个菜单(顶级菜单)*/
|
||||
function getTopMenu(tag = false): menuType {
|
||||
const topMenu = handleTopMenu(
|
||||
usePermissionStoreHook().wholeMenus[0]?.children[0]
|
||||
);
|
||||
tag && useMultiTagsStoreHook().handleTags("push", topMenu);
|
||||
return topMenu;
|
||||
}
|
||||
|
||||
export {
|
||||
hasAuth,
|
||||
getAuths,
|
||||
ascending,
|
||||
filterTree,
|
||||
initRouter,
|
||||
getTopMenu,
|
||||
addPathMatch,
|
||||
isOneOfArray,
|
||||
getHistoryMode,
|
||||
addAsyncRoutes,
|
||||
getParentPaths,
|
||||
findRouteByPath,
|
||||
handleAliveRoute,
|
||||
formatTwoStageRoutes,
|
||||
formatFlatteningRoutes,
|
||||
filterNoPermissionTree
|
||||
};
|
||||
Reference in New Issue
Block a user