| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 |
- import fs from 'node:fs';
- import path from 'node:path';
- import crypto from 'node:crypto';
- import assert from 'node:assert/strict';
- // Change navigation only; keep the current multi-round manual and report bodies.
- const root = 'docs/workshop-quality-r13-20261110';
- const out = 'docs/workshop-dialogue-guide-20261111/site-navigation';
- const manual = 'final-康师傅工作坊操作手册.html';
- const sha = value => crypto.createHash('sha256').update(value).digest('hex');
- const read = file => fs.readFileSync(path.join(root, file), 'utf8');
- const write = (file, text) => fs.writeFileSync(path.join(root, file), text);
- const walk = dir => fs.readdirSync(dir, { withFileTypes: true }).flatMap(entry => {
- const file = path.join(dir, entry.name);
- return entry.isDirectory() ? walk(file) : [file];
- });
- fs.mkdirSync(out, { recursive: true });
- const articleHashes = {};
- const reportPages = [];
- for (const file of walk(root).filter(file => file.endsWith('.html'))) {
- const rel = path.relative(root, file).replaceAll('\\', '/');
- let html = read(rel);
- if (!html.includes('<header class="site-topbar">') || rel === 'index.html') continue;
- const articles = [...html.matchAll(/<article\b[\s\S]*?<\/article>/g)].map(match => match[0]);
- articles.forEach((article, index) => { articleHashes[`${rel}#${index}`] = sha(article); });
- const base = rel.includes('/') ? '../' : '';
- const header = `<header class="site-topbar"><span>产品创新工作坊</span><nav class="site-links" aria-label="网站导航"><a href="${base}${manual}">操作手册</a><a href="${base}案例报告合集.html" aria-current="page">报告案例</a><a href="https://kangshifu.fmode.cn/" target="_blank" rel="noopener">成果看板 <span aria-hidden="true">↗</span></a></nav></header>`;
- assert.equal([...html.matchAll(/<header class="site-topbar">/g)].length, 1, rel);
- html = html.replace(/<header class="site-topbar">[\s\S]*?<\/header>/, () => header)
- .replaceAll(`href="${base}index.html"`, `href="${base}${manual}"`)
- .replaceAll('>工作坊首页</a>', '>操作手册</a>');
- assert.deepEqual([...html.matchAll(/<article\b[\s\S]*?<\/article>/g)].map(match => match[0]), articles);
- write(rel, html);
- reportPages.push(rel);
- }
- let css = read('assets/workshop-reports.css');
- css = css.replace('justify-content:space-between;gap:18px;align-items:center;padding:14px 28px;z-index:20',
- 'gap:18px;align-items:center;height:51px;padding:0 28px;z-index:20')
- .replace('.site-links{display:flex;align-items:center;gap:20px}', '.site-links{display:flex;align-items:center;justify-content:flex-end;margin-left:auto;gap:20px}')
- .replace('.site-topbar{padding:14px 22px}', '.site-topbar{padding:0 22px}')
- .replace('html{scroll-padding-top:24px}.site-layout{display:block}', 'html{scroll-padding-top:70px}.site-layout{display:block;padding-top:51px}')
- .replace('.site-topbar{position:static;padding:11px 16px}', '.site-topbar{position:fixed;top:0;left:0;right:0;height:51px;padding:0 16px}')
- .replace('.site-links{width:100%;gap:18px}.site-links a:last-child{margin-left:auto}', '.site-links{gap:18px}')
- .replace('.source-item{scroll-margin-top:16px}', '.source-item{scroll-margin-top:70px}');
- write('assets/workshop-reports.css', css);
- let html = read(manual);
- html = html.replace('.topbar>span{margin-right:auto;font-size:11px}.topbar>a{font-size:11px}', '.topbar>span{display:none}.top-links{gap:18px}')
- .replace('.topbar>span{font-size:10px}.topbar>a{font-size:10px}', '.top-links{gap:16px}');
- write(manual, html);
- function redirect(target) {
- return `<!doctype html>
- <html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
- <title>康师傅 · 产品创新工作坊操作手册</title>
- <script>
- const destination = new URL(${JSON.stringify(target)}, location.href);
- destination.search = location.search;
- destination.hash = location.hash;
- location.replace(destination.href);
- </script>
- <noscript><meta http-equiv="refresh" content="0;url=${target}"></noscript>
- </head><body><p><a href="${target}">打开康师傅工作坊操作手册</a></p></body></html>
- `;
- }
- write('index.html', redirect(manual));
- write('manual/index.html', redirect('../' + manual));
- const routes = JSON.parse(read('site-routes.json'));
- routes.home = manual;
- routes.manual = manual;
- routes.redirects = { 'index.html': manual, 'manual/index.html': manual };
- routes.navigation = [
- { label: '操作手册', path: manual },
- { label: '报告案例', path: '案例报告合集.html' },
- { label: '成果看板', url: 'https://kangshifu.fmode.cn/' }
- ];
- write('site-routes.json', JSON.stringify(routes, null, 2) + '\n');
- const dataHashes = Object.fromEntries(walk(path.join(root, 'data')).map(file => [path.relative(root, file).replaceAll('\\', '/'), sha(fs.readFileSync(file))]));
- const beforeFile = path.join(out, 'before.json');
- if (!fs.existsSync(beforeFile)) fs.writeFileSync(beforeFile, JSON.stringify({ reportPages, articleHashes, dataHashes }, null, 2) + '\n');
- console.log(JSON.stringify({ ok: true, reportPagesUpdated: reportPages.length, home: manual, navigationLinks: 3 }));
|