Files
platform-prototype/index.html
T
fengpuandClaude Opus 4.7 6a505f2d84 feat: 批量充值抽离独立页面 + 餐品档案定价区改造 + 账户交互优化
- din-user-recharge.html 新增(从 din-user-mgmt.html 弹窗抽离,含筛选/候选表/配置区/汇总栏)
- din-user-mgmt.html 清理批量充值弹窗 CSS/JS/HTML,「批量充值」按钮改为跳转
- din-dish-add/all/own.html 定价区改造 + 成本核算按钮修复 + 营养指标精简
- din-user-account.html 支付方式精简为微信/现金/支付宝
- me-account.html 姓名行增加可编辑态
- index.html SITE_MAP 新增「批量充值」入口
- CHANGELOG 同步记录

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-04 14:35:26 +08:00

1201 lines
80 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>健康CQ升级 - 高保真交互原型导航</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(320px, 1fr)); gap: 20px; 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); }
.role-icon--miniprogram { background: linear-gradient(135deg, #52c41a, #95de64); }
.role-icon--screen { background: linear-gradient(135deg, #722ED1, #b37feb); }
.role-icon--pad { background: linear-gradient(135deg, #1677FF, #69b1ff); }
/* ===== 呼吸灯动画 ===== */
@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">健康CQ升级项目 · 高保真交互原型</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>',
other: '<svg class="tab-nav__icon" viewBox="0 0 18 18" fill="none"><rect x="1" y="3" width="7" height="5" rx="1" stroke="currentColor" stroke-width="1.3"/><rect x="10" y="3" width="7" height="5" rx="1" stroke="currentColor" stroke-width="1.3"/><rect x="1" y="10" width="7" height="5" rx="1" stroke="currentColor" stroke-width="1.3"/><rect x="10" y="10" width="7" height="5" rx="1" stroke="currentColor" stroke-width="1.3"/></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:'🔗', miniprogram:'📱', screen:'🖥', pad:'📋' }
};
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', entry:'archive-personal-weight-data.html', prd:'web-admin/weight-management/doc/PRD.md', done:true, pages:[
{ file:'archive-personal-weight-data.html', label:'个人体重数据' },
{ file:'archive-personal-weight-detail.html', label:'个人体重数据详情' },
{ file:'archive-unit-report.html', label:'单位报表数据' },
{ file:'archive-algorithm-model.html', label:'算法模型' },
{ file:'plan-list.html', label:'计划列表' },
{ file:'plan-edit.html', label:'新增/编辑计划' },
{ file:'registration-tracking.html', label:'报名跟踪' },
{ file:'target-weight-review.html', label:'目标体重审核' },
{ file:'action-record.html', label:'行动记录' },
{ file:'action-overview.html', label:'活动总览' },
{ file:'action-overview-detail.html', label:'赛况明细' },
{ file:'weight-ranking.html', label:'体重排名' },
{ file:'activity-statistics.html', label:'活动统计' },
{ file:'history-plan-list.html', label:'历史干预-计划列表' },
{ file:'history-plan-detail.html', label:'历史干预-计划详情' },
{ file:'personal-target-data.html', label:'个人目标数据' },
{ file:'personal-target-detail.html', label:'个人目标数据-详情' },
{ file:'scale-device.html', label:'体重秤-设备管理' },
{ file:'scale-monitor.html', label:'体重秤-设备监测' },
{ file:'scale-device-add.html', label:'体重秤-新增设备' },
{ file:'scale-monitor-detail.html', label:'体重秤-数据明细' },
{ file:'bluetooth-device.html', label:'蓝牙称-设备管理' },
{ file:'bluetooth-monitor.html', label:'蓝牙称-设备监测' },
{ file:'bluetooth-device-add.html', label:'蓝牙称-新增设备' },
{ file:'bluetooth-monitor-detail.html', label:'蓝牙称-数据明细' },
{ file:'fat-device.html', label:'体脂仪-设备管理' },
{ file:'fat-monitor.html', label:'体脂仪-设备监测' },
{ file:'fat-device-add.html', label:'体脂仪-新增设备' },
{ file:'fat-monitor-detail.html', label:'体脂仪-数据明细' },
{ file:'sport-device.html', label:'运动器械-设备管理' },
{ file:'sport-monitor.html', label:'运动器械-设备监测' },
{ file:'sport-device-add.html', label:'运动器械-新增设备' },
{ file:'sport-monitor-detail.html', label:'运动器械-数据明细' },
{ file:'exception-system-dirty.html', label:'系统脏数据' },
{ file:'exception-result-abnormal.html', label:'结果异常' },
{ file:'dept-stat.html', label:'单位数据统计' },
{ file:'employee-data.html', label:'员工监测数据' },
{ file:'employee-data-record.html', label:'员工监测数据记录' },
{ file:'employee-stat.html', label:'员工数据统计' }
]},
{ name:'营养管理', dir:'web-admin/nutrition', entry:'trace-query.html', pages:[
{ file:'trace-query.html', label:'食材溯源' },
{ file:'trace-timeline-ingredient.html', label:'食材链路' },
{ file:'trace-dish.html', label:'餐品溯源' },
{ file:'trace-timeline-dish.html', label:'餐品链路' },
{ file:'arc-employee.html', label:'员工营养数据' },
{ file:'arc-employee-detail.html', label:'员工营养详情' },
{ file:'arc-unit.html', label:'单位报表数据' },
{ file:'arc-canteen.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:'sup-biz-analysis.html', label:'加工经营分析' },
{ file:'sup-process-analysis.html', label:'加工分析' },
{ file:'din-user-mgmt.html', label:'用户管理' },
{ file:'din-user-recharge.html', label:'批量充值' },
{ file:'din-dish-all.html', label:'菜品总库' },
{ file:'din-dish-own.html', label:'自有餐品库' },
{ file:'din-dish-all-dialog.html', label:'菜品总库弹窗', dialog:true },
{ file:'din-dish-add.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-content-center.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-canteen.html', label:'食堂管理' },
{ file:'din-canteen-add.html', label:'新增食堂' },
{ file:'din-canteen-line.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:'din-biz-analysis.html', label:'经营分析' },
{ file:'din-meal-analysis.html', label:'餐品分析' },
{ file:'prod-recipe-plan.html', label:'食谱排期' },
{ 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:'prod-qc-nutrition-add.html', label:'营养质检-新增' },
{ file:'prod-qc-nutrition-sync.html', label:'营养质检-同步记录' },
{ file:'ano-dirty.html', label:'系统脏数据' },
{ file:'ano-result.html', label:'结果异常' },
{ file:'edge-server-management.html', label:'边缘服务器管理' },
{ file:'edge-server-detail.html', label:'边缘服务器详情' },
{ file:'edge-report-management.html', label:'数据上报管理' },
{ file:'edge-dispatch-management.html', label:'数据下发管理' },
{ file:'edge-dispatch-detail.html', label:'数据下发详情' },
{ file:'edge-alert-center.html', label:'异常告警中心' }
]},
{ name:'运动管理', dir:'web-admin/exercise', pages:[] },
{ name:'健康监测', dir:'web-admin/health-monitor', entry:'archive-personal.html', prd:'web-admin/health-monitor/doc/PRD.md', done:true, pages:[
{ file:'archive-personal.html', label:'个人监测数据' },
{ file:'archive-personal-detail.html', label:'个人监测数据详情' },
{ file:'archive-unit.html', label:'单位报表数据' },
{ file:'archive-unit-crowd-status-detail.html', label:'单位报表-健康现状详情' },
{ file:'archive-unit-health-checkup-detail.html', label:'单位报表-健康排查详情' },
{ file:'archive-unit-wearable-detail.html', label:'单位报表-穿戴监测详情' },
{ file:'archive-unit-environment-detail.html', label:'单位报表-环境监测详情' },
{ file:'archive-algorithm.html', label:'算法模型' },
{ file:'crowd-status-employee.html', label:'健康现状-员工评估信息' },
{ file:'crowd-status-unit.html', label:'健康现状-单位评估统计' },
{ file:'health-checkup/survey-category.html', label:'排查问卷-问题分类' },
{ file:'health-checkup/survey-manage.html', label:'问卷管理' },
{ file:'health-checkup/survey-edit.html', label:'新增/编辑问卷', dialog:true },
{ file:'health-checkup/activity-manage.html', label:'活动管理' },
{ file:'health-checkup/activity-edit.html', label:'新增/编辑活动', dialog:true },
{ file:'health-checkup/activity-detail.html', label:'活动详情', dialog:true },
{ file:'wearable/tool-manage.html', label:'穿戴监测-工具管理' },
{ file:'wearable/bind-record.html', label:'穿戴监测-绑定记录' },
{ file:'wearable/user-data.html', label:'穿戴监测-用户数据' },
{ file:'wearable/exception-event.html', label:'穿戴监测-异常事件' },
{ file:'wearable/notification.html', label:'穿戴监测-通知消息' },
{ file:'wearable/dept-stat.html', label:'穿戴监测-部门数据统计' },
{ file:'wearable/personal-stat.html', label:'穿戴监测-个人数据统计' },
/* 运维菜单:隐藏,恢复时删除注释 */
/* { file:'wearable/dept-report.html', label:'穿戴监测-单位数据分析报告' },
{ file:'wearable/ops/terminal-default-config.html', label:'穿戴监测-运维中心-默认配置' },
{ file:'wearable/ops/terminal-config-info.html', label:'穿戴监测-运维中心-配置信息' },
{ file:'wearable/ops/monitor-ecg.html', label:'穿戴监测-运维中心-心电图信息' },
{ file:'wearable/ops/monitor-spo2.html', label:'穿戴监测-运维中心-血氧信息' },
{ file:'wearable/ops/monitor-temp.html', label:'穿戴监测-运维中心-体温信息' },
{ file:'wearable/ops/monitor-stress.html', label:'穿戴监测-运维中心-压力信息' },
{ file:'wearable/ops/monitor-exercise.html', label:'穿戴监测-运维中心-锻炼信息' },
{ file:'wearable/ops/monitor-heartrate.html', label:'穿戴监测-运维中心-心率信息' },
{ file:'wearable/ops/monitor-sleep.html', label:'穿戴监测-运维中心-睡眠信息' },
{ file:'wearable/ops/monitor-sleep-watch5.html', label:'穿戴监测-运维中心-睡眠信息(watch5)' },
{ file:'wearable/ops/monitor-activity.html', label:'穿戴监测-运维中心-活动信息' },
{ file:'wearable/ops/alarm-data.html', label:'穿戴监测-运维中心-事件监听' },
{ file:'wearable/ops/log-network.html', label:'穿戴监测-运维中心-手表网络切换日志' },
{ file:'wearable/ops/log-version.html', label:'穿戴监测-运维中心-版本更新历史' },
{ file:'wearable/ops/log-error.html', label:'穿戴监测-运维中心-手表错误日志' },
{ file:'wearable/ops/log-task.html', label:'穿戴监测-运维中心-任务日志' },
{ file:'wearable/ops/log-mqtt.html', label:'穿戴监测-运维中心-mqtt消息日志' },
{ file:'wearable/ops/sdc-overview.html', label:'穿戴监测-运维中心-SDC信息' }, */
{ file:'environment/indoor-device-list.html', label:'室内空气-设备列表' },
{ file:'environment/indoor-device-category.html', label:'室内空气-设备分类' },
{ file:'environment/indoor-data-monitor.html', label:'室内空气-数据监测' },
{ file:'environment/indoor-data-analysis.html', label:'室内空气-数据分析' },
{ file:'environment/indoor-exception-alarm.html', label:'室内空气-异常报警' },
{ file:'environment/indoor-app-record.html', label:'室内空气-应用记录' },
{ file:'environment/indoor-history-data.html', label:'室内空气-历史数据' },
{ file:'environment/indoor-history-monitor-detail.html', label:'室内空气-历史监测详情' },
{ file:'environment/indoor-history-alarm-detail.html', label:'室内空气-历史报警详情' },
{ file:'environment/outdoor-device-list.html', label:'室外空气-设备列表' },
{ file:'environment/outdoor-device-category.html', label:'室外空气-设备分类' },
{ file:'environment/outdoor-data-monitor.html', label:'室外空气-数据监测' },
{ file:'environment/outdoor-data-analysis.html', label:'室外空气-数据分析' },
{ file:'environment/outdoor-exception-alarm.html', label:'室外空气-异常报警' },
{ file:'environment/outdoor-app-record.html', label:'室外空气-应用记录' },
{ file:'environment/outdoor-history-data.html', label:'室外空气-历史数据' },
{ file:'environment/outdoor-history-monitor-detail.html', label:'室外空气-历史监测详情' },
{ file:'environment/outdoor-history-alarm-detail.html', label:'室外空气-历史报警详情' },
{ file:'environment/water-device-list.html', label:'水质监测-设备列表' },
{ file:'environment/water-device-category.html', label:'水质监测-设备分类' },
{ file:'environment/water-data-monitor.html', label:'水质监测-数据监测' },
{ file:'environment/water-data-analysis.html', label:'水质监测-数据分析' },
{ file:'environment/water-exception-alarm.html', label:'水质监测-异常报警' },
{ file:'environment/water-app-record.html', label:'水质监测-应用记录' },
{ file:'environment/water-history-data.html', label:'水质监测-历史数据' },
{ file:'environment/water-history-monitor-detail.html', label:'水质监测-历史监测详情' },
{ file:'environment/water-history-alarm-detail.html', label:'水质监测-历史报警详情' },
{ file:'environment/pollen-device-list.html', label:'花粉监测-设备列表' },
{ file:'environment/pollen-device-category.html', label:'花粉监测-设备分类' },
{ file:'environment/pollen-data-monitor.html', label:'花粉监测-数据监测' },
{ file:'environment/pollen-data-analysis.html', label:'花粉监测-数据分析' },
{ file:'environment/pollen-exception-alarm.html', label:'花粉监测-异常报警' },
{ file:'environment/pollen-app-record.html', label:'花粉监测-应用记录' },
{ file:'environment/pollen-history-data.html', label:'花粉监测-历史数据' },
{ file:'environment/pollen-history-monitor-detail.html', label:'花粉监测-历史监测详情' },
{ file:'environment/pollen-history-alarm-detail.html', label:'花粉监测-历史报警详情' },
{ file:'exception-dirty.html', label:'异常事件-系统脏数据' },
{ file:'exception-result.html', label:'异常事件-结果异常' }
]},
{ 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:'report-gastro.html', label:'胃肠镜报告详情' },
{ file:'project-detail.html', label:'体检项目详情' }
] },
{ name:'体重管理', dir:'employee-app/weight-management', entry:'weight-main-v2.html', prd:'employee-app/weight-management/doc/PRD.md', done:true, pages:[
{ file:'doc/design-overview.html', label:'页面总览' },
{ file:'weight-main.html', label:'体重管理主页' },
{ file:'weight-ranking.html', label:'排行榜' },
{ file:'eat-checkin.html', label:'吃打卡' },
{ file:'eat-history.html', label:'摄入记录历史' },
{ file:'goal-commitment.html', label:'目标承诺' },
{ file:'basic-info.html', label:'基本信息' },
{ file:'indicators-diseases.html', label:'指标慢病' },
{ file:'goal-management.html', label:'目标管理' },
{ file:'activity-detail.html', label:'活动详情' },
{ file:'contraindications.html', label:'禁忌证' },
{ file:'promise-signing.html', label:'承诺签订' },
{ file:'my-promise.html', label:'我的承诺书' },
{ file:'balance-history.html', label:'吃动平衡历史' },
{ file:'weight-history.html', label:'体重记录历史' },
{ file:'weight-report.html', label:'体重报告' },
{ file:'diet-advice.html', label:'膳食建议' },
{ file:'exercise-advice.html', label:'运动建议' },
{ file:'exercise-checkin.html', label:'动打卡' },
{ file:'exercise-history.html', label:'消耗记录' },
{ file:'weight-checkin.html', label:'重打卡' }
] },
{ name:'营养管理', dir:'employee-app/nutrition', pages:[] },
{ name:'运动管理', dir:'employee-app/exercise', pages:[] },
{ name:'健康监测', dir:'employee-app/health-monitor', entry:'index.html', prd:'employee-app/health-monitor/doc/PRD.md', done:true, pages:[
{ file:'doc/design-overview.html', label:'页面总览' },
{ file:'index.html', label:'健康监测主页' },
{ file:'health-status.html', label:'健康现状' },
{ file:'wearable/index.html', label:'穿戴监测首页' },
{ file:'wearable/steps.html', label:'步数详情' },
{ file:'wearable/sleep.html', label:'睡眠详情' },
{ file:'wearable/heart-rate.html', label:'心率详情' },
{ file:'wearable/heart-rate-alerts.html', label:'心率预警历史' },
{ file:'wearable/blood-oxygen.html', label:'血氧详情' },
{ file:'wearable/blood-oxygen-alerts.html', label:'血氧预警历史' },
{ file:'wearable/blood-pressure.html', label:'血压详情' },
{ file:'wearable/blood-pressure-alerts.html', label:'血压预警历史' },
{ file:'wearable/temperature.html', label:'体温详情' },
{ file:'wearable/temperature-alerts.html', label:'体温预警历史' },
{ file:'wearable/stress.html', label:'压力详情' },
{ file:'wearable/stress-alerts.html', label:'压力预警历史' },
{ file:'wearable/ecg.html', label:'心电图列表' },
{ file:'wearable/ecg-detail.html', label:'心电图详情' },
{ file:'wearable/exercise.html', label:'锻炼记录' },
{ file:'health-checkup/index.html', label:'健康排查活动列表' },
{ file:'health-checkup/activity-detail.html', label:'活动详情' },
{ file:'health-checkup/activity-fill.html', label:'问卷填写' },
{ file:'health-checkup/questionnaire-fill.html', label:'问卷详情查看' },
{ file:'environment/index.html', label:'环境监测首页' },
{ file:'environment/device-detail.html', label:'环境监测-设备详情' },
{ file:'environment/alarm-data.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:'doc/design-overview.html', label:'页面总览' },
{ 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:'../doc/design-overview.html',label:'页面总览'},
{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:'../doc/design-overview.html',label:'页面总览'},
{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:'../doc/design-overview.html',label:'页面总览'},
{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',prd:'emergency-app/admin/doc/PRD.md',done:true,pages:[
{file:'../doc/design-overview.html',label:'页面总览'},{file:'index.html',label:'工作台首页'},{file:'checkup-progress.html',label:'体检进度跟踪'},{file:'emergency-orders.html',label:'应急求助工单'},{file:'emergency-order-detail.html',label:'应急工单详情'},{file:'wearable-alerts.html',label:'穿戴预警记录'},{file:'weight-detail.html',label:'体重管理详情'},{file:'goal-review.html',label:'目标审核'},{file:'eat-detail.html',label:'饮食记录详情'},{file:'diet-employee-detail.html',label:'饮食明细'},{file:'exercise-detail.html',label:'运动记录详情'},{file:'exercise-employee-detail.html',label:'运动明细'},{file:'weight-record.html',label:'体重记录详情'},{file:'weight-employee-detail.html',label:'体重明细'}
]},
{name:'知识库',dir:'emergency-app/admin/knowledge',done:true,pages:[
{file:'index.html',label:'知识库首页'},{file:'detail.html',label:'知识详情'}
]},
{name:'我的',dir:'emergency-app/admin/profile',done:true,pages:[
{file:'index.html',label:'个人中心'},
{file:'edit-profile.html',label:'修改个人信息'},{file:'change-pwd.html',label:'修改密码'}
]}
]}
],
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:'聊天'}
]}
]
},
{ id:'other-terminals', label:'其他终端', icon:'other', color:'#b37feb', modules:[
{ name:'营养管理小程序', dir:'other-module/mini-program/nutrition', entry:'eat-home.html', pages:[
{label:'吃-首页', file:'eat-home.html'},
{label:'自记餐品', file:'eat-self-record.html'},
{label:'营养明细', file:'eat-nutrition.html'},
{label:'今日食谱', file:'eat-recipe.html'},
{label:'今日食谱展示', file:'eat-recipe-today.html'},
{label:'菜品详情', file:'eat-dish-detail.html'},
{label:'订餐', file:'eat-order.html'},
{label:'订单确认', file:'eat-order-confirm.html'},
{label:'购物车', file:'eat-cart.html'},
{label:'就餐记录', file:'eat-meal-record.html'},
{label:'AI营养助手', file:'ask-home.html'},
{label:'AI对话', file:'ask-chat.html'},
{label:'历史对话', file:'ask-history.html'},
{label:'做-社区', file:'make-home.html'},
{label:'照做食谱', file:'make-follow.html'},
{label:'创作食谱', file:'make-create.html'},
{label:'创作排行', file:'make-ranking.html'},
{label:'分享赚钱', file:'make-share.html'},
{label:'热门创作', file:'make-popular.html'},
{label:'我的创作', file:'make-my-recipes.html'},
{label:'微信授权登录', file:'me-login.html'},
{label:'个人中心', file:'me-home.html'},
{label:'收货地址', file:'me-address.html'},
{label:'健康档案', file:'me-health.html'},
{label:'我的订单', file:'me-orders.html'},
{label:'订单详情', file:'me-order-detail.html'},
{label:'订单详情-待处理', file:'me-order-detail-pending.html'},
{label:'我的收藏', file:'me-favorites.html'},
{label:'我的余额', file:'me-wallet.html'},
{label:'会员中心', file:'me-vip.html'},
{label:'消息中心', file:'me-messages.html'},
{label:'操作日志', file:'me-logs.html'},
{label:'设置', file:'me-settings.html'},
{label:'账户管理', file:'me-account.html'},
{label:'扫码溯源', file:'trace-query.html'},
{label:'溯源链路', file:'trace-detail.html'}
] },
{ name:'西安应急中心大屏', dir:'other-module/emergency-screen/xian-center', entry:'index-v3.html', pages:[
{label:'应急中心大屏', file:'index-v3.html'}
] },
{ name:'标准版中心大屏', dir:'other-module/emergency-screen/standard-center', pages:[] },
{ name:'九厂应急中心大屏', dir:'other-module/emergency-screen/jiuchang-center', pages:[] },
{ name:'医疗点PAD端', dir:'other-module/medical-pad', pages:[] },
{ name:'餐厅终端设备', dir:'other-module/canteen-device', done:true, pages:[
{label:'餐线营养秤 · 模块导航', file:'canteen-nutrition-scale/index.html'},
{label:'餐线营养秤 · 开餐界面', file:'canteen-nutrition-scale/01-meal-start.html'},
{label:'餐线营养秤 · 餐品展示', file:'canteen-nutrition-scale/02-dish-display.html'},
{label:'餐线营养秤 · 取餐(单屏)', file:'canteen-nutrition-scale/03-meal-pickup.html'},
{label:'餐线营养秤 · 取餐(双屏·大屏)', file:'canteen-nutrition-scale/03-meal-pickup-dual-large.html'},
{label:'餐线营养秤 · 取餐(双屏·小屏)', file:'canteen-nutrition-scale/03-meal-pickup-dual-small.html'},
{label:'餐线营养秤 · 设置', file:'canteen-nutrition-scale/04-settings.html'},
{label:'餐线营养秤 · 餐品采样', file:'canteen-nutrition-scale/05-dish-sampling.html'},
{label:'餐线营养秤 · 采样历史', file:'canteen-nutrition-scale/06-sampling-history.html'},
{label:'净菜包装贴标秤 · 模块导航', file:'clean-veg-packaging-labeler/index.html'},
{label:'净菜包装贴标秤 · 员工登录', file:'clean-veg-packaging-labeler/00-login.html'},
{label:'净菜包装贴标秤 · 包装任务', file:'clean-veg-packaging-labeler/01-orders.html'},
{label:'净菜包装贴标秤 · 包装作业', file:'clean-veg-packaging-labeler/02-labeling.html'},
{label:'净菜包装贴标秤 · 包装历史', file:'clean-veg-packaging-labeler/03-history.html'},
{label:'净菜包装贴标秤 · 标签预览', file:'clean-veg-packaging-labeler/label-preview.html'},
{label:'LED餐品展示屏', file:'led-food-display/index.html'}
] },
{ name:'蓝牙称小程序', dir:'other-module/bluetooth-scale-miniprogram', entry:'index.html', prd:'other-module/bluetooth-scale-miniprogram/doc/PRD.md', done:true, pages:[
{ file:'doc/design-overview.html', label:'页面总览' },
{label:'登录页', file:'index.html'},
{label:'完善个人信息', file:'profile-complete.html'},
{label:'主页(四Tab', file:'main.html'},
{label:'搜索设备', file:'search-device.html'},
{label:'WiFi连接', file:'wifi-connect.html'},
{label:'成员管理', file:'device-members.html'},
{label:'添加成员', file:'add-member.html'},
{label:'数据Tab', file:'main.html#tab-data'},
{label:'详细报告', file:'data-detail.html'},
{label:'个人信息', file:'profile.html'},
{label:'帮助中心', file:'help-center.html'},
{label:'帮助详情', file:'help-detail.html'}
] }
]}
];
const PROJECT_DOCS = [
{
title: '缓存中转服务器功能架构与数据流转方案',
file: 'web-admin/nutrition/doc/缓存中转服务器功能架构与数据流转方案.md',
desc: '营养管理服务云端主服务与食堂边缘节点服务的架构、数据流转和接口方案。'
},
{
title: '缓存中转服务器产品需求说明书',
file: 'web-admin/nutrition/doc/缓存中转服务器产品需求说明书.md',
desc: '缓存中转服务器的业务目标、功能范围、页面与交互需求说明。'
},
{
title: '异常事件数据质量控制规则说明书',
file: 'doc/数据质量筛查规则说明书.md',
desc: '定义平台各业务板块系统脏数据的统一质量校验规则,含 6 个板块共 188 条检测规则。'
}
];
/* ===== 渲染逻辑 ===== */
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>`;
/* 生成卡片 — 点击卡片跳转主页,点击徽章弹出页面列表(popover 延迟渲染) */
let openPopover = null;
const pageDataMap = {};
function cardHTML(mod, uid) {
const has = mod.pages.length > 0;
const isPending = !!mod.pending;
const isDone = has && mod.done && !isPending;
const isWip = has && !mod.done && !isPending;
const cls = isPending ? 'module-card module-card--empty' : isDone ? 'module-card module-card--ready' : isWip ? 'module-card module-card--wip' : 'module-card module-card--empty';
const mainHref = has && !isPending ? `${mod.dir}/${mod.entry || 'index.html'}` : '';
const onclick = has && !isPending ? `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 && !isPending) {
/* 存储页面数据供延迟渲染 popover(不生成隐藏 DOM,大幅减少初始化开销) */
pageDataMap[uid] = { dir: mod.dir, pages: mod.pages };
pageCountHtml = `<span class="module-card__page-count" data-pop="${uid}" onclick="togglePop('${uid}',event)">${mod.pages.length} 页</span>`;
}
/* 底部行占位:无内容时保持高度一致 */
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, e) {
if (e) { e.stopPropagation(); e.preventDefault(); }
let el = document.getElementById('pop-' + uid);
/* 延迟构建 popover DOM(首次点击时才创建) */
if (!el) {
const data = pageDataMap[uid];
if (!data) return;
const popItems = data.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="${data.dir}/${p.file}" target="_blank">${icon}${p.label}</a>`;
}).join('');
const countEl = document.querySelector('[data-pop="' + uid + '"]');
if (countEl) {
countEl.insertAdjacentHTML('afterend', '<div class="page-popover" id="pop-' + uid + '" onclick="event.stopPropagation()"><div class="page-popover__title">页面列表</div>' + popItems + '</div>');
el = document.getElementById('pop-' + uid);
}
}
if (!el) return;
if (openPopover && openPopover !== el) {
openPopover.classList.remove('page-popover--visible');
var prevCard = openPopover.closest('.module-card');
if (prevCard) prevCard.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')) {
if (card) card.classList.add('module-card--pop-open');
openPopover = el;
} else {
if (card) card.classList.remove('module-card--pop-open');
openPopover = null;
}
}
/* 点击页面任意空白处关闭 popover(不依赖 stopPropagation */
document.addEventListener('click', function(e) {
if (!openPopover) return;
/* 点击在 popover 内部 或 页数徽章上 → 不关闭 */
if (e.target.closest('.page-popover')) return;
if (e.target.closest('.module-card__page-count')) return;
/* 其他情况 → 关闭 */
var card = openPopover.closest('.module-card');
if (card) card.classList.remove('module-card--pop-open');
openPopover.classList.remove('page-popover--visible');
openPopover = null;
});
/* 生成 Tab 内容(延迟渲染:首次只构建当前 tab,其余按需创建) */
const renderedTabs = {};
let uidCounter = 0;
function ensureTabRendered(idx) {
if (renderedTabs[idx]) return renderedTabs[idx];
if (idx >= SITE_MAP.length) return null;
const t = SITE_MAP[idx];
const div = document.createElement('div');
div.className = 'tab-content';
div.dataset.idx = idx;
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>';
});
if (t.shared && t.shared.length > 0) {
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);
renderedTabs[idx] = div;
return div;
}
/* 首次加载只渲染第一个 tab */
const firstTab = ensureTabRendered(0);
if (firstTab) firstTab.classList.add('tab-content--active');
/* Tab 切换(首次切换到某 tab 时自动渲染其内容) */
tabNav.addEventListener('click', e => {
const item = e.target.closest('.tab-nav__item');
if (!item) return;
const idx = parseInt(item.dataset.idx, 10);
/* 延迟渲染:首次访问该 tab 时才创建内容 DOM */
if (idx < SITE_MAP.length) {
ensureTabRendered(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');
const target = tabContents.querySelector('[data-idx="' + idx + '"]');
if (target) target.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>';
});
html += `<div class="prd-section">
<div class="prd-section__title">${ICONS.prd}项目资料<span class="tab-nav__badge" style="margin-left:6px">${PROJECT_DOCS.length}</span></div>
<div class="prd-grid">`;
PROJECT_DOCS.forEach(doc => {
html += `<div class="prd-item" onclick="openMdPreview('${doc.file}','${doc.title}')">
<div class="prd-item__icon prd-item__icon--ready">${ICONS.prdSmall}</div>
<div class="prd-item__info">
<div class="prd-item__name">${doc.title}</div>
<div class="prd-item__path">${doc.desc}</div>
</div>
<span class="prd-item__status prd-item__status--ready">查看</span>
</div>`;
});
html += '</div></div>';
div.innerHTML = html;
tabContents.appendChild(div);
})();
/* ===== Markdown 预览(新标签页打开) ===== */
function openMdPreview(filePath, title) {
window.open('_templates/md-viewer.html?file=' + encodeURIComponent(filePath) + '&title=' + encodeURIComponent(title || ''), '_blank');
}
</script>
</body>
</html>