Files
aurora-admin/frameworks/tabs.html
T
aurora-admin 382ca79b66 v1.4.0: 组件层全面令牌化(设计系统根基修复)
问题
- 79 个组件的 844 个颜色属性里,只有 2 个(0.2%)引用令牌,
  788 个(93.4%)硬编码 hex
- 后果:改令牌组件不跟着变、主题定制器对组件无效、For Agents 页写的
  「所有色值必须取自 au-* 变量」形同虚设
- 这是本项目第五次「声称≠实现」,且动摇了设计系统的根本

两层令牌化(此前只改了从未被加载的文件,走了弯路)
- 组件样式表 frameworks/*.css:915 处 / 78 文件
- 演示页内嵌 <style>(实际生效的样式层):287 处 / 51 文件
- 内嵌样式层是决定性的:演示页只 link colors_and_type.css + 自己的 CSS,
  而多数页面把关键样式写在 <style> 块里 —— 只改 CSS 文件不生效

安全策略
- 只替换颜色属性值位置的 hex(color/background*/border*/outline*/fill/stroke)
- 跳过 rgba()/渐变/url() 复合值;不动 SVG 属性与 JS 数据(色板数组)
- 歧义色值按属性语义消解(#FFFFFF 作 color→text-inverse,作 background→card-bg)
- 幂等,可重复运行

补齐令牌语义(71 → 74)
- --au-color-text-tertiary #595959(规范未定义、实现补齐,7.00:1)
- --au-color-text-disabled #BFBFBF(规范定义于组件1/2.txt;WCAG 1.4.3 豁免)
- --au-color-border-strong #F0F0F0(规范未定义、实现补齐)
- 同步 legacy 别名;修复 colors_and_type.css 一处重复注释起始行

修复
- Countdown 首帧空白:setInterval(render, 1000) 首次执行要等 1 秒,
  期间容器为空。改为定义函数后立即执行一次再挂 interval

验证(本轮核心验收)
- 逐组件验证「改令牌 → 元素是否跟着变」:79/79 全部响应
- 过程中修正两处测量方法缺陷(初测 40/79 是误报):
  ① 选择器 .demo * 在无 .demo 容器的页面抓不到元素
  ② 探针令牌选错 —— Breadcrumb 用 text-tertiary 而只改了 text-body
- CSS 结构完整:158 个文件括号平衡、var(--*) 引用全部有定义
- 回归保持 100%:961 断言 / 0 失败 / 79 页全通过,三次连跑一致
2026-09-11 17:29:30 +08:00

128 lines
5.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aurora Admin · Tabs (H5)</title>
<link rel="stylesheet" href="../.design_library/aurora-admin/colors_and_type.css">
<link rel="stylesheet" href="./Tabs.css">
<style>
.demo { padding: 24px; background: var(--au-color-page-bg); font-family: var(--font-family); }
h1 { font-size: 20px; margin: 0 0 4px; color: var(--au-color-text-title); }
.sub { color: var(--au-color-text-secondary); margin: 0 0 24px; font-size: 12px; }
.demo-block { background: var(--au-color-card-bg); border-radius: 8px; padding: 16px; margin-bottom: 24px; }
.demo-block > .hint { color: var(--au-color-text-secondary); font-size: 12px; margin: 0 0 12px; }
</style>
</head>
<body>
<div class="demo">
<h1>Aurora Admin · Tabs 组件(纯 H5)</h1>
<p class="sub">对齐 PageTabs 规范:线型 / 卡片型 / 胶囊型,支持禁用、关闭、新增</p>
<!-- 线型 -->
<div class="demo-block">
<p class="hint">线型(默认)</p>
<div class="aa-tabs type-line" data-tabs id="tabs-line">
<div class="aa-tabs-nav" role="tablist">
<div class="aa-tabs-tab is-active" role="tab" tabindex="0" data-key="basic">基本信息</div>
<div class="aa-tabs-tab" role="tab" tabindex="0" data-key="safe">安全设置</div>
<div class="aa-tabs-tab" role="tab" tabindex="0" data-key="notify">通知</div>
<div class="aa-tabs-tab is-disabled" role="tab" tabindex="0" data-key="auth">授权</div>
</div>
<div class="aa-tabs-content" data-panels>
<div data-panel="basic">这里是「基本信息」面板内容。</div>
<div data-panel="safe" hidden>这里是「安全设置」面板内容。</div>
<div data-panel="notify" hidden>这里是「通知」面板内容。</div>
<div data-panel="auth" hidden>这里是「授权」面板内容。</div>
</div>
</div>
</div>
<!-- 卡片型(可关闭 + 新增) -->
<div class="demo-block">
<p class="hint">卡片型(可关闭 / 新增)</p>
<div class="aa-tabs type-card" data-tabs id="tabs-card">
<div class="aa-tabs-nav" role="tablist">
<div class="aa-tabs-tab is-active" role="tab" tabindex="0" data-key="t1">订单 <span class="aa-tabs-close" data-close>×</span></div>
<div class="aa-tabs-tab" role="tab" tabindex="0" data-key="t2">退款 <span class="aa-tabs-close" data-close>×</span></div>
<div class="aa-tabs-add" role="button" tabindex="0" aria-label="新增标签页" data-add>+</div>
</div>
<div class="aa-tabs-content" data-panels>
<div data-panel="t1">订单标签页内容。</div>
<div data-panel="t2" hidden>退款标签页内容。</div>
</div>
</div>
</div>
<!-- 胶囊型 -->
<div class="demo-block">
<p class="hint">胶囊型</p>
<div class="aa-tabs type-pill" data-tabs id="tabs-pill">
<div class="aa-tabs-nav" role="tablist">
<div class="aa-tabs-tab is-active" role="tab" tabindex="0" data-key="all">全部</div>
<div class="aa-tabs-tab" role="tab" tabindex="0" data-key="todo">待办</div>
<div class="aa-tabs-tab" role="tab" tabindex="0" data-key="done">已完成</div>
</div>
<div class="aa-tabs-content" data-panels>
<div data-panel="all">全部任务。</div>
<div data-panel="todo" hidden>待办任务。</div>
<div data-panel="done" hidden>已完成任务。</div>
</div>
</div>
</div>
</div>
<script>
function initTabs(root) {
const nav = root.querySelector('.aa-tabs-nav');
const panels = root.querySelector('[data-panels]');
function activate(key) {
root.querySelectorAll('.aa-tabs-tab').forEach(t => {
const on = t.dataset.key === key;
t.classList.toggle('is-active', on);
});
if (panels) panels.querySelectorAll('[data-panel]').forEach(p => {
p.hidden = p.dataset.panel !== key;
});
}
nav.addEventListener('click', (e) => {
const closeBtn = e.target.closest('[data-close]');
const addBtn = e.target.closest('[data-add]');
const tab = e.target.closest('.aa-tabs-tab');
if (closeBtn && tab) {
e.stopPropagation();
const removedKey = tab.dataset.key;
tab.remove();
const first = root.querySelector('.aa-tabs-tab');
if (first) activate(first.dataset.key);
return;
}
if (addBtn) {
const n = root.querySelectorAll('.aa-tabs-tab').length + 1;
const key = 'new' + n;
const el = document.createElement('div');
el.className = 'aa-tabs-tab';
el.dataset.key = key;
el.innerHTML = '新标签 <span class="aa-tabs-close" data-close>×</span>';
addBtn.before(el);
const p = document.createElement('div');
p.dataset.panel = key;
p.textContent = '新标签内容 #' + n;
panels.appendChild(p);
activate(key);
return;
}
if (tab && !tab.classList.contains('is-disabled')) {
activate(tab.dataset.key);
}
});
}
document.querySelectorAll('[data-tabs]').forEach(initTabs);
</script>
</body>
</html>