import { h, unref } from 'vue'; import type { App, Component, Plugin } from 'vue'; import { NIcon, NTag } from 'naive-ui'; import { isObject } from './is/index'; /** * render 图标 * */ export function renderIcon(icon: any) { return () => h(NIcon, null, { default: () => h(icon) }); } /** * font 图标(Font class) * */ export function renderFontClassIcon(icon: string, iconName = 'iconfont') { return () => h('span', { class: [iconName, icon] }); } /** * font 图标(Unicode) * */ export function renderUnicodeIcon(icon: string, iconName = 'iconfont') { return () => h('span', { class: [iconName], innerHTML: icon }); } /** * font svg 图标 * */ export function renderfontsvg(icon: any) { return () => h(NIcon, null, { default: () => h('svg', { class: `icon`, 'aria-hidden': 'true' }, h('use', { 'xlink:href': `#${icon}` })) }); } /** * render new Tag * */ const newTagColors = { color: '#f90', textColor: '#fff', borderColor: '#f90' }; export function renderNew(type = 'warning', text = 'New', color: object = newTagColors) { return () => h( NTag as any, { type, round: true, size: 'small', color }, { default: () => text } ); } export const withInstall = (component: T, alias?: string) => { const comp = component as any; comp.install = (app: App) => { app.component(comp.name || comp.displayName, component); if (alias) { app.config.globalProperties[alias] = component; } }; return component as T & Plugin; }; /** * 找到所有节点 * */ const treeAll: any[] = []; export function getTreeAll(data: any[]): any[] { data.forEach(item => { treeAll.push(item.key); if (item.children && item.children.length) { getTreeAll(item.children); } }); return treeAll; } // dynamic use hook props export function getDynamicProps, U>(props: T): Partial { const ret: Recordable = {}; Object.keys(props).forEach((key: any) => { ret[key] = unref((props as Recordable)[key]); }); return ret as Partial; } export function deepMerge(src: any = {}, target: any = {}): T { let key: string; for (key in target) { if (Object.hasOwn(target, key)) { src[key] = isObject(src[key]) ? deepMerge(src[key], target[key]) : (src[key] = target[key]); } } return src; } /** * Sums the passed percentage to the R, G or B of a HEX color * @param {string} color The color to change * @param {number} amount The amount to change the color by * @returns {string} The processed part of the color */ function addLight(color: string, amount: number) { const cc = Number.parseInt(color, 16) + amount; const c = cc > 255 ? 255 : cc; return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`; } /** * Lightens a 6 char HEX color according to the passed percentage * @param {string} color The color to change * @param {number} amount The amount to change the color by * @returns {string} The processed color represented as HEX */ export function lighten(color: string, amount: number) { const hexColor = color.includes('#') ? color.substring(1, color.length) : color; const adjustedAmount = Math.trunc((255 * amount) / 100); return `#${addLight(hexColor.substring(0, 2), adjustedAmount)}${addLight( hexColor.substring(2, 4), adjustedAmount )}${addLight(hexColor.substring(4, 6), adjustedAmount)}`; } /** * 判断是否 url * */ export function isUrl(url: string) { return /^(http|https):\/\//g.test(url); } /** * 获取uuid */ export function getUUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { const r = Math.random() * 16; const v = c === 'x' ? Math.floor(r) : Math.floor((r * 4) % 4) + 8; return v.toString(16); }); } interface OriginalMenuItem { menuId: number; parentId: number; parentName: string | null; name: string; url: string; perms: string; type: number; icon: string | null; orderNum: number; list: OriginalMenuItem[] | null; component: string; hideInMenu: boolean; } interface OriginalMenuData { menuList: OriginalMenuItem[]; } interface ConvertedMenuItem { name: string; path: string; component: string; meta: { title: string; // i18nKey: string; icon?: string; order?: number; hideInMenu?: boolean; }; children?: ConvertedMenuItem[]; } export function convertMenuData(originalData: OriginalMenuData): ConvertedMenuItem[] { // 首先过滤出顶级菜单 (parentId === 0) const topLevelMenus = originalData.menuList.filter(menu => menu.parentId === 0); return topLevelMenus.map(menu => { return convertMenuItem(menu, originalData.menuList); }); } function convertMenuItem(menu: OriginalMenuItem, allMenus: OriginalMenuItem[]): ConvertedMenuItem { // 生成路由名称 (使用小写字母和下划线) const routeName = menu.url .replace(/^\/+/, '') // 去掉开头的一个或多个 '/' .replace(/[/\s]/g, '_') // 替换剩余的 '/' 和空格为 '_' .toLowerCase(); // 生成路由路径 let routePath = `${routeName}`; if (menu.url && menu.url.trim() !== '') { routePath = `${menu.url}`; } // 转换基本信息 const converted: ConvertedMenuItem = { name: routeName, path: routePath, component: menu.component, meta: { title: menu.name, // i18nKey: `route.${routeName}`, order: menu.orderNum, hideInMenu: menu.hideInMenu, icon: String(menu.icon) } }; // 处理子菜单 if (menu.list && menu.list.length > 0) { converted.children = menu.list.map(child => convertMenuItem(child, allMenus)); } else { // 如果没有直接子菜单,但可能有其他关联的子菜单 const children = allMenus.filter(m => m.parentId === menu.menuId); if (children.length > 0) { converted.children = children.map(child => convertMenuItem(child, allMenus)); } } return converted; } /** * 菜单转为树型结构 * @param data * @returns */ export function buildMenuTree(data: any) { // 创建根节点数组和映射表 const rootNodes: any = []; const nodeMap: any = {}; // 首先将所有节点存入映射表 data.forEach((item: any) => { nodeMap[item.menuId] = { ...item }; }); // 构建树形结构 data.forEach((item: any) => { const node = nodeMap[item.menuId]; if (item.parentId === 0) { // 根节点 rootNodes.push(node); } else { // 子节点,找到父节点并添加到父节点的children中 const parent = nodeMap[item.parentId]; if (parent) { if (!parent.children) { parent.children = []; } parent.children.push(node); } else { // 如果父节点不存在,也作为根节点 rootNodes.push(node); } } }); // 删除空children字段 const removeEmptyChildren = (nodes: any) => { nodes.forEach((node: any) => { if (node.children && node.children.length === 0) { delete node.children; } else if (node.children) { removeEmptyChildren(node.children); } }); return nodes; }; // 对根节点和子节点按orderNum排序 const sortByOrderNum = (nodes: any) => { return nodes .sort((a: any, b: any) => a.orderNum - b.orderNum) .map((node: any) => { if (node.children) { node.children = sortByOrderNum(node.children); } return node; }); }; return removeEmptyChildren(sortByOrderNum(rootNodes)); }