cpms_operation_platform/src/router/utils.ts

90 lines
2.4 KiB
TypeScript
Raw Normal View History

import { h, type Component, type VNode } from 'vue';
2026-07-14 10:31:17 +08:00
import type { RouteRecordRaw } from 'vue-router';
/** 路由 meta 类型 */
export type AppMeta = {
/** 页面 / 菜单标题,同时用于面包屑 */
2026-07-14 10:31:17 +08:00
title: string;
/**
*
* - iconfont class 'icon-changguan1'
* - ant-design BarChartOutlined
*/
icon?: Component | string;
/** 是否在侧边栏菜单中隐藏(详情页等) */
2026-07-14 10:31:17 +08:00
hideInMenu?: boolean;
/** 高亮的菜单 key路径不等于菜单 key 的场景,如详情页) */
2026-07-14 10:31:17 +08:00
activeMenu?: string;
};
/**
*
* PascalCase name
*/
export function createRoute(
path: string,
component: RouteRecordRaw['component'],
meta: AppMeta,
children: RouteRecordRaw[] = [],
): RouteRecordRaw {
const cleanPath = path.replace(/[:/]/g, '-');
const name = cleanPath
.split('-')
.filter(Boolean)
.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
.join('');
return {
path,
name: name || undefined,
meta,
component,
children,
};
}
/** 侧边栏菜单项ant-design-vue Menu 的 items 结构) */
export interface MenuItem {
key: string;
label: string;
icon?: VNode;
children?: MenuItem[];
}
/**
*
*
* @description
* /
* - meta.title hideInMenu
* -
* - meta.icon
*
* @param routes children
*/
export function generateMenu(routes: RouteRecordRaw[] = []): MenuItem[] {
const items: MenuItem[] = [];
for (const route of routes) {
const meta = route.meta as AppMeta | undefined;
if (!meta?.title || meta.hideInMenu) {
continue;
}
const children = generateMenu(route.children);
const item: MenuItem = { key: route.path, label: meta.title };
if (meta.icon) {
// 字符串 -> iconfont 图标(<i class="iconfont icon-xxx" />);组件 -> 直接渲染
item.icon =
typeof meta.icon === 'string' ? h('i', { class: ['iconfont', meta.icon] }) : h(meta.icon);
}
if (children.length) {
item.children = children;
}
items.push(item);
}
return items;
}