Files
platform-prototype/index.html
T
冯普andClaude Opus 4.6 be6f5a7f81 feat: 营养管理模块原型页面新增及链路可视化重构
- 新增56个营养管理原型页面(溯源、档案、种采、供应、生产、用餐、异常)
- 更新 index.html SITE_MAP 同步所有营养管理页面条目
- trace-timeline.html 重构为数据驱动渲染,侧边栏修正为深色模式

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 08:35:50 +08:00

849 lines
55 KiB
HTML

<!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;
--primary-light: #E6F7FF;
--primary-dark: #0958D9;
--success: #52C41A;
--warning: #FAAD14;
--danger: #FF4D4F;
--purple: #722ED1;
--green: #52C41A;
--green-light: #f6ffed;
--green-border: #b7eb8f;
--text-primary: #1a1a2e;
--text-secondary: #5a607f;
--text-placeholder: #999999;
--border: #e8ecf1;
--background: #f0f2f8;
--card-bg: #ffffff;
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.04), 0 1px 2px rgba(0,0,0,0.06);
--shadow-md: 0 4px 16px rgba(22,119,255,0.10), 0 2px 6px rgba(0,0,0,0.06);
--shadow-lg: 0 10px 30px rgba(22,119,255,0.15), 0 4px 10px rgba(0,0,0,0.06);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: var(--background); font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', 'Segoe UI', sans-serif; color: var(--text-primary); min-height: 100vh; }
/* ===== Header ===== */
.header { position: relative; background: linear-gradient(135deg, #0b3d91 0%, var(--primary) 40%, #4facfe 100%); color: #fff; padding: 40px 48px 20px; overflow: hidden; }
.header::before { content: ''; position: absolute; top: -60px; right: -40px; width: 260px; height: 260px; background: radial-gradient(circle, rgba(255,255,255,0.12) 0%, transparent 70%); border-radius: 50%; }
.header::after { content: ''; position: absolute; bottom: -80px; left: 20%; width: 360px; height: 360px; background: radial-gradient(circle, rgba(255,255,255,0.06) 0%, transparent 70%); border-radius: 50%; }
.header__inner { position: relative; z-index: 1; max-width: 1400px; margin: 0 auto; text-align: center; }
.header__title { font-size: 28px; font-weight: 800; letter-spacing: 1px; text-shadow: 0 2px 8px rgba(0,0,0,0.15); }
.header__sub { font-size: 14px; color: rgba(255,255,255,0.7); margin-top: 8px; }
/* ===== Tab 导航(嵌入 Header 底部) ===== */
.tab-nav { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 20px 48px 0; position: relative; z-index: 1; }
.tab-nav__item { padding: 10px 28px; font-size: 15px; font-weight: 600; color: rgba(255,255,255,0.7); cursor: pointer; border-radius: 28px; transition: all .25s ease; user-select: none; display: flex; align-items: center; gap: 8px; border: 1.5px solid rgba(255,255,255,0.25); background: rgba(255,255,255,0.08); backdrop-filter: blur(6px); }
.tab-nav__item:hover { color: #fff; background: rgba(255,255,255,0.18); border-color: rgba(255,255,255,0.4); }
.tab-nav__item--active { color: var(--primary-dark); background: #fff; border-color: #fff; box-shadow: 0 4px 16px rgba(0,0,0,0.15); }
.tab-nav__item--active .tab-nav__badge { background: var(--primary); color: #fff; }
.tab-nav__badge { font-size: 11px; padding: 2px 9px; border-radius: 10px; background: rgba(255,255,255,0.2); color: rgba(255,255,255,0.9); font-weight: 700; transition: all .25s; }
.tab-nav__icon { width: 18px; height: 18px; flex-shrink: 0; }
/* ===== 内容区 ===== */
.main-content { max-width: 1400px; margin: 0 auto; padding: 28px 48px 48px; }
.tab-content { display: none; animation: fadeUp .35s ease; }
.tab-content--active { display: block; }
@keyframes fadeUp { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
/* ===== 模块网格 ===== */
.module-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 20px; align-items: stretch; }
.module-grid--center { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 20px; max-width: 960px; margin: 0 auto; align-items: stretch; }
/* ===== 角色分组 ===== */
.role-group { margin-bottom: 28px; text-align: center; }
.role-group:last-child { margin-bottom: 0; }
.role-group__title { display: inline-flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 600; color: var(--text-primary); margin-bottom: 16px; padding: 6px 16px 6px 12px; background: var(--card-bg); border-radius: var(--radius-md); box-shadow: var(--shadow-sm); }
.role-icon { width: 22px; height: 22px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 13px; color: #fff; flex-shrink: 0; }
.role-icon--expert { background: linear-gradient(135deg, #1677FF, #4facfe); }
.role-icon--assistant { background: linear-gradient(135deg, #722ED1, #b37feb); }
.role-icon--operator { background: linear-gradient(135deg, #fa541c, #ff7a45); }
.role-icon--professional { background: linear-gradient(135deg, #52c41a, #95de64); }
.role-icon--onsite { background: linear-gradient(135deg, #faad14, #ffc53d); }
.role-icon--admin { background: linear-gradient(135deg, #eb2f96, #ff85c0); }
.role-icon--shared { background: linear-gradient(135deg, #13c2c2, #5cdbd3); }
/* ===== 呼吸灯动画 ===== */
@keyframes breathe {
0%, 100% { border-color: rgba(82,196,26,0.35); box-shadow: 0 0 0 0 rgba(82,196,26,0); }
50% { border-color: rgba(82,196,26,0.7); box-shadow: 0 0 12px 2px rgba(82,196,26,0.18); }
}
/* ===== 模块卡片 ===== */
.module-card { position: relative; background: var(--card-bg); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); padding: 16px 20px; transition: all .25s ease; border: 1.5px solid var(--border); cursor: pointer; z-index: 1; display: flex; flex-direction: column; justify-content: space-between; }
.module-card--pop-open { z-index: 200; }
.module-card:hover { box-shadow: var(--shadow-lg); transform: translateY(-3px); }
.module-card--ready { border-color: var(--green-border); animation: breathe 3s ease-in-out infinite; }
.module-card--ready:hover { border-color: var(--green); animation: none; box-shadow: 0 8px 24px rgba(82,196,26,0.2); }
.module-card--wip { border-color: #ffe58f; }
.module-card--wip:hover { border-color: var(--warning); box-shadow: 0 8px 24px rgba(250,173,20,0.15); }
.module-card--empty { opacity: 0.55; border-style: dashed; border-color: #d0d5dd; cursor: default; }
.module-card--empty:hover { transform: none; box-shadow: var(--shadow-sm); }
/* ===== 圆形水印 ===== */
.module-card__watermark { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 64px; height: 64px; border-radius: 50%; border: 2px dashed rgba(250,173,20,0.28); display: flex; align-items: center; justify-content: center; pointer-events: none; z-index: 0; }
.module-card__watermark-text { font-size: 11px; font-weight: 700; color: rgba(250,140,22,0.28); text-align: center; line-height: 1.3; letter-spacing: 0.5px; }
/* ===== 卡片头部行:左名称 + 右状态标签 ===== */
.module-card__header { display: flex; align-items: center; justify-content: space-between; position: relative; z-index: 1; }
.module-card__name { font-size: 15px; font-weight: 700; color: var(--text-primary); }
.module-card__status { font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 10px; letter-spacing: 0.3px; flex-shrink: 0; }
.module-card__status--pending { background: #f5f5f5; color: #999; border: 1px solid #e8e8e8; }
.module-card__status--wip { background: #fff7e6; color: #d46b08; border: 1px solid #ffd591; }
.module-card__status--done { background: var(--green-light); color: #389e0d; border: 1px solid var(--green-border); }
/* ===== 底部行:PRD左 + 页数右 ===== */
.module-card__bottom { display: flex; align-items: center; justify-content: space-between; margin-top: 12px; position: relative; z-index: 1; }
.module-card__page-count { font-size: 11px; padding: 3px 10px; border-radius: 12px; font-weight: 600; letter-spacing: 0.3px; background: var(--primary-light); color: var(--primary); border: 1px solid #91caff; cursor: pointer; transition: all .15s; position: relative; margin-left: auto; }
.module-card__page-count:hover { background: #bae0ff; transform: scale(1.05); }
/* ===== 悬浮页面列表 ===== */
.page-popover { display: none; position: absolute; top: 100%; right: 0; margin-top: 6px; background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: 0 8px 30px rgba(0,0,0,0.15); padding: 8px 0; z-index: 100; min-width: 200px; max-height: 320px; overflow-y: auto; animation: popIn .2s ease; }
.page-popover--visible { display: block; }
@keyframes popIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }
.page-popover__title { font-size: 12px; color: var(--text-placeholder); padding: 4px 14px 8px; border-bottom: 1px solid var(--border); margin-bottom: 4px; }
.page-popover__item { display: flex; align-items: center; gap: 8px; padding: 7px 14px; font-size: 13px; color: var(--text-primary); text-decoration: none; transition: background .15s; }
.page-popover__item:hover { background: var(--primary-light); color: var(--primary-dark); }
.page-popover__item svg { flex-shrink: 0; }
.page-popover__item--dialog { color: #ad4e00; }
.page-popover__item--dialog:hover { background: #fff7e6; color: #ad4e00; }
/* ===== 卡片底部 PRD 链接 ===== */
.module-card__prd { font-size: 12px; color: var(--primary); cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: var(--radius-sm); transition: all .15s; }
.module-card__prd:hover { background: var(--primary-light); color: var(--primary-dark); }
.module-card__prd svg { flex-shrink: 0; }
/* ===== PRD Tab 页 ===== */
.prd-page__global { margin-bottom: 28px; }
.prd-global-card { background: linear-gradient(135deg, #f6ffed 0%, #e6f7ff 100%); border: 1.5px solid var(--green-border); border-radius: var(--radius-lg); padding: 20px 24px; cursor: pointer; transition: all .25s; display: flex; align-items: center; gap: 16px; }
.prd-global-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.prd-global-card__icon { width: 44px; height: 44px; border-radius: 12px; background: linear-gradient(135deg, var(--primary), #4facfe); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.prd-global-card__info { flex: 1; }
.prd-global-card__title { font-size: 16px; font-weight: 700; color: var(--text-primary); }
.prd-global-card__desc { font-size: 13px; color: var(--text-secondary); margin-top: 4px; }
.prd-section { margin-bottom: 28px; }
.prd-section__title { font-size: 15px; font-weight: 600; color: var(--text-primary); margin-bottom: 14px; padding: 6px 16px; background: var(--card-bg); border-radius: var(--radius-md); box-shadow: var(--shadow-sm); display: inline-flex; align-items: center; gap: 8px; }
.prd-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }
.prd-item { background: var(--card-bg); border: 1.5px solid var(--border); border-radius: var(--radius-md); padding: 16px 18px; cursor: pointer; transition: all .2s; display: flex; align-items: center; gap: 12px; }
.prd-item:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); border-color: var(--primary); }
.prd-item--disabled { opacity: 0.45; cursor: default; border-style: dashed; }
.prd-item--disabled:hover { box-shadow: none; transform: none; border-color: var(--border); }
.prd-item__icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; font-size: 18px; }
.prd-item__icon--ready { background: var(--green-light); color: #389e0d; }
.prd-item__icon--empty { background: #f5f5f5; color: #bbb; }
.prd-item__info { flex: 1; min-width: 0; }
.prd-item__name { font-size: 14px; font-weight: 600; color: var(--text-primary); }
.prd-item__path { font-size: 11px; color: var(--text-placeholder); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.prd-item__status { font-size: 11px; padding: 2px 8px; border-radius: 10px; font-weight: 600; flex-shrink: 0; }
.prd-item__status--ready { background: var(--green-light); color: #389e0d; border: 1px solid var(--green-border); }
.prd-item__status--empty { background: #fffbe6; color: #d48806; border: 1px solid #ffe58f; }
</style>
</head>
<body>
<div class="header">
<div class="header__inner">
<div class="header__title">健康长庆升级项目 · 高保真交互原型</div>
<div class="header__sub">点击模块卡片跳转至对应模块主页,点击页数徽章查看全部页面,点击PRD文档在线预览详细功能说明</div>
</div>
<div class="tab-nav" id="tab-nav"></div>
</div>
<div class="main-content">
<div id="tab-contents"></div>
</div>
<script>
/* ===== SVG 图标 ===== */
const ICONS = {
web: '<svg class="tab-nav__icon" viewBox="0 0 18 18" fill="none"><rect x="1" y="2" width="16" height="12" rx="2" stroke="currentColor" stroke-width="1.5"/><line x1="5" y1="16" x2="13" y2="16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><line x1="9" y1="14" x2="9" y2="16" stroke="currentColor" stroke-width="1.5"/></svg>',
app: '<svg class="tab-nav__icon" viewBox="0 0 18 18" fill="none"><rect x="4" y="1" width="10" height="16" rx="2" stroke="currentColor" stroke-width="1.5"/><line x1="7.5" y1="14" x2="10.5" y2="14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>',
emergency: '<svg class="tab-nav__icon" viewBox="0 0 18 18" fill="none"><path d="M9 1L2 6.5V16h5v-4h4v4h5V6.5L9 1z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M9 7v3M9 11.5v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>',
page: '<svg width="14" height="14" viewBox="0 0 12 12" fill="none"><path d="M3 1h4l3 3v7H3V1z" stroke="currentColor" stroke-width="1.1"/><path d="M7 1v3h3" stroke="currentColor" stroke-width="1.1"/></svg>',
dialog: '<svg width="14" height="14" viewBox="0 0 12 12" fill="none"><rect x="1.5" y="2" width="9" height="8" rx="1.5" stroke="currentColor" stroke-width="1.1"/><line x1="4" y1="5" x2="8" y2="5" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/><line x1="4" y1="7.5" x2="6.5" y2="7.5" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/></svg>',
empty: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.2" stroke-dasharray="3 2"/><path d="M8 5.5v3M8 10v.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>',
prd: '<svg class="tab-nav__icon" viewBox="0 0 18 18" fill="none"><path d="M4 1h7l4 4v12H4V1z" stroke="currentColor" stroke-width="1.5"/><path d="M11 1v4h4" stroke="currentColor" stroke-width="1.5"/><line x1="7" y1="9" x2="13" y2="9" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/><line x1="7" y1="12" x2="11" y2="12" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>',
prdSmall: '<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 1h5l2.5 2.5v7.5h-7.5V1z" stroke="currentColor" stroke-width="1"/><path d="M7.5 1v2.5h2.5" stroke="currentColor" stroke-width="1"/></svg>',
role: { expert:'👨‍⚕', assistant:'🤖', operator:'📋', professional:'👷', onsite:'🏥', admin:'⚙', shared:'🔗' }
};
const SITE_MAP = [
{ id:'web-admin', label:'Web 管理端', icon:'web', color:'#69b1ff', modules:[
{ name:'系统管理', dir:'web-admin/system-management', pages:[] },
{ name:'健康体检', dir:'web-admin/health-checkup', entry:'archive-personal.html', pages:[
{ file:'archive-personal.html', label:'个人体检数据' },
{ file:'archive-unit.html', label:'单位报表数据' },
{ file:'config-material-notice.html', label:'体检须知' },
{ file:'config-material-video.html', label:'视频信息' },
{ file:'config-hospital-basic.html', label:'基本信息配置' },
{ file:'config-hospital-combo.html', label:'检查项组合配置' },
{ file:'config-report-template.html', label:'体检报告模板' },
{ file:'config-standard-category.html', label:'体检项目分类' },
{ file:'config-standard-basic.html', label:'体检基本项目' },
{ file:'config-standard-hospital.html', label:'医院体检项目' },
{ file:'pre-hospital-scope.html', label:'医院承检范围' },
{ file:'pre-plan.html', label:'体检计划管理' },
{ file:'pre-quota-group.html', label:'群体额度' },
{ file:'pre-quota-personal.html', label:'个人额度' },
{ file:'pre-item-hospital-fee.html', label:'医院体检项目费用' },
{ file:'pre-item-unit-fee.html', label:'单位体检项目费用' },
{ file:'pre-item-combo-template.html', label:'体检项目组合模板' },
{ file:'pre-item-exception.html', label:'组合模板例外配置' },
{ file:'pre-item-required.html', label:'必检/选检项目配置' },
{ file:'pre-special-cancer.html', label:'防癌筛查人员' },
{ file:'pre-special-occupation.html', label:'职业体检人员' },
{ file:'pre-special-gastro.html', label:'胃肠镜体检人员' },
{ file:'pre-special-cta.html', label:'冠脉CTA体检人员' },
{ file:'pre-special-hospital.html', label:'指定员工体检医院' },
{ file:'mid-appointment-track.html', label:'员工预约跟踪' },
{ file:'mid-unit-appointment.html', label:'单位预约统计' },
{ file:'mid-participation.html', label:'员工参检情况' },
{ file:'mid-hospital-realtime.html', label:'医院实时预约人数' },
{ file:'mid-doctor-realtime.html', label:'医生实时检查情况' },
{ file:'mid-doctor-count.html', label:'医生检查人数统计' },
{ file:'mid-hospital-appointment.html', label:'医院预约情况统计' },
{ file:'post-unit-count.html', label:'单位体检人数统计' },
{ file:'post-hospital-count.html', label:'医院体检人数统计' },
{ file:'post-doctor-count.html', label:'医生检查人数统计' },
{ file:'post-item-stat.html', label:'医院体检项目统计' },
{ file:'post-cancer-screening.html', label:'防癌筛查项目统计' },
{ file:'post-standard-stat.html', label:'系统标准化项目统计' },
{ file:'post-contracted-hospital.html', label:'签约医院人数统计' },
{ file:'post-report-employee.html', label:'员工体检报告' },
{ file:'post-report-unmatched.html', label:'体检报告未匹配项目' },
{ file:'post-report-roster.html', label:'体检结果名册' },
{ file:'exception-dirty.html', label:'系统脏数据' },
{ file:'exception-result.html', label:'结果异常' }
]},
{ name:'体重管理', dir:'web-admin/weight-management', pages:[] },
{ name:'营养管理', dir:'web-admin/nutrition', entry:'trace-query.html', pages:[
{ file:'trace-query.html', label:'溯源查询' },
{ file:'trace-timeline.html', label:'链路可视化' },
{ file:'arc-employee.html', label:'员工营养数据' },
{ file:'arc-unit.html', label:'单位报表数据' },
{ file:'arc-algo.html', label:'算法模型' },
{ file:'farm-land.html', label:'土地环境' },
{ file:'farm-monitor-crop.html', label:'作物种植监控' },
{ file:'farm-monitor-livestock.html', label:'畜禽养殖监控' },
{ file:'farm-harvest.html', label:'收成入库' },
{ file:'farm-supplier.html', label:'供商管理' },
{ file:'farm-purchase.html', label:'采供管理' },
{ file:'sup-hygiene-check.html', label:'卫生检测' },
{ file:'sup-in-raw.html', label:'毛菜入库' },
{ file:'sup-in-clean.html', label:'净菜加工' },
{ file:'sup-in-cleanin.html', label:'净菜入库' },
{ file:'sup-pkg-label.html', label:'净菜包装' },
{ file:'sup-warehouse.html', label:'仓储管理' },
{ file:'sup-clean-internal.html', label:'净菜内供' },
{ file:'sup-clean-external.html', label:'净菜外售' },
{ file:'din-user-mgmt.html', label:'用户管理' },
{ file:'din-user-mgmt-dialog.html', label:'用户管理弹窗', dialog:true },
{ file:'din-dish-all.html', label:'菜品总库' },
{ file:'din-dish-all-dialog.html', label:'菜品总库弹窗', dialog:true },
{ file:'din-dish-add.html', label:'新增菜品' },
{ file:'din-dish-composition.html', label:'菜品成分' },
{ file:'din-ingredient.html', label:'食材管理' },
{ file:'din-ingredient-dialog.html', label:'食材管理弹窗', dialog:true },
{ file:'din-ingredient-detail.html', label:'食材详情' },
{ file:'din-recipe-mgmt.html', label:'食谱管理' },
{ file:'din-terminal.html', label:'终端管理' },
{ file:'din-order-meal.html', label:'点餐订单' },
{ file:'din-order-line.html', label:'排队取餐' },
{ file:'din-order-pickup.html', label:'取餐管理' },
{ file:'din-innovation.html', label:'菜品创新' },
{ file:'din-dict-mgmt.html', label:'字典管理' },
{ file:'din-dict-config.html', label:'字典配置' },
{ file:'din-algo-population.html', label:'群体算法' },
{ file:'din-algo-vip.html', label:'VIP算法' },
{ file:'din-model-llm.html', label:'大模型管理' },
{ file:'din-model-small.html', label:'小模型管理' },
{ file:'din-data-nutri.html', label:'营养数据' },
{ file:'din-data-consume.html', label:'消费数据' },
{ file:'din-data-benefit.html', label:'效益数据' },
{ file:'prod-recipe-plan.html', label:'食谱排期' },
{ file:'prod-recipe-plan-dialog.html', label:'食谱排期弹窗', dialog:true },
{ file:'prod-clean-order.html', label:'净菜下单' },
{ file:'prod-proc-combo.html', label:'配菜组合' },
{ file:'prod-proc-cook.html', label:'烹饪加工' },
{ file:'prod-proc-serve.html', label:'出餐管理' },
{ file:'prod-proc-surplus.html', label:'余量处理' },
{ file:'prod-proc-cabinet.html', label:'取餐柜管理' },
{ file:'prod-proc-stats.html', label:'加工统计' },
{ file:'prod-qc-daily.html', label:'日常质检' },
{ file:'prod-qc-nutrition.html', label:'营养质检' },
{ file:'ano-dirty.html', label:'系统脏数据' },
{ file:'ano-result.html', label:'结果异常' }
]},
{ name:'运动管理', dir:'web-admin/exercise', pages:[] },
{ name:'健康监测', dir:'web-admin/health-monitor', pages:[] },
{ name:'健康评估', dir:'web-admin/health-assessment', pages:[] },
{ name:'知识普及', dir:'web-admin/knowledge', pages:[] },
{ name:'健康数据', dir:'web-admin/health-data', pages:[] },
{ name:'健康档案', dir:'web-admin/health-record', pages:[] },
{ name:'心脑血管病预防', dir:'web-admin/cardiovascular', pages:[] },
{ name:'糖尿病预防', dir:'web-admin/diabetes', pages:[] },
{ name:'癌症预防', dir:'web-admin/cancer', pages:[] },
{ name:'专家咨询(管理员)', dir:'web-admin/health-consult', entry:'history-expert.html', prd:'web-admin/health-consult/doc/PRD.md', done:true, pages:[
{ file:'history-expert.html', label:'专家咨询记录' },
{ file:'video-detail-pending-confirm.html', label:'视频详情-待确认' },
{ file:'video-detail-pending-start.html', label:'视频详情-待开始' },
{ file:'video-detail-completed.html', label:'视频详情-已完成' },
{ file:'video-detail-rejected.html', label:'视频详情-已拒绝' },
{ file:'video-detail-cancelled.html', label:'视频详情-已取消' },
{ file:'video-detail-in-progress.html', label:'视频详情-进行中' },
{ file:'history-assistant.html', label:'小助手咨询记录' },
{ file:'history-evaluation.html', label:'用户评价' },
{ file:'consult-record.html', label:'咨询档案' },
{ file:'consult-record-detail.html', label:'咨询档案详情' },
{ file:'archive-personal.html', label:'个人咨询数据' },
{ file:'archive-personal-detail.html', label:'员工咨询明细' },
{ file:'archive-unit.html', label:'单位报表数据' },
{ file:'archive-unit-detail.html', label:'单位报表明细' },
{ file:'expert-statistics.html', label:'专家咨询统计' },
{ file:'unit-statistics.html', label:'单位咨询统计' },
{ file:'settlement.html', label:'咨询结算' },
{ file:'settlement-add.html', label:'新增结算' },
{ file:'settlement-report.html', label:'结算报表' },
{ file:'exception-dirty.html', label:'系统脏数据' },
{ file:'config-hospital.html', label:'医院配置' },
{ file:'config-department.html', label:'科室配置' },
{ file:'config-disease.html', label:'疾病配置' },
{ file:'config-notice.html', label:'咨询须知' },
{ file:'config-phrase.html', label:'常用语管理' },
{ file:'config-hospital-dialog.html', label:'医院配置弹窗', dialog:true },
{ file:'config-department-dialog.html', label:'科室配置弹窗', dialog:true },
{ file:'config-disease-dialog.html', label:'疾病配置弹窗', dialog:true },
{ file:'config-phrase-dialog.html', label:'常用语弹窗', dialog:true }
]},
{ name:'专家咨询(小助手)', dir:'web-admin/health-consult/assistant', entry:'current-consult.html', prd:'web-admin/health-consult/assistant/doc/PRD.md', done:true, pages:[
{ file:'current-consult.html', label:'当前咨询' },
{ file:'chat-message.html', label:'对话消息' },
{ file:'history-consult.html', label:'历史咨询' },
{ file:'expert-manage.html', label:'专家管理' },
{ file:'checkup-standard.html', label:'体检标准化' },
{ file:'sms-send.html', label:'短信发送' },
{ file:'config-phrase.html', label:'常用语' },
{ file:'consult-settle.html', label:'咨询结算' },
{ file:'consult-settle-add.html', label:'新增/编辑结算配置' },
{ file:'consult-settle-report.html', label:'结算报表' },
{ file:'employee-profile-dialog.html', label:'咨询人档案弹窗', dialog:true },
{ file:'select-expert-dialog.html', label:'选择专家弹窗', dialog:true },
{ file:'contacts-dialog.html', label:'通讯录弹窗', dialog:true },
{ file:'chat-detail-dialog.html', label:'对话详情弹窗', dialog:true },
{ file:'expert-detail-dialog.html', label:'专家详情弹窗', dialog:true },
{ file:'expert-import-dialog.html', label:'批量导入弹窗', dialog:true },
{ file:'export-task-dialog.html', label:'导出任务弹窗', dialog:true },
{ file:'import-task-dialog.html', label:'导入任务弹窗', dialog:true },
{ file:'item-detail-dialog.html', label:'项目明细弹窗', dialog:true },
{ file:'send-dialog.html', label:'新建短信弹窗', dialog:true },
{ file:'sms-detail-dialog.html', label:'发送详情弹窗', dialog:true },
{ file:'report-detail-dialog.html', label:'报表详情弹窗', dialog:true },
{ file:'expert-schedule-dialog.html', label:'专家排班弹窗', dialog:true }
]},
{ name:'应急就医(管理员)', dir:'web-admin/emergency-dispatch/admin', entry:'archive-personal.html', prd:'web-admin/emergency-dispatch/admin/doc/PRD.md', done:true, pages:[
{ file:'archive-personal.html', label:'个人应急数据' },
{ file:'archive-personal-detail.html', label:'个人应急数据详情' },
{ file:'archive-personal-detail-illness.html', label:'个人大病就医详情' },
{ file:'archive-unit.html', label:'单位报表数据' },
{ file:'archive-unit-detail.html', label:'单位报表详情' },
{ file:'archive-unit-detail-illness.html', label:'单位大病就医详情' },
{ file:'archive-center.html', label:'应急中心报表数据' },
{ file:'resource-hospital.html', label:'医院信息' },
{ file:'resource-medical-point.html', label:'医疗点信息' },
{ file:'resource-medical-point-detail.html', label:'医疗点详情' },
{ file:'resource-aed.html', label:'AED设备信息-设备管理' },
{ file:'resource-aed-add.html', label:'AED设备信息-新增AED设备' },
{ file:'resource-aed-deploy.html', label:'AED设备信息-布点管理' },
{ file:'resource-aed-deploy-add.html', label:'AED设备信息-新增布点' },
{ file:'resource-aed-map.html', label:'AED设备信息-AED地图' },
{ file:'resource-aed-certifier.html', label:'AED设备信息-取证人员' },
{ file:'resource-aed-certifier-add.html', label:'AED设备信息-新增取证人员' },
{ file:'resource-aed-maintenance.html', label:'AED设备信息-维护记录' },
{ file:'resource-aed-maintenance-add.html', label:'AED设备信息-新增维护记录' },
{ file:'resource-aed-training.html', label:'AED设备信息-培演记录' },
{ file:'resource-aed-training-add.html', label:'AED设备信息-新增培演记录' },
{ file:'resource-aed-usage.html', label:'AED设备信息-应用记录' },
{ file:'resource-aed-usage-add.html', label:'AED设备信息-新增应用记录' },
{ file:'resource-ambulance.html', label:'救护车信息' },
{ file:'resource-ambulance-detail.html', label:'救护车详情' },
{ file:'resource-first-aid-kit.html', label:'急救包信息' },
{ file:'resource-first-aid-kit-add.html', label:'新增急救包' },
{ file:'resource-doctor.html', label:'急救医生信息' },
{ file:'resource-doctor-add.html', label:'新增急救医生' },
{ file:'resource-emergency-contact.html', label:'紧急联系人信息' },
{ file:'center-basic-info.html', label:'应急中心基本信息' },
{ file:'center-basic-info-add.html', label:'新增应急中心' },
{ file:'center-basic-info-detail.html', label:'应急中心详情' },
{ file:'center-work-order.html', label:'应急工单' },
{ file:'center-work-order-add.html', label:'新增应急工单' },
{ file:'center-work-order-detail.html', label:'应急工单详情' },
{ file:'center-group.html', label:'分组管理' },
{ file:'center-call-record.html', label:'通话记录' },
{ file:'center-serious-illness.html', label:'大病就医' },
{ file:'center-serious-illness-add.html', label:'新增大病就医' },
{ file:'center-serious-illness-detail.html', label:'大病就医详情' },
{ file:'center-data-screen.html', label:'大屏数据配置' },
{ file:'center-schedule.html', label:'排班配置' },
{ file:'center-simulation.html', label:'模拟急救联动' },
{ file:'exception-dirty.html', label:'系统脏数据' },
{ file:'exception-result.html', label:'结果异常' }
]},
{ name:'应急就医(操作人员)', dir:'web-admin/emergency-dispatch/operator', entry:'emergency-active.html', prd:'web-admin/emergency-dispatch/operator/doc/PRD.md', done:true, pages:[
{ file:'emergency-active.html', label:'正在应急' },
{ file:'work-order.html', label:'应急工单' },
{ file:'call-record.html', label:'通话记录' },
{ file:'duty-group.html', label:'值班小组' },
{ file:'serious-illness.html', label:'大病就医' },
{ file:'chat-message.html', label:'对话消息' }
]},
{ name:'应急就医(专业人员)', dir:'web-admin/emergency-dispatch/professional', entry:'professional.html', prd:'web-admin/emergency-dispatch/professional/doc/PRD.md', done:true, pages:[
{ file:'professional.html', label:'正在应急' },
{ file:'chat-message.html', label:'对话消息' }
]},
{ name:'一线医疗', dir:'web-admin/frontline-medical', pages:[] }
]},
{ id:'employee-app', label:'员工端 APP', icon:'app', color:'#95de64', modules:[
{ name:'AI会话', dir:'employee-app/ai', pages:[] },
{ name:'应用主页', dir:'employee-app/home', entry:'index.html', pages:[
{n:'应用主页(收起态)',f:'index.html'},
] },
{ name:'健康体检', dir:'employee-app/health-checkup', entry:'index.html', pages:[
{ file:'index.html', label:'健康体检主页' },
{ file:'index-inactive.html', label:'健康体检-未开展' },
{ file:'booking-type.html', label:'选择体检类型' },
{ file:'booking-hospital.html', label:'预约-选择医院' },
{ file:'booking-project.html', label:'预约-选择项目' },
{ file:'booking-project-selected.html', label:'预约-已选项目' },
{ file:'booking-date.html', label:'预约-选择日期' },
{ file:'booking-date-selected.html', label:'预约-已选日期' },
{ file:'my-appointments-detail.html', label:'预约详情' },
{ file:'my-appointments-detail-done.html', label:'预约详情-已完成' },
{ file:'my-appointments-detail-cancelled.html', label:'预约详情-已取消' },
{ file:'my-appointments.html', label:'我的预约-待体检' },
{ file:'my-appointments-all.html', label:'我的预约-全部' },
{ file:'my-appointments-done.html', label:'我的预约-已完成' },
{ file:'my-appointments-cancelled.html', label:'我的预约-已取消' },
{ file:'hospital-checkin.html', label:'到院体检' },
{ file:'reports.html', label:'体检报告' },
{ file:'report-query.html', label:'报告查询详情' },
{ file:'project-detail.html', label:'体检项目详情' }
] },
{ name:'体重管理', dir:'employee-app/weight-management', pages:[] },
{ name:'营养管理', dir:'employee-app/nutrition', pages:[] },
{ name:'运动管理', dir:'employee-app/exercise', pages:[] },
{ name:'健康监测', dir:'employee-app/health-monitor', entry:'index.html', pages:[
{ file:'index.html', label:'健康监测首页' },
{ file:'steps.html', label:'步数详情' },
{ file:'sleep.html', label:'睡眠详情' },
{ file:'heart-rate.html', label:'心率详情' },
{ file:'heart-rate-alerts.html', label:'心率预警历史' },
{ file:'temperature.html', label:'体温详情' },
{ file:'temperature-alerts.html', label:'体温预警历史' },
{ file:'blood-oxygen.html', label:'血氧详情' },
{ file:'blood-oxygen-alerts.html', label:'血氧预警历史' },
{ file:'stress.html', label:'压力详情' },
{ file:'stress-alerts.html', label:'压力预警历史' },
{ file:'exercise.html', label:'锻炼详情' }
] },
{ name:'健康评估', dir:'employee-app/health-assessment', pages:[] },
{ name:'心脑血管病预防', dir:'employee-app/cardiovascular', pages:[] },
{ name:'糖尿病预防', dir:'employee-app/diabetes', pages:[] },
{ name:'癌症预防', dir:'employee-app/cancer', pages:[] },
{ name:'健康咨询', dir:'employee-app/health-consult', prd:'employee-app/health-consult/doc/PRD.md', done:true, pages:[
{ file:'index.html', label:'咨询首页' }, { file:'search.html', label:'综合搜索' },
{ file:'find-expert.html', label:'找专家' }, { file:'expert-list.html', label:'专家列表' },
{ file:'expert-profile.html', label:'专家主页' }, { file:'expert-reviews.html', label:'专家评价' },
{ file:'hospital-detail.html', label:'医院详情' },
{ file:'select-person.html', label:'选择咨询人' }, { file:'select-record.html', label:'发送档案' }, { file:'consult-person-detail.html', label:'咨询人档案' },
{ file:'add-health-record.html', label:'新增健康档案' },
{ file:'text-chat.html', label:'图文咨询' }, { file:'text-chat-history.html', label:'图文历史对话' },
{ file:'assistant-chat.html', label:'小助手对话' }, { file:'assistant-chat-history.html', label:'小助手历史对话' },
{ file:'video-booking.html', label:'视频预约' }, { file:'video-success.html', label:'预约成功' },
{ file:'video-detail-confirming.html', label:'视频待确认' }, { file:'video-detail-pending.html', label:'视频待开始' },
{ file:'video-detail-done.html', label:'视频已完成' }, { file:'video-detail-cancelled.html', label:'视频已取消' },
{ file:'video-detail-rejected.html', label:'视频已拒绝' },
{ file:'video-detail-ongoing.html', label:'视频进行中' },
{ file:'my-records.html', label:'咨询记录' },
{ file:'record-detail-rated.html', label:'已评价详情' }, { file:'record-detail-unrated.html', label:'未评价详情' }, { file:'record-detail-timeout.html', label:'已超时详情' },
{ file:'rate-doctor.html', label:'评价医生' },
{ file:'end-consult-dialog.html', label:'结束咨询弹窗', dialog:true },
{ file:'select-report-dialog.html', label:'选择报告弹窗', dialog:true },
{ file:'consult-flowchart.html', label:'咨询流程图' }
]},
{ name:'应急就医', dir:'employee-app/emergency-medical', entry:'index.html', prd:'employee-app/emergency-medical/doc/PRD.md', done:true, pages:[
{ file:'index.html', label:'应急就医主页' },
{ file:'emergency-chat.html', label:'应急对话' },
{ file:'resource-map.html', label:'应急资源地图' },
{ file:'resource-map-clinic.html', label:'地图-医疗点' },
{ file:'resource-map-aed.html', label:'地图-AED' },
{ file:'resource-map-ambulance.html', label:'地图-救护车' },
{ file:'resource-map-kit.html', label:'地图-急救包' },
{ file:'hospital-detail.html', label:'油田医院详情' },
{ file:'clinic-detail.html', label:'医疗点详情' },
{ file:'aed-detail.html', label:'AED详情' },
{ file:'ambulance-detail.html', label:'救护车详情' },
{ file:'first-aid-kit-detail.html', label:'急救包详情' },
{ file:'emergency-tips.html', label:'急救提示书' },
{ file:'help-records.html', label:'应急求助记录' },
{ file:'appointment-list.html', label:'大病就医预约' },
{ file:'appointment-list-confirmed.html', label:'预约-已确认' },
{ file:'appointment-list-done.html', label:'预约-已完成' },
{ file:'appointment-list-cancelled.html', label:'预约-已取消' },
{ file:'appointment-detail-pending.html', label:'就医详情-待确认' },
{ file:'appointment-detail-confirmed.html', label:'就医详情-已确认' },
{ file:'appointment-detail-done.html', label:'就医详情-已完成' },
{ file:'appointment-detail-cancelled.html', label:'就医详情-已取消' },
{ file:'appointment-form.html', label:'发起新预约' },
{ file:'first-aid-videos.html', label:'急救教学视频' }
]},
{ name:'一线医疗', dir:'employee-app/frontline-medical', pages:[] },
{ name:'健康档案', dir:'employee-app/health-record', pages:[] },
{ name:'知识普及', dir:'employee-app/knowledge', pages:[] },
{ name:'个人中心', dir:'employee-app/profile', entry:'index.html', pages:[
{ file:'index.html', label:'我的主页' },
{ file:'emergency-contacts.html', label:'紧急联系人' },
{ file:'personal-info.html', label:'个人资料' },
{ file:'settings.html', label:'设置' },
{ file:'avatar-view.html', label:'查看头像' },
{ file:'avatar-edit.html', label:'查看头像-修改' },
{ file:'change-phone-verify-old.html', label:'修改手机号-原手机号验证' },
{ file:'change-phone-unavailable.html', label:'修改手机号-手机不可用' },
{ file:'change-phone-verify-new.html', label:'修改手机号-新手机号验证' },
{ file:'change-password.html', label:'修改密码' },
{ file:'forgot-password-verify.html', label:'忘记密码-身份验证' },
{ file:'forgot-password-reset.html', label:'忘记密码-重置密码' },
{ file:'phone-list.html', label:'手机号' }
]}
]},
{ id:'emergency-app', label:'健康应急 APP', icon:'emergency', color:'#ffc069',
roles:[
{ role:'咨询专家', id:'expert', modules:[
{ name:'工作台', dir:'emergency-app/expert/workbench', prd:'emergency-app/expert/doc/PRD.md', done:true, pages:[
{file:'index.html',label:'工作台首页'},{file:'text-chat.html',label:'图文咨询'},
{file:'text-chat-done.html',label:'图文咨询完成'},
{file:'text-consult-done.html',label:'咨询完成'},{file:'text-consult-timeout.html',label:'咨询超时'},
{file:'video-appt.html',label:'视频预约'},
{file:'video-ongoing.html',label:'视频待开始'},
{file:'video-in-progress.html',label:'视频进行中'},
{file:'video-done.html',label:'视频完成'},
{file:'video-cancelled.html',label:'视频已取消'},{file:'video-rejected.html',label:'视频已拒绝'}
]},
{ name:'知识库', dir:'emergency-app/expert/knowledge', done:true, pages:[
{file:'index.html',label:'知识库首页'},{file:'detail.html',label:'知识详情'}
]},
{ name:'我的', dir:'emergency-app/expert/profile', done:true, pages:[
{file:'index.html',label:'个人中心'},{file:'edit-profile.html',label:'编辑资料'},
{file:'edit-diseases.html',label:'擅长疾病'},
{file:'change-pwd.html',label:'修改密码'},{file:'schedule.html',label:'排班设置'},
{file:'stats.html',label:'咨询统计'},{file:'reviews.html',label:'我的评价'}
]}
]},
{ role:'咨询小助手', id:'assistant', modules:[
{ name:'工作台', dir:'emergency-app/assistant/workbench', prd:'emergency-app/assistant/doc/PRD.md', done:true, pages:[
{file:'index.html',label:'工作台首页'},{file:'text-chat.html',label:'图文咨询'},
{file:'text-chat-done.html',label:'图文咨询完成'},
{file:'text-consult-done.html',label:'咨询完成'},{file:'text-consult-timeout.html',label:'咨询超时'},
{file:'employee-profile.html',label:'员工档案'},
{file:'end-consult-dialog.html',label:'结束咨询弹窗',dialog:true},
{file:'push-expert.html',label:'推送专家'}
]},
{ name:'知识库', dir:'emergency-app/assistant/knowledge', done:true, pages:[
{file:'index.html',label:'知识库首页'},{file:'detail.html',label:'知识详情'}
]},
{ name:'我的', dir:'emergency-app/assistant/profile', done:true, pages:[
{file:'index.html',label:'个人中心'},{file:'edit-profile.html',label:'编辑资料'},
{file:'change-pwd.html',label:'修改密码'}
] }
]},
{ role:'应急操作人员', id:'operator', modules:[
{name:'工作台',dir:'emergency-app/operator/workbench',prd:'emergency-app/operator/doc/PRD.md',done:true,pages:[
{file:'index.html',label:'工作台首页(应急求助)'},
{file:'serious-illness-list.html',label:'大病就医首页'},
{file:'emergency-chat.html',label:'应急聊天'},
{file:'emergency-chat-done.html',label:'应急聊天(已结束)'},
{file:'patient-location.html',label:'患者定位'},
{file:'dispatch-onsite.html',label:'派单驻场人员'},
{file:'dispatch-ambulance.html',label:'派单救护车'},
{file:'work-order-detail.html',label:'工单详情(已完成)'},
{file:'work-order-detail-ongoing.html',label:'工单详情(进行中)'},
{file:'rescue-summary.html',label:'救助信息'},
{file:'serious-illness-detail-pending.html',label:'大病就医详情(待确认)'},
{file:'serious-illness-detail-confirmed.html',label:'大病就医详情(已确认)'},
{file:'serious-illness-detail-done.html',label:'大病就医详情(已完成)'},
{file:'serious-illness-detail-cancelled.html',label:'大病就医详情(已取消)'}
]},
{name:'知识库',dir:'emergency-app/operator/knowledge',done:true,pages:[
{file:'index.html',label:'知识库首页'},{file:'article-detail.html',label:'知识详情'}
]},
{name:'我的',dir:'emergency-app/operator/profile',done:true,pages:[
{file:'index.html',label:'我的主页'},
{file:'duty-schedule.html',label:'值班信息'},
{file:'duty-config.html',label:'排班配置'},
{file:'edit-profile.html',label:'修改个人信息'},
{file:'change-pwd.html',label:'修改密码'}
]}
]},
{ role:'应急专业人员', id:'professional', modules:[
{ name:'工作台', dir:'emergency-app/professional/workbench', prd:'emergency-app/professional/doc/PRD.md', done:true, pages:[
{file:'index.html',label:'工作台首页'},
{file:'emergency-chat-ongoing.html',label:'应急聊天(进行中)'},
{file:'emergency-chat-done.html',label:'应急聊天(已结束)'},
{file:'rescue-opinion.html',label:'救助意见'},
{file:'patient-location.html',label:'患者定位'},
{file:'work-order-detail.html',label:'工单详情(已完成)'},
{file:'work-order-detail-ongoing.html',label:'工单详情(进行中)'}
]},
{ name:'知识库', dir:'emergency-app/professional/knowledge', done:true, pages:[
{file:'index.html',label:'知识库首页'},{file:'article-detail.html',label:'知识详情'}
]},
{ name:'我的', dir:'emergency-app/professional/profile', done:true, pages:[
{file:'index.html',label:'个人中心'},
{file:'duty-schedule.html',label:'值班信息'},
{file:'edit-profile.html',label:'修改个人信息'},{file:'change-pwd.html',label:'修改密码'}
]}
]},
{ role:'应急驻场人员', id:'onsite', modules:[
{name:'工作台',dir:'emergency-app/onsite/workbench',prd:'emergency-app/onsite/doc/PRD.md',done:true,pages:[
{file:'index.html',label:'工作台首页(应急求助)'},
{file:'serious-illness-list.html',label:'大病就医首页'},
{file:'work-order-detail-ongoing.html',label:'工单详情(待处理)'},
{file:'work-order-detail.html',label:'工单详情(已完成)'},
{file:'serious-illness-detail-pending.html',label:'大病就医详情(待处理)'},
{file:'serious-illness-detail-done.html',label:'大病就医详情(已完成)'},
{file:'serious-illness-detail-cancelled.html',label:'大病就医详情(已取消)'}
]},
{name:'知识库',dir:'emergency-app/onsite/knowledge',done:true,pages:[
{file:'index.html',label:'知识库首页'},{file:'article-detail.html',label:'知识详情'}
]},
{name:'我的',dir:'emergency-app/onsite/profile',done:true,pages:[
{file:'index.html',label:'个人中心'},
{file:'edit-profile.html',label:'修改个人信息'},{file:'change-pwd.html',label:'修改密码'}
]}
]},
{ role:'管理员', id:'admin', modules:[
{name:'工作台',dir:'emergency-app/admin/workbench',pages:[]},
{name:'知识库',dir:'emergency-app/admin/knowledge',pages:[]},
{name:'我的',dir:'emergency-app/admin/profile',pages:[]}
]}
],
shared:[
{ name:'AI助手', dir:'emergency-app/ai-assistant', done:true, pages:[{file:'index.html',label:'AI助手'}] },
{ name:'消息', dir:'emergency-app/message', done:true, pages:[
{file:'index.html',label:'消息列表'},{file:'contacts.html',label:'通讯录'},{file:'im-chat.html',label:'聊天'}
]}
]
}
];
/* ===== 渲染逻辑 ===== */
function countPages(t) {
if (t.modules) return t.modules.reduce((s, m) => s + m.pages.length, 0);
let n = 0;
if (t.roles) t.roles.forEach(r => r.modules.forEach(m => n += m.pages.length));
if (t.shared) t.shared.forEach(m => n += m.pages.length);
return n;
}
/* Tab 导航 */
const tabNav = document.getElementById('tab-nav');
const tabContents = document.getElementById('tab-contents');
const totalTabs = SITE_MAP.length + 1; /* +1 for PRD tab */
SITE_MAP.forEach((t, i) => {
tabNav.innerHTML += `<div class="tab-nav__item${i===0?' tab-nav__item--active':''}" data-idx="${i}">${ICONS[t.icon]}${t.label}<span class="tab-nav__badge">${countPages(t)}</span></div>`;
});
/* PRD Tab */
const prdTabIdx = SITE_MAP.length;
tabNav.innerHTML += `<div class="tab-nav__item" data-idx="${prdTabIdx}">${ICONS.prd}产品需求文档</div>`;
/* 生成卡片 — 点击卡片跳转主页,点击徽章弹出页面列表 */
let openPopover = null;
function cardHTML(mod, uid) {
const has = mod.pages.length > 0;
const isDone = has && mod.done;
const isWip = has && !mod.done;
const cls = isDone ? 'module-card module-card--ready' : isWip ? 'module-card module-card--wip' : 'module-card module-card--empty';
const mainHref = has ? `${mod.dir}/${mod.entry || 'index.html'}` : '';
const onclick = has ? `onclick="window.open('${mainHref}','_blank')"` : '';
/* 右上角状态标签 */
let statusHtml = '';
if (isDone) statusHtml = '<span class="module-card__status module-card__status--done">已完成</span>';
else if (isWip) statusHtml = '<span class="module-card__status module-card__status--wip">设计中</span>';
else statusHtml = '<span class="module-card__status module-card__status--pending">待设计</span>';
/* 圆形水印(仅设计中) */
let watermarkHtml = '';
if (isWip) watermarkHtml = '<div class="module-card__watermark"><span class="module-card__watermark-text">暂未<br>定稿</span></div>';
/* 底部行:左PRD + 右页数 */
let prdLink = '';
if (mod.prd) {
prdLink = `<a class="module-card__prd" onclick="event.stopPropagation();openMdPreview('${mod.prd}','${mod.name} PRD')">${ICONS.prdSmall}PRD文档</a>`;
}
let pageCountHtml = '';
if (has) {
let popItems = mod.pages.map(p => {
const dcls = p.dialog ? ' page-popover__item--dialog' : '';
const icon = p.dialog ? ICONS.dialog : ICONS.page;
return `<a class="page-popover__item${dcls}" href="${mod.dir}/${p.file}" target="_blank">${icon}${p.label}</a>`;
}).join('');
pageCountHtml = `<span class="module-card__page-count" data-pop="${uid}" onclick="event.stopPropagation();togglePop('${uid}')">${mod.pages.length} 页</span><div class="page-popover" id="pop-${uid}" onclick="event.stopPropagation()"><div class="page-popover__title">页面列表</div>${popItems}</div>`;
}
/* 底部行占位:无内容时保持高度一致 */
const bottomPlaceholder = (!prdLink && !pageCountHtml) ? '<span>&nbsp;</span>' : '';
return `<div class="${cls}" ${onclick}>${watermarkHtml}<div class="module-card__header"><span class="module-card__name">${mod.name}</span>${statusHtml}</div><div class="module-card__bottom">${prdLink}${pageCountHtml}${bottomPlaceholder}</div></div>`;
}
function togglePop(uid) {
const el = document.getElementById('pop-' + uid);
if (openPopover && openPopover !== el) {
openPopover.classList.remove('page-popover--visible');
openPopover.closest('.module-card').classList.remove('module-card--pop-open');
}
el.classList.toggle('page-popover--visible');
const card = el.closest('.module-card');
if (el.classList.contains('page-popover--visible')) {
card.classList.add('module-card--pop-open');
openPopover = el;
} else {
card.classList.remove('module-card--pop-open');
openPopover = null;
}
}
document.addEventListener('click', () => {
if (openPopover) {
openPopover.closest('.module-card').classList.remove('module-card--pop-open');
openPopover.classList.remove('page-popover--visible');
openPopover = null;
}
});
/* 生成 Tab 内容 */
let uidCounter = 0;
SITE_MAP.forEach((t, i) => {
const div = document.createElement('div');
div.className = 'tab-content' + (i === 0 ? ' tab-content--active' : '');
div.dataset.idx = i;
let html = '';
if (t.modules) {
html = '<div class="module-grid">' + t.modules.map(m => cardHTML(m, 'u' + uidCounter++)).join('') + '</div>';
} else {
t.roles.forEach(r => {
const icon = ICONS.role[r.id] || '';
html += `<div class="role-group"><div class="role-group__title"><span class="role-icon role-icon--${r.id}">${icon}</span>${r.role}</div><div class="module-grid module-grid--center">`;
html += r.modules.map(m => cardHTML(m, 'u' + uidCounter++)).join('');
html += '</div></div>';
});
html += `<div class="role-group"><div class="role-group__title"><span class="role-icon role-icon--shared">${ICONS.role.shared}</span>全角色共享</div><div class="module-grid module-grid--center">`;
html += t.shared.map(m => cardHTML(m, 'u' + uidCounter++)).join('');
html += '</div></div>';
}
div.innerHTML = html;
tabContents.appendChild(div);
});
/* Tab 切换 */
tabNav.addEventListener('click', e => {
const item = e.target.closest('.tab-nav__item');
if (!item) return;
const idx = item.dataset.idx;
tabNav.querySelectorAll('.tab-nav__item').forEach(el => el.classList.remove('tab-nav__item--active'));
tabContents.querySelectorAll('.tab-content').forEach(el => el.classList.remove('tab-content--active'));
item.classList.add('tab-nav__item--active');
tabContents.querySelector(`[data-idx="${idx}"]`).classList.add('tab-content--active');
});
/* ===== PRD Tab 内容生成 ===== */
(function buildPrdTab() {
const div = document.createElement('div');
div.className = 'tab-content';
div.dataset.idx = prdTabIdx;
let html = '';
/* 顶部:共性需求说明 */
html += `<div class="prd-page__global">
<div class="prd-global-card" onclick="openMdPreview('共性需求说明.md','共性需求说明')">
<div class="prd-global-card__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M6 2h9l5 5v15H6V2z" stroke="#fff" stroke-width="1.8"/><path d="M15 2v5h5" stroke="#fff" stroke-width="1.8"/><line x1="9" y1="11" x2="17" y2="11" stroke="#fff" stroke-width="1.4" stroke-linecap="round"/><line x1="9" y1="14.5" x2="15" y2="14.5" stroke="#fff" stroke-width="1.4" stroke-linecap="round"/><line x1="9" y1="18" x2="13" y2="18" stroke="#fff" stroke-width="1.4" stroke-linecap="round"/></svg>
</div>
<div class="prd-global-card__info">
<div class="prd-global-card__title">共性需求说明</div>
<div class="prd-global-card__desc">全平台通用的业务规则、交互规范和共性功能说明</div>
</div>
<span class="prd-item__status prd-item__status--ready">查看</span>
</div>
</div>`;
/* 收集所有模块的 PRD 信息 */
function collectModules(siteEntry) {
const result = [];
if (siteEntry.modules) {
siteEntry.modules.forEach(m => {
result.push({ name: m.name, dir: m.dir, prd: m.prd || null });
});
}
if (siteEntry.roles) {
siteEntry.roles.forEach(r => {
r.modules.forEach(m => {
result.push({ name: r.role + ' - ' + m.name, dir: m.dir, prd: m.prd || null });
});
});
}
if (siteEntry.shared) {
siteEntry.shared.forEach(m => {
result.push({ name: '共享 - ' + m.name, dir: m.dir, prd: m.prd || null });
});
}
return result;
}
SITE_MAP.forEach(t => {
const mods = collectModules(t);
const readyCount = mods.filter(m => m.prd).length;
html += `<div class="prd-section">
<div class="prd-section__title">${ICONS[t.icon] || ''}${t.label}<span class="tab-nav__badge" style="margin-left:6px">${readyCount}/${mods.length}</span></div>
<div class="prd-grid">`;
mods.forEach(m => {
if (m.prd) {
html += `<div class="prd-item" onclick="openMdPreview('${m.prd}','${m.name} PRD')">
<div class="prd-item__icon prd-item__icon--ready">📄</div>
<div class="prd-item__info">
<div class="prd-item__name">${m.name} PRD</div>
<div class="prd-item__path">${m.prd}</div>
</div>
<span class="prd-item__status prd-item__status--ready">查看</span>
</div>`;
} else {
html += `<div class="prd-item prd-item--disabled">
<div class="prd-item__icon prd-item__icon--empty">📄</div>
<div class="prd-item__info">
<div class="prd-item__name">${m.name} PRD</div>
<div class="prd-item__path">${m.dir}/doc/PRD.md</div>
</div>
<span class="prd-item__status prd-item__status--empty">待编写</span>
</div>`;
}
});
html += '</div></div>';
});
div.innerHTML = html;
tabContents.appendChild(div);
})();
/* ===== Markdown 预览(新标签页打开) ===== */
function openMdPreview(filePath, title) {
window.open('md-viewer.html?file=' + encodeURIComponent(filePath) + '&title=' + encodeURIComponent(title || ''), '_blank');
}
</script>
</body>
</html>