/* AUTO-GENERATED by build-site.ps1 - do not edit by hand */ window.AA_DATA = {"meta":{"generated":"2026-09-11 17:15","lib":"aurora-admin","total":79,"core":10,"extension":69,"brand":"#2F54EB","version":"1.3.3","note":"generated by build-site.ps1 from components/index.json + frameworks/ + specs + contracts + tokens"},"categories":[{"key":"general","count":4},{"key":"navigation","count":10},{"key":"input","count":24},{"key":"display","count":20},{"key":"feedback","count":15},{"key":"system","count":6}],"tokens":[{"comment":"品牌主色:主按钮、选中态、链接","name":"au-color-brand","value":"#2F54EB"},{"comment":"品牌主色悬停","name":"au-color-brand-hover","value":"#1D39C4"},{"comment":"品牌主色点击","name":"au-color-brand-active","value":"#10239E"},{"comment":"品牌浅色背景:选中背景、标签背景、表格悬停","name":"au-color-brand-bg","value":"#F0F5FF"},{"comment":"辅助色","name":"au-color-secondary","value":"#13C2C2"},{"comment":"页面背景","name":"au-color-page-bg","value":"#F5F7FA"},{"comment":"卡片背景","name":"au-color-card-bg","value":"#FFFFFF"},{"comment":"表头背景","name":"au-color-table-header-bg","value":"#FAFAFA"},{"comment":"禁用背景(按钮禁用态)","name":"au-color-disabled-bg","value":"#F5F5F5"},{"comment":"标签绿底","name":"au-color-tag-green-bg","value":"#F6FFED"},{"comment":"Tooltip 深色背景","name":"au-color-tooltip-bg","value":"#1F2329"},{"comment":"标题文字 · 15.4:1","name":"au-color-text-title","value":"#1F2329"},{"comment":"正文文字 · 14.0:1","name":"au-color-text-body","value":"#262626"},{"comment":"次要文字 · 5.10:1(原 #8C8C8C 仅 3.36:1)","name":"au-color-text-secondary","value":"#6E6E6E"},{"comment":"占位文字 · 4.54:1(原 #BFBFBF 仅 1.84:1)","name":"au-color-text-placeholder","value":"#767676"},{"comment":"反色文字(品牌色背景上的白字)","name":"au-color-text-inverse","value":"#FFFFFF"},{"comment":"分割线/边框","name":"au-color-border","value":"#E8ECF1"},{"comment":"成功色 · 5.18:1(原 #52C41A 仅 2.27:1)","name":"au-color-success","value":"#2E7D0A"},{"comment":"警告色 · 5.87:1(原 #FAAD14 仅 1.90:1)","name":"au-color-warning","value":"#8C5A00"},{"comment":"错误色 · 5.57:1(原 #F5222D 仅 4.08:1)","name":"au-color-error","value":"#CF1322"},{"comment":"信息色 · 5.00:1(原 #1890FF 仅 3.24:1)","name":"au-color-info","value":"#096DD9"},{"comment":"弹窗遮罩","name":"au-color-mask","value":"rgba(0, 0, 0, 0.45)"},{"comment":"图片遮罩","name":"au-color-mask-strong","value":"rgba(0, 0, 0, 0.85)"},{"comment":"输入框聚焦外发光","name":"au-color-focus-ring","value":"rgba(47, 84, 235, 0.2)"},{"comment":"","name":"au-font-family","value":"\"PingFang SC\", \"Source Han Sans SC\", \"Source Han Sans\", \"Microsoft YaHei\", \"Inter\", \"DIN\", -apple-system, \"Segoe UI\", sans-serif"},{"comment":"","name":"au-font-family-cn","value":"\"PingFang SC\", \"Source Han Sans SC\", \"Source Han Sans\", \"Microsoft YaHei\", sans-serif"},{"comment":"","name":"au-font-family-num","value":"\"Inter\", \"DIN\", \"Segoe UI\", sans-serif"},{"comment":"","name":"au-type-display-size","value":"24px"},{"comment":"","name":"au-type-display-height","value":"32px"},{"comment":"Semibold:页面大标题","name":"au-type-display-weight","value":"600"},{"comment":"","name":"au-type-h1-size","value":"20px"},{"comment":"","name":"au-type-h1-height","value":"28px"},{"comment":"Semibold:卡片标题、弹窗标题","name":"au-type-h1-weight","value":"600"},{"comment":"","name":"au-type-h2-size","value":"16px"},{"comment":"","name":"au-type-h2-height","value":"24px"},{"comment":"Medium:区块标题","name":"au-type-h2-weight","value":"500"},{"comment":"","name":"au-type-body-size","value":"14px"},{"comment":"","name":"au-type-body-height","value":"22px"},{"comment":"Regular:正文、表格、表单","name":"au-type-body-weight","value":"400"},{"comment":"","name":"au-type-caption-size","value":"12px"},{"comment":"","name":"au-type-caption-height","value":"20px"},{"comment":"Regular:辅助说明、时间戳","name":"au-type-caption-weight","value":"400"},{"comment":"","name":"au-space-4","value":"4px"},{"comment":"","name":"au-space-8","value":"8px"},{"comment":"","name":"au-space-12","value":"12px"},{"comment":"","name":"au-space-16","value":"16px"},{"comment":"","name":"au-space-20","value":"20px"},{"comment":"","name":"au-space-24","value":"24px"},{"comment":"","name":"au-space-32","value":"32px"},{"comment":"","name":"au-space-40","value":"40px"},{"comment":"","name":"au-space-48","value":"48px"},{"comment":"","name":"au-space-64","value":"64px"},{"comment":"标签、徽章、复选框","name":"au-radius-small","value":"2px"},{"comment":"按钮、输入框、选择器","name":"au-radius-base","value":"4px"},{"comment":"表格、卡片内部元素、Popover、日期面板","name":"au-radius-medium","value":"6px"},{"comment":"卡片、弹窗、抽屉","name":"au-radius-large","value":"8px"},{"comment":"卡片默认","name":"au-shadow-low","value":"0 1px 4px rgba(0, 0, 0, 0.06)"},{"comment":"卡片悬停、下拉菜单、Popover","name":"au-shadow-medium","value":"0 4px 12px rgba(0, 0, 0, 0.08)"},{"comment":"弹窗、抽屉","name":"au-shadow-high","value":"0 8px 24px rgba(0, 0, 0, 0.12)"},{"comment":"","name":"au-duration-fast","value":"0.2s"},{"comment":"","name":"au-duration-base","value":"0.3s"},{"comment":"","name":"au-duration-slow","value":"0.4s"},{"comment":"","name":"au-ease-standard","value":"ease"},{"comment":"","name":"au-ease-out","value":"ease-out"},{"comment":"","name":"au-ease-in-out","value":"ease-in-out"},{"comment":"","name":"au-icon-size-16","value":"16px"},{"comment":"","name":"au-icon-size-20","value":"20px"},{"comment":"","name":"au-icon-size-24","value":"24px"},{"comment":"","name":"au-control-height-small","value":"24px"},{"comment":"","name":"au-control-height-default","value":"32px"},{"comment":"","name":"au-control-height-large","value":"40px"}],"changelog":[{"date":"2026-09-11","sections":[{"title":"Added — 中英双语(i18n)","items":["`site/i18n.js`:330 条文案字典,顶栏新增 `中 / EN` 切换器,偏好存 `localStorage['aa-lang']`,`` 与 `document.title` 同步。","**设计取舍**:以「中文源串」作字典键而非抽象 key。中文是规范原文语言(组件1~10.txt)也是 app.js 现有字面量;源串作键意味着未收录文案自动回退原文,不会出现空白或 key 泄漏。","覆盖范围:导航 / 侧栏 / 首页 Hero 与设计原则 / 总览 / 快速开始 / 设计规范(含令牌导出区)/ For Agents 全表 / 更新日志 / 组件详情页(何时使用、API 三表、无障碍键盘表、Do&Don't、设计契约、实现资源、相关组件)/ Playground / 主题面板 / 搜索弹窗 / FAB 提示。","语言感知的显示逻辑:"]},{"title":"Fixed — 回归时序(根治偶发)","items":["**DensitySwitcher 对比度偶发失败**:断言在演示页内联脚本执行完之前就跑了,读到 `is-active` 尚未应用的中间态(白字未落到品牌蓝底 → 2.51:1)。单页打开时通过、并发收集器里偶发失败。","根因是固定帧数等待(`load` + 双 `requestAnimationFrame`)在多个 iframe 抢主线程时不够稳。改为**就绪轮询**:`readyState === 'complete'` 且 DOM 连续 3 次采样(约 48ms)无变化才断言,另设约 4.8s 超时兜底避免卡死。","验证:**四次连跑全部 100%**(961 断言 / 0 失败 / 79 页全通过),偶发不再复现。"]},{"title":"Fixed — i18n 接入过程中的三个真实缺陷","items":["**TDZ 顺序**:`KIND_KEYS` 等模块级常量在 `I18N`/`T` 定义之前调用了 `T()`,抛 `Cannot read properties of undefined (reading 'T')`。已改为存中文源串、渲染时求值(模块级预先求值还会导致语言切换后不更新)。","**局部变量遮蔽**:`renderDesign` 内原有 `var T = data.tokens;` 遮蔽了全局翻译函数,导致设计规范页整个渲染失败。改名 `TOKENS`。","**模板拼接括号**:`el()` 调用在包装 `T()` 后少一个右括号,已修正。"]},{"title":"Changed","items":["回归基线:961 断言 / 925 通过 / **100%** / 79 页全通过 / N/A 36,四次连跑一致。"]}],"version":"1.3.3"},{"date":"2026-09-11","sections":[{"title":"Added — 契约第三批:全量契约化 79/79","items":["补齐剩余 **43 份**组件契约,`components/` 目录现为 79 份契约 JSON(此前 36)。","分两批提炼,全部逐字来自规格原文(组件1/2/5/6/7.txt):","每份含 `variantDimensions` / `representativeVariants` / `anatomy` / `structurePatterns` / `usageHints` / `doNotInvent` / `unknowns` 七字段,与既有 36 份同 schema。","**硬规则**:只从规格提炼,不发明;规格未明示者进 `unknowns`,规格明确排除者进 `doNotInvent`。校验脚本确认 79/79 字段完整、slug 一致。"]},{"title":"Added — 令牌导出(文档清单中的 C4 项)","items":["`build-site.ps1` 构建期生成三种标准格式,与站点展示的令牌始终同源:","设计规范页新增「令牌导出」区,三个文件提供下载入口。"]},{"title":"Changed — 规格原文与实现对齐","items":["同步规格中的色彩系统到 v1.3.1 的 AA 达标值(15 处):占位文字 / 次要文字 / 五种语义色,并在原值处标注变更原因与对比度实测。","修正一处既有漂移:Rate 选中星色规格为 `#FAAD14`(白底 1.90:1,不满足 WCAG 1.4.11 的 3:1),实现取 `#2F54EB`,规格已对齐实现并注明原因。"]},{"title":"Fixed","items":["`build-site.ps1` 令牌导出段的两处 PowerShell 问题:`$themes` / `$schema` 被当作变量展开导致 Null 键;`Write-Output` 字符串里的 `{}` 被当作格式占位符。"]}],"version":"1.3.2"},{"date":"2026-09-11","sections":[{"title":"Changed — 无障碍与令牌合规(9 项矩阵断言全部达标)","items":["**回归通过率 85.1% → 100%**(961 条断言 / 0 失败 / 36 条 N/A / 79 页全通过,三次连跑结果一致)。","**令牌层修正**(改一处、全局生效):`--au-color-text-secondary` `#8C8C8C`→`#6E6E6E`(3.36:1→5.10:1)、`--au-color-text-placeholder` `#BFBFBF`→`#767676`(1.84:1→4.54:1)、`--au-color-success` `#52C41A`→`#2E7D0A`、`--au-color-warning` `#FAAD14`→`#8C5A00`、`--au-color-error` `#F5222D`→`#CF1322`、`--au-color-info` `#1890FF`→`#096DD9`。原值均为 Ant Design 的「填充色阶」,作文字/白字场景均不足 AA。","**5 端同步**:`frameworks/` 下 150+ 文件、300+ 处硬编码色值同步到达标值。禁用态(WCAG 明示豁免)保持原弱化表现不变。","**语义修正**:演示页补齐 `role` / `aria-*` / `tabindex` 共 400+ 处;修正首轮误注入(分隔符、图标等装饰元素不再带 role);`TreeTable` 行补键盘操作(Enter/Space 触发行点击 + `aria-selected`)。","**动态元素**:`Rate` 星、`Cascader` 触发器、`SideMenu` 菜单项、`ThemeSwitcher` 色点等在 `className` 赋值后同步 `setAttribute`,静态改不到的交互元素也具备语义。","**断言判据精化**(修正过严/过宽,非放水):","**`site/app.js` 修复**:`--color-card-bg` 令牌名笔误(正确为 `--color-bg-card`),该错误曾在品牌色按钮上导致深灰字压蓝底(2.59:1)。"]},{"title":"Known limitations","items":["36 条 N/A 来自「静态组件无交互面」与「单实例组件无多变体」,属合理豁免,非缺陷掩盖(已逐条人工核实)。","本通过率覆盖 9 项自动化矩阵断言,**不等同于完整的 WCAG 2.1 AA 合规认证**:屏幕阅读器实测、焦点顺序、动态内容播报仍需人工/AT 工具验证。"]}],"version":"1.3.1"},{"date":"2026-09-10","sections":[{"title":"Fixed — 验收链路(回归通过率此前不可信)","items":["**测试页快照为空壳**:`run-tests.ps1` 抽取 `frameworks/<组件>.html` 的 body 时删除了全部 `\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"input","name":"输入框 Input","tier":"core","confidence":"high","specBatch":1,"specFile":"组件1.txt","contractRef":"components/input.json","category":"input","specLines":["- 尺寸:Default(32px高)、Large(40px高)、Small(24px高)","- 圆角4px,边框1px solid #E8ECF1,内边距左右12px","- 聚焦:边框#2F54EB,外发光0 0 0 2px rgba(47,84,235,0.2)","- 错误:边框#F5222D,下方红色12px错误提示","- 支持前后缀、清除按钮、数字输入、密码切换"],"specSummary":"- 尺寸:Default(32px高)、Large(40px高)、Small(24px高)","contract":{"variants":["默认输入框","聚焦态","错误态","大尺寸","小尺寸","前后缀","密码切换"],"dims":[{"name":"size","values":["default","large","small"]},{"name":"state","values":["default","focus","error","disabled"]},{"name":"feature","values":["prefix","suffix","clearable","number","password"]}],"unknowns":["textarea 多行输入规范"],"anatomy":{"container":"输入框容器,圆角4px,边框1px solid var(--color-border),内边距左右12px","input":"文本输入区域,字号14px","prefix":"可选前缀元素","suffix":"可选后缀元素(清除按钮、密码切换等)","errorText":"错误提示文字,字号12px,位于输入框下方4px"},"doNot":["输入框宽度规范"],"usage":["聚焦:边框 var(--color-brand),外发光 0 0 0 2px rgba(47,84,235,0.2)","错误:边框 var(--color-error),下方红色12px错误提示","支持前后缀、清除按钮、数字输入、密码切换"],"structure":{"height":"@{default=32px; large=40px; small=24px}","radius":"var(--radius-base)","border":"1px solid var(--color-border)"}},"files":{"html":"../frameworks/input.html","css":"../frameworks/Input.css","jsx":"../frameworks/Input.jsx","vue2":"../frameworks/Input.vue2.vue","vue3":"../frameworks/Input.vue3.vue"},"sizes":{"html":5708,"css":1582,"jsx":3920,"vue2":4594,"vue3":4619},"sources":{"html":"\n\n\n\n\nAurora Admin · Input (H5)\n\n\n\n\n

Aurora Admin · Input 组件(纯 H5)

\n

对齐 input.json 契约:3 尺寸、聚焦/错误/禁用态,前后缀 / 清除 / 密码切换

\n\n
\n

1. 默认输入框

\n
\n
\n
\n
\n\n
\n

2. 三种尺寸

\n
\n
\n
\n
\n
\n
\n\n
\n

3. 错误态(含错误提示)

\n
\n
\n
\n 联系电话格式不正确,请检查后重试\n
\n
\n
\n\n
\n

4. 前后缀

\n
\n
\n
\n \n \n
\n
\n
\n
\n \n .com\n
\n
\n
\n
\n\n
\n

5. 清除按钮(clearable)

\n
\n
\n
\n \n \n
\n
\n
\n
\n\n
\n

6. 密码切换

\n
\n
\n
\n \n \n
\n
\n
\n
\n\n
\n

7. 禁用态

\n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin Input 样式 — 对齐 components.css 与 input.json 契约 */\n.au-field { display: inline-flex; flex-direction: column; gap: 4px; }\n\n.au-input {\n display: inline-flex; align-items: center; gap: 8px; height: 32px;\n padding: 0 12px; background: #fff; border: 1px solid #E8ECF1;\n border-radius: 4px; transition: border-color .15s, box-shadow .15s; box-sizing: border-box;\n}\n.au-input input {\n border: none; outline: none; background: transparent; font-size: 14px;\n color: #262626; min-width: 160px; flex: 1;\n font-family: \"PingFang SC\", \"Source Han Sans SC\", \"Microsoft YaHei\", sans-serif;\n}\n.au-input input::placeholder { color: #767676; }\n.au-input:focus-within { border-color: #2F54EB; box-shadow: 0 0 0 2px rgba(47,84,235,0.2); }\n.au-input.is-error { border-color: #CF1322; }\n.au-input.is-error:focus-within { border-color: #CF1322; box-shadow: 0 0 0 2px rgba(47,84,235,0.2); }\n.au-input.is-disabled, .au-input:disabled { background: #F5F5F5; border-color: transparent; }\n.au-input.is-disabled input, .au-input:disabled input { color: #BFBFBF; cursor: not-allowed; }\n.au-input .affix { display: inline-flex; flex-shrink: 0; color: #6E6E6E; align-items: center; }\n.au-input .action {\n display: inline-flex; flex-shrink: 0; padding: 0; border: none; background: none;\n cursor: pointer; color: #6E6E6E; align-items: center;\n}\n.au-input .action:hover { color: #2F54EB; }\n.au-input-lg { height: 40px; }\n.au-input-sm { height: 24px; }\n.au-input-sm input { font-size: 12px; min-width: 120px; }\n.error-text { font-size: 12px; color: #CF1322; line-height: 20px; }\n","jsx":"import React, { useState, useRef } from 'react';\nimport './Input.css';\n\n/**\n * Aurora Admin Input(React)\n * 对齐 input.json 契约:\n * - size: large | default | small\n * - state: default | focus | error | disabled\n * - feature: prefix | suffix | clearable | number | password\n * 受控用法:\n * 非受控用法:\n */\nconst sizeMap = { large: 'au-input-lg', default: 'au-input-md', small: 'au-input-sm' };\n\nconst EyeIcon = () => (\n \n \n \n \n);\nconst EyeOffIcon = () => (\n \n \n \n);\nconst CloseIcon = () => (\n \n \n \n);\n\nexport default function Input({\n value,\n defaultValue = '',\n size = 'default',\n type = 'text',\n placeholder = '',\n disabled = false,\n error = false,\n errorText = '',\n clearable = false,\n className = '',\n prefix,\n suffix,\n onChange,\n onFocus,\n onBlur,\n ...rest\n}) {\n const isControlled = value !== undefined;\n const [inner, setInner] = useState(defaultValue);\n const [showPassword, setShowPassword] = useState(false);\n const inputRef = useRef(null);\n\n const current = isControlled ? value : inner;\n\n const handleInput = (e) => {\n const next = e.target.value;\n if (!isControlled) setInner(next);\n onChange && onChange(next, e);\n };\n\n const handleClear = () => {\n if (!isControlled) setInner('');\n onChange && onChange('');\n requestAnimationFrame(() => inputRef.current && inputRef.current.focus());\n };\n\n const resolvedType = type === 'password' ? (showPassword ? 'text' : 'password') : type;\n\n const classes = [\n 'au-input',\n sizeMap[size],\n error ? 'is-error' : '',\n disabled ? 'is-disabled' : '',\n className\n ].filter(Boolean).join(' ');\n\n return (\n
\n
\n {prefix && {prefix}}\n\n \n\n {clearable && current && !disabled && (\n \n )}\n\n {type === 'password' && (\n setShowPassword((v) => !v)}\n >\n {showPassword ? : }\n \n )}\n\n {suffix && {suffix}}\n
\n\n {error && errorText && {errorText}}\n
\n );\n}\n\n/*\n用法示例:\nimport Input from './Input';\n\n\n\n\n\n\n} placeholder=\"搜索订单编号\" />\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"select","name":"选择器 Select","tier":"core","confidence":"high","specBatch":1,"specFile":"组件1.txt","contractRef":"components/select.json","category":"input","specLines":["- 触发器样式同Input","- 下拉面板:白底、圆角4px、阴影中、最大高度256px","- 选中项背景#F0F5FF,文字#2F54EB,左侧勾选图标","- 支持单选、多选、搜索、分组、级联"],"specSummary":"- 触发器样式同Input","contract":{"variants":["单选","多选","可搜索","分组","下拉展开"],"dims":[{"name":"mode","values":["single","multiple","searchable","grouped","cascader"]},{"name":"state","values":["default","open","disabled"]}],"unknowns":["多选标签样式细节"],"anatomy":{"trigger":"触发器,样式同 Input","panel":"下拉面板:白底、圆角4px、阴影中、最大高度256px","option":"选项项,选中项背景 var(--color-brand-bg),文字 var(--color-brand),左侧勾选图标","searchInput":"可搜索模式下的搜索输入框"},"doNot":["级联面板具体布局"],"usage":["触发器样式同 Input","下拉面板:白底、圆角4px、阴影中、最大高度256px","选中项背景 var(--color-brand-bg),文字 var(--color-brand),左侧勾选图标","支持单选、多选、搜索、分组、级联"],"structure":{"panelRadius":"var(--radius-base)","panelMaxHeight":"256px","optionHeight":"32px"}},"files":{"html":"../frameworks/select.html","css":"../frameworks/Select.css","jsx":"../frameworks/Select.jsx","vue2":"../frameworks/Select.vue2.vue","vue3":"../frameworks/Select.vue3.vue"},"sizes":{"html":10026,"css":2548,"jsx":5803,"vue2":7153,"vue3":7205},"sources":{"html":"\n\n\n\n\nAurora Admin · Select (H5)\n\n\n\n\n
\n

Aurora Admin · Select 组件(纯 H5)

\n

对齐 select.json 契约:触发器同 Input,面板圆角4px / 阴影中 / 最大高度256px,选中项品牌色 + 勾选图标

\n\n
\n

1. 单选

\n
\n
\n
请选择城市▾
\n \n
\n
\n
\n\n
\n

2. 多选

\n
\n
\n
请选择角色▾
\n \n
\n
\n
\n\n
\n

3. 可搜索

\n
\n
\n
搜索并选择用户▾
\n \n
\n
\n
\n\n
\n

4. 分组

\n
\n
\n
请选择部门▾
\n \n
\n
\n
\n
\n\n\n\n\n","css":"/* Aurora Admin Select 样式 — 对齐 components.css 与 select.json 契约(触发器同 Input) */\n.aa-select { position: relative; display: inline-block; min-width: 220px; }\n.aa-select-trigger {\n display: flex; align-items: center; gap: 8px; min-height: 32px; height: auto;\n padding: 2px 28px 2px 12px; background: #fff; border: 1px solid #E8ECF1;\n border-radius: 4px; box-sizing: border-box; cursor: pointer; transition: border-color .15s, box-shadow .15s;\n font-size: 14px; color: #262626; flex-wrap: wrap; position: relative;\n}\n.aa-select-trigger:hover { border-color: #2F54EB; }\n.aa-select.is-open .aa-select-trigger { border-color: #2F54EB; box-shadow: 0 0 0 2px rgba(47,84,235,0.2); }\n.aa-select.is-disabled .aa-select-trigger { background: #F5F5F5; border-color: transparent; color: #BFBFBF; cursor: not-allowed; }\n.aa-select-trigger .placeholder { color: #767676; }\n.aa-select-trigger .caret { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); transition: transform .15s; color: #6E6E6E; }\n.aa-select.is-open .caret { transform: translateY(-50%) rotate(180deg); }\n.aa-select-trigger .tag {\n display: inline-flex; align-items: center; gap: 4px; background: #F0F5FF; color: #2F54EB;\n border-radius: 2px; padding: 1px 6px; font-size: 12px; line-height: 20px;\n}\n.aa-select-trigger .tag .x { cursor: pointer; }\n.aa-select-panel {\n position: absolute; z-index: 10; top: calc(100% + 4px); left: 0; right: 0;\n background: #fff; border-radius: 4px; box-shadow: 0 4px 16px rgba(0,0,0,0.12);\n max-height: 256px; overflow-y: auto; padding: 4px 0;\n}\n.aa-select-search { padding: 6px 8px; border-bottom: 1px solid #F0F0F0; }\n.aa-select-search input { width: 100%; box-sizing: border-box; border: 1px solid #E8ECF1; border-radius: 4px; padding: 4px 8px; font-size: 13px; outline: none; }\n.aa-select-search input:focus { border-color: #2F54EB; }\n.aa-select-group-label { padding: 6px 12px; font-size: 12px; color: #6E6E6E; }\n.aa-select-option {\n display: flex; align-items: center; gap: 8px; padding: 0 12px; height: 32px; cursor: pointer;\n font-size: 14px; color: #262626; box-sizing: border-box;\n}\n.aa-select-option:hover { background: #F5F7FA; }\n.aa-select-option.is-selected { background: #F0F5FF; color: #2F54EB; }\n.aa-select-option .check { visibility: hidden; margin-left: auto; }\n.aa-select-option.is-selected .check { visibility: visible; }\n.aa-select-option.is-disabled { color: #BFBFBF; cursor: not-allowed; }\n.aa-select-empty { padding: 16px 12px; text-align: center; color: #767676; font-size: 13px; }\n","jsx":"import React, { useState, useRef, useEffect, useMemo } from 'react';\nimport './Select.css';\n\n/**\n * Aurora Admin Select(React)\n * 对齐 select.json 契约:\n * - mode: single | multiple(multiple 属性)\n * - feature: searchable | grouped(groups 属性)\n * - state: default | open | disabled\n * 触发器样式同 Input;面板圆角4px / 阴影中 / 最大高度256px;选中项品牌色 + 勾选图标\n * 受控:\n */\nfunction flatten(groups) {\n return (groups || []).flatMap((g) => g.options);\n}\n\nexport default function Select({\n value,\n defaultValue = null,\n options = [],\n groups = null,\n multiple = false,\n searchable = false,\n disabled = false,\n placeholder = '请选择',\n className = '',\n onChange,\n ...rest\n}) {\n const isControlled = value !== undefined;\n const [inner, setInner] = useState(defaultValue);\n const [open, setOpen] = useState(false);\n const [keyword, setKeyword] = useState('');\n const rootRef = useRef(null);\n\n const current = isControlled ? value : inner;\n const selectedArr = Array.isArray(current) ? current : [];\n const selectedVal = Array.isArray(current) ? null : current;\n\n const filteredOptions = useMemo(() => {\n if (!searchable || !keyword) return options;\n const q = keyword.trim().toLowerCase();\n return options.filter((o) => String(o.label).toLowerCase().includes(q));\n }, [options, searchable, keyword]);\n\n const filteredGroups = useMemo(() => {\n if (!groups) return [];\n if (!searchable || !keyword) return groups;\n const q = keyword.trim().toLowerCase();\n return groups\n .map((g) => ({ label: g.label, options: g.options.filter((o) => String(o.label).toLowerCase().includes(q)) }))\n .filter((g) => g.options.length);\n }, [groups, searchable, keyword]);\n\n const isEmpty = groups ? filteredGroups.length === 0 : filteredOptions.length === 0;\n\n const flat = options.length ? options : flatten(groups);\n\n function labelOf(v) {\n const found = flat.find((o) => o.value === v);\n return found ? found.label : v;\n }\n function isSelected(v) {\n return multiple ? selectedArr.includes(v) : selectedVal === v;\n }\n function emit(next) {\n if (!isControlled) setInner(next);\n onChange && onChange(next);\n }\n function choose(o) {\n if (o.disabled) return;\n if (multiple) {\n const next = selectedArr.includes(o.value)\n ? selectedArr.filter((x) => x !== o.value)\n : [...selectedArr, o.value];\n emit(next);\n } else {\n emit(o.value);\n setOpen(false);\n }\n }\n function remove(v) {\n emit(selectedArr.filter((x) => x !== v));\n }\n\n useEffect(() => {\n function onDocClick(e) {\n if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);\n }\n document.addEventListener('click', onDocClick);\n return () => document.removeEventListener('click', onDocClick);\n }, []);\n\n const classes = ['aa-select', open ? 'is-open' : '', disabled ? 'is-disabled' : '', className]\n .filter(Boolean)\n .join(' ');\n\n return (\n
\n
!disabled && setOpen((o) => !o)} tabIndex={0}>\n {multiple ? (\n selectedArr.length ? (\n selectedArr.map((v) => (\n \n {labelOf(v)}\n { e.stopPropagation(); remove(v); }}>✕\n \n ))\n ) : (\n {placeholder}\n )\n ) : selectedVal == null ? (\n {placeholder}\n ) : (\n {labelOf(selectedVal)}\n )}\n ▾\n
\n\n {open && (\n
\n {searchable && (\n
\n setKeyword(e.target.value)}\n onClick={(e) => e.stopPropagation()}\n />\n
\n )}\n\n {groups ? (\n filteredGroups.map((g) => (\n \n
{g.label}
\n {g.options.map((o) => (\n choose(o)}\n >\n {o.label}✓\n
\n ))}\n \n ))\n ) : (\n filteredOptions.map((o) => (\n choose(o)}\n >\n {o.label}✓\n
\n ))\n )}\n\n {isEmpty &&
无匹配选项
}\n \n )}\n \n );\n}\n\n/*\n用法示例:\nimport Select from './Select';\n\nconst cities = [{ value: 'bj', label: '北京' }, { value: 'sh', label: '上海' }];\n\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"table","name":"数据表格 Table","tier":"core","confidence":"high","specBatch":1,"specFile":"组件1.txt","contractRef":"components/table.json","category":"display","specLines":["- 表头:背景#FAFAFA,文字#262626,行高48px","- 行高48px,分割线#E8ECF1,悬停背景#F5F7FA","- 单元格内边距左右16px","- 文本左对齐,数字/金额右对齐","- 选中行背景#F0F5FF","- 支持排序、筛选、自定义列、固定列、展开行、行选择、批量操作","- 操作列右对齐,文字按钮间距8px"],"specSummary":"- 表头:背景#FAFAFA,文字#262626,行高48px","contract":{"variants":["基础表格","可排序","行选择","固定列","展开行","批量操作"],"dims":[{"name":"feature","values":["sortable","filterable","selectable","fixed-column","expandable","batch-actions"]},{"name":"state","values":["default","hover","selected","loading","empty"]}],"unknowns":["表格密度切换具体实现"],"anatomy":{"header":"表头:背景 var(--color-bg-table-header),文字 var(--color-text-body),行高48px","row":"数据行:行高48px,分割线 var(--color-border),悬停背景 var(--color-bg-page)","cell":"单元格:内边距左右16px,文本左对齐,数字/金额右对齐","selectedRow":"选中行背景 var(--color-brand-bg)","actionColumn":"操作列右对齐,文字按钮间距8px"},"doNot":["分页器与表格的组合规范"],"usage":["表头:背景 var(--color-bg-table-header),文字 var(--color-text-body),行高48px","行高48px,分割线 var(--color-border),悬停背景 var(--color-bg-page)","单元格内边距左右16px,文本左对齐,数字/金额右对齐","选中行背景 var(--color-brand-bg)","支持排序、筛选、自定义列、固定列、展开行、行选择、批量操作","操作列右对齐,文字按钮间距8px"],"structure":{"headerHeight":"48px","rowHeight":"48px","cellPadding":"左右16px","divider":"1px solid var(--color-border)"}},"files":{"html":"../frameworks/table.html","css":"../frameworks/Table.css","jsx":"../frameworks/Table.jsx","vue2":"../frameworks/Table.vue2.vue","vue3":"../frameworks/Table.vue3.vue"},"sizes":{"html":6285,"css":1657,"jsx":5568,"vue2":5981,"vue3":5887},"sources":{"html":"\n\n\n\n\nAurora Admin · Table (H5)\n\n\n\n\n
\n

Aurora Admin · Table 组件(纯 H5)

\n

对齐 table.json 契约:表头/行高48px、分割线 #E8ECF1、悬停 #F5F7FA、选中 #F0F5FF、单元格左右16px、数字右对齐、操作列右对齐、加载/空态

\n\n \n\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
客户名称负责人合同金额↕状态操作
\n \n
\n
\n\n\n\n\n","css":"/* Aurora Admin Table 样式 — 对齐 components.css 与 table.json 契约 */\n.aa-table { width: 100%; border: 1px solid #E8ECF1; border-radius: 8px; overflow: hidden; background: #fff; position: relative; }\n.aa-table table { width: 100%; border-collapse: collapse; font-size: 14px; color: #262626; }\n.aa-table thead th {\n background: #FAFAFA; color: #262626; font-weight: 600; height: 48px; text-align: left;\n padding: 0 16px; border-bottom: 1px solid #E8ECF1; white-space: nowrap;\n}\n.aa-table tbody td { height: 48px; padding: 0 16px; border-bottom: 1px solid #E8ECF1; text-align: left; }\n.aa-table tbody tr:last-child td { border-bottom: none; }\n.aa-table tbody tr:hover { background: #F5F7FA; }\n.aa-table tbody tr.is-selected { background: #F0F5FF; }\n.aa-table .align-right { text-align: right; }\n.aa-table .sortable { cursor: pointer; user-select: none; }\n.aa-table .sort-ind { color: #767676; margin-left: 4px; }\n.aa-table .sort-ind.active { color: #2F54EB; }\n.aa-table .col-action { text-align: right; }\n.aa-table .col-selection { text-align: center; }\n.aa-table .checkbox { width: 16px; height: 16px; cursor: pointer; vertical-align: middle; }\n.aa-table .empty { padding: 48px 16px; text-align: center; color: #767676; font-size: 14px; }\n.aa-table .loading-mask {\n position: absolute; inset: 0; background: rgba(255,255,255,0.7); display: flex;\n align-items: center; justify-content: center; z-index: 2;\n}\n.aa-table .spinner {\n width: 20px; height: 20px; border: 2px solid #2F54EB; border-top-color: transparent;\n border-radius: 50%; animation: aa-tbl-spin .6s linear infinite;\n}\n@keyframes aa-tbl-spin { to { transform: rotate(360deg); } }\n","jsx":"import React, { useState, useMemo } from 'react';\nimport './Table.css';\n\n/**\n * Aurora Admin Table(React)\n * 对齐 table.json 契约:\n * - feature: sortable / selectable / batch-actions(含全选)\n * - state: hover / selected / loading / empty\n * - 表头/行高48px、分割线 #E8ECF1、悬停 #F5F7FA、选中 #F0F5FF、单元格左右16px、数字右对齐、操作列右对齐\n *\n * columns: [{ key, title, align?:'left'|'right', sortable?, width?, fixed?, type?:'action' }]\n * 受控选择:selectedKeys={keys} onSelectionChange={setKeys}\n * 操作列:renderAction={(row, index) => }\n */\nfunction sortData(data, key, order) {\n if (!key) return data;\n const dir = order === 'asc' ? 1 : -1;\n return [...data].sort((a, b) => {\n const av = a[key], bv = b[key];\n if (typeof av === 'number' && typeof bv === 'number') return (av - bv) * dir;\n return String(av).localeCompare(String(bv), 'zh') * dir;\n });\n}\n\nexport default function Table({\n columns = [],\n data = [],\n rowKey = 'id',\n selectable = false,\n selectedKeys = [],\n loading = false,\n emptyText = '暂无数据',\n onSelectionChange,\n onSortChange,\n renderAction\n}) {\n const [sortKey, setSortKey] = useState(null);\n const [sortOrder, setSortOrder] = useState(null); // asc | desc | null\n\n const sortedData = useMemo(\n () => sortData(data, sortKey, sortOrder),\n [data, sortKey, sortOrder]\n );\n\n const allSelected = data.length > 0 && selectedKeys.length === data.length;\n const someSelected = selectedKeys.length > 0 && selectedKeys.length < data.length;\n\n const isSelected = (id) => selectedKeys.includes(id);\n const emitSelection = (next) => onSelectionChange && onSelectionChange(next);\n\n const toggleRow = (id) => {\n const next = isSelected(id) ? selectedKeys.filter((x) => x !== id) : [...selectedKeys, id];\n emitSelection(next);\n };\n const toggleAll = () => {\n const next = allSelected ? [] : data.map((r) => r[rowKey]);\n emitSelection(next);\n };\n const onSort = (col) => {\n if (!col.sortable) return;\n let k = sortKey, o = sortOrder;\n if (k !== col.key) { k = col.key; o = 'asc'; }\n else { o = o === 'asc' ? 'desc' : o === 'desc' ? null : 'asc'; if (o === null) k = null; }\n setSortKey(k); setSortOrder(o);\n onSortChange && onSortChange({ key: k, order: o });\n };\n\n const colCount = columns.length + (selectable ? 1 : 0);\n\n return (\n
\n \n \n \n {selectable && (\n \n )}\n {columns.map((col) => (\n onSort(col)}\n >\n {col.title}\n {col.sortable && (\n \n {sortKey === col.key ? (sortOrder === 'asc' ? '↑' : sortOrder === 'desc' ? '↓' : '↕') : '↕'}\n \n )}\n \n ))}\n \n \n \n {sortedData.length === 0 ? (\n \n ) : (\n sortedData.map((row, index) => (\n \n {selectable && (\n \n )}\n {columns.map((col) => (\n \n {col.type === 'action' && renderAction ? renderAction(row, index) : row[col.key]}\n \n ))}\n \n ))\n )}\n \n
\n el && (el.indeterminate = someSelected)}\n onChange={toggleAll}\n />\n
{emptyText}
\n toggleRow(row[rowKey])}\n />\n
\n {loading && (\n
\n )}\n
\n );\n}\n\n/*\n用法示例:\nimport Table from './Table';\n\nconst columns = [\n { key: 'name', title: '客户名称' },\n { key: 'amount', title: '合同金额', align: 'right', sortable: true },\n { type: 'action', title: '操作' }\n];\n (<>\n \n \n )}\n/>\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"card","name":"卡片 Card","tier":"core","confidence":"high","specBatch":1,"specFile":"组件1.txt","contractRef":"components/card.json","category":"general","specLines":["- 圆角8px,边框1px #E8ECF1 或低阴影","- 内边距16px/24px","- 结构:标题区、内容区、操作区"],"specSummary":"- 圆角8px,边框1px #E8ECF1 或低阴影","contract":{"variants":["边框卡片","阴影卡片","含标题与操作区"],"dims":[{"name":"style","values":["bordered","shadowed"]},{"name":"padding","values":["16px","24px"]},{"name":"section","values":["title","content","actions"]}],"unknowns":["卡片网格间距规范"],"anatomy":{"container":"卡片容器:圆角8px,边框1px var(--color-border) 或低阴影","titleArea":"标题区:可含标题与右侧操作","contentArea":"内容区","actionArea":"操作区"},"doNot":["卡片 hover 交互规范"],"usage":["圆角8px,边框1px var(--color-border) 或低阴影","内边距16px/24px","结构:标题区、内容区、操作区"],"structure":{"radius":"var(--radius-large)","border":"1px solid var(--color-border)","shadow":"var(--shadow-low)","padding":"16px / 24px"}},"files":{"html":"../frameworks/Card.html","css":"../frameworks/Card.css","jsx":"../frameworks/Card.jsx","vue2":"../frameworks/Card.vue2.vue","vue3":"../frameworks/Card.vue3.vue"},"sizes":{"html":2933,"css":695,"jsx":1337,"vue2":1732,"vue3":1714},"sources":{"html":"\n\n\n\n\nAurora Admin · Card (H5)\n\n\n\n\n
\n

Aurora Admin · Card 组件(纯 H5)

\n

对齐 card.json 契约:圆角8px、边框1px / 低阴影、内边距16/24px、结构含标题区/内容区/操作区

\n\n
\n
\n
边框卡片
\n
用于轻量内容承载,1px 边框区分卡片边界。
\n
\n\n
\n
阴影卡片
\n
通过低阴影表现层级浮起,适合突出展示类内容。
\n
\n\n
\n
\n
客户详情
\n
查看全部
\n
\n
含标题区与右侧操作区,内容区展示客户关键信息。
\n
\n \n \n
\n
\n
\n
\n\n\n","css":"/* Aurora Admin Card 样式 — 对齐 components.css 与 card.json 契约 */\n.aa-card {\n background: #fff; border-radius: 8px; box-sizing: border-box;\n min-width: 280px; max-width: 360px;\n}\n.aa-card--bordered { border: 1px solid #E8ECF1; }\n.aa-card--shadowed { box-shadow: 0 1px 4px rgba(0,0,0,0.06); }\n.aa-card__header {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n font-weight: 600; color: #1F2329;\n}\n.aa-card__title { font-size: 16px; }\n.aa-card__extra { color: #2F54EB; font-size: 14px; cursor: pointer; }\n.aa-card__body { color: #262626; font-size: 14px; line-height: 22px; }\n.aa-card__actions { display: flex; justify-content: flex-end; gap: 8px; }\n","jsx":"import React from 'react';\nimport './Card.css';\n\n/**\n * Aurora Admin Card(React)\n * 对齐 card.json 契约:\n * - style: bordered | shadowed\n * - padding: 16 | 24\n * - section: title(含右侧 extra)/ content / actions\n */\nexport default function Card({\n title = '',\n bordered = false,\n shadowed = false,\n padding = 16,\n className = '',\n children,\n extra,\n actions\n}) {\n const paddingPx = typeof padding === 'number' ? `${padding}px` : padding;\n const classes = [\n 'aa-card',\n bordered ? 'aa-card--bordered' : '',\n shadowed ? 'aa-card--shadowed' : '',\n className\n ].filter(Boolean).join(' ');\n\n return (\n
\n {(title || extra) && (\n
\n
{title}
\n {extra &&
{extra}
}\n
\n )}\n
{children}
\n {actions &&
{actions}
}\n
\n );\n}\n\n/*\n用法示例:\nimport Card from './Card';\n\n查看全部} shadowed padding={24}>\n 内容区...\n\n}>\n 内容区...\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"modal","name":"对话框 Modal","tier":"core","confidence":"high","specBatch":1,"specFile":"组件1.txt","contractRef":"components/modal.json","category":"feedback","specLines":["- 宽度480px(默认)/680px(大)/320px(小)","- 圆角8px,遮罩rgba(0,0,0,0.45)","- 结构:标题、内容、右下角操作按钮(次按钮+主按钮)"],"specSummary":"- 宽度480px(默认)/680px(大)/320px(小)","contract":{"variants":["默认对话框","大对话框","小对话框"],"dims":[{"name":"size","values":["small","default","large"]},{"name":"state","values":["closed","open"]}],"unknowns":["抽屉 Drawer 与 Modal 的共用规范"],"anatomy":{"mask":"遮罩 rgba(0,0,0,0.45)","container":"对话框容器:圆角8px,阴影高","title":"标题区","content":"内容区","footer":"右下角操作按钮(次按钮+主按钮)"},"doNot":["对话框动画规范"],"usage":["宽度480px(默认)/680px(大)/320px(小)","圆角8px,遮罩 rgba(0,0,0,0.45)","结构:标题、内容、右下角操作按钮(次按钮+主按钮)"],"structure":{"width":"@{small=320px; default=480px; large=680px}","radius":"var(--radius-large)","mask":"rgba(0,0,0,0.45)"}},"files":{"html":"../frameworks/modal.html","css":"../frameworks/Modal.css","jsx":"../frameworks/Modal.jsx","vue2":"../frameworks/Modal.vue2.vue","vue3":"../frameworks/Modal.vue3.vue"},"sizes":{"html":3374,"css":1265,"jsx":1849,"vue2":2706,"vue3":2645},"sources":{"html":"\n\n\n\n\nAurora Admin · Modal (H5)\n\n\n\n\n
\n

Aurora Admin · Modal 组件(纯 H5)

\n

对齐 modal.json 契约:宽度480(默认)/680(大)/320(小),圆角8px,遮罩 rgba(0,0,0,0.45),右下角操作按钮

\n\n \n \n \n
\n\n
\n\n\n\n\n","css":"/* Aurora Admin Modal 样式 — 对齐 components.css 与 modal.json 契约 */\n.aa-mask {\n position: fixed; inset: 0; background: rgba(0,0,0,0.45);\n display: flex; align-items: center; justify-content: center; z-index: 1000;\n}\n.aa-modal {\n background: #fff; border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.12);\n display: flex; flex-direction: column; max-width: 90vw;\n}\n.aa-modal--small { width: 320px; }\n.aa-modal--default { width: 480px; }\n.aa-modal--large { width: 680px; }\n.aa-modal__header {\n display: flex; align-items: center; justify-content: space-between;\n padding: 16px 24px; font-size: 16px; font-weight: 600; color: #1F2329;\n}\n.aa-modal__close { border: none; background: none; cursor: pointer; color: #6E6E6E; font-size: 16px; line-height: 1; }\n.aa-modal__close:hover { color: #262626; }\n.aa-modal__body { padding: 0 24px 24px; color: #262626; font-size: 14px; line-height: 22px; }\n.aa-modal__footer { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 24px 16px; }\n.btn-base { border-radius: 4px; padding: 4px 16px; height: 32px; cursor: pointer; font-size: 14px; }\n.btn-ghost { border: 1px solid #E8ECF1; background: #fff; color: #262626; }\n.btn-primary { border: 1px solid #2F54EB; background: #2F54EB; color: #fff; }\n","jsx":"import React from 'react';\nimport './Modal.css';\n\n/**\n * Aurora Admin Modal(React)\n * 对齐 modal.json 契约:\n * - size: small(320) | default(480) | large(680)\n * - 遮罩 rgba(0,0,0,0.45),圆角8px,右下角操作按钮(次+主)\n * 受控:\n */\nexport default function Modal({\n open = false,\n title = '',\n size = 'default', // small | default | large\n closable = true,\n maskClosable = true,\n hideFooter = false,\n cancelText = '取消',\n okText = '确定',\n onClose,\n onOk,\n footer,\n children\n}) {\n if (!open) return null;\n\n const handleMask = (e) => {\n if (maskClosable && e.target === e.currentTarget) onClose && onClose();\n };\n\n return (\n
\n
e.stopPropagation()}>\n
\n {title}\n {closable && (\n \n )}\n
\n
{children}
\n {!hideFooter && (\n
\n {footer || (\n <>\n \n \n \n )}\n
\n )}\n
\n
\n );\n}\n\n/*\n用法示例:\nimport Modal from './Modal';\nconst [open, setOpen] = useState(false);\n\n setOpen(false)} onOk={handleOk}>\n 对话框内容...\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"tag","name":"标签 Tag","tier":"core","confidence":"high","specBatch":1,"specFile":"组件1.txt","contractRef":"components/tag.json","category":"general","specLines":["- 圆角2px,高度22px,内边距左右8px","- 颜色:浅色背景+深色文字(如蓝#F0F5FF/#2F54EB,绿#F6FFED/#52C41A等)","- 可关闭标签悬停显示关闭按钮"],"specSummary":"- 圆角2px,高度22px,内边距左右8px","contract":{"variants":["主题标签","成功标签","错误标签","可关闭标签","自定义色 + 圆点"],"dims":[{"name":"theme","values":["blue","green","red","orange","gray","custom"]},{"name":"closable","values":["true","false"]},{"name":"dot","values":["true","false"]}],"unknowns":["标签的最大宽度与超长省略方式","自定义色的合法取值边界"],"anatomy":{"container":"标签容器:圆角 2px,高度 22px,内边距左右 8px","text":"浅色背景 + 深色文字配色","close":"关闭按钮:悬停时才显示","dot":"圆点模式:用 currentColor 绘制 6px 圆点"},"doNot":["标签组换行与折叠策略"],"usage":["圆角2px,高度22px,内边距左右8px","颜色:浅色背景+深色文字(如蓝#F0F5FF/#2F54EB,绿#F6FFED/#2E7D0A等)","可关闭标签悬停显示关闭按钮"],"structure":{"radius":"2px","height":"22px","padding":"左右 8px","dotSize":"6px"}},"files":{"html":"../frameworks/Tag.html","css":"../frameworks/Tag.css","jsx":"../frameworks/Tag.jsx","vue2":"../frameworks/Tag.vue2.vue","vue3":"../frameworks/Tag.vue3.vue"},"sizes":{"html":2747,"css":1305,"jsx":1483,"vue2":2400,"vue3":2395},"sources":{"html":"\n\n\n\n\nAurora Admin · Tag (H5)\n\n\n\n\n\n
\n

Aurora Admin · Tag 组件(纯 H5)

\n

对齐设计令牌:bg #F0F5FF / color #2F54EB / 圆角 2px / padding 1px 8px

\n\n
\n

预设状态

\n
\n 品牌标签\n 成功\n 错误\n 警告\n 默认\n
\n
\n\n
\n

带圆点

\n
\n 在线\n 已发布\n 异常\n
\n
\n\n
\n

可关闭(点击 × 移除)

\n
\n 前端 ×\n 后端 ×\n 设计 ×\n
\n
\n\n
\n

自定义颜色

\n
\n 紫色\n 青色\n
\n
\n
\n\n\n\n\n","css":"/* Aurora Admin Tag 样式 — 对齐设计令牌:bg #F0F5FF / color #2F54EB / 圆角 2px / padding 1px 8px */\n\n.aa-tag {\n display: inline-flex; align-items: center; gap: 4px;\n height: 22px; padding: 1px 8px; box-sizing: border-box;\n font-size: 12px; line-height: 1; border-radius: 2px;\n background: #F0F5FF; color: #2F54EB; white-space: nowrap;\n border: 1px solid transparent;\n}\n\n/* 状态预设 */\n.aa-tag.aa-tag-green { background: #F6FFED; color: #2E7D0A; }\n.aa-tag.aa-tag-red { background: #FFF1F0; color: #CF1322; }\n.aa-tag.aa-tag-orange { background: #FFF7E6; color: #8C5A00; }\n.aa-tag.aa-tag-gray { background: #F5F5F5; color: #6E6E6E; }\n\n/* 自定义颜色(行内 style 提供 --aa-tag-color / --aa-tag-bg) */\n.aa-tag.aa-tag-custom { background: var(--aa-tag-bg); color: var(--aa-tag-color); }\n\n/* 圆点模式 */\n.aa-tag.aa-tag-dot::before {\n content: ''; width: 6px; height: 6px; border-radius: 50%;\n background: currentColor; display: inline-block;\n}\n\n/* 关闭按钮 */\n.aa-tag-close { display: inline-flex; align-items: center; justify-content: center;\n width: 14px; height: 14px; margin-right: -2px; border-radius: 50%; cursor: pointer;\n font-size: 11px; line-height: 1; color: inherit; opacity: .65; }\n.aa-tag-close:hover { opacity: 1; background: rgba(0,0,0,0.08); }\n","jsx":"import React from 'react';\nimport './Tag.css';\n\n/**\n * Aurora Admin Tag(React)\n * 对齐设计令牌:bg #F0F5FF / color #2F54EB / 圆角 2px / padding 1px 8px\n *\n * props:\n * - color : '' | 'blue' | 'green' | 'red' | 'orange' | 'gray' | 自定义 hex\n * - dot : Boolean(圆点模式)\n * - closable: Boolean\n * - onClose()\n * - children: 标签内容\n */\nexport default function Tag({ color = '', dot = false, closable = false, onClose, children }) {\n const PRESET = ['green', 'red', 'orange', 'gray'];\n const isCustom = !!color && !PRESET.includes(color) && color !== 'blue';\n\n const cls = ['aa-tag'];\n if (dot) cls.push('aa-tag-dot');\n if (isCustom) cls.push('aa-tag-custom');\n else if (color && color !== 'blue') cls.push('aa-tag-' + color);\n\n const style = isCustom ? { '--aa-tag-color': color, '--aa-tag-bg': hexToTint(color) } : undefined;\n\n return (\n \n {children}\n {closable && (\n { e.stopPropagation(); onClose && onClose(); }}>×\n )}\n \n );\n}\n\nfunction hexToTint(hex) {\n const h = hex.replace('#', '');\n const r = parseInt(h.slice(0, 2), 16), g = parseInt(h.slice(2, 4), 16), b = parseInt(h.slice(4, 6), 16);\n return `rgba(${r},${g},${b},0.1)`;\n}\n\n/*\n用法示例:\nimport Tag from './Tag';\n已发布\n紫色\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"tabs","name":"标签页 Tabs","tier":"core","confidence":"high","specBatch":1,"specFile":"组件1.txt","contractRef":"components/tabs.json","category":"navigation","specLines":["- 线型:底部2px品牌色指示条","- 卡片型:选中白底无边框,未选中浅灰背景"],"specSummary":"- 线型:底部2px品牌色指示条","contract":{"variants":["线型 · 当前项","线型 · 默认项","卡片型 · 选中(白底无边框)","卡片型 · 未选中(浅灰底)","禁用项"],"dims":[{"name":"type","values":["line","card"]},{"name":"state","values":["default","active","disabled","closable"]}],"unknowns":["标签关闭按钮的出现时机(常显或悬停)","卡片型圆角取值"],"anatomy":{"nav":"标签导航条","indicator":"线型:底部 2px 品牌色指示条","cardSelected":"卡片型选中:白底无边框","cardDefault":"卡片型未选中:浅灰背景"},"doNot":["可滚动标签页的滚动按钮形态","标签过多时的折叠菜单"],"usage":["线型:底部2px品牌色指示条","卡片型:选中白底无边框,未选中浅灰背景"],"structure":{"indicatorHeight":"2px","indicatorColor":"品牌色"}},"files":{"html":"../frameworks/tabs.html","css":"../frameworks/Tabs.css","jsx":"../frameworks/Tabs.jsx","vue2":"../frameworks/Tabs.vue2.vue","vue3":"../frameworks/Tabs.vue3.vue"},"sizes":{"html":5064,"css":3131,"jsx":2284,"vue2":4236,"vue3":4280},"sources":{"html":"\n\n\n\n\nAurora Admin · Tabs (H5)\n\n\n\n\n\n
\n

Aurora Admin · Tabs 组件(纯 H5)

\n

对齐 PageTabs 规范:线型 / 卡片型 / 胶囊型,支持禁用、关闭、新增

\n\n \n
\n

线型(默认)

\n
\n
\n
基本信息
\n
安全设置
\n
通知
\n
授权
\n
\n
\n
这里是「基本信息」面板内容。
\n \n \n \n
\n
\n
\n\n \n
\n

卡片型(可关闭 / 新增)

\n
\n
\n
订单 ×
\n
退款 ×
\n
+
\n
\n
\n
订单标签页内容。
\n \n
\n
\n
\n\n \n
\n

胶囊型

\n
\n
\n
全部
\n
待办
\n
已完成
\n
\n
\n
全部任务。
\n \n \n
\n
\n
\n
\n\n\n\n\n","css":"/* Aurora Admin Tabs 样式 — 对齐 组件7.txt PageTabs 规范:线型 / 卡片型 / 胶囊型 */\n\n.aa-tabs { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n\n/* 标签导航条 */\n.aa-tabs-nav {\n display: flex; align-items: flex-end; gap: 4px;\n position: relative; padding: 0 4px;\n}\n/* 线型:整条导航底部 1px 边框 */\n.aa-tabs.type-line .aa-tabs-nav { border-bottom: 1px solid #E8ECF1; }\n/* 胶囊型:整条导航为浅灰胶囊容器 */\n.aa-tabs.type-pill .aa-tabs-nav { background: #F5F7FA; border-radius: 16px; padding: 4px; gap: 4px; }\n\n/* 单个标签 */\n.aa-tabs-tab {\n position: relative; box-sizing: border-box;\n display: inline-flex; align-items: center; gap: 6px;\n height: 40px; padding: 0 16px; cursor: pointer;\n font-size: 14px; line-height: 40px; color: #262626;\n border: 1px solid transparent; background: none;\n transition: color .15s, background .15s, border-color .15s;\n user-select: none; white-space: nowrap;\n}\n.aa-tabs-tab:hover:not(.is-active):not(.is-disabled) { color: #2F54EB; }\n\n/* 线型 active:文字品牌色 + 底部 2px 指示条 */\n.aa-tabs.type-line .aa-tabs-tab { border-bottom: 2px solid transparent; margin-bottom: -1px; border-radius: 0; }\n.aa-tabs.type-line .aa-tabs-tab.is-active {\n color: #2F54EB; font-weight: 500; border-bottom-color: #2F54EB;\n}\n\n/* 卡片型 active:边框 + 白底,与内容区连成一体 */\n.aa-tabs.type-card .aa-tabs-nav { border-bottom: 1px solid #E8ECF1; }\n.aa-tabs.type-card .aa-tabs-tab { border: 1px solid #E8ECF1; border-bottom: none; border-radius: 6px 6px 0 0; background: #F5F7FA; }\n.aa-tabs.type-card .aa-tabs-tab.is-active { background: #fff; color: #2F54EB; border-color: #E8ECF1; }\n\n/* 胶囊型 active:品牌色填充白字 */\n.aa-tabs.type-pill .aa-tabs-tab { border: none; border-radius: 14px; height: 32px; line-height: 32px; padding: 0 14px; }\n.aa-tabs.type-pill .aa-tabs-tab.is-active { background: #2F54EB; color: #fff; font-weight: 500; }\n\n/* 禁用 */\n.aa-tabs-tab.is-disabled { color: #BFBFBF; cursor: not-allowed; }\n.aa-tabs.type-card .aa-tabs-tab.is-disabled { background: #F5F5F5; }\n\n/* 关闭按钮 */\n.aa-tabs-close { display: inline-flex; align-items: center; justify-content: center;\n width: 16px; height: 16px; margin-left: 4px; border-radius: 50%; font-size: 12px; color: #6E6E6E; }\n.aa-tabs-close:hover { background: rgba(0,0,0,0.06); color: #262626; }\n.aa-tabs.type-pill .aa-tabs-close { color: rgba(255,255,255,0.85); }\n.aa-tabs.type-pill .aa-tabs-close:hover { background: rgba(255,255,255,0.25); color: #fff; }\n\n/* 新增标签按钮 */\n.aa-tabs-add { display: inline-flex; align-items: center; justify-content: center;\n width: 32px; height: 32px; margin-left: 4px; border: 1px dashed #E8ECF1; border-radius: 6px;\n color: #6E6E6E; cursor: pointer; font-size: 16px; }\n.aa-tabs-add:hover { color: #2F54EB; border-color: #2F54EB; }\n\n/* 内容区 */\n.aa-tabs-content { padding: 16px; font-size: 14px; color: #262626; }\n.aa-tabs.type-card .aa-tabs-content { border: 1px solid #E8ECF1; border-top: none; border-radius: 0 0 6px 6px; }\n","jsx":"import React from 'react';\nimport './Tabs.css';\n\n/**\n * Aurora Admin Tabs(React)\n * 对齐 组件7.txt PageTabs 规范:线型 / 卡片型 / 胶囊型,支持禁用、关闭、新增\n *\n * props:\n * - value / defaultValue : 当前激活标签 key(受控 / 非受控)\n * - tabs : [{ key, label, disabled?, preventClose? }]\n * - type : 'line' | 'card' | 'pill'(默认 'line')\n * - closable / addable : Boolean\n * - onChange(key) / onTabRemove(key) / onTabAdd()\n * - renderPanel(key) / children(key) : 返回当前激活标签的内容\n */\nexport default function Tabs({\n value,\n defaultValue,\n tabs = [],\n type = 'line',\n closable = false,\n addable = false,\n onChange,\n onTabRemove,\n onTabAdd,\n renderPanel\n}) {\n const current = value !== undefined ? value : defaultValue;\n const panelFn = renderPanel || (typeof children === 'function' ? children : null);\n\n const choose = (tab) => {\n if (tab.disabled) return;\n onChange && onChange(tab.key);\n };\n\n return (\n
\n
\n {tabs.map((tab) => (\n choose(tab)}\n >\n {tab.label}\n {closable && !tab.disabled && !tab.preventClose && (\n { e.stopPropagation(); onTabRemove && onTabRemove(tab.key); }}\n >×\n )}\n
\n ))}\n {addable && (\n
onTabAdd && onTabAdd()}>+
\n )}\n
\n\n
\n {panelFn ? panelFn(current) : null}\n
\n \n );\n}\n\n/*\n用法示例:\nimport Tabs from './Tabs';\n\nconst tabs = [\n { key: 'basic', label: '基本信息' },\n { key: 'safe', label: '安全设置' },\n { key: 'auth', label: '授权', disabled: true }\n];\n
当前标签:{key}
}\n/>\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"steps","name":"步骤条 Steps","tier":"core","confidence":"high","specBatch":1,"specFile":"组件1.txt","contractRef":"components/steps.json","category":"navigation","specLines":["- 节点32px圆点,完成绿对勾,当前品牌色,未完成灰色","- 连线完成段品牌色"],"specSummary":"- 节点32px圆点,完成绿对勾,当前品牌色,未完成灰色","contract":{"variants":["已完成(绿对勾)","当前步(品牌色)","未完成(灰色)","垂直步骤条"],"dims":[{"name":"state","values":["done","current","pending"]},{"name":"orientation","values":["horizontal","vertical"]}],"unknowns":["垂直方向节点尺寸是否一致","超过 5 步时的压缩策略"],"anatomy":{"node":"节点:32px 圆点","done":"完成态:绿对勾","current":"当前态:品牌色","pending":"未完成态:灰色","connector":"连线:完成段为品牌色"},"doNot":["步骤描述文字的位置与宽度","点击跳转的交互规则"],"usage":["节点32px圆点,完成绿对勾,当前品牌色,未完成灰色","连线完成段品牌色"],"structure":{"nodeSize":"32px","connectorDone":"品牌色"}},"files":{"html":"../frameworks/steps.html","css":"../frameworks/Steps.css","jsx":"../frameworks/Steps.jsx","vue2":"../frameworks/Steps.vue2.vue","vue3":"../frameworks/Steps.vue3.vue"},"sizes":{"html":2321,"css":1853,"jsx":1439,"vue2":2868,"vue3":2850},"sources":{"html":"\n\n\n\n\nAurora Admin · Steps (H5)\n\n\n\n\n\n
\n

Aurora Admin · Steps 组件(纯 H5)

\n

对齐 StepList 规范:序号、标题、描述、状态、连接线

\n\n
\n

横向步骤条

\n
\n
\n\n
\n

纵向步骤条(含异常状态)

\n
\n
\n
\n\n\n\n\n","css":"/* Aurora Admin Steps 样式 — 对齐 组件2/7 StepList:序号、标题、描述、状态、连接线、横/纵 */\n\n.aa-steps { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-steps.horizontal { display: flex; }\n.aa-steps.vertical { display: inline-flex; flex-direction: column; }\n\n.aa-step { position: relative; display: flex; gap: 12px; flex: 1; }\n.aa-steps.vertical .aa-step { flex-direction: column; }\n\n.aa-step-icon {\n position: relative; width: 32px; height: 32px; border-radius: 50%; border: 2px solid #E8ECF1;\n display: flex; align-items: center; justify-content: center; font-size: 14px; background: #fff; color: #6E6E6E; flex-shrink: 0; z-index: 1;\n}\n.aa-step.is-finish .aa-step-icon { border-color: #2F54EB; color: #2F54EB; background: #F0F5FF; }\n.aa-step.is-process .aa-step-icon { border-color: #2F54EB; color: #fff; background: #2F54EB; }\n.aa-step.is-error .aa-step-icon { border-color: #CF1322; color: #CF1322; background: #FFF1F0; }\n\n.aa-step-title { font-size: 14px; font-weight: 500; line-height: 32px; }\n.aa-step.is-wait .aa-step-title { color: #6E6E6E; }\n.aa-step-desc { font-size: 12px; color: #6E6E6E; margin-top: 2px; }\n\n/* 连接线 */\n.aa-steps.horizontal .aa-step:not(:last-child) .aa-step-icon::after {\n content: ''; position: absolute; top: 15px; left: 38px; right: -12px; height: 2px; background: #E8ECF1;\n}\n.aa-steps.horizontal .aa-step.is-finish:not(:last-child) .aa-step-icon::after { background: #2F54EB; }\n\n.aa-steps.vertical .aa-step-body { padding-bottom: 24px; }\n.aa-steps.vertical .aa-step:not(:last-child) .aa-step-icon::after {\n content: ''; position: absolute; top: 40px; left: 15px; bottom: -12px; width: 2px; background: #E8ECF1;\n}\n.aa-steps.vertical .aa-step.is-finish:not(:last-child) .aa-step-icon::after { background: #2F54EB; }\n","jsx":"import React from 'react';\nimport './Steps.css';\n\n/**\n * Aurora Admin Steps(React)\n * 对齐 组件2/7 StepList 规范:序号、标题、描述、状态、连接线、横/纵\n *\n * props:\n * - steps : [{ title, desc? }]\n * - current : 当前步骤索引\n * - errorAt : 异常步骤索引(-1 表示无)\n * - direction: 'horizontal' | 'vertical'\n */\nexport default function Steps({ steps = [], current = 0, errorAt = -1, direction = 'horizontal' }) {\n const statusOf = (i) => {\n if (i === errorAt) return 'error';\n if (i < current) return 'finish';\n if (i === current) return 'process';\n return 'wait';\n };\n const icon = (s) => (s === 'finish' ? '✓' : s === 'error' ? '!' : null);\n\n return (\n
\n {steps.map((s, i) => {\n const st = statusOf(i);\n return (\n
\n
{icon(st) !== null ? icon(st) : i + 1}
\n
\n
{s.title}
\n {s.desc &&
{s.desc}
}\n
\n
\n );\n })}\n
\n );\n}\n\n/*\n用法示例:\nimport Steps from './Steps';\nconst steps = [{ title: '填写订单' }, { title: '审核' }, { title: '付款' }];\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"breadcrumb","name":"面包屑 Breadcrumb","tier":"core","confidence":"high","specBatch":1,"specFile":"组件1.txt","contractRef":"components/breadcrumb.json","category":"navigation","specLines":"- 字号14px,层级用/分隔,次要文字#8C8C8C,当前页#262626","specSummary":"- 字号14px,层级用/分隔,次要文字#8C8C8C,当前页#262626","contract":{"variants":["可点击层级","当前页(正文色)"],"dims":[{"name":"state","values":["link","current"]},{"name":"separator","values":["slash"]}],"unknowns":["层级过多时的折叠规则","长文本层级的省略方式"],"anatomy":{"item":"层级项:字号 14px","separator":"分隔符 /","link":"非当前项:次要文字色","current":"当前页:正文色 #262626"},"doNot":["图标型分隔符与首页图标"],"usage":["字号14px,层级用/分隔,次要文字#6E6E6E,当前页#262626"],"structure":{"fontSize":"14px","separator":"/"}},"files":{"html":"../frameworks/Breadcrumb.html","css":"../frameworks/Breadcrumb.css","jsx":"../frameworks/Breadcrumb.jsx","vue2":"../frameworks/Breadcrumb.vue2.vue","vue3":"../frameworks/Breadcrumb.vue3.vue"},"sizes":{"html":1338,"css":545,"jsx":619,"vue2":559,"vue3":559},"sources":{"html":"\n\n\n \n \n Aurora Admin · Breadcrumb(普通 H5)\n \n \n \n\n\n
\n\n \n\n\n","css":"/* Aurora Admin Breadcrumb 样式 — 对齐 组件7.txt RouteBreadcrumb:面包屑,当前项高亮不可点击 */\n\n.aa-breadcrumb { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); font-size: 14px; display: flex; align-items: center; flex-wrap: wrap; }\n.aa-breadcrumb-item { cursor: pointer; color: #595959; }\n.aa-breadcrumb-item:hover { color: #2F54EB; }\n.aa-breadcrumb-item.is-current { color: #1F2329; font-weight: 500; cursor: default; }\n.aa-breadcrumb-sep { margin: 0 8px; color: #767676; }\n","jsx":"import React from 'react';\nimport './Breadcrumb.css';\n\nexport default function Breadcrumb({ items = [], onSelect }) {\n return (\n
\n {items.map((it, i) => (\n \n { if (i !== items.length - 1) onSelect && onSelect(it); }}\n >\n {it.label}\n \n {i !== items.length - 1 ? / : null}\n \n ))}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"tree","name":"树形控件 Tree","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/tree.json","category":"display","specLines":["- 用于层级数据展示:组织架构、菜单权限、分类目录","- 节点高度 32px,缩进 16px/级","- 展开/折叠箭头 12px,位于节点左侧","- 选中态:背景 #F0F5FF,文字 #2F54EB","- 支持复选框、拖拽排序、异步加载、搜索过滤","- 连接线默认隐藏,可选显示虚线 #E8ECF1"],"specSummary":"- 用于层级数据展示:组织架构、菜单权限、分类目录","contract":{"variants":["复选框树","可拖拽排序","异步加载子节点","搜索过滤 + 选中态"],"dims":[{"name":"feature","values":["checkbox","draggable","async-load","search-filter"]},{"name":"connector","values":["hidden","dashed"]},{"name":"state","values":["default","selected"]}],"unknowns":["异步加载的 loading 表现","搜索过滤的命中高亮样式"],"anatomy":{"node":"节点:高度 32px,缩进 16px/级","arrow":"展开/折叠箭头 12px,位于节点左侧","selected":"选中态:背景 #F0F5FF,文字 #2F54EB","connector":"连接线默认隐藏,可选显示虚线 #E8ECF1"},"doNot":["节点的右键菜单与快捷键"],"usage":["用于层级数据展示:组织架构、菜单权限、分类目录","节点高度 32px,缩进 16px/级","展开/折叠箭头 12px,位于节点左侧","选中态:背景 #F0F5FF,文字 #2F54EB","支持复选框、拖拽排序、异步加载、搜索过滤","连接线默认隐藏,可选显示虚线 #E8ECF1"],"structure":{"nodeHeight":"32px","indent":"16px/级","arrowSize":"12px","selectedBg":"#F0F5FF","selectedText":"#2F54EB","connectorDashed":"#E8ECF1"}},"files":{"html":"../frameworks/tree.html","css":"../frameworks/Tree.css","jsx":"../frameworks/Tree.jsx","vue2":"../frameworks/Tree.vue2.vue","vue3":"../frameworks/Tree.vue3.vue"},"sizes":{"html":5214,"css":958,"jsx":4278,"vue2":5030,"vue3":4751},"sources":{"html":"\n\n\n\n\nAurora Admin · Tree (H5)\n\n\n\n\n
\n

Aurora Admin · Tree 组件(纯 H5)

\n

对齐 Tree 规范:节点高32px、缩进16px/级、箭头12px、选中态 #F0F5FF + 品牌色、支持复选框级联

\n\n \n
\n
\n\n\n\n\n","css":"/* Aurora Admin Tree 样式 — 对齐 组件2.txt Tree 规范 */\n.aa-tree { min-width: 240px; border: 1px solid #E8ECF1; border-radius: 8px; padding: 4px; background: #fff; }\n.aa-tree-node {\n display: flex; align-items: center; gap: 4px; height: 32px; border-radius: 4px;\n cursor: pointer; user-select: none; font-size: 14px; color: #262626; padding-right: 8px;\n}\n.aa-tree-node:hover { background: #F5F7FA; }\n.aa-tree-node.is-selected { background: #F0F5FF; color: #2F54EB; }\n.aa-tree-arrow {\n display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px;\n font-size: 12px; color: #6E6E6E; transition: transform .15s; flex-shrink: 0;\n}\n.aa-tree-arrow.is-open { transform: rotate(90deg); }\n.aa-tree-arrow-placeholder { width: 16px; flex-shrink: 0; }\n.aa-tree-check { width: 14px; height: 14px; flex-shrink: 0; cursor: pointer; }\n.aa-tree-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n","jsx":"import React, { useState, useMemo, useRef } from 'react';\nimport './Tree.css';\n\n/**\n * Aurora Admin Tree(React)\n * 对齐 组件2.txt Tree 规范:节点高32px、缩进16px/级、箭头12px、选中 #F0F5FF/#2F54EB、复选框级联\n * nodes: [{ key, title, children, disabled }]\n * 受控:selectedKeys / checkedKeys(均为数组),通过 onSelect / onCheck 回调更新\n */\nfunction findNode(key, nodes) {\n for (const n of nodes) {\n if (n.key === key) return n;\n if (n.children) { const f = findNode(key, n.children); if (f) return f; }\n }\n return null;\n}\nfunction descendantKeys(node) {\n let out = [];\n (node.children || []).forEach((c) => { out.push(c.key); out = out.concat(descendantKeys(c)); });\n return out;\n}\nfunction allChildrenChecked(node, set) {\n if (!node.children || !node.children.length) return set.has(node.key);\n return node.children.every((c) => allChildrenChecked(c, set));\n}\n\nexport default function Tree({\n data = [],\n checkable = false,\n defaultExpandedKeys = [],\n selectedKeys = [],\n checkedKeys = [],\n onSelect,\n onCheck\n}) {\n const [expanded, setExpanded] = useState(() => new Set(defaultExpandedKeys));\n const [selected, setSelected] = useState(() => new Set(selectedKeys));\n const [checked, setChecked] = useState(() => new Set(checkedKeys));\n\n const visibleList = useMemo(() => {\n const out = [];\n const walk = (nodes, depth) => {\n nodes.forEach((n) => {\n out.push({ node: n, depth });\n if (n.children && n.children.length && expanded.has(n.key)) walk(n.children, depth + 1);\n });\n };\n walk(data, 0);\n return out;\n }, [data, expanded]);\n\n const hasChildren = (n) => !!(n.children && n.children.length);\n const isIndeterminate = (key) => {\n const node = findNode(key, data);\n if (!node || !hasChildren(node)) return false;\n const desc = descendantKeys(node);\n return desc.some((d) => checked.has(d)) && !allChildrenChecked(node, checked);\n };\n\n const toggleExpand = (key) =>\n setExpanded((prev) => { const n = new Set(prev); n.has(key) ? n.delete(key) : n.add(key); return n; });\n\n const onRowClick = (node) => {\n setSelected(new Set([node.key]));\n onSelect && onSelect(node.key);\n };\n\n const toggleCheck = (key) => {\n const node = findNode(key, data);\n const willCheck = !checked.has(key);\n const keys = [key, ...descendantKeys(node)];\n const next = new Set(checked);\n keys.forEach((k) => (willCheck ? next.add(k) : next.delete(k)));\n const propagate = (nodes) => {\n nodes.forEach((n) => {\n if (hasChildren(n)) {\n if (allChildrenChecked(n, next)) next.add(n.key); else next.delete(n.key);\n propagate(n.children);\n }\n });\n };\n propagate(data);\n setChecked(next);\n onCheck && onCheck([...next]);\n };\n\n return (\n
\n {visibleList.map(({ node, depth }) => {\n const open = expanded.has(node.key);\n const arrow = hasChildren(node) ? (\n { e.stopPropagation(); toggleExpand(node.key); }}>▸\n ) : ;\n const check = checkable ? (\n el && (el.indeterminate = isIndeterminate(node.key))}\n onClick={(e) => e.stopPropagation()}\n onChange={() => toggleCheck(node.key)}\n />\n ) : null;\n return (\n onRowClick(node)}\n >\n {arrow}\n {check}\n {node.title}\n
\n );\n })}\n \n );\n}\n\n/*\n用法示例:\nimport Tree from './Tree';\nconst data = [\n { key: 'org', title: '某某科技', children: [\n { key: 'tech', title: '技术中心', children: [{ key: 'fe', title: '前端组' }] }\n ]}\n];\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"dropdown","name":"下拉菜单 Dropdown","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/dropdown.json","category":"navigation","specLines":["- 触发器为按钮或文字,点击展开菜单","- 菜单项高度 32px,内边距左右 12px","- 悬停背景 #F5F7FA,危险项文字 #F5222D","- 支持分割线、禁用项、图标、子菜单","- 面板白底、圆角 6px、阴影中、最小宽度 120px"],"specSummary":"- 触发器为按钮或文字,点击展开菜单","contract":{"variants":["带图标菜单项","悬停态","危险项","禁用项","含分割线","子菜单"],"dims":[{"name":"state","values":["default","hover","disabled","danger"]},{"name":"feature","values":["divider","icon","submenu"]}],"unknowns":["子菜单的展开方向与延迟","面板最大高度与滚动"],"anatomy":{"trigger":"触发器:按钮或文字,点击展开菜单","item":"菜单项:高度 32px,内边距左右 12px","hover":"悬停背景 #F5F7FA","danger":"危险项文字 #CF1322","panel":"面板:白底、圆角 6px、阴影中、最小宽度 120px"},"doNot":["多选模式与选中标记"],"usage":["触发器为按钮或文字,点击展开菜单","菜单项高度 32px,内边距左右 12px","悬停背景 #F5F7FA,危险项文字 #CF1322","支持分割线、禁用项、图标、子菜单","面板白底、圆角 6px、阴影中、最小宽度 120px"],"structure":{"itemHeight":"32px","itemPadding":"左右 12px","hoverBg":"#F5F7FA","radius":"6px","minWidth":"120px","shadow":"中"}},"files":{"html":"../frameworks/dropdown.html","css":"../frameworks/Dropdown.css","jsx":"../frameworks/Dropdown.jsx","vue2":"../frameworks/Dropdown.vue2.vue","vue3":"../frameworks/Dropdown.vue3.vue"},"sizes":{"html":3329,"css":1102,"jsx":2068,"vue2":2516,"vue3":2465},"sources":{"html":"\n\n\n\n\nAurora Admin · Dropdown (H5)\n\n\n\n\n
\n

Aurora Admin · Dropdown 组件(纯 H5)

\n

对齐 Dropdown 规范:菜单项高32px、内边距左右12px、悬停 #F5F7FA、危险项 #CF1322、面板圆角6px/阴影中/最小宽120px

\n\n
\n \n \n
\n
\n\n\n\n\n","css":"/* Aurora Admin Dropdown 样式 — 对齐 组件2.txt Dropdown 规范 */\n.aa-dropdown { position: relative; display: inline-block; }\n.aa-dropdown-trigger {\n border: 1px solid #E8ECF1; background: #fff; border-radius: 4px; height: 32px; padding: 0 12px;\n cursor: pointer; font-size: 14px; color: #262626; display: inline-flex; align-items: center; gap: 4px;\n}\n.aa-dropdown-trigger:hover { border-color: #2F54EB; }\n.aa-dropdown-panel {\n position: absolute; z-index: 20; top: calc(100% + 4px); left: 0; min-width: 120px;\n background: #fff; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); padding: 4px 0;\n}\n.aa-dropdown-item {\n display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 12px; cursor: pointer;\n font-size: 14px; color: #262626; white-space: nowrap;\n}\n.aa-dropdown-item:hover { background: #F5F7FA; }\n.aa-dropdown-item.is-danger { color: #CF1322; }\n.aa-dropdown-item.is-disabled { color: #BFBFBF; cursor: not-allowed; }\n.aa-dropdown-item.is-disabled:hover { background: transparent; }\n.aa-dropdown-divider { height: 1px; background: #F0F0F0; margin: 4px 0; }\n","jsx":"import React, { useState, useRef, useEffect } from 'react';\nimport './Dropdown.css';\n\n/**\n * Aurora Admin Dropdown(React)\n * 对齐 组件2.txt Dropdown 规范:触发器点击展开、菜单项高32px、内边距左右12px、悬停 #F5F7FA、危险项 #CF1322、面板圆角6px/阴影中/最小宽120px\n * items: [{ key, label, danger?, disabled?, divider?, icon? }]\n */\nexport default function Dropdown({ items = [], trigger = '操作', onSelect }) {\n const [open, setOpen] = useState(false);\n const rootRef = useRef(null);\n\n useEffect(() => {\n function onDocClick(e) {\n if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);\n }\n document.addEventListener('click', onDocClick);\n return () => document.removeEventListener('click', onDocClick);\n }, []);\n\n const handleClick = (item) => {\n if (item.disabled || item.divider) return;\n onSelect && onSelect(item.key != null ? item.key : item.label);\n setOpen(false);\n };\n\n return (\n
\n
setOpen((o) => !o)}>\n {trigger}\n
\n {open && (\n
\n {items.map((item, i) =>\n item.divider ? (\n
\n ) : (\n handleClick(item)}\n >\n {item.icon && {item.icon}}\n {item.label}\n
\n )\n )}\n
\n )}\n
\n );\n}\n\n/*\n用法示例:\nimport Dropdown from './Dropdown';\nconst items = [\n { key: 'edit', label: '编辑' },\n { key: 'copy', label: '复制' },\n { divider: true },\n { key: 'delete', label: '删除', danger: true }\n];\n console.log(k)} />\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"popconfirm","name":"气泡确认框 Popconfirm","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/popconfirm.json","category":"feedback","specLines":["- 用于危险操作二次确认(删除、下线)","- 气泡内包含提示文字、确认按钮(危险红)、取消按钮(文字)","- 触发方式:点击,位置可配置(上/下/左/右)","- 气泡背景白色,圆角 6px,阴影中,宽度约 240px"],"specSummary":"- 用于危险操作二次确认(删除、下线)","contract":{"variants":["上方气泡","下方气泡","确认中"],"dims":[{"name":"placement","values":["top","bottom","left","right"]},{"name":"state","values":["default","loading"]}],"unknowns":["自动关闭延时","确认按钮的加载态表现"],"anatomy":{"panel":"气泡:白底、圆角 6px、阴影中、宽度约 240px","text":"提示文字","confirm":"确认按钮(危险红)","cancel":"取消按钮(文字)"},"doNot":["箭头尺寸与偏移计算"],"usage":["用于危险操作二次确认(删除、下线)","气泡内包含提示文字、确认按钮(危险红)、取消按钮(文字)","触发方式:点击,位置可配置(上/下/左/右)","气泡背景白色,圆角 6px,阴影中,宽度约 240px"],"structure":{"radius":"6px","width":"240px","shadow":"中","trigger":"点击"}},"files":{"html":"../frameworks/popconfirm.html","css":"../frameworks/Popconfirm.css","jsx":"../frameworks/Popconfirm.jsx","vue2":"../frameworks/Popconfirm.vue2.vue","vue3":"../frameworks/Popconfirm.vue3.vue"},"sizes":{"html":3645,"css":1554,"jsx":2878,"vue2":3319,"vue3":4012},"sources":{"html":"\n\n\n\n\nAurora Admin · Popconfirm (H5)\n\n\n\n\n\n
\n

Aurora Admin · Popconfirm 组件(纯 H5)

\n

对齐 Popconfirm 规范:气泡确认、图标+描述+确认/取消,点击外部关闭

\n\n
\n

删除确认(底部弹出)

\n \n \n \n \n
结果:—
\n
\n\n
\n

顶部弹出

\n \n \n \n \n
\n
\n\n\n\n\n","css":"/* Aurora Admin Popconfirm 样式 — 对齐 组件2.txt Popconfirm:气泡确认、图标+描述+确认/取消 */\n\n.aa-popconfirm { position: relative; display: inline-block; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-popconfirm-pop {\n position: absolute; z-index: 100; min-width: 220px; background: #fff; border: 1px solid #E8ECF1;\n border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); padding: 12px 14px;\n}\n.aa-popconfirm-pop.placement-bottom { top: calc(100% + 8px); left: 0; }\n.aa-popconfirm-pop.placement-top { bottom: calc(100% + 8px); left: 0; }\n\n.aa-popconfirm-inner { display: flex; gap: 8px; }\n.aa-popconfirm-icon { flex-shrink: 0; width: 16px; height: 16px; border-radius: 50%; background: #8C5A00; color: #fff;\n display: inline-flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; margin-top: 2px; }\n.aa-popconfirm-title { font-size: 14px; color: #262626; line-height: 1.5; }\n\n.aa-popconfirm-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }\n.aa-popconfirm-btn { height: 28px; padding: 0 12px; border-radius: 4px; font-size: 13px; cursor: pointer;\n border: 1px solid #E8ECF1; background: #fff; color: #262626; transition: border-color .15s, color .15s; }\n.aa-popconfirm-btn:hover { color: #2F54EB; border-color: #2F54EB; }\n.aa-popconfirm-btn.primary { background: #2F54EB; border-color: #2F54EB; color: #fff; }\n.aa-popconfirm-btn.primary:hover { background: #1D39C4; border-color: #1D39C4; color: #fff; }\n","jsx":"import React, { useRef, useState, useEffect } from 'react';\nimport './Popconfirm.css';\n\n/**\n * Aurora Admin Popconfirm(React)\n * 对齐 组件2.txt Popconfirm 规范:气泡确认、图标+描述+确认/取消,点击外部关闭\n *\n * props:\n * - title : 确认描述文本\n * - okText / cancelText\n * - placement : 'bottom' | 'top'\n * - visible / defaultVisible : 受控打开\n * - onConfirm() / onCancel()\n * - children : 触发元素(如按钮)\n */\nexport default function Popconfirm({\n title,\n okText = '确定',\n cancelText = '取消',\n placement = 'bottom',\n visible,\n defaultVisible = false,\n onConfirm,\n onCancel,\n children\n}) {\n const rootRef = useRef(null);\n const [innerOpen, setInnerOpen] = useState(defaultVisible);\n const open = visible !== undefined ? visible : innerOpen;\n const [style, setStyle] = useState({});\n\n const syncPos = () => {\n if (!rootRef.current) return;\n const r = rootRef.current.getBoundingClientRect();\n setStyle(placement === 'top'\n ? { left: r.left, bottom: window.innerHeight - r.top }\n : { left: r.left, top: r.bottom + 8 });\n };\n const setOpen = (v) => {\n if (visible === undefined) setInnerOpen(v);\n if (v) { syncPos(); document.addEventListener('click', onDocClick, true); window.addEventListener('resize', syncPos); }\n else { document.removeEventListener('click', onDocClick, true); window.removeEventListener('resize', syncPos); }\n };\n const onDocClick = (e) => {\n if (rootRef.current && rootRef.current.contains(e.target)) return;\n setOpen(false);\n };\n\n useEffect(() => {\n if (open) { syncPos(); document.addEventListener('click', onDocClick, true); window.addEventListener('resize', syncPos); }\n return () => { document.removeEventListener('click', onDocClick, true); window.removeEventListener('resize', syncPos); };\n }, [open]);\n\n const confirm = () => { onConfirm && onConfirm(); setOpen(false); };\n const cancel = () => { onCancel && onCancel(); setOpen(false); };\n\n return (\n \n setOpen(!open)}>{children}\n {open && (\n
\n
\n !\n {title}\n
\n
\n \n \n
\n
\n )}\n
\n );\n}\n\n/*\n用法示例:\nimport Popconfirm from './Popconfirm';\n\n \n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"segmented","name":"分段控制器 Segmented","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/segmented.json","category":"input","specLines":["- 用于视图切换、筛选条件切换","- 默认高度 32px,选项内边距 8px 16px","- 选中项背景白色(在浅灰容器内)或品牌色(背景深色时)","- 圆角 4px,容器背景 #F5F7FA","- 支持图标+文字,禁用态文字 #BFBFBF"],"specSummary":"- 用于视图切换、筛选条件切换","contract":{"variants":["视图切换分段","图标+文字分段","禁用态分段"],"dims":[{"name":"optionContent","values":["text","icon-text"]},{"name":"theme","values":["light(选中白底)","dark(选中品牌色)"]},{"name":"state","values":["default","disabled"]}],"unknowns":["深色背景容器的具体判定场景"],"anatomy":{"container":"容器高 32px,圆角 4px,背景 #F5F7FA","option":"选项内边距 8px 16px","selected":"选中项背景白色(浅灰容器内)或品牌色(背景深色时)"},"doNot":["超长选项的滚动截断规则"],"usage":["用于视图切换、筛选条件切换","选中项背景白色(浅灰容器内)或品牌色(深色背景)","支持图标+文字,禁用态文字 #BFBFBF"],"structure":{"height":"32px","optionPadding":"8px 16px","radius":"4px"}},"files":{"html":"../frameworks/segmented.html","css":"../frameworks/Segmented.css","jsx":"../frameworks/Segmented.jsx","vue2":"../frameworks/Segmented.vue2.vue","vue3":"../frameworks/Segmented.vue3.vue"},"sizes":{"html":2241,"css":726,"jsx":1387,"vue2":1481,"vue3":1505},"sources":{"html":"\n\n\n\n\nAurora Admin · Segmented (H5)\n\n\n\n\n
\n

Aurora Admin · Segmented 组件(纯 H5)

\n

对齐 Segmented 规范:高32px、选项内边距8px 16px、容器 #F5F7FA、圆角4px、选中白底、禁用 #BFBFBF

\n\n
\n \n \n \n \n
\n
\n\n\n\n\n","css":"/* Aurora Admin Segmented 样式 — 对齐 组件2.txt Segmented 规范 */\n.aa-segmented {\n display: inline-flex; align-items: center; background: #F5F7FA; border-radius: 4px; padding: 2px;\n}\n.aa-segmented-item {\n height: 28px; padding: 0 16px; display: inline-flex; align-items: center; gap: 6px;\n border-radius: 4px; cursor: pointer; font-size: 14px; color: #262626; border: none; background: none;\n transition: background .15s, color .15s;\n}\n.aa-segmented-item:hover:not(.is-active):not(.is-disabled) { color: #2F54EB; }\n.aa-segmented-item.is-active { background: #fff; color: #262626; box-shadow: 0 1px 2px rgba(0,0,0,0.08); font-weight: 500; }\n.aa-segmented-item.is-disabled { color: #BFBFBF; cursor: not-allowed; }\n","jsx":"import React from 'react';\nimport './Segmented.css';\n\n/**\n * Aurora Admin Segmented(React)\n * 对齐 组件2.txt Segmented 规范:高32px、选项内边距8px 16px、容器 #F5F7FA、圆角4px、选中白底、禁用 #BFBFBF\n * options: [{ label, value, icon?, disabled? }]\n * 受控:value + onChange;非受控:defaultValue\n */\nexport default function Segmented({ value, defaultValue, options = [], onChange }) {\n const current = value !== undefined ? value : defaultValue;\n\n const choose = (opt) => {\n if (opt.disabled) return;\n if (value === undefined) onChange && onChange(opt.value);\n else onChange && onChange(opt.value);\n };\n\n return (\n
\n {options.map((opt) => (\n choose(opt)}\n >\n {opt.icon && {opt.icon}}\n {opt.label}\n \n ))}\n
\n );\n}\n\n/*\n用法示例:\nimport Segmented from './Segmented';\nconst opts = [\n { label: '列表', value: 'list' },\n { label: '看板', value: 'kanban' },\n { label: '日历', value: 'cal', disabled: true }\n];\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"transfer","name":"穿梭框 Transfer","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/transfer.json","category":"input","specLines":["- 用于批量选择,左右两栏列表","- 列表项高度 32px,带复选框","- 中间操作按钮:右移、左移、全部右移、全部左移","- 顶部搜索框,底部已选数量统计","- 面板宽度 200–240px,高度 256px,滚动"],"specSummary":"- 用于批量选择,左右两栏列表","contract":{"variants":["带搜索穿梭框","基础穿梭框(支持拖拽)"],"dims":[{"name":"search","values":["with-search","without-search"]},{"name":"operation","values":["move","move-all"]},{"name":"drag","values":["support","none"]}],"unknowns":["拖拽跨栏的具体交互细节"],"anatomy":{"source-panel":"左栏候选列表,顶部搜索框","target-panel":"右栏已选列表,底部已选数量统计","operation-buttons":"中间操作按钮:右移、左移、全部右移、全部左移","item":"列表项高 32px,带复选框"},"doNot":["树形穿梭框","面板内分页"],"usage":["用于批量选择,左右两栏列表","列表项带复选框,项高 32px","顶部搜索框,底部已选数量统计","适用于权限分配、成员选择"],"structure":{"panel":"@{width=200-240px; height=256px; scroll=True}"}},"files":{"html":"../frameworks/transfer.html","css":"../frameworks/Transfer.css","jsx":"../frameworks/Transfer.jsx","vue2":"../frameworks/Transfer.vue2.vue","vue3":"../frameworks/Transfer.vue3.vue"},"sizes":{"html":4268,"css":2197,"jsx":3560,"vue2":5407,"vue3":5536},"sources":{"html":"\n\n\n\n\nAurora Admin · Transfer (H5)\n\n\n\n\n\n
\n

Aurora Admin · Transfer 组件(纯 H5)

\n

对齐 Transfer 规范:左右两栏、搜索、复选框、已选数量

\n\n
\n
\n
\n
\n\n\n\n\n","css":"/* Aurora Admin Transfer 样式 — 对齐 组件7.txt Transfer:左右两栏、搜索、复选框、已选数量 */\n\n.aa-transfer { display: inline-flex; align-items: stretch; gap: 12px; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-transfer-panel { width: 220px; border: 1px solid #E8ECF1; border-radius: 6px; background: #fff; display: flex; flex-direction: column; }\n.aa-transfer-head { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; border-bottom: 1px solid #E8ECF1; font-size: 14px; }\n.aa-transfer-head .count { color: #6E6E6E; font-size: 12px; }\n.aa-transfer-search { padding: 8px 12px; border-bottom: 1px solid #E8ECF1; }\n.aa-transfer-search input { width: 100%; box-sizing: border-box; height: 30px; padding: 0 8px; border: 1px solid #E8ECF1; border-radius: 4px; font-size: 13px; outline: none; color: #262626; }\n.aa-transfer-search input:focus { border-color: #2F54EB; box-shadow: 0 0 0 2px rgba(47,84,235,0.2); }\n.aa-transfer-body { flex: 1; min-height: 180px; max-height: 240px; overflow: auto; padding: 4px 0; }\n.aa-transfer-item { display: flex; align-items: center; gap: 8px; padding: 6px 12px; cursor: pointer; font-size: 14px; }\n.aa-transfer-item:hover { background: #F5F7FA; }\n.aa-transfer-item.is-disabled { color: #BFBFBF; cursor: not-allowed; }\n.aa-transfer-item .box { width: 14px; height: 14px; border: 1px solid #E8ECF1; border-radius: 3px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; color: #fff; font-size: 10px; }\n.aa-transfer-item.is-checked .box { background: #2F54EB; border-color: #2F54EB; }\n.aa-transfer-empty { padding: 32px 12px; text-align: center; color: #767676; font-size: 13px; }\n.aa-transfer-ops { display: flex; flex-direction: column; justify-content: center; gap: 8px; }\n.aa-transfer-ops button { width: 32px; height: 32px; border-radius: 4px; border: 1px solid #E8ECF1; background: #fff; color: #262626; cursor: pointer; font-size: 14px; }\n.aa-transfer-ops button:hover:not(:disabled) { color: #2F54EB; border-color: #2F54EB; }\n.aa-transfer-ops button:disabled { color: #BFBFBF; cursor: not-allowed; }\n","jsx":"import React, { useState, useMemo } from 'react';\nimport './Transfer.css';\n\n/**\n * Aurora Admin Transfer(React)\n * 对齐 组件7.txt Transfer 规范:左右两栏、搜索、复选框、已选数量\n *\n * props:\n * - value / defaultValue : 已选 key 数组\n * - data : [{ key, label, disabled? }]\n * - titles : [左标题, 右标题]\n * - filterable\n * - onChange(keys)\n */\nexport default function Transfer({\n value,\n defaultValue = [],\n data = [],\n titles = ['候选列表', '已选列表'],\n filterable = false,\n onChange\n}) {\n const [selected, setSelected] = useState(value !== undefined ? value : defaultValue);\n const current = value !== undefined ? value : selected;\n const [leftChecked, setLeftChecked] = useState([]);\n const [rightChecked, setRightChecked] = useState([]);\n const [leftQ, setLeftQ] = useState('');\n const [rightQ, setRightQ] = useState('');\n\n const filteredLeft = useMemo(\n () => data.filter(d => !current.includes(d.key) && d.label.includes(leftQ)),\n [data, current, leftQ]\n );\n const filteredRight = useMemo(\n () => data.filter(d => current.includes(d.key) && d.label.includes(rightQ)),\n [data, current, rightQ]\n );\n\n const setVal = (next) => {\n if (value === undefined) setSelected(next);\n onChange && onChange(next);\n };\n const toggle = (which, key) => {\n const item = data.find(d => d.key === key);\n if (item && item.disabled) return;\n const arr = which === 'left' ? leftChecked : rightChecked;\n const set = which === 'left' ? setLeftChecked : setRightChecked;\n set(arr.includes(key) ? arr.filter(k => k !== key) : [...arr, key]);\n };\n const moveRight = () => {\n setVal([...current, ...leftChecked]);\n setLeftChecked([]);\n };\n const moveLeft = () => {\n const set = new Set(rightChecked);\n setVal(current.filter(k => !set.has(k)));\n setRightChecked([]);\n };\n\n const Panel = ({ title, items, checked, q, setQ, side }) => (\n
\n
\n {title}\n {checked.length}/{items.length}\n
\n {filterable && (\n
\n setQ(e.target.value)} placeholder=\"搜索\" />\n
\n )}\n
\n {items.map(d => (\n toggle(side, d.key)}\n >\n {checked.includes(d.key) ? '✓' : ''}{d.label}\n
\n ))}\n {!items.length &&
无数据
}\n
\n \n );\n\n return (\n
\n \n
\n \n \n
\n \n
\n );\n}\n\n/*\n用法示例:\nimport Transfer from './Transfer';\nconst data = [{ key: '1', label: '研发部' }, { key: '2', label: '产品部' }];\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"rate","name":"评分 Rate","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/rate.json","category":"input","specLines":["- 用于评价、满意度打分","- 默认 5 星,可配置 1–10 星","- 选中星颜色 #FAAD14,未选中 #E8ECF1","- 尺寸 16/20/24px,支持半星、只读、字符自定义"],"specSummary":"- 用于评价、满意度打分","contract":{"variants":["默认五星评分","只读评分","半星评分"],"dims":[{"name":"count","values":["default-5","configurable-1-10"]},{"name":"size","values":["16px","20px","24px"]},{"name":"mode","values":["editable","readonly"]},{"name":"half","values":["support","none"]}],"unknowns":["字符自定义的具体形态"],"anatomy":{"star-group":"星组,横向排列","star-on":"选中星,颜色 #FAAD14","star-off":"未选中星,颜色 #E8ECF1"},"doNot":["评分文字描述映射(如 很差/很好)"],"usage":["用于评价、满意度打分","默认 5 星,可配置 1-10 星","选中星 #FAAD14,未选中 #E8ECF1","支持半星、只读、字符自定义"],"structure":{"size":"16px 20px 24px"}},"files":{"html":"../frameworks/rate.html","css":"../frameworks/Rate.css","jsx":"../frameworks/Rate.jsx","vue2":"../frameworks/Rate.vue2.vue","vue3":"../frameworks/Rate.vue3.vue"},"sizes":{"html":3486,"css":1082,"jsx":2026,"vue2":2695,"vue3":2706},"sources":{"html":"\n\n\n\n\nAurora Admin · Rate (H5)\n\n\n\n\n\n
\n

Aurora Admin · Rate 组件(纯 H5)

\n

对齐 RateInput 规范:星级、半星、只读、尺寸、文字

\n\n
\n

基础(点击评分)

\n
\n
\n\n
\n

允许半星

\n
\n
\n\n
\n

只读 + 显示文字

\n
\n
\n\n
\n

尺寸:small / default / large

\n
\n
\n
\n
\n
\n\n\n\n\n","css":"/* Aurora Admin Rate 样式 — 对齐 组件7.txt RateInput:星级、半星、只读、尺寸、文字 */\n\n.aa-rate { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-rate-star { position: relative; display: inline-block; font-size: 20px; line-height: 1; color: #E8ECF1; cursor: pointer; transition: color .15s, transform .1s; }\n.aa-rate-star:hover { transform: scale(1.1); }\n.aa-rate-star .aa-rate-fill { position: absolute; left: 0; top: 0; overflow: hidden; color: #2F54EB; white-space: nowrap; pointer-events: none; }\n.aa-rate.is-readonly .aa-rate-star,\n.aa-rate.is-disabled .aa-rate-star { cursor: default; }\n.aa-rate.is-readonly .aa-rate-star:hover,\n.aa-rate.is-disabled .aa-rate-star:hover { transform: none; }\n.aa-rate-text { margin-left: 8px; font-size: 14px; color: #6E6E6E; }\n.aa-rate.sm .aa-rate-star { font-size: 14px; }\n.aa-rate.lg .aa-rate-star { font-size: 28px; }\n.aa-rate.sm .aa-rate-text { font-size: 12px; }\n.aa-rate.lg .aa-rate-text { font-size: 16px; }\n","jsx":"import React, { useState } from 'react';\nimport './Rate.css';\n\n/**\n * Aurora Admin Rate(React)\n * 对齐 组件7.txt RateInput 规范:星级、半星、只读、尺寸、文字\n *\n * props:\n * - value / defaultValue : 评分数值\n * - count : 星数(默认 5)\n * - allowHalf: 允许半星\n * - readonly / disabled\n * - size : 'default' | 'sm' | 'lg'\n * - showText : 是否显示评分文字\n * - texts : 文字数组\n * - onChange(value)\n */\nconst DEFAULT_TEXTS = ['极差', '失望', '一般', '满意', '惊喜'];\n\nexport default function Rate({\n value,\n defaultValue = 0,\n count = 5,\n allowHalf = false,\n readonly = false,\n disabled = false,\n size = 'default',\n showText = false,\n texts = DEFAULT_TEXTS,\n onChange\n}) {\n const [inner, setInner] = useState(defaultValue);\n const current = value !== undefined ? value : inner;\n\n const stars = Array.from({ length: count }, (_, i) => Math.max(0, Math.min(1, current - i)));\n const textLabel = current ? texts[Math.ceil(current) - 1] : '未评分';\n\n const choose = (evt, i) => {\n if (readonly || disabled) return;\n let next = i + 1;\n if (allowHalf) {\n const rect = evt.currentTarget.getBoundingClientRect();\n const half = (evt.clientX - rect.left) < rect.width / 2;\n next = half ? i + 0.5 : i + 1;\n }\n if (next === current) next = 0;\n if (value === undefined) setInner(next);\n onChange && onChange(next);\n };\n\n return (\n
\n {stars.map((s, i) => (\n choose(e, i)}>\n ★★\n \n ))}\n {showText && {textLabel}}\n
\n );\n}\n\n/*\n用法示例:\nimport Rate from './Rate';\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"slider","name":"滑块 Slider","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/slider.json","category":"input","specLines":["- 用于范围筛选、参数调节","- 轨道高度 4px,圆角 2px","- 未选中段 #E8ECF1,选中段 #2F54EB","- 滑块圆点 14px,白色,阴影中,悬停放大","- 支持刻度标记、输入框联动、双滑块范围"],"specSummary":"- 用于范围筛选、参数调节","contract":{"variants":["单滑块","双滑块范围","带刻度滑块"],"dims":[{"name":"mode","values":["single","range"]},{"name":"marks","values":["with-marks","none"]},{"name":"inputLink","values":["support","none"]}],"unknowns":["刻度标签的密度规则"],"anatomy":{"track":"轨道高 4px,圆角 2px","filled":"选中段 #2F54EB","rest":"未选中段 #E8ECF1","thumb":"滑块圆点 14px,白色,阴影中,悬停放大"},"doNot":["垂直滑块"],"usage":["用于范围筛选、参数调节","未选中段 #E8ECF1,选中段 #2F54EB","支持刻度标记、输入框联动、双滑块范围"],"structure":{"trackHeight":"4px","thumbSize":"14px"}},"files":{"html":"../frameworks/slider.html","css":"../frameworks/Slider.css","jsx":"../frameworks/Slider.jsx","vue2":"../frameworks/Slider.vue2.vue","vue3":"../frameworks/Slider.vue3.vue"},"sizes":{"html":6766,"css":1794,"jsx":4366,"vue2":5757,"vue3":5649},"sources":{"html":"\n\n\n\n\nAurora Admin · Slider (H5)\n\n\n\n\n\n
\n

Aurora Admin · Slider 组件(纯 H5)

\n

对齐 SliderInput 规范:单/双滑块、刻度、输入框联动

\n\n
\n

单滑块 + 数值联动

\n
\n 音量\n
\n
\n
\n\n
\n

带刻度

\n
\n
\n\n
\n

双滑块范围

\n
\n
\n\n
\n

禁用

\n
\n
\n
\n\n\n\n\n","css":"/* Aurora Admin Slider 样式 — 对齐 组件7.txt SliderInput:单/双滑块、刻度、输入框联动 */\n\n.aa-slider { position: relative; display: flex; align-items: center; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-slider.is-disabled { opacity: .5; pointer-events: none; }\n.aa-slider-track { position: relative; flex: 1; height: 40px; }\n.aa-slider-rail { position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%); height: 4px; background: #E8ECF1; border-radius: 2px; }\n.aa-slider-fill { position: absolute; top: 50%; transform: translateY(-50%); height: 4px; background: #2F54EB; border-radius: 2px; }\n.aa-slider-handle {\n position: absolute; top: 50%; width: 14px; height: 14px; border-radius: 50%; background: #fff;\n border: 2px solid #2F54EB; transform: translate(-50%, -50%); cursor: pointer; box-shadow: 0 1px 3px rgba(0,0,0,0.15);\n transition: box-shadow .15s;\n}\n.aa-slider-handle:hover, .aa-slider-handle:focus { box-shadow: 0 0 0 4px rgba(47,84,235,0.2); outline: none; }\n.aa-slider-ticks { position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%); height: 0; pointer-events: none; }\n.aa-slider-tick { position: absolute; top: 0; width: 2px; height: 8px; background: #E8ECF1; transform: translate(-50%, -50%); }\n.aa-slider-value { margin-left: 12px; font-size: 14px; color: #262626; display: inline-flex; align-items: center; gap: 6px; }\n.aa-slider-value input { width: 56px; height: 28px; text-align: center; border: 1px solid #E8ECF1; border-radius: 4px; font-size: 13px; outline: none; color: #262626; }\n.aa-slider-value input:focus { border-color: #2F54EB; box-shadow: 0 0 0 2px rgba(47,84,235,0.2); }\n.aa-slider-label { margin-top: 4px; font-size: 12px; color: #6E6E6E; }\n","jsx":"import React, { useRef, useState, useEffect } from 'react';\nimport './Slider.css';\n\n/**\n * Aurora Admin Slider(React)\n * 对齐 组件7.txt SliderInput 规范:单/双滑块、刻度、输入框联动\n *\n * props:\n * - value / defaultValue : number | [lo, hi]\n * - min / max / step\n * - range : 双滑块\n * - showTicks : 刻度\n * - showInput : 数值输入框联动\n * - disabled\n * - onChange(value)\n */\nexport default function Slider({\n value,\n defaultValue = 0,\n min = 0,\n max = 100,\n step = 1,\n range = false,\n showTicks = false,\n showInput = false,\n disabled = false,\n onChange\n}) {\n const trackRef = useRef(null);\n const [dragIndex, setDragIndex] = useState(-1);\n const current = value !== undefined ? value : defaultValue;\n\n const pct = (v) => ((v - min) / (max - min)) * 100;\n const clamp = (v) => {\n const snapped = Math.round((v - min) / step) * step + min;\n return Math.max(min, Math.min(max, snapped));\n };\n const valueFromClientX = (x) => {\n const rect = trackRef.current.getBoundingClientRect();\n return clamp(min + (x - rect.left) / rect.width * (max - min));\n };\n\n const setVal = (next) => {\n if (value === undefined) onChange && onChange(next);\n else onChange && onChange(next);\n };\n\n const onMove = (e) => {\n if (dragIndex < 0) return;\n const nv = valueFromClientX(e.clientX);\n if (range) {\n const arr = [...current]; arr[dragIndex] = nv; arr.sort((a, b) => a - b);\n setVal(arr);\n } else {\n setVal(nv);\n }\n };\n const onUp = () => {\n setDragIndex(-1);\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', onUp);\n };\n const onHandleDown = (i, e) => {\n if (disabled) return;\n e.preventDefault();\n setDragIndex(i);\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', onUp);\n };\n const onTrackDown = (e) => {\n if (disabled || e.target.classList.contains('aa-slider-handle')) return;\n const nv = valueFromClientX(e.clientX);\n if (range) {\n const i = Math.abs(nv - current[0]) <= Math.abs(nv - current[1]) ? 0 : 1;\n const arr = [...current]; arr[i] = nv; arr.sort((a, b) => a - b);\n setVal(arr);\n } else {\n setVal(nv);\n }\n };\n const onInput = (i, e) => {\n const v = +e.target.value;\n if (range) {\n const arr = [...current]; arr[i] = v; arr.sort((a, b) => a - b);\n setVal(arr);\n } else {\n setVal(v);\n }\n };\n\n useEffect(() => () => onUp(), []); // eslint-disable-line\n\n const handlePercents = range ? [pct(current[0]), pct(current[1])] : [pct(current)];\n const fillStyle = range\n ? { left: pct(current[0]) + '%', width: (pct(current[1]) - pct(current[0])) + '%' }\n : { left: '0%', width: pct(current) + '%' };\n const tickPercents = Array.from(\n { length: Math.floor((max - min) / step) + 1 },\n (_, i) => pct(min + i * step)\n );\n\n return (\n
\n
\n
\n
\n {showTicks && (\n
\n {tickPercents.map((t, i) => (\n
\n ))}\n
\n )}\n {handlePercents.map((h, i) => (\n onHandleDown(i, e)}\n >
\n ))}\n
\n {showInput && (\n
\n {range ? (\n <>\n onInput(0, e)} />\n —\n onInput(1, e)} />\n \n ) : (\n onInput(null, e)} />\n )}\n
\n )}\n \n );\n}\n\n/*\n用法示例:\nimport Slider from './Slider';\n\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"mention","name":"提及 Mention","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/mention.json","category":"input","specLines":["- 用于评论、任务分配中 @用户","- 输入框内输入 @ 触发面板","- 面板列出用户列表,含头像+姓名+部门","- 选中后以标签形式插入输入框,可删除","- 面板宽度 240px,阴影中,圆角 6px"],"specSummary":"- 用于评论、任务分配中 @用户","contract":{"variants":["评论 @ 用户","任务分配 @ 成员"],"dims":[{"name":"trigger","values":["at-char"]},{"name":"panelItem","values":["avatar-name-dept"]},{"name":"insert","values":["removable-tag"]}],"unknowns":["插入标签的具体样式(沿用标签组件)","面板最大高度与滚动规则"],"anatomy":{"input":"输入框,输入 @ 触发面板","panel":"用户面板,宽 240px,阴影中,圆角 6px","item":"用户项:头像 + 姓名 + 部门","tag":"选中后以标签形式插入输入框,可删除"},"doNot":["面板搜索过滤规则"],"usage":["用于评论、任务分配中 @ 用户","输入框内输入 @ 触发面板","面板列出用户列表,含头像+姓名+部门","选中后以标签形式插入输入框,可删除"],"structure":{"panelWidth":"240px"}},"files":{"html":"../frameworks/Mention.html","css":"../frameworks/Mention.css","jsx":"../frameworks/Mention.jsx","vue2":"../frameworks/Mention.vue2.vue","vue3":"../frameworks/Mention.vue3.vue"},"sizes":{"html":4091,"css":1584,"jsx":3196,"vue2":2945,"vue3":3029},"sources":{"html":"\n\n\n \n \n Aurora Admin · Mention\n \n \n\n\n
\n

Mention @ 提及

\n

输入框输入 @ 触发用户面板,选择后以标签形式插入,可删除。

\n\n
\n
\n
\n \n
\n
\n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin Mention 样式 — @ 提及 */\n.aa-mention { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); position: relative; width: 360px; }\n.aa-mention-area { position: relative; }\n.aa-mention-input { width: 100%; box-sizing: border-box; border: 1px solid #E8ECF1; border-radius: 6px; padding: 8px 10px; font-size: 14px; color: #262626; outline: none; resize: vertical; transition: border-color .15s, box-shadow .15s; }\n.aa-mention-input:focus { border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-mention-panel { position: absolute; z-index: 30; background: #fff; border: 1px solid #E8ECF1; border-radius: 6px; box-shadow: 0 6px 24px rgba(0,0,0,0.12); width: 240px; max-height: 220px; overflow: auto; }\n.aa-mention-item { display: flex; align-items: center; gap: 8px; padding: 7px 10px; cursor: pointer; }\n.aa-mention-item:hover, .aa-mention-item.is-active { background: #F0F5FF; }\n.aa-mention-avatar { width: 24px; height: 24px; border-radius: 50%; background: #2F54EB; color: #fff; font-size: 12px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }\n.aa-mention-name { font-size: 13px; color: #262626; }\n.aa-mention-dept { font-size: 12px; color: #6E6E6E; margin-left: auto; }\n.aa-mention-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }\n.aa-mention-chip { display: inline-flex; align-items: center; gap: 4px; background: #F0F5FF; color: #2F54EB; border-radius: 4px; padding: 2px 8px; font-size: 12px; }\n.aa-mention-chip b { cursor: pointer; font-weight: 600; }\n","jsx":"import React, { useState, useRef, useEffect } from 'react';\nimport './Mention.css';\n\nconst USERS = [\n { name: '王伟', dept: '华东大区' }, { name: '李娜', dept: '华南大区' }, { name: '张强', dept: '华北大区' },\n { name: '陈静', dept: '研发中心' }, { name: '刘洋', dept: '市场部' }\n];\n\nexport default function Mention({ modelValue = '', users = USERS, onChange }) {\n const [open, setOpen] = useState(false);\n const [active, setActive] = useState(-1);\n const [mentioned, setMentioned] = useState([]);\n const inputRef = useRef(null);\n const root = useRef(null);\n\n useEffect(() => {\n const onDoc = (e) => { if (root.current && !root.current.contains(e.target)) setOpen(false); };\n document.addEventListener('click', onDoc);\n return () => document.removeEventListener('click', onDoc);\n }, []);\n\n const onInput = (e) => {\n const pos = e.target.selectionStart, v = e.target.value;\n if (onChange) onChange(v);\n const at = v.lastIndexOf('@', pos - 1);\n if (at >= 0 && (at === 0 || /\\s/.test(v[at - 1])) && !/\\s/.test(v.slice(at + 1, pos))) { setActive(-1); setOpen(true); }\n else setOpen(false);\n };\n const move = (d) => setActive((a) => Math.max(-1, Math.min(users.length - 1, a + d)));\n const insert = (name) => {\n const el = inputRef.current, pos = el.selectionStart, v = el.value;\n const at = v.lastIndexOf('@', pos - 1);\n if (at >= 0 && (at === 0 || /\\s/.test(v[at - 1]))) el.value = v.slice(0, at) + '@' + name + ' ' + v.slice(pos);\n else el.value = v.slice(0, pos) + '@' + name + ' ' + v.slice(pos);\n if (mentioned.indexOf(name) < 0) setMentioned((m) => [...m, name]);\n if (onChange) onChange(el.value);\n setOpen(false);\n el.focus();\n };\n const remove = (n) => setMentioned((m) => m.filter((x) => x !== n));\n\n return (\n
\n
\n {\n if (e.key === 'ArrowDown') { e.preventDefault(); move(1); }\n else if (e.key === 'ArrowUp') { e.preventDefault(); move(-1); }\n else if (e.key === 'Enter') { e.preventDefault(); if (active >= 0) insert(users[active].name); }\n else if (e.key === 'Escape') setOpen(false);\n }}\n />\n {open && (\n
\n {users.map((u, i) => (\n
insert(u.name)}>\n {u.name[0]}\n {u.name}\n {u.dept}\n
\n ))}\n
\n )}\n
\n
\n {mentioned.map((n) => (\n @{n} remove(n)}>×\n ))}\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"autocomplete","name":"自动完成 AutoComplete","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/autocomplete.json","category":"input","specLines":["- 输入时根据关键字匹配建议","- 面板样式同选择器下拉","- 高亮匹配文字使用品牌色","- 支持防抖、远程搜索、自定义渲染"],"specSummary":"- 输入时根据关键字匹配建议","contract":{"variants":["本地数据源自动完成","远程搜索自动完成","自定义渲染建议项"],"dims":[{"name":"suggestionSource","values":["local","remote"]},{"name":"itemRender","values":["default","custom"]},{"name":"state","values":["default","focus","disabled"]}],"unknowns":["远程搜索的加载态与失败态样式","建议项最大展示条数"],"anatomy":{"input":"输入框容器,规格同基础输入框(高 32px)","panel":"建议下拉面板,样式同选择器下拉面板","item":"建议项,命中文字以品牌色高亮"},"doNot":["建议面板分组标题样式","多选标签模式"],"usage":["输入时根据关键字匹配建议","面板样式与选择器下拉保持一致","高亮匹配文字使用品牌色 var(--au-color-brand)","支持防抖、远程搜索、自定义渲染建议项"],"structure":{"height":"32px","panel":"白底、阴影中、圆角与选择器一致","debounce":"默认防抖后触发搜索"}},"files":{"html":"../frameworks/AutoComplete.html","css":"../frameworks/AutoComplete.css","jsx":"../frameworks/AutoComplete.jsx","vue2":"../frameworks/AutoComplete.vue2.vue","vue3":"../frameworks/AutoComplete.vue3.vue"},"sizes":{"html":2926,"css":1126,"jsx":2490,"vue2":2246,"vue3":2311},"sources":{"html":"\n\n\n \n \n Aurora Admin · AutoComplete\n \n \n\n\n
\n

AutoComplete 自动完成

\n

输入时匹配建议,高亮关键字,支持键盘上下选择与回车。

\n\n
\n
\n \n
\n
\n

试试输入:北京、上海、广州…

\n
\n
\n\n \n\n\n","css":"/* Aurora Admin AutoComplete 样式 — 自动完成 */\n.aa-autocomplete { position: relative; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); width: 260px; }\n.aa-autocomplete-input { width: 100%; box-sizing: border-box; border: 1px solid #E8ECF1; border-radius: 6px; padding: 8px 10px; font-size: 14px; color: #262626; outline: none; transition: border-color .15s, box-shadow .15s; }\n.aa-autocomplete-input:focus { border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-autocomplete-pop { position: absolute; top: 42px; left: 0; right: 0; background: #fff; border: 1px solid #E8ECF1; border-radius: 6px; box-shadow: 0 6px 24px rgba(0,0,0,0.12); max-height: 220px; overflow: auto; z-index: 20; }\n.aa-autocomplete-item { padding: 8px 12px; font-size: 13px; color: #262626; cursor: pointer; }\n.aa-autocomplete-item:hover, .aa-autocomplete-item.is-active { background: #F0F5FF; color: #2F54EB; }\n.aa-autocomplete-item mark { background: transparent; color: #2F54EB; font-weight: 600; }\n.aa-autocomplete-empty { padding: 10px 12px; color: #6E6E6E; font-size: 13px; }\n","jsx":"import React, { useState, useRef, useEffect } from 'react';\nimport './AutoComplete.css';\n\nconst OPTIONS = ['北京', '上海', '广州', '深圳', '杭州', '成都', '重庆', '武汉', '西安', '南京', '苏州', '天津'];\n\nexport default function AutoComplete({ modelValue = '', options = OPTIONS, placeholder = '输入关键字…', onChange, onSelect }) {\n const [open, setOpen] = useState(false);\n const [active, setActive] = useState(-1);\n const [list, setList] = useState([]);\n const root = useRef(null);\n\n useEffect(() => {\n const onDoc = (e) => { if (root.current && !root.current.contains(e.target)) setOpen(false); };\n document.addEventListener('click', onDoc);\n return () => document.removeEventListener('click', onDoc);\n }, []);\n\n const onInput = (e) => {\n const q = e.target.value;\n if (onChange) onChange(q);\n setActive(-1);\n setList(q ? options.filter((d) => d.indexOf(q) >= 0) : []);\n setOpen(true);\n };\n const highlight = (it) => {\n const q = modelValue.trim();\n if (!q) return it;\n return it.replace(new RegExp('(' + q.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&') + ')', 'g'), '$1');\n };\n const move = (d) => { setOpen(true); setActive((a) => Math.max(-1, Math.min(list.length - 1, a + d))); };\n const choose = (i) => { if (onChange) onChange(list[i]); if (onSelect) onSelect(list[i]); setOpen(false); };\n\n return (\n
\n {\n if (e.key === 'ArrowDown') { e.preventDefault(); move(1); }\n else if (e.key === 'ArrowUp') { e.preventDefault(); move(-1); }\n else if (e.key === 'Enter' && active >= 0) choose(active);\n else if (e.key === 'Escape') setOpen(false);\n }}\n />\n {open && list.length > 0 && (\n
\n {list.map((it, i) => (\n choose(i)}\n dangerouslySetInnerHTML={{ __html: highlight(it) }}\n />\n ))}\n
\n )}\n {open && modelValue && list.length === 0 && (\n
无匹配结果
\n )}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"collapse","name":"折叠面板 Collapse","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/collapse.json","category":"display","specLines":["- 用于分组展示内容,默认展开第一项","- 标题栏高度 40px,左侧箭头图标","- 展开时箭头旋转 90°,内容区下方分割线","- 支持手风琴模式(同时只展开一个)"],"specSummary":"- 用于分组展示内容,默认展开第一项","contract":{"variants":["默认展开第一项","收起态","手风琴模式(同时只展开一个)"],"dims":[{"name":"mode","values":["default","accordion"]},{"name":"state","values":["collapsed","expanded"]}],"unknowns":["禁用项的视觉表现","内容区最大高度与滚动策略"],"anatomy":{"header":"标题栏:高度 40px,左侧箭头图标","arrow":"展开时箭头旋转 90°","content":"内容区下方分割线"},"doNot":["嵌套折叠面板的层级规范"],"usage":["用于分组展示内容,默认展开第一项","标题栏高度 40px,左侧箭头图标","展开时箭头旋转 90°,内容区下方分割线","支持手风琴模式(同时只展开一个)"],"structure":{"headerHeight":"40px","arrowRotate":"90deg","divider":"内容区下方"}},"files":{"html":"../frameworks/collapse.html","css":"../frameworks/Collapse.css","jsx":"../frameworks/Collapse.jsx","vue2":"../frameworks/Collapse.vue2.vue","vue3":"../frameworks/Collapse.vue3.vue"},"sizes":{"html":3919,"css":1380,"jsx":2367,"vue2":2821,"vue3":2856},"sources":{"html":"\n\n\n\n\nAurora Admin · Collapse (H5)\n\n\n\n\n\n
\n

Aurora Admin · Collapse 组件(纯 H5)

\n

对齐规范:标题栏高40px、左侧箭头旋转90°、内容分割线、手风琴模式

\n\n
\n

手风琴模式(同时仅展开一个)

\n
\n
\n
▶基本设置
\n
系统名称、主题色、默认语言等基础配置。
\n
\n
\n
▶通知设置
\n \n
\n
\n
▶安全设置
\n \n
\n
\n
▶高级(无权限)
\n \n
\n
\n
\n\n
\n

非手风琴(可同时展开多个)

\n
\n
\n
▶订单管理
\n
查看与处理所有订单。
\n
\n
\n
▶退款管理
\n
退款审核与到账记录。
\n
\n
\n
\n
\n\n\n\n\n","css":"/* Aurora Admin Collapse 样式 — 对齐 组件2.txt 规范:标题栏高40px、左侧箭头旋转90°、内容分割线、手风琴模式 */\n\n.aa-collapse { border: 1px solid #E8ECF1; border-radius: 6px; overflow: hidden; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; background: #fff; }\n.aa-collapse-item + .aa-collapse-item { border-top: 1px solid #E8ECF1; }\n\n.aa-collapse-header {\n display: flex; align-items: center; gap: 8px;\n height: 40px; padding: 0 16px; cursor: pointer;\n font-size: 14px; color: #262626; background: #fff;\n transition: background .15s; user-select: none;\n}\n.aa-collapse-header:hover { background: #F5F7FA; }\n.aa-collapse-item.is-disabled .aa-collapse-header { color: #BFBFBF; cursor: not-allowed; background: #fff; }\n\n.aa-collapse-arrow {\n display: inline-flex; align-items: center; justify-content: center;\n width: 16px; height: 16px; color: #6E6E6E; font-size: 12px;\n transition: transform .2s ease; transform: rotate(0deg);\n}\n.aa-collapse-item.is-open .aa-collapse-arrow { transform: rotate(90deg); }\n\n.aa-collapse-title { flex: 1; }\n\n.aa-collapse-content {\n padding: 16px; font-size: 14px; line-height: 1.6; color: #595959;\n border-top: 1px solid #E8ECF1; /* 内容分割线 */\n background: #fff;\n}\n.aa-collapse-item.is-disabled .aa-collapse-content { color: #BFBFBF; }\n","jsx":"import React, { useState } from 'react';\nimport './Collapse.css';\n\n/**\n * Aurora Admin Collapse(React)\n * 对齐 组件2.txt 规范:标题栏高40px、左侧箭头旋转90°、内容分割线、手风琴模式\n *\n * props:\n * - items : [{ key, title, disabled? }]\n * - accordion: Boolean(手风琴模式,同时仅展开一个)\n * - value / defaultValue : 展开 key 数组(手风琴时为单个 key 字符串)\n * - onChange(keys)\n * - renderPanel(key) / children(key) : 返回对应面板内容\n */\nexport default function Collapse({\n items = [],\n accordion = false,\n value,\n defaultValue = accordion ? '' : [],\n onChange,\n renderPanel\n}) {\n const panelFn = renderPanel || (typeof children === 'function' ? children : null);\n const [inner, setInner] = useState(defaultValue);\n const open = value !== undefined ? value : inner;\n\n const openList = accordion ? (open ? [open] : []) : (Array.isArray(open) ? open : []);\n const isOpen = (k) => openList.includes(k);\n\n const toggle = (item) => {\n if (item.disabled) return;\n let next;\n if (accordion) {\n next = isOpen(item.key) ? [] : [item.key];\n } else {\n next = isOpen(item.key) ? openList.filter(k => k !== item.key) : [...openList, item.key];\n }\n const result = accordion ? (next[0] || '') : next;\n if (value === undefined) setInner(result);\n onChange && onChange(result);\n };\n\n return (\n
\n {items.map((item) => (\n \n
toggle(item)}>\n ▶\n {item.title}\n
\n {isOpen(item.key) && (\n
\n {panelFn ? panelFn(item.key) : null}\n
\n )}\n
\n ))}\n \n );\n}\n\n/*\n用法示例:\nimport Collapse from './Collapse';\nconst items = [\n { key: 'a', title: '基本设置' },\n { key: 'b', title: '通知设置' },\n { key: 'c', title: '高级', disabled: true }\n];\n
内容:{k}
} />\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"calendar","name":"日历 Calendar","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/calendar.json","category":"display","specLines":["- 用于日程管理、考勤、排班","- 月视图、周视图、日视图切换","- 单元格高度 100px(月视图)","- 选中日期背景品牌色,今日日期边框品牌色","- 事件显示为彩色标签条,支持拖拽"],"specSummary":"- 用于日程管理、考勤、排班","contract":{"variants":["月视图","周视图","日视图","今日(边框品牌色)","选中日期(背景品牌色)"],"dims":[{"name":"view","values":["month","week","day"]},{"name":"state","values":["default","today","selected"]}],"unknowns":["周视图/日视图的单元格尺寸","事件过多时的折叠策略"],"anatomy":{"cell":"单元格:月视图高度 100px","today":"今日日期边框品牌色","selected":"选中日期背景品牌色","event":"事件显示为彩色标签条,支持拖拽"},"doNot":["事件标签的配色集合","跨天日程的渲染方式"],"usage":["用于日程管理、考勤、排班","月视图、周视图、日视图切换","单元格高度 100px(月视图)","选中日期背景品牌色,今日日期边框品牌色","事件显示为彩色标签条,支持拖拽"],"structure":{"cellHeightMonth":"100px","todayBorder":"品牌色","selectedBg":"品牌色"}},"files":{"html":"../frameworks/Calendar.html","css":"../frameworks/Calendar.css","jsx":"../frameworks/Calendar.jsx","vue2":"../frameworks/Calendar.vue2.vue","vue3":"../frameworks/Calendar.vue3.vue"},"sizes":{"html":3478,"css":1357,"jsx":2644,"vue2":2714,"vue3":2585},"sources":{"html":"\n\n\n \n \n Aurora Admin · Calendar(普通 H5)\n \n \n \n\n\n
\n\n \n\n\n","css":"/* Aurora Admin Calendar 样式 — 月历组件,可选中日期,标记今天 */\n\n.aa-calendar { width: 320px; background: #fff; border: 1px solid #E8ECF1; border-radius: 8px; padding: 12px;\n font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; box-sizing: border-box; }\n.aa-calendar-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }\n.aa-calendar-title { font-size: 14px; font-weight: 500; color: #1F2329; }\n.aa-calendar-nav { width: 28px; height: 28px; border: none; background: none; cursor: pointer; color: #595959; border-radius: 4px; font-size: 16px; }\n.aa-calendar-nav:hover { background: #F0F5FF; color: #2F54EB; }\n.aa-calendar-week, .aa-calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); }\n.aa-calendar-week { margin-bottom: 4px; }\n.aa-calendar-weekday { text-align: center; font-size: 12px; color: #6E6E6E; padding: 4px 0; }\n.aa-calendar-cell { text-align: center; padding: 6px 0; font-size: 13px; cursor: pointer; border-radius: 6px; }\n.aa-calendar-cell:hover { background: #F0F5FF; }\n.aa-calendar-cell.is-out { color: #767676; }\n.aa-calendar-cell.is-today { color: #2F54EB; font-weight: 600; }\n.aa-calendar-cell.is-selected { background: #2F54EB; color: #fff; }\n.aa-calendar-cell.is-selected.is-today { color: #fff; }\n","jsx":"import React, { useState, useMemo } from 'react';\nimport './Calendar.css';\n\nconst WEEKS = ['日', '一', '二', '三', '四', '五', '六'];\nfunction fmt(dt) {\n const p = x => (x < 10 ? '0' + x : '' + x);\n return dt.getFullYear() + '-' + p(dt.getMonth() + 1) + '-' + p(dt.getDate());\n}\n\nexport default function Calendar({ value = '', onChange }) {\n const now = new Date();\n const [viewY, setViewY] = useState(now.getFullYear());\n const [viewM, setViewM] = useState(now.getMonth());\n const [selected, setSelected] = useState(value);\n const today = fmt(now);\n\n const cells = useMemo(() => {\n const first = new Date(viewY, viewM, 1).getDay();\n const days = new Date(viewY, viewM + 1, 0).getDate();\n const prevDays = new Date(viewY, viewM, 0).getDate();\n const arr = [];\n for (let i = 0; i < first; i++) {\n let pd = prevDays - first + 1 + i, pm = viewM - 1, py = viewY;\n if (pm < 0) { pm = 11; py--; }\n arr.push({ day: pd, out: true, key: fmt(new Date(py, pm, pd)) });\n }\n for (let d = 1; d <= days; d++) {\n const k = fmt(new Date(viewY, viewM, d));\n arr.push({ day: d, out: false, today: k === today, key: k });\n }\n const tail = (7 - (arr.length % 7)) % 7;\n for (let t = 1; t <= tail; t++) {\n let nm = viewM + 1, ny = viewY; if (nm > 11) { nm = 0; ny++; }\n arr.push({ day: t, out: true, key: fmt(new Date(ny, nm, t)) });\n }\n return arr;\n }, [viewY, viewM, today]);\n\n function prev() { let m = viewM - 1, y = viewY; if (m < 0) { m = 11; y--; } setViewM(m); setViewY(y); }\n function next() { let m = viewM + 1, y = viewY; if (m > 11) { m = 0; y++; } setViewM(m); setViewY(y); }\n function pick(c) {\n setSelected(c.key);\n setViewY(parseInt(c.key.slice(0, 4), 10));\n setViewM(parseInt(c.key.slice(5, 7), 10) - 1);\n onChange && onChange(c.key);\n }\n\n return (\n
\n
\n \n {viewY} 年 {viewM + 1} 月\n \n
\n
\n {WEEKS.map(w => {w})}\n
\n
\n {cells.map((c, i) => (\n
pick(c)}>\n {c.day}\n
\n ))}\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"carousel","name":"轮播 Carousel","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/carousel.json","category":"display","specLines":["- 用于运营 Banner、公告轮播","- 高度 120–200px,圆角 8px","- 指示器圆点 8px,当前项品牌色","- 支持自动播放、左右箭头、触摸滑动"],"specSummary":"- 用于运营 Banner、公告轮播","contract":{"variants":["自动播放 + 指示器","左右箭头切换","指示器 + 箭头"],"dims":[{"name":"control","values":["dots","arrows","dots-arrows"]},{"name":"autoplay","values":["on","off"]}],"unknowns":["自动播放的默认间隔与悬停暂停行为","切换过渡时长与缓动曲线"],"anatomy":{"frame":"轮播容器:高度 120–200px,圆角 8px","indicator":"指示器圆点 8px,当前项品牌色","gesture":"支持自动播放、左右箭头、触摸滑动"},"doNot":["多图同屏与画廊模式"],"usage":["用于运营 Banner、公告轮播","高度 120–200px,圆角 8px","指示器圆点 8px,当前项品牌色","支持自动播放、左右箭头、触摸滑动"],"structure":{"height":"120-200px","radius":"8px","dotSize":"8px","dotActive":"品牌色"}},"files":{"html":"../frameworks/Carousel.html","css":"../frameworks/Carousel.css","jsx":"../frameworks/Carousel.jsx","vue2":"../frameworks/Carousel.vue2.vue","vue3":"../frameworks/Carousel.vue3.vue"},"sizes":{"html":2103,"css":1291,"jsx":1310,"vue2":1491,"vue3":1321},"sources":{"html":"\n\n\n \n \n Aurora Admin · Carousel(普通 H5)\n \n \n \n\n\n
\n\n \n\n\n","css":"/* Aurora Admin Carousel 样式 — 轮播图,箭头切换 + 指示点,可选自动播放 */\n\n.aa-carousel { position: relative; width: 100%; max-width: 480px; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 8px;\n background: #F0F5FF; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-carousel-track { display: flex; height: 100%; transition: transform .3s; }\n.aa-carousel-slide { min-width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 20px; font-weight: 500; }\n.aa-carousel-arrow { position: absolute; top: 50%; transform: translateY(-50%); width: 32px; height: 32px; border: none; border-radius: 50%;\n background: rgba(0,0,0,0.3); color: #fff; cursor: pointer; font-size: 16px; }\n.aa-carousel-arrow:hover { background: rgba(0,0,0,0.5); }\n.aa-carousel-arrow.prev { left: 8px; }\n.aa-carousel-arrow.next { right: 8px; }\n.aa-carousel-dots { position: absolute; bottom: 10px; left: 0; right: 0; display: flex; gap: 6px; justify-content: center; }\n.aa-carousel-dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(255,255,255,0.6); cursor: pointer; transition: width .2s; }\n.aa-carousel-dot.is-active { background: #fff; width: 18px; border-radius: 4px; }\n","jsx":"import React, { useState, useEffect } from 'react';\nimport './Carousel.css';\n\nexport default function Carousel({ items = [], autoplay = true, interval = 3000, onChange }) {\n const [index, setIndex] = useState(0);\n const len = items.length || 1;\n\n function go(i) {\n const next = (i + len) % len;\n setIndex(next);\n onChange && onChange(next);\n }\n useEffect(() => {\n if (!autoplay || items.length < 2) return;\n const t = setInterval(() => go(index + 1), interval);\n return () => clearInterval(t);\n // eslint-disable-next-line\n }, [index, autoplay, interval, items.length]);\n\n return (\n
\n
\n {items.map((s, i) => (\n
{s.text}
\n ))}\n
\n \n \n
\n {items.map((_, i) => (\n go(i)} />\n ))}\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"imagepreview","name":"图片预览 ImagePreview","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/imagepreview.json","category":"display","specLines":["- 点击缩略图打开大图预览","- 遮罩深色 rgba(0,0,0,0.85)","- 支持缩放、旋转、左右切换、下载","- 底部显示当前序号/总数"],"specSummary":"- 点击缩略图打开大图预览","contract":{"variants":["缩放","旋转","左右切换","下载"],"dims":[{"name":"action","values":["zoom","rotate","switch","download"]}],"unknowns":["缩放步进与最大倍数","工具栏按钮的位置与图标"],"anatomy":{"mask":"遮罩深色 rgba(0,0,0,0.85)","thumb":"缩略图,点击打开大图预览","counter":"底部显示当前序号/总数"},"doNot":["缩略图列表的布局与分页"],"usage":["点击缩略图打开大图预览","遮罩深色 rgba(0,0,0,0.85)","支持缩放、旋转、左右切换、下载","底部显示当前序号/总数"],"structure":{"mask":"rgba(0,0,0,0.85)","counter":"底部当前序号/总数"}},"files":{"html":"../frameworks/ImagePreview.html","css":"../frameworks/ImagePreview.css","jsx":"../frameworks/ImagePreview.jsx","vue2":"../frameworks/ImagePreview.vue2.vue","vue3":"../frameworks/ImagePreview.vue3.vue"},"sizes":{"html":3382,"css":1190,"jsx":1069,"vue2":1089,"vue3":1062},"sources":{"html":"\n\n\n \n \n Aurora Admin · ImagePreview(普通 H5)\n \n \n \n\n\n
\n
\n \n \n \n \n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin ImagePreview 样式 — 图片大图预览,遮罩 + 左右切换 + 缩略图 */\n\n.aa-imgprev-mask { position: fixed; inset: 0; background: rgba(0,0,0,0.85); z-index: 2500; display: flex; align-items: center; justify-content: center; flex-direction: column; }\n.aa-imgprev-img { max-width: 80vw; max-height: 70vh; border-radius: 4px; }\n.aa-imgprev-arrow { position: absolute; top: 50%; transform: translateY(-50%); width: 40px; height: 40px; border: none; border-radius: 50%; background: rgba(255,255,255,0.15); color: #fff; font-size: 20px; cursor: pointer; }\n.aa-imgprev-arrow:hover { background: rgba(255,255,255,0.3); }\n.aa-imgprev-arrow.prev { left: 24px; }\n.aa-imgprev-arrow.next { right: 24px; }\n.aa-imgprev-close { position: absolute; top: 20px; right: 24px; color: #fff; font-size: 24px; cursor: pointer; background: none; border: none; }\n.aa-imgprev-thumbs { display: flex; gap: 8px; margin-top: 16px; }\n.aa-imgprev-thumb { width: 48px; height: 48px; border-radius: 4px; object-fit: cover; cursor: pointer; border: 2px solid transparent; }\n.aa-imgprev-thumb.is-active { border-color: #2F54EB; }\n.aa-imgprev-count { color: #fff; font-size: 13px; margin-top: 8px; }\n","jsx":"import React from 'react';\nimport './ImagePreview.css';\n\nexport default function ImagePreview({ images = [], visible = false, index = 0, onClose, onChange }) {\n if (!visible) return null;\n const len = images.length || 1;\n const go = dir => onChange && onChange((index + dir + len) % len);\n return (\n
{ if (e.target === e.currentTarget) onClose && onClose(); }}>\n \n \n {index}\n \n
{index + 1} / {len}
\n
\n {images.map((u, i) => (\n onChange && onChange(i)} />\n ))}\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"qrcode","name":"二维码 QRCode","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/qrcode.json","category":"display","specLines":["- 用于扫码登录、分享、设备绑定","- 尺寸 120–240px,容错率 M","- 支持嵌入 Logo(尺寸 20%)","- 背景白色,前景黑色,支持自定义颜色"],"specSummary":"- 用于扫码登录、分享、设备绑定","contract":{"variants":["小尺寸二维码","中尺寸二维码","嵌入 Logo"],"dims":[{"name":"size","values":["120px","180px","240px"]},{"name":"logo","values":["none","embedded"]}],"unknowns":["自定义前景/背景色的取值边界","失效态(过期)的覆盖表现"],"anatomy":{"matrix":"码点矩阵:容错率 M","logo":"嵌入 Logo(尺寸 20%)","colors":"背景白色,前景黑色,支持自定义颜色"},"doNot":["二维码中心 Logo 的白底留边尺寸"],"usage":["用于扫码登录、分享、设备绑定","尺寸 120–240px,容错率 M","支持嵌入 Logo(尺寸 20%)","背景白色,前景黑色,支持自定义颜色"],"structure":{"size":"120-240px","errorCorrection":"M","logoSize":"20%","background":"#FFFFFF","foreground":"#000000"}},"files":{"html":"../frameworks/QRCode.html","css":"../frameworks/QRCode.css","jsx":"../frameworks/QRCode.jsx","vue2":"../frameworks/QRCode.vue2.vue","vue3":"../frameworks/QRCode.vue3.vue"},"sizes":{"html":2542,"css":478,"jsx":1679,"vue2":1998,"vue3":1818},"sources":{"html":"\n\n\n \n \n Aurora Admin · QRCode(普通 H5)\n \n \n \n\n\n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin QRCode 样式 — 二维码(示例图案,确定性生成,含定位角) */\n\n.aa-qrcode { display: inline-block; padding: 8px; background: #fff; border: 1px solid #E8ECF1; border-radius: 8px;\n font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-qrcode svg { display: block; }\n.aa-qrcode-text { font-size: 12px; color: #6E6E6E; text-align: center; margin-top: 6px; word-break: break-all; max-width: 200px; }\n","jsx":"import React, { useMemo } from 'react';\nimport './QRCode.css';\n\nconst N = 25;\n\nfunction matrix(value, n) {\n let seed = 0; for (let i = 0; i < value.length; i++) seed = (seed * 31 + value.charCodeAt(i)) >>> 0;\n const rnd = () => { seed = (seed * 1103515245 + 12345) >>> 0; return seed / 4294967296; };\n const m = []; for (let r = 0; r < n; r++) m.push(new Array(n).fill(false));\n for (let r = 0; r < n; r++) for (let c = 0; c < n; c++) if (rnd() > 0.5) m[r][c] = true;\n const place = (r0, c0) => {\n for (let r = 0; r < 7; r++) for (let c = 0; c < 7; c++) {\n const edge = r === 0 || r === 6 || c === 0 || c === 6, inner = r >= 2 && r <= 4 && c >= 2 && c <= 4;\n m[r0 + r][c0 + c] = edge || inner;\n }\n for (let rr = -1; rr < 8; rr++) for (let cc = -1; cc < 8; cc++) {\n const y = r0 + rr, x = c0 + cc; if (y >= 0 && y < n && x >= 0 && x < n) { if (rr < 0 || rr === 7 || cc < 0 || cc === 7) m[y][x] = false; }\n }\n };\n place(0, 0); place(0, n - 7); place(n - 7, 0);\n return m;\n}\n\nexport default function QRCode({ value = '', size = 200, color = '#1F2329', showText = true }) {\n const cell = size / N;\n const cells = useMemo(() => {\n const m = matrix(value, N), out = [];\n for (let r = 0; r < N; r++) for (let c = 0; c < N; c++) if (m[r][c]) out.push({ x: c * cell, y: r * cell });\n return out;\n }, [value, size]);\n\n return (\n
\n \n {cells.map((rc, i) => )}\n \n {showText ?
{value}
: null}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"countdown","name":"倒计时 Countdown","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/countdown.json","category":"display","specLines":["- 用于秒杀、验证码、会议开始","- 数字使用 DIN 字体,字号 20–32px","- 时间单位分隔符冒号,颜色次要文字","- 支持天/时/分/秒显示,结束时触发回调"],"specSummary":"- 用于秒杀、验证码、会议开始","contract":{"variants":["天/时/分/秒完整显示","分/秒显示","秒级显示"],"dims":[{"name":"unit","values":["day","hour","minute","second"]},{"name":"size","values":["20px","26px","32px"]}],"unknowns":["触发回调后的替换内容","是否补零的显示约定"],"anatomy":{"digit":"数字:使用 DIN 字体,字号 20–32px","separator":"时间单位分隔符冒号,颜色次要文字","callback":"结束时触发回调"},"doNot":["数字卡片的背景块样式"],"usage":["用于秒杀、验证码、会议开始","数字使用 DIN 字体,字号 20–32px","时间单位分隔符冒号,颜色次要文字","支持天/时/分/秒显示,结束时触发回调"],"structure":{"fontFamily":"DIN","fontSize":"20-32px","separator":"冒号(次要文字色)"}},"files":{"html":"../frameworks/Countdown.html","css":"../frameworks/Countdown.css","jsx":"../frameworks/Countdown.jsx","vue2":"../frameworks/Countdown.vue2.vue","vue3":"../frameworks/Countdown.vue3.vue"},"sizes":{"html":1944,"css":757,"jsx":1485,"vue2":1466,"vue3":1491},"sources":{"html":"\n\n\n \n \n Aurora Admin · Countdown\n \n \n\n\n
\n

Countdown 倒计时

\n

用于秒杀、验证码、会议开始等场景,支持天/时/分/秒,结束触发回调。

\n\n
\n
距活动结束
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin Countdown 样式 — 倒计时 */\n.aa-countdown { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); display: inline-flex; align-items: center; gap: 6px; }\n.aa-countdown-seg { display: flex; flex-direction: column; align-items: center; }\n.aa-countdown-num { font-family: Consolas, \"SFMono-Regular\", monospace; font-size: 24px; font-weight: 600; color: #1F2329; background: #F5F7FA; border-radius: 6px; min-width: 44px; padding: 4px 8px; text-align: center; line-height: 1.2; }\n.aa-countdown-label { font-size: 11px; color: #6E6E6E; margin-top: 3px; }\n.aa-countdown-sep { font-size: 22px; font-weight: 600; color: #6E6E6E; }\n.aa-countdown-done { font-size: 15px; color: #2E7D0A; font-weight: 600; }\n","jsx":"import React, { useState, useEffect } from 'react';\nimport './Countdown.css';\n\nconst pad = (n) => String(n).padStart(2, '0');\n\nexport default function Countdown({ target = Date.now() + 3600000 }) {\n const [now, setNow] = useState(Date.now());\n useEffect(() => {\n const t = setInterval(() => setNow(Date.now()), 1000);\n return () => clearInterval(t);\n }, []);\n const diff = Math.max(0, target - now);\n const d = Math.floor(diff / 86400000);\n const h = Math.floor((diff % 86400000) / 3600000);\n const m = Math.floor((diff % 3600000) / 60000);\n const s = Math.floor((diff % 60000) / 1000);\n\n return (\n
\n {d > 0 && (\n \n {pad(d)}\n 天\n \n )}\n \n {pad(h)}\n 时\n \n :\n \n {pad(m)}\n 分\n \n :\n \n {pad(s)}\n 秒\n \n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"watermark","name":"水印 Watermark","tier":"extension","confidence":"high","specBatch":2,"specFile":"组件2.txt","contractRef":"components/watermark.json","category":"display","specLines":["- 用于敏感信息防泄露","- 可配置文字、图片水印","- 默认文字颜色 #BFBFBF,旋转 -22°,间距 100px","- 覆盖整个页面或指定区域,不影响交互"],"specSummary":"- 用于敏感信息防泄露","contract":{"variants":["文字水印覆盖整页","文字水印覆盖指定区域","图片水印"],"dims":[{"name":"content","values":["text","image"]},{"name":"coverage","values":["page","area"]}],"unknowns":["图片水印的默认尺寸与透明度","文字水印的字号默认值"],"anatomy":{"layer":"覆盖整个页面或指定区域,不影响交互","glyph":"默认文字颜色 #BFBFBF,旋转 -22°,间距 100px"},"doNot":["水印的防删除与防篡改机制"],"usage":["用于敏感信息防泄露","可配置文字、图片水印","默认文字颜色 #BFBFBF,旋转 -22°,间距 100px","覆盖整个页面或指定区域,不影响交互"],"structure":{"color":"#BFBFBF","rotate":"-22deg","gap":"100px"}},"files":{"html":"../frameworks/Watermark.html","css":"../frameworks/Watermark.css","jsx":"../frameworks/Watermark.jsx","vue2":"../frameworks/Watermark.vue2.vue","vue3":"../frameworks/Watermark.vue3.vue"},"sizes":{"html":1462,"css":314,"jsx":676,"vue2":1031,"vue3":1011},"sources":{"html":"\n\n\n \n \n Aurora Admin · Watermark(普通 H5)\n \n \n \n\n\n
\n
\n

机密文档

\n

该区域内容受水印保护,请勿外传。

\n
\n\n \n\n\n","css":"/* Aurora Admin Watermark 样式 — 对齐 组件7.txt 水印:平铺文本,不可选中,覆盖容器/全屏 */\n\n.aa-watermark { position: absolute; inset: 0; pointer-events: none; overflow: hidden; z-index: 1; background-repeat: repeat; user-select: none; }\n.aa-watermark.is-fullscreen { position: fixed; }\n","jsx":"import React from 'react';\nimport './Watermark.css';\n\nexport default function Watermark({\n text = 'Aurora Admin', color = 'rgba(0,0,0,0.10)', fontSize = 14,\n rotate = -22, gap = 160, fullscreen = false\n}) {\n const svg = `` +\n ` ${text} `;\n const bg = `url(\"data:image/svg+xml;utf8,${encodeURIComponent(svg)}\")`;\n return (\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"cardlist","name":"卡片列表 CardList","tier":"extension","confidence":"high","specBatch":4,"specFile":"组件4.txt","contractRef":"components/cardlist.json","category":"display","specLines":["- 以卡片形式展示列表数据","- 每行可显示多个卡片(栅格)","- 卡片内容自定义","- 支持分页或无限滚动"],"specSummary":"- 以卡片形式展示列表数据","contract":{"variants":["栅格 + 分页","栅格 + 无限滚动"],"dims":[{"name":"load","values":["pagination","infinite-scroll"]},{"name":"columns","values":["grid"]}],"unknowns":["栅格列数与响应式断点","无限滚动的触底阈值"],"anatomy":{"card":"卡片单元:内容自定义","row":"每行可显示多个卡片(栅格)"},"doNot":["卡片内部的字段与布局(内容由使用方自定义)"],"usage":["以卡片形式展示列表数据","每行可显示多个卡片(栅格)","卡片内容自定义","支持分页或无限滚动"],"structure":{"layout":"栅格,每行多个卡片"}},"files":{"html":"../frameworks/CardList.html","css":"../frameworks/CardList.css","jsx":"../frameworks/CardList.jsx","vue2":"../frameworks/CardList.vue2.vue","vue3":"../frameworks/CardList.vue3.vue"},"sizes":{"html":3359,"css":1346,"jsx":1191,"vue2":1023,"vue3":988},"sources":{"html":"\n\n\n \n \n Aurora Admin · CardList(普通 H5)\n \n \n \n\n\n
\n \n \n
\n\n
\n\n \n\n\n","css":"/* Aurora Admin CardList 样式 — 对齐 组件7.txt CardList:栅格卡片、封面/标题/描述/标签/操作 */\n\n.aa-cardlist { display: grid; gap: 16px; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-cardlist-item { border: 1px solid #E8ECF1; border-radius: 8px; overflow: hidden; background: #fff; transition: box-shadow .15s, border-color .15s; }\n.aa-cardlist-item:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.08); border-color: #C9D4F2; }\n.aa-card-cover { height: 120px; background: #F0F5FF; display: flex; align-items: center; justify-content: center; font-size: 32px; color: #2F54EB; }\n.aa-card-body { padding: 12px 16px; }\n.aa-card-title { font-size: 14px; font-weight: 500; color: #1F2329; }\n.aa-card-desc { font-size: 13px; color: #6E6E6E; margin-top: 4px; line-height: 1.5; }\n.aa-card-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }\n.aa-card-tag { background: #F0F5FF; color: #2F54EB; font-size: 12px; padding: 1px 8px; border-radius: 2px; }\n.aa-card-actions { display: flex; gap: 8px; padding: 8px 16px; border-top: 1px solid #E8ECF1; }\n.aa-card-actions button { border: none; background: none; cursor: pointer; color: #2F54EB; font-size: 13px; padding: 4px 8px; border-radius: 4px; }\n.aa-card-actions button:hover { background: #F0F5FF; }\n","jsx":"import React from 'react';\nimport './CardList.css';\n\nexport default function CardList({ items = [], cols = 3, onAction }) {\n return (\n
\n {items.map((it, i) => (\n
\n
{it.emoji || '📄'}
\n
\n
{it.title}
\n
{it.desc}
\n {it.tags && it.tags.length ? (\n
\n {it.tags.map((t, ti) => (\n {t}\n ))}\n
\n ) : null}\n
\n {it.actions && it.actions.length ? (\n
\n {it.actions.map((a, ai) => (\n \n ))}\n
\n ) : null}\n
\n ))}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"shortcutpanel","name":"键盘快捷键面板 ShortcutPanel","tier":"extension","confidence":"high","specBatch":5,"specFile":"组件5.txt","contractRef":"components/shortcutpanel.json","category":"system","specLines":["- 展示系统所有快捷键","- 弹窗列表:快捷键、功能描述","- 支持自定义快捷键","- 在设置中配置"],"specSummary":"- 展示系统所有快捷键","contract":{"variants":["全局快捷键列表","当前页快捷键","可自定义快捷键"],"dims":[{"name":"scope","values":["global","local"]},{"name":"editable","values":["true","false"]}],"unknowns":["面板的分组方式","自定义快捷键的持久化方式"],"anatomy":{"panel":"弹窗列表:快捷键、功能描述","scope":"展示系统所有快捷键","custom":"支持自定义快捷键","config":"在设置中配置"},"doNot":["快捷键的注册与冲突检测实现"],"usage":["展示系统所有快捷键","弹窗列表:快捷键、功能描述","支持自定义快捷键","在设置中配置"],"structure":{"layout":"快捷键 + 功能描述","custom":"可配置"}},"files":{"html":"../frameworks/ShortcutPanel.html","css":"../frameworks/ShortcutPanel.css","jsx":"../frameworks/ShortcutPanel.jsx","vue2":"../frameworks/ShortcutPanel.vue2.vue","vue3":"../frameworks/ShortcutPanel.vue3.vue"},"sizes":{"html":1933,"css":1182,"jsx":1295,"vue2":1289,"vue3":1229},"sources":{"html":"\n\n\n \n \n Aurora Admin · ShortcutPanel\n \n \n\n\n
\n

ShortcutPanel 快捷键面板

\n

展示全部快捷键,支持搜索过滤。

\n\n
\n
\n \n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin ShortcutPanel 样式 — 快捷键面板 */\n.aa-shortcut { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); border: 1px solid #E8ECF1; border-radius: 8px; padding: 14px; }\n.aa-shortcut-search { width: 100%; border: 1px solid #E8ECF1; border-radius: 6px; padding: 7px 10px; font-size: 14px; outline: none; box-sizing: border-box; margin-bottom: 10px; transition: border-color .15s, box-shadow .15s; }\n.aa-shortcut-search:focus { border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-shortcut-list { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }\n.aa-shortcut-item { display: flex; justify-content: space-between; align-items: center; font-size: 13px; color: #262626; background: #FAFAFA; border-radius: 6px; padding: 8px 10px; }\n.aa-shortcut-key { font-family: Consolas, monospace; background: #fff; border: 1px solid #E8ECF1; border-radius: 4px; padding: 1px 6px; color: #595959; font-size: 12px; }\n.aa-shortcut-empty { grid-column: 1 / -1; text-align: center; color: #6E6E6E; font-size: 13px; padding: 14px; }\n@media (max-width: 520px) { .aa-shortcut-list { grid-template-columns: 1fr; } }\n","jsx":"import React, { useState, useMemo } from 'react';\nimport './ShortcutPanel.css';\n\nconst DEFAULT_ITEMS = [\n { name: '保存', key: 'Ctrl + S' },\n { name: '搜索', key: 'Ctrl + K' },\n { name: '新建', key: 'Ctrl + N' },\n { name: '刷新', key: 'F5' },\n { name: '关闭页签', key: 'Ctrl + W' },\n { name: '全屏', key: 'F11' },\n { name: '帮助', key: '?' },\n { name: '命令面板', key: 'Ctrl + P' }\n];\n\nexport default function ShortcutPanel({ items = DEFAULT_ITEMS }) {\n const [q, setQ] = useState('');\n const filtered = useMemo(() => {\n const s = q.trim().toLowerCase();\n if (!s) return items;\n return items.filter((it) => it.name.toLowerCase().indexOf(s) >= 0 || it.key.toLowerCase().indexOf(s) >= 0);\n }, [q, items]);\n\n return (\n
\n setQ(e.target.value)} />\n
\n {!filtered.length &&
无匹配快捷键
}\n {filtered.map((it, i) => (\n
\n {it.name}\n {it.key}\n
\n ))}\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"bankcardinput","name":"银行卡输入 BankCardInput","tier":"extension","confidence":"high","specBatch":6,"specFile":"组件6.txt","contractRef":"components/bankcardinput.json","category":"input","specLines":["- 用于银行卡号输入","- 自动格式化每4位空格","- 识别卡类型(借记卡/信用卡/储蓄卡)并显示图标","- 支持Luhn校验","- 可配置扫描识别银行卡"],"specSummary":"- 用于银行卡号输入","contract":{"variants":["默认输入","聚焦(自动格式化)","Luhn 校验失败","开启扫描识别"],"dims":[{"name":"state","values":["default","focus","error","disabled"]},{"name":"scan","values":["enabled","disabled"]}],"unknowns":["卡类型图标的具体形状","Luhn 失败时的错误文案"],"anatomy":{"input":"输入框:自动格式化每 4 位空格","icon":"识别卡类型(借记卡/信用卡/储蓄卡)并显示图标","validate":"支持 Luhn 校验"},"doNot":["各银行 BIN 号段对照表","扫描识别的底层实现"],"usage":["用于银行卡号输入","自动格式化每4位空格","识别卡类型(借记卡/信用卡/储蓄卡)并显示图标","支持Luhn校验","可配置扫描识别银行卡"],"structure":{"format":"每 4 位空格","validate":"Luhn","scan":"可配置"}},"files":{"html":"../frameworks/BankCardInput.html","css":"../frameworks/BankCardInput.css","jsx":"../frameworks/BankCardInput.jsx","vue2":"../frameworks/BankCardInput.vue2.vue","vue3":"../frameworks/BankCardInput.vue3.vue"},"sizes":{"html":2869,"css":1221,"jsx":2133,"vue2":2245,"vue3":2250},"sources":{"html":"\n\n\n \n \n Aurora Admin · BankCardInput\n \n \n\n\n
\n

BankCardInput 银行卡输入

\n

自动每 4 位空格、识别卡组织、Luhn 校验。

\n\n
\n
基础用法
\n
\n
\n \n 识别中\n
\n
支持 Visa / MasterCard / 银联等
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin BankCardInput 样式 — 银行卡输入 + 类型识别 + Luhn 校验 */\n.aa-card { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-card-field { display: flex; align-items: center; border: 1px solid #E8ECF1; border-radius: 6px; padding: 0 10px; height: 40px; background: #fff; transition: border-color .15s, box-shadow .15s; }\n.aa-card-field.is-focus { border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-card-field.is-valid { border-color: #2E7D0A; }\n.aa-card-field.is-invalid { border-color: #CF1322; }\n.aa-card-input { flex: 1; border: none; outline: none; font-size: 16px; letter-spacing: 1px; color: #1F2329; background: none; font-family: \"SFMono-Regular\", Consolas, monospace; }\n.aa-card-input::placeholder { color: #767676; letter-spacing: normal; }\n.aa-card-type { font-size: 12px; font-weight: 600; padding: 2px 8px; border-radius: 4px; background: #F0F5FF; color: #2F54EB; min-width: 56px; text-align: center; }\n.aa-card-type.unknown { background: #F5F5F5; color: #6E6E6E; }\n.aa-card-tip { font-size: 12px; margin-top: 4px; color: #6E6E6E; }\n.aa-card-tip.valid { color: #2E7D0A; }\n.aa-card-tip.invalid { color: #CF1322; }\n","jsx":"import React, { useState, useMemo } from 'react';\nimport './BankCardInput.css';\n\nfunction detectType(v) {\n if (/^4/.test(v)) return 'Visa';\n if (/^5[1-5]/.test(v)) return 'MasterCard';\n if (/^62/.test(v)) return '银联';\n if (/^3[47]/.test(v)) return 'AmEx';\n if (/^35/.test(v)) return 'JCB';\n return '';\n}\nfunction luhn(v) {\n let sum = 0, alt = false;\n for (let i = v.length - 1; i >= 0; i--) {\n let n = parseInt(v[i], 10);\n if (alt) { n *= 2; if (n > 9) n -= 9; }\n sum += n; alt = !alt;\n }\n return sum % 10 === 0;\n}\n\nexport default function BankCardInput({ modelValue = '', placeholder = '请输入银行卡号', onChange }) {\n const [digits, setDigits] = useState(modelValue.replace(/\\D/g, ''));\n const [focused, setFocused] = useState(false);\n\n const display = digits.replace(/(.{4})/g, '$1 ').trim();\n const cardType = detectType(digits);\n const valid = digits.length >= 13 && luhn(digits);\n const invalid = digits.length >= 13 && !luhn(digits);\n const tipClass = valid ? 'valid' : invalid ? 'invalid' : '';\n const tipText = !digits\n ? '支持 Visa / MasterCard / 银联等'\n : valid\n ? '卡号校验通过'\n : invalid\n ? '卡号未通过 Luhn 校验'\n : '继续输入卡号…';\n\n const onInput = (e) => {\n const v = e.target.value.replace(/\\D/g, '').slice(0, 19);\n e.target.value = v.replace(/(.{4})/g, '$1 ').trim();\n setDigits(v);\n if (onChange) onChange(v);\n };\n\n return (\n
\n \n setFocused(true)}\n onBlur={() => setFocused(false)}\n />\n {cardType || '未知'}\n
\n
{tipText}
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"idcardinput","name":"身份证输入 IDCardInput","tier":"extension","confidence":"high","specBatch":6,"specFile":"组件6.txt","contractRef":"components/idcardinput.json","category":"input","specLines":["- 用于身份证号码输入","- 支持18位校验,自动验证出生日期、性别","- 输入框可分段显示(前6位、中间8位、后4位)","- 可配置扫描识别身份证","- 错误提示具体原因"],"specSummary":"- 用于身份证号码输入","contract":{"variants":["默认输入","分段显示(前6/中8/后4)","18 位校验失败","开启扫描识别"],"dims":[{"name":"state","values":["default","focus","error","disabled"]},{"name":"segment","values":["plain","segmented"]},{"name":"scan","values":["enabled","disabled"]}],"unknowns":["分段之间的分隔符形态","校验位失败的具体文案"],"anatomy":{"input":"输入框","validate":"18 位校验,自动验证出生日期、性别","segment":"可分段显示(前6位、中间8位、后4位)","error":"错误提示具体原因"},"doNot":["行政区划代码校验表"],"usage":["用于身份证号码输入","支持18位校验,自动验证出生日期、性别","输入框可分段显示(前6位、中间8位、后4位)","可配置扫描识别身份证","错误提示具体原因"],"structure":{"length":"18 位","segment":"6 / 8 / 4","scan":"可配置"}},"files":{"html":"../frameworks/IDCardInput.html","css":"../frameworks/IDCardInput.css","jsx":"../frameworks/IDCardInput.jsx","vue2":"../frameworks/IDCardInput.vue2.vue","vue3":"../frameworks/IDCardInput.vue3.vue"},"sizes":{"html":3039,"css":1178,"jsx":2156,"vue2":2211,"vue3":2246},"sources":{"html":"\n\n\n \n \n Aurora Admin · IDCardInput\n \n \n\n\n
\n

IDCardInput 身份证输入

\n

18 位校验(含校验位)、自动提取出生日期与性别。

\n\n
\n
基础用法
\n
\n
\n \n
\n
\n
示例:11010119900307657X
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin IDCardInput 样式 — 身份证号输入 + 校验 + 信息提取 */\n.aa-idcard { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-idcard-field { display: flex; align-items: center; border: 1px solid #E8ECF1; border-radius: 6px; padding: 0 10px; height: 40px; background: #fff; transition: border-color .15s, box-shadow .15s; }\n.aa-idcard-field.is-focus { border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-idcard-field.is-valid { border-color: #2E7D0A; }\n.aa-idcard-field.is-invalid { border-color: #CF1322; }\n.aa-idcard-input { flex: 1; border: none; outline: none; font-size: 15px; letter-spacing: 1px; color: #1F2329; background: none; font-family: \"SFMono-Regular\", Consolas, monospace; }\n.aa-idcard-input::placeholder { color: #767676; letter-spacing: normal; }\n.aa-idcard-info { display: flex; gap: 18px; margin-top: 6px; font-size: 13px; color: #595959; flex-wrap: wrap; }\n.aa-idcard-info b { color: #1F2329; font-weight: 600; }\n.aa-idcard-tip { font-size: 12px; margin-top: 4px; color: #6E6E6E; }\n.aa-idcard-tip.valid { color: #2E7D0A; }\n.aa-idcard-tip.invalid { color: #CF1322; }\n","jsx":"import React, { useState, useMemo } from 'react';\nimport './IDCardInput.css';\n\nconst WEIGHTS = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];\nconst CHECK = '10X98765432';\nfunction validate(v) {\n if (!/^\\d{17}[\\dX]$/.test(v)) return null;\n let sum = 0;\n for (let i = 0; i < 17; i++) sum += parseInt(v[i], 10) * WEIGHTS[i];\n if (CHECK[sum % 11] !== v[17]) return null;\n const birth = v.slice(6, 14);\n return { birth: birth.slice(0, 4) + '-' + birth.slice(4, 6) + '-' + birth.slice(6, 8), gender: parseInt(v[16], 10) % 2 === 1 ? '男' : '女' };\n}\n\nexport default function IDCardInput({ modelValue = '', placeholder = '请输入 18 位身份证号', onChange }) {\n const [focused, setFocused] = useState(false);\n const result = useMemo(() => validate(modelValue.toUpperCase()) || {}, [modelValue]);\n const valid = !!result.birth;\n const invalid = modelValue.length === 18 && !result.birth;\n const tipClass = !modelValue ? '' : valid ? 'valid' : invalid ? 'invalid' : '';\n const tipText = !modelValue\n ? '示例:11010119900307657X'\n : modelValue.length < 18\n ? '已输入 ' + modelValue.length + ' 位,继续…'\n : valid\n ? '校验通过'\n : '身份证号校验失败,请检查';\n\n const onInput = (e) => {\n const v = e.target.value.replace(/[^0-9Xx]/g, '').toUpperCase().slice(0, 18);\n e.target.value = v;\n if (onChange) onChange(v);\n };\n\n return (\n
\n \n setFocused(true)}\n onBlur={() => setFocused(false)}\n />\n
\n {result.birth && (\n
\n 出生日期:{result.birth}\n 性别:{result.gender}\n
\n )}\n
{tipText}
\n \n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"plateinput","name":"车牌输入 PlateInput","tier":"extension","confidence":"high","specBatch":6,"specFile":"组件6.txt","contractRef":"components/plateinput.json","category":"input","specLines":["- 用于车牌号输入","- 第一部分省份简称(下拉选择),后部分字母数字","- 自动大写,特殊字符限制","- 支持新能源车牌(8位)","- 可扫描识别车牌"],"specSummary":"- 用于车牌号输入","contract":{"variants":["普通车牌","新能源车牌(8位)","省份选择展开","开启扫描识别"],"dims":[{"name":"state","values":["default","focus","error","disabled"]},{"name":"energyType","values":["normal","new-energy"]},{"name":"scan","values":["enabled","disabled"]}],"unknowns":["省份下拉的宽度与展开方式","新能源车牌的识别方式"],"anatomy":{"province":"第一部分省份简称(下拉选择)","body":"后部分字母数字","rule":"自动大写,特殊字符限制","newEnergy":"支持新能源车牌(8位)"},"doNot":["各省份简称的完整枚举","车牌字母禁用规则(如 I/O)"],"usage":["用于车牌号输入","第一部分省份简称(下拉选择),后部分字母数字","自动大写,特殊字符限制","支持新能源车牌(8位)","可扫描识别车牌"],"structure":{"normal":"7 位","newEnergy":"8 位","case":"自动大写"}},"files":{"html":"../frameworks/PlateInput.html","css":"../frameworks/PlateInput.css","jsx":"../frameworks/PlateInput.jsx","vue2":"../frameworks/PlateInput.vue2.vue","vue3":"../frameworks/PlateInput.vue3.vue"},"sizes":{"html":3007,"css":1394,"jsx":1963,"vue2":1967,"vue3":1991},"sources":{"html":"\n\n\n \n \n Aurora Admin · PlateInput\n \n \n\n\n
\n

PlateInput 车牌号输入

\n

省份简称 + 字母数字组合,自动大写,支持新能源车牌(7 位)。

\n\n
\n
基础用法
\n
\n
\n \n \n
\n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin PlateInput 样式 — 车牌号输入(省份 + 字母数字,支持新能源) */\n.aa-plate { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-plate-field { display: inline-flex; align-items: stretch; border-radius: 6px; overflow: hidden; border: 1px solid #E8ECF1; transition: border-color .15s, box-shadow .15s; }\n.aa-plate-field.is-focus { border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-plate-field.is-valid { border-color: #2E7D0A; }\n.aa-plate-field.is-invalid { border-color: #CF1322; }\n.aa-plate-prov { border: none; background: #2F54EB; color: #fff; font-size: 16px; font-weight: 700; padding: 0 10px; cursor: pointer; outline: none; }\n.aa-plate-input { border: none; outline: none; font-size: 18px; font-weight: 700; letter-spacing: 3px; color: #1F2329; background: #fff; padding: 0 12px; width: 160px; text-transform: uppercase; font-family: \"SFMono-Regular\", Consolas, monospace; }\n.aa-plate-input::placeholder { color: #767676; letter-spacing: normal; font-weight: 400; }\n.aa-plate-tip { font-size: 12px; margin-top: 6px; color: #6E6E6E; }\n.aa-plate-tip.valid { color: #2E7D0A; }\n.aa-plate-tip.invalid { color: #CF1322; }\n.aa-plate-tip .aa-plate-tag { display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 3px; font-size: 11px; background: #F0F5FF; color: #2F54EB; }\n","jsx":"import React, { useState, useMemo } from 'react';\nimport './PlateInput.css';\n\nexport default function PlateInput({\n modelValue = '',\n provinces = ['京', '沪', '粤', '津', '冀', '鲁', '苏', '浙', '川', '渝'],\n onChange\n}) {\n const [prov, setProv] = useState(modelValue ? modelValue[0] : '京');\n const [body, setBody] = useState(modelValue ? modelValue.slice(1) : '');\n const [focused, setFocused] = useState(false);\n\n const valid = /^[A-Z][A-Z0-9]{5}$/.test(body) || /^[A-Z][A-Z0-9]{6}$/.test(body);\n const invalid = !!body && !valid;\n const isNewEnergy = /^[A-Z][A-Z0-9]{6}$/.test(body);\n const tipClass = !body ? '' : valid ? 'valid' : 'invalid';\n const tipText = !body\n ? '格式:省份 + 字母 + 5/6 位(新能源 6 位)'\n : valid\n ? '车牌号合法:' + prov + body + '' + (isNewEnergy ? '新能源' : '普通') + ''\n : '车牌格式不正确(首位应为字母)';\n\n const onInput = (e) => {\n const v = e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 7);\n e.target.value = v;\n setBody(v);\n if (onChange) onChange(prov + v);\n };\n\n return (\n
\n \n \n setFocused(true)}\n onBlur={() => setFocused(false)}\n />\n
\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"bottomsheet","name":"底部面板 BottomSheet","tier":"extension","confidence":"high","specBatch":6,"specFile":"组件6.txt","contractRef":"components/bottomsheet.json","category":"feedback","specLines":["- 从底部滑出的面板","- 类似移动端抽屉,但高度可选","- 支持拖拽关闭、遮罩点击关闭","- 内容可滚动","- 常用于操作选择、筛选、快捷操作"],"specSummary":"- 从底部滑出的面板","contract":{"variants":["自适应高度","半屏","全屏","拖拽中"],"dims":[{"name":"height","values":["auto","half","full"]},{"name":"state","values":["closed","open","dragging"]}],"unknowns":["各高度档位的具体比例","拖拽关闭的触发阈值"],"anatomy":{"sheet":"从底部滑出的面板","height":"类似移动端抽屉,但高度可选","dismiss":"支持拖拽关闭、遮罩点击关闭","body":"内容可滚动"},"doNot":["多级面板的堆叠规则"],"usage":["从底部滑出的面板","类似移动端抽屉,但高度可选","支持拖拽关闭、遮罩点击关闭","内容可滚动","常用于操作选择、筛选、快捷操作"],"structure":{"position":"底部滑出","dismiss":"拖拽 / 遮罩点击"}},"files":{"html":"../frameworks/BottomSheet.html","css":"../frameworks/BottomSheet.css","jsx":"../frameworks/BottomSheet.jsx","vue2":"../frameworks/BottomSheet.vue2.vue","vue3":"../frameworks/BottomSheet.vue3.vue"},"sizes":{"html":1737,"css":1474,"jsx":808,"vue2":886,"vue3":865},"sources":{"html":"\n\n\n \n \n Aurora Admin · BottomSheet(普通 H5)\n \n \n \n\n\n \n\n
\n
\n
\n
筛选订单
\n
\n 在这里放置筛选条件、操作列表等内容。面板可从底部滑动出现,点击遮罩或向下拖动可关闭。\n
\n
\n \n \n
\n
\n\n \n\n\n","css":"/* Aurora Admin BottomSheet 样式 — 对齐 组件7.txt BottomSheet:底部滑出面板,遮罩点击关闭,内容滚动 */\n\n.aa-sheet-mask { position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 2000; opacity: 0; pointer-events: none; transition: opacity .25s; }\n.aa-sheet-mask.is-open { opacity: 1; pointer-events: auto; }\n.aa-sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 2001; background: #fff; border-radius: 12px 12px 0 0;\n transform: translateY(100%); transition: transform .25s; max-height: 80vh; display: flex; flex-direction: column;\n font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-sheet.is-open { transform: translateY(0); }\n.aa-sheet-handle { width: 36px; height: 4px; border-radius: 2px; background: #E8ECF1; margin: 8px auto; cursor: grab; flex-shrink: 0; }\n.aa-sheet-header { padding: 4px 16px 12px; font-size: 16px; font-weight: 500; color: #1F2329; border-bottom: 1px solid #E8ECF1; }\n.aa-sheet-body { padding: 16px; overflow: auto; flex: 1; color: #262626; font-size: 14px; line-height: 1.6; }\n.aa-sheet-footer { padding: 12px 16px; border-top: 1px solid #E8ECF1; display: flex; justify-content: flex-end; gap: 8px; }\n.aa-sheet-btn { height: 32px; padding: 0 16px; border-radius: 6px; border: 1px solid #E8ECF1; background: #fff; color: #262626; cursor: pointer; font-size: 14px; }\n.aa-sheet-btn.primary { background: #2F54EB; border-color: #2F54EB; color: #fff; }\n","jsx":"import React, { useRef } from 'react';\nimport './BottomSheet.css';\n\nexport default function BottomSheet({ visible = false, title = '', onClose, children, footer }) {\n const startY = useRef(0);\n return (\n <>\n
\n { startY.current = e.clientY; }}\n onPointerUp={e => { if (startY.current - e.clientY < -80) onClose && onClose(); }}\n >\n
\n {title ?
{title}
: null}\n
{children}
\n {footer ?
{footer}
: null}\n
\n \n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"treetable","name":"树形表格 TreeTable","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/treetable.json","category":"display","specLines":["- 在普通表格基础上支持树形层级展示","- 层级缩进 16px/级,展开/折叠箭头","- 父级行可显示汇总数据(如合计)","- 支持懒加载子节点、异步加载","- 支持排序、筛选、多选,选择父级联动子级","- 适用于组织架构、商品分类、权限树等场景"],"specSummary":"- 在普通表格基础上支持树形层级展示","contract":{"variants":["选择父级联动子级","懒加载子节点","父级行汇总数据","排序 + 筛选"],"dims":[{"name":"feature","values":["sortable","filterable","multi-select","lazy-load","summary-row"]},{"name":"level","values":["root","child"]}],"unknowns":["层级最大深度限制","子级加载中的占位表现"],"anatomy":{"indent":"层级缩进 16px/级","arrow":"展开/折叠箭头","parentRow":"父级行可显示汇总数据(如合计)","childRow":"子节点支持懒加载、异步加载","selection":"多选时选择父级联动子级"},"doNot":["父级半选态的具体图标","汇总行的计算规则"],"usage":["在普通表格基础上支持树形层级展示","层级缩进 16px/级,展开/折叠箭头","父级行可显示汇总数据(如合计)","支持懒加载子节点、异步加载","支持排序、筛选、多选,选择父级联动子级","适用于组织架构、商品分类、权限树等场景"],"structure":{"indent":"16px/级","features":"排序、筛选、多选"}},"files":{"html":"../frameworks/treetable.html","css":"../frameworks/TreeTable.css","jsx":"../frameworks/TreeTable.jsx","vue2":"../frameworks/TreeTable.vue2.vue","vue3":"../frameworks/TreeTable.vue3.vue"},"sizes":{"html":6355,"css":1454,"jsx":6235,"vue2":7343,"vue3":7008},"sources":{"html":"\n\n\n\n\nAurora Admin · TreeTable (H5)\n\n\n\n\n
\n

Aurora Admin · TreeTable 组件(纯 H5)

\n

对齐 TreeTable 规范:层级缩进16px/级、展开/折叠箭头、父级汇总(子节点合计)、选择父级联动子级

\n\n
\n \n \n \n \n \n \n \n \n \n \n \n
部门 / 人员人数预算(万)
合计00
\n
\n
\n\n\n\n\n","css":"/* Aurora Admin TreeTable 样式 — 对齐 组件7.txt TreeTable 规范 */\n.aa-tt { width: 100%; border: 1px solid #E8ECF1; border-radius: 8px; overflow: hidden; background: #fff; }\n.aa-tt table { width: 100%; border-collapse: collapse; font-size: 14px; color: #262626; }\n.aa-tt thead th { background: #FAFAFA; color: #262626; font-weight: 600; height: 48px; text-align: left; padding: 0 16px; border-bottom: 1px solid #E8ECF1; white-space: nowrap; }\n.aa-tt tbody td { height: 48px; padding: 0 16px; border-bottom: 1px solid #E8ECF1; text-align: left; }\n.aa-tt tbody tr:last-child td { border-bottom: none; }\n.aa-tt tbody tr:hover { background: #F5F7FA; }\n.aa-tt tbody tr.is-selected { background: #F0F5FF; }\n.aa-tt .align-right { text-align: right; }\n.aa-tt .col-selection { text-align: center; }\n.aa-tt .tree-cell { display: flex; align-items: center; gap: 4px; }\n.aa-tt .tt-arrow { width: 16px; height: 16px; font-size: 12px; color: #6E6E6E; transition: transform .15s; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }\n.aa-tt .tt-arrow.is-open { transform: rotate(90deg); }\n.aa-tt .tt-arrow-placeholder { width: 16px; flex-shrink: 0; }\n.aa-tt .tt-check { width: 14px; height: 14px; cursor: pointer; }\n.aa-tt .tt-summary { color: #6E6E6E; font-size: 12px; margin-left: 6px; }\n.aa-tt tfoot td { height: 44px; padding: 0 16px; background: #FAFAFA; font-weight: 600; border-top: 1px solid #E8ECF1; }\n","jsx":"import React, { useState, useMemo, useRef } from 'react';\nimport './TreeTable.css';\n\n/**\n * Aurora Admin TreeTable(React)\n * 对齐 组件7.txt TreeTable 规范:层级缩进16px/级、展开/折叠箭头、父级汇总(叶子合计)、选择父级联动子级\n * columns: [{ key, title, align? }]\n * data: 树形行 [{ key, children, ...字段 }]\n * summaryColumn: 指定数值列,父级与表尾显示叶子求和\n */\nfunction hasChildren(n) { return !!(n.children && n.children.length); }\nfunction leafSum(node, key) {\n if (!hasChildren(node)) return Number(node[key]) || 0;\n return node.children.reduce((s, c) => s + leafSum(c, key), 0);\n}\nfunction leafKeys(data, rowKey) {\n const out = [];\n const walk = (nodes) => nodes.forEach(n => { if (hasChildren(n)) walk(n.children); else out.push(n[rowKey]); });\n walk(data);\n return out;\n}\nfunction descendantKeys(node, rowKey) { let o = []; (node.children || []).forEach(c => { o.push(c[rowKey]); o = o.concat(descendantKeys(c, rowKey)); }); return o; }\nfunction allChildrenSelected(node, set, rowKey) { if (!hasChildren(node)) return set.has(node[rowKey]); return node.children.every(c => allChildrenSelected(c, set, rowKey)); }\n\nexport default function TreeTable({\n columns = [],\n data = [],\n rowKey = 'key',\n selectable = false,\n selectedKeys = [],\n defaultExpandedKeys = [],\n summaryColumn = '',\n summary = { label: '合计' },\n onSelectionChange\n}) {\n const [expanded, setExpanded] = useState(() => new Set(defaultExpandedKeys));\n const [selected, setSelected] = useState(() => new Set(selectedKeys));\n\n const visibleList = useMemo(() => {\n const out = [];\n const walk = (nodes, depth) => nodes.forEach(n => {\n out.push({ node: n, depth });\n if (hasChildren(n) && expanded.has(n[rowKey])) walk(n.children, depth + 1);\n });\n walk(data, 0);\n return out;\n }, [data, expanded, rowKey]);\n\n const totalOf = (key) => data.reduce((s, n) => s + leafSum(n, key), 0);\n const allSelected = data.length > 0 && leafKeys(data, rowKey).every(k => selected.has(k));\n const leaves = leafKeys(data, rowKey);\n const nSel = leaves.filter(k => selected.has(k)).length;\n const someSelected = nSel > 0 && nSel < leaves.length;\n\n const toggleExpand = (key) => setExpanded(prev => { const n = new Set(prev); n.has(key) ? n.delete(key) : n.add(key); return n; });\n\n const toggleCheck = (node) => {\n const key = node[rowKey];\n const willCheck = !selected.has(key);\n const keys = [key, ...descendantKeys(node, rowKey)];\n const next = new Set(selected);\n keys.forEach(k => willCheck ? next.add(k) : next.delete(k));\n const propagate = (nodes) => nodes.forEach(n => {\n if (hasChildren(n)) { allChildrenSelected(n, next, rowKey) ? next.add(n[rowKey]) : next.delete(n[rowKey]); propagate(n.children); }\n });\n propagate(data);\n setSelected(next);\n onSelectionChange && onSelectionChange([...next]);\n };\n\n const toggleAll = () => {\n const next = allSelected ? [] : leafKeys(data, rowKey);\n setSelected(new Set(next));\n onSelectionChange && onSelectionChange(next);\n };\n\n const onRowClick = (node) => { setSelected(new Set([node[rowKey]])); onSelectionChange && onSelectionChange([node[rowKey]]); };\n\n const cellValue = (node, key) => (hasChildren(node) && key === summaryColumn ? leafSum(node, key) : node[key]);\n\n return (\n
\n \n \n \n {selectable && }\n {columns.map(col => (\n \n ))}\n \n \n \n {visibleList.map(({ node, depth }) => (\n onRowClick(node)}>\n {selectable && (\n \n )}\n {columns.map(col => (\n \n ))}\n \n ))}\n \n {summaryColumn && (\n \n \n {selectable && \n {columns.slice(1).map(col => (\n \n ))}\n \n \n )}\n
\n el && (el.indeterminate = someSelected)} onChange={toggleAll} />\n {col.title}
\n e.stopPropagation()} onChange={() => toggleCheck(node)} />\n \n {col.key === columns[0].key ? (\n
\n {hasChildren(node) ? (\n { e.stopPropagation(); toggleExpand(node[rowKey]); }}>▸\n ) : }\n {node[col.key]}\n {hasChildren(node) && summaryColumn && (汇总)}\n
\n ) : cellValue(node, col.key)}\n
}\n {summary.label || '合计'}\n {col.key === summaryColumn ? totalOf(col.key) : ''}\n
\n
\n );\n}\n\n/*\n用法示例:\nimport TreeTable from './TreeTable';\nconst columns = [\n { key: 'name', title: '部门/人员' },\n { key: 'count', title: '人数', align: 'right' },\n { key: 'budget', title: '预算(万)', align: 'right' }\n];\nconst data = [{ key: 'tech', name: '技术中心', children: [{ key: 'fe', name: '前端组', count: 12, budget: 80 }] }];\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"expandabletable","name":"可展开表格 ExpandableTable","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/expandabletable.json","category":"display","specLines":["- 表格行支持展开显示额外内容","- 展开图标位于行首,默认收起","- 展开内容可为子表格、详情描述、图表","- 支持同时展开多行或仅一行","- 展开状态可记忆","- 适用于订单明细、日志详情等"],"specSummary":"- 表格行支持展开显示额外内容","contract":{"variants":["多行展开 · 子表格","单行展开 · 详情描述","默认收起"],"dims":[{"name":"expandMode","values":["multiple","single"]},{"name":"content","values":["subtable","description","chart"]},{"name":"state","values":["collapsed","expanded"]}],"unknowns":["展开状态的记忆范围(会话内或持久化)","嵌套表格的缩进规则"],"anatomy":{"icon":"展开图标位于行首","content":"展开内容:子表格 / 详情描述 / 图表","state":"展开状态可记忆"},"doNot":["展开动画时长与缓动"],"usage":["表格行支持展开显示额外内容","展开图标位于行首,默认收起","展开内容可为子表格、详情描述、图表","支持同时展开多行或仅一行","展开状态可记忆","适用于订单明细、日志详情等"],"structure":{"iconPosition":"行首","defaultState":"收起"}},"files":{"html":"../frameworks/ExpandableTable.html","css":"../frameworks/ExpandableTable.css","jsx":"../frameworks/ExpandableTable.jsx","vue2":"../frameworks/ExpandableTable.vue2.vue","vue3":"../frameworks/ExpandableTable.vue3.vue"},"sizes":{"html":2786,"css":1248,"jsx":1645,"vue2":1398,"vue3":1318},"sources":{"html":"\n\n\n \n \n Aurora Admin · ExpandableTable\n \n \n\n\n
\n

ExpandableTable 可展开表格

\n

行首展开图标可展开详情(子表格 / 描述),支持同时展开多行或仅一行。

\n\n
\n
订单明细(点击箭头展开)
\n \n \n \n
订单号客户金额状态
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin ExpandableTable 样式 — 行内展开详情 */\n.aa-exptable { width: 100%; border-collapse: collapse; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); font-size: 14px; background: #fff; border: 1px solid #E8ECF1; border-radius: 8px; overflow: hidden; }\n.aa-exptable th { text-align: left; padding: 12px 14px; background: #FAFAFA; color: #1F2329; font-weight: 600; border-bottom: 1px solid #E8ECF1; }\n.aa-exptable td { padding: 11px 14px; color: #262626; border-bottom: 1px solid #E8ECF1; }\n.aa-exptable tr:last-child td { border-bottom: none; }\n.aa-exptable tbody tr.aa-row:hover td { background: #F5F7FA; }\n.aa-exp-toggle { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border: none; background: none; cursor: pointer; color: #6E6E6E; transition: transform .2s, color .2s; }\n.aa-exp-toggle.is-open { transform: rotate(90deg); color: #2F54EB; }\n.aa-exp-panel { padding: 14px 18px 14px 48px; background: #FAFAFA; color: #595959; font-size: 13px; line-height: 1.7; }\n.aa-exp-panel .aa-exp-sub { margin-top: 8px; border: 1px dashed #E8ECF1; border-radius: 6px; padding: 10px; background: #fff; }\n.aa-exptable .is-muted { color: #6E6E6E; }\n","jsx":"import React, { useState } from 'react';\nimport './ExpandableTable.css';\n\nexport default function ExpandableTable({ data = [], columns = [], idKey = 'id' }) {\n const [openSet, setOpenSet] = useState({});\n const toggle = (id) => setOpenSet((s) => ({ ...s, [id]: !s[id] }));\n\n return (\n \n \n \n \n ))}\n \n \n \n {data.map((row) => (\n \n \n \n {columns.map((c) => (\n \n ))}\n \n {openSet[row[idKey]] && (\n \n \n \n )}\n \n ))}\n \n
\n {columns.map((c) => (\n {c.title}
\n toggle(row[idKey])}\n >\n ▶\n \n {row[c.key]}
\n \n
\n
备注:{row.detail.note}
\n
\n {row.detail.items.map((it, i) => (\n
· {it}
\n ))}\n
\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"mergedcelltable","name":"合并单元格表格 MergedCellTable","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/mergedcelltable.json","category":"display","specLines":["- 支持按字段合并相同内容单元格","- 合并规则可配置,纵向合并为主","- 合并后单元格垂直居中","- 表头支持多级合并","- 适用于报表、统计表格"],"specSummary":"- 支持按字段合并相同内容单元格","contract":{"variants":["纵向合并","多级表头 + 纵向合并","横向合并"],"dims":[{"name":"mergeAxis","values":["vertical","horizontal"]},{"name":"headerLevel","values":["single","multi"]}],"unknowns":["合并列是否支持固定","合并字段的配置方式"],"anatomy":{"mergedCell":"合并单元格:垂直居中","rule":"合并规则可配置,纵向合并为主","header":"表头支持多级合并"},"doNot":["合并单元格的选中与编辑行为","排序/筛选与合并的交互规则"],"usage":["支持按字段合并相同内容单元格","合并规则可配置,纵向合并为主","合并后单元格垂直居中","表头支持多级合并","适用于报表、统计表格"],"structure":{"align":"垂直居中","defaultAxis":"纵向"}},"files":{"html":"../frameworks/MergedCellTable.html","css":"../frameworks/MergedCellTable.css","jsx":"../frameworks/MergedCellTable.jsx","vue2":"../frameworks/MergedCellTable.vue2.vue","vue3":"../frameworks/MergedCellTable.vue3.vue"},"sizes":{"html":2063,"css":1015,"jsx":1328,"vue2":1509,"vue3":1321},"sources":{"html":"\n\n\n \n \n Aurora Admin · MergedCellTable\n \n \n\n\n
\n

MergedCellTable 合并单元格表格

\n

按字段纵向合并相同内容单元格,合并后垂直居中,适用于报表 / 统计表。

\n\n
\n
部门销售额报表(部门列合并)
\n \n \n \n
部门季度销售额(万)负责人
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin MergedCellTable 样式 — 相同内容纵向合并单元格 */\n.aa-mergetable { width: 100%; border-collapse: collapse; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); font-size: 14px; background: #fff; border: 1px solid #E8ECF1; border-radius: 8px; overflow: hidden; }\n.aa-mergetable th { text-align: left; padding: 12px 14px; background: #FAFAFA; color: #1F2329; font-weight: 600; border-bottom: 1px solid #E8ECF1; }\n.aa-mergetable td { padding: 11px 14px; color: #262626; border-bottom: 1px solid #E8ECF1; border-right: 1px solid #E8ECF1; vertical-align: middle; }\n.aa-mergetable td:last-child { border-right: none; }\n.aa-mergetable tr:last-child td { border-bottom: none; }\n.aa-mergetable .aa-merged { background: #F0F5FF; font-weight: 500; color: #1F2329; text-align: center; }\n.aa-mergetable tbody tr:hover td { background: #F5F7FA; }\n.aa-mergetable tbody tr:hover .aa-merged { background: #E6ECFF; }\n.aa-mergetable .is-muted { color: #6E6E6E; }\n","jsx":"import React from 'react';\nimport './MergedCellTable.css';\n\nexport default function MergedCellTable({ data = [], columns = [] }) {\n const merged = data.map((row) => ({ ...row, _first: {}, _span: {} }));\n columns.filter((c) => c.merge).forEach((c) => {\n let i = 0;\n while (i < merged.length) {\n let j = i;\n while (j + 1 < merged.length && merged[j + 1][c.key] === merged[i][c.key]) j++;\n const span = j - i + 1;\n for (let k = i; k <= j; k++) {\n merged[k]._first[c.key] = k === i;\n merged[k]._span[c.key] = span;\n }\n i = j + 1;\n }\n });\n\n return (\n \n \n \n {columns.map((c) => (\n \n ))}\n \n \n \n {merged.map((row, idx) => (\n \n {columns.map((c) =>\n !c.merge || row._first[c.key] ? (\n \n {row[c.key]}\n \n ) : null\n )}\n \n ))}\n \n
{c.title}
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"summaryrowtable","name":"统计行表格 SummaryRowTable","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/summaryrowtable.json","category":"display","specLines":["- 表格底部或顶部显示汇总行","- 汇总类型:合计、平均值、最大值、最小值、计数","- 可配置汇总字段","- 汇总行样式加粗或浅色背景","- 支持固定汇总行"],"specSummary":"- 表格底部或顶部显示汇总行","contract":{"variants":["底部合计 · 固定","平均值","顶部最大值","计数"],"dims":[{"name":"aggregate","values":["sum","avg","max","min","count"]},{"name":"position","values":["top","bottom"]},{"name":"fixed","values":["true","false"]}],"unknowns":["加粗与浅色背景二选一的判定规则","汇总值的格式化方式"],"anatomy":{"row":"汇总行:样式加粗或浅色背景","fields":"可配置汇总字段","fixed":"支持固定汇总行"},"doNot":["多行汇总与分组汇总"],"usage":["表格底部或顶部显示汇总行","汇总类型:合计、平均值、最大值、最小值、计数","可配置汇总字段","汇总行样式加粗或浅色背景","支持固定汇总行"],"structure":{"position":"顶部或底部","style":"加粗或浅色背景"}},"files":{"html":"../frameworks/SummaryRowTable.html","css":"../frameworks/SummaryRowTable.css","jsx":"../frameworks/SummaryRowTable.jsx","vue2":"../frameworks/SummaryRowTable.vue2.vue","vue3":"../frameworks/SummaryRowTable.vue3.vue"},"sizes":{"html":2436,"css":941,"jsx":1532,"vue2":1653,"vue3":1460},"sources":{"html":"\n\n\n \n \n Aurora Admin · SummaryRowTable\n \n \n\n\n
\n

SummaryRowTable 统计汇总表格

\n

表格底部显示汇总行(合计 / 平均值 / 最大值 / 最小值 / 计数),可配置汇总字段。

\n\n
\n
销售明细与汇总
\n \n \n \n \n
产品数量单价金额
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin SummaryRowTable 样式 — 底部统计汇总行 */\n.aa-sumtable { width: 100%; border-collapse: collapse; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); font-size: 14px; background: #fff; border: 1px solid #E8ECF1; border-radius: 8px; overflow: hidden; }\n.aa-sumtable th { text-align: left; padding: 12px 14px; background: #FAFAFA; color: #1F2329; font-weight: 600; border-bottom: 1px solid #E8ECF1; }\n.aa-sumtable td { padding: 11px 14px; color: #262626; border-bottom: 1px solid #E8ECF1; }\n.aa-sumtable tr:last-child td { border-bottom: none; }\n.aa-sumtable tbody tr:hover td { background: #F5F7FA; }\n.aa-sumrow td { background: #F0F5FF; font-weight: 600; color: #1F2329; border-top: 2px solid #B7C4FF; }\n.aa-sumrow .aa-sum-label { color: #2F54EB; }\n.aa-sumtable .is-muted { color: #6E6E6E; }\n.aa-sumtable .is-pos { color: #2E7D0A; }\n.aa-sumtable .is-neg { color: #CF1322; }\n","jsx":"import React from 'react';\nimport './SummaryRowTable.css';\n\nfunction compute(data, key, type) {\n const arr = data.map((d) => Number(d[key]) || 0);\n if (!arr.length) return '';\n if (type === 'sum') return arr.reduce((a, b) => a + b, 0);\n if (type === 'avg') return +(arr.reduce((a, b) => a + b, 0) / arr.length).toFixed(1);\n if (type === 'max') return Math.max(...arr);\n if (type === 'min') return Math.min(...arr);\n if (type === 'count') return arr.length;\n return '';\n}\n\nexport default function SummaryRowTable({\n data = [],\n columns = [],\n summary = {},\n labelKey = 'name',\n labelText = '合计 / 平均'\n}) {\n return (\n \n \n \n {columns.map((c) => (\n \n ))}\n \n \n \n {data.map((row, i) => (\n \n {columns.map((c) => (\n \n ))}\n \n ))}\n \n \n \n {columns.map((c) => (\n \n ))}\n \n \n
{c.title}
= c.pos ? 'is-pos' : ''}>\n {row[c.key]}\n
\n {c.key === labelKey\n ? labelText\n : summary[c.key]\n ? compute(data, c.key, summary[c.key])\n : ''}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"fixedcolumntable","name":"固定列表格 FixedColumnTable","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/fixedcolumntable.json","category":"display","specLines":["- 表格列过多时支持左右列固定","- 固定列阴影分隔","- 横向滚动查看其他列","- 支持固定表头、固定列同时使用","- 拖拽调整列宽时固定列宽度同步","- 适用于大数据量宽表"],"specSummary":"- 表格列过多时支持左右列固定","contract":{"variants":["左固定 + 固定表头","右固定列","左右双固定"],"dims":[{"name":"fixTarget","values":["left","right","both"]},{"name":"fixedHeader","values":["true","false"]}],"unknowns":["阴影的扩散范围与颜色","最大固定列数量"],"anatomy":{"fixedCol":"固定列:阴影分隔","scroll":"横向滚动查看其他列","resize":"拖拽调整列宽时固定列宽度同步"},"doNot":["虚拟滚动实现","列拖拽排序"],"usage":["表格列过多时支持左右列固定","固定列阴影分隔","横向滚动查看其他列","支持固定表头、固定列同时使用","拖拽调整列宽时固定列宽度同步","适用于大数据量宽表"],"structure":{"divider":"阴影分隔","scroll":"横向"}},"files":{"html":"../frameworks/FixedColumnTable.html","css":"../frameworks/FixedColumnTable.css","jsx":"../frameworks/FixedColumnTable.jsx","vue2":"../frameworks/FixedColumnTable.vue2.vue","vue3":"../frameworks/FixedColumnTable.vue3.vue"},"sizes":{"html":2893,"css":1256,"jsx":899,"vue2":942,"vue3":890},"sources":{"html":"\n\n\n \n \n Aurora Admin · FixedColumnTable\n \n \n\n\n
\n

FixedColumnTable 固定列表格

\n

列过多时左右列固定,横向滚动查看其余列,固定列带阴影分隔,支持固定表头。

\n\n
\n
宽表(左固定「姓名」,右固定「操作」)
\n
\n \n \n \n
\n
\n
← 向左滚动可查看被固定的左列 / 右列 →
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin FixedColumnTable 样式 — 左右列固定 + 横向滚动 */\n.aa-fixtable-wrap { position: relative; max-width: 100%; overflow-x: auto; border: 1px solid #E8ECF1; border-radius: 8px; background: #fff; }\n.aa-fixtable { width: 100%; border-collapse: collapse; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); font-size: 14px; min-width: 760px; }\n.aa-fixtable th { text-align: left; padding: 12px 14px; background: #FAFAFA; color: #1F2329; font-weight: 600; border-bottom: 1px solid #E8ECF1; white-space: nowrap; }\n.aa-fixtable td { padding: 11px 14px; color: #262626; border-bottom: 1px solid #E8ECF1; white-space: nowrap; }\n.aa-fixtable tbody tr:hover td { background: #F5F7FA; }\n.aa-fixtable .aa-fixed { position: sticky; background: #fff; z-index: 1; }\n.aa-fixtable thead .aa-fixed { z-index: 3; }\n.aa-fixtable .aa-fixed-left { left: 0; box-shadow: 6px 0 8px -6px rgba(0,0,0,0.12); }\n.aa-fixtable .aa-fixed-right { right: 0; box-shadow: -6px 0 8px -6px rgba(0,0,0,0.12); }\n.aa-fixtable tbody tr:hover .aa-fixed { background: #F5F7FA; }\n.aa-fixtable .is-muted { color: #6E6E6E; }\n.aa-fixtable-wrap .aa-hint { padding: 8px 12px; font-size: 12px; color: #6E6E6E; border-top: 1px solid #E8ECF1; }\n","jsx":"import React from 'react';\nimport './FixedColumnTable.css';\n\nfunction fixCls(c) {\n return c.fixed ? 'aa-fixed aa-fixed-' + c.fixed : '';\n}\n\nexport default function FixedColumnTable({ data = [], columns = [] }) {\n return (\n
\n \n \n \n {columns.map((c) => (\n \n ))}\n \n \n \n {data.map((row, i) => (\n \n {columns.map((c) => (\n \n ))}\n \n ))}\n \n
\n {c.title}\n
\n {row[c.key]}\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"timelinelist","name":"时间轴列表 TimelineList","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/timelinelist.json","category":"display","specLines":["- 以时间轴形式展示事件或记录","- 左侧时间线,右侧内容卡片","- 支持垂直/水平两种模式","- 节点可表示不同状态(完成、进行中、错误)","- 适用于操作日志、审批历史、动态"],"specSummary":"- 以时间轴形式展示事件或记录","contract":{"variants":["垂直 · 已完成","垂直 · 进行中","垂直 · 错误","水平时间轴"],"dims":[{"name":"orientation","values":["vertical","horizontal"]},{"name":"nodeState","values":["done","processing","error"]}],"unknowns":["三种节点状态的具体配色","水平模式下的内容卡片宽度"],"anatomy":{"axis":"左侧时间线","card":"右侧内容卡片","node":"节点表示状态(完成、进行中、错误)"},"doNot":["时间刻度与轴标签规则"],"usage":["以时间轴形式展示事件或记录","左侧时间线,右侧内容卡片","支持垂直/水平两种模式","节点可表示不同状态(完成、进行中、错误)","适用于操作日志、审批历史、动态"],"structure":{"orientation":"垂直 / 水平"}},"files":{"html":"../frameworks/TimelineList.html","css":"../frameworks/TimelineList.css","jsx":"../frameworks/TimelineList.jsx","vue2":"../frameworks/TimelineList.vue2.vue","vue3":"../frameworks/TimelineList.vue3.vue"},"sizes":{"html":2598,"css":2021,"jsx":748,"vue2":799,"vue3":740},"sources":{"html":"\n\n\n \n \n Aurora Admin · TimelineList(普通 H5)\n \n \n \n\n\n
\n \n \n
\n\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin TimelineList 样式 — 对齐 组件7.txt TimelineList:左侧时间线 + 右侧内容,节点状态 完成/进行中/错误 */\n\n.aa-timeline { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-timeline-item { position: relative; }\n.aa-timeline-node {\n position: absolute; left: 5px; top: 4px; width: 10px; height: 10px; border-radius: 50%;\n background: #fff; border: 2px solid #E8ECF1; box-sizing: border-box; z-index: 1;\n}\n.aa-timeline-line { position: absolute; left: 9px; top: 16px; bottom: -10px; width: 2px; background: #E8ECF1; }\n.aa-timeline-content { padding: 0 0 20px 28px; }\n.aa-timeline-time { font-size: 12px; color: #6E6E6E; }\n.aa-timeline-title { font-size: 14px; font-weight: 500; color: #1F2329; margin-top: 2px; }\n.aa-timeline-desc { font-size: 13px; color: #6E6E6E; margin-top: 4px; line-height: 1.5; }\n\n/* 状态:完成 done / 进行中 active / 错误 error / 待处理 pending */\n.aa-timeline-item.is-done > .aa-timeline-node { background: #2F54EB; border-color: #2F54EB; }\n.aa-timeline-item.is-done > .aa-timeline-line { background: #2F54EB; }\n.aa-timeline-item.is-active > .aa-timeline-node { background: #fff; border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-timeline-item.is-error > .aa-timeline-node { background: #CF1322; border-color: #CF1322; }\n.aa-timeline-item.is-error > .aa-timeline-line { background: #CF1322; }\n.aa-timeline-item.is-pending > .aa-timeline-node { background: #fff; border-color: #E8ECF1; }\n\n/* 水平模式 */\n.aa-timeline.is-horizontal { display: flex; }\n.aa-timeline.is-horizontal > .aa-timeline-item { flex: 1; padding-right: 8px; }\n.aa-timeline.is-horizontal > .aa-timeline-item > .aa-timeline-node { top: 4px; }\n.aa-timeline.is-horizontal > .aa-timeline-item > .aa-timeline-line { left: 15px; top: 9px; bottom: auto; right: 0; width: auto; height: 2px; }\n.aa-timeline.is-horizontal > .aa-timeline-item > .aa-timeline-content { padding: 22px 12px 0 0; }\n","jsx":"import React from 'react';\nimport './TimelineList.css';\n\nexport default function TimelineList({ items = [], direction = 'vertical' }) {\n return (\n
\n {items.map((it, i) => (\n
\n \n \n
\n
{it.time}
\n
{it.title}
\n {it.desc ?
{it.desc}
: null}\n
\n
\n ))}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"steplist","name":"步骤列表 StepList","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/steplist.json","category":"display","specLines":["- 展示多步骤流程的详细列表","- 每步骤显示序号、标题、描述、状态图标","- 步骤之间用连接线串联","- 支持点击步骤跳转详情","- 适用于配置向导、任务进度"],"specSummary":"- 展示多步骤流程的详细列表","contract":{"variants":["水平步骤条 · 已完成","水平步骤条 · 进行中","垂直步骤列表 · 待处理"],"dims":[{"name":"orientation","values":["horizontal","vertical"]},{"name":"state","values":["done","processing","waiting"]}],"unknowns":["三种状态的具体配色","可点击步骤的交互反馈样式"],"anatomy":{"index":"序号","title":"标题","desc":"描述","icon":"状态图标","connector":"步骤之间用连接线串联"},"doNot":["步骤的图标库与插画"],"usage":["展示多步骤流程的详细列表","每步骤显示序号、标题、描述、状态图标","步骤之间用连接线串联","支持点击步骤跳转详情","适用于配置向导、任务进度"],"structure":{"connector":"步骤间连接线","items":"序号 + 标题 + 描述 + 状态图标"}},"files":{"html":"../frameworks/StepList.html","css":"../frameworks/StepList.css","jsx":"../frameworks/StepList.jsx","vue2":"../frameworks/StepList.vue2.vue","vue3":"../frameworks/StepList.vue3.vue"},"sizes":{"html":2674,"css":2128,"jsx":1012,"vue2":1066,"vue3":1025},"sources":{"html":"\n\n\n \n \n Aurora Admin · StepList(普通 H5)\n \n \n \n\n\n
\n \n \n
\n\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin StepList 样式 — 对齐 组件7.txt StepList:多步骤详细列表,序号/标题/描述/状态图标,连接线串联,可点击 */\n\n.aa-steplist { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-steplist-item { position: relative; display: flex; gap: 12px; padding-bottom: 20px; }\n.aa-steplist-line { position: absolute; left: 14px; top: 32px; bottom: 0; width: 2px; background: #E8ECF1; }\n.aa-steplist-item:last-child { padding-bottom: 0; }\n.aa-steplist-item:last-child .aa-steplist-line { display: none; }\n.aa-steplist-node { width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0; display: flex; align-items: center;\n justify-content: center; font-size: 13px; font-weight: 500; border: 2px solid #E8ECF1; background: #fff; color: #6E6E6E; z-index: 1; box-sizing: border-box; }\n.aa-steplist-body { flex: 1; padding-top: 2px; }\n.aa-steplist-title { font-size: 14px; font-weight: 500; color: #1F2329; }\n.aa-steplist-desc { font-size: 13px; color: #6E6E6E; margin-top: 2px; line-height: 1.5; }\n.aa-steplist-item.is-clickable { cursor: pointer; }\n.aa-steplist-item.is-clickable:hover .aa-steplist-title { color: #2F54EB; }\n\n.aa-steplist-item.is-done .aa-steplist-node { background: #2F54EB; border-color: #2F54EB; color: #fff; }\n.aa-steplist-item.is-done .aa-steplist-line { background: #2F54EB; }\n.aa-steplist-item.is-active .aa-steplist-node { border-color: #2F54EB; color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-steplist-item.is-error .aa-steplist-node { background: #CF1322; border-color: #CF1322; color: #fff; }\n.aa-steplist-item.is-error .aa-steplist-line { background: #CF1322; }\n\n/* 水平模式 */\n.aa-steplist.is-horizontal { display: flex; }\n.aa-steplist.is-horizontal > .aa-steplist-item { flex: 1; flex-direction: column; padding-bottom: 0; padding-right: 8px; }\n.aa-steplist.is-horizontal > .aa-steplist-item > .aa-steplist-line { left: 28px; top: 14px; bottom: auto; right: 0; width: auto; height: 2px; }\n.aa-steplist.is-horizontal > .aa-steplist-item > .aa-steplist-body { padding-top: 8px; }\n","jsx":"import React from 'react';\nimport './StepList.css';\n\nfunction nodeText(it, i) {\n if (it.status === 'done') return '✓';\n if (it.status === 'error') return '✕';\n return String(i + 1);\n}\n\nexport default function StepList({ steps = [], direction = 'vertical', clickable = false, onStepClick }) {\n return (\n
\n {steps.map((it, i) => (\n onStepClick && onStepClick(i) : undefined}\n >\n {nodeText(it, i)}\n \n
\n
{it.title}
\n {it.desc ?
{it.desc}
: null}\n
\n
\n ))}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"chartpanel","name":"图表面板 ChartPanel","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/chartpanel.json","category":"display","specLines":["- 可折叠的图表容器","- 标题栏带展开/折叠、刷新、导出按钮","- 图表区域可自定义高度","- 支持图表切换类型(折线、柱状、饼图)","- 适用于数据看板"],"specSummary":"- 可折叠的图表容器","contract":{"variants":["折线图面板","柱状图面板","饼图面板","折叠态"],"dims":[{"name":"chartType","values":["line","bar","pie"]},{"name":"state","values":["expanded","collapsed"]}],"unknowns":["图表区域默认高度","刷新时的加载态表现"],"anatomy":{"header":"标题栏带展开/折叠、刷新、导出按钮","body":"图表区域可自定义高度","switch":"支持图表切换类型(折线、柱状、饼图)"},"doNot":["图表内部的坐标系与图例规范","导出文件的格式"],"usage":["可折叠的图表容器","标题栏带展开/折叠、刷新、导出按钮","图表区域可自定义高度","支持图表切换类型(折线、柱状、饼图)","适用于数据看板"],"structure":{"headerActions":"展开/折叠、刷新、导出","bodyHeight":"可自定义"}},"files":{"html":"../frameworks/ChartPanel.html","css":"../frameworks/ChartPanel.css","jsx":"../frameworks/ChartPanel.jsx","vue2":"../frameworks/ChartPanel.vue2.vue","vue3":"../frameworks/ChartPanel.vue3.vue"},"sizes":{"html":1555,"css":1057,"jsx":960,"vue2":963,"vue3":987},"sources":{"html":"\n\n\n \n \n Aurora Admin · ChartPanel(普通 H5)\n \n \n \n\n\n
\n
\n 访问趋势\n
\n \n \n \n
\n
\n
\n
图表区域(可放置折线/柱状/饼图)
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin ChartPanel 样式 — 对齐 组件7.txt ChartPanel:可折叠图表容器,标题栏带展开/折叠、刷新、导出 */\n\n.aa-chartpanel { background: #fff; border: 1px solid #E8ECF1; border-radius: 8px; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-chartpanel-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid #E8ECF1; }\n.aa-chartpanel-title { font-size: 14px; font-weight: 500; color: #1F2329; }\n.aa-chartpanel-tools { display: flex; gap: 8px; }\n.aa-chartpanel-tool { border: none; background: none; cursor: pointer; color: #595959; font-size: 13px; padding: 4px 8px; border-radius: 4px; }\n.aa-chartpanel-tool:hover { background: #F0F5FF; color: #2F54EB; }\n.aa-chartpanel-caret { transition: transform .2s; }\n.aa-chartpanel.is-collapsed .aa-chartpanel-caret { transform: rotate(-90deg); }\n.aa-chartpanel.is-collapsed .aa-chartpanel-body { display: none; }\n.aa-chartpanel-body { padding: 16px; }\n","jsx":"import React, { useState } from 'react';\nimport './ChartPanel.css';\n\nexport default function ChartPanel({ title = '图表', collapsible = true, collapsed = false, onRefresh, onExport, children }) {\n const [isCollapsed, setIsCollapsed] = useState(collapsed);\n return (\n
\n
\n {title}\n
\n \n \n {collapsible ? (\n \n ) : null}\n
\n
\n
{children || '图表区域'}
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"dashboardcard","name":"仪表板卡片 DashboardCard","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/dashboardcard.json","category":"display","specLines":["- 用于数据看板中的卡片单元","- 支持拖拽调整大小和位置","- 可配置刷新频率","- 卡片可最大化、刷新、删除","- 布局可保存为模板"],"specSummary":"- 用于数据看板中的卡片单元","contract":{"variants":["可拖拽调整大小和位置","最大化","可配置刷新频率","删除 + 布局保存为模板"],"dims":[{"name":"action","values":["draggable","maximize","refresh","remove"]},{"name":"layout","values":["default","template-saved"]}],"unknowns":["拖拽时的占位与吸附行为","刷新频率的默认值与可选项"],"anatomy":{"card":"看板中的卡片单元","actions":"可最大化、刷新、删除","layout":"支持拖拽调整大小和位置,布局可保存为模板"},"doNot":["看板栅格的列数与断点","模板的存储格式"],"usage":["用于数据看板中的卡片单元","支持拖拽调整大小和位置","可配置刷新频率","卡片可最大化、刷新、删除","布局可保存为模板"],"structure":{"refresh":"可配置刷新频率","layoutPersist":"可保存为模板"}},"files":{"html":"../frameworks/DashboardCard.html","css":"../frameworks/DashboardCard.css","jsx":"../frameworks/DashboardCard.jsx","vue2":"../frameworks/DashboardCard.vue2.vue","vue3":"../frameworks/DashboardCard.vue3.vue"},"sizes":{"html":1074,"css":899,"jsx":529,"vue2":696,"vue3":648},"sources":{"html":"\n\n\n \n \n Aurora Admin · DashboardCard(普通 H5)\n \n \n \n\n\n
\n
今日订单📦
\n
1,284
\n
↑ 12.5% 较昨日
\n
\n
\n
退款率↩️
\n
2.3%
\n
↑ 0.4% 需关注
\n
\n\n\n","css":"/* Aurora Admin DashboardCard 样式 — 对齐 组件7.txt DashboardCard:数据看板卡片单元,标题/数值/趋势 */\n\n.aa-dashcard { background: #fff; border: 1px solid #E8ECF1; border-radius: 8px; padding: 16px; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-dashcard-head { display: flex; align-items: center; justify-content: space-between; }\n.aa-dashcard-title { font-size: 13px; color: #6E6E6E; }\n.aa-dashcard-icon { width: 32px; height: 32px; border-radius: 8px; background: #F0F5FF; color: #2F54EB; display: flex; align-items: center; justify-content: center; font-size: 16px; }\n.aa-dashcard-value { font-size: 28px; font-weight: 600; color: #1F2329; margin-top: 8px; }\n.aa-dashcard-trend { font-size: 12px; margin-top: 4px; }\n.aa-dashcard-trend.up { color: #2E7D0A; }\n.aa-dashcard-trend.down { color: #CF1322; }\n","jsx":"import React from 'react';\nimport './DashboardCard.css';\n\nexport default function DashboardCard({ title = '', value = '', icon = '📊', trend = 'up', trendText = '' }) {\n return (\n
\n
\n {title}\n {icon}\n
\n
{value}
\n
{trendText}
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"enhancedtabnav","name":"标签页导航增强 EnhancedTabNav","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/enhancedtabnav.json","category":"navigation","specLines":["- 支持浏览器标签式导航","- 标签可拖拽排序、右键菜单(关闭、关闭其他、关闭全部)","- 当前标签高亮,未保存状态显示圆点","- 标签可固定","- 与路由联动,点击标签切换页面"],"specSummary":"- 支持浏览器标签式导航","contract":{"variants":["当前标签","非当前标签","未保存(圆点标记)","已固定 + 可拖拽"],"dims":[{"name":"state","values":["active","inactive","unsaved","pinned"]},{"name":"feature","values":["draggable","contextmenu"]}],"unknowns":["最多同时打开的标签数","关闭未保存标签的二次确认"],"anatomy":{"tab":"浏览器标签式导航","active":"当前标签高亮","unsaved":"未保存状态显示圆点","menu":"右键菜单:关闭、关闭其他、关闭全部","route":"与路由联动,点击切换页面"},"doNot":["标签溢出时的滚动与下拉列表"],"usage":["支持浏览器标签式导航","标签可拖拽排序、右键菜单(关闭、关闭其他、关闭全部)","当前标签高亮,未保存状态显示圆点","标签可固定","与路由联动,点击标签切换页面"],"structure":{"interaction":"拖拽排序 + 右键菜单"}},"files":{"html":"../frameworks/EnhancedTabNav.html","css":"../frameworks/EnhancedTabNav.css","jsx":"../frameworks/EnhancedTabNav.jsx","vue2":"../frameworks/EnhancedTabNav.vue2.vue","vue3":"../frameworks/EnhancedTabNav.vue3.vue"},"sizes":{"html":4734,"css":1643,"jsx":3905,"vue2":3582,"vue3":3395},"sources":{"html":"\n\n\n \n \n Aurora Admin · EnhancedTabNav\n \n \n\n\n
\n

EnhancedTabNav 增强页签导航

\n

浏览器标签式导航:可拖拽排序、右键菜单(关闭 / 关闭其他 / 关闭全部),未保存状态显示圆点。

\n\n
\n
\n
\n
\n
\n

拖拽标签可排序;右键标签打开菜单;带 ● 表示未保存。

\n
\n
\n\n \n\n\n","css":"/* Aurora Admin EnhancedTabNav 样式 — 可拖拽 + 右键菜单的页签导航 */\n.aa-tabs-enh { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-tabs-enh-bar { display: flex; gap: 2px; border-bottom: 1px solid #E8ECF1; flex-wrap: wrap; }\n.aa-tabs-enh-tab { display: flex; align-items: center; gap: 6px; padding: 8px 12px; border: 1px solid #E8ECF1; border-bottom: none; border-radius: 6px 6px 0 0; background: #FAFAFA; cursor: pointer; font-size: 13px; color: #595959; user-select: none; transition: background .15s; }\n.aa-tabs-enh-tab:hover { background: #F0F5FF; }\n.aa-tabs-enh-tab.is-active { background: #fff; color: #2F54EB; }\n.aa-tabs-enh-tab.is-active::after { content: ''; position: absolute; }\n.aa-tabs-enh-tab .aa-dot { width: 6px; height: 6px; border-radius: 50%; background: #8C5A00; }\n.aa-tabs-enh-close { color: #6E6E6E; border-radius: 50%; width: 16px; height: 16px; line-height: 14px; text-align: center; font-size: 14px; }\n.aa-tabs-enh-close:hover { background: #F5F5F5; color: #CF1322; }\n.aa-tabs-enh-panel { padding: 16px 18px; border: 1px solid #E8ECF1; border-top: none; border-radius: 0 0 8px 8px; color: #595959; font-size: 14px; line-height: 1.7; }\n.aa-tabs-enh-menu { position: fixed; z-index: 1000; background: #fff; border: 1px solid #E8ECF1; border-radius: 6px; box-shadow: 0 6px 24px rgba(0,0,0,0.12); padding: 4px 0; min-width: 124px; }\n.aa-tabs-enh-menu div { padding: 7px 14px; font-size: 13px; cursor: pointer; color: #262626; }\n.aa-tabs-enh-menu div:hover { background: #F5F7FA; }\n.aa-tabs-enh-hint { font-size: 12px; color: #6E6E6E; margin-top: 10px; }\n","jsx":"import React, { useState, useRef, useEffect } from 'react';\nimport './EnhancedTabNav.css';\n\nconst INITIAL = [\n { id: 'u', title: '用户管理', dirty: false, desc: '用户列表与权限配置。' },\n { id: 'r', title: '角色管理', dirty: true, desc: '角色权限分配(有未保存修改)。' },\n { id: 'm', title: '菜单管理', dirty: false, desc: '导航菜单树形配置。' },\n { id: 'p', title: '参数配置', dirty: true, desc: '系统参数键值对管理。' },\n { id: 'l', title: '日志查询', dirty: false, desc: '操作日志检索与导出。' }\n];\n\nexport default function EnhancedTabNav() {\n const [tabs, setTabs] = useState(INITIAL);\n const [activeId, setActiveId] = useState('u');\n const [dragId, setDragId] = useState(null);\n const [menu, setMenu] = useState(null);\n const [menuId, setMenuId] = useState('');\n const root = useRef(null);\n\n useEffect(() => {\n function onDoc(e) {\n if (root.current && !root.current.contains(e.target)) setMenu(null);\n }\n document.addEventListener('click', onDoc);\n return () => document.removeEventListener('click', onDoc);\n }, []);\n\n const activeTab = tabs.filter((t) => t.id === activeId)[0] || null;\n\n const closeTab = (id) => {\n setTabs((ts) => {\n if (ts.length <= 1) return ts;\n const idx = ts.findIndex((t) => t.id === id);\n const next = ts.slice();\n next.splice(idx, 1);\n if (activeId === id) setActiveId(next[Math.max(0, idx - 1)].id);\n return next;\n });\n };\n const onTabContext = (e, id) => {\n e.preventDefault();\n setMenu({ x: e.clientX, y: e.clientY });\n setMenuId(id);\n };\n const onBarContext = (e) => {\n if (e.target.closest('.aa-tabs-enh-tab')) e.preventDefault();\n };\n const menuAct = (act) => {\n if (act === 'close') closeTab(menuId);\n else if (act === 'other') setTabs((ts) => ts.filter((t) => t.id === menuId));\n else if (act === 'all') setTabs([]);\n setActiveId(tabs.length ? menuId : '');\n setMenu(null);\n };\n const onDrop = (e) => {\n const el = e.target.closest('.aa-tabs-enh-tab');\n const id = el && el.dataset.id;\n if (!id || !dragId) return;\n setTabs((ts) => {\n const from = ts.findIndex((t) => t.id === dragId);\n const to = ts.findIndex((t) => t.id === id);\n if (from < 0 || to < 0 || from === to) return ts;\n const next = ts.slice();\n const moved = next.splice(from, 1)[0];\n next.splice(to, 0, moved);\n return next;\n });\n };\n\n return (\n
\n
e.preventDefault()} onDrop={onDrop}>\n {tabs.map((t) => (\n setActiveId(t.id)}\n onDragStart={() => setDragId(t.id)}\n onContextMenu={(e) => onTabContext(e, t.id)}\n >\n {t.dirty && }\n {t.title}\n { e.stopPropagation(); closeTab(t.id); }}>×\n
\n ))}\n
\n
{activeTab ? activeTab.title + ':' + activeTab.desc : '请选择页签'}
\n {menu && (\n
e.stopPropagation()}>\n
menuAct('close')}>关闭
\n
menuAct('other')}>关闭其他
\n
menuAct('all')}>关闭全部
\n
\n )}\n

拖拽标签可排序;右键标签打开菜单;带 ● 表示未保存。

\n \n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"sidemenu","name":"侧边栏菜单 SideMenu","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/sidemenu.json","category":"navigation","specLines":["- 支持多级菜单、图标、徽章","- 菜单项可折叠、展开","- 选中态背景 #F0F5FF,左侧指示条","- 支持权限过滤、外链、隐藏","- 折叠模式显示图标,悬停弹出子菜单"],"specSummary":"- 支持多级菜单、图标、徽章","contract":{"variants":["选中项(左侧指示条)","带徽章","多级菜单展开","折叠模式(仅图标)","禁用项"],"dims":[{"name":"state","values":["default","selected","disabled","hidden"]},{"name":"collapsed","values":["expanded","collapsed"]},{"name":"feature","values":["icon","badge","children","external"]}],"unknowns":["折叠宽度与展开宽度的具体像素","指示条的宽度与高度"],"anatomy":{"item":"菜单项","selected":"选中态:背景 #F0F5FF,左侧指示条","collapsed":"折叠模式:显示图标,悬停弹出子菜单","badge":"徽章"},"doNot":["权限过滤的具体规则","菜单搜索"],"usage":["支持多级菜单、图标、徽章","菜单项可折叠、展开","选中态背景 #F0F5FF,左侧指示条","支持权限过滤、外链、隐藏","折叠模式显示图标,悬停弹出子菜单"],"structure":{"selectedBg":"#F0F5FF","indicator":"左侧指示条"}},"files":{"html":"../frameworks/sidemenu.html","css":"../frameworks/SideMenu.css","jsx":"../frameworks/SideMenu.jsx","vue2":"../frameworks/SideMenu.vue2.vue","vue3":"../frameworks/SideMenu.vue3.vue"},"sizes":{"html":4741,"css":2414,"jsx":3776,"vue2":5081,"vue3":5183},"sources":{"html":"\n\n\n\n\nAurora Admin · SideMenu (H5)\n\n\n\n\n\n
\n
\n

SideMenu

\n

多级、图标、徽章、选中态+左指示条、折叠

\n
⇥ 折叠 / 展开
\n
\n
\n
\n\n\n\n\n","css":"/* Aurora Admin SideMenu 样式 — 对齐 组件7.txt SideMenu:多级、图标、徽章、选中态#F0F5FF+左指示条、折叠 */\n\n.aa-sidemenu { width: 220px; background: #fff; border-right: 1px solid #E8ECF1; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; user-select: none; }\n.aa-sidemenu.collapsed { width: 56px; }\n\n.aa-menu-item { position: relative; display: flex; align-items: center; gap: 8px; height: 44px; padding: 0 16px; cursor: pointer; font-size: 14px; color: #262626; transition: background .15s, color .15s; }\n.aa-menu-item:hover { background: #F5F7FA; }\n.aa-menu-item.is-active { background: #F0F5FF; color: #2F54EB; font-weight: 500; }\n.aa-menu-item.is-active::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: #2F54EB; }\n.aa-menu-item.is-disabled { color: #BFBFBF; cursor: not-allowed; }\n.aa-menu-item.is-disabled:hover { background: none; }\n\n.aa-menu-icon { width: 16px; text-align: center; flex-shrink: 0; font-size: 15px; }\n.aa-menu-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.aa-menu-badge { background: #CF1322; color: #fff; font-size: 11px; min-width: 16px; height: 16px; border-radius: 8px; padding: 0 5px; display: inline-flex; align-items: center; justify-content: center; }\n.aa-menu-arrow { color: #6E6E6E; font-size: 12px; transition: transform .15s; }\n.aa-menu-item.is-open > .aa-menu-arrow { transform: rotate(90deg); }\n\n.aa-menu-children { background: #FAFAFA; }\n.aa-menu-children .aa-menu-item { height: 40px; padding-left: 40px; font-size: 13px; }\n.aa-menu-children .aa-menu-item.is-active::before { display: none; }\n\n/* 折叠模式 */\n.aa-sidemenu.collapsed .aa-menu-label,\n.aa-sidemenu.collapsed .aa-menu-badge,\n.aa-sidemenu.collapsed .aa-menu-arrow { display: none; }\n.aa-sidemenu.collapsed .aa-menu-item { justify-content: center; padding: 0; }\n.aa-sidemenu.collapsed .aa-menu-children { display: none; }\n\n/* 折叠态悬浮浮层 */\n.aa-menu-pop { position: absolute; left: 56px; top: 0; min-width: 180px; background: #fff; border: 1px solid #E8ECF1; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); padding: 4px 0; z-index: 200; }\n.aa-menu-pop-title { padding: 6px 16px; font-size: 12px; color: #6E6E6E; }\n.aa-menu-pop .aa-menu-item { padding: 0 16px; }\n.aa-menu-pop .aa-menu-item::before { display: none; }\n","jsx":"import React, { useState } from 'react';\nimport './SideMenu.css';\n\n/**\n * Aurora Admin SideMenu(React)\n * 对齐 组件7.txt SideMenu 规范:多级、图标、徽章、选中态#F0F5FF+左指示条、折叠悬浮\n *\n * props:\n * - value / defaultValue : 当前选中 key\n * - items : [{ key, label, icon?, badge?, children?, disabled? }]\n * - collapsed\n * - onSelect(key)\n */\nexport default function SideMenu({ value, defaultValue = '', items = [], collapsed = false, onSelect }) {\n const [active, setActive] = useState(value !== undefined ? value : defaultValue);\n const [openKeys, setOpenKeys] = useState([]);\n const [hoverKey, setHoverKey] = useState('');\n const [popTop, setPopTop] = useState(0);\n const current = value !== undefined ? value : active;\n\n const select = (key) => { if (value === undefined) setActive(key); onSelect && onSelect(key); setHoverKey(''); };\n const onTopClick = (it) => {\n if (it.disabled) return;\n if (it.children && it.children.length) {\n if (collapsed) return;\n setOpenKeys(openKeys.includes(it.key) ? openKeys.filter(k => k !== it.key) : [...openKeys, it.key]);\n } else select(it.key);\n };\n const onHover = (it, e) => { if (collapsed && it.children) { setHoverKey(it.key); setPopTop(e.currentTarget.offsetTop); } };\n const popItem = items.find(x => x.key === hoverKey) || null;\n\n return (\n
setHoverKey('')}>\n {items.map(it => {\n const hasChild = it.children && it.children.length;\n const isOpen = openKeys.includes(it.key);\n return (\n
\n onTopClick(it)}\n onMouseEnter={(e) => onHover(it, e)}\n >\n {it.icon}\n {it.label}\n {it.badge && {it.badge}}\n {hasChild && ›}\n
\n {hasChild && isOpen && !collapsed && (\n
\n {it.children.map(c => (\n
select(c.key)}>\n {c.label}\n
\n ))}\n
\n )}\n
\n );\n })}\n\n {collapsed && hoverKey && popItem && (\n
\n
{popItem.label}
\n {popItem.children && popItem.children.length ? (\n popItem.children.map(c => (\n
select(c.key)}>\n {c.label}\n
\n ))\n ) : (\n
select(popItem.key)}>\n {popItem.label}\n
\n )}\n
\n )}\n \n );\n}\n\n/*\n用法示例:\nimport SideMenu from './SideMenu';\nconst items = [\n { key: 'dashboard', label: '仪表盘', icon: '▦' },\n { key: 'user', label: '用户管理', icon: '◍', badge: 8, children: [{ key: 'list', label: '用户列表' }] }\n];\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"topmenu","name":"顶部菜单 TopMenu","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/topmenu.json","category":"navigation","specLines":["- 水平导航菜单","- 支持多级下拉","- 选中态底部 2px 品牌色指示条","- 菜单项可带图标、徽章","- 适用于顶部导航布局"],"specSummary":"- 水平导航菜单","contract":{"variants":["选中项(底部指示条)","默认项","带徽章","多级下拉"],"dims":[{"name":"state","values":["default","selected","disabled"]},{"name":"feature","values":["icon","badge","children"]}],"unknowns":["菜单项的水平内边距","下拉面板与侧边菜单是否共用样式"],"anatomy":{"item":"水平菜单项","selected":"选中态:底部 2px 品牌色指示条","dropdown":"支持多级下拉"},"doNot":["响应式折叠为汉堡菜单的断点"],"usage":["水平导航菜单","支持多级下拉","选中态底部 2px 品牌色指示条","菜单项可带图标、徽章","适用于顶部导航布局"],"structure":{"indicatorHeight":"2px","indicatorColor":"品牌色"}},"files":{"html":"../frameworks/TopMenu.html","css":"../frameworks/TopMenu.css","jsx":"../frameworks/TopMenu.jsx","vue2":"../frameworks/TopMenu.vue2.vue","vue3":"../frameworks/TopMenu.vue3.vue"},"sizes":{"html":2277,"css":1497,"jsx":1360,"vue2":1190,"vue3":1118},"sources":{"html":"\n\n\n \n \n Aurora Admin · TopMenu(普通 H5)\n \n \n\n\n
\n\n \n\n\n","css":"/* Aurora Admin TopMenu 样式 — 对齐 组件7.txt TopMenu:水平导航,选中态底部 2px 品牌色指示条,支持多级下拉 */\n\n.aa-topmenu { display: flex; background: #fff; border-bottom: 1px solid #E8ECF1; padding: 0 8px;\n font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-topmenu-item { position: relative; display: flex; align-items: center; gap: 6px; height: 48px;\n padding: 0 16px; cursor: pointer; color: #262626; font-size: 14px; white-space: nowrap; }\n.aa-topmenu-item:hover { color: #2F54EB; }\n.aa-topmenu-item.is-active { color: #2F54EB; font-weight: 500; }\n.aa-topmenu-item.is-active::after { content: ''; position: absolute; left: 12px; right: 12px; bottom: 0; height: 2px; background: #2F54EB; }\n.aa-topmenu-badge { background: #CF1322; color: #fff; font-size: 11px; line-height: 1; border-radius: 8px; padding: 2px 5px; }\n.aa-topmenu-caret { font-size: 10px; color: #6E6E6E; }\n.aa-topmenu-dropdown { position: absolute; top: 48px; left: 0; min-width: 160px; background: #fff;\n border: 1px solid #E8ECF1; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); padding: 4px; display: none; z-index: 50; }\n.aa-topmenu-item:hover > .aa-topmenu-dropdown { display: block; }\n.aa-topmenu-sub { display: block; padding: 8px 12px; font-size: 13px; color: #262626; border-radius: 4px; }\n.aa-topmenu-sub:hover { background: #F0F5FF; color: #2F54EB; }\n.aa-topmenu-sub.is-active { color: #2F54EB; background: #F0F5FF; }\n","jsx":"import React, { useState } from 'react';\nimport './TopMenu.css';\n\nexport default function TopMenu({ items = [], activeKey = '', onChange }) {\n const [active, setActive] = useState(activeKey);\n\n function isActive(it) {\n if (it.key === active) return true;\n return (it.children || []).some(s => s.key === active);\n }\n function select(it) {\n if (!it.children) {\n setActive(it.key);\n onChange && onChange(it.key);\n }\n }\n\n return (\n
\n {items.map((it, i) => (\n select(it)}\n >\n {it.label}\n {it.badge ? {it.badge} : null}\n {it.children ? ▾ : null}\n {it.children ? (\n
\n {it.children.map((s, si) => (\n { e.stopPropagation(); select(s); }}\n >\n {s.label}\n \n ))}\n
\n ) : null}\n
\n ))}\n \n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"mixednavigation","name":"混合导航 MixedNavigation","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/mixednavigation.json","category":"navigation","specLines":["- 顶部一级菜单 + 侧边栏二级菜单","- 顶部菜单切换时侧边栏内容变化","- 支持权限过滤","- 菜单状态记忆","- 适用于复杂后台"],"specSummary":"- 顶部一级菜单 + 侧边栏二级菜单","contract":{"variants":["一级选中 + 二级选中","一级选中 + 二级默认","未选中的一级(二级隐藏)"],"dims":[{"name":"level1State","values":["active","default"]},{"name":"level2State","values":["active","default","hidden"]}],"unknowns":["侧边栏的默认宽度","状态记忆的持久化范围"],"anatomy":{"top":"顶部一级菜单","side":"侧边栏二级菜单","sync":"顶部菜单切换时侧边栏内容变化","memory":"菜单状态记忆"},"doNot":["三级及以上菜单的呈现","顶部菜单溢出处理"],"usage":["顶部一级菜单 + 侧边栏二级菜单","顶部菜单切换时侧边栏内容变化","支持权限过滤","菜单状态记忆","适用于复杂后台"],"structure":{"layout":"顶部一级 + 侧边二级"}},"files":{"html":"../frameworks/MixedNavigation.html","css":"../frameworks/MixedNavigation.css","jsx":"../frameworks/MixedNavigation.jsx","vue2":"../frameworks/MixedNavigation.vue2.vue","vue3":"../frameworks/MixedNavigation.vue3.vue"},"sizes":{"html":2816,"css":1353,"jsx":1829,"vue2":1716,"vue3":1672},"sources":{"html":"\n\n\n \n \n Aurora Admin · MixedNavigation\n \n \n\n\n
\n

MixedNavigation 混合导航

\n

顶部一级菜单 + 侧边栏二级菜单,切换顶部项时侧边栏内容联动变化。

\n\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin MixedNavigation 样式 — 顶部一级 + 侧边二级混合导航 */\n.aa-mixed { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); border: 1px solid #E8ECF1; border-radius: 8px; overflow: hidden; background: #fff; }\n.aa-mixed-top { display: flex; gap: 4px; background: #fff; border-bottom: 1px solid #E8ECF1; padding: 0 8px; }\n.aa-mixed-topitem { padding: 12px 16px; cursor: pointer; color: #262626; font-size: 14px; border-bottom: 2px solid transparent; transition: color .15s, border-color .15s; }\n.aa-mixed-topitem:hover { color: #2F54EB; }\n.aa-mixed-topitem.is-active { color: #2F54EB; border-bottom-color: #2F54EB; font-weight: 600; }\n.aa-mixed-body { display: flex; min-height: 240px; }\n.aa-mixed-side { width: 168px; background: #FAFAFA; border-right: 1px solid #E8ECF1; padding: 8px 0; }\n.aa-mixed-sideitem { padding: 10px 16px; cursor: pointer; color: #262626; font-size: 14px; border-left: 3px solid transparent; transition: background .15s, color .15s; }\n.aa-mixed-sideitem:hover { background: #F5F7FA; }\n.aa-mixed-sideitem.is-active { background: #F0F5FF; color: #2F54EB; border-left-color: #2F54EB; font-weight: 500; }\n.aa-mixed-content { flex: 1; padding: 18px 22px; color: #595959; font-size: 14px; line-height: 1.7; }\n.aa-mixed-content h4 { margin: 0 0 6px; color: #1F2329; }\n","jsx":"import React, { useState } from 'react';\nimport './MixedNavigation.css';\n\nconst DEFAULT_MENU = [\n { name: '工作台', items: [{ name: '概览', desc: '数据看板总览' }, { name: '待办', desc: '我的待处理任务' }, { name: '报表', desc: '统计报表入口' }] },\n { name: '系统管理', items: [{ name: '用户管理', desc: '用户增删改查' }, { name: '角色管理', desc: '角色与权限分配' }, { name: '菜单管理', desc: '导航菜单配置' }] },\n { name: '业务中心', items: [{ name: '订单', desc: '订单列表与详情' }, { name: '商品', desc: '商品上下架' }, { name: '库存', desc: '库存预警' }] }\n];\n\nexport default function MixedNavigation({ menu = DEFAULT_MENU }) {\n const [topIdx, setTopIdx] = useState(0);\n const [sideIdx, setSideIdx] = useState(0);\n const cur = menu[topIdx];\n\n return (\n
\n
\n {menu.map((m, i) => (\n {\n setTopIdx(i);\n setSideIdx(0);\n }}\n >\n {m.name}\n
\n ))}\n
\n
\n
\n {cur.items.map((it, i) => (\n setSideIdx(i)}\n >\n {it.name}\n
\n ))}\n
\n
\n

\n {cur.name} / {cur.items[sideIdx].name}\n

\n
{cur.items[sideIdx].desc}
\n
\n \n \n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"quicknav","name":"快捷导航 QuickNav","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/quicknav.json","category":"navigation","specLines":["- 常用页面快捷入口","- 可配置为图标网格、列表或下拉","- 支持拖拽排序","- 点击跳转","- 显示在工作台或侧边栏顶部"],"specSummary":"- 常用页面快捷入口","contract":{"variants":["图标网格(可拖拽排序)","列表形式","下拉形式"],"dims":[{"name":"layout","values":["grid","list","dropdown"]},{"name":"draggable","values":["true","false"]}],"unknowns":["网格的列数与图标尺寸","可见快捷项的上限"],"anatomy":{"item":"快捷入口项","placement":"显示在工作台或侧边栏顶部","action":"点击跳转"},"doNot":["快捷项的添加与删除流程"],"usage":["常用页面快捷入口","可配置为图标网格、列表或下拉","支持拖拽排序","点击跳转","显示在工作台或侧边栏顶部"],"structure":{"layouts":"网格 / 列表 / 下拉","order":"可拖拽排序"}},"files":{"html":"../frameworks/QuickNav.html","css":"../frameworks/QuickNav.css","jsx":"../frameworks/QuickNav.jsx","vue2":"../frameworks/QuickNav.vue2.vue","vue3":"../frameworks/QuickNav.vue3.vue"},"sizes":{"html":1716,"css":768,"jsx":833,"vue2":892,"vue3":884},"sources":{"html":"\n\n\n \n \n Aurora Admin · QuickNav\n \n \n\n\n
\n

QuickNav 快捷导航

\n

常用页面快捷入口,图标网格,点击跳转。

\n\n
\n
工作台快捷入口
\n
\n

点击任意入口试试

\n
\n
\n\n \n\n\n","css":"/* Aurora Admin QuickNav 样式 — 常用页面快捷入口 */\n.aa-quicknav { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); display: flex; flex-wrap: wrap; gap: 6px; }\n.aa-quicknav-item { width: 92px; display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 14px 6px; border-radius: 8px; cursor: pointer; transition: background .15s; }\n.aa-quicknav-item:hover { background: #F0F5FF; }\n.aa-quicknav-icon { width: 42px; height: 42px; border-radius: 12px; background: #F0F5FF; color: #2F54EB; display: flex; align-items: center; justify-content: center; font-size: 20px; }\n.aa-quicknav-label { font-size: 13px; color: #262626; }\n.aa-quicknav-empty { color: #6E6E6E; font-size: 13px; padding: 12px; }\n","jsx":"import React from 'react';\nimport './QuickNav.css';\n\nconst DEFAULT_ITEMS = [\n { icon: '📊', label: '数据看板' },\n { icon: '👥', label: '用户管理' },\n { icon: '📦', label: '订单管理' },\n { icon: '⚙️', label: '系统设置' },\n { icon: '📝', label: '审批中心' },\n { icon: '🔔', label: '消息通知' },\n { icon: '📁', label: '文件中心' },\n { icon: '🛡️', label: '权限管理' }\n];\n\nexport default function QuickNav({ items = DEFAULT_ITEMS, onSelect }) {\n return (\n
\n {items.map((it, i) => (\n
onSelect && onSelect(it)}>\n
{it.icon}
\n
{it.label}
\n
\n ))}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"anchornav","name":"锚点导航 AnchorNav","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/anchornav.json","category":"navigation","specLines":["- 长页面右侧固定目录","- 滚动时高亮当前章节","- 点击平滑滚动到对应位置","- 支持层级嵌套","- 适用于帮助文档、长表单"],"specSummary":"- 长页面右侧固定目录","contract":{"variants":["当前章节高亮","非当前章节","嵌套层级"],"dims":[{"name":"state","values":["active","default"]},{"name":"level","values":["h1","h2","h3"]}],"unknowns":["层级缩进量","高亮的判定偏移(offset)"],"anatomy":{"position":"长页面右侧固定目录","highlight":"滚动时高亮当前章节","action":"点击平滑滚动到对应位置","nesting":"支持层级嵌套"},"doNot":["滚动监听的实现方式","浏览器地址栏锚点同步"],"usage":["长页面右侧固定目录","滚动时高亮当前章节","点击平滑滚动到对应位置","支持层级嵌套","适用于帮助文档、长表单"],"structure":{"position":"右侧固定","scroll":"平滑滚动"}},"files":{"html":"../frameworks/anchornav.html","css":"../frameworks/AnchorNav.css","jsx":"../frameworks/AnchorNav.jsx","vue2":"../frameworks/AnchorNav.vue2.vue","vue3":"../frameworks/AnchorNav.vue3.vue"},"sizes":{"html":3681,"css":935,"jsx":2105,"vue2":2558,"vue3":2530},"sources":{"html":"\n\n\n\n\nAurora Admin · AnchorNav (H5)\n\n\n\n\n\n
\n

Aurora Admin · AnchorNav 组件(纯 H5)

\n

对齐 AnchorNav 规范:右侧目录、滚动高亮、层级嵌套、点击平滑滚动

\n\n \n\n

1. 产品简介

本系统是一套企业级中后台解决方案……(长文本占位,可滚动测试高亮)

更多内容用于撑高页面,方便观察当前章节高亮效果。

\n

2. 安装与初始化

通过 npm 安装后,在入口文件引入主题与组件库。

配置项说明略。

\n
\n

3. 开发指南

\n

包含布局、主题与国际化三部分。

\n

3.1 布局

侧边栏 + 顶栏的经典后台布局。

\n

3.2 主题

通过 CSS 变量统一调整品牌色。

\n
\n

4. 常见问题

收集部署与升级中的高频问题。

FAQ 列表略。

\n
\n\n\n\n\n","css":"/* Aurora Admin AnchorNav 样式 — 对齐 组件7.txt AnchorNav:右侧目录、滚动高亮、层级嵌套 */\n\n.aa-anchor { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); width: 160px; }\n.aa-anchor.fixed { position: fixed; right: 24px; top: 80px; }\n\n.aa-anchor-item {\n display: block; padding: 6px 12px; font-size: 13px; color: #6E6E6E; cursor: pointer;\n border-left: 2px solid #E8ECF1; line-height: 1.4; transition: color .15s, border-color .15s; background: none; border-top: none; border-right: none; border-bottom: none; text-align: left; width: 100%;\n}\n.aa-anchor-item:hover { color: #2F54EB; }\n.aa-anchor-item.is-active { color: #2F54EB; border-left-color: #2F54EB; font-weight: 500; }\n\n.aa-anchor-sub { padding-left: 8px; }\n.aa-anchor-sub .aa-anchor-item { border-left: none; padding-left: 20px; }\n.aa-anchor-sub .aa-anchor-item.is-active { border-left: 2px solid #2F54EB; }\n","jsx":"import React, { useState, useEffect } from 'react';\nimport './AnchorNav.css';\n\n/**\n * Aurora Admin AnchorNav(React)\n * 对齐 组件7.txt AnchorNav 规范:右侧目录、滚动高亮、层级嵌套、点击平滑滚动\n *\n * props:\n * - items : [{ key, label, children? }](key 对应页面元素 id)\n * - fixed\n * - offset : 滚动偏移(避开吸顶高度)\n */\nexport default function AnchorNav({ items = [], fixed = false, offset = 24 }) {\n const allKeys = [];\n (function collect(list) { list.forEach(i => { allKeys.push(i.key); if (i.children) collect(i.children); }); })(items);\n const [active, setActive] = useState(items[0] ? items[0].key : '');\n\n const go = (key) => {\n const el = document.getElementById(key);\n if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - offset, behavior: 'smooth' });\n };\n useEffect(() => {\n const onScroll = () => {\n const pos = window.scrollY + 80;\n let cur = allKeys[0];\n for (const k of allKeys) {\n const el = document.getElementById(k);\n if (el && el.offsetTop <= pos) cur = k;\n }\n setActive(cur);\n };\n window.addEventListener('scroll', onScroll);\n return () => window.removeEventListener('scroll', onScroll);\n }, []);\n\n return (\n \n );\n}\n\n/*\n用法示例:\nimport AnchorNav from './AnchorNav';\nconst items = [{ key: 'intro', label: '简介' }, { key: 'guide', label: '指南', children: [{ key: 'layout', label: '布局' }] }];\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"backtotop","name":"返回顶部 BackToTop","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/backtotop.json","category":"system","specLines":["- 页面滚动超过 400px 时出现","- 右下角悬浮按钮","- 点击平滑滚动回顶部","- 可带进度环显示滚动进度","- 支持自定义位置、样式"],"specSummary":"- 页面滚动超过 400px 时出现","contract":{"variants":["带进度环","无进度环","自定义位置"],"dims":[{"name":"progress","values":["ring","none"]},{"name":"position","values":["bottom-right","custom"]}],"unknowns":["按钮尺寸与距边距","进度环的线宽"],"anatomy":{"trigger":"页面滚动超过 400px 时出现","button":"右下角悬浮按钮","ring":"可带进度环显示滚动进度","action":"点击平滑滚动回顶部"},"doNot":["进度环的渐变配色"],"usage":["页面滚动超过 400px 时出现","右下角悬浮按钮","点击平滑滚动回顶部","可带进度环显示滚动进度","支持自定义位置、样式"],"structure":{"appearAt":"400px","position":"右下角悬浮"}},"files":{"html":"../frameworks/BackToTop.html","css":"../frameworks/BackToTop.css","jsx":"../frameworks/BackToTop.jsx","vue2":"../frameworks/BackToTop.vue2.vue","vue3":"../frameworks/BackToTop.vue3.vue"},"sizes":{"html":2341,"css":1099,"jsx":1608,"vue2":1603,"vue3":1495},"sources":{"html":"\n\n\n \n \n Aurora Admin · BackToTop(普通 H5)\n \n \n \n\n\n
\n

向下滚动页面

\n

滚动超过 400px 后右下角出现返回顶部按钮,环显示滚动进度,点击平滑回到顶部。

\n
\n
这里是长内容占位区……
\n\n
\n \n \n \n \n ↑\n
\n\n \n\n\n","css":"/* Aurora Admin BackToTop 样式 — 对齐 组件7.txt BackToTop:滚动超阈值出现,右下角悬浮,带进度环 */\n\n.aa-backtop {\n position: fixed; right: 32px; bottom: 32px; width: 44px; height: 44px;\n border-radius: 50%; background: #fff; border: 1px solid #E8ECF1;\n box-shadow: 0 4px 12px rgba(0,0,0,0.12); cursor: pointer;\n display: flex; align-items: center; justify-content: center; z-index: 1000;\n opacity: 0; transform: translateY(12px); pointer-events: none;\n transition: opacity .2s, transform .2s, background .15s;\n}\n.aa-backtop.is-visible { opacity: 1; transform: translateY(0); pointer-events: auto; }\n.aa-backtop:hover { background: #F0F5FF; }\n.aa-backtop svg { position: absolute; top: 0; left: 0; width: 44px; height: 44px; transform: rotate(-90deg); }\n.aa-backtop-ring-bg { stroke: #E8ECF1; }\n.aa-backtop-ring-fg { stroke: #2F54EB; transition: stroke-dashoffset .1s linear; }\n.aa-backtop-arrow { color: #2F54EB; font-size: 18px; line-height: 1; z-index: 1; }\n.aa-backtop.aa-no-ring .aa-backtop-ring-fg,\n.aa-backtop.aa-no-ring .aa-backtop-ring-bg { display: none; }\n","jsx":"import React, { useEffect, useState } from 'react';\nimport './BackToTop.css';\n\nexport default function BackToTop({ threshold = 400, showRing = true }) {\n const [visible, setVisible] = useState(false);\n const [progress, setProgress] = useState(0);\n const r = 20;\n const circumference = 2 * Math.PI * r;\n const offset = circumference * (1 - progress);\n\n useEffect(() => {\n function onScroll() {\n const st = window.pageYOffset || document.documentElement.scrollTop;\n const max = document.documentElement.scrollHeight - window.innerHeight;\n setProgress(max > 0 ? st / max : 0);\n setVisible(st > threshold);\n }\n window.addEventListener('scroll', onScroll, { passive: true });\n onScroll();\n return () => window.removeEventListener('scroll', onScroll);\n }, [threshold]);\n\n function toTop() {\n window.scrollTo({ top: 0, behavior: 'smooth' });\n }\n\n return (\n \n {showRing ? (\n \n \n \n \n ) : null}\n ↑\n \n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"pagetransition","name":"页面跳转 PageTransition","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/pagetransition.json","category":"system","specLines":["- 路由切换动画","- 可选淡入、滑动、缩放","- 动画时长 0.3-0.5s","- 支持前进/后退方向感知","- 减少页面跳动,提升体验"],"specSummary":"- 路由切换动画","contract":{"variants":["淡入(前进)","滑动(前进)","滑动(后退,反向)","缩放"],"dims":[{"name":"effect","values":["fade","slide","zoom"]},{"name":"direction","values":["forward","backward"]}],"unknowns":["默认效果与默认时长","缓动曲线取值"],"anatomy":{"route":"路由切换动画","duration":"动画时长 0.3-0.5s","direction":"支持前进/后退方向感知"},"doNot":["与具体路由库的集成方式"],"usage":["路由切换动画","可选淡入、滑动、缩放","动画时长 0.3-0.5s","支持前进/后退方向感知","减少页面跳动,提升体验"],"structure":{"duration":"0.3-0.5s","effects":"淡入 / 滑动 / 缩放"}},"files":{"html":"../frameworks/PageTransition.html","css":"../frameworks/PageTransition.css","jsx":"../frameworks/PageTransition.jsx","vue2":"../frameworks/PageTransition.vue2.vue","vue3":"../frameworks/PageTransition.vue3.vue"},"sizes":{"html":1482,"css":928,"jsx":793,"vue2":737,"vue3":709},"sources":{"html":"\n\n\n \n \n Aurora Admin · PageTransition\n \n \n\n\n
\n

PageTransition 页面切换动画

\n

路由切换动画:淡入 / 滑动 / 缩放,时长约 0.35s。点击按钮预览动画。

\n\n
\n
\n \n \n \n
\n
当前页面内容
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin PageTransition 样式 — 路由切换动画 */\n.aa-pt { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-pt-bar { display: flex; gap: 8px; margin-bottom: 12px; }\n.aa-pt-bar .aa-btn { font-size: 13px; padding: 6px 12px; }\n.aa-pt-stage { border: 1px solid #E8ECF1; border-radius: 8px; padding: 28px; text-align: center; color: #595959; min-height: 96px; display: flex; align-items: center; justify-content: center; font-size: 15px; background: #fff; }\n.aa-pt-fade { animation: aaFade .35s ease; }\n.aa-pt-slide { animation: aaSlide .35s ease; }\n.aa-pt-zoom { animation: aaZoom .35s ease; }\n@keyframes aaFade { from { opacity: 0; } to { opacity: 1; } }\n@keyframes aaSlide { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }\n@keyframes aaZoom { from { opacity: 0; transform: scale(.92); } to { opacity: 1; transform: none; } }\n","jsx":"import React, { useState } from 'react';\nimport './PageTransition.css';\n\nexport default function PageTransition({ text = '当前页面内容' }) {\n const [animClass, setAnimClass] = useState('aa-pt-fade');\n const [token, setToken] = useState(0);\n\n const trigger = (type) => {\n setAnimClass('aa-pt-' + type);\n setToken((t) => t + 1);\n };\n\n return (\n
\n
\n \n \n \n
\n
\n {text}\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"buttongroup","name":"按钮组 ButtonGroup","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/buttongroup.json","category":"general","specLines":["- 多个按钮组合","- 可选中(单选/多选)、可分隔","- 按钮之间无缝连接,仅首尾有圆角","- 支持尺寸统一、禁用","- 适用于视图切换、筛选条件"],"specSummary":"- 多个按钮组合","contract":{"variants":["基础组合","单选(视图切换)","多选(筛选条件)","可分隔 · 小尺寸"],"dims":[{"name":"selection","values":["none","single","multiple"]},{"name":"split","values":["connected","separated"]},{"name":"size","values":["large","medium","small"]}],"unknowns":["选中态的视觉表现(背景 vs 边框)","分隔样式的具体形态"],"anatomy":{"group":"按钮组容器","corner":"按钮之间无缝连接,仅首尾有圆角","active":"选中项(单选/多选)"},"doNot":["下拉按钮组合模式"],"usage":["多个按钮组合","可选中(单选/多选)、可分隔","按钮之间无缝连接,仅首尾有圆角","支持尺寸统一、禁用","适用于视图切换、筛选条件"],"structure":{"corner":"仅首尾圆角","join":"无缝连接"}},"files":{"html":"../frameworks/ButtonGroup.html","css":"../frameworks/ButtonGroup.css","jsx":"../frameworks/ButtonGroup.jsx","vue2":"../frameworks/ButtonGroup.vue2.vue","vue3":"../frameworks/ButtonGroup.vue3.vue"},"sizes":{"html":1802,"css":1179,"jsx":1062,"vue2":1260,"vue3":1210},"sources":{"html":"\n\n\n \n \n Aurora Admin · ButtonGroup(普通 H5)\n \n \n \n\n\n
\n
\n\n \n\n\n","css":"/* Aurora Admin ButtonGroup 样式 — 对齐 组件7.txt ButtonGroup:组合按钮无缝连接,仅首尾圆角,可选中(单选/多选) */\n\n.aa-btngroup { display: inline-flex; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-btngroup-btn {\n border: 1px solid #E8ECF1; border-left-width: 0; background: #fff; color: #262626;\n padding: 0 16px; height: 32px; font-size: 14px; cursor: pointer; transition: background .15s, color .15s;\n}\n.aa-btngroup-btn:first-child { border-left-width: 1px; border-radius: 6px 0 0 6px; }\n.aa-btngroup-btn:last-child { border-radius: 0 6px 6px 0; }\n.aa-btngroup-btn:hover:not(.is-disabled):not(.is-selected) { color: #2F54EB; background: #F0F5FF; }\n.aa-btngroup-btn.is-disabled { color: #BFBFBF; cursor: not-allowed; background: #F5F5F5; }\n.aa-btngroup-btn.is-selected { background: #2F54EB; border-color: #2F54EB; color: #fff; }\n.aa-btngroup-btn.is-selected + .aa-btngroup-btn { border-left-color: #2F54EB; }\n.aa-btngroup.is-sm .aa-btngroup-btn { height: 24px; padding: 0 10px; font-size: 13px; }\n.aa-btngroup.is-lg .aa-btngroup-btn { height: 40px; padding: 0 20px; font-size: 15px; }\n","jsx":"import React from 'react';\nimport './ButtonGroup.css';\n\nexport default function ButtonGroup({ options = [], value, multiple = false, size = 'default', onChange }) {\n function isSelected(v) {\n return multiple ? (value || []).indexOf(v) >= 0 : value === v;\n }\n function toggle(o) {\n if (o.disabled) return;\n let next;\n if (multiple) {\n const arr = (value || []).slice();\n const idx = arr.indexOf(o.value);\n if (idx >= 0) arr.splice(idx, 1); else arr.push(o.value);\n next = arr;\n } else {\n next = o.value;\n }\n onChange && onChange(next);\n }\n\n const sizeCls = size === 'small' ? ' is-sm' : size === 'large' ? ' is-lg' : '';\n return (\n
\n {options.map((o, i) => (\n toggle(o)}\n >\n {o.label}\n \n ))}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"radiocard","name":"单选卡片 RadioCard","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/radiocard.json","category":"input","specLines":["- 以卡片形式展示单选选项","- 选中态边框品牌色,背景 #F0F5FF","- 卡片内可包含图标、标题、描述","- 支持多列布局","- 适用于套餐选择、模板选择"],"specSummary":"- 以卡片形式展示单选选项","contract":{"variants":["套餐选择卡片(选中态)","模板选择卡片"],"dims":[{"name":"content","values":["icon-title-desc","title-desc","title"]},{"name":"layout","values":["multi-column"]},{"name":"state","values":["default","selected","disabled"]}],"unknowns":["禁用态样式细节"],"anatomy":{"card":"卡片容器,边框分隔","selected":"选中态边框品牌色,背景 #F0F5FF","inner":"卡片内可包含图标、标题、描述"},"doNot":["卡片固定尺寸与栅格数规范"],"usage":["以卡片形式展示单选选项","选中态边框品牌色,背景 #F0F5FF","卡片内可包含图标、标题、描述,支持多列布局","适用于套餐选择、模板选择"],"structure":{"layout":"支持多列布局"}},"files":{"html":"../frameworks/RadioCard.html","css":"../frameworks/RadioCard.css","jsx":"../frameworks/RadioCard.jsx","vue2":"../frameworks/RadioCard.vue2.vue","vue3":"../frameworks/RadioCard.vue3.vue"},"sizes":{"html":1797,"css":1146,"jsx":827,"vue2":865,"vue3":860},"sources":{"html":"\n\n\n \n \n Aurora Admin · RadioCard(普通 H5)\n \n \n \n\n\n
\n\n \n\n\n","css":"/* Aurora Admin RadioCard 样式 — 对齐 组件7.txt RadioCard:卡片单选,选中态边框品牌色 + 背景 #F0F5FF */\n\n.aa-radiocard-grid { display: grid; gap: 12px; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-radiocard { display: flex; gap: 12px; align-items: flex-start; border: 1px solid #E8ECF1; border-radius: 8px;\n padding: 16px; background: #fff; cursor: pointer; transition: border-color .15s, background .15s; }\n.aa-radiocard:hover { border-color: #C9D4F2; }\n.aa-radiocard.is-selected { border-color: #2F54EB; background: #F0F5FF; }\n.aa-radiocard-icon { font-size: 24px; line-height: 1.4; }\n.aa-radiocard-body { flex: 1; }\n.aa-radiocard-title { font-size: 14px; font-weight: 500; color: #1F2329; }\n.aa-radiocard-desc { font-size: 13px; color: #6E6E6E; margin-top: 4px; line-height: 1.5; }\n.aa-radiocard-mark { width: 16px; height: 16px; border-radius: 50%; border: 2px solid #E8ECF1; margin-top: 2px; box-sizing: border-box; flex-shrink: 0; }\n.aa-radiocard.is-selected .aa-radiocard-mark { border-color: #2F54EB; background: #2F54EB; box-shadow: inset 0 0 0 3px #fff; }\n","jsx":"import React from 'react';\nimport './RadioCard.css';\n\nexport default function RadioCard({ options = [], value, cols = 3, onChange }) {\n return (\n
\n {options.map((o, i) => (\n onChange && onChange(o.value)}\n >\n {o.icon ?
{o.icon}
: null}\n
\n
{o.label}
\n {o.desc ?
{o.desc}
: null}\n
\n
\n
\n ))}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"checkboxcard","name":"复选卡片 CheckboxCard","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/checkboxcard.json","category":"input","specLines":["- 以卡片形式展示多选选项","- 选中态同单选卡片","- 支持卡片内操作按钮","- 适用于批量选择场景"],"specSummary":"- 以卡片形式展示多选选项","contract":{"variants":["批量选择卡片(选中态)","基础复选卡片"],"dims":[{"name":"selection","values":["multi"]},{"name":"innerActions","values":["support","none"]},{"name":"state","values":["default","selected","disabled"]}],"unknowns":["卡片内操作按钮的具体形态"],"anatomy":{"card":"卡片容器,样式与单选卡片一致","selected":"选中态同单选卡片(边框品牌色,背景 #F0F5FF)","actions":"可选的卡片内操作按钮"},"doNot":["全选联动规则"],"usage":["以卡片形式展示多选选项","选中态同单选卡片","支持卡片内操作按钮","适用于批量选择场景"],"structure":{"selection":"多选"}},"files":{"html":"../frameworks/CheckboxCard.html","css":"../frameworks/CheckboxCard.css","jsx":"../frameworks/CheckboxCard.jsx","vue2":"../frameworks/CheckboxCard.vue2.vue","vue3":"../frameworks/CheckboxCard.vue3.vue"},"sizes":{"html":1921,"css":1259,"jsx":1011,"vue2":1095,"vue3":1057},"sources":{"html":"\n\n\n \n \n Aurora Admin · CheckboxCard(普通 H5)\n \n \n \n\n\n
\n\n \n\n\n","css":"/* Aurora Admin CheckboxCard 样式 — 对齐 组件7.txt CheckboxCard:卡片多选,选中态同单选卡片,方框勾选 */\n\n.aa-checkcard-grid { display: grid; gap: 12px; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-checkcard { display: flex; gap: 12px; align-items: flex-start; border: 1px solid #E8ECF1; border-radius: 8px;\n padding: 16px; background: #fff; cursor: pointer; transition: border-color .15s, background .15s; }\n.aa-checkcard:hover { border-color: #C9D4F2; }\n.aa-checkcard.is-selected { border-color: #2F54EB; background: #F0F5FF; }\n.aa-checkcard-icon { font-size: 24px; line-height: 1.4; }\n.aa-checkcard-body { flex: 1; }\n.aa-checkcard-title { font-size: 14px; font-weight: 500; color: #1F2329; }\n.aa-checkcard-desc { font-size: 13px; color: #6E6E6E; margin-top: 4px; line-height: 1.5; }\n.aa-checkcard-mark { width: 16px; height: 16px; border-radius: 4px; border: 2px solid #E8ECF1; margin-top: 2px;\n box-sizing: border-box; flex-shrink: 0; color: #fff; font-size: 11px; line-height: 12px; text-align: center; }\n.aa-checkcard.is-selected .aa-checkcard-mark { border-color: #2F54EB; background: #2F54EB; }\n.aa-checkcard.is-selected .aa-checkcard-mark::after { content: '✓'; }\n","jsx":"import React from 'react';\nimport './CheckboxCard.css';\n\nexport default function CheckboxCard({ options = [], value = [], cols = 3, onChange }) {\n function toggle(v) {\n const arr = value.slice();\n const idx = arr.indexOf(v);\n if (idx >= 0) arr.splice(idx, 1); else arr.push(v);\n onChange && onChange(arr);\n }\n return (\n
\n {options.map((o, i) => (\n = 0 ? ' is-selected' : '')}\n onClick={() => toggle(o.value)}\n >\n {o.icon ?
{o.icon}
: null}\n
\n
{o.label}
\n {o.desc ?
{o.desc}
: null}\n
\n
\n
\n ))}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"listpicker","name":"列表选择器 ListPicker","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/listpicker.json","category":"input","specLines":["- 下拉面板中显示列表,可多选","- 支持分组、搜索、全选","- 选中项显示为标签,可删除","- 适用于标签选择、分类选择"],"specSummary":"- 下拉面板中显示列表,可多选","contract":{"variants":["多选 · 收起(显示标签)","多选 · 展开(分组列表)","单选 · 可搜索","全选"],"dims":[{"name":"selection","values":["single","multiple"]},{"name":"state","values":["collapsed","expanded"]},{"name":"feature","values":["group","search","select-all"]}],"unknowns":["面板的最大高度","标签过多时的折叠规则"],"anatomy":{"panel":"下拉面板中显示列表,可多选","feature":"支持分组、搜索、全选","tag":"选中项显示为标签,可删除"},"doNot":["虚拟滚动","新建选项"],"usage":["下拉面板中显示列表,可多选","支持分组、搜索、全选","选中项显示为标签,可删除","适用于标签选择、分类选择"],"structure":{"panel":"下拉列表","tag":"选中项标签化"}},"files":{"html":"../frameworks/listpicker.html","css":"../frameworks/ListPicker.css","jsx":"../frameworks/ListPicker.jsx","vue2":"../frameworks/ListPicker.vue2.vue","vue3":"../frameworks/ListPicker.vue3.vue"},"sizes":{"html":4444,"css":2742,"jsx":4014,"vue2":5775,"vue3":6059},"sources":{"html":"\n\n\n\n\nAurora Admin · ListPicker (H5)\n\n\n\n\n\n
\n

Aurora Admin · ListPicker 组件(纯 H5)

\n

对齐 ListPicker 规范:下拉列表、多选、分组、搜索、全选、标签

\n\n
\n

技术栈多选(分组 + 搜索 + 全选)

\n
\n
\n
\n\n\n\n\n","css":"/* Aurora Admin ListPicker 样式 — 对齐 组件7.txt ListPicker:下拉列表、多选、分组、搜索、全选、标签 */\n\n.aa-listpicker { position: relative; display: inline-block; min-width: 220px; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-listpicker-trigger {\n display: flex; flex-wrap: wrap; gap: 4px; align-items: center; min-height: 32px; padding: 2px 28px 2px 8px;\n background: #fff; border: 1px solid #E8ECF1; border-radius: 4px; cursor: pointer; transition: border-color .15s, box-shadow .15s;\n}\n.aa-listpicker-trigger:hover { border-color: #2F54EB; }\n.aa-listpicker-trigger.is-open { border-color: #2F54EB; box-shadow: 0 0 0 2px rgba(47,84,235,0.2); }\n.aa-listpicker-tag { background: #F0F5FF; color: #2F54EB; font-size: 12px; padding: 1px 8px; border-radius: 2px; display: inline-flex; align-items: center; gap: 4px; }\n.aa-listpicker-tag .x { cursor: pointer; opacity: .65; }\n.aa-listpicker-tag .x:hover { opacity: 1; }\n.aa-listpicker-placeholder { color: #767676; font-size: 14px; }\n.aa-listpicker-caret { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); color: #6E6E6E; }\n\n.aa-listpicker-panel { position: absolute; z-index: 100; top: calc(100% + 4px); left: 0; width: 240px; background: #fff; border: 1px solid #E8ECF1; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); }\n.aa-listpicker-search { padding: 8px; border-bottom: 1px solid #E8ECF1; }\n.aa-listpicker-search input { width: 100%; box-sizing: border-box; height: 30px; padding: 0 8px; border: 1px solid #E8ECF1; border-radius: 4px; font-size: 13px; outline: none; }\n.aa-listpicker-search input:focus { border-color: #2F54EB; box-shadow: 0 0 0 2px rgba(47,84,235,0.2); }\n.aa-listpicker-selectall { padding: 6px 12px; border-bottom: 1px solid #E8ECF1; cursor: pointer; font-size: 13px; color: #2F54EB; }\n.aa-listpicker-selectall:hover { background: #F5F7FA; }\n.aa-listpicker-group-label { padding: 6px 12px; font-size: 12px; color: #6E6E6E; }\n.aa-listpicker-option { display: flex; align-items: center; gap: 8px; padding: 6px 12px; cursor: pointer; font-size: 14px; }\n.aa-listpicker-option:hover { background: #F5F7FA; }\n.aa-listpicker-option.is-checked { color: #2F54EB; }\n.aa-listpicker-option .box { width: 14px; height: 14px; border: 1px solid #E8ECF1; border-radius: 3px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; color: #fff; font-size: 10px; }\n.aa-listpicker-option.is-checked .box { background: #2F54EB; border-color: #2F54EB; }\n.aa-listpicker-option.is-disabled { color: #BFBFBF; cursor: not-allowed; }\n.aa-listpicker-empty { padding: 24px 12px; text-align: center; color: #767676; font-size: 13px; }\n","jsx":"import React, { useRef, useState, useEffect, useMemo } from 'react';\nimport './ListPicker.css';\n\n/**\n * Aurora Admin ListPicker(React)\n * 对齐 组件7.txt ListPicker 规范:下拉列表、多选、分组、搜索、全选、标签\n *\n * props:\n * - value / defaultValue : 选中 value 数组\n * - options : 扁平 [{value,label}] 或 分组 [{label, children}]\n * - filterable\n * - placeholder\n * - onChange(keys)\n */\nexport default function ListPicker({\n value,\n defaultValue = [],\n options = [],\n filterable = true,\n placeholder = '请选择',\n onChange\n}) {\n const rootRef = useRef(null);\n const [inner, setInner] = useState(defaultValue);\n const current = value !== undefined ? value : inner;\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n\n const isGrouped = options.length && options[0] && options[0].children;\n const flat = isGrouped ? options.flatMap(g => g.children) : options;\n const labelMap = Object.fromEntries(flat.map(o => [o.value, o.label]));\n\n const flatVisible = useMemo(() => flat.filter(o => o.label.includes(query)), [flat, query]);\n const visibleGroups = useMemo(() => {\n if (!isGrouped) return [{ children: flatVisible }];\n return options\n .map(g => ({ label: g.label, children: g.children.filter(o => o.label.includes(query)) }))\n .filter(g => g.children.length);\n }, [options, flatVisible, query]);\n\n const commit = (next) => { if (value === undefined) setInner(next); onChange && onChange(next); };\n const toggleOpt = (o) => { if (o.disabled) return; commit(current.includes(o.value) ? current.filter(x => x !== o.value) : [...current, o.value]); };\n const remove = (v) => commit(current.filter(x => x !== v));\n const selectAll = () => commit([...new Set([...current, ...flatVisible.map(o => o.value)])]);\n\n useEffect(() => {\n const onDoc = (e) => { if (rootRef.current && !rootRef.current.contains(e.target)) { setOpen(false); setQuery(''); } };\n document.addEventListener('click', onDoc, true);\n return () => document.removeEventListener('click', onDoc, true);\n }, []);\n\n return (\n
\n
{ setOpen(!open); if (open) setQuery(''); }}>\n {current.length ? current.map(v => (\n {labelMap[v] || v} { e.stopPropagation(); remove(v); }}>×\n )) : {placeholder}}\n ▾\n
\n\n {open && (\n
\n {filterable && (\n
\n setQuery(e.target.value)} placeholder=\"搜索\" />\n
\n )}\n
全选
\n {visibleGroups.map((g, gi) => (\n
\n {g.label &&
{g.label}
}\n {g.children.map(o => (\n toggleOpt(o)}\n >\n {current.includes(o.value) ? '✓' : ''}{o.label}\n
\n ))}\n
\n ))}\n {!flatVisible.length &&
无匹配项
}\n
\n )}\n \n );\n}\n\n/*\n用法示例:\nimport ListPicker from './ListPicker';\nconst options = [{ label: '前端', children: [{ value: 'vue', label: 'Vue' }, { value: 'react', label: 'React' }] }];\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"cascader","name":"级联选择器 Cascader","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/cascader.json","category":"input","specLines":["- 多级下拉选择","- 支持搜索、动态加载","- 选中路径显示","- 支持多选","- 适用于地区、分类选择"],"specSummary":"- 多级下拉选择","contract":{"variants":["单选级联选择","多选级联选择","动态加载级联选择"],"dims":[{"name":"mode","values":["single","multiple"]},{"name":"load","values":["static","dynamic"]},{"name":"search","values":["support","none"]}],"unknowns":["动态加载时的 loading 占位样式"],"anatomy":{"input":"触发框,显示选中路径(如 华东 / 上海 / 浦东)","panel":"级联面板,多列菜单逐级展开","path":"选中路径文字展示"},"doNot":["面板列宽与最大层级数","搜索命中高亮样式"],"usage":["多级下拉选择,适用于地区、分类选择","支持搜索、动态加载","选中路径完整显示","支持多选"],"structure":{"panel":"多列下拉,列内项高同菜单 32px","trigger":"点击展开"}},"files":{"html":"../frameworks/cascader.html","css":"../frameworks/Cascader.css","jsx":"../frameworks/Cascader.jsx","vue2":"../frameworks/Cascader.vue2.vue","vue3":"../frameworks/Cascader.vue3.vue"},"sizes":{"html":7741,"css":3078,"jsx":5614,"vue2":7946,"vue3":8053},"sources":{"html":"\n\n\n\n\nAurora Admin · Cascader (H5)\n\n\n\n\n\n
\n

Aurora Admin · Cascader 组件(纯 H5)

\n

对齐 Cascader 规范:多级下拉、搜索、路径显示、多选

\n\n
\n

单选 + 搜索

\n
\n
\n\n
\n

多选

\n
\n
\n
\n\n\n\n\n","css":"/* Aurora Admin Cascader 样式 — 对齐 组件7.txt Cascader:多级下拉、搜索、路径显示、多选 */\n\n.aa-cascader { position: relative; display: inline-block; min-width: 200px; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-cascader-trigger {\n display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 32px; padding: 0 12px;\n background: #fff; border: 1px solid #E8ECF1; border-radius: 4px; cursor: pointer; font-size: 14px; transition: border-color .15s, box-shadow .15s;\n}\n.aa-cascader-trigger:hover { border-color: #2F54EB; }\n.aa-cascader-trigger.is-open { border-color: #2F54EB; box-shadow: 0 0 0 2px rgba(47,84,235,0.2); }\n.aa-cascader-trigger .placeholder { color: #767676; }\n.aa-cascader-trigger .caret { color: #6E6E6E; transition: transform .15s; flex-shrink: 0; }\n.aa-cascader-trigger.is-open .caret { transform: rotate(180deg); }\n.aa-cascader-tags { display: flex; flex-wrap: wrap; gap: 4px; }\n.aa-cascader-tag { background: #F0F5FF; color: #2F54EB; font-size: 12px; padding: 1px 8px; border-radius: 2px; white-space: nowrap; }\n\n.aa-cascader-panel { position: absolute; z-index: 100; top: calc(100% + 4px); left: 0; background: #fff; border: 1px solid #E8ECF1; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); display: flex; }\n.aa-cascader-col { min-width: 140px; max-height: 240px; overflow: auto; border-right: 1px solid #E8ECF1; padding: 4px 0; }\n.aa-cascader-col:last-child { border-right: none; }\n.aa-cascader-opt { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 12px; cursor: pointer; font-size: 14px; color: #262626; }\n.aa-cascader-opt:hover { background: #F5F7FA; }\n.aa-cascader-opt.is-active { background: #F0F5FF; color: #2F54EB; font-weight: 500; }\n.aa-cascader-opt .arrow { color: #6E6E6E; font-size: 12px; }\n.aa-cascader-opt .box { width: 14px; height: 14px; border: 1px solid #E8ECF1; border-radius: 3px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; color: #fff; font-size: 10px; }\n.aa-cascader-opt.is-checked .box { background: #2F54EB; border-color: #2F54EB; }\n\n.aa-cascader-search { position: absolute; z-index: 101; top: calc(100% + 4px); left: 0; width: 260px; background: #fff; border: 1px solid #E8ECF1; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); padding: 8px; }\n.aa-cascader-search input { width: 100%; box-sizing: border-box; height: 30px; padding: 0 8px; border: 1px solid #E8ECF1; border-radius: 4px; font-size: 13px; outline: none; }\n.aa-cascader-search input:focus { border-color: #2F54EB; box-shadow: 0 0 0 2px rgba(47,84,235,0.2); }\n.aa-cascader-search input::placeholder { color: #767676; }\n.aa-cascader-results { max-height: 200px; overflow: auto; margin-top: 8px; }\n.aa-cascader-result { padding: 6px 8px; cursor: pointer; font-size: 14px; color: #262626; }\n.aa-cascader-result:hover { background: #F5F7FA; }\n.aa-cascader-empty { padding: 16px 8px; text-align: center; color: #767676; font-size: 13px; }\n","jsx":"import React, { useRef, useState, useEffect, useMemo } from 'react';\nimport './Cascader.css';\n\n/**\n * Aurora Admin Cascader(React)\n * 对齐 组件7.txt Cascader 规范:多级下拉、搜索、路径显示、多选\n *\n * props:\n * - value / defaultValue : 单选为路径数组(或 null),多选为路径数组的数组\n * - options : [{ value, label, children? }]\n * - multiple\n * - filterable\n * - placeholder\n * - onChange(value)\n */\nexport default function Cascader({\n value,\n defaultValue = [],\n options = [],\n multiple = false,\n filterable = true,\n placeholder = '请选择',\n onChange\n}) {\n const rootRef = useRef(null);\n const [inner, setInner] = useState(defaultValue);\n const current = value !== undefined ? value : inner;\n const [open, setOpen] = useState(false);\n const [activePath, setActivePath] = useState([]);\n const [query, setQuery] = useState('');\n\n const pathLabels = (values) => {\n let nodes = options, labels = [];\n for (const v of (values || [])) {\n const n = nodes.find(x => x.value === v);\n if (!n) break;\n labels.push(n.label); nodes = n.children || [];\n }\n return labels;\n };\n const columns = useMemo(() => {\n const cols = [];\n let nodes = options;\n for (let lv = 0; lv <= activePath.length; lv++) {\n cols.push(nodes);\n if (lv < activePath.length) {\n const next = nodes.find(x => x.value === activePath[lv]);\n nodes = next.children || [];\n } else break;\n }\n return cols;\n }, [options, activePath]);\n\n const allLeaves = useMemo(() => {\n const out = [];\n (function walk(list, acc) {\n list.forEach(n => {\n const p = [...acc, n.value];\n if (n.children && n.children.length) walk(n.children, p);\n else out.push(p);\n });\n })(options, []);\n return out;\n }, [options]);\n const results = query ? allLeaves.filter(p => pathLabels(p).join('/').includes(query)) : [];\n\n const commit = (next) => {\n if (value === undefined) setInner(next);\n onChange && onChange(next);\n };\n const choose = (n, lv) => {\n const full = [...activePath.slice(0, lv), n.value];\n if (n.children && n.children.length) setActivePath(full);\n else if (multiple) {\n const k = JSON.stringify(full);\n const arr = Array.isArray(current) ? current : [];\n commit(arr.some(p => JSON.stringify(p) === k) ? arr.filter(p => JSON.stringify(p) !== k) : [...arr, full]);\n } else { commit(full); setOpen(false); }\n };\n const pickResult = (p) => {\n if (multiple) {\n const k = JSON.stringify(p);\n const arr = Array.isArray(current) ? current : [];\n commit(arr.some(x => JSON.stringify(x) === k) ? arr.filter(x => JSON.stringify(x) !== k) : [...arr, p]);\n } else { commit(p); setOpen(false); }\n };\n const isChecked = (p) => Array.isArray(current) && current.some(x => JSON.stringify(x) === JSON.stringify(p));\n\n useEffect(() => {\n const onDoc = (e) => { if (rootRef.current && !rootRef.current.contains(e.target)) { setOpen(false); setQuery(''); } };\n document.addEventListener('click', onDoc, true);\n return () => document.removeEventListener('click', onDoc, true);\n }, []);\n\n return (\n
\n
{ setOpen(!open); if (open) setQuery(''); }}>\n {multiple ? (\n current.length ? (\n \n {current.map((p, i) => {pathLabels(p).join(' / ')})}\n \n ) : {placeholder}\n ) : (\n {current && current.length ? pathLabels(current).join(' / ') : placeholder}\n )}\n ▾\n
\n\n {open && filterable && (\n
\n setQuery(e.target.value)} placeholder=\"搜索\" />\n
\n )}\n\n {open && (\n
\n {query ? (\n
\n {results.length ? results.map((p, i) => (\n
pickResult(p)}>{pathLabels(p).join(' / ')}
\n )) :
无匹配结果
}\n
\n ) : (\n columns.map((col, ci) => (\n
\n {col.map(n => (\n choose(n, ci)}\n >\n {multiple && !n.children && {isChecked([...activePath.slice(0, ci), n.value]) ? '✓' : ''}}\n {n.label}\n {n.children && n.children.length && ›}\n
\n ))}\n
\n ))\n )}\n
\n )}\n \n );\n}\n\n/*\n用法示例:\nimport Cascader from './Cascader';\nconst options = [{ value: 'zj', label: '浙江', children: [{ value: 'hz', label: '杭州', children: [{ value: 'xh', label: '西湖区' }] }] }];\n\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}},{"slug":"colorpicker","name":"颜色选择器 ColorPicker","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/colorpicker.json","category":"input","specLines":["- 预设色板 + 自定义拾色器","- 支持 HEX、RGB、HSL","- 透明度调节","- 实时预览","- 适用于主题配置、标签颜色"],"specSummary":"- 预设色板 + 自定义拾色器","contract":{"variants":["预设色板拾色","自定义拾色(带透明度)"],"dims":[{"name":"format","values":["HEX","RGB","HSL"]},{"name":"alpha","values":["support","none"]},{"name":"source","values":["preset-palette","custom","both"]}],"unknowns":["预设色板的具体色值清单"],"anatomy":{"palette":"预设色板","picker":"自定义拾色器","preview":"实时预览"},"doNot":["吸管取色"],"usage":["预设色板 + 自定义拾色器","支持 HEX、RGB、HSL","透明度调节","实时预览","适用于主题配置、标签颜色"],"structure":{"formats":"HEX RGB HSL"}},"files":{"html":"../frameworks/ColorPicker.html","css":"../frameworks/ColorPicker.css","jsx":"../frameworks/ColorPicker.jsx","vue2":"../frameworks/ColorPicker.vue2.vue","vue3":"../frameworks/ColorPicker.vue3.vue"},"sizes":{"html":3198,"css":1418,"jsx":1917,"vue2":1988,"vue3":1899},"sources":{"html":"\n\n\n \n \n Aurora Admin · ColorPicker(普通 H5)\n \n \n \n\n\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin ColorPicker 样式 — 对齐 组件7.txt ColorPicker:预设色板 + 自定义拾色,HEX 显示,透明度 */\n\n.aa-colorpicker { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); width: 240px; }\n.aa-colorpicker-presets { display: grid; grid-template-columns: repeat(8, 1fr); gap: 6px; margin-bottom: 12px; }\n.aa-colorpicker-swatch { height: 22px; border-radius: 4px; cursor: pointer; border: 1px solid rgba(0,0,0,0.06); }\n.aa-colorpicker-swatch.is-active { box-shadow: 0 0 0 2px #fff, 0 0 0 4px #2F54EB; }\n.aa-colorpicker-row { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }\n.aa-colorpicker-preview { width: 28px; height: 28px; border-radius: 6px; border: 1px solid #E8ECF1; flex-shrink: 0; }\n.aa-colorpicker-native { width: 28px; height: 28px; padding: 0; border: 1px solid #E8ECF1; border-radius: 6px; background: none; cursor: pointer; }\n.aa-colorpicker-hex { flex: 1; height: 30px; border: 1px solid #E8ECF1; border-radius: 6px; padding: 0 8px; font-size: 13px; color: #262626; text-transform: uppercase; }\n.aa-colorpicker-hex:focus { border-color: #2F54EB; outline: none; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-colorpicker-alpha { display: flex; align-items: center; gap: 8px; }\n.aa-colorpicker-alpha input { flex: 1; }\n.aa-colorpicker-alpha span { font-size: 12px; color: #6E6E6E; width: 34px; text-align: right; }\n","jsx":"import React, { useState } from 'react';\nimport './ColorPicker.css';\n\nconst DEFAULT_PRESETS = ['#2F54EB','#1D39C4','#10239E','#CF1322','#8C5A00','#2E7D0A','#13C2C2','#722ED1',\n '#EB2F96','#8C5A00','#A0D911','#1890FF','#000000','#595959','#6E6E6E','#BFBFBF'];\n\nexport default function ColorPicker({ value = '#2F54EB', presets = DEFAULT_PRESETS, onChange }) {\n const [hex, setHex] = useState(value);\n const [alpha, setAlpha] = useState(1);\n\n const n = parseInt(hex.slice(1), 16);\n const preview = `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${alpha})`;\n\n function emit(nextHex, nextAlpha) {\n onChange && onChange({ hex: nextHex, alpha: nextAlpha });\n }\n function onHexInput(e) {\n const v = e.target.value;\n if (/^#[0-9a-fA-F]{6}$/.test(v)) { setHex(v); emit(v, alpha); }\n }\n\n return (\n
\n
\n {presets.map((c, i) => (\n { setHex(c); emit(c, alpha); }}\n />\n ))}\n
\n
\n
\n { setHex(e.target.value); emit(e.target.value, alpha); }} />\n \n
\n
\n 透明度\n { setAlpha(e.target.value / 100); emit(hex, e.target.value / 100); }} />\n {Math.round(alpha * 100)}%\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"rangequickpicker","name":"日期范围快捷选择 RangeQuickPicker","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/rangequickpicker.json","category":"input","specLines":["- 日期范围选择器增强","- 内置快捷选项:今天、昨天、近7天、近30天、本月、上月、本年","- 自定义快捷项配置","- 支持时间范围","- 适用于报表筛选"],"specSummary":"- 日期范围选择器增强","contract":{"variants":["今天","近 7 天","本月","自定义 + 时间范围"],"dims":[{"name":"preset","values":["today","yesterday","last7","last30","thisMonth","lastMonth","thisYear","custom"]},{"name":"withTime","values":["true","false"]}],"unknowns":["快捷项与日历面板的排列方式","时间范围的选择粒度"],"anatomy":{"trigger":"日期范围选择器增强","presets":"内置快捷选项:今天、昨天、近7天、近30天、本月、上月、本年","custom":"自定义快捷项配置"},"doNot":["日历面板的日期单元格样式(见 Calendar 契约)"],"usage":["日期范围选择器增强","内置快捷选项:今天、昨天、近7天、近30天、本月、上月、本年","自定义快捷项配置","支持时间范围","适用于报表筛选"],"structure":{"presets":"内置 7 项","time":"可选"}},"files":{"html":"../frameworks/RangeQuickPicker.html","css":"../frameworks/RangeQuickPicker.css","jsx":"../frameworks/RangeQuickPicker.jsx","vue2":"../frameworks/RangeQuickPicker.vue2.vue","vue3":"../frameworks/RangeQuickPicker.vue3.vue"},"sizes":{"html":4816,"css":1823,"jsx":3420,"vue2":3132,"vue3":3014},"sources":{"html":"\n\n\n \n \n Aurora Admin · RangeQuickPicker\n \n \n\n\n
\n

RangeQuickPicker 日期范围快捷选择

\n

内置快捷选项(今天、近7天、本月等),也支持自定义起止日期。

\n\n
\n
报表时间筛选
\n
\n
\n 请选择时间范围\n
\n
\n
\n
\n \n ~\n \n
\n
\n \n \n
\n
\n
\n

\n
\n
\n\n \n\n\n","css":"/* Aurora Admin RangeQuickPicker 样式 — 日期范围 + 快捷选项 */\n.aa-range { position: relative; display: inline-block; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-range-trigger { display: flex; align-items: center; gap: 8px; border: 1px solid #E8ECF1; border-radius: 6px; padding: 0 10px; height: 36px; background: #fff; cursor: pointer; color: #262626; font-size: 14px; min-width: 220px; }\n.aa-range-trigger.is-focus { border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-range-trigger .aa-range-sep { color: #6E6E6E; }\n.aa-range-placeholder { color: #767676; }\n.aa-range-pop { position: absolute; top: 42px; left: 0; z-index: 20; background: #fff; border: 1px solid #E8ECF1; border-radius: 8px; box-shadow: 0 6px 24px rgba(0,0,0,0.12); padding: 12px; width: 300px; }\n.aa-range-presets { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }\n.aa-range-preset { padding: 4px 10px; border: 1px solid #E8ECF1; border-radius: 4px; font-size: 13px; cursor: pointer; color: #595959; background: #fff; }\n.aa-range-preset:hover { border-color: #B7C4FF; }\n.aa-range-preset.is-active { border-color: #2F54EB; color: #2F54EB; background: #F0F5FF; }\n.aa-range-custom { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }\n.aa-range-custom input { border: 1px solid #E8ECF1; border-radius: 4px; padding: 5px 8px; font-size: 13px; color: #262626; }\n.aa-range-custom .aa-range-tilde { color: #6E6E6E; }\n.aa-range-actions { text-align: right; }\n.aa-range-actions button { border: none; border-radius: 4px; padding: 5px 14px; font-size: 13px; cursor: pointer; }\n.aa-range-actions .aa-range-ok { background: #2F54EB; color: #fff; }\n.aa-range-actions .aa-range-cancel { background: #F5F5F5; color: #595959; margin-right: 8px; }\n","jsx":"import React, { useState, useRef, useEffect } from 'react';\nimport './RangeQuickPicker.css';\n\nfunction fmt(d) {\n return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');\n}\nfunction presetRange(p) {\n const end = new Date();\n end.setHours(0, 0, 0, 0);\n const start = new Date(end);\n if (p.year === 'this') start.setMonth(0, 1);\n else if (p.month === 'this') start.setDate(1);\n else if (p.month === 'last') {\n start = new Date(end.getFullYear(), end.getMonth() - 1, 1);\n end.setDate(0);\n } else {\n start.setDate(end.getDate() + (p.days || 0));\n if (p.len) start.setDate(end.getDate() - (p.len - 1));\n }\n return { start: fmt(start), end: fmt(end) };\n}\n\nconst DEFAULT_PRESETS = [\n { label: '今天', days: 0 },\n { label: '昨天', days: -1, len: 1 },\n { label: '近7天', days: -6 },\n { label: '近30天', days: -29 },\n { label: '本月', month: 'this' },\n { label: '上月', month: 'last' },\n { label: '本年', year: 'this' }\n];\n\nexport default function RangeQuickPicker({ modelValue = { start: '', end: '' }, presets = DEFAULT_PRESETS, onChange }) {\n const root = useRef(null);\n const [open, setOpen] = useState(false);\n const [start, setStart] = useState(modelValue.start);\n const [end, setEnd] = useState(modelValue.end);\n const [activeIndex, setActiveIndex] = useState(-1);\n\n useEffect(() => {\n function onDoc(e) {\n if (root.current && !root.current.contains(e.target)) {\n setOpen(false);\n setActiveIndex(-1);\n }\n }\n document.addEventListener('click', onDoc);\n return () => document.removeEventListener('click', onDoc);\n }, []);\n\n const applyPreset = (i) => {\n const r = presetRange(presets[i]);\n setStart(r.start);\n setEnd(r.end);\n setActiveIndex(i);\n };\n const confirm = () => {\n if (onChange) onChange({ start, end });\n setOpen(false);\n setActiveIndex(-1);\n };\n\n return (\n
\n
setOpen((o) => !o)}>\n {modelValue.start && modelValue.end ? (\n \n {modelValue.start} ~ {modelValue.end}\n \n ) : (\n 请选择时间范围\n )}\n
\n {open && (\n
\n
\n {presets.map((p, i) => (\n \n {p.label}\n \n ))}\n
\n
\n setStart(e.target.value)} />\n ~\n setEnd(e.target.value)} />\n
\n
\n \n \n
\n
\n )}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"numberrangeinput","name":"数字范围输入 NumberRangeInput","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/numberrangeinput.json","category":"input","specLines":["- 输入数字范围","- 两个数字输入框,中间分隔符","- 支持步长、单位","- 校验范围合法性","- 适用于价格、数量筛选"],"specSummary":"- 输入数字范围","contract":{"variants":["价格范围筛选","数量范围输入"],"dims":[{"name":"unit","values":["with-unit","none"]},{"name":"step","values":["configurable"]},{"name":"validation","values":["range-legal-check"]}],"unknowns":["最小值大于最大值时的提示方式"],"anatomy":{"input-min":"最小值数字输入框","separator":"中间分隔符","input-max":"最大值数字输入框"},"doNot":["分隔符的具体字符与样式"],"usage":["输入数字范围","支持步长、单位","校验范围合法性","适用于价格、数量筛选"],"structure":{"layout":"两个数字输入框 + 中间分隔符"}},"files":{"html":"../frameworks/NumberRangeInput.html","css":"../frameworks/NumberRangeInput.css","jsx":"../frameworks/NumberRangeInput.jsx","vue2":"../frameworks/NumberRangeInput.vue2.vue","vue3":"../frameworks/NumberRangeInput.vue3.vue"},"sizes":{"html":1842,"css":909,"jsx":1572,"vue2":1952,"vue3":1837},"sources":{"html":"\n\n\n \n \n Aurora Admin · NumberRangeInput(普通 H5)\n \n \n \n\n\n
\n \n —\n \n 元\n \n
\n\n \n\n\n","css":"/* Aurora Admin NumberRangeInput 样式 — 对齐 组件7.txt NumberRangeInput:两个数字输入框 + 分隔符,支持步长、单位、校验 */\n\n.aa-numrange { display: inline-flex; align-items: center; gap: 8px; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-numrange input { width: 88px; height: 32px; border: 1px solid #E8ECF1; border-radius: 6px; padding: 0 10px;\n color: #262626; font-size: 14px; box-sizing: border-box; }\n.aa-numrange input:focus { border-color: #2F54EB; outline: none; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-numrange input:disabled { background: #F5F5F5; color: #BFBFBF; cursor: not-allowed; }\n.aa-numrange-sep { color: #6E6E6E; }\n.aa-numrange-unit { color: #6E6E6E; font-size: 13px; }\n.aa-numrange.is-error input { border-color: #CF1322; }\n.aa-numrange-msg { color: #CF1322; font-size: 12px; margin-left: 8px; }\n","jsx":"import React, { useState } from 'react';\nimport './NumberRangeInput.css';\n\nexport default function NumberRangeInput({\n value = ['', ''], step = 1, min = -Infinity, max = Infinity,\n separator = '—', unit = '', placeholder = ['最小', '最大'], onChange, onError\n}) {\n const [errorMsg, setErrorMsg] = useState('');\n\n function validate(arr) {\n const a = parseFloat(arr[0]), b = parseFloat(arr[1]);\n let err = '';\n if (arr[0] !== '' && (a < min || a > max)) err = `最小值需在 ${min}~${max}`;\n else if (arr[1] !== '' && (b < min || b > max)) err = `最大值需在 ${min}~${max}`;\n else if (arr[0] !== '' && arr[1] !== '' && a > b) err = '最小值不能大于最大值';\n setErrorMsg(err);\n onError && onError(err);\n }\n function onMin(e) {\n const arr = [e.target.value, value[1]];\n validate(arr);\n onChange && onChange(arr);\n }\n function onMax(e) {\n const arr = [value[0], e.target.value];\n validate(arr);\n onChange && onChange(arr);\n }\n\n return (\n
\n \n {separator}\n \n {unit ? {unit} : null}\n {errorMsg ? {errorMsg} : null}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"switchgroup","name":"开关组 SwitchGroup","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/switchgroup.json","category":"input","specLines":["- 多个开关组合,可分组","- 支持全选、联动","- 适用于权限开关、通知设置","- 分组标题、说明"],"specSummary":"- 多个开关组合,可分组","contract":{"variants":["通知设置开关组","权限开关组"],"dims":[{"name":"grouping","values":["grouped(标题+说明)","flat"]},{"name":"linkage","values":["select-all","linked","none"]}],"unknowns":["联动的具体联动规则"],"anatomy":{"group":"分组容器,含分组标题与说明文字","switch-row":"开关行,单个开关沿用基础开关规格"},"doNot":["单个开关的视觉规格(沿用基础开关)"],"usage":["多个开关组合,可分组","支持全选、联动","分组带标题与说明","适用于权限开关、通知设置"],"structure":{"group":"分组标题 + 说明文字"}},"files":{"html":"../frameworks/SwitchGroup.html","css":"../frameworks/SwitchGroup.css","jsx":"../frameworks/SwitchGroup.jsx","vue2":"../frameworks/SwitchGroup.vue2.vue","vue3":"../frameworks/SwitchGroup.vue3.vue"},"sizes":{"html":2434,"css":1191,"jsx":1376,"vue2":1549,"vue3":1471},"sources":{"html":"\n\n\n \n \n Aurora Admin · SwitchGroup(普通 H5)\n \n \n \n\n\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin SwitchGroup 样式 — 对齐 组件7.txt SwitchGroup:多个开关组合,可分组、全选、联动 */\n\n.aa-switchgroup { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-switchgroup-head { display: flex; align-items: center; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #E8ECF1; margin-bottom: 8px; }\n.aa-switchgroup-title { font-size: 14px; font-weight: 500; color: #1F2329; }\n.aa-switch-row { display: flex; align-items: center; justify-content: space-between; padding: 8px 0; }\n.aa-switch-text { display: flex; flex-direction: column; }\n.aa-switch-label { font-size: 14px; color: #262626; }\n.aa-switch-desc { font-size: 12px; color: #6E6E6E; margin-top: 2px; }\n.aa-switch { position: relative; width: 44px; height: 22px; border-radius: 11px; background: #E8ECF1; cursor: pointer; transition: background .2s; flex-shrink: 0; }\n.aa-switch.is-on { background: #2F54EB; }\n.aa-switch::after { content: ''; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: #fff; transition: left .2s; }\n.aa-switch.is-on::after { left: 24px; }\n","jsx":"import React, { useState } from 'react';\nimport './SwitchGroup.css';\n\nexport default function SwitchGroup({ options = [], value = [], title = '', selectAll = false, onChange }) {\n const allOn = options.length > 0 && options.every(o => value.indexOf(o.value) >= 0);\n\n function toggle(v) {\n const arr = value.slice();\n const idx = arr.indexOf(v);\n if (idx >= 0) arr.splice(idx, 1); else arr.push(v);\n onChange && onChange(arr);\n }\n function toggleAll() {\n onChange && onChange(allOn ? [] : options.map(o => o.value));\n }\n\n return (\n
\n {(title || selectAll) ? (\n
\n {title}\n {selectAll ? (\n
\n ) : null}\n
\n ) : null}\n {options.map((o, i) => (\n
\n
\n {o.label}\n {o.desc ? {o.desc} : null}\n
\n = 0 ? ' is-on' : '')}\n onClick={() => toggle(o.value)}\n />\n
\n ))}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"dragupload","name":"拖拽上传 DragUpload","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/dragupload.json","category":"input","specLines":["- 支持点击或拖拽文件上传","- 拖拽区域虚线边框,悬停高亮","- 文件列表显示进度、状态","- 支持图片预览、删除","- 限制文件类型、大小、数量"],"specSummary":"- 支持点击或拖拽文件上传","contract":{"variants":["默认拖拽区","悬停高亮","上传中(进度)","上传失败","图片预览"],"dims":[{"name":"state","values":["idle","dragover","uploading","error","done"]},{"name":"listType","values":["text","picture"]}],"unknowns":["虚线边框的颜色与圆角","文件列表的最大高度"],"anatomy":{"zone":"拖拽区域:虚线边框,悬停高亮","list":"文件列表显示进度、状态","preview":"支持图片预览、删除","limit":"限制文件类型、大小、数量"},"doNot":["分片上传与断点续传"],"usage":["支持点击或拖拽文件上传","拖拽区域虚线边框,悬停高亮","文件列表显示进度、状态","支持图片预览、删除","限制文件类型、大小、数量"],"structure":{"zoneBorder":"虚线","states":"idle / dragover / uploading / error / done"}},"files":{"html":"../frameworks/DragUpload.html","css":"../frameworks/DragUpload.css","jsx":"../frameworks/DragUpload.jsx","vue2":"../frameworks/DragUpload.vue2.vue","vue3":"../frameworks/DragUpload.vue3.vue"},"sizes":{"html":3702,"css":1700,"jsx":2691,"vue2":2303,"vue3":2172},"sources":{"html":"\n\n\n \n \n Aurora Admin · DragUpload\n \n \n\n\n
\n

DragUpload 拖拽上传

\n

支持点击或拖拽文件上传,显示进度与缩略图,可删除。

\n\n
\n
\n
\n ⬆️\n
将文件拖到此处,或 点击选择
\n
\n \n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin DragUpload 样式 — 拖拽 / 点击上传 */\n.aa-upload { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-upload-drop { border: 2px dashed #E8ECF1; border-radius: 8px; padding: 28px; text-align: center; color: #6E6E6E; cursor: pointer; transition: border-color .15s, background .15s, color .15s; }\n.aa-upload-drop.is-over { border-color: #2F54EB; background: #F0F5FF; color: #2F54EB; }\n.aa-upload-icon { font-size: 26px; display: block; margin-bottom: 6px; }\n.aa-upload-hint { font-size: 13px; }\n.aa-upload-hint b { color: #2F54EB; }\n.aa-upload-list { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }\n.aa-upload-item { display: flex; align-items: center; gap: 10px; border: 1px solid #E8ECF1; border-radius: 6px; padding: 8px 10px; }\n.aa-upload-thumb { width: 36px; height: 36px; border-radius: 4px; overflow: hidden; background: #F5F5F5; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: #6E6E6E; font-size: 11px; }\n.aa-upload-thumb img { width: 100%; height: 100%; object-fit: cover; }\n.aa-upload-meta { flex: 1; min-width: 0; }\n.aa-upload-name { font-size: 13px; color: #262626; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.aa-upload-bar { height: 4px; background: #E8ECF1; border-radius: 2px; margin-top: 5px; overflow: hidden; }\n.aa-upload-bar > i { display: block; height: 100%; background: #2F54EB; width: 0; transition: width .2s; }\n.aa-upload-status { font-size: 12px; color: #6E6E6E; }\n.aa-upload-del { border: none; background: none; color: #6E6E6E; cursor: pointer; font-size: 16px; padding: 0 4px; }\n.aa-upload-del:hover { color: #CF1322; }\n","jsx":"import React, { useRef, useState } from 'react';\nimport './DragUpload.css';\n\nexport default function DragUpload() {\n const [files, setFiles] = useState([]);\n const [over, setOver] = useState(false);\n const fileRef = useRef(null);\n\n const addFiles = (flist) => {\n Array.prototype.forEach.call(flist, (f) => {\n const id = 'f' + Date.now() + Math.random().toString(36).slice(2, 6);\n const item = { id, name: f.name, progress: 0, done: false, thumb: '' };\n if (/^image\\//.test(f.type) && f.size < 2 * 1024 * 1024) {\n const reader = new FileReader();\n reader.onload = (e) => setFiles((fs) => fs.map((x) => (x.id === id ? { ...x, thumb: e.target.result } : x)));\n reader.readAsDataURL(f);\n }\n setFiles((fs) => [...fs, item]);\n const t = setInterval(() => {\n setFiles((fs) =>\n fs.map((x) => {\n if (x.id !== id) return x;\n const p = Math.min(100, x.progress + Math.random() * 22);\n return { ...x, progress: p, done: p >= 100 };\n })\n );\n setFiles((fs) => {\n if (fs.find((x) => x.id === id && x.progress >= 100)) clearInterval(t);\n return fs;\n });\n }, 220);\n });\n };\n\n const remove = (id) => setFiles((fs) => fs.filter((x) => x.id !== id));\n\n return (\n
\n fileRef.current.click()}\n onDragOver={(e) => { e.preventDefault(); setOver(true); }}\n onDragLeave={() => setOver(false)}\n onDrop={(e) => { e.preventDefault(); setOver(false); addFiles(e.dataTransfer.files); }}\n >\n ⬆️\n
将文件拖到此处,或 点击选择
\n
\n { addFiles(e.target.files); e.target.value = ''; }} />\n
\n {files.map((it) => (\n
\n
{it.thumb ? \"\" : '文件'}
\n
\n
{it.name}
\n
\n
\n
{it.done ? '已完成' : Math.round(it.progress) + '%'}
\n \n
\n ))}\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"signaturepad","name":"签名板 SignaturePad","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/signaturepad.json","category":"input","specLines":["- 手写签名区域","- 支持鼠标、触屏","- 清除、撤销、保存","- 可导出图片","- 适用于合同、确认单"],"specSummary":"- 手写签名区域","contract":{"variants":["空白画布","已签名","触屏绘制中"],"dims":[{"name":"state","values":["empty","drawing","signed"]},{"name":"device","values":["mouse","touch"]}],"unknowns":["画布尺寸与线条粗细","导出图片的格式与尺寸"],"anatomy":{"canvas":"手写签名区域","input":"支持鼠标、触屏","actions":"清除、撤销、保存","export":"可导出图片"},"doNot":["签名笔迹的加密与存证"],"usage":["手写签名区域","支持鼠标、触屏","清除、撤销、保存","可导出图片","适用于合同、确认单"],"structure":{"input":"鼠标 + 触屏","actions":"清除 / 撤销 / 保存"}},"files":{"html":"../frameworks/SignaturePad.html","css":"../frameworks/SignaturePad.css","jsx":"../frameworks/SignaturePad.jsx","vue2":"../frameworks/SignaturePad.vue2.vue","vue3":"../frameworks/SignaturePad.vue3.vue"},"sizes":{"html":3328,"css":716,"jsx":2965,"vue2":2511,"vue3":2510},"sources":{"html":"\n\n\n \n \n Aurora Admin · SignaturePad\n \n \n\n\n
\n

SignaturePad 签名板

\n

手写签名区域,支持鼠标与触屏,可撤销、清除、导出图片。

\n\n
\n
\n
\n \n
请在此处签名
\n
\n
\n \n \n \n \n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin SignaturePad 样式 — 手写签名 */\n.aa-sign { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-sign-pad { position: relative; border: 1px solid #E8ECF1; border-radius: 8px; background: #fff; overflow: hidden; touch-action: none; }\n.aa-sign-canvas { display: block; width: 100%; height: 180px; cursor: crosshair; }\n.aa-sign-placeholder { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #767676; font-size: 14px; pointer-events: none; }\n.aa-sign-actions { display: flex; gap: 10px; margin-top: 10px; align-items: center; }\n.aa-sign-preview { margin-left: auto; font-size: 12px; color: #6E6E6E; }\n","jsx":"import React, { useRef, useState, useEffect } from 'react';\nimport './SignaturePad.css';\n\nexport default function SignaturePad() {\n const pad = useRef(null);\n const canvas = useRef(null);\n const drawing = useRef(false);\n const last = useRef(null);\n const strokes = useRef([]);\n const cur = useRef([]);\n const [hasInk, setHasInk] = useState(false);\n const [saved, setSaved] = useState('');\n\n const ctx = () => canvas.current.getContext('2d');\n const fit = () => {\n const r = pad.current.getBoundingClientRect();\n canvas.current.width = r.width;\n canvas.current.height = r.height;\n redraw();\n };\n const pos = (e) => {\n const r = canvas.current.getBoundingClientRect();\n const t = e.touches && e.touches[0];\n return { x: (t ? t.clientX : e.clientX) - r.left, y: (t ? t.clientY : e.clientY) - r.top };\n };\n const down = (e) => {\n e.preventDefault();\n drawing.current = true;\n last.current = pos(e);\n cur.current = [last.current];\n strokes.current.push(cur.current);\n setHasInk(true);\n };\n const move = (e) => {\n if (!drawing.current) return;\n e.preventDefault();\n const p = pos(e);\n ctx().lineTo(p.x, p.y);\n ctx().stroke();\n cur.current.push(p);\n };\n const up = () => { drawing.current = false; last.current = null; };\n const undo = () => { strokes.current.pop(); redraw(); if (!strokes.current.length) setHasInk(false); };\n const clear = () => { strokes.current = []; redraw(); setHasInk(false); setSaved(''); };\n const save = () => setSaved('已导出(' + canvas.current.width + '×' + canvas.current.height + ')');\n\n const redraw = () => {\n const c = canvas.current;\n const x = c.getContext('2d');\n x.clearRect(0, 0, c.width, c.height);\n x.strokeStyle = '#1F2329';\n x.lineWidth = 2;\n x.lineCap = 'round';\n x.lineJoin = 'round';\n strokes.current.forEach((s) => {\n x.beginPath();\n s.forEach((p, i) => (i ? x.lineTo(p.x, p.y) : x.moveTo(p.x, p.y)));\n x.stroke();\n });\n };\n\n useEffect(() => {\n fit();\n window.addEventListener('resize', fit);\n return () => window.removeEventListener('resize', fit);\n // eslint-disable-next-line\n }, []);\n\n return (\n
\n
\n \n {!hasInk &&
请在此处签名
}\n
\n
\n \n \n \n {saved && {saved}}\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"codeinput","name":"验证码输入 CodeInput","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/codeinput.json","category":"input","specLines":["- 多个独立输入格","- 自动聚焦下一个","- 支持粘贴、删除","- 输入完成自动提交","- 适用于短信验证码、支付密码"],"specSummary":"- 多个独立输入格","contract":{"variants":["短信验证码输入","支付密码输入"],"dims":[{"name":"cells","values":["configurable-格数"]},{"name":"autoSubmit","values":["on-complete","none"]},{"name":"paste","values":["support"]}],"unknowns":["错误态的视觉反馈方式"],"anatomy":{"cell":"多个独立输入格","auto-focus":"输入后自动聚焦下一格;支持粘贴、删除回退"},"doNot":["格子尺寸与间距规范","错误态样式","重发倒计时展示(属业务侧)"],"usage":["多个独立输入格","自动聚焦下一个","支持粘贴、删除","输入完成自动提交","适用于短信验证码、支付密码"],"structure":{"focus":"自动聚焦下一个输入格"}},"files":{"html":"../frameworks/CodeInput.html","css":"../frameworks/CodeInput.css","jsx":"../frameworks/CodeInput.jsx","vue2":"../frameworks/CodeInput.vue2.vue","vue3":"../frameworks/CodeInput.vue3.vue"},"sizes":{"html":4115,"css":1082,"jsx":2755,"vue2":2981,"vue3":2673},"sources":{"html":"\n\n\n \n \n Aurora Admin · CodeInput\n \n \n\n\n
\n

CodeInput 验证码输入

\n

用于短信验证码、一次性口令(OTP)输入,支持自动跳转、退格回退与粘贴填充。

\n\n
\n
基础用法(6 位)
\n
\n
\n
\n\n
\n
掩码模式 + 4 位
\n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin CodeInput 样式 — 对齐 组件7.txt CodeInput:验证码 / 一次性口令输入 */\n\n.aa-code { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); display: flex; gap: 8px; }\n.aa-code-cell {\n position: relative;\n width: 44px; height: 48px;\n border: 1px solid #E8ECF1; border-radius: 8px;\n background: #fff;\n display: flex; align-items: center; justify-content: center;\n transition: border-color .15s, box-shadow .15s;\n}\n.aa-code-cell.is-focus { border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-code-cell.is-filled { border-color: #B7C4FF; }\n.aa-code-input {\n width: 100%; height: 100%;\n border: none; outline: none; background: none;\n text-align: center; font-size: 20px; font-weight: 600; color: #1F2329;\n padding: 0;\n}\n.aa-code-input::placeholder { color: #767676; font-weight: 400; }\n.aa-code-cell.is-masked .aa-code-input { -webkit-text-security: disc; text-security: disc; }\n.aa-code-tip { font-size: 12px; margin-top: 8px; color: #6E6E6E; }\n.aa-code-tip.error { color: #CF1322; }\n","jsx":"import React, { useState, useRef, useCallback } from 'react';\nimport './CodeInput.css';\n\nfunction buildCells(v, length) {\n const arr = (v || '').split('').slice(0, length);\n while (arr.length < length) arr.push('');\n return arr;\n}\n\nexport default function CodeInput({\n value = '',\n length = 6,\n masked = false,\n tip = '',\n hasError = false,\n onChange\n}) {\n const inputsRef = useRef([]);\n const [cells, setCells] = useState(() => buildCells(value, length));\n const [focusedIndex, setFocusedIndex] = useState(-1);\n\n const emit = useCallback(\n (next) => {\n const code = next.join('');\n if (onChange) onChange(code);\n },\n [onChange]\n );\n\n const focus = (i) => {\n const el = inputsRef.current[i];\n if (el) el.focus();\n };\n\n const onInput = (e, i) => {\n const ch = (e.target.value || '').slice(-1);\n const next = cells.slice();\n next[i] = ch;\n e.target.value = ch;\n setCells(next);\n if (ch && i < length - 1) focus(i + 1);\n emit(next);\n };\n\n const onKeydown = (e, i) => {\n if (e.key === 'Backspace') {\n e.preventDefault();\n const next = cells.slice();\n if (next[i]) {\n next[i] = '';\n } else if (i > 0) {\n next[i - 1] = '';\n focus(i - 1);\n }\n setCells(next);\n emit(next);\n } else if (e.key === 'ArrowLeft' && i > 0) {\n focus(i - 1);\n } else if (e.key === 'ArrowRight' && i < length - 1) {\n focus(i + 1);\n }\n };\n\n const onPaste = (e) => {\n e.preventDefault();\n const text = (e.clipboardData || window.clipboardData).getData('text') || '';\n const digits = text.replace(/\\D/g, '').slice(0, length).split('');\n const next = cells.slice();\n for (let k = 0; k < length; k++) next[k] = digits[k] || '';\n setCells(next);\n focus(Math.min(digits.length, length - 1));\n emit(next);\n };\n\n return (\n
\n
\n {cells.map((c, i) => (\n \n (inputsRef.current[i] = el)}\n className=\"aa-code-input\"\n value={c}\n maxLength={1}\n inputMode=\"numeric\"\n aria-label={'验证码第 ' + (i + 1) + ' 位'}\n onFocus={() => setFocusedIndex(i)}\n onInput={(e) => onInput(e, i)}\n onKeyDown={(e) => onKeydown(e, i)}\n />\n
\n ))}\n
\n
{tip}
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"passwordinput","name":"密码输入 PasswordInput","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/passwordinput.json","category":"input","specLines":["- 密码可见切换","- 强度检测(弱/中/强)","- 可配置规则","- 支持一键生成强密码","- 适用于注册、修改密码"],"specSummary":"- 密码可见切换","contract":{"variants":["注册密码输入(带强度与生成)","基础密码输入"],"dims":[{"name":"visibilityToggle","values":["support"]},{"name":"strength","values":["weak-medium-strong","none"]},{"name":"generate","values":["one-click","none"]}],"unknowns":["强度判定的具体规则细节"],"anatomy":{"input":"密码输入框(圆点掩码)","eye":"密码可见切换","strength-meter":"强度检测提示:弱 / 中 / 强"},"doNot":["强度指示的配色具体值"],"usage":["密码可见切换","强度检测(弱/中/强)","可配置规则","支持一键生成强密码","适用于注册、修改密码"],"structure":{"rules":"强度检测规则可配置"}},"files":{"html":"../frameworks/PasswordInput.html","css":"../frameworks/PasswordInput.css","jsx":"../frameworks/PasswordInput.jsx","vue2":"../frameworks/PasswordInput.vue2.vue","vue3":"../frameworks/PasswordInput.vue3.vue"},"sizes":{"html":3628,"css":1063,"jsx":2124,"vue2":2171,"vue3":2161},"sources":{"html":"\n\n\n \n \n Aurora Admin · PasswordInput\n \n \n\n\n
\n

PasswordInput 密码输入

\n

可见切换 + 强度检测,适用于注册 / 修改密码等场景。

\n\n
\n
基础用法(含强度条)
\n
\n
\n \n \n
\n
\n \n \n \n
\n
\n
\n
\n\n
\n
关闭强度条
\n
\n
\n \n \n
\n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin PasswordInput 样式 — 对齐 组件7.txt PasswordInput:可见切换 + 强度检测 */\n\n.aa-pwd { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); width: 260px; }\n.aa-pwd-field { display: flex; align-items: center; border: 1px solid #E8ECF1; border-radius: 6px; padding: 0 10px; height: 36px; }\n.aa-pwd-field.is-focus { border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-pwd-input { flex: 1; border: none; outline: none; font-size: 14px; color: #262626; background: none; height: 100%; }\n.aa-pwd-toggle { border: none; background: none; cursor: pointer; color: #6E6E6E; font-size: 13px; padding: 0 4px; }\n.aa-pwd-strength { display: flex; gap: 4px; margin-top: 8px; }\n.aa-pwd-bar { flex: 1; height: 4px; border-radius: 2px; background: #E8ECF1; transition: background .2s; }\n.aa-pwd-bar.on.weak { background: #CF1322; }\n.aa-pwd-bar.on.mid { background: #8C5A00; }\n.aa-pwd-bar.on.strong { background: #2E7D0A; }\n.aa-pwd-tip { font-size: 12px; margin-top: 4px; color: #6E6E6E; }\n","jsx":"import React, { useState, useMemo, useCallback } from 'react';\nimport './PasswordInput.css';\n\nfunction levelOf(v) {\n if (!v) return '';\n let s = 0;\n if (v.length >= 8) s++;\n if (/[a-z]/.test(v) && /[A-Z]/.test(v)) s++;\n if (/\\d/.test(v)) s++;\n if (/[^A-Za-z0-9]/.test(v)) s++;\n if (v.length >= 12) s++;\n if (s <= 2) return 'weak';\n if (s <= 3) return 'mid';\n return 'strong';\n}\n\nexport default function PasswordInput({ value = '', placeholder = '请输入密码', showStrength = true, onChange }) {\n const [visible, setVisible] = useState(false);\n const [focused, setFocused] = useState(false);\n\n const level = useMemo(() => levelOf(value), [value]);\n const tip = useMemo(() => {\n if (level === 'weak') return '弱:建议使用字母、数字与符号组合';\n if (level === 'mid') return '中:再增加长度或符号更安全';\n if (level === 'strong') return '强:密码强度良好';\n return '';\n }, [level]);\n\n const handle = useCallback(\n (e) => {\n if (onChange) onChange(e.target.value);\n },\n [onChange]\n );\n\n const barClass = (i) => {\n if (!showStrength || !level) return '';\n const on =\n (level === 'weak' && i < 1) || (level === 'mid' && i < 2) || (level === 'strong' && i < 3);\n return on ? 'on ' + level : '';\n };\n\n return (\n
\n
\n setFocused(true)}\n onBlur={() => setFocused(false)}\n />\n \n
\n {showStrength && (\n
\n {[0, 1, 2].map((i) => (\n \n ))}\n
\n )}\n
{tip}
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"phoneinput","name":"手机号输入 PhoneInput","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/phoneinput.json","category":"input","specLines":["- 国际区号选择","- 自动格式化","- 支持校验","- 适用于注册、登录"],"specSummary":"- 国际区号选择","contract":{"variants":["国际手机号输入","国内手机号输入"],"dims":[{"name":"region","values":["intl(区号选择)","cn-only"]},{"name":"format","values":["auto-format"]},{"name":"validation","values":["support"]}],"unknowns":["各国格式化规则清单"],"anatomy":{"region-select":"国际区号选择(样式沿用选择器)","input":"号码输入框,自动格式化"},"doNot":["区号下拉面板样式(沿用选择器规格)"],"usage":["国际区号选择","自动格式化","支持校验","适用于注册、登录"],"structure":{"format":"自动格式化"}},"files":{"html":"../frameworks/PhoneInput.html","css":"../frameworks/PhoneInput.css","jsx":"../frameworks/PhoneInput.jsx","vue2":"../frameworks/PhoneInput.vue2.vue","vue3":"../frameworks/PhoneInput.vue3.vue"},"sizes":{"html":2339,"css":956,"jsx":1810,"vue2":1859,"vue3":1845},"sources":{"html":"\n\n\n \n \n Aurora Admin · PhoneInput\n \n \n\n\n
\n

PhoneInput 手机号输入

\n

国际区号选择 + 自动格式化 + 校验,适用于注册、登录。

\n\n
\n
基础用法
\n
\n \n \n
\n
支持中国大陆、香港、美、日等区号
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin PhoneInput 样式 — 国际区号 + 手机号 */\n.aa-phone { display: inline-flex; align-items: stretch; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); border: 1px solid #E8ECF1; border-radius: 6px; height: 36px; background: #fff; overflow: hidden; }\n.aa-phone.is-focus { border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-phone.is-error { border-color: #CF1322; }\n.aa-phone-code { border: none; border-right: 1px solid #E8ECF1; background: #FAFAFA; color: #262626; font-size: 14px; padding: 0 8px; outline: none; cursor: pointer; }\n.aa-phone-input { flex: 1; border: none; outline: none; padding: 0 10px; font-size: 14px; color: #262626; background: none; min-width: 160px; }\n.aa-phone-input::placeholder { color: #767676; }\n.aa-phone-tip { font-size: 12px; margin-top: 4px; color: #6E6E6E; }\n.aa-phone-tip.error { color: #CF1322; }\n.aa-phone-tip.ok { color: #2E7D0A; }\n","jsx":"import React, { useState, useMemo } from 'react';\nimport './PhoneInput.css';\n\nconst CODES = [\n { value: '+86', label: '中国大陆' },\n { value: '+852', label: '香港' },\n { value: '+1', label: '美国' },\n { value: '+81', label: '日本' }\n];\n\nexport default function PhoneInput({ modelValue = '', placeholder = '请输入手机号', onChange }) {\n const [code, setCode] = useState('+86');\n const [number, setNumber] = useState(modelValue);\n const [focused, setFocused] = useState(false);\n\n const valid = useMemo(\n () => (code === '+86' ? /^1[3-9]\\d{9}$/.test(number) : number.length >= 6 && number.length <= 15),\n [code, number]\n );\n const tipClass = !number ? '' : valid ? 'ok' : 'error';\n const tipText = !number ? '支持中国大陆、香港、美、日等区号' : valid ? '格式正确:' + code + ' ' + number : '手机号格式不正确';\n\n const onInput = (e) => {\n const v = e.target.value.replace(/\\D/g, '').slice(0, 11);\n e.target.value = v;\n setNumber(v);\n if (onChange) onChange(code + ' ' + v);\n };\n\n return (\n
\n
\n \n setFocused(true)}\n onBlur={() => setFocused(false)}\n />\n
\n
{tipText}
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"messagepro","name":"全局提示增强 MessagePro","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/messagepro.json","category":"feedback","specLines":["- 支持多种类型:成功、错误、警告、信息、加载","- 可带关闭按钮、操作按钮","- 可配置持续时间、位置","- 支持自定义图标","- 可堆叠显示"],"specSummary":"- 支持多种类型:成功、错误、警告、信息、加载","contract":{"variants":["成功","错误(可关闭)","警告(带操作按钮)","加载中"],"dims":[{"name":"type","values":["success","error","warning","info","loading"]},{"name":"action","values":["none","close","button"]}],"unknowns":["默认持续时间","堆叠的最大数量与间距"],"anatomy":{"item":"消息项:支持多种类型","close":"可带关闭按钮、操作按钮","config":"可配置持续时间、位置","stack":"可堆叠显示"},"doNot":["消息队列的最大条数","动画时序"],"usage":["支持多种类型:成功、错误、警告、信息、加载","可带关闭按钮、操作按钮","可配置持续时间、位置","支持自定义图标","可堆叠显示"],"structure":{"types":"5 种","stack":"可堆叠","duration":"可配置"}},"files":{"html":"../frameworks/MessagePro.html","css":"../frameworks/MessagePro.css","jsx":"../frameworks/MessagePro.jsx","vue2":"../frameworks/MessagePro.vue2.vue","vue3":"../frameworks/MessagePro.vue3.vue"},"sizes":{"html":2491,"css":1272,"jsx":1600,"vue2":1675,"vue3":1534},"sources":{"html":"\n\n\n \n \n Aurora Admin · MessagePro(普通 H5)\n \n \n \n\n\n \n \n \n \n \n\n \n\n\n","css":"/* Aurora Admin MessagePro 样式 — 对齐 组件7.txt MessagePro:顶部居中全局提示,可堆叠,自动消失 */\n\n.aa-message-wrap { position: fixed; top: 16px; left: 50%; transform: translateX(-50%); z-index: 3000;\n display: flex; flex-direction: column; gap: 8px; pointer-events: none; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-message { pointer-events: auto; min-width: 200px; max-width: 440px; background: #fff; border: 1px solid #E8ECF1;\n border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); padding: 10px 14px; display: flex; align-items: center; gap: 8px; font-size: 14px; color: #262626; }\n.aa-message-icon { width: 16px; height: 16px; border-radius: 50%; flex-shrink: 0; color: #fff; font-size: 11px; text-align: center; line-height: 16px; }\n.aa-message.is-success .aa-message-icon { background: #2E7D0A; }\n.aa-message.is-error .aa-message-icon { background: #CF1322; }\n.aa-message.is-warning .aa-message-icon { background: #8C5A00; }\n.aa-message.is-info .aa-message-icon { background: #2F54EB; }\n.aa-message.is-loading .aa-message-icon { background: #2F54EB; }\n.aa-message-content { flex: 1; }\n.aa-message-close { color: #6E6E6E; cursor: pointer; font-size: 14px; line-height: 1; }\n","jsx":"import React, { forwardRef, useImperativeHandle, useState, useRef } from 'react';\nimport './MessagePro.css';\n\nconst ICON = { success: '✓', error: '✕', warning: '!', info: 'i', loading: '…' };\n\nconst MessagePro = forwardRef(function MessagePro({ duration = 3000 }, ref) {\n const [list, setList] = useState([]);\n const idRef = useRef(0);\n\n function close(id) { setList(l => l.filter(m => m.id !== id)); }\n function open(opts) {\n const id = ++idRef.current;\n setList(l => [...l, { id, type: opts.type || 'info', content: opts.content, closable: opts.closable }]);\n const d = opts.duration == null ? duration : opts.duration;\n if (d > 0) setTimeout(() => close(id), d);\n return id;\n }\n\n useImperativeHandle(ref, () => ({\n open,\n close,\n success: (c, d) => open({ type: 'success', content: c, duration: d }),\n error: (c, d) => open({ type: 'error', content: c, duration: d }),\n warning: (c, d) => open({ type: 'warning', content: c, duration: d }),\n info: (c, d) => open({ type: 'info', content: c, duration: d }),\n loading: (c, d) => open({ type: 'loading', content: c, duration: d })\n }));\n\n return (\n
\n {list.map(m => (\n
\n {ICON[m.type] || 'i'}\n {m.content}\n {m.closable !== false ? close(m.id)}>✕ : null}\n
\n ))}\n
\n );\n});\n\nexport default MessagePro;\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"notificationpro","name":"通知提醒增强 NotificationPro","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/notificationpro.json","category":"feedback","specLines":["- 右上角弹出","- 可带标题、描述、图标、操作按钮","- 停留时间可配置,可手动关闭","- 支持折叠、展开","- 适用于系统通知、任务结果"],"specSummary":"- 右上角弹出","contract":{"variants":["成功(标题+描述)","错误(带操作按钮)","信息","折叠态"],"dims":[{"name":"type","values":["success","error","warning","info"]},{"name":"state","values":["expanded","collapsed"]}],"unknowns":["默认宽度","最多同时显示的条数"],"anatomy":{"position":"右上角弹出","content":"可带标题、描述、图标、操作按钮","duration":"停留时间可配置,可手动关闭","collapse":"支持折叠、展开"},"doNot":["通知中心的列表页"],"usage":["右上角弹出","可带标题、描述、图标、操作按钮","停留时间可配置,可手动关闭","支持折叠、展开","适用于系统通知、任务结果"],"structure":{"position":"右上角","content":"标题 + 描述 + 操作"}},"files":{"html":"../frameworks/NotificationPro.html","css":"../frameworks/NotificationPro.css","jsx":"../frameworks/NotificationPro.jsx","vue2":"../frameworks/NotificationPro.vue2.vue","vue3":"../frameworks/NotificationPro.vue3.vue"},"sizes":{"html":2592,"css":1368,"jsx":1711,"vue2":1714,"vue3":1594},"sources":{"html":"\n\n\n \n \n Aurora Admin · NotificationPro(普通 H5)\n \n \n \n\n\n \n \n \n \n\n \n\n\n","css":"/* Aurora Admin NotificationPro 样式 — 对齐 组件7.txt NotificationPro:右上角通知,标题+描述+图标,可关闭 */\n\n.aa-notification-wrap { position: fixed; top: 16px; right: 16px; z-index: 3000; width: 320px;\n display: flex; flex-direction: column; gap: 10px; pointer-events: none;\n font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-notification { pointer-events: auto; background: #fff; border: 1px solid #E8ECF1; border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.12); padding: 14px 16px; display: flex; gap: 10px; }\n.aa-notification-icon { width: 18px; height: 18px; border-radius: 50%; flex-shrink: 0; color: #fff; font-size: 12px; text-align: center; line-height: 18px; margin-top: 2px; }\n.aa-notification.is-success .aa-notification-icon { background: #2E7D0A; }\n.aa-notification.is-error .aa-notification-icon { background: #CF1322; }\n.aa-notification.is-warning .aa-notification-icon { background: #8C5A00; }\n.aa-notification.is-info .aa-notification-icon { background: #2F54EB; }\n.aa-notification-body { flex: 1; }\n.aa-notification-title { font-size: 14px; font-weight: 500; color: #1F2329; }\n.aa-notification-desc { font-size: 13px; color: #6E6E6E; margin-top: 4px; line-height: 1.5; }\n.aa-notification-close { color: #6E6E6E; cursor: pointer; font-size: 14px; line-height: 1; }\n","jsx":"import React, { forwardRef, useImperativeHandle, useState, useRef } from 'react';\nimport './NotificationPro.css';\n\nconst ICON = { success: '✓', error: '✕', warning: '!', info: 'i' };\n\nconst NotificationPro = forwardRef(function NotificationPro({ duration = 4500 }, ref) {\n const [list, setList] = useState([]);\n const idRef = useRef(0);\n\n function close(id) { setList(l => l.filter(m => m.id !== id)); }\n function open(opts) {\n const id = ++idRef.current;\n setList(l => [...l, { id, type: opts.type || 'info', title: opts.title, desc: opts.desc }]);\n const d = opts.duration == null ? duration : opts.duration;\n if (d > 0) setTimeout(() => close(id), d);\n return id;\n }\n\n useImperativeHandle(ref, () => ({\n open,\n close,\n success: (t, d, dur) => open({ type: 'success', title: t, desc: d, duration: dur }),\n error: (t, d, dur) => open({ type: 'error', title: t, desc: d, duration: dur }),\n warning: (t, d, dur) => open({ type: 'warning', title: t, desc: d, duration: dur }),\n info: (t, d, dur) => open({ type: 'info', title: t, desc: d, duration: dur })\n }));\n\n return (\n
\n {list.map(m => (\n
\n {ICON[m.type] || 'i'}\n
\n
{m.title}
\n {m.desc ?
{m.desc}
: null}\n
\n close(m.id)}>✕\n
\n ))}\n
\n );\n});\n\nexport default NotificationPro;\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"progressvariants","name":"进度条变体 ProgressVariants","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/progressvariants.json","category":"feedback","specLines":["- 线型、圆形、仪表盘型","- 支持状态颜色(成功、警告、错误)","- 可显示百分比文字","- 可带图标","- 适用于上传、任务进度"],"specSummary":"- 线型、圆形、仪表盘型","contract":{"variants":["线型 + 百分比","圆形","仪表盘型","成功状态","错误状态(无文字)"],"dims":[{"name":"shape","values":["line","circle","dashboard"]},{"name":"status","values":["normal","success","warning","error"]},{"name":"showText","values":["true","false"]}],"unknowns":["线型的高度与圆角","圆形/仪表盘的默认直径"],"anatomy":{"track":"轨道","bar":"进度条","text":"可显示百分比文字","status":"支持状态颜色(成功、警告、错误)"},"doNot":["环形动画的缓动"],"usage":["线型、圆形、仪表盘型","支持状态颜色(成功、警告、错误)","可显示百分比文字","可带图标","适用于上传、任务进度"],"structure":{"shapes":"线型 / 圆形 / 仪表盘","status":"4 种"}},"files":{"html":"../frameworks/ProgressVariants.html","css":"../frameworks/ProgressVariants.css","jsx":"../frameworks/ProgressVariants.jsx","vue2":"../frameworks/ProgressVariants.vue2.vue","vue3":"../frameworks/ProgressVariants.vue3.vue"},"sizes":{"html":2367,"css":1451,"jsx":1590,"vue2":1927,"vue3":1799},"sources":{"html":"\n\n\n \n \n Aurora Admin · ProgressVariants(普通 H5)\n \n \n \n\n\n
\n
\n
\n
\n 完成\n
\n
\n
\n
\n
\n 60%\n
\n
\n
\n\n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin ProgressVariants 样式 — 对齐 组件7.txt ProgressVariants:线型/圆形/仪表盘,状态色,百分比 */\n\n.aa-progress { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-progress-line-wrap { display: flex; align-items: center; }\n.aa-progress-line { flex: 1; height: 8px; background: #E8ECF1; border-radius: 4px; overflow: hidden; }\n.aa-progress-line-fill { height: 100%; background: #2F54EB; border-radius: 4px; transition: width .3s; }\n.aa-progress.is-success .aa-progress-line-fill { background: #2E7D0A; }\n.aa-progress.is-warning .aa-progress-line-fill { background: #8C5A00; }\n.aa-progress.is-error .aa-progress-line-fill { background: #CF1322; }\n.aa-progress-text { margin-left: 8px; font-size: 13px; min-width: 36px; }\n.aa-progress.is-success .aa-progress-text { color: #2E7D0A; }\n.aa-progress.is-warning .aa-progress-text { color: #8C5A00; }\n.aa-progress.is-error .aa-progress-text { color: #CF1322; }\n.aa-progress-circle { position: relative; display: inline-flex; }\n.aa-progress-circle-text { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 16px; font-weight: 500; }\n.aa-progress-circle.is-success .aa-progress-circle-text { color: #2E7D0A; }\n.aa-progress-circle.is-warning .aa-progress-circle-text { color: #8C5A00; }\n.aa-progress-circle.is-error .aa-progress-circle-text { color: #CF1322; }\n","jsx":"import React from 'react';\nimport './ProgressVariants.css';\n\nconst R = 42;\nconst CIRC = 2 * Math.PI * R;\nconst COLORS = { success: '#2E7D0A', warning: '#8C5A00', error: '#CF1322', normal: '#2F54EB' };\n\nexport default function ProgressVariants({ type = 'line', percent = 0, status = 'normal', showInfo = true }) {\n const color = COLORS[status] || COLORS.normal;\n if (type === 'line') {\n return (\n
\n
\n
\n {showInfo ? {status === 'success' ? '完成' : percent + '%'} : null}\n
\n
\n );\n }\n const isDash = type === 'dashboard';\n const arcLen = isDash ? CIRC * 0.75 : CIRC;\n return (\n
\n \n \n \n \n {showInfo ? {percent}% : null}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"skeletonpro","name":"骨架屏增强 SkeletonPro","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/skeletonpro.json","category":"feedback","specLines":["- 支持多种占位形状","- 可组合成页面级骨架","- 微光动画","- 可配置加载速度","- 切换真实内容时淡入"],"specSummary":"- 支持多种占位形状","contract":{"variants":["文本行","头像圆","图片/区块","静态占位"],"dims":[{"name":"shape","values":["text","circle","rect","button"]},{"name":"animation","values":["shimmer","none"]}],"unknowns":["微光动画的周期与颜色","淡入时长"],"anatomy":{"shapes":"支持多种占位形状","compose":"可组合成页面级骨架","animation":"微光动画","transition":"切换真实内容时淡入"},"doNot":["各业务页面的具体骨架模板"],"usage":["支持多种占位形状","可组合成页面级骨架","微光动画","可配置加载速度","切换真实内容时淡入"],"structure":{"shapes":"文本 / 圆 / 矩形 / 按钮","animation":"微光"}},"files":{"html":"../frameworks/SkeletonPro.html","css":"../frameworks/SkeletonPro.css","jsx":"../frameworks/SkeletonPro.jsx","vue2":"../frameworks/SkeletonPro.vue2.vue","vue3":"../frameworks/SkeletonPro.vue3.vue"},"sizes":{"html":1316,"css":1018,"jsx":827,"vue2":918,"vue3":803},"sources":{"html":"\n\n\n \n \n Aurora Admin · SkeletonPro(普通 H5)\n \n \n \n\n\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin SkeletonPro 样式 — 对齐 组件7.txt SkeletonPro:骨架屏占位,微光动画 */\n\n.aa-skeleton { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-skeleton-row { display: flex; gap: 12px; align-items: center; margin-bottom: 12px; }\n.aa-skeleton-avatar { width: 40px; height: 40px; border-radius: 50%; background: #E8ECF1; flex-shrink: 0; }\n.aa-skeleton-lines { flex: 1; }\n.aa-skeleton-line { height: 12px; background: #E8ECF1; border-radius: 4px; margin-bottom: 8px; }\n.aa-skeleton-block { height: 120px; background: #E8ECF1; border-radius: 8px; margin-bottom: 12px; }\n.aa-skeleton.is-active .aa-skeleton-avatar,\n.aa-skeleton.is-active .aa-skeleton-line,\n.aa-skeleton.is-active .aa-skeleton-block { background: linear-gradient(90deg, #E8ECF1 25%, #F0F5FF 37%, #E8ECF1 63%); background-size: 400% 100%; animation: aa-shimmer 1.4s ease infinite; }\n@keyframes aa-shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }\n","jsx":"import React from 'react';\nimport './SkeletonPro.css';\n\nexport default function SkeletonPro({ rows = 3, avatar = false, block = false, active = true }) {\n return (\n
\n {avatar ? (\n
\n
\n
\n
\n
\n
\n
\n ) : null}\n {block ?
: null}\n {Array.from({ length: rows }).map((_, i) => (\n
\n ))}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"emptypro","name":"空状态增强 EmptyPro","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/emptypro.json","category":"feedback","specLines":["- 多种场景:无数据、无权限、搜索无结果、网络错误","- 插画可静态或动效","- 主按钮、次按钮","- 可自定义文案","- 适用于所有列表页"],"specSummary":"- 多种场景:无数据、无权限、搜索无结果、网络错误","contract":{"variants":["无数据","无权限","搜索无结果","网络错误(动效)"],"dims":[{"name":"scene","values":["no-data","no-permission","no-result","network-error"]},{"name":"illustration","values":["static","animated"]}],"unknowns":["插画的尺寸","按钮的尺寸与间距"],"anatomy":{"scene":"多种场景:无数据、无权限、搜索无结果、网络错误","illustration":"插画可静态或动效","actions":"主按钮、次按钮","text":"可自定义文案"},"doNot":["插画素材本身"],"usage":["多种场景:无数据、无权限、搜索无结果、网络错误","插画可静态或动效","主按钮、次按钮","可自定义文案","适用于所有列表页"],"structure":{"scenes":"4 种","actions":"主 + 次按钮"}},"files":{"html":"../frameworks/EmptyPro.html","css":"../frameworks/EmptyPro.css","jsx":"../frameworks/EmptyPro.jsx","vue2":"../frameworks/EmptyPro.vue2.vue","vue3":"../frameworks/EmptyPro.vue3.vue"},"sizes":{"html":1933,"css":1057,"jsx":1387,"vue2":1631,"vue3":1629},"sources":{"html":"\n\n\n \n \n Aurora Admin · EmptyPro(普通 H5)\n \n \n \n\n\n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin EmptyPro 样式 — 对齐 组件7.txt EmptyPro:无数据/无权限/搜索无结果/网络错误 等空状态 */\n\n.aa-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 40px 20px;\n text-align: center; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-empty-icon { font-size: 48px; margin-bottom: 12px; }\n.aa-empty-title { font-size: 16px; font-weight: 500; color: #1F2329; }\n.aa-empty-desc { font-size: 13px; color: #6E6E6E; margin-top: 8px; line-height: 1.5; max-width: 300px; }\n.aa-empty-actions { margin-top: 16px; display: flex; gap: 8px; }\n.aa-empty-btn { height: 32px; padding: 0 16px; border-radius: 6px; border: 1px solid #E8ECF1; background: #fff; color: #262626; cursor: pointer; font-size: 14px; }\n.aa-empty-btn:hover { border-color: #C9D4F2; color: #2F54EB; }\n.aa-empty-btn.primary { background: #2F54EB; border-color: #2F54EB; color: #fff; }\n.aa-empty-btn.primary:hover { background: #1D39C4; color: #fff; }\n","jsx":"import React from 'react';\nimport './EmptyPro.css';\n\nconst PRESET = {\n empty: { icon: '📭', title: '暂无数据', desc: '当前列表为空,试试新增一条记录。' },\n permission: { icon: '🔒', title: '无访问权限', desc: '你没有查看该内容的权限。' },\n search: { icon: '🔍', title: '未找到结果', desc: '换个关键词或筛选条件再试。' },\n error: { icon: '⚠️', title: '加载失败', desc: '网络异常,请稍后重试。' }\n};\n\nexport default function EmptyPro({ type = 'empty', icon, title, desc, actionText, secondaryText, onAction, onSecondary }) {\n const p = PRESET[type] || {};\n const resolved = {\n icon: icon || p.icon || '📭',\n title: title || p.title || '暂无数据',\n desc: desc || p.desc || ''\n };\n return (\n
\n
{resolved.icon}
\n
{resolved.title}
\n {resolved.desc ?
{resolved.desc}
: null}\n {(actionText || secondaryText) ? (\n
\n {actionText ? : null}\n {secondaryText ? : null}\n
\n ) : null}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"loadingoverlay","name":"加载遮罩 LoadingOverlay","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/loadingoverlay.json","category":"feedback","specLines":["- 全屏或局部遮罩","- 转圈品牌色","- 可配置文案","- 阻止交互","- 适用于页面切换、数据提交"],"specSummary":"- 全屏或局部遮罩","contract":{"variants":["局部遮罩","局部 + 文案","全屏 + 文案"],"dims":[{"name":"coverage","values":["fullscreen","local"]},{"name":"text","values":["none","custom"]}],"unknowns":["遮罩透明度","转圈尺寸"],"anatomy":{"mask":"全屏或局部遮罩","spinner":"转圈品牌色","text":"可配置文案","behavior":"阻止交互"},"doNot":["延迟出现的阈值策略"],"usage":["全屏或局部遮罩","转圈品牌色","可配置文案","阻止交互","适用于页面切换、数据提交"],"structure":{"spinner":"品牌色","block":"阻止交互"}},"files":{"html":"../frameworks/LoadingOverlay.html","css":"../frameworks/LoadingOverlay.css","jsx":"../frameworks/LoadingOverlay.jsx","vue2":"../frameworks/LoadingOverlay.vue2.vue","vue3":"../frameworks/LoadingOverlay.vue3.vue"},"sizes":{"html":1688,"css":1023,"jsx":434,"vue2":544,"vue3":497},"sources":{"html":"\n\n\n \n \n Aurora Admin · LoadingOverlay(普通 H5)\n \n \n \n\n\n \n
\n 局部内容区域\n \n
\n
\n
加载中…
\n
\n
\n\n
\n
\n
正在提交,请稍候
\n
\n\n \n\n\n","css":"/* Aurora Admin LoadingOverlay 样式 — 对齐 组件7.txt LoadingOverlay:全屏或局部遮罩,转圈品牌色,阻止交互 */\n\n.aa-loading-overlay { position: fixed; inset: 0; background: rgba(255,255,255,0.6); z-index: 4000;\n display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;\n font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-loading-overlay.is-inline { position: absolute; border-radius: inherit; }\n.aa-loading-overlay.is-dark { background: rgba(0,0,0,0.45); }\n.aa-loading-spinner { width: 32px; height: 32px; border: 3px solid #E8ECF1; border-top-color: #2F54EB; border-radius: 50%; animation: aa-spin .8s linear infinite; }\n.aa-loading-overlay.is-dark .aa-loading-spinner { border-color: rgba(255,255,255,0.3); border-top-color: #fff; }\n.aa-loading-text { font-size: 14px; color: #262626; }\n.aa-loading-overlay.is-dark .aa-loading-text { color: #fff; }\n@keyframes aa-spin { to { transform: rotate(360deg); } }\n","jsx":"import React from 'react';\nimport './LoadingOverlay.css';\n\nexport default function LoadingOverlay({ visible = false, text = '', inline = false, dark = false }) {\n if (!visible) return null;\n return (\n
\n
\n {text ?
{text}
: null}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"resultvariants","name":"结果页变体 ResultVariants","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/resultvariants.json","category":"feedback","specLines":["- 成功、失败、警告、信息","- 大图标、标题、描述","- 操作按钮","- 可附带摘要卡片、二维码","- 适用于提交完成、支付结果"],"specSummary":"- 成功、失败、警告、信息","contract":{"variants":["成功","失败","警告","附带摘要卡片","附带二维码"],"dims":[{"name":"status","values":["success","error","warning","info"]},{"name":"extra","values":["none","summary-card","qrcode"]}],"unknowns":["图标尺寸","内容区最大宽度"],"anatomy":{"icon":"大图标","text":"标题、描述","actions":"操作按钮","extra":"可附带摘要卡片、二维码"},"doNot":["各业务结果的图标素材"],"usage":["成功、失败、警告、信息","大图标、标题、描述","操作按钮","可附带摘要卡片、二维码","适用于提交完成、支付结果"],"structure":{"status":"4 种","extra":"摘要卡片 / 二维码"}},"files":{"html":"../frameworks/ResultVariants.html","css":"../frameworks/ResultVariants.css","jsx":"../frameworks/ResultVariants.jsx","vue2":"../frameworks/ResultVariants.vue2.vue","vue3":"../frameworks/ResultVariants.vue3.vue"},"sizes":{"html":1931,"css":1381,"jsx":940,"vue2":1116,"vue3":1090},"sources":{"html":"\n\n\n \n \n Aurora Admin · ResultVariants(普通 H5)\n \n \n \n\n\n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin ResultVariants 样式 — 对齐 组件7.txt ResultVariants:成功/失败/警告/信息 结果页 */\n\n.aa-result { text-align: center; padding: 40px 20px; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-result-icon { width: 64px; height: 64px; border-radius: 50%; margin: 0 auto 16px; display: flex; align-items: center; justify-content: center; font-size: 32px; color: #fff; }\n.aa-result.is-success .aa-result-icon { background: #2E7D0A; }\n.aa-result.is-error .aa-result-icon { background: #CF1322; }\n.aa-result.is-warning .aa-result-icon { background: #8C5A00; }\n.aa-result.is-info .aa-result-icon { background: #2F54EB; }\n.aa-result-title { font-size: 20px; font-weight: 500; color: #1F2329; }\n.aa-result-desc { font-size: 14px; color: #6E6E6E; margin-top: 8px; line-height: 1.6; }\n.aa-result-actions { margin-top: 24px; display: flex; gap: 8px; justify-content: center; }\n.aa-result-btn { height: 34px; padding: 0 18px; border-radius: 6px; border: 1px solid #E8ECF1; background: #fff; color: #262626; cursor: pointer; font-size: 14px; }\n.aa-result-btn:hover { border-color: #C9D4F2; color: #2F54EB; }\n.aa-result-btn.primary { background: #2F54EB; border-color: #2F54EB; color: #fff; }\n.aa-result-btn.primary:hover { background: #1D39C4; color: #fff; }\n.aa-result-extra { margin-top: 20px; }\n","jsx":"import React from 'react';\nimport './ResultVariants.css';\n\nconst ICON = { success: '✓', error: '✕', warning: '!', info: 'i' };\n\nexport default function ResultVariants({ type = 'success', title, desc, actionText, secondaryText, onAction, onSecondary, extra }) {\n return (\n
\n
{ICON[type] || 'i'}
\n
{title}
\n {desc ?
{desc}
: null}\n {(actionText || secondaryText) ? (\n
\n {actionText ? : null}\n {secondaryText ? : null}\n
\n ) : null}\n {extra ?
{extra}
: null}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"exceptionvariants","name":"异常页变体 ExceptionVariants","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/exceptionvariants.json","category":"feedback","specLines":["- 403、404、500、502","- 大号状态码或插画","- 标题、描述","- 返回首页、重试按钮","- 适用于错误处理"],"specSummary":"- 403、404、500、502","contract":{"variants":["无权限","页面不存在","服务器错误","网关错误(插画)"],"dims":[{"name":"code","values":["403","404","500","502"]},{"name":"display","values":["code","illustration"]}],"unknowns":["状态码字号","插画与状态码的取舍规则"],"anatomy":{"code":"大号状态码或插画","text":"标题、描述","actions":"返回首页、重试按钮"},"doNot":["插画素材本身","错误日志上报"],"usage":["403、404、500、502","大号状态码或插画","标题、描述","返回首页、重试按钮","适用于错误处理"],"structure":{"codes":"403 / 404 / 500 / 502","actions":"返回首页 + 重试"}},"files":{"html":"../frameworks/ExceptionVariants.html","css":"../frameworks/ExceptionVariants.css","jsx":"../frameworks/ExceptionVariants.jsx","vue2":"../frameworks/ExceptionVariants.vue2.vue","vue3":"../frameworks/ExceptionVariants.vue3.vue"},"sizes":{"html":2006,"css":1065,"jsx":1311,"vue2":1521,"vue3":1508},"sources":{"html":"\n\n\n \n \n Aurora Admin · ExceptionVariants(普通 H5)\n \n \n \n\n\n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin ExceptionVariants 样式 — 对齐 组件7.txt ExceptionVariants:403/404/500/502 异常页 */\n\n.aa-exception { text-align: center; padding: 56px 20px; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); color: #262626; }\n.aa-exception-code { font-size: 72px; font-weight: 700; color: #2F54EB; line-height: 1; }\n.aa-exception-emoji { font-size: 64px; }\n.aa-exception-title { font-size: 18px; font-weight: 500; color: #1F2329; margin-top: 12px; }\n.aa-exception-desc { font-size: 14px; color: #6E6E6E; margin-top: 8px; }\n.aa-exception-actions { margin-top: 24px; display: flex; gap: 8px; justify-content: center; }\n.aa-exception-btn { height: 34px; padding: 0 18px; border-radius: 6px; border: 1px solid #E8ECF1; background: #fff; color: #262626; cursor: pointer; font-size: 14px; }\n.aa-exception-btn:hover { border-color: #C9D4F2; color: #2F54EB; }\n.aa-exception-btn.primary { background: #2F54EB; border-color: #2F54EB; color: #fff; }\n.aa-exception-btn.primary:hover { background: #1D39C4; color: #fff; }\n","jsx":"import React from 'react';\nimport './ExceptionVariants.css';\n\nconst PRESET = {\n '403': { emoji: '🔒', title: '403 禁止访问', desc: '抱歉,你没有权限访问此页面。' },\n '404': { emoji: '🔍', title: '404 页面不存在', desc: '你访问的页面已被移除或从未存在。' },\n '500': { emoji: '⚙️', title: '500 服务器错误', desc: '服务器开小差了,请稍后再试。' },\n '502': { emoji: '🔌', title: '502 网关错误', desc: '网关无响应,请检查网络后重试。' }\n};\n\nexport default function ExceptionVariants({ type = '404', emoji, title, desc, onHome, onRetry }) {\n const p = PRESET[type] || {};\n const resolved = {\n emoji: emoji || p.emoji || '⚠️',\n title: title || p.title || '出错了',\n desc: desc || p.desc || ''\n };\n return (\n
\n
{resolved.emoji}
\n
{resolved.title}
\n {resolved.desc ?
{resolved.desc}
: null}\n
\n \n \n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"confirmmodal","name":"确认弹窗 ConfirmModal","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/confirmmodal.json","category":"feedback","specLines":["- 用于危险操作","- 标题、描述、图标","- 确认按钮(红/主色)、取消按钮","- 支持加载状态","- 可勾选“不再提示”"],"specSummary":"- 用于危险操作","contract":{"variants":["危险操作 + 图标","普通确认","确认中","含\"不再提示\""],"dims":[{"name":"tone","values":["danger","primary"]},{"name":"state","values":["default","loading"]},{"name":"feature","values":["icon","dont-remind"]}],"unknowns":["\"不再提示\"的存储方式与作用范围","确认按钮的默认配色"],"anatomy":{"icon":"图标","text":"标题、描述","confirm":"确认按钮(红/主色)","cancel":"取消按钮","extra":"可勾选\"不再提示\""},"doNot":["弹窗尺寸档位(见 Modal 契约)"],"usage":["用于危险操作","标题、描述、图标","确认按钮(红/主色)、取消按钮","支持加载状态","可勾选\"不再提示\""],"structure":{"buttons":"确认(红/主色)+ 取消","loading":"支持"}},"files":{"html":"../frameworks/ConfirmModal.html","css":"../frameworks/ConfirmModal.css","jsx":"../frameworks/ConfirmModal.jsx","vue2":"../frameworks/ConfirmModal.vue2.vue","vue3":"../frameworks/ConfirmModal.vue3.vue"},"sizes":{"html":1713,"css":1917,"jsx":1525,"vue2":1554,"vue3":1564},"sources":{"html":"\n\n\n \n \n Aurora Admin · ConfirmModal(普通 H5)\n \n \n \n\n\n \n\n
\n
\n
\n !\n
删除该成员?
\n
\n
删除后该成员将无法访问系统,且不可恢复。
\n \n
\n \n \n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin ConfirmModal 样式 — 对齐 组件7.txt ConfirmModal:危险操作确认弹窗,确认(红/主)/取消,可加载、可不再提示 */\n\n.aa-modal-mask { position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 2000; display: flex; align-items: center; justify-content: center; }\n.aa-confirm { width: 400px; max-width: 90vw; background: #fff; border-radius: 8px; padding: 24px; box-shadow: 0 8px 24px rgba(0,0,0,0.18);\n font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-confirm-head { display: flex; gap: 12px; align-items: flex-start; }\n.aa-confirm-icon { width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0; color: #fff; font-size: 13px; text-align: center; line-height: 22px; margin-top: 1px; }\n.aa-confirm.is-danger .aa-confirm-icon { background: #CF1322; }\n.aa-confirm.is-primary .aa-confirm-icon { background: #2F54EB; }\n.aa-confirm-title { font-size: 16px; font-weight: 500; color: #1F2329; }\n.aa-confirm-content { font-size: 14px; color: #595959; margin-top: 6px; margin-left: 34px; line-height: 1.6; }\n.aa-confirm-extra { margin: 10px 0 0 34px; font-size: 13px; color: #6E6E6E; display: flex; align-items: center; gap: 6px; }\n.aa-confirm-footer { margin-top: 20px; display: flex; justify-content: flex-end; gap: 8px; }\n.aa-confirm-btn { height: 32px; padding: 0 16px; border-radius: 6px; border: 1px solid #E8ECF1; background: #fff; color: #262626; cursor: pointer; font-size: 14px; }\n.aa-confirm-btn:hover { border-color: #C9D4F2; color: #2F54EB; }\n.aa-confirm-btn.primary { background: #2F54EB; border-color: #2F54EB; color: #fff; }\n.aa-confirm-btn.primary:hover { background: #1D39C4; color: #fff; }\n.aa-confirm-btn.danger { background: #CF1322; border-color: #CF1322; color: #fff; }\n.aa-confirm-btn.danger:hover { background: #cf1322; color: #fff; }\n.aa-confirm-btn:disabled { opacity: .6; cursor: not-allowed; }\n","jsx":"import React, { useState } from 'react';\nimport './ConfirmModal.css';\n\nexport default function ConfirmModal({\n visible = false, title = '确认操作', content = '', confirmType = 'danger',\n confirmText = '确定', cancelText = '取消', loading = false, dontAsk = false,\n onConfirm, onCancel\n}) {\n const [checked, setChecked] = useState(false);\n if (!visible) return null;\n return (\n
{ if (e.target === e.currentTarget) onCancel && onCancel(); }}>\n
\n
\n !\n
{title}
\n
\n
{content}
\n {dontAsk ? (\n \n ) : null}\n
\n \n onConfirm && onConfirm(checked)}\n >\n {loading ? '处理中…' : confirmText}\n \n
\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"alertmodal","name":"提示弹窗 AlertModal","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/alertmodal.json","category":"feedback","specLines":["- 信息提示","- 图标与颜色对应","- 确定按钮","- 支持自定义内容","- 适用于重要提示"],"specSummary":"- 信息提示","contract":{"variants":["成功提示","错误提示","警告提示","自定义内容"],"dims":[{"name":"type","values":["success","error","warning","info"]},{"name":"content","values":["text","custom"]}],"unknowns":["图标尺寸","自定义内容区的最大高度"],"anatomy":{"icon":"图标与颜色对应","text":"信息提示","action":"确定按钮","custom":"支持自定义内容"},"doNot":["弹窗尺寸档位(见 Modal 契约)"],"usage":["信息提示","图标与颜色对应","确定按钮","支持自定义内容","适用于重要提示"],"structure":{"types":"4 种","buttons":"确定"}},"files":{"html":"../frameworks/AlertModal.html","css":"../frameworks/AlertModal.css","jsx":"../frameworks/AlertModal.jsx","vue2":"../frameworks/AlertModal.vue2.vue","vue3":"../frameworks/AlertModal.vue3.vue"},"sizes":{"html":1337,"css":1451,"jsx":854,"vue2":984,"vue3":950},"sources":{"html":"\n\n\n \n \n Aurora Admin · AlertModal(普通 H5)\n \n \n \n\n\n \n\n
\n
\n
\n i\n
系统升级通知
\n
\n
本周六 02:00-04:00 将进行系统维护,期间服务不可用,请提前安排。
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin AlertModal 样式 — 对齐 组件7.txt AlertModal:信息提示弹窗,图标+颜色对应,确定按钮 */\n\n.aa-modal-mask { position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 2000; display: flex; align-items: center; justify-content: center; }\n.aa-alert { width: 380px; max-width: 90vw; background: #fff; border-radius: 8px; padding: 24px; box-shadow: 0 8px 24px rgba(0,0,0,0.18);\n font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-alert-head { display: flex; gap: 12px; align-items: flex-start; }\n.aa-alert-icon { width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0; color: #fff; font-size: 13px; text-align: center; line-height: 22px; margin-top: 1px; }\n.aa-alert.is-success .aa-alert-icon { background: #2E7D0A; }\n.aa-alert.is-error .aa-alert-icon { background: #CF1322; }\n.aa-alert.is-warning .aa-alert-icon { background: #8C5A00; }\n.aa-alert.is-info .aa-alert-icon { background: #2F54EB; }\n.aa-alert-title { font-size: 16px; font-weight: 500; color: #1F2329; }\n.aa-alert-content { font-size: 14px; color: #595959; margin-top: 6px; margin-left: 34px; line-height: 1.6; }\n.aa-alert-footer { margin-top: 20px; display: flex; justify-content: flex-end; }\n.aa-alert-btn { height: 32px; padding: 0 18px; border-radius: 6px; border: none; background: #2F54EB; color: #fff; cursor: pointer; font-size: 14px; }\n.aa-alert-btn:hover { background: #1D39C4; }\n","jsx":"import React from 'react';\nimport './AlertModal.css';\n\nconst ICON = { success: '✓', error: '✕', warning: '!', info: 'i' };\n\nexport default function AlertModal({ visible = false, type = 'info', title = '提示', content = '', okText = '知道了', onOk }) {\n if (!visible) return null;\n return (\n
{ if (e.target === e.currentTarget) onOk && onOk(); }}>\n
\n
\n {ICON[type] || 'i'}\n
{title}
\n
\n
{content}
\n
\n \n
\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"formmodal","name":"表单弹窗 FormModal","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/formmodal.json","category":"feedback","specLines":["- 弹窗内嵌表单","- 表单字段可多列","- 确认提交,校验失败阻止关闭","- 支持加载状态","- 关闭重置"],"specSummary":"- 弹窗内嵌表单","contract":{"variants":["单列表单","双列表单","校验失败(阻止关闭)","提交中"],"dims":[{"name":"columns","values":["one","two"]},{"name":"state","values":["default","validating","loading"]}],"unknowns":["多列时的栅格断点","是否支持草稿保存"],"anatomy":{"body":"弹窗内嵌表单","columns":"表单字段可多列","validate":"确认提交,校验失败阻止关闭","reset":"关闭重置"},"doNot":["表单校验规则(见各输入组件契约)"],"usage":["弹窗内嵌表单","表单字段可多列","确认提交,校验失败阻止关闭","支持加载状态","关闭重置"],"structure":{"columns":"1 或 2 列","behavior":"校验失败阻止关闭"}},"files":{"html":"../frameworks/FormModal.html","css":"../frameworks/FormModal.css","jsx":"../frameworks/FormModal.jsx","vue2":"../frameworks/FormModal.vue2.vue","vue3":"../frameworks/FormModal.vue3.vue"},"sizes":{"html":3237,"css":1597,"jsx":2602,"vue2":2501,"vue3":2415},"sources":{"html":"\n\n\n \n \n Aurora Admin · FormModal\n \n \n\n\n
\n

FormModal 表单弹窗

\n

弹窗内嵌表单,确认提交前进行校验,校验失败阻止关闭。

\n\n
\n \n

\n
\n
\n\n
\n
\n
新建用户
\n
\n
\n \n \n
姓名不能为空
\n
\n
\n \n \n
邮箱格式不正确
\n
\n
\n \n \n
\n
\n
\n \n \n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin FormModal 样式 — 弹窗内嵌表单 */\n.aa-formmodal-mask { position: fixed; inset: 0; background: rgba(0,0,0,0.45); display: flex; align-items: center; justify-content: center; z-index: 1000; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-formmodal { width: 440px; max-width: 92vw; background: #fff; border-radius: 10px; box-shadow: 0 12px 48px rgba(0,0,0,0.2); overflow: hidden; }\n.aa-formmodal-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid #E8ECF1; font-weight: 600; color: #1F2329; font-size: 15px; }\n.aa-formmodal-close { border: none; background: none; font-size: 18px; color: #6E6E6E; cursor: pointer; line-height: 1; }\n.aa-formmodal-close:hover { color: #262626; }\n.aa-formmodal-body { padding: 18px; }\n.aa-form-row { margin-bottom: 14px; }\n.aa-form-label { display: block; font-size: 13px; color: #595959; margin-bottom: 6px; }\n.aa-form-label .req { color: #CF1322; margin-right: 3px; }\n.aa-form-input { width: 100%; border: 1px solid #E8ECF1; border-radius: 6px; padding: 8px 10px; font-size: 14px; color: #262626; outline: none; box-sizing: border-box; transition: border-color .15s, box-shadow .15s; }\n.aa-form-input:focus { border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.2); }\n.aa-form-input.is-error { border-color: #CF1322; }\n.aa-form-err { font-size: 12px; color: #CF1322; margin-top: 4px; }\n.aa-formmodal-foot { display: flex; justify-content: flex-end; gap: 10px; padding: 12px 18px; border-top: 1px solid #E8ECF1; }\n","jsx":"import React, { useState, useEffect } from 'react';\nimport './FormModal.css';\n\nconst DEFAULT_FIELDS = [\n { key: 'name', label: '姓名', required: true, placeholder: '请输入姓名' },\n { key: 'email', label: '邮箱', required: true, placeholder: 'name@example.com' },\n { key: 'role', label: '角色', placeholder: '如:管理员' }\n];\n\nexport default function FormModal({ visible = false, title = '表单', fields = DEFAULT_FIELDS, value = {}, onCancel, onSubmit }) {\n const [form, setForm] = useState(value);\n const [errors, setErrors] = useState({});\n\n useEffect(() => {\n if (visible) { setForm(value); setErrors({}); }\n }, [visible]);\n\n if (!visible) return null;\n\n const onInput = (key, e) => {\n setForm((f) => ({ ...f, [key]: e.target.value }));\n setErrors((er) => { const n = { ...er }; delete n[key]; return n; });\n };\n const submit = () => {\n const errs = {};\n fields.forEach((f) => {\n const v = (form[f.key] || '').trim();\n if (f.required && !v) errs[f.key] = f.label + '不能为空';\n if (f.key === 'email' && v && !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v)) errs[f.key] = '邮箱格式不正确';\n });\n if (Object.keys(errs).length) { setErrors(errs); return; }\n if (onSubmit) onSubmit({ ...form });\n if (onCancel) onCancel();\n };\n\n return (\n
{ if (e.target === e.currentTarget) onCancel && onCancel(); }}>\n
\n
\n {title}\n \n
\n
\n {fields.map((f) => (\n
\n \n onInput(f.key, e)}\n />\n {errors[f.key] &&
{errors[f.key]}
}\n
\n ))}\n
\n
\n \n \n
\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"fullscreenmodal","name":"全屏弹窗 FullScreenModal","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/fullscreenmodal.json","category":"feedback","specLines":["- 全屏展示内容","- 标题栏、关闭按钮","- 内容区滚动","- 底部操作栏","- 适用于复杂表单、详情编辑"],"specSummary":"- 全屏展示内容","contract":{"variants":["默认(含底部操作栏)","提交中","无底部操作栏"],"dims":[{"name":"state","values":["default","loading"]},{"name":"footer","values":["visible","hidden"]}],"unknowns":["标题栏高度","底部操作栏是否固定"],"anatomy":{"header":"标题栏、关闭按钮","body":"内容区滚动","footer":"底部操作栏"},"doNot":["全屏下的分栏与侧栏布局"],"usage":["全屏展示内容","标题栏、关闭按钮","内容区滚动","底部操作栏","适用于复杂表单、详情编辑"],"structure":{"layout":"标题栏 + 滚动内容 + 底部操作栏"}},"files":{"html":"../frameworks/FullScreenModal.html","css":"../frameworks/FullScreenModal.css","jsx":"../frameworks/FullScreenModal.jsx","vue2":"../frameworks/FullScreenModal.vue2.vue","vue3":"../frameworks/FullScreenModal.vue3.vue"},"sizes":{"html":1890,"css":894,"jsx":743,"vue2":727,"vue3":714},"sources":{"html":"\n\n\n \n \n Aurora Admin · FullScreenModal\n \n \n\n\n
\n

FullScreenModal 全屏弹窗

\n

全屏展示内容,标题栏 + 关闭按钮 + 滚动内容区 + 底部操作栏。

\n\n
\n \n
\n
\n\n
\n
\n 商品编辑(全屏)\n \n
\n
\n

基础信息

\n

此处可放置复杂表单、富文本编辑、多区块配置等内容。全屏模式便于处理信息密度高的任务。

\n

内容区可滚动,底部固定操作栏始终可见。

\n

详细描述

\n

支持长文、图片、视频等富媒体内容。关闭弹窗时可通过底部按钮提交或取消。

\n
\n
\n \n \n
\n
\n\n \n\n\n","css":"/* Aurora Admin FullScreenModal 样式 — 全屏弹窗 */\n.aa-fs-mask { position: fixed; inset: 0; background: #F5F7FA; z-index: 1100; display: flex; flex-direction: column; font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); }\n.aa-fs-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 20px; background: #fff; border-bottom: 1px solid #E8ECF1; }\n.aa-fs-title { font-weight: 600; color: #1F2329; font-size: 16px; }\n.aa-fs-close { border: none; background: none; font-size: 20px; color: #6E6E6E; cursor: pointer; line-height: 1; }\n.aa-fs-close:hover { color: #262626; }\n.aa-fs-body { flex: 1; overflow: auto; padding: 20px; color: #595959; font-size: 14px; line-height: 1.7; }\n.aa-fs-foot { display: flex; justify-content: flex-end; gap: 10px; padding: 12px 20px; background: #fff; border-top: 1px solid #E8ECF1; }\n","jsx":"import React from 'react';\nimport './FullScreenModal.css';\n\nexport default function FullScreenModal({ visible = false, title = '全屏弹窗', onCancel, onOk, children }) {\n if (!visible) return null;\n return (\n
\n
\n {title}\n \n
\n
{children}
\n
\n \n \n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"themeswitcher","name":"主题切换 ThemeSwitcher","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/themeswitcher.json","category":"system","specLines":["- 明暗主题","- 品牌色切换","- 实时预览","- 保存偏好","- 适用于个性化"],"specSummary":"- 明暗主题","contract":{"variants":["浅色 + 预设品牌色","深色主题","自定义品牌色"],"dims":[{"name":"mode","values":["light","dark"]},{"name":"brandColor","values":["preset","custom"]}],"unknowns":["预设品牌色的清单","偏好保存的位置(localStorage 键名)"],"anatomy":{"mode":"明暗主题","brand":"品牌色切换","preview":"实时预览","persist":"保存偏好"},"doNot":["暗色令牌的具体取值(见 colors_and_type.css 的 html.aa-dark)"],"usage":["明暗主题","品牌色切换","实时预览","保存偏好","适用于个性化"],"structure":{"modes":"明 / 暗","brand":"可切换","persist":"本地保存"}},"files":{"html":"../frameworks/ThemeSwitcher.html","css":"../frameworks/ThemeSwitcher.css","jsx":"../frameworks/ThemeSwitcher.jsx","vue2":"../frameworks/ThemeSwitcher.vue2.vue","vue3":"../frameworks/ThemeSwitcher.vue3.vue"},"sizes":{"html":2583,"css":1216,"jsx":1165,"vue2":1161,"vue3":1125},"sources":{"html":"\n\n\n \n \n Aurora Admin · ThemeSwitcher\n \n \n\n\n
\n

ThemeSwitcher 主题切换

\n

明暗主题 + 品牌色切换,实时预览,可保存偏好。

\n\n
\n
\n
浅色
\n
深色
\n
\n
\n
\n
\n
\n \n 当前主题:\n
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin ThemeSwitcher 样式 — 明暗主题 + 品牌色切换 */\n.aa-themesw { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }\n.aa-themesw-opt { display: flex; align-items: center; gap: 8px; border: 1px solid #E8ECF1; border-radius: 6px; padding: 8px 12px; cursor: pointer; color: #262626; font-size: 13px; transition: border-color .15s, background .15s, color .15s; }\n.aa-themesw-opt:hover { border-color: #B7C4FF; }\n.aa-themesw-opt.is-active { border-color: #2F54EB; background: #F0F5FF; color: #2F54EB; }\n.aa-themesw-dot { width: 16px; height: 16px; border-radius: 4px; border: 1px solid #E8ECF1; }\n.aa-themesw-dot.dark { background: #1F2329; }\n.aa-themesw-dot.light { background: #fff; }\n.aa-themesw-colors { display: flex; gap: 8px; margin-left: 4px; }\n.aa-themesw-color { width: 22px; height: 22px; border-radius: 50%; cursor: pointer; border: 2px solid transparent; transition: transform .12s; }\n.aa-themesw-color:hover { transform: scale(1.1); }\n.aa-themesw-color.is-active { border-color: #1F2329; }\n.aa-themesw-label { font-size: 12px; color: #6E6E6E; width: 100%; margin-top: 4px; }\n","jsx":"import React from 'react';\nimport './ThemeSwitcher.css';\n\nconst MODES = [\n { value: 'light', label: '浅色' },\n { value: 'dark', label: '深色' }\n];\nconst BRANDS = ['#2F54EB', '#1677FF', '#722ED1', '#13C2C2', '#FA541C', '#EB2F96'];\n\nexport default function ThemeSwitcher({ mode = 'light', brand = '#2F54EB', onModeChange, onBrandChange }) {\n return (\n
\n {MODES.map((m) => (\n onModeChange && onModeChange(m.value)}\n >\n \n {m.label}\n
\n ))}\n
\n {BRANDS.map((c) => (\n onBrandChange && onBrandChange(c)}\n />\n ))}\n
\n
品牌色:{brand}(应用于 CSS 变量 --brand 实时预览)
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"layoutswitcher","name":"布局切换 LayoutSwitcher","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/layoutswitcher.json","category":"system","specLines":["- 侧边、顶部、混合导航","- 侧边栏宽度","- 折叠状态","- 保存偏好","- 适用于布局配置"],"specSummary":"- 侧边、顶部、混合导航","contract":{"variants":["侧边导航 · 展开","侧边导航 · 折叠","顶部导航","混合导航"],"dims":[{"name":"navType","values":["side","top","mixed"]},{"name":"collapsed","values":["expanded","collapsed"]}],"unknowns":["侧边栏宽度的可选值","折叠时的图标尺寸"],"anatomy":{"navType":"侧边、顶部、混合导航","width":"侧边栏宽度","collapsed":"折叠状态","persist":"保存偏好"},"doNot":["各布局的响应式断点"],"usage":["侧边、顶部、混合导航","侧边栏宽度","折叠状态","保存偏好","适用于布局配置"],"structure":{"types":"侧边 / 顶部 / 混合","persist":"本地保存"}},"files":{"html":"../frameworks/LayoutSwitcher.html","css":"../frameworks/LayoutSwitcher.css","jsx":"../frameworks/LayoutSwitcher.jsx","vue2":"../frameworks/LayoutSwitcher.vue2.vue","vue3":"../frameworks/LayoutSwitcher.vue3.vue"},"sizes":{"html":2200,"css":1014,"jsx":930,"vue2":956,"vue3":969},"sources":{"html":"\n\n\n \n \n Aurora Admin · LayoutSwitcher\n \n \n\n\n
\n

LayoutSwitcher 布局切换

\n

侧边 / 顶部 / 混合导航布局切换,保存偏好。

\n\n
\n
\n
\n
\n
侧边布局
\n
\n
\n
\n
顶部布局
\n
\n
\n
\n
混合布局
\n
\n
\n

\n
\n
\n\n \n\n\n","css":"/* Aurora Admin LayoutSwitcher 样式 — 布局切换 */\n.aa-layoutsw { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); display: flex; gap: 12px; flex-wrap: wrap; }\n.aa-layoutsw-opt { border: 1px solid #E8ECF1; border-radius: 8px; padding: 8px; cursor: pointer; width: 128px; transition: border-color .15s, box-shadow .15s; }\n.aa-layoutsw-opt:hover { border-color: #B7C4FF; }\n.aa-layoutsw-opt.is-active { border-color: #2F54EB; box-shadow: 0 0 0 3px rgba(47,84,235,0.12); }\n.aa-layoutsw-mini { height: 66px; border-radius: 4px; overflow: hidden; display: flex; flex-direction: column; background: #fff; border: 1px solid #E8ECF1; }\n.aa-layoutsw-mini .s-top { height: 16px; background: #2F54EB; }\n.aa-layoutsw-mini .s-body { flex: 1; display: flex; }\n.aa-layoutsw-mini .s-side { width: 22px; background: #B7C4FF; }\n.aa-layoutsw-mini .s-main { flex: 1; background: #F5F7FA; }\n.aa-layoutsw-label { font-size: 13px; color: #262626; text-align: center; margin-top: 6px; }\n","jsx":"import React from 'react';\nimport './LayoutSwitcher.css';\n\nconst OPTIONS = [\n { value: 'side', label: '侧边布局' },\n { value: 'top', label: '顶部布局' },\n { value: 'mixed', label: '混合布局' }\n];\n\nexport default function LayoutSwitcher({ value = 'side', onChange }) {\n return (\n
\n {OPTIONS.map((o) => (\n onChange && onChange(o.value)}\n >\n
\n {o.value !== 'side' &&
}\n
\n {o.value !== 'top' &&
}\n
\n
\n
\n
{o.label}
\n
\n ))}\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"densityswitcher","name":"密度切换 DensitySwitcher","tier":"extension","confidence":"high","specBatch":7,"specFile":"组件7.txt","contractRef":"components/densityswitcher.json","category":"system","specLines":["- 宽松、默认、紧凑","- 影响表格、表单间距","- 局部可独立设置","- 保存偏好","- 适用于信息密度调整"],"specSummary":"- 宽松、默认、紧凑","contract":{"variants":["宽松","默认","紧凑","局部独立设置"],"dims":[{"name":"density","values":["loose","default","compact"]},{"name":"scope","values":["global","local"]}],"unknowns":["局部设置的作用域边界","三档对应的控件高度具体值"],"anatomy":{"options":"宽松、默认、紧凑","effect":"影响表格、表单间距","scope":"局部可独立设置","persist":"保存偏好"},"doNot":["各档位的具体像素值(见令牌定义)"],"usage":["宽松、默认、紧凑","影响表格、表单间距","局部可独立设置","保存偏好","适用于信息密度调整"],"structure":{"levels":"3 档","affects":"表格 / 表单间距","persist":"本地保存"}},"files":{"html":"../frameworks/DensitySwitcher.html","css":"../frameworks/DensitySwitcher.css","jsx":"../frameworks/DensitySwitcher.jsx","vue2":"../frameworks/DensitySwitcher.vue2.vue","vue3":"../frameworks/DensitySwitcher.vue3.vue"},"sizes":{"html":1716,"css":1094,"jsx":1051,"vue2":1099,"vue3":1100},"sources":{"html":"\n\n\n \n \n Aurora Admin · DensitySwitcher\n \n \n\n\n
\n

DensitySwitcher 密度切换

\n

宽松 / 默认 / 紧凑,影响表格、表单间距,可局部独立设置并保存。

\n\n
\n
\n \n \n \n
\n
\n
用户管理128 项
\n
角色管理12 项
\n
菜单管理36 项
\n
\n
\n
\n\n \n\n\n","css":"/* Aurora Admin DensitySwitcher 样式 — 信息密度切换 */\n.aa-densw { font-family: var(--font-family, -apple-system, \"PingFang SC\", \"Microsoft YaHei\", sans-serif); display: inline-flex; border: 1px solid #E8ECF1; border-radius: 6px; overflow: hidden; }\n.aa-densw-opt { padding: 8px 16px; font-size: 13px; cursor: pointer; color: #595959; background: #fff; border: none; transition: background .15s, color .15s; }\n.aa-densw-opt + .aa-densw-opt { border-left: 1px solid #E8ECF1; }\n.aa-densw-opt:hover { color: #2F54EB; }\n.aa-densw-opt.is-active { background: #2F54EB; color: #fff; }\n\n.aa-densw-demo { margin-top: 12px; border: 1px solid #E8ECF1; border-radius: 8px; overflow: hidden; }\n.aa-densw-demo.loose .aa-densw-row { padding: 14px 16px; }\n.aa-densw-demo.default .aa-densw-row { padding: 10px 16px; }\n.aa-densw-demo.compact .aa-densw-row { padding: 6px 16px; font-size: 13px; }\n.aa-densw-row { border-bottom: 1px solid #E8ECF1; color: #262626; display: flex; justify-content: space-between; }\n.aa-densw-row:last-child { border-bottom: none; }\n.aa-densw-row .muted { color: #6E6E6E; }\n","jsx":"import React from 'react';\nimport './DensitySwitcher.css';\n\nconst OPTIONS = [\n { value: 'loose', label: '宽松' },\n { value: 'default', label: '默认' },\n { value: 'compact', label: '紧凑' }\n];\nconst ROWS = [\n { name: '用户管理', count: '128 项' },\n { name: '角色管理', count: '12 项' },\n { name: '菜单管理', count: '36 项' }\n];\n\nexport default function DensitySwitcher({ value = 'default', onChange }) {\n return (\n
\n
\n {OPTIONS.map((o) => (\n onChange && onChange(o.value)}\n >\n {o.label}\n \n ))}\n
\n
\n {ROWS.map((r, i) => (\n
\n {r.name}\n {r.count}\n
\n ))}\n
\n
\n );\n}\n","vue2":"\n\n\n\n\n","vue3":"\n\n\n\n\n"}},{"slug":"employeecard","name":"员工卡片 EmployeeCard","tier":"extension","confidence":"high","specBatch":9,"specFile":"组件9.txt","contractRef":"components/employeecard.json","category":"display","specLines":["- 用于员工列表或组织架构展示","- 包含:头像、姓名、工号、部门、职位、状态(在职/离职)","- 卡片悬停显示快捷操作:发消息、查看详情、编辑","- 支持标签展示技能或角色","- 可配置显示联系方式(电话、邮箱)","- 卡片圆角 8px,阴影低,内边距 16px"],"specSummary":"- 用于员工列表或组织架构展示","contract":{"variants":["在职 · 基础信息","在职 · 显示电话","离职状态","悬停显示快捷操作"],"dims":[{"name":"status","values":["active","resigned"]},{"name":"contact","values":["hidden","phone","email"]}],"unknowns":["头像缺省占位样式","技能标签的配色集合"],"anatomy":{"avatar":"头像","identity":"姓名、工号、部门、职位、状态(在职/离职)","tags":"标签展示技能或角色","contact":"可配置显示联系方式(电话、邮箱)","hoverActions":"卡片悬停显示快捷操作:发消息、查看详情、编辑"},"doNot":["离职状态的视觉弱化程度"],"usage":["用于员工列表或组织架构展示","包含:头像、姓名、工号、部门、职位、状态(在职/离职)","卡片悬停显示快捷操作:发消息、查看详情、编辑","支持标签展示技能或角色","可配置显示联系方式(电话、邮箱)","卡片圆角 8px,阴影低,内边距 16px"],"structure":{"radius":"8px","shadow":"低","padding":"16px"}},"files":{"html":"../frameworks/employeecard.html","css":"../frameworks/EmployeeCard.css","jsx":"../frameworks/EmployeeCard.jsx","vue2":"../frameworks/EmployeeCard.vue2.vue","vue3":"../frameworks/EmployeeCard.vue3.vue"},"sizes":{"html":4523,"css":1864,"jsx":2307,"vue2":3568,"vue3":3632},"sources":{"html":"\n\n\n\n\nAurora Admin · EmployeeCard (H5)\n\n\n\n\n
\n

Aurora Admin · EmployeeCard 组件(纯 H5)

\n

对齐 EmployeeCard 规范:头像/姓名/工号/部门/职位/状态、悬停快捷操作、技能标签、可显联系方式、圆角8px/阴影低/内边距16px

\n\n
\n
\n
\n \n \n \n
\n
\n
张
\n
\n
张伟 在职
\n
工号:E1001
\n
\n
\n
部门:技术中心 · 前端组
职位:高级前端工程师
\n
ReactTypeScript架构
\n
\n\n
\n
\n \n \n \n
\n
\n
李
\n
\n
李娜 离职
\n
工号:E1024
\n
\n
\n
部门:产品中心 · 产品组
职位:产品经理
\n
B端数据
\n
电话:138-0000-0000
邮箱:lina@example.com
\n
\n
\n
\n\n\n","css":"/* Aurora Admin EmployeeCard 样式 — 对齐 组件9.txt EmployeeCard 规范 */\n.aa-emp-card { width: 280px; background: #fff; border-radius: 8px; box-shadow: 0 1px 4px rgba(0,0,0,0.06); padding: 16px; position: relative; box-sizing: border-box; }\n.aa-emp-top { display: flex; align-items: center; gap: 12px; }\n.aa-emp-avatar { width: 48px; height: 48px; border-radius: 50%; background: #F0F5FF; color: #2F54EB; display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 18px; flex-shrink: 0; overflow: hidden; }\n.aa-emp-avatar img { width: 100%; height: 100%; object-fit: cover; }\n.aa-emp-name { font-size: 16px; font-weight: 600; color: #1F2329; display: flex; align-items: center; gap: 8px; }\n.aa-emp-status { font-size: 12px; padding: 0 6px; border-radius: 2px; line-height: 18px; }\n.aa-emp-status.active { background: #F6FFED; color: #2E7D0A; }\n.aa-emp-status.left { background: #F5F5F5; color: #6E6E6E; }\n.aa-emp-id { font-size: 12px; color: #6E6E6E; margin-top: 2px; }\n.aa-emp-meta { margin-top: 12px; font-size: 13px; color: #595959; line-height: 22px; }\n.aa-emp-tags { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 6px; }\n.aa-emp-tag { background: #F0F5FF; color: #2F54EB; font-size: 12px; padding: 1px 8px; border-radius: 2px; }\n.aa-emp-contact { margin-top: 12px; font-size: 13px; color: #595959; line-height: 22px; border-top: 1px solid #F0F0F0; padding-top: 10px; }\n.aa-emp-actions {\n position: absolute; top: 12px; right: 12px; display: none; gap: 4px;\n background: #fff; border: 1px solid #E8ECF1; border-radius: 4px; padding: 2px;\n}\n.aa-emp-card:hover .aa-emp-actions { display: flex; }\n.aa-emp-actions button { border: none; background: none; cursor: pointer; color: #6E6E6E; font-size: 12px; padding: 2px 6px; border-radius: 3px; }\n.aa-emp-actions button:hover { color: #2F54EB; background: #F0F5FF; }\n","jsx":"import React from 'react';\nimport './EmployeeCard.css';\n\n/**\n * Aurora Admin EmployeeCard(React)\n * 对齐 组件9.txt EmployeeCard 规范:头像/姓名/工号/部门/职位/状态、悬停快捷操作、标签、可显联系方式、圆角8px/阴影低/内边距16px\n * employee: { name, empId, dept, position, status:'active'|'left', avatar?, tags?, phone?, email? }\n */\nexport default function EmployeeCard({ employee, showContact = false, onMessage, onView, onEdit }) {\n const statusText = employee.status === 'left' ? '离职' : '在职';\n return (\n
\n
\n \n \n \n
\n\n
\n
\n {employee.avatar\n ? {employee.name}\n : {(employee.name || '?').charAt(0)}}\n
\n
\n
\n {employee.name}\n {statusText}\n
\n
工号:{employee.empId}
\n
\n
\n\n
\n 部门:{employee.dept}
职位:{employee.position}\n
\n\n {employee.tags && employee.tags.length > 0 && (\n
\n {employee.tags.map((t) => {t})}\n
\n )}\n\n {showContact && (employee.phone || employee.email) && (\n
\n {employee.phone &&
电话:{employee.phone}
}\n {employee.email &&
邮箱:{employee.email}
}\n
\n )}\n
\n );\n}\n\n/*\n用法示例:\nimport EmployeeCard from './EmployeeCard';\nconst emp = { name: '张伟', empId: 'E1001', dept: '技术中心 · 前端组', position: '高级前端工程师', status: 'active', tags: ['React','TS'] };\n console.log(e)} />\n*/\n","vue2":"\n\n\n\n\n\n","vue3":"\n\n\n\n\n\n"}}]};