Files
aurora-admin/tools/lib/family-model.mjs
T
aurora-admin f1fbfc2ddb
Regression / regression (push) Canceled after 0s
feat(品牌标识): 几何 K 图标(favicon/顶栏标记/theme-color) + 并行会话成果入库
## 品牌标识(本次会话)

起因:品牌此前没有任何图形标识 —— 唯一 favicon 是内联 data-URI 里的字母「A」,
那是 v2.0.0「Aurora Admin → Kole UI」改名漏掉的一处(PC 顶栏也是「A」,
移动端站已是「K」;移动端文档站则完全没有 favicon)。

- 几何:24 网格三个互不接触的笔画(竖 + 两斜),圆头描边;
  描边 2.25 → 16px 标签页尺寸下正好 1.5px = 规范原文「描边1.5px」
- 取色分两套(刻意):favicon 硬编码品牌蓝/白(渲染在浏览器标签栏,不继承 kole-dark);
  顶栏标记走 currentColor(实测暗色下自动转 rgb(20,22,28))
- 新增 theme-color 双条(light #FFFFFF / dark #1C1F26,取 --kole-color-card-bg)
- 修 site/app.js hero 标语 KOLE ADMIN → KOLE UI(改名变形残留)
- 移动端 7 个模板补 favicon(此前计数 0)

验收:门禁 9 条全 OK(site-routing/site-routes/mobile-docs/mobile-site/isolation/
theme/nav/i18n/icons);PC 回归 1464/1464 · 移动端 807/807,各连跑 8 次一致;
两端 favicon 405 字节逐字节一致;PC 站控制台错误 1→0。

## 并行会话成果(本次一并入库)

- 图标系统:2576 图标(TDesign/Element Plus,MIT)+ 11 端注入 + 5 个构建门禁工具
  + IconPreview 预览页 + ICON-SPEC.md 冻结规格
- 移动端平台:47 组件 × 6 端 + 文档站 53 页 + 隔离门禁
- PC 组件:103 个大后台组件 / 组件11 批次
- uni-app:PC 端试点 + 移动端端实现 + 真实编译验证

## 工程

- .gitignore 补 .scratch/ 与 .zcode-preexisting-*.txt(会话中间产物,实测 9.1MB,不入库)
- CHANGELOG 补品牌标识条目
- ROADMAP 登 S8-P4(品牌标识任务包 + og:image/apple-touch-icon 未做部分)
2026-09-21 10:05:48 +08:00

306 lines
17 KiB
JavaScript
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.
/**
* family-model.mjs - 组件族的唯一真源(数据层)
*
* 设计目标:把「同一交互/结构的重复组件」表达为「1 个基座组件 + N 个参数取值」。
*
* 硬约束(改这个文件前必须读):
* - frameworks/ 必须恰好 395 文件(79 × 5),任何族成员都不得增删文件;
* - site/data.json 必须保持完整 79 个 slug(对外承诺,见 AGENTS.md 铁律 5);
* - 族的合并发生在「实现来源」层(一份模板 + 参数)与「契约/API」层,
* 不改变组件身份(slug)、class 前缀与视觉契约。
*
* 每个族的 `mergeBasis` 必须写明实测依据,禁止凭名字臆断。
* 依据来源:.design_library/kole-ui/components/<slug>.json 的
* semanticTypeCandidates / variantDimensions / anatomy / structurePatterns / doNotInvent。
*/
/** 参数取值类型:enum=枚举、bool=开关、list=数组、string=字符串、int=整数 */
export const FAMILIES = [
{
id: 'nav-menu',
label: '导航菜单',
base: 'sidemenu',
mergeBasis:
'topmenu 与 sidemenu 的 semanticTypeCandidates 分别为 menu|topnav 与 menu|sidebar,同一 menu 语义的两个 direction 取值;' +
'mixednavigation 的 anatomy 即 [top/side/sync/memory],本身就是前两者的参数化组合。' +
'sidemenu 实现内已存在通用 menu 词汇(kole-menu-item/icon/label/badge/arrow/children/pop)。',
paramSurface: [
{ name: 'direction', type: 'enum', values: ['top', 'side', 'mixed'], default: 'side', desc: '导航方向,决定一级菜单的排布' },
{ name: 'collapsed', type: 'bool', default: false, desc: '侧边模式折叠为仅图标,悬停弹出子级' },
{ name: 'level', type: 'enum', values: ['1', '2'], default: '2', desc: '最大层级;mixed 固定 2 级(顶一级 + 侧二级)' },
{ name: 'badge', type: 'bool', default: false, desc: '菜单项徽章' },
{ name: 'indicator', type: 'enum', values: ['none', 'top', 'left'], default: 'left', desc: '选中指示条;top 模式默认 top,side 模式默认 left' },
],
members: [
{ slug: 'sidemenu', role: 'base', params: { direction: 'side', indicator: 'left', collapsed: false } },
{ slug: 'topmenu', role: 'variant', params: { direction: 'top', indicator: 'top', collapsed: false } },
{ slug: 'mixednavigation', role: 'variant', params: { direction: 'mixed', level: '2', collapsed: false } },
],
},
{
id: 'table',
label: '数据表格',
base: 'table',
mergeBasis:
'六个成员的 semanticTypeCandidates 首项均为 table;每个变体的 anatomy 都是 Table 骨架(header/row/cell/selectedRow)加一个能力维度。' +
'treetable 额外带 indent/arrow/parentRow/childRow,是 table 的超集而非并列组件。',
paramSurface: [
{ name: 'expandable', type: 'bool', default: false, desc: '行展开;anatomy 增 icon/content/state' },
{ name: 'fixedTarget', type: 'enum', values: ['none', 'left', 'right', 'header'], default: 'none', desc: '固定列 / 固定表头' },
{ name: 'mergeAxis', type: 'enum', values: ['none', 'vertical', 'horizontal'], default: 'none', desc: '单元格合并轴向' },
{ name: 'summary', type: 'enum', values: ['none', 'top', 'bottom'], default: 'none', desc: '统计行位置' },
{ name: 'treeMode', type: 'bool', default: false, desc: '树形层级(缩进 16px/级 + 展开箭头)' },
],
members: [
{ slug: 'table', role: 'base', params: {} },
{ slug: 'expandabletable', role: 'variant', params: { expandable: true } },
{ slug: 'fixedcolumntable', role: 'variant', params: { fixedTarget: 'left' } },
{ slug: 'mergedcelltable', role: 'variant', params: { mergeAxis: 'vertical' } },
{ slug: 'summaryrowtable', role: 'variant', params: { summary: 'bottom' } },
{ slug: 'treetable', role: 'variant', params: { treeMode: true } },
],
},
{
id: 'masked-input',
label: '掩码输入',
base: 'input',
mergeBasis:
'七个成员的 semanticTypeCandidates 第二项均为 input;anatomy 均为 input 加 format / validate / segment 的子集。' +
'差异只是掩码规则与校验规则不同,结构完全同源。',
paramSurface: [
{ name: 'mask', type: 'string', default: '', desc: '掩码模式(每 4 位空格、6/8/4 分段、7/8 位车牌等)' },
{ name: 'length', type: 'int', default: 0, desc: '期望长度;0 表示不限' },
{ name: 'segment', type: 'list', default: [], desc: '分段长度数组,如 [6,8,4]' },
{ name: 'validate', type: 'enum', values: ['none', 'luhn', 'checksum', 'regex'], default: 'none', desc: '校验规则' },
{ name: 'scan', type: 'bool', default: false, desc: '扫码 / 识别入口' },
{ name: 'autoFocusNext', type: 'bool', default: false, desc: '输入满格自动跳下一格' },
{ name: 'masked', type: 'bool', default: false, desc: '回显脱敏(密码)' },
],
members: [
{ slug: 'input', role: 'base', params: {} },
{ slug: 'codeinput', role: 'variant', params: { length: 6, autoFocusNext: true, scan: false } },
{ slug: 'passwordinput', role: 'variant', params: { masked: true } },
{ slug: 'phoneinput', role: 'variant', params: { mask: '3-4-4', length: 11 } },
{ slug: 'idcardinput', role: 'variant', params: { segment: [6, 8, 4], length: 18, scan: true } },
{ slug: 'bankcardinput', role: 'variant', params: { mask: '4-4-4-4', validate: 'luhn', scan: true } },
{ slug: 'plateinput', role: 'variant', params: { length: 7, validate: 'regex' } },
],
},
{
id: 'modal-shell',
label: '对话弹窗',
base: 'modal',
mergeBasis:
'alertmodal 与 confirmmodal 的 doNotInvent 原文写着「弹窗尺寸档位(见 Modal 契约)」——规范自身声明从属。' +
'四个变体的 anatomy 均为 mask/container/title/content/footer 的子集或替换。' +
'popconfirm(240px 无遮罩面板)与 bottomsheet(底部滑出)不属本族:archetype 不同。',
paramSurface: [
{ name: 'size', type: 'enum', values: ['sm', 'md', 'lg'], default: 'md', desc: '宽度档位' },
{ name: 'tone', type: 'enum', values: ['none', 'success', 'error', 'warning', 'info'], default: 'none', desc: '语义色 + 图标' },
{ name: 'footer', type: 'enum', values: ['none', 'confirm', 'confirm-cancel'], default: 'confirm-cancel', desc: '底部操作' },
{ name: 'columns', type: 'enum', values: ['1', '2'], default: '1', desc: '表单类弹窗的列数' },
{ name: 'fullscreen', type: 'bool', default: false, desc: '全屏铺满' },
],
members: [
{ slug: 'modal', role: 'base', params: {} },
{ slug: 'alertmodal', role: 'variant', params: { tone: 'info', footer: 'confirm' } },
{ slug: 'confirmmodal', role: 'variant', params: { footer: 'confirm-cancel' } },
{ slug: 'formmodal', role: 'variant', params: { columns: '2', footer: 'confirm-cancel' } },
{ slug: 'fullscreenmodal', role: 'variant', params: { fullscreen: true } },
],
},
{
id: 'preference-switcher',
label: '偏好切换器',
base: 'themeswitcher',
mergeBasis:
'三个成员的 anatomy 全部包含 persist,structurePatterns 全部写着持久化到 localStorage,' +
'semanticTypeCandidates 末项均为 preference,变体维度均为同一形态的 3 档选项。三者完全同构,仅选项内容不同。',
paramSurface: [
{ name: 'options', type: 'list', default: [], desc: '选项数组 [{value,label,preview?}]' },
{ name: 'selected', type: 'string', default: '', desc: '当前选中值' },
{ name: 'persistKey', type: 'string', default: '', desc: 'localStorage 键名;空则持久化到内存' },
{ name: 'applyScope', type: 'enum', values: ['global', 'local'], default: 'global', desc: '生效范围:全站令牌 / 局部预览' },
{ name: 'extra', type: 'enum', values: ['none', 'brand-palette', 'demo-list'], default: 'none', desc: '附加区块(品牌色板 / 示例列表)' },
],
members: [
{ slug: 'themeswitcher', role: 'base', params: { extra: 'brand-palette', applyScope: 'global' } },
{ slug: 'layoutswitcher', role: 'variant', params: { extra: 'none', applyScope: 'global' } },
{ slug: 'densityswitcher', role: 'variant', params: { extra: 'demo-list', applyScope: 'global' } },
],
},
{
id: 'card-shell',
label: '卡片容器',
base: 'card',
mergeBasis:
'四个成员的 semanticTypeCandidates 均含 card 或 panel,anatomy 均为 card 骨架(style/padding/section)加各自填充区。' +
'本轮不合并实现:employeecard 有独立的字段面(status/contact),合并会污染 card 参数面,仅登记族关系。',
paramSurface: [
{ name: 'style', type: 'enum', values: ['plain', 'outlined'], default: 'plain', desc: '描边 / 阴影' },
{ name: 'padding', type: 'enum', values: ['md', 'lg'], default: 'md', desc: '内边距档位' },
{ name: 'sections', type: 'list', default: [], desc: '区块序列:header/body/footer/media' },
{ name: 'actions', type: 'list', default: [], desc: '操作区按钮' },
{ name: 'layout', type: 'enum', values: ['vertical', 'horizontal'], default: 'vertical', desc: '内容排布方向' },
],
members: [
{ slug: 'card', role: 'base', params: {} },
{ slug: 'dashboardcard', role: 'variant', params: { layout: 'horizontal' } },
{ slug: 'employeecard', role: 'variant', params: { sections: ['media', 'body'] } },
{ slug: 'chartpanel', role: 'variant', params: { sections: ['header', 'body'] } },
],
mergeStatus: 'registered-only',
},
{
id: 'feedback-page',
label: '结果与异常页',
base: 'resultvariants',
mergeBasis:
'三个成员的 semanticTypeCandidates 分别为 result|feedback-page、exception|error-page、empty|placeholder,' +
'均为「占满内容区的状态说明 + 插画 + 操作」。resultvariants.status[4] 与 exceptionvariants.code[4] 同构。',
paramSurface: [
{ name: 'status', type: 'enum', values: ['success', 'error', 'warning', 'info', 'empty'], default: 'info', desc: '状态语义' },
{ name: 'illustration', type: 'enum', values: ['none', 'icon', 'image'], default: 'icon', desc: '插画形态' },
{ name: 'extra', type: 'list', default: [], desc: '底部操作区' },
],
members: [
{ slug: 'resultvariants', role: 'base', params: {} },
{ slug: 'exceptionvariants', role: 'variant', params: { status: 'error' } },
{ slug: 'emptypro', role: 'variant', params: { status: 'empty' } },
],
},
{
id: 'loading-state',
label: '加载态',
base: 'loadingoverlay',
mergeBasis:
'三个成员的 semanticTypeCandidates 分别为 overlay|loading、skeleton|placeholder、progress|indicator,' +
'是同一语义(加载中)的三种承载形态,共享 coverage / status / showText 三要素。',
paramSurface: [
{ name: 'shape', type: 'enum', values: ['overlay', 'block', 'bar'], default: 'overlay', desc: '承载形态:遮罩 / 占位块 / 进度条' },
{ name: 'status', type: 'enum', values: ['active', 'success', 'exception'], default: 'active', desc: '状态' },
{ name: 'coverage', type: 'enum', values: ['full', 'local'], default: 'local', desc: '覆盖范围' },
{ name: 'showText', type: 'bool', default: false, desc: '显示文案 / 百分比' },
],
members: [
{ slug: 'loadingoverlay', role: 'base', params: { shape: 'overlay' } },
{ slug: 'skeletonpro', role: 'variant', params: { shape: 'block' } },
{ slug: 'progressvariants', role: 'variant', params: { shape: 'bar' } },
],
},
{
id: 'steps',
label: '步骤',
base: 'steps',
mergeBasis:
'steps 与 steplist 的 semanticTypeCandidates 完全相同(均为 steps|wizard),变体维度同为 orientation[2] + state[3]。' +
'唯一差异是节点形态(横向步骤条 / 纵向列表)。',
paramSurface: [
{ name: 'nodeStyle', type: 'enum', values: ['bar', 'list'], default: 'bar', desc: '步骤条 / 步骤列表' },
{ name: 'orientation', type: 'enum', values: ['horizontal', 'vertical'], default: 'horizontal', desc: '排布方向' },
{ name: 'editable', type: 'bool', default: false, desc: '可点击回退' },
],
members: [
{ slug: 'steps', role: 'base', params: { nodeStyle: 'bar' } },
{ slug: 'steplist', role: 'variant', params: { nodeStyle: 'list', orientation: 'vertical' } },
],
},
{
id: 'tabs',
label: '标签页',
base: 'tabs',
mergeBasis:
'tabs 的 semanticTypeCandidates 为 tabs|tabnav,enhancedtabnav 为 tabs|browser-tabs;' +
'enhancedtabnav 的变体维度 state[4]+feature[2] 是 tabs 的 state[4] 超集 —— 增强项即参数。',
paramSurface: [
{ name: 'type', type: 'enum', values: ['line', 'card'], default: 'line', desc: '标签形态' },
{ name: 'draggable', type: 'bool', default: false, desc: '拖拽排序' },
{ name: 'contextMenu', type: 'bool', default: false, desc: '右键菜单' },
{ name: 'unsavedMark', type: 'bool', default: false, desc: '未保存标记' },
],
members: [
{ slug: 'tabs', role: 'base', params: {} },
{ slug: 'enhancedtabnav', role: 'variant', params: { draggable: true, contextMenu: true, unsavedMark: true } },
],
},
{
id: 'notice',
label: '全局轻提示',
base: 'messagepro',
mergeBasis:
'messagepro 为 message|toast、notificationpro 为 notification|notice,同属全局轻提示;' +
'差异只在承载形态(顶部条 / 卡片)与位置,type/action/duration 三要素共享。',
paramSurface: [
{ name: 'placement', type: 'enum', values: ['top', 'top-right', 'bottom'], default: 'top', desc: '出现位置' },
{ name: 'type', type: 'enum', values: ['success', 'error', 'warning', 'info', 'loading'], default: 'info', desc: '语义类型' },
{ name: 'action', type: 'bool', default: false, desc: '操作按钮' },
{ name: 'duration', type: 'int', default: 3000, desc: '自动关闭毫秒数;0 为不自动关闭' },
],
members: [
{ slug: 'messagepro', role: 'base', params: { placement: 'top' } },
{ slug: 'notificationpro', role: 'variant', params: { placement: 'top-right', action: true } },
],
},
{
id: 'selection-card',
label: '选择卡片',
base: 'radiocard',
mergeBasis:
'radiocard(radio|card)与 checkboxcard(checkbox|card)的 anatomy 同为 card + 选中态 + 内嵌操作,' +
'变体维度均含 selection,差异仅为单选 / 多选语义。',
paramSurface: [
{ name: 'mode', type: 'enum', values: ['single', 'multiple'], default: 'single', desc: '单选 / 多选' },
{ name: 'content', type: 'enum', values: ['text', 'rich'], default: 'text', desc: '内容形态' },
{ name: 'innerActions', type: 'bool', default: false, desc: '卡片内嵌操作区' },
],
members: [
{ slug: 'radiocard', role: 'base', params: { mode: 'single' } },
{ slug: 'checkboxcard', role: 'variant', params: { mode: 'multiple', innerActions: true } },
],
},
{
id: 'combobox',
label: '候选输入',
base: 'autocomplete',
mergeBasis:
'autocomplete(combobox|input)与 mention(mention|input)的 anatomy 均为 input + 候选面板 + 键盘选择,' +
'类名集合 Jaccard 0.448(全库最高组之一);差异仅为触发方式(连续输入 / 触发符)与插入行为。',
paramSurface: [
{ name: 'trigger', type: 'enum', values: ['input', 'symbol'], default: 'input', desc: '触发方式:连续输入 / 触发符' },
{ name: 'suggestionSource', type: 'enum', values: ['static', 'async'], default: 'static', desc: '候选来源' },
{ name: 'itemRender', type: 'enum', values: ['plain', 'custom'], default: 'plain', desc: '候选项渲染' },
{ name: 'insert', type: 'enum', values: ['replace', 'token'], default: 'replace', desc: '选中后插入行为' },
],
members: [
{ slug: 'autocomplete', role: 'base', params: { trigger: 'input' } },
{ slug: 'mention', role: 'variant', params: { trigger: 'symbol', insert: 'token' } },
],
},
];
/** 本批次实际用生成器重写实现的族(其余仅登记契约层族关系) */
export const IMPLEMENTED_FAMILIES = ['nav-menu'];
/** 全部族成员 slug(去重) */
export function allMemberSlugs() {
const set = new Set();
for (const f of FAMILIES) for (const m of f.members) set.add(m.slug);
return [...set];
}
/** slug -> {family, role, params} */
export function memberIndex() {
const map = {};
for (const f of FAMILIES) {
for (const m of f.members) {
if (map[m.slug]) throw new Error(`组件 ${m.slug} 同时属于多个族:${map[m.slug].family} 与 ${f.id}`);
map[m.slug] = { family: f.id, role: m.role, params: m.params, base: f.base };
}
}
return map;
}
export function familyById(id) {
return FAMILIES.find((f) => f.id === id) || null;
}