chore: remove overview generation script
This commit is contained in:
@@ -1,508 +0,0 @@
|
||||
/**
|
||||
* 全平台原型完成情况汇总页生成脚本
|
||||
*
|
||||
* 用法:node scripts/generate-overview.js [输出目录]
|
||||
* 默认输出到:C:\Users\lenovo\Desktop\数字味道\蚁熊SAAS项目\产品\已完成\全平台
|
||||
*
|
||||
* 由 git post-merge hook 在 git pull 后自动触发
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// ===== 配置 =====
|
||||
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
||||
const INDEX_HTML = path.join(PROJECT_ROOT, 'index.html');
|
||||
const OUTPUT_DIR = process.argv[2] || 'C:\\Users\\lenovo\\Desktop\\数字味道\\蚁熊SAAS项目\\产品\\已完成\\全平台';
|
||||
|
||||
// ===== 从 index.html 提取 SITE_MAP 数据 =====
|
||||
function extractSiteMap(html) {
|
||||
// 找到 const SITE_MAP = [ 开始的位置
|
||||
const startMarker = 'const SITE_MAP = [';
|
||||
const startIdx = html.indexOf(startMarker);
|
||||
if (startIdx === -1) throw new Error('未找到 SITE_MAP 定义');
|
||||
|
||||
// 从 SITE_MAP 开始,数括号匹配找到结束的 ];
|
||||
let depth = 0;
|
||||
let inString = false;
|
||||
let stringChar = '';
|
||||
let i = startIdx + startMarker.length - 1; // -1 因为 [ 已经包含在 marker 中
|
||||
|
||||
for (; i < html.length; i++) {
|
||||
const ch = html[i];
|
||||
if (inString) {
|
||||
if (ch === '\\') { i++; continue; }
|
||||
if (ch === stringChar) { inString = false; }
|
||||
continue;
|
||||
}
|
||||
if (ch === '"' || ch === "'" || ch === '`') {
|
||||
inString = true;
|
||||
stringChar = ch;
|
||||
continue;
|
||||
}
|
||||
if (ch === '[' || ch === '{') { depth++; }
|
||||
else if (ch === ']' || ch === '}') {
|
||||
depth--;
|
||||
if (depth === 0) {
|
||||
// 找到了闭合的 ]
|
||||
const jsCode = html.substring(startIdx + startMarker.indexOf('['), i + 1);
|
||||
// 处理 JS 中的尾逗号(JSON 不允许)
|
||||
const cleanedCode = jsCode
|
||||
.replace(/,(\s*[}\]])/g, '$1') // 移除尾逗号
|
||||
.replace(/\/\*[\s\S]*?\*\//g, '') // 移除块注释
|
||||
.replace(/\/\/.*$/gm, ''); // 移除行注释
|
||||
try {
|
||||
return eval('(' + cleanedCode + ')');
|
||||
} catch (e) {
|
||||
throw new Error('SITE_MAP 解析失败: ' + e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error('SITE_MAP 未正确闭合');
|
||||
}
|
||||
|
||||
// ===== 提取 ICONS SVG 定义 =====
|
||||
function extractIcons(html) {
|
||||
const match = html.match(/const ICONS = (\{[\s\S]*?\n\};)/);
|
||||
if (!match) return {};
|
||||
try {
|
||||
const cleaned = match[1].replace(/,(\s*\})/g, '$1');
|
||||
return eval('(' + cleaned + ')');
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 计算模块状态 =====
|
||||
function getModuleStatus(mod) {
|
||||
const hasPages = mod.pages && mod.pages.length > 0;
|
||||
if (!hasPages) return { status: 'pending', label: '待设计', cls: 'pending' };
|
||||
if (mod.done) return { status: 'done', label: '已完成', cls: 'done' };
|
||||
return { status: 'wip', label: '设计中', cls: 'wip' };
|
||||
}
|
||||
|
||||
// ===== 统计各端数据 =====
|
||||
function buildTabStats(tab) {
|
||||
if (tab.roles) {
|
||||
// 健康应急 APP 特殊结构:roles + shared
|
||||
const roleModules = [];
|
||||
tab.roles.forEach(role => {
|
||||
role.modules.forEach(mod => {
|
||||
const st = getModuleStatus(mod);
|
||||
roleModules.push({
|
||||
name: role.role + ' - ' + mod.name,
|
||||
dir: mod.dir,
|
||||
pages: mod.pages || [],
|
||||
pageCount: (mod.pages || []).length,
|
||||
status: st.status,
|
||||
statusLabel: st.label,
|
||||
statusCls: st.cls,
|
||||
prd: mod.prd || null
|
||||
});
|
||||
});
|
||||
});
|
||||
if (tab.shared) {
|
||||
tab.shared.forEach(mod => {
|
||||
const st = getModuleStatus(mod);
|
||||
roleModules.push({
|
||||
name: '共享 - ' + mod.name,
|
||||
dir: mod.dir,
|
||||
pages: mod.pages || [],
|
||||
pageCount: (mod.pages || []).length,
|
||||
status: st.status,
|
||||
statusLabel: st.label,
|
||||
statusCls: st.cls,
|
||||
prd: null
|
||||
});
|
||||
});
|
||||
}
|
||||
return {
|
||||
id: tab.id,
|
||||
label: tab.label,
|
||||
modules: roleModules,
|
||||
totalPages: roleModules.reduce((s, m) => s + m.pageCount, 0),
|
||||
doneModules: roleModules.filter(m => m.status === 'done').length,
|
||||
wipModules: roleModules.filter(m => m.status === 'wip').length,
|
||||
pendingModules: roleModules.filter(m => m.status === 'pending').length,
|
||||
totalModules: roleModules.length
|
||||
};
|
||||
}
|
||||
|
||||
// 标准结构:modules[]
|
||||
const modules = (tab.modules || []).map(mod => {
|
||||
const st = getModuleStatus(mod);
|
||||
return {
|
||||
name: mod.name,
|
||||
dir: mod.dir,
|
||||
pages: mod.pages || [],
|
||||
pageCount: (mod.pages || []).length,
|
||||
status: st.status,
|
||||
statusLabel: st.label,
|
||||
statusCls: st.cls,
|
||||
prd: mod.prd || null,
|
||||
entry: mod.entry || 'index.html'
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
id: tab.id,
|
||||
label: tab.label,
|
||||
modules,
|
||||
totalPages: modules.reduce((s, m) => s + m.pageCount, 0),
|
||||
doneModules: modules.filter(m => m.status === 'done').length,
|
||||
wipModules: modules.filter(m => m.status === 'wip').length,
|
||||
pendingModules: modules.filter(m => m.status === 'pending').length,
|
||||
totalModules: modules.length
|
||||
};
|
||||
}
|
||||
|
||||
// ===== 生成 HTML =====
|
||||
function generateHTML(tabStats, generatedAt) {
|
||||
const grandTotalPages = tabStats.reduce((s, t) => s + t.totalPages, 0);
|
||||
const grandTotalModules = tabStats.reduce((s, t) => s + t.totalModules, 0);
|
||||
const grandDoneModules = tabStats.reduce((s, t) => s + t.doneModules, 0);
|
||||
|
||||
const platformColors = {
|
||||
'web-admin': '#69b1ff',
|
||||
'employee-app': '#95de64',
|
||||
'emergency-app': '#ffc069',
|
||||
'other-terminals': '#b37feb'
|
||||
};
|
||||
|
||||
const platformIcons = {
|
||||
'web-admin': '💻',
|
||||
'employee-app': '📱',
|
||||
'emergency-app': '🎥',
|
||||
'other-terminals': '🌐'
|
||||
};
|
||||
|
||||
const platformLabels = {
|
||||
'web-admin': 'Web 管理端',
|
||||
'employee-app': '员工端 APP',
|
||||
'emergency-app': '健康应急 APP',
|
||||
'other-terminals': '其他终端'
|
||||
};
|
||||
|
||||
function renderModuleCard(mod) {
|
||||
const statusConfig = {
|
||||
done: { cls: 'card--done', badge: '已完成', badgeCls: 'badge--done' },
|
||||
wip: { cls: 'card--wip', badge: '设计中', badgeCls: 'badge--wip' },
|
||||
pending: { cls: 'card--pending', badge: '待设计', badgeCls: 'badge--pending' }
|
||||
};
|
||||
const sc = statusConfig[mod.status];
|
||||
const pageLabel = mod.pageCount > 0 ? `${mod.pageCount} 页` : '暂无页面';
|
||||
const prdLink = mod.prd ? `<a class="card__prd" href="../platform-prototype/${mod.prd}" target="_blank">PRD</a>` : '';
|
||||
|
||||
return `
|
||||
<div class="card ${sc.cls}">
|
||||
<div class="card__header">
|
||||
<span class="card__name">${mod.name}</span>
|
||||
<span class="badge ${sc.badgeCls}">${sc.badge}</span>
|
||||
</div>
|
||||
<div class="card__meta">
|
||||
<span class="card__pages">${pageLabel}</span>
|
||||
${prdLink}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function renderTabSection(stats) {
|
||||
const color = platformColors[stats.id] || '#ccc';
|
||||
const icon = platformIcons[stats.id] || '';
|
||||
const donePct = stats.totalModules > 0
|
||||
? Math.round((stats.doneModules / stats.totalModules) * 100)
|
||||
: 0;
|
||||
|
||||
return `
|
||||
<div class="platform">
|
||||
<div class="platform__header" style="border-left-color: ${color}">
|
||||
<div class="platform__title">
|
||||
<span class="platform__icon">${icon}</span>
|
||||
<h2>${stats.label}</h2>
|
||||
</div>
|
||||
<div class="platform__summary">
|
||||
<span class="stat"><strong>${stats.totalModules}</strong> 模块</span>
|
||||
<span class="stat"><strong>${stats.totalPages}</strong> 页面</span>
|
||||
<span class="stat stat--done"><strong>${stats.doneModules}</strong> 已完成</span>
|
||||
<span class="stat stat--wip"><strong>${stats.wipModules}</strong> 设计中</span>
|
||||
<span class="stat stat--pending"><strong>${stats.pendingModules}</strong> 待设计</span>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-bar__fill" style="width:${donePct}%; background:${color}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="module-grid">
|
||||
${stats.modules.map(renderModuleCard).join('')}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>全平台原型完成情况汇总</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #1677FF;
|
||||
--text-primary: #1a1a2e;
|
||||
--text-secondary: #5a607f;
|
||||
--border: #e8ecf1;
|
||||
--bg: #f0f2f8;
|
||||
--card-bg: #fff;
|
||||
--radius: 10px;
|
||||
--shadow: 0 1px 3px rgba(0,0,0,0.04), 0 1px 2px rgba(0,0,0,0.06);
|
||||
--shadow-hover: 0 4px 16px rgba(22,119,255,0.10), 0 2px 6px rgba(0,0,0,0.06);
|
||||
}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.header {
|
||||
background: linear-gradient(135deg, #0b3d91 0%, #1677FF 40%, #4facfe 100%);
|
||||
color: #fff;
|
||||
padding: 32px 48px 24px;
|
||||
text-align: center;
|
||||
}
|
||||
.header__title { font-size: 24px; font-weight: 800; }
|
||||
.header__sub { font-size: 13px; color: rgba(255,255,255,0.65); margin-top: 6px; }
|
||||
|
||||
/* 总览卡片 */
|
||||
.overview {
|
||||
max-width: 1400px;
|
||||
margin: -16px auto 0;
|
||||
padding: 0 48px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.overview__cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
.overview__card {
|
||||
background: var(--card-bg);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.08);
|
||||
padding: 20px 24px;
|
||||
text-align: center;
|
||||
}
|
||||
.overview__card__num {
|
||||
font-size: 36px;
|
||||
font-weight: 800;
|
||||
color: var(--primary);
|
||||
line-height: 1.2;
|
||||
}
|
||||
.overview__card__num--green { color: #52c41a; }
|
||||
.overview__card__num--orange { color: #fa8c16; }
|
||||
.overview__card__num--purple { color: #722ed1; }
|
||||
.overview__card__label {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* 平台分区 */
|
||||
.content {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 28px 48px 48px;
|
||||
}
|
||||
.platform {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.platform__header {
|
||||
border-left: 4px solid;
|
||||
padding-left: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.platform__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.platform__icon { font-size: 22px; }
|
||||
.platform__title h2 { font-size: 18px; font-weight: 700; }
|
||||
.platform__summary {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.stat { font-size: 13px; color: var(--text-secondary); }
|
||||
.stat strong { color: var(--text-primary); }
|
||||
.stat--done strong { color: #52c41a; }
|
||||
.stat--wip strong { color: #fa8c16; }
|
||||
.stat--pending strong { color: #999; }
|
||||
.progress-bar {
|
||||
height: 6px;
|
||||
background: #e8ecf1;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.progress-bar__fill {
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
transition: width 0.6s ease;
|
||||
}
|
||||
|
||||
/* 模块网格 */
|
||||
.module-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* 模块卡片 */
|
||||
.card {
|
||||
background: var(--card-bg);
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--shadow);
|
||||
padding: 14px 16px;
|
||||
border: 1.5px solid var(--border);
|
||||
transition: all .2s;
|
||||
}
|
||||
.card:hover { box-shadow: var(--shadow-hover); transform: translateY(-2px); }
|
||||
.card--done { border-color: #b7eb8f; }
|
||||
.card--wip { border-color: #ffe58f; }
|
||||
.card--pending { border-style: dashed; border-color: #d0d5dd; opacity: 0.55; }
|
||||
.card__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.card__name { font-size: 14px; font-weight: 600; }
|
||||
.card__meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.card__pages { font-size: 12px; color: var(--text-secondary); }
|
||||
.card__prd {
|
||||
font-size: 11px;
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
padding: 2px 8px;
|
||||
background: #e6f7ff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.card__prd:hover { text-decoration: underline; }
|
||||
.badge {
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.badge--done { background: #f6ffed; color: #52c41a; border: 1px solid #b7eb8f; }
|
||||
.badge--wip { background: #fffbe6; color: #fa8c16; border: 1px solid #ffe58f; }
|
||||
.badge--pending { background: #fafafa; color: #999; border: 1px solid #d0d5dd; }
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 20px 48px 32px;
|
||||
font-size: 12px;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
/* 模块入口链接 */
|
||||
.card__link {
|
||||
font-size: 11px;
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
padding: 2px 8px;
|
||||
background: #f0f5ff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.card__link:hover { text-decoration: underline; }
|
||||
|
||||
@media print {
|
||||
body { background: #fff; }
|
||||
.header { background: #1677FF !important; -webkit-print-color-adjust: exact; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="header">
|
||||
<h1 class="header__title">健康CQ升级 — 全平台原型完成情况</h1>
|
||||
<p class="header__sub">自动生成于 ${generatedAt} | 数据来源:git pull 后最新 SITE_MAP</p>
|
||||
</div>
|
||||
|
||||
<div class="overview">
|
||||
<div class="overview__cards">
|
||||
<div class="overview__card">
|
||||
<div class="overview__card__num">${grandTotalPages}</div>
|
||||
<div class="overview__card__label">全平台页面总数</div>
|
||||
</div>
|
||||
<div class="overview__card">
|
||||
<div class="overview__card__num overview__card__num--green">${grandDoneModules}</div>
|
||||
<div class="overview__card__label">已完成模块</div>
|
||||
</div>
|
||||
<div class="overview__card">
|
||||
<div class="overview__card__num overview__card__num--orange">${grandTotalModules - grandDoneModules}</div>
|
||||
<div class="overview__card__label">未完成模块</div>
|
||||
</div>
|
||||
<div class="overview__card">
|
||||
<div class="overview__card__num overview__card__num--purple">${grandTotalModules > 0 ? Math.round((grandDoneModules / grandTotalModules) * 100) : 0}%</div>
|
||||
<div class="overview__card__label">模块完成率</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
${tabStats.map(renderTabSection).join('')}
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
本文件由 git post-merge hook 自动生成 | 最近一次 git 提交后触发 | 请勿手动编辑
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
// ===== 主流程 =====
|
||||
function main() {
|
||||
console.log('[generate-overview] 读取 index.html...');
|
||||
const html = fs.readFileSync(INDEX_HTML, 'utf-8');
|
||||
|
||||
console.log('[generate-overview] 解析 SITE_MAP...');
|
||||
const siteMap = extractSiteMap(html);
|
||||
|
||||
console.log('[generate-overview] 统计各端数据...');
|
||||
const tabStats = siteMap.map(buildTabStats);
|
||||
|
||||
const now = new Date();
|
||||
const generatedAt = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
|
||||
|
||||
console.log('[generate-overview] 生成 HTML...');
|
||||
const outputHTML = generateHTML(tabStats, generatedAt);
|
||||
|
||||
// 确保输出目录存在
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
const dateStr = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`;
|
||||
const outputFile = path.join(OUTPUT_DIR, `全平台原型进度_${dateStr}.html`);
|
||||
|
||||
fs.writeFileSync(outputFile, outputHTML, 'utf-8');
|
||||
console.log(`[generate-overview] 已生成: ${outputFile}`);
|
||||
|
||||
// 输出统计摘要
|
||||
const grandTotal = tabStats.reduce((s, t) => s + t.totalPages, 0);
|
||||
console.log(`[generate-overview] 全平台总计: ${grandTotal} 个页面, ${tabStats.reduce((s, t) => s + t.totalModules, 0)} 个模块`);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user