123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- 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 = <T extends Component>(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<T extends Record<string, any>, U>(props: T): Partial<U> {
- const ret: Recordable = {};
- Object.keys(props).forEach((key: any) => {
- ret[key] = unref((props as Recordable)[key]);
- });
- return ret as Partial<U>;
- }
- export function deepMerge<T = any>(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));
- }
|