/* Aurora Admin 组件库文档 — 交互逻辑(无依赖 vanilla JS) 布局与交互对齐 Element 文档:首页落地页 / 快速开始 / 分类侧栏 / 多演示块样式(居中箭头展开)/ 右侧锚点目录 / Ctrl+K 搜索 */ (function () { 'use strict'; var LIB_ROOT = '../.design_library/aurora-admin/'; var SPEC_ROOT = '../'; var TAB_ORDER = ['html', 'css', 'jsx', 'vue2', 'vue3']; var TAB_LABELS = { html: 'H5 HTML', css: 'CSS', jsx: 'React JSX', vue2: 'Vue 2', vue3: 'Vue 3' }; var BATCH_LABELS = { 1: '核心规范', 2: '第二批', 3: '第三批', 4: '第四批', 5: '第五批', 6: '第六批', 7: '第七批', 8: '第八批', 9: '第九批', 10: '第十批' }; var CAT_LABELS = { general: '通用', navigation: '导航', input: '数据录入', display: '数据展示', feedback: '反馈', system: '工具与系统' }; var CAT_EN = { general: 'General', navigation: 'Navigation', input: 'Data Entry', display: 'Data Display', feedback: 'Feedback', system: 'System' }; var CAT_ORDER = ['general', 'navigation', 'input', 'display', 'feedback', 'system']; var KIND_LABELS = { html: 'H5 静态页', css: '样式表', jsx: 'React 组件', vue2: 'Vue 2 组件', vue3: 'Vue 3 组件' }; /* 框架感知:每种技术栈需要的文件形态 */ var FRAMEWORKS = { h5: 'H5', react: 'React', vue2: 'Vue 2', vue3: 'Vue 3' }; var FW_FILES = { h5: ['html', 'css'], react: ['jsx', 'css'], vue2: ['vue2'], vue3: ['vue3'] }; var data = window.AA_DATA; var $ = function (id) { return document.getElementById(id); }; function fatal(msg) { var el = $('fatal'); el.textContent = msg; el.classList.remove('hidden'); } if (!data || !data.components || !data.components.length) { fatal('数据加载失败:site/data.js 缺失或为空。请先在仓库根目录运行 build-site.ps1 重新生成。'); return; } var comps = data.components; var bySlugMap = {}; comps.forEach(function (c) { bySlugMap[c.slug] = c; }); var state = { route: { page: 'home', slug: null }, query: '', ovCat: '', ovQ: '', framework: 'h5', searchCat: '' }; var thumbObserver = null; function loadFramework() { try { var saved = localStorage.getItem('aa-framework'); if (saved && FRAMEWORKS[saved]) return saved; } catch (e) { /* ignore */ } return 'h5'; } /* ---------- 工具 ---------- */ function el(tag, cls, html) { var e = document.createElement(tag); if (cls) e.className = cls; if (html != null) e.innerHTML = html; return e; } function esc(s) { return String(s == null ? '' : s) .replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } function splitName(name) { var m = name.match(/^(.+?)\s+([A-Za-z0-9].*)$/); return m ? { zh: m[1], en: m[2] } : { zh: name, en: '' }; } function batchLabel(b) { return BATCH_LABELS[b] || ('第' + b + '批'); } function catLabel(key) { return CAT_LABELS[key] || key || '未分类'; } function catEn(key) { return CAT_EN[key] || ''; } function fmtSize(bytes) { if (bytes == null) return '—'; if (bytes < 1024) return bytes + ' B'; return (bytes / 1024).toFixed(1) + ' KB'; } function specLink(file) { return SPEC_ROOT + file; } function cleanSummary(s) { return (s || '').replace(/^[-•·]\s*/, '').trim(); } /* ---------- 路由 ---------- */ function parseHash() { var h = location.hash.replace(/^#/, ''); if (!h || h === '/') return { page: 'home', slug: null }; if (h === '/overview') return { page: 'overview', slug: null }; if (h === '/guide') return { page: 'guide', slug: null }; if (h === '/design') return { page: 'design', slug: null }; if (h === '/agents') return { page: 'agents', slug: null }; if (h === '/changelog') return { page: 'changelog', slug: null }; var m = h.match(/^\/component\/([a-z0-9-]+)/i); if (m && bySlugMap[m[1]]) return { page: 'component', slug: m[1] }; return { page: 'home', slug: null }; } function navigate(opts) { var fwOnly = opts && opts.fwOnly; state.route = parseHash(); closeSearch(); renderSidebar(); renderTopnav(); renderToc([]); var content = $('content'); if (state.route.page === 'home') renderHome(content); else if (state.route.page === 'overview') renderOverview(content); else if (state.route.page === 'guide') renderGuide(content); else if (state.route.page === 'design') renderDesign(content); else if (state.route.page === 'agents') renderAgents(content); else if (state.route.page === 'changelog') renderChangelog(content); else renderComponent(content, state.route.slug); /* 仅切换框架时不滚到顶部,避免用户感觉"页面没切换" */ if (!fwOnly) window.scrollTo(0, 0); if (window.aaLogger) { window.aaLogger.render(state.route.page || 'unknown', { slug: state.route.slug || null, framework: state.framework }); } } function renderTopnav() { var r = state.route.page; document.querySelectorAll('.topnav a').forEach(function (a) { var nav = a.getAttribute('data-nav'); var active = (nav === r) || (nav === 'overview' && r === 'component'); a.classList.toggle('active', active); }); } /* ---------- 侧边栏 ---------- */ function renderSidebar() { var sb = $('sidebar'); sb.innerHTML = ''; var r = state.route.page; var links = [ { label: '组件总览', hash: '#/overview', active: r === 'overview' || r === 'component' }, { label: '快速开始', hash: '#/guide', active: r === 'guide' }, { label: '设计规范', hash: '#/design', active: r === 'design' } ]; links.forEach(function (L) { var a = el('a', 'side-link' + (L.active ? ' active' : ''), esc(L.label)); a.href = L.hash; sb.appendChild(a); }); CAT_ORDER.forEach(function (cat) { var items = comps.filter(function (c) { return c.category === cat; }); if (!items.length) return; var g = el('div', 'side-group'); g.innerHTML = '' + items.length + ''; g.querySelector('.cat-zh').textContent = catLabel(cat); g.querySelector('.cat-en').textContent = catEn(cat); sb.appendChild(g); items.forEach(function (c) { var n = splitName(c.name); var b = el('button', 'side-item'); b.type = 'button'; b.setAttribute('data-slug', c.slug); b.innerHTML = ''; b.querySelector('.zh').textContent = n.zh; b.querySelector('.en').textContent = n.en; if (r === 'component' && state.route.slug === c.slug) b.classList.add('active'); b.addEventListener('click', function () { location.hash = '#/component/' + c.slug; }); sb.appendChild(b); }); }); } /* ---------- 右侧目录 ---------- */ function renderToc(items) { var toc = $('toc'); toc.innerHTML = ''; if (!items.length) { toc.style.display = 'none'; return; } toc.style.display = ''; toc.appendChild(el('div', 'toc-title', '本页目录')); items.forEach(function (it) { var a = el('a', null, esc(it.label)); a.setAttribute('data-anchor', it.id); a.addEventListener('click', function (e) { e.preventDefault(); var target = document.getElementById(it.id); if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' }); }); toc.appendChild(a); }); setupScrollSpy(items); } var spyHandler = null; function setupScrollSpy(items) { if (spyHandler) window.removeEventListener('scroll', spyHandler); spyHandler = function () { var pos = window.scrollY + 120; var current = items[0] && items[0].id; items.forEach(function (it) { var node = document.getElementById(it.id); if (node && node.offsetTop <= pos) current = it.id; }); document.querySelectorAll('.toc a').forEach(function (a) { a.classList.toggle('active', a.getAttribute('data-anchor') === current); }); }; window.addEventListener('scroll', spyHandler, { passive: true }); spyHandler(); } /* ---------- 首页(落地页) ---------- */ function renderHome(content) { content.innerHTML = ''; var inner = el('div', 'page-inner home'); /* Hero stats 第三项加入"测试" */ var hero = el('div', 'hero'); hero.innerHTML = '
AURORA ADMIN DESIGN SYSTEM
' + '

Aurora Admin 组件库

' + '

理性、高效、克制的 B 端中后台设计系统——信息密度适中,层级清晰,交互反馈即时。

' + '
' + ' 浏览组件 →' + ' 场景示范' + ' 快速开始' + ' 测试总览' + ' 设计规范' + '
' + '
' + '
' + data.meta.total + '组件
' + '
' + CAT_ORDER.length + '大类
' + '
' + data.tokens.length + '设计令牌
' + '
5端实现
' + '
10份规范
' + '
79测试页
' + '
'; inner.appendChild(hero); /* 测试通过率:来自 tests/report.json(headless 回归产出,缺失时静默不显示) */ fetch('../tests/report.json', { cache: 'no-cache' }).then(function (r) { return r.ok ? r.json() : null; }).then(function (rep) { var node = document.getElementById('stat-passrate'); if (rep && rep.passRate != null && node) { node.textContent = '通过率 ' + rep.passRate + '%'; node.title = rep.generated ? 'headless 回归 ' + rep.generated + ':' + rep.assertions.pass + '/' + rep.assertions.total + ' 断言通过' : ''; } }).catch(function () { /* 报告不存在时不显示 */ }); /* 设计原则四卡 */ inner.appendChild(el('h2', 'home-h2', '设计原则')); var feats = [ ['一致优先', '同类组件使用相同的尺寸、颜色与间距,跨页面保持一致体验。', 'M4 12l5 5L20 7'], ['清晰高效', '信息层级明确,操作路径最短,避免过度装饰。', 'M13 2L3 14h9l-1 8 10-12h-9l1-8z'], ['反馈及时', '所有操作都有加载、成功、失败三类状态反馈。', 'M22 11.08V12a10 10 0 1 1-5.93-9.14'], ['无障碍', '满足键盘操作与焦点管理,正文对比度 ≥ 4.5:1。', 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z'] ]; var fgrid = el('div', 'feat-grid'); feats.forEach(function (f) { var card = el('div', 'feat-card'); card.innerHTML = '
' + '

'; card.querySelector('h3').textContent = f[0]; card.querySelector('p').textContent = f[1]; fgrid.appendChild(card); }); inner.appendChild(fgrid); /* 核心组件快捷入口 */ inner.appendChild(el('h2', 'home-h2', '核心组件')); var core = el('div', 'core-grid'); comps.filter(function (c) { return c.contract; }).forEach(function (c) { var n = splitName(c.name); var a = el('a', 'core-card'); a.href = '#/component/' + c.slug; a.innerHTML = '
'; a.querySelector('.core-zh').textContent = n.zh; a.querySelector('.core-en').textContent = n.en; core.appendChild(a); }); inner.appendChild(core); /* 批次入口 */ inner.appendChild(el('h2', 'home-h2', '规格批次')); var batchWrap = el('div', 'batch-wrap'); [1, 2, 4, 5, 6, 7, 9].forEach(function (b) { var items = comps.filter(function (c) { return c.specBatch === b; }); if (!items.length) return; var a = el('a', 'batch-chip'); a.href = specLink('组件' + b + '.txt'); a.target = '_blank'; a.textContent = batchLabel(b) + ' · ' + items.length + ' 组件'; batchWrap.appendChild(a); }); inner.appendChild(batchWrap); content.appendChild(inner); } /* ---------- 快速开始 ---------- */ function renderGuide(content) { content.innerHTML = ''; var inner = el('div', 'page-inner'); inner.appendChild(el('h1', 'page-title', '快速开始')); inner.appendChild(el('p', 'page-desc', '了解如何在本项目中使用 Aurora Admin 组件:引入设计令牌、按端取用组件源码、以及目录与命名约定。')); var tocItems = []; /* 1. 引入设计令牌 */ var s1 = el('div', 'sec'); s1.id = 'install'; s1.appendChild(el('h2', null, '引入设计令牌')); s1.appendChild(el('p', 'guide-p', '所有组件都基于同一份 CSS 变量令牌。在使用任何组件前,先在页面中引入令牌文件:')); var code1 = el('pre', 'code-inline'); code1.appendChild(el('code', null, esc(''))); s1.appendChild(code1); s1.appendChild(el('p', 'guide-p', '如需结构化的令牌数据(供工具消费),可读取同目录的 css.json;聚合组件样式见 components.css。')); inner.appendChild(s1); tocItems.push({ id: 'install', label: '引入设计令牌' }); /* 2. 按端使用组件 */ var s2 = el('div', 'sec'); s2.id = 'usage'; s2.appendChild(el('h2', null, '按端使用组件')); s2.appendChild(el('p', 'guide-p', '每个组件在 frameworks/ 下有五种形态(H5 / CSS / React / Vue 2 / Vue 3),按你的技术栈取用:')); var tbl = el('table', 'res-table'); var thead = el('thead'); var trh = el('tr'); ['端', '文件', '使用方式'].forEach(function (t) { trh.appendChild(el('th', null, t)); }); thead.appendChild(trh); tbl.appendChild(thead); var tbody = el('tbody'); [ ['H5 静态页', 'Button.html', '直接在浏览器打开即为完整演示页;将结构与内联样式拷贝进你的页面'], ['React', 'Button.jsx + Button.css', '将 JSX 与 CSS 拷入工程,按需改造数据绑定'], ['Vue 2', 'Button.vue2.vue', 'Options API 单文件组件,直接注册使用'], ['Vue 3', 'Button.vue3.vue', '