This commit is contained in:
wangzzpeng
2026-09-28 09:27:51 +08:00
parent 7bc84bbe5e
commit 6eb2bfcbfd
25 changed files with 2887 additions and 0 deletions

94
src/utils/entryIcon.ts Normal file
View File

@@ -0,0 +1,94 @@
import type { EntryTone, PortalEntryCard, PortalEntryGroup, PortalEntryVo } from '@/types/business/home'
/**
* 门户入口的视图映射:后端 icon 字段 → 页面可直接渲染的图标 + 底色
*
* <p>后端 icon 是「图标名 / url」自由文本,可能为空、可能是图片地址、
* 也可能是一个标识符(如 folder)。这里按优先级兜底,保证任何取值都能渲染出可视化图标。</p>
*/
/** 图标底色(设计稿为五色循环),按序号轮转分配 */
const TONE_CYCLE: EntryTone[] = ['purple', 'green', 'blue', 'amber', 'red']
/** 显示名 → emoji 兜底表(后端 icon 为空或非 emoji 时使用) */
const NAME_ICON_MAP: Record<string, string> = {
项目中心: '🗂️',
需求中心: '🧾',
需求池: '🧾',
工单需求池: '📋',
快捷提报: '➕',
在线客服: '🎧',
客服工作台: '💬',
合同管理: '📑',
合同台账: '📄',
合同列表: '📁',
合同档案: '📁',
财务管理: '💰',
财务台账: '💰',
财务档案: '💰',
案例库管理: '📁',
案例库: '📁',
个人中心: '👤',
消息中心: '💬',
账号安全: '🔒',
意见反馈: '✉️',
交易列表: '📊',
交易明细: '📊',
部署管理: '🚀',
设备工作台: '🖥️',
设备管理: '🖥️',
资质列表: '📋',
帮助中心: '❓',
关于我们: 'ℹ️',
}
/** 兜底图标 */
const DEFAULT_ICON = '📦'
/** 是否图片地址 */
function isImageUrl(value: string): boolean {
return /^(https?:)?\/\//i.test(value) || value.startsWith('/')
}
/** 是否可直接当作 emoji / 字符图标使用(非纯字母数字标识符) */
function isGlyph(value: string): boolean {
return !/^[a-zA-Z0-9_-]+$/.test(value)
}
/** 解析单项图标与底色 */
function resolveIcon(icon: string | null, name: string): { icon: string; isImage: boolean } {
const raw = (icon ?? '').trim()
if (raw) {
if (isImageUrl(raw)) return { icon: raw, isImage: true }
if (isGlyph(raw)) return { icon: raw, isImage: false }
}
return { icon: NAME_ICON_MAP[name] ?? DEFAULT_ICON, isImage: false }
}
/**
* 后端入口 → 页面卡片
*
* @param vo 后端入口
* @param index 组内序号(决定底色轮转)
*/
export function toPortalEntryCard(vo: PortalEntryVo, index: number): PortalEntryCard | null {
if (vo.id == null) return null
const label = (vo.name ?? '').trim() || '未命名入口'
const { icon, isImage } = resolveIcon(vo.icon, label)
return {
id: vo.id,
label,
route: (vo.route ?? '').trim(),
groupType: (vo.groupType === '1' ? '1' : '0') as PortalEntryGroup,
icon,
isImage,
tone: TONE_CYCLE[index % TONE_CYCLE.length],
}
}
/** 后端入口列表 → 页面卡片列表(过滤无效项,保持后端下发顺序) */
export function toPortalEntryCards(list: PortalEntryVo[]): PortalEntryCard[] {
return list
.map((vo, index) => toPortalEntryCard(vo, index))
.filter((item): item is PortalEntryCard => item !== null)
}