/** * 小程序工作台功能入口下发数据的通用组装工具 * 后端 /xxx-dashboard/entries 返回扁平数组:[{ code, name, description, icon, theme, path, group_name }] * 这里统一转换成四个工作台首页渲染用的 sections 结构:[{ key, title, items: [{ key, label, desc, icon, theme, path }] }] * 行为字段(角标/表单预填/弹窗等)由各首页按 code 通过 decorate 回调注入,DB 只管展示与有无 */ /** 未配置主题时的兜底配色(与各端硬编码菜单的占位灰一致) */ const FALLBACK_THEME = { bg: 'linear-gradient(135deg, #F3F4F6, #E5E7EB)', color: '#6B7280' }; /** * 单条下发入口 → 渲染项归一化(首页 sections 与功能页常用/历史共用同一映射,保证字段一致) * @param {Object} entry 后端下发的入口对象 * @returns {Object|null} { key, label, desc, icon, theme, path, group };非法入口返回 null */ export function normalizeEntry(entry) { if (!entry || !entry.code) return null; return { key: entry.code, label: entry.name || '', desc: entry.description || '', icon: entry.icon || 'grid-fill', theme: entry.theme && entry.theme.bg ? entry.theme : FALLBACK_THEME, path: entry.path || '', group: entry.group_name || '', }; } /** * 下发入口 → sections 结构 * @param {Array} entries 后端下发的入口数组(已按后台配置排序) * @param {Function} [decorate] 可选行为装饰回调 (item, entry) => item|null,返回 null 表示丢弃该项 * @returns {Array} sections;entries 为空/非法时返回空数组(调用方据此回落硬编码菜单) */ export function groupEntriesToSections(entries, decorate) { if (!Array.isArray(entries) || entries.length === 0) return []; const sections = []; entries.forEach((entry) => { let item = normalizeEntry(entry); if (!item) return; if (typeof decorate === 'function') { item = decorate(item, entry); if (!item) return; } const title = entry.group_name || '快捷服务'; let section = sections.find((s) => s.title === title); if (!section) { // key 用分组标题即可(仅作 v-for key,首个出现顺序即分组展示顺序) section = { key: title, title, items: [] }; sections.push(section); } section.items.push(item); }); // 丢弃被 decorate 全部过滤掉的空分组 return sections.filter((s) => s.items.length > 0); }