You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

97 lines
4.2 KiB

1 week ago
// 票 01:生成 pages-index.md(sitemap 树 + 每页文本量 + 重点标注)
import fs from 'node:fs';
import path from 'node:path';
const ROOT = 'e:/code/crm-backend-matt';
const IN_JSON = `${ROOT}/.scratch/lanhu-latest/axure-v29.json`;
const OUT_DIR = `${ROOT}/.scratch/opportunity-bugfix/lanhu-pages`;
const OUT = `${ROOT}/.scratch/lanhu-latest/pages-index.md`;
const data = JSON.parse(fs.readFileSync(IN_JSON, 'utf8'));
// sitemap walk,保持树形结构输出
const lines = [];
const stats = { total: 0, withFile: 0, noText: 0 };
const focus = { a1x: [], a3: [], a4: [], notes: [] };
function mdOf(pageName) {
// 与 fetch-all-pages.mjs 相同的 safeName 规则
return pageName.replace(/[\\/:*?"<>|\s]+/g, '_') + '.md';
}
const walk = (nodes, depth, folder) => {
for (const n of nodes || []) {
const hasKids = Array.isArray(n.children) && n.children.length > 0;
// 带 url 的 Folder:自身也算页面,再递归 children(Axure sitemap 常见形态)
if (n.url) {
stats.total++;
const file = path.join(OUT_DIR, mdOf(n.pageName));
let texts = -1, exists = fs.existsSync(file) && fs.statSync(file).size > 100;
if (exists) {
stats.withFile++;
const head = fs.readFileSync(file, 'utf8').split('\n').slice(0, 6).join('\n');
const m = head.match(/有文本: (\d+)/);
texts = m ? +m[1] : -1;
if (texts === 0) stats.noText++;
}
const mark = !exists ? ' ⚠缺失' : texts === 0 ? ' (空页)' : '';
const isNote = exists && texts >= 0 && (() => {
const c = fs.readFileSync(file, 'utf8');
return c.includes('开发说明') || c.includes('变更记录');
})();
lines.push(`${' '.repeat(depth)}- ${n.pageName}${mark}${isNote ? ' 〔含批注〕' : ''} ${texts >= 0 ? `(${texts})` : ''}`);
// 重点标注
const url = n.url;
if (/^a1x/i.test(n.pageName) || url.toLowerCase().startsWith('a1x')) focus.a1x.push(n.pageName);
if (/^a3/i.test(n.pageName) || url.toLowerCase().startsWith('a3')) focus.a3.push(n.pageName);
if (/^a4/i.test(n.pageName) || url.toLowerCase().startsWith('a4')) focus.a4.push(n.pageName);
if (/暂缓|关闭/.test(n.pageName)) focus.notes.push(n.pageName);
}
if (hasKids) {
if (!n.url) lines.push(`${' '.repeat(depth)}- **${n.pageName}/**`);
walk(n.children, n.url ? depth + 1 : depth, [...folder, n.pageName]);
}
}
};
walk(data.sitemap.rootNodes, 0, []);
// sitemap 没覆盖的 pages key(兜底页)
const inTree = new Set();
const collect = (nodes) => (nodes || []).forEach(n => {
if (n.url) inTree.add(n.url);
if (n.children) collect(n.children);
});
collect(data.sitemap.rootNodes);
const orphan = Object.keys(data.pages).filter(u => !inTree.has(u));
const out = [
'# v29 原型页面索引(182 页文本库)',
'',
`- 清单: .scratch/lanhu-latest/axure-v29.json(web 端 v29, 2026-08-28)`,
`- 文本库: .scratch/opportunity-bugfix/lanhu-pages/*.md(id | 类型 | 名称 | 文本,含需求批注区)`,
`- 括号数字 = 该页有文本控件数;〔含批注〕= 含开发说明/变更记录批注;⚠缺失 = 文本库无对应文件`,
`- 拉取日志: .scratch/opportunity-bugfix/fetch-all.log`,
'',
`## 统计:sitemap 页 ${stats.total},已落盘 ${stats.withFile},空页 ${stats.noText},孤儿清单页 ${orphan.length}`,
'',
'## 重点页(票 02/07/08 输入)',
'',
`### A1X 工作计划(${focus.a1x.length} 页,票 07 可行性)`,
...focus.a1x.map(s => `- ${s}`), '',
`### A4 客户管理(${focus.a4.length} 页,票 08 可行性)`,
...focus.a4.map(s => `- ${s}`), '',
`### A3 商机全族(${focus.a3.length} 页,票 02 复测)`,
...focus.a3.map(s => `- ${s}`), '',
`### 暂缓/关闭相关页(D-14 等裁决线索)`,
...focus.notes.map(s => `- ${s}`), '',
'## Sitemap 树',
'',
...lines,
'',
];
if (orphan.length) out.push('## 清单里有但 sitemap 树没有的页', '', ...orphan.map(u => `- ${u}${mdOf(u.replace(/\.html$/, '').replace(/[\\/:*?"<>|\s]+/g, '_'))}`), '');
fs.writeFileSync(OUT, out.join('\n'), 'utf8');
console.log(`index written: ${OUT}`);
console.log(JSON.stringify(stats));
console.log('focus counts:', Object.fromEntries(Object.entries(focus).map(([k, v]) => [k, v.length])));