feat(S2-P9+S4-P12): FAQ页240条聚合+发布流程release.mjs
P9: #/faq 路由+renderFaq(details批量懒加载每批10个)+顶栏/侧边栏双入口
+guide-h3样式+9条i18n英文;浏览器实测240条/搜索宽度11条/0 JS错误。
规划偏差:契约已在P4-D移出data.js,FAQ走details懒加载。
P12: tools/release.mjs(检查+bump双模式,只做本地准备不自动push/tag)
+CONTRIBUTING发布5步+版本号规则;检查模式实测版本一致通过。
回归零影响:report.json 79/79、data.json未动。
This commit is contained in:
+120
-1
@@ -198,6 +198,7 @@
|
||||
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 === '/faq') return { page: 'faq', 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] };
|
||||
@@ -217,6 +218,7 @@
|
||||
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 === 'faq') renderFaq(content);
|
||||
else if (state.route.page === 'changelog') renderChangelog(content);
|
||||
else renderComponent(content, state.route.slug);
|
||||
/* 仅切换框架时不滚到顶部,避免用户感觉"页面没切换" */
|
||||
@@ -245,7 +247,8 @@
|
||||
var links = [
|
||||
{ label: T('组件总览'), hash: '#/overview', active: r === 'overview' || r === 'component' },
|
||||
{ label: T('快速开始'), hash: '#/guide', active: r === 'guide' },
|
||||
{ label: T('设计规范'), hash: '#/design', active: r === 'design' }
|
||||
{ label: T('设计规范'), hash: '#/design', active: r === 'design' },
|
||||
{ label: T('常见问题'), hash: '#/faq', active: r === 'faq' }
|
||||
];
|
||||
links.forEach(function (L) {
|
||||
var a = el('a', 'side-link' + (L.active ? ' active' : ''), esc(L.label));
|
||||
@@ -584,6 +587,122 @@
|
||||
renderToc(tocItems);
|
||||
}
|
||||
|
||||
/* ---------- 常见问题(S2-P9:契约 unknowns + doNotInvent 聚合) ---------- */
|
||||
function renderFaq(content) {
|
||||
content.innerHTML = '';
|
||||
var inner = el('div', 'page-inner');
|
||||
inner.appendChild(el('h1', 'page-title', T('常见问题')));
|
||||
inner.appendChild(el('p', 'page-desc', T('自动聚合 79 个组件契约里的“规范未明示”(unknowns)与“不要自行发明”(doNotInvent),共 240 条。支持按组件浏览与关键词过滤。')));
|
||||
|
||||
var tocItems = [];
|
||||
|
||||
/* 搜索框(纯前端过滤,零依赖) */
|
||||
var s0 = el('div', 'sec'); s0.id = 'faq-search';
|
||||
var input = el('input', 'search-input');
|
||||
input.placeholder = T('搜索问题,如:宽度、省略、对齐…');
|
||||
input.setAttribute('aria-label', T('搜索常见问题'));
|
||||
s0.appendChild(input);
|
||||
var stat = el('p', 'guide-p');
|
||||
s0.appendChild(stat);
|
||||
inner.appendChild(s0);
|
||||
|
||||
/* 按组件分组(P4 阶段D:contract 已移出 data.js,details/*.json 懒加载批量取) */
|
||||
var groups = [];
|
||||
var listWrap = el('div', 'faq-list');
|
||||
inner.appendChild(listWrap);
|
||||
|
||||
function buildGroup(c, contract) {
|
||||
var un = (contract && contract.unknowns) || [];
|
||||
var dn = (contract && (contract.doNot || contract.doNotInvent)) || [];
|
||||
if (!un.length && !dn.length) return null;
|
||||
var box = el('div', 'sec');
|
||||
box.id = 'faq-' + c.slug;
|
||||
box.setAttribute('data-slug', c.slug);
|
||||
var nm = splitName(c.name || c.slug);
|
||||
box.appendChild(el('h2', null, esc(isEn() && nm.en ? nm.en : nm.zh) + ' · ' + esc(c.slug)));
|
||||
if (un.length) {
|
||||
box.appendChild(el('h3', 'guide-h3', T('规范未明示') + '(' + un.length + ')'));
|
||||
var ul1 = el('ul', 'guide-ul');
|
||||
un.forEach(function (q) {
|
||||
var li = el('li', null, esc(q));
|
||||
li.setAttribute('data-q', q);
|
||||
ul1.appendChild(li);
|
||||
});
|
||||
box.appendChild(ul1);
|
||||
}
|
||||
if (dn.length) {
|
||||
box.appendChild(el('h3', 'guide-h3', T('不要自行发明') + '(' + dn.length + ')'));
|
||||
var ul2 = el('ul', 'guide-ul');
|
||||
dn.forEach(function (q) {
|
||||
var li = el('li', null, esc(q));
|
||||
li.setAttribute('data-q', q);
|
||||
ul2.appendChild(li);
|
||||
});
|
||||
box.appendChild(ul2);
|
||||
}
|
||||
inner.appendChild(box);
|
||||
listWrap.appendChild(box);
|
||||
groups.push({ id: 'faq-' + c.slug, label: c.slug, box: box });
|
||||
return box;
|
||||
}
|
||||
|
||||
function applyFilter() {
|
||||
var q = (input.value || '').trim().toLowerCase();
|
||||
var shownGroups = 0, shownItems = 0, totalItems = 0;
|
||||
groups.forEach(function (g) {
|
||||
var items = g.box.querySelectorAll('li[data-q]');
|
||||
var vis = 0;
|
||||
items.forEach(function (li) {
|
||||
totalItems++;
|
||||
var hit = !q || (li.getAttribute('data-q') || '').toLowerCase().indexOf(q) > -1;
|
||||
li.style.display = hit ? '' : 'none';
|
||||
if (hit) vis++;
|
||||
});
|
||||
g.box.style.display = vis ? '' : 'none';
|
||||
if (vis) { shownGroups++; shownItems += vis; }
|
||||
});
|
||||
stat.textContent = loaded < comps.length
|
||||
? T('加载中…') + ' ' + loaded + ' / ' + comps.length
|
||||
: (q
|
||||
? T('命中') + ' ' + shownItems + ' / ' + totalItems + T(' 条,') + shownGroups + ' / ' + groups.length + T(' 个组件')
|
||||
: T('共') + ' ' + totalItems + ' ' + T('条,') + groups.length + ' ' + T('个组件有条目'));
|
||||
}
|
||||
input.addEventListener('input', applyFilter);
|
||||
|
||||
/* 批量懒加载 details(每批 10 个,避免 79 并发打爆 dev-server) */
|
||||
var loaded = 0;
|
||||
stat.textContent = T('加载中…') + ' 0 / ' + comps.length;
|
||||
content.appendChild(inner);
|
||||
renderToc([]);
|
||||
|
||||
function loadOne(c) {
|
||||
var got = getDetail(c);
|
||||
if (got) { // data.js 内嵌旧数据或缓存命中:同步建组
|
||||
buildGroup(c, got.contract);
|
||||
loaded++;
|
||||
if (loaded === comps.length) { applyFilter(); renderToc(tocItems.map(function (t) { return { id: t.id, label: t.label }; })); }
|
||||
return Promise.resolve();
|
||||
}
|
||||
return fetch('details/' + c.slug + '.json', { cache: 'force-cache' })
|
||||
.then(function (r) { return r.ok ? r.json() : { contract: null }; })
|
||||
.catch(function () { return { contract: null }; })
|
||||
.then(function (d) {
|
||||
detailCache[c.slug] = d;
|
||||
buildGroup(c, d.contract);
|
||||
loaded++;
|
||||
stat.textContent = T('加载中…') + ' ' + loaded + ' / ' + comps.length;
|
||||
if (loaded === comps.length) { applyFilter(); renderToc(tocItems.map(function (t) { return { id: t.id, label: t.label }; })); }
|
||||
});
|
||||
}
|
||||
(function loadBatch(i) {
|
||||
if (!content.isConnected) return;
|
||||
var batch = comps.slice(i, i + 10);
|
||||
Promise.all(batch.map(loadOne)).then(function () {
|
||||
if (i + 10 < comps.length) loadBatch(i + 10);
|
||||
});
|
||||
})(0);
|
||||
}
|
||||
|
||||
/* ---------- 更新日志 ---------- */
|
||||
function renderChangelog(content) {
|
||||
content.innerHTML = '';
|
||||
|
||||
@@ -199,6 +199,22 @@
|
||||
'No structured changelog data yet — run build-site.ps1 to regenerate; the build parses CHANGELOG.md into data.js.',
|
||||
'暂无数据': 'No data yet',
|
||||
|
||||
/* ---------- 常见问题(S2-P9) ---------- */
|
||||
'常见问题': 'FAQ',
|
||||
'自动聚合 79 个组件契约里的“规范未明示”(unknowns)与“不要自行发明”(doNotInvent),共 240 条。支持按组件浏览与关键词过滤。':
|
||||
'Auto-aggregated from 79 component contracts: “unspecified by spec” (unknowns) and “do not invent” (doNotInvent), 240 entries. Browse by component or filter by keyword.',
|
||||
'搜索问题,如:宽度、省略、对齐…': 'Search, e.g. width, ellipsis, alignment…',
|
||||
'搜索常见问题': 'Search FAQ',
|
||||
'规范未明示': 'Unspecified by spec',
|
||||
'不要自行发明': 'Do not invent',
|
||||
'命中': 'Matched',
|
||||
'条,': ' entries, ',
|
||||
'个组件': ' components',
|
||||
'共': 'Total ',
|
||||
'条,': ' entries, ',
|
||||
'个组件有条目': ' components have entries',
|
||||
'加载中…': 'Loading…',
|
||||
|
||||
/* ---------- 组件详情页 ---------- */
|
||||
'何时使用': 'When to Use',
|
||||
'本组件暂无独立规格说明,参见 ': 'No standalone spec for this component; see ',
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<a href="#/overview" data-nav="overview">组件</a>
|
||||
<a href="#/guide" data-nav="guide">快速开始</a>
|
||||
<a href="#/design" data-nav="design">设计规范</a>
|
||||
<a href="#/faq" data-nav="faq">常见问题</a>
|
||||
<a href="#/changelog" data-nav="changelog">更新日志</a>
|
||||
<a href="#/agents" data-nav="agents">AI 消费</a>
|
||||
</nav>
|
||||
|
||||
@@ -860,6 +860,8 @@ a.chip:hover { color: var(--au-color-brand); }
|
||||
}
|
||||
.guide-ul { margin: 0; padding-left: 18px; }
|
||||
.guide-ul li { margin: 6px 0; }
|
||||
/* S2-P9 FAQ 页:分组小标题(复用 contract-block h3 的视觉层级) */
|
||||
.guide-h3 { margin: 12px 0 4px; font-size: 14px; font-weight: 600; color: var(--au-color-text-title); }
|
||||
|
||||
/* ---- 侧栏类目英文副标 ---- */
|
||||
.side-group .cat-zh { font-style: normal; }
|
||||
|
||||
Reference in New Issue
Block a user