front/src/utils/menu-icon.ts
zzh e356ddb9cf feat(order): 订单批量任务管理页与 SPA 路由配置
新增批量任务列表/详情治理界面,补齐 API、权限菜单与 nginx/vite 前端路由白名单。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-08 12:42:28 +08:00

162 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as Icons from '@arco-design/web-vue/es/icon'
import type { Component } from 'vue'
/** 一级菜单默认图标(按 locale key) */
export const TOP_MENU_DEFAULT_ICONS: Record<string, string> = {
'menu.home': 'IconDashboard',
'menu.user': 'IconUser',
'menu.finance': 'IconStorage',
'menu.order': 'IconFile',
'menu.channel': 'IconLink',
'menu.track': 'IconComputer',
'menu.operation': 'IconSettings',
'menu.api': 'IconComputer',
'menu.system': 'IconSettings',
'menu.xhs': 'IconBook',
}
/** 子菜单默认图标(按 locale key) */
export const SUB_MENU_DEFAULT_ICONS: Record<string, string> = {
'menu.home': 'IconHome',
'menu.user.customer': 'IconUserGroup',
'menu.user.profile': 'IconUser',
'menu.user.recharge': 'IconGift',
'menu.finance.rechargeRecord': 'IconList',
'menu.finance.accountFlow': 'IconSwap',
'menu.finance.receivablePayable': 'IconFile',
'menu.order.priceCheck': 'IconSearch',
'menu.order.placeOrder': 'IconPlus',
'menu.order.myOrders': 'IconOrderedList',
'menu.order.batchJobs': 'IconSchedule',
'menu.channel.salesProducts': 'IconTag',
'menu.channel.serviceChannels': 'IconSend',
'menu.channel.priceArea': 'IconLocation',
'menu.channel.blacklist': 'IconCloseCircle',
'menu.channel.remoteZones': 'IconCompass',
'menu.channel.weightRules': 'IconExperiment',
'menu.channel.weightRounding': 'IconCode',
'menu.channel.autoTrigger': 'IconThunderbolt',
'menu.channel.issueTemplate': 'IconFile',
'menu.track.trackGuide': 'IconBook',
'menu.track.trackOrder': 'IconList',
'menu.track.warningOrder': 'IconExclamationCircle',
'menu.track.trackState': 'IconCheckCircle',
'menu.track.trackFlag': 'IconFlag',
'menu.track.trackContent': 'IconMessage',
'menu.track.trackPull': 'IconDownload',
'menu.track.trackPush': 'IconUpload',
'menu.track.kpiRegion': 'IconBarChart',
'menu.track.kpiEarlyWarningConfig': 'IconSettings',
'menu.operation.inboundScan': 'IconImport',
'menu.operation.outboundScan': 'IconExport',
'menu.operation.printLabel': 'IconPrinter',
'menu.api.upstreamInterface': 'IconCloud',
'menu.api.downstreamInterface': 'IconCloudDownload',
'menu.api.createInterface': 'IconPlus',
'menu.api.editInterface': 'IconEdit',
'menu.api.appList': 'IconApps',
'menu.api.requestLog': 'IconFile',
'menu.api.callStatistics': 'IconBarChart',
'menu.api.createApplication': 'IconPlusCircle',
'menu.api.apiDebugger': 'IconBug',
'menu.api.apiLike': 'IconCode',
'menu.api.apiTest': 'IconExperiment',
'menu.system.organization': 'IconUserGroup',
'menu.system.user': 'IconUser',
'menu.system.position': 'IconIdcard',
'menu.system.role': 'IconSafe',
'menu.system.module': 'IconApps',
'menu.system.menu': 'IconMenu',
'menu.system.warehouseAddress': 'IconHome',
'menu.system.otherConfig': 'IconSettings',
'menu.system.xhsAiAssistant': 'IconRobot',
'menu.system.dataPerm': 'IconLock',
'menu.system.dataPermUsers': 'IconUser',
'menu.system.dataPermTemplates': 'IconFile',
'menu.system.dataPermAudit': 'IconHistory',
'menu.xhs.startService': 'IconPlayArrow',
'menu.xhs.analysis': 'IconBarChart',
'menu.xhs.creation': 'IconEdit',
'menu.xhs.publishHistory': 'IconHistory',
'menu.xhs.skills': 'IconApps',
'menu.xhs.humanSettings': 'IconSettings',
}
export function toPascalIconName(icon: string): string {
const trimmed = icon.trim()
if (!trimmed) return ''
if (/^Icon[A-Z]/.test(trimmed)) return trimmed
if (trimmed.startsWith('icon-')) {
return `Icon${trimmed
.slice(5)
.split('-')
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join('')}`
}
return trimmed
}
export function resolveArcoIcon(icon?: string | null): Component | null {
if (!icon) return null
const pascal = toPascalIconName(icon)
return (Icons as Record<string, Component>)[pascal] ?? null
}
type MenuIconItem = {
name?: string
path?: string
route?: string
meta?: {
icon?: string
title?: string
}
}
function resolveLocaleKey(
menuItem: MenuIconItem,
nameToLocaleKeyMap?: Record<string, string>,
): string | undefined {
const title = menuItem.name || menuItem.meta?.title || ''
if (!title) return undefined
if (title.startsWith('menu.')) return title
return nameToLocaleKeyMap?.[title]
}
/** 解析顶部导航子菜单图标:优先菜单配置,其次默认映射,再继承父级,最后兜底 */
export function getSubMenuIcon(
menuItem: MenuIconItem,
parentMenuItem?: MenuIconItem,
nameToLocaleKeyMap?: Record<string, string>,
): Component {
const configured = resolveArcoIcon(menuItem.meta?.icon)
if (configured) return configured
const localeKey = resolveLocaleKey(menuItem, nameToLocaleKeyMap)
if (localeKey) {
const mapped = resolveArcoIcon(SUB_MENU_DEFAULT_ICONS[localeKey])
if (mapped) return mapped
}
const parentIcon = resolveArcoIcon(parentMenuItem?.meta?.icon)
if (parentIcon) return parentIcon
return Icons.IconApps
}
/** 解析顶部导航一级菜单图标:优先菜单配置,其次默认映射,最后兜底 */
export function getTopMenuIcon(
menuItem: MenuIconItem,
nameToLocaleKeyMap?: Record<string, string>,
): Component {
const configured = resolveArcoIcon(menuItem.meta?.icon)
if (configured) return configured
const localeKey = resolveLocaleKey(menuItem, nameToLocaleKeyMap)
if (localeKey) {
const mapped = resolveArcoIcon(TOP_MENU_DEFAULT_ICONS[localeKey])
if (mapped) return mapped
}
return Icons.IconMenu
}