feat: 检后模块新增影像数据管理菜单

This commit is contained in:
fengpu
2026-09-18 11:51:58 +08:00
parent c3c51978de
commit b0479ee9d1
29 changed files with 2646 additions and 210 deletions
+1 -1
View File
@@ -130,7 +130,7 @@ Web 管理端和员工端 APP 的其余模块(系统管理、运动管理、
- 2026-09-07:净菜类型拆分独立管理(commit 891678d)——①`din-ingredient.html`/`din-ingredient-add.html` 净菜类型按**规格行级**维护「可食部(%)」字段(位于规格别名后,新增规格时随行出现,新增/编辑/详情三页同步);②食材列表操作区「食材分类管理」按钮后新增「净菜管理」入口;③新建 `din-clean-veg-mgmt.html` 净菜管理二级页:统计卡片 + 食材名称(下拉搜索)/食材编码/净菜类型(净菜俗名/净菜标准名/规格俗名三源关键字)/切配形状筛选 + 异步导出按钮组(导出列表数据+查看导出任务,样式对齐全仓列表页惯例)+ 列表组级列 rowspan 合并、可食部/规格别名/切配规格/刀工按规格逐行拆分(组内虚线分隔、规格列区纵向分隔线)+ 先选食材回显已有净菜类型再新增/编辑的维护弹窗;SITE_MAP 与营养模块 CHANGELOG 同步。(相关决策:[[decisions.md#净菜类型数据模型与净菜管理页展示模式]]
- 2026-09-07:全仓截图脚本与截图质量修复(commit 7f18470)——assistant/expert/health-monitor 截图脚本失效页面引用改回现存文件名并清理孤儿截图;weight-main-v2 初始化改为解析 `?type=` 参数,修复设计总览六种目标状态截图雷同问题,重拍 weight-main/bluetooth-scale 共 10 张;补拍 PRD 引用但从未生成的视频进行中截图;体重管理 PRD 修复两处截图断链。
- 2026-09-04:医疗点PAD端首版原型(commit 15ca37b)——新建 `other-module/medical-pad/` 20 页(1280×800 横屏,抽屉交互页内实现),SITE_MAP 与项目背景尺寸规范同步,新增模块 CHANGELOG;同提交:全站演示数据脱敏(示例人名王建国→石维国、周明远→曲志明,机构长庆油田→CQ油田,覆盖约 30 页)、现场体检签到码改条形码/二维码双选项卡切换、新增健康长寿系统升级改造详细规划 V8 文档。(相关规范:[[feedback_medical_pad_migration.md]]
- 2026-09-04:医疗点PAD端首版原型(commit 15ca37b)——新建 `other-module/medical-pad/` 20 页(1280×800 横屏,抽屉交互页内实现),SITE_MAP 与项目背景尺寸规范同步,新增模块 CHANGELOG;同提交:全站演示数据脱敏(示例人名王建国→石维国、周明远→曲志明,机构CQ能源→CQ油田,覆盖约 30 页)、现场体检签到码改条形码/二维码双选项卡切换、新增健康长寿系统升级改造详细规划 V8 文档。(相关规范:[[feedback_medical_pad_migration.md]]
- 2026-09-04:体重管理员工端设计总览重构(commit 29a09e9)——总览页按目标承诺与签署/体重主页/每日打卡/历史记录/建议与报告/蓝牙设备六大主线重排,页面清单全面切换到 weight-main-v2/goal-management-v2 等 v2 新版页面,新增 playwright 统一截图脚本 `screenshot-weight.mjs` 并重新生成全部截图。
- 2026-09-03~09-04:健康体检员工端与管理端多轮完善(commit 118d143、05e157e)——员工端:选项目页费用横幅重做为体检额度卡片(额度使用进度条+必检/选检/可用额度三格,年度额度 1600,新增心脏彩超与胃幽门螺杆菌两项选检),"我的预约"更名重做为"现场体检",胃肠镜预约结果页成功/失败两态演示;管理端:体检报告模板更名体检导入模板管理、模板额度配置更名群体额度配置、大病人员信息新增弹窗(员工搜索下拉+信息卡片)、指标异常通知固定列+发送通知/短信、单位/胃肠镜预约统计改单位级别 TAB。
- 2026-09-02~09-03:健康体检预约流程与检中页面完善(commit a443c61、0d1c770、37ae054、3a838d0、2213106)——员工端"我的预约"合并单页+新增指标纵向对比与消息中心;胃肠镜医院预选征集/已提交页、检前首页预选通知条、选日期页预约状态条与可约窗口限制(常规近30天/胃肠镜7~30天)、预约结果页成功/失败演示与取消预约二次确认;管理端新增大病人员信息/指标异常通知/已选项目员工信息页,胃肠镜体检计划管理新增/编辑/详情抽屉(承检医院多选+机构树选人),组合模板/例外配置/医院费用/单位费用统一详情弹窗与编辑抽屉,检中页更名员工预约体检跟踪、统一员工编号筛选与列、体检编号更名体检编码。
+4
View File
@@ -271,6 +271,10 @@ const SITE_MAP = [
{ file:'post-disease-info.html', label:'病种信息配置' },
{ file:'post-conclusion-disease.html', label:'体检结论关联病种' },
{ file:'post-auto-select-disease.html', label:'员工病种信息' },
{ file:'post-imc-user.html', label:'用户体检信息' },
{ file:'post-imc-class.html', label:'用户影像文件分类' },
{ file:'post-imc-info.html', label:'用户影像文件详细' },
{ file:'post-imc-log.html', label:'文件处理日志' },
{ file:'agent-device.html', label:'前置机管理 · 常规体检前置服务' },
{ file:'exception-dirty.html', label:'系统脏数据' },
{ file:'exception-result.html', label:'结果异常' }
-209
View File
@@ -1,209 +0,0 @@
/**
* 体重管理模块统一截图脚本
* 规则(参考健康监测 screenshot-final.mjs):
* - phone-shell 尺寸 390 宽
* - 'fixed':弹窗/浮层类截图,严格 390×844,弹窗叠在当前页面之上
* - 'auto' :纯页面截图,短页面最低 844,长页面按真实内容展开
* 用法:node screenshot-weight.mjs [输出名子串过滤...]
*/
import { chromium } from 'playwright';
import { fileURLToPath } from 'url';
import path from 'path';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const docDir = path.join(__dirname, 'employee-app', 'weight-management', 'doc');
const root = path.join(__dirname, 'employee-app', 'weight-management');
const tasks = [
// ========== 目标承诺与签署 ==========
{ html: 'goal-management-v2.html', out: 'goal-management', mode: 'auto' },
{ html: 'goal-management-v2.html', out: 'goal-management-personal', mode: 'auto', eval: "switchType('personal')" },
{ html: 'goal-management-v2.html', out: 'goal-management-activity', mode: 'fixed', eval: 'openActivitySheet()' },
{ html: 'goal-management-v2.html', out: 'goal-management-confirm', mode: 'fixed', eval: "openActivitySheet();var i=document.querySelector('#actSheet .sheet-item');i.click();confirmActivity();closeActivitySheet();submitGoal()" },
{ html: 'goal-management-v2.html', out: 'goal-management-confirm-personal', mode: 'fixed', eval: "switchType('personal');submitGoal()" },
{ html: 'goal-detail-v2.html', out: 'goal-detail', mode: 'auto', query: '?type=activity&state=review' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-approved', mode: 'auto', query: '?type=activity&state=approved' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-rejected', mode: 'auto', query: '?type=activity&state=rejected' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-signed', mode: 'auto', query: '?type=activity&state=signed' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-ended', mode: 'auto', query: '?type=activity&state=ended' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-personal', mode: 'auto', query: '?type=personal' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-edit', mode: 'fixed', query: '?type=personal', eval: 'openEditGoalModal()' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-menu', mode: 'fixed', query: '?type=activity&state=signed', eval: "document.getElementById('navMore').click()" },
{ html: 'promise-signing.html', out: 'promise-signing', mode: 'auto', landscape: true },
{ html: 'promise-signing.html', out: 'promise-signing-giveup', mode: 'fixed', landscape: true, eval: 'openGiveup()' },
{ html: 'my-promise.html', out: 'my-promise', mode: 'auto' },
{ html: 'goal-history-v2.html', out: 'goal-history', mode: 'auto' },
{ html: 'goal-history-v2.html', out: 'goal-history-personal', mode: 'auto', eval: "switchTab('personal')" },
{ html: 'activity-detail.html', out: 'activity-detail', mode: 'auto' },
{ html: 'basic-info.html', out: 'basic-info', mode: 'auto' },
{ html: 'basic-info.html', out: 'basic-info-height', mode: 'fixed', eval: 'openHeightSheet()' },
{ html: 'basic-info.html', out: 'basic-info-activity', mode: 'fixed', eval: 'openSheet()' },
// ========== 体重管理主页 ==========
{ html: 'weight-main-v2.html', out: 'weight-main', mode: 'auto', query: '?type=hasact-signed' },
{ html: 'weight-main-v2.html', out: 'weight-main-review', mode: 'auto', query: '?type=hasact-review' },
{ html: 'weight-main-v2.html', out: 'weight-main-approved', mode: 'auto', query: '?type=hasact-approved' },
{ html: 'weight-main-v2.html', out: 'weight-main-rejected', mode: 'auto', query: '?type=hasact-rejected' },
{ html: 'weight-main-v2.html', out: 'weight-main-personal', mode: 'auto', query: '?type=personal' },
{ html: 'weight-main-v2.html', out: 'weight-main-none', mode: 'auto', query: '?type=none' },
{ html: 'weight-main-v2.html', out: 'weight-main-record', mode: 'fixed', query: '?type=hasact-signed', eval: 'openRecordSheet()' },
{ html: 'weight-main-v2.html', out: 'weight-main-record-success', mode: 'fixed', query: '?type=hasact-signed', eval: "openRecordSheet();submitRecord()" },
// ========== 每日打卡(吃/动/体重) ==========
{ html: 'eat-checkin-v2.html', out: 'eat-checkin', mode: 'auto' },
{ html: 'eat-checkin-v2.html', out: 'eat-checkin-lunch', mode: 'auto', query: '?meal=lunch' },
{ html: 'eat-checkin-v2.html', out: 'eat-checkin-recorded', mode: 'auto', eval: "selectCategory('recorded')" },
{ html: 'eat-checkin-v2.html', out: 'eat-checkin-bag', mode: 'fixed', eval: 'openBagPanel()' },
{ html: 'eat-checkin-v2.html', out: 'eat-checkin-gram', mode: 'fixed', eval: "var d;Object.keys(foodData).some(function(k){return foodData[k].items.some(function(it){if(it.name){d=it;return true}})});openGramPanel(d.id)" },
{ html: 'eat-checkin-v2.html', out: 'eat-checkin-success', mode: 'fixed', eval: 'submitCheckin()' },
{ html: 'eat-checkin-v2.html', out: 'eat-checkin-meals', mode: 'fixed', eval: "document.querySelector('.filter-row__meal-btn').click()" },
{ html: 'exercise-checkin-v2.html', out: 'exercise-checkin', mode: 'auto' },
{ html: 'exercise-checkin-v2.html', out: 'exercise-checkin-recorded', mode: 'auto', eval: "selectCategory('recorded')" },
{ html: 'exercise-checkin-v2.html', out: 'exercise-checkin-bag', mode: 'fixed', eval: 'openBagPanel()' },
{ html: 'exercise-checkin-v2.html', out: 'exercise-checkin-duration', mode: 'fixed', eval: "var d;Object.keys(exerciseData).some(function(k){return exerciseData[k].items.some(function(it){if(it.name){d=it;return true}})});openDurPanel(d.id)" },
{ html: 'exercise-checkin-v2.html', out: 'exercise-checkin-success', mode: 'fixed', eval: 'submitCheckin()' },
{ html: 'scan-checkin.html', out: 'scan-checkin', mode: 'fixed' },
{ html: 'scan-checkin.html', out: 'scan-checkin-analyzing', mode: 'fixed', eval: "takePhoto();clearInterval(analyzeTimer);analyzeTimer=null;setTimeout(function(){document.getElementById('analyzingBar').style.width='45%';document.getElementById('analyzingSub').textContent='正在匹配食物数据库...'},300)", wait: 900 },
{ html: 'scan-checkin.html', out: 'scan-checkin-result', mode: 'fixed', eval: 'showResult()' },
// ========== 历史记录 ==========
{ html: 'weight-record-detail-v2.html', out: 'weight-record-detail', mode: 'auto' },
{ html: 'weight-record-detail-v2.html', out: 'weight-record-detail-activity', mode: 'auto', eval: "switchTab('activity')" },
{ html: 'eat-detail-v2.html', out: 'eat-detail', mode: 'auto' },
{ html: 'eat-detail-v2.html', out: 'eat-detail-month', mode: 'fixed', eval: 'toggleMonthPicker()' },
{ html: 'exercise-detail-v2.html', out: 'exercise-detail', mode: 'auto' },
{ html: 'exercise-detail-v2.html', out: 'exercise-detail-month', mode: 'fixed', eval: 'toggleMonthPicker()' },
{ html: 'exercise-detail-v2.html', out: 'exercise-detail-tip', mode: 'fixed', eval: "document.querySelector('.chart-dot').click()" },
{ html: 'eat-exercise-detail-v2.html', out: 'eat-exercise-detail', mode: 'auto' },
{ html: 'eat-exercise-detail-v2.html', out: 'eat-exercise-detail-month', mode: 'fixed', eval: 'toggleMonthPicker()' },
{ html: 'weight-detail-v2.html', out: 'weight-detail', mode: 'auto' },
{ html: 'weight-detail-v2.html', out: 'weight-detail-record', mode: 'fixed', eval: 'openRecordSheet()' },
{ html: 'weight-detail-v2.html', out: 'weight-detail-month', mode: 'fixed', eval: 'toggleMonthPicker()' },
{ html: 'weight-detail-v2.html', out: 'weight-detail-tip', mode: 'fixed', eval: "document.querySelector('.chart-dot').click()" },
{ html: 'report-detail.html', out: 'report-detail-fat', mode: 'auto', query: '?device=fat' },
{ html: 'report-detail.html', out: 'report-detail-scale', mode: 'auto', query: '?device=scale' },
{ html: 'report-detail.html', out: 'report-detail-bluetooth', mode: 'auto', query: '?device=bluetooth' },
{ html: 'body-fat-report.html', out: 'body-fat-report', mode: 'auto' },
{ html: 'height-scale-report.html', out: 'height-scale-report', mode: 'auto' },
// ========== 建议与报告 ==========
{ html: 'diet-advice-v2.html', out: 'diet-advice', mode: 'auto' },
{ html: 'diet-advice-v2.html', out: 'diet-advice-detail', mode: 'fixed', eval: "openFoodDetail('breakfast',0)" },
{ html: 'diet-advice-v2.html', out: 'diet-advice-dislike', mode: 'fixed', eval: 'showDislikeSheet()' },
{ html: 'diet-advice-v2.html', out: 'diet-advice-nutrition', mode: 'fixed', eval: 'showNutritionEmpty(true)' },
{ html: 'diet-advice-v2.html', out: 'diet-advice-nutrition-tab', mode: 'auto', eval: "switchTab('nutrition')" },
{ html: 'diet-advice-v2.html', out: 'diet-advice-month', mode: 'fixed', eval: 'toggleMonthPicker()' },
{ html: 'exercise-advice-v2.html', out: 'exercise-advice', mode: 'auto' },
{ html: 'exercise-advice-v2.html', out: 'exercise-advice-month', mode: 'fixed', eval: 'toggleMonthPicker()' },
{ html: 'weight-report.html', out: 'weight-report', mode: 'auto', query: '?type=hasact' },
{ html: 'weight-report.html', out: 'weight-report-personal', mode: 'auto', query: '?type=personal' },
{ html: 'weight-report.html', out: 'weight-report-none', mode: 'auto', query: '?type=none' },
{ html: 'weight-ranking.html', out: 'weight-ranking', mode: 'auto', query: '?type=hasact' },
{ html: 'weight-ranking.html', out: 'weight-ranking-personal', mode: 'auto', query: '?type=personal' },
{ html: 'weight-ranking.html', out: 'weight-ranking-none', mode: 'auto', query: '?type=none' },
// ========== 蓝牙设备 ==========
{ html: 'bluetooth-scale-v2.html', out: 'bluetooth-scale', mode: 'auto', query: '#paired' },
{ html: 'bluetooth-scale-v2.html', out: 'bluetooth-scale-empty', mode: 'auto' },
{ html: 'bluetooth-device-members-v2.html', out: 'bluetooth-device-members', mode: 'auto', query: '?device=s1' },
{ html: 'bluetooth-device-members-v2.html', out: 'bluetooth-device-members-empty', mode: 'auto', query: '?device=b2' },
{ html: 'bluetooth-device-members-v2.html', out: 'bluetooth-device-members-delete', mode: 'fixed', query: '?device=s1', eval: "document.querySelector('.action-btn--delete').click()" },
{ html: 'bluetooth-device-members-v2.html', out: 'bluetooth-device-members-sync', mode: 'fixed', query: '?device=s1', eval: 'syncMembers()', wait: 1600 },
{ html: 'bluetooth-search-v2.html', out: 'bluetooth-search', mode: 'auto' },
{ html: 'bluetooth-wifi-v2.html', out: 'bluetooth-wifi', mode: 'auto' },
];
(async () => {
const filters = process.argv.slice(2);
const runTasks = filters.length ? tasks.filter(t => filters.some(f => t.out.includes(f))) : tasks;
const browser = await chromium.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
const context = await browser.newContext({ viewport: { width: 390, height: 4000 } });
for (const t of runTasks) {
const page = await context.newPage();
const filePath = path.join(root, t.html);
const url = `file:///${filePath.replace(/\\/g, '/')}${t.query || ''}`;
await page.goto(url, { waitUntil: 'networkidle' });
if (t.eval) {
await page.evaluate(t.eval).catch(e => console.log(`! eval failed [${t.out}]: ${e.message}`));
await page.waitForTimeout(t.wait || 300);
}
if (t.landscape) {
// 横屏页:phone-shell 固定 844×390,不展开、按元素原尺寸截取
await page.evaluate(() => {
const shell = document.querySelector('.phone-shell');
if (shell) {
shell.style.borderRadius = '0';
shell.style.boxShadow = 'none';
}
document.body.style.padding = '0';
document.body.style.margin = '0';
document.body.style.minHeight = 'auto';
document.body.style.background = '#fff';
document.documentElement.style.display = 'block';
});
await page.setViewportSize({ width: 844, height: 390 });
await page.waitForTimeout(200);
} else if (t.mode === 'fixed') {
await page.evaluate(() => {
const shell = document.querySelector('.phone-shell');
if (shell) {
shell.style.borderRadius = '0';
shell.style.boxShadow = 'none';
}
document.body.style.padding = '0';
document.body.style.minHeight = 'auto';
document.body.style.background = '#fff';
});
await page.setViewportSize({ width: 390, height: 844 });
await page.waitForTimeout(200);
} else {
await page.evaluate(() => {
const shell = document.querySelector('.phone-shell');
if (shell) {
shell.style.height = 'auto';
shell.style.minHeight = '844px';
shell.style.maxHeight = 'none';
shell.style.borderRadius = '0';
shell.style.boxShadow = 'none';
shell.style.overflow = 'visible';
shell.style.display = 'block';
}
document.querySelectorAll('.scroll-content, .page-content, .main-content, .list-wrap, .content').forEach(c => {
c.style.overflow = 'visible';
c.style.maxHeight = 'none';
c.style.height = 'auto';
c.style.flex = 'none';
});
document.body.style.padding = '0';
document.body.style.minHeight = 'auto';
document.body.style.background = '#fff';
});
await page.waitForTimeout(300);
const measured = await page.evaluate(() => {
const shell = document.querySelector('.phone-shell');
if (!shell) return document.body.scrollHeight;
return Math.ceil(shell.getBoundingClientRect().height);
});
const h = Math.max(measured, 844);
await page.setViewportSize({ width: 390, height: h });
await page.waitForTimeout(150);
}
const shell = await page.$('.phone-shell');
const target = shell || page;
await target.screenshot({
path: path.join(docDir, 'screenshots', `${t.out}.jpg`),
type: 'jpeg',
quality: 85,
});
console.log(`${t.out}.jpg [${t.mode}]`);
await page.close();
}
await browser.close();
console.log(`\n完成!共截取 ${runTasks.length}`);
})();
+12
View File
@@ -1,5 +1,17 @@
# 健康体检模块 CHANGELOG
## [Unreleased] 2026-09-18
### Added
- 检后侧边栏新增「体检影像管理」二级菜单(位于冠脉CTA体检分组之后),复刻老系统 data-center-vue3 `views/imc` 四个列表页为纯 HTML 原型:
- `post-imc-user.html`(用户体检信息):筛选(姓名/身份证号/体检年份/医院名称);行勾选 + 批量删除;主表列(单位信息/姓名/身份证号/体检年份/医院名称/体检编号/创建日期);行首箭头展开子表(检查项目名称/处理状态/影像信息·预览/影像报告·查看,无数据显示「暂无/无文件」);操作列编辑/详情/删除 + 编辑弹窗(7 字段)+ 详情弹窗
- `post-imc-class.html`(用户影像文件分类):筛选(影像分类/检查项目名称/处理状态);操作栏新增/导出/导入 + 批量删除;列(影像分类/检查项目名称/影像文件类型/报告文件类型/处理状态/检查结果/错误信息/出报告时间/创建日期),检查结果与错误信息长文本截断、悬停显示全文;操作列编辑/详情/删除 + 新增/编辑弹窗(10 字段)+ 详情弹窗
- `post-imc-info.html`(用户影像文件详细):筛选(文件类型/患者ID/研究实例ID);操作栏新增/导出/导入 + 批量删除;列(文件类型·影像/报告标签/影像序号/错误信息/文件地址·下载/dcm实例ID/研究实例ID/患者ID/研究ID/创建日期);操作列编辑/详情/删除 + 新增/编辑弹窗(10 字段)+ 详情弹窗
- `post-imc-log.html`(文件处理日志):筛选(身份证号/体检年份/体检编号/影像分类/处理状态);行勾选 + 批量删除;列(医院名称/姓名/身份证号/体检年份/体检编号/影像分类/json数据·截断/文件地址·下载zip/文件大小MB/处理状态/错误信息/传输开始/传输完成/解析开始/解析完成时间),默认按传输开始时间倒序;操作列详情/删除 + 详情弹窗(15 字段含 json 数据代码块)
- 数据字典沿用老系统:影像分类(超声/CT/MRI/X射线/心电)、处理状态(新建/待解析/失败/解析中/成功/失败待重试/最终失败,彩色标签)、文件类型(影像/报告)
- `index.html` SITE_MAP 健康体检模块新增「用户体检信息/用户影像文件分类/用户影像文件详细/文件处理日志」四个入口
- 25 个检后页面侧边栏统一新增「体检影像管理」分组(21 个存量页面批量同步 + 4 个新页面自身 active 指向)
## [Unreleased] 2026-09-17
### Changed
@@ -198,6 +198,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -181,6 +181,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -208,6 +208,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -172,6 +172,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -239,6 +239,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -198,6 +198,18 @@
<a href="post-cta-stat.html" class="sidebar-item active">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -206,6 +206,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -181,6 +181,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -194,6 +194,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -198,6 +198,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -181,6 +181,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -208,6 +208,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -0,0 +1,619 @@
<!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>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
:root{
--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;
--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--background:#F5F7FA;
--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;
--radius-sm:4px;--radius-md:8px;--radius-lg:12px;
--shadow-sm:0 1px 4px rgba(0,0,0,0.08);--shadow-md:0 4px 12px rgba(0,0,0,0.12);
}
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
.top-bar__logo svg{width:28px;height:28px;}
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
.top-bar__nav a{color:rgba(255,255,255,0.65);text-decoration:none;padding:6px 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,0.08);}
.top-bar__nav a.active{color:#fff;background:var(--primary);}
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);}
.top-bar__right .btn-back{color:rgba(255,255,255,0.65);cursor:pointer;display:flex;align-items:center;gap:4px;text-decoration:none;}
.top-bar__right .btn-back:hover{color:#fff;}
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);cursor:pointer;}
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);background:rgba(255,255,255,0.04);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;transition:transform .2s;flex-shrink:0;}
.sidebar-subgroup__header .arrow path{stroke:rgba(255,255,255,0.45);}
.sidebar-subgroup.open>.sidebar-subgroup__header{color:rgba(255,255,255,0.85);}
.sidebar-subgroup.open>.sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;padding-left:16px;}
.sidebar-subgroup.open>.sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:36px;font-size:12px;}
.sidebar-subgroup .sidebar-subgroup{padding-left:12px;}
.sidebar-subgroup .sidebar-subgroup .sidebar-item{padding-left:40px;}
.sidebar-subgroup .sidebar-subgroup>.sidebar-subgroup__header{font-size:12px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.filter-bar{background:#fff;border-radius:var(--radius-md);padding:20px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:16px;flex-wrap:wrap;}
.filter-item{display:flex;align-items:center;gap:8px;}
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
.filter-input{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;min-width:170px;}
.filter-input:focus{border-color:var(--primary);}
.filter-input::placeholder{color:var(--text-placeholder);}
.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;cursor:pointer;min-width:140px;}
.filter-select:focus{border-color:var(--primary);}
.filter-select.placeholder{color:var(--text-placeholder);}
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.table-card__content{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;}
.table-card__content table{min-width:100%;white-space:nowrap;}
table{width:100%;border-collapse:collapse;}
thead th{background:#FAFAFA;padding:12px 16px;text-align:center;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;text-align:center;}
tbody tr:last-child td{border-bottom:none;}
tbody tr:hover{background:#F8FBFF;}
#classTable thead th:last-child{position:sticky;right:0;background:#FAFAFA;border-left:1px solid var(--border);}
#classTable tbody td:last-child{position:sticky;right:0;background:#fff;border-left:1px solid var(--border);}
#classTable tbody tr:hover td:last-child{background:#F8FBFF;}
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);}.link:hover{color:#40a9ff;}
.link-danger{color:var(--danger);cursor:pointer;font-size:var(--font-sm);}.link-danger:hover{color:#ff7875;}
.empty-tip{padding:48px 0;text-align:center;font-size:var(--font-sm);color:var(--text-placeholder);}
.muted{color:var(--text-placeholder);}
.tag{display:inline-flex;align-items:center;padding:2px 10px;border-radius:10px;font-size:var(--font-xs);}
.tag-blue{background:#E6F7FF;color:#1890FF;}
.tag-green{background:#F6FFED;color:#52C41A;}
.tag-orange{background:#FFF7E6;color:#FA8C16;}
.tag-red{background:#FFF1F0;color:#FF4D4F;}
.tag-gray{background:#FAFAFA;color:#999;}
.pagination{display:flex;align-items:center;justify-content:flex-end;padding:16px 24px;gap:8px;border-top:1px solid var(--border);flex-wrap:wrap;}
.pagination span{font-size:var(--font-sm);color:var(--text-secondary);}
.page-btn{width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-sm);display:inline-flex;align-items:center;justify-content:center;transition:all .2s;color:var(--text-primary);}
.page-btn:hover:not(:disabled),.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.page-btn:disabled{color:var(--text-placeholder);cursor:not-allowed;background:#FAFAFA;}
.page-ellipsis{width:24px;text-align:center;color:var(--text-placeholder);font-size:var(--font-sm);}
.page-size{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 24px 0 10px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;appearance:none;cursor:pointer;outline:none;}
.page-jump input{width:48px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);text-align:center;font-size:var(--font-sm);outline:none;margin:0 4px;}
.page-jump input:focus{border-color:var(--primary);}
.modal-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:200;overflow-y:auto;}
.modal-mask.open{display:flex;align-items:flex-start;justify-content:center;padding:60px 24px;}
.modal{width:640px;max-width:100%;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-md);display:flex;flex-direction:column;max-height:calc(100vh - 120px);}
.modal__header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--border);flex-shrink:0;}
.modal__close{cursor:pointer;color:var(--text-secondary);display:flex;align-items:center;transition:color .2s;}
.modal__close:hover{color:var(--danger);}
.modal__title{font-size:var(--font-base);font-weight:600;}
.modal__body{padding:20px;overflow-y:auto;}
.modal__footer{display:flex;justify-content:flex-end;gap:12px;padding:14px 20px;border-top:1px solid var(--border);flex-shrink:0;}
.form-item{margin-bottom:16px;}
.form-item:last-child{margin-bottom:0;}
.form-label{font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:6px;display:flex;align-items:center;gap:4px;font-weight:500;}
.form-label .required{color:var(--danger);}
.form-input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;}
.form-input:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.2);}
.form-input::placeholder{color:var(--text-placeholder);}
.form-select{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;cursor:pointer;}
.form-select:focus{border-color:var(--primary);}
.form-textarea{width:100%;border:1px solid var(--border);border-radius:var(--radius-sm);padding:8px 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;resize:vertical;min-height:72px;font-family:inherit;}
.form-textarea:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.2);}
.detail-table{width:100%;border-collapse:collapse;border:1px solid var(--border);}
.detail-table th,.detail-table td{border-bottom:1px solid var(--border);text-align:left;padding:12px 16px;font-size:var(--font-sm);}
.detail-table tr:last-child td{border-bottom:none;}
.detail-table th{width:130px;background:#FAFAFA;color:var(--text-secondary);font-weight:500;}
</style>
</head>
<body>
<div class="top-bar">
<div class="top-bar__logo">
<svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
健康体检
</div>
<nav class="top-bar__nav">
<a href="archive-personal.html">档案</a>
<a href="config-material-notice.html">基础配置</a>
<a href="pre-hospital-scope.html">检前</a>
<a href="mid-appointment-track.html">检中</a>
<a href="post-unit-count.html" class="active">检后</a>
<a href="exception-dirty.html">异常事件</a>
</nav>
<div class="top-bar__right">
<a class="btn-back" href="../../index.html">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M10 3L5 8l5 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
返回首页
</a>
<div class="avatar"></div>
<span style="color:rgba(255,255,255,0.65);">系统管理员 ▾</span>
</div>
</div>
<aside class="sidebar">
<div class="sidebar-menu__title">检后</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
<a href="post-indicator-notify.html" class="sidebar-item">指标异常通知</a>
<a href="post-report-unmatched.html" class="sidebar-item">体检报告未匹配项目</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检统计汇总
</div>
<div class="sidebar-subgroup__body">
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
<a href="post-cancer-screening.html" class="sidebar-item">防癌筛查人数统计</a>
<a href="post-standard-stat.html" class="sidebar-item">系统标准化项目统计</a>
<a href="post-contracted-hospital.html" class="sidebar-item">签约医院人数统计</a>
<a href="post-key-indicator-stat.html" class="sidebar-item">重点指标异常统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
病种信息管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-disease-info.html" class="sidebar-item">病种信息配置</a>
<a href="post-conclusion-disease.html" class="sidebar-item">体检结论关联病种</a>
<a href="post-auto-select-disease.html" class="sidebar-item">员工病种信息</a>
</div>
</div>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item active">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<main class="main">
<div class="filter-bar">
<div class="filter-item">
<label>影像分类:</label>
<select class="filter-select placeholder" id="f-imgclass" onchange="this.classList.toggle('placeholder',!this.value)">
<option value="">请选择影像分类</option>
<option value="超声">超声</option>
<option value="CT">CT</option>
<option value="MRI">MRI</option>
<option value="X射线">X射线</option>
<option value="心电">心电</option>
</select>
</div>
<div class="filter-item">
<label>检查项目名称:</label>
<input class="filter-input" type="text" id="f-name" placeholder="请输入检查项目名称" style="min-width:180px;">
</div>
<div class="filter-item">
<label>处理状态:</label>
<select class="filter-select placeholder" id="f-status" onchange="this.classList.toggle('placeholder',!this.value)">
<option value="">请选择处理状态</option>
<option value="1">新建</option>
<option value="2">待解析</option>
<option value="4">解析中</option>
<option value="5">成功</option>
<option value="6">失败待重试</option>
<option value="3">失败</option>
<option value="7">最终失败</option>
</select>
</div>
<div style="margin-left:auto;display:flex;gap:12px;">
<button class="btn btn-primary" onclick="doQuery()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4.5" stroke="#fff" stroke-width="1.4"/><path d="M9.5 9.5l3 3" stroke="#fff" stroke-width="1.4" stroke-linecap="round"/></svg>
查询
</button>
<button class="btn btn-default" onclick="doReset()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M12 7a5 5 0 1 1-1.5-3.6M12 1.5V4h-2.5" stroke="#666" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
重置
</button>
</div>
</div>
<div style="display:flex;gap:12px;margin-bottom:16px;align-items:center;">
<button class="btn btn-primary" onclick="openForm(null)">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 2v10M2 7h10" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/></svg>
新增
</button>
<button class="btn btn-default" onclick="exportList()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 2v7M4 6l3 3 3-3M2 12.5h10" stroke="#666" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
导出
</button>
<button class="btn btn-default" onclick="importList()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 9V2M4 5l3-3 3 3M2 12.5h10" stroke="#666" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
导入
</button>
<button class="btn btn-default" onclick="batchDelete()" style="margin-left:auto;">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 3.5h10M5.5 3.5V2h3v1.5M3.5 3.5l.7 8.5h5.6l.7-8.5" stroke="#666" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>
批量删除
</button>
</div>
<div class="table-card">
<div class="table-card__content">
<table id="classTable">
<thead>
<tr>
<th style="width:48px;"><input type="checkbox" id="checkAll" onclick="toggleCheckAll(this)"></th>
<th style="width:56px;">序号</th>
<th>影像分类</th>
<th>检查项目名称</th>
<th>影像文件类型</th>
<th>报告文件类型</th>
<th>处理状态</th>
<th>检查结果</th>
<th>错误信息</th>
<th>出报告时间</th>
<th>创建日期</th>
<th>操作</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
</table>
</div>
<div class="pagination" id="pagination"></div>
</div>
</main>
<div class="modal-mask" id="formMask" onclick="if(event.target===this)closeForm()">
<div class="modal">
<div class="modal__header">
<span class="modal__close" onclick="closeForm()">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
</span>
<span class="modal__title" id="formTitle">新增用户影像文件分类</span>
</div>
<div class="modal__body">
<div class="form-item">
<div class="form-label">用户体检信息ID</div>
<input class="form-input" id="formMuid" type="text" placeholder="请输入用户体检信息ID">
</div>
<div class="form-item">
<div class="form-label"><span class="required">*</span> 影像分类</div>
<select class="form-select" id="formImgclass">
<option value="超声">超声</option>
<option value="CT">CT</option>
<option value="MRI">MRI</option>
<option value="X射线">X射线</option>
<option value="心电">心电</option>
</select>
</div>
<div class="form-item">
<div class="form-label"><span class="required">*</span> 检查项目名称</div>
<input class="form-input" id="formName" type="text" placeholder="请输入检查项目名称">
</div>
<div class="form-item">
<div class="form-label">影像文件类型</div>
<select class="form-select" id="formImgtype">
<option value="非DICOM">非DICOM</option>
<option value="dic">dic</option>
<option value="dcm">dcm</option>
</select>
</div>
<div class="form-item">
<div class="form-label">报告文件类型</div>
<select class="form-select" id="formReptype">
<option value="png">png</option>
<option value="jpg">jpg</option>
<option value="pdf">pdf</option>
</select>
</div>
<div class="form-item">
<div class="form-label">处理状态</div>
<select class="form-select" id="formStatus">
<option value="1">新建</option>
<option value="2">待解析</option>
<option value="3">失败</option>
<option value="4">解析中</option>
<option value="5">成功</option>
<option value="6">失败待重试</option>
<option value="7">最终失败</option>
</select>
</div>
<div class="form-item">
<div class="form-label">检查结果</div>
<textarea class="form-textarea" id="formContent" placeholder="请输入检查结果"></textarea>
</div>
<div class="form-item">
<div class="form-label">错误信息</div>
<textarea class="form-textarea" id="formMsg" placeholder="请输入错误信息"></textarea>
</div>
<div class="form-item">
<div class="form-label">出报告时间</div>
<input class="form-input" id="formReportdate" type="text" placeholder="如 2026-08-15 12:00:00">
</div>
<div class="form-item">
<div class="form-label">创建日期</div>
<input class="form-input" id="formCreatetime" type="text" placeholder="如 2026-08-15 11:30:00">
</div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeForm()">取消</button>
<button class="btn btn-primary" onclick="saveForm()">保存</button>
</div>
</div>
</div>
<div class="modal-mask" id="detailMask" onclick="if(event.target===this)closeDetail()">
<div class="modal">
<div class="modal__header">
<span class="modal__close" onclick="closeDetail()">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
</span>
<span class="modal__title">用户影像文件分类详情</span>
</div>
<div class="modal__body" id="detailBody"></div>
</div>
</div>
<script>
var handleStatusMap={1:'新建',2:'待解析',3:'失败',4:'解析中',5:'成功',6:'失败待重试',7:'最终失败'};
function statusTag(s){
var cls='tag-gray';
if(s===5)cls='tag-green';
else if(s===4||s===6)cls='tag-orange';
else if(s===3||s===7)cls='tag-red';
else if(s===1)cls='tag-blue';
return '<span class="tag '+cls+'">'+handleStatusMap[s]+'</span>';
}
var rows=[
{imgClass:'CT',name:'胸部CT平扫',imgType:'dcm',repType:'pdf',status:5,content:'双肺纹理清晰,未见明显异常密度影,纵隔及肺门未见肿大淋巴结。',msg:'',reportDate:'2026-08-15 12:00:00',createTime:'2026-08-15 11:30:00',muId:'MU1001'},
{imgClass:'MRI',name:'头颅MRI平扫',imgType:'dcm',repType:'pdf',status:5,content:'颅脑平扫未见明显异常信号影,脑室系统形态正常。',msg:'',reportDate:'2026-08-15 13:10:00',createTime:'2026-08-15 12:30:00',muId:'MU1001'},
{imgClass:'超声',name:'腹部超声',imgType:'非DICOM',repType:'png',status:4,content:'',msg:'',reportDate:'',createTime:'2026-08-15 14:00:00',muId:'MU1001'},
{imgClass:'CT',name:'胸部CT平扫',imgType:'dcm',repType:'pdf',status:5,content:'双肺纹理清晰,右肺上叶见微小结节影,建议随访。',msg:'',reportDate:'2026-08-15 15:20:00',createTime:'2026-08-15 14:50:00',muId:'MU1002'},
{imgClass:'心电',name:'心电图',imgType:'非DICOM',repType:'jpg',status:5,content:'窦性心律,正常心电图。',msg:'',reportDate:'2026-08-15 15:40:00',createTime:'2026-08-15 15:10:00',muId:'MU1002'},
{imgClass:'X射线',name:'胸部X射线',imgType:'dcm',repType:'jpg',status:7,content:'',msg:'DICOM标签解析失败:缺少 PatientID 标签',reportDate:'',createTime:'2026-08-15 16:00:00',muId:'MU1003'},
{imgClass:'超声',name:'腹部超声',imgType:'非DICOM',repType:'png',status:6,content:'',msg:'文件服务器上传超时,等待重试',reportDate:'',createTime:'2026-08-15 16:20:00',muId:'MU1003'},
{imgClass:'CT',name:'冠脉CTA',imgType:'dcm',repType:'pdf',status:2,content:'',msg:'',reportDate:'',createTime:'2026-08-16 09:12:00',muId:'MU1004'},
{imgClass:'CT',name:'胸部CT平扫',imgType:'dcm',repType:'pdf',status:5,content:'双肺纹理清晰,未见明显异常。',msg:'',reportDate:'2025-08-20 16:00:00',createTime:'2025-08-20 15:30:00',muId:'MU1018'},
{imgClass:'MRI',name:'头颅MRI平扫',imgType:'dcm',repType:'pdf',status:5,content:'颅脑平扫未见明显异常。',msg:'',reportDate:'2025-08-20 16:30:00',createTime:'2025-08-20 16:00:00',muId:'MU1018'},
{imgClass:'超声',name:'腹部超声',imgType:'非DICOM',repType:'png',status:5,content:'肝胆胰脾肾未见明显异常。',msg:'',reportDate:'2025-08-21 10:00:00',createTime:'2025-08-21 09:30:00',muId:'MU1021'},
{imgClass:'超声',name:'腹部超声',imgType:'非DICOM',repType:'png',status:7,content:'',msg:'报告文件格式与声明类型不一致',reportDate:'',createTime:'2025-08-21 11:00:00',muId:'MU1021'}
];
var state={imgclass:'',name:'',status:'',page:1,pageSize:10};
function doQuery(){
state.imgclass=document.getElementById('f-imgclass').value;
state.name=document.getElementById('f-name').value.trim();
state.status=document.getElementById('f-status').value;
state.page=1;
renderTable();
}
function doReset(){
var ic=document.getElementById('f-imgclass');ic.value='';ic.classList.add('placeholder');
document.getElementById('f-name').value='';
var st=document.getElementById('f-status');st.value='';st.classList.add('placeholder');
state.imgclass='';state.name='';state.status='';
state.page=1;
renderTable();
}
function filteredRows(){
return rows.filter(function(r){
if(state.imgclass&&r.imgClass!==state.imgclass)return false;
if(state.name&&r.name.indexOf(state.name)===-1)return false;
if(state.status&&String(r.status)!==state.status)return false;
return true;
});
}
function renderTable(){
var list=filteredRows();
var totalPages=Math.max(1,Math.ceil(list.length/state.pageSize));
if(state.page>totalPages)state.page=totalPages;
var start=(state.page-1)*state.pageSize;
var pageRows=list.slice(start,start+state.pageSize);
var tbody=document.getElementById('tbody');
tbody.innerHTML=pageRows.length?pageRows.map(function(r,pos){
var idx=rows.indexOf(r);
var content=r.content?r.content:'<span class="muted"></span>';
if(r.content&&r.content.length>18)content='<span title="'+r.content+'">'+r.content.slice(0,18)+'…</span>';
var msg=r.msg?r.msg:'<span class="muted"></span>';
if(r.msg&&r.msg.length>18)msg='<span title="'+r.msg+'">'+r.msg.slice(0,18)+'…</span>';
return '<tr>'
+'<td><input type="checkbox" class="row-check" data-idx="'+idx+'"></td>'
+'<td>'+(start+pos+1)+'</td>'
+'<td>'+r.imgClass+'</td><td>'+r.name+'</td><td>'+r.imgType+'</td><td>'+r.repType+'</td>'
+'<td>'+statusTag(r.status)+'</td><td style="text-align:left;">'+content+'</td><td style="text-align:left;">'+msg+'</td>'
+'<td>'+(r.reportDate||'<span class="muted"></span>')+'</td><td>'+r.createTime+'</td>'
+'<td><span class="link" onclick="openForm('+idx+')">编辑</span> <span class="link" style="margin-left:8px;" onclick="openDetail('+idx+')">详情</span> <span class="link-danger" style="margin-left:8px;" onclick="deleteRow('+idx+')">删除</span></td>'
+'</tr>';
}).join(''):'<tr><td colspan="12"><div class="empty-tip">暂无数据</div></td></tr>';
var all=tbody.querySelectorAll('.row-check');
var checked=tbody.querySelectorAll('.row-check:checked');
document.getElementById('checkAll').checked=all.length>0&&all.length===checked.length;
renderPagination(list.length,totalPages);
}
function pageSeq(p,last){
var seq=[];
if(last<=7){for(var i=1;i<=last;i++)seq.push(i);return seq;}
if(p<=4){seq=[1,2,3,4,5,'...',last];}
else if(p>=last-3){seq=[1,'...',last-4,last-3,last-2,last-1,last];}
else{seq=[1,'...',p-1,p,p+1,'...',last];}
return seq;
}
function renderPagination(total,totalPages){
var html='<span>共 '+total+' 条数据</span>';
if(totalPages>1){
html+='<button class="page-btn"'+(state.page===1?' disabled':'')+' onclick="goPage('+(state.page-1)+')">&lt;</button>';
pageSeq(state.page,totalPages).forEach(function(p){
if(p==='...')html+='<span class="page-ellipsis">...</span>';
else html+='<button class="page-btn'+(p===state.page?' active':'')+'" onclick="goPage('+p+')">'+p+'</button>';
});
html+='<button class="page-btn"'+(state.page===totalPages?' disabled':'')+' onclick="goPage('+(state.page+1)+')">&gt;</button>';
}
html+='<select class="page-size" onchange="changeSize(this.value)">'
+[10,20,50].map(function(n){return '<option value="'+n+'"'+(n===state.pageSize?' selected':'')+'>'+n+'条/页</option>';}).join('')
+'</select>';
html+='<span class="page-jump">跳至<input type="number" id="jumpInput" min="1" value="'+state.page+'" onkeydown="if(event.key===\'Enter\')jumpTo('+totalPages+')"><button class="page-btn" onclick="jumpTo('+totalPages+')"></button></span>';
document.getElementById('pagination').innerHTML=html;
}
function goPage(p){state.page=p;renderTable();}
function changeSize(v){state.pageSize=parseInt(v,10);state.page=1;renderTable();}
function jumpTo(totalPages){
var input=document.getElementById('jumpInput');
var p=parseInt(input.value,10);
if(isNaN(p)||p<1)p=1;
if(p>totalPages)p=totalPages;
state.page=p;
renderTable();
}
function toggleCheckAll(cb){
document.querySelectorAll('.row-check').forEach(function(c){c.checked=cb.checked;});
}
function checkedIdx(){
var arr=[];
document.querySelectorAll('.row-check:checked').forEach(function(c){arr.push(parseInt(c.dataset.idx,10));});
return arr;
}
function batchDelete(){
var idxs=checkedIdx();
if(!idxs.length){alert('请先勾选要删除的记录');return;}
if(!confirm('确认删除选中的 '+idxs.length+' 条记录?'))return;
idxs.sort(function(a,b){return b-a;}).forEach(function(i){rows.splice(i,1);});
renderTable();
}
function deleteRow(idx){
if(!confirm('确认删除该影像文件分类记录?'))return;
rows.splice(idx,1);
renderTable();
}
function exportList(){alert('导出当前筛选结果(Excel');}
function importList(){alert('导入影像文件分类数据(Excel');}
var editingIdx=null;
function openForm(idx){
editingIdx=idx;
document.getElementById('formTitle').textContent=idx===null?'新增用户影像文件分类':'编辑用户影像文件分类';
if(idx===null){
document.getElementById('formMuid').value='';
document.getElementById('formImgclass').value='超声';
document.getElementById('formName').value='';
document.getElementById('formImgtype').value='dcm';
document.getElementById('formReptype').value='pdf';
document.getElementById('formStatus').value='1';
document.getElementById('formContent').value='';
document.getElementById('formMsg').value='';
document.getElementById('formReportdate').value='';
document.getElementById('formCreatetime').value='';
}else{
var r=rows[idx];
document.getElementById('formMuid').value=r.muId;
document.getElementById('formImgclass').value=r.imgClass;
document.getElementById('formName').value=r.name;
document.getElementById('formImgtype').value=r.imgType;
document.getElementById('formReptype').value=r.repType;
document.getElementById('formStatus').value=String(r.status);
document.getElementById('formContent').value=r.content;
document.getElementById('formMsg').value=r.msg;
document.getElementById('formReportdate').value=r.reportDate;
document.getElementById('formCreatetime').value=r.createTime;
}
document.getElementById('formMask').classList.add('open');
}
function closeForm(){document.getElementById('formMask').classList.remove('open');}
function saveForm(){
var name=document.getElementById('formName').value.trim();
if(!name){alert('请输入检查项目名称');return;}
var data={
imgClass:document.getElementById('formImgclass').value,
name:name,
imgType:document.getElementById('formImgtype').value,
repType:document.getElementById('formReptype').value,
status:parseInt(document.getElementById('formStatus').value,10),
content:document.getElementById('formContent').value.trim(),
msg:document.getElementById('formMsg').value.trim(),
reportDate:document.getElementById('formReportdate').value.trim(),
createTime:document.getElementById('formCreatetime').value.trim(),
muId:document.getElementById('formMuid').value.trim()
};
if(editingIdx===null){
rows.unshift(data);
}else{
rows[editingIdx]=data;
}
closeForm();
renderTable();
}
function openDetail(idx){
var r=rows[idx];
document.getElementById('detailBody').innerHTML=
'<table class="detail-table"><tbody>'
+'<tr><th>用户体检信息ID</th><td>'+r.muId+'</td></tr>'
+'<tr><th>影像分类</th><td>'+r.imgClass+'</td></tr>'
+'<tr><th>检查项目名称</th><td>'+r.name+'</td></tr>'
+'<tr><th>影像文件类型</th><td>'+r.imgType+'</td></tr>'
+'<tr><th>报告文件类型</th><td>'+r.repType+'</td></tr>'
+'<tr><th>处理状态</th><td>'+handleStatusMap[r.status]+'</td></tr>'
+'<tr><th>检查结果</th><td>'+(r.content||'<span class="muted"></span>')+'</td></tr>'
+'<tr><th>错误信息</th><td>'+(r.msg||'<span class="muted"></span>')+'</td></tr>'
+'<tr><th>出报告时间</th><td>'+(r.reportDate||'<span class="muted"></span>')+'</td></tr>'
+'<tr><th>创建日期</th><td>'+r.createTime+'</td></tr>'
+'</tbody></table>';
document.getElementById('detailMask').classList.add('open');
}
function closeDetail(){document.getElementById('detailMask').classList.remove('open');}
document.addEventListener('keydown',function(e){
if(e.key==='Escape'){closeForm();closeDetail();}
});
function toggleSubgroup(header){
header.parentElement.classList.toggle('open');
}
(function(){
renderTable();
var active=document.querySelector('.sidebar-item.active');
if(!active)return;
var sub=active.closest('.sidebar-subgroup');
while(sub){ sub.classList.add('open'); sub=sub.parentElement.closest('.sidebar-subgroup'); }
})();
</script>
</body>
</html>
+580
View File
@@ -0,0 +1,580 @@
<!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>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
:root{
--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;
--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--background:#F5F7FA;
--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;
--radius-sm:4px;--radius-md:8px;--radius-lg:12px;
--shadow-sm:0 1px 4px rgba(0,0,0,0.08);--shadow-md:0 4px 12px rgba(0,0,0,0.12);
}
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
.top-bar__logo svg{width:28px;height:28px;}
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
.top-bar__nav a{color:rgba(255,255,255,0.65);text-decoration:none;padding:6px 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,0.08);}
.top-bar__nav a.active{color:#fff;background:var(--primary);}
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);}
.top-bar__right .btn-back{color:rgba(255,255,255,0.65);cursor:pointer;display:flex;align-items:center;gap:4px;text-decoration:none;}
.top-bar__right .btn-back:hover{color:#fff;}
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);cursor:pointer;}
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);background:rgba(255,255,255,0.04);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;transition:transform .2s;flex-shrink:0;}
.sidebar-subgroup__header .arrow path{stroke:rgba(255,255,255,0.45);}
.sidebar-subgroup.open>.sidebar-subgroup__header{color:rgba(255,255,255,0.85);}
.sidebar-subgroup.open>.sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;padding-left:16px;}
.sidebar-subgroup.open>.sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:36px;font-size:12px;}
.sidebar-subgroup .sidebar-subgroup{padding-left:12px;}
.sidebar-subgroup .sidebar-subgroup .sidebar-item{padding-left:40px;}
.sidebar-subgroup .sidebar-subgroup>.sidebar-subgroup__header{font-size:12px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.filter-bar{background:#fff;border-radius:var(--radius-md);padding:20px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:16px;flex-wrap:wrap;}
.filter-item{display:flex;align-items:center;gap:8px;}
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
.filter-input{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;min-width:190px;}
.filter-input:focus{border-color:var(--primary);}
.filter-input::placeholder{color:var(--text-placeholder);}
.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;cursor:pointer;min-width:130px;}
.filter-select:focus{border-color:var(--primary);}
.filter-select.placeholder{color:var(--text-placeholder);}
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.table-card__content{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;}
.table-card__content table{min-width:100%;white-space:nowrap;}
table{width:100%;border-collapse:collapse;}
thead th{background:#FAFAFA;padding:12px 16px;text-align:center;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;text-align:center;}
tbody tr:last-child td{border-bottom:none;}
tbody tr:hover{background:#F8FBFF;}
#infoTable thead th:last-child{position:sticky;right:0;background:#FAFAFA;border-left:1px solid var(--border);}
#infoTable tbody td:last-child{position:sticky;right:0;background:#fff;border-left:1px solid var(--border);}
#infoTable tbody tr:hover td:last-child{background:#F8FBFF;}
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);}.link:hover{color:#40a9ff;}
.link-danger{color:var(--danger);cursor:pointer;font-size:var(--font-sm);}.link-danger:hover{color:#ff7875;}
.empty-tip{padding:48px 0;text-align:center;font-size:var(--font-sm);color:var(--text-placeholder);}
.muted{color:var(--text-placeholder);}
.tag{display:inline-flex;align-items:center;padding:2px 10px;border-radius:10px;font-size:var(--font-xs);}
.tag-blue{background:#E6F7FF;color:#1890FF;}
.tag-purple{background:#F9F0FF;color:#722ED1;}
.pagination{display:flex;align-items:center;justify-content:flex-end;padding:16px 24px;gap:8px;border-top:1px solid var(--border);flex-wrap:wrap;}
.pagination span{font-size:var(--font-sm);color:var(--text-secondary);}
.page-btn{width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-sm);display:inline-flex;align-items:center;justify-content:center;transition:all .2s;color:var(--text-primary);}
.page-btn:hover:not(:disabled),.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.page-btn:disabled{color:var(--text-placeholder);cursor:not-allowed;background:#FAFAFA;}
.page-ellipsis{width:24px;text-align:center;color:var(--text-placeholder);font-size:var(--font-sm);}
.page-size{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 24px 0 10px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;appearance:none;cursor:pointer;outline:none;}
.page-jump input{width:48px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);text-align:center;font-size:var(--font-sm);outline:none;margin:0 4px;}
.page-jump input:focus{border-color:var(--primary);}
.modal-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:200;overflow-y:auto;}
.modal-mask.open{display:flex;align-items:flex-start;justify-content:center;padding:60px 24px;}
.modal{width:640px;max-width:100%;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-md);display:flex;flex-direction:column;max-height:calc(100vh - 120px);}
.modal__header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--border);flex-shrink:0;}
.modal__close{cursor:pointer;color:var(--text-secondary);display:flex;align-items:center;transition:color .2s;}
.modal__close:hover{color:var(--danger);}
.modal__title{font-size:var(--font-base);font-weight:600;}
.modal__body{padding:20px;overflow-y:auto;}
.modal__footer{display:flex;justify-content:flex-end;gap:12px;padding:14px 20px;border-top:1px solid var(--border);flex-shrink:0;}
.form-item{margin-bottom:16px;}
.form-item:last-child{margin-bottom:0;}
.form-label{font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:6px;display:flex;align-items:center;gap:4px;font-weight:500;}
.form-label .required{color:var(--danger);}
.form-input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;}
.form-input:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.2);}
.form-input::placeholder{color:var(--text-placeholder);}
.form-select{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;cursor:pointer;}
.form-select:focus{border-color:var(--primary);}
.form-textarea{width:100%;border:1px solid var(--border);border-radius:var(--radius-sm);padding:8px 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;resize:vertical;min-height:72px;font-family:inherit;}
.form-textarea:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.2);}
.detail-table{width:100%;border-collapse:collapse;border:1px solid var(--border);}
.detail-table th,.detail-table td{border-bottom:1px solid var(--border);text-align:left;padding:12px 16px;font-size:var(--font-sm);word-break:break-all;}
.detail-table tr:last-child td{border-bottom:none;}
.detail-table th{width:130px;background:#FAFAFA;color:var(--text-secondary);font-weight:500;}
</style>
</head>
<body>
<div class="top-bar">
<div class="top-bar__logo">
<svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
健康体检
</div>
<nav class="top-bar__nav">
<a href="archive-personal.html">档案</a>
<a href="config-material-notice.html">基础配置</a>
<a href="pre-hospital-scope.html">检前</a>
<a href="mid-appointment-track.html">检中</a>
<a href="post-unit-count.html" class="active">检后</a>
<a href="exception-dirty.html">异常事件</a>
</nav>
<div class="top-bar__right">
<a class="btn-back" href="../../index.html">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M10 3L5 8l5 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
返回首页
</a>
<div class="avatar"></div>
<span style="color:rgba(255,255,255,0.65);">系统管理员 ▾</span>
</div>
</div>
<aside class="sidebar">
<div class="sidebar-menu__title">检后</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
<a href="post-indicator-notify.html" class="sidebar-item">指标异常通知</a>
<a href="post-report-unmatched.html" class="sidebar-item">体检报告未匹配项目</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检统计汇总
</div>
<div class="sidebar-subgroup__body">
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
<a href="post-cancer-screening.html" class="sidebar-item">防癌筛查人数统计</a>
<a href="post-standard-stat.html" class="sidebar-item">系统标准化项目统计</a>
<a href="post-contracted-hospital.html" class="sidebar-item">签约医院人数统计</a>
<a href="post-key-indicator-stat.html" class="sidebar-item">重点指标异常统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
病种信息管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-disease-info.html" class="sidebar-item">病种信息配置</a>
<a href="post-conclusion-disease.html" class="sidebar-item">体检结论关联病种</a>
<a href="post-auto-select-disease.html" class="sidebar-item">员工病种信息</a>
</div>
</div>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item active">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<main class="main">
<div class="filter-bar">
<div class="filter-item">
<label>文件类型:</label>
<select class="filter-select placeholder" id="f-filetype" onchange="this.classList.toggle('placeholder',!this.value)">
<option value="">请选择文件类型</option>
<option value="影像">影像</option>
<option value="报告">报告</option>
</select>
</div>
<div class="filter-item">
<label>患者ID</label>
<input class="filter-input" type="text" id="f-patient" placeholder="请输入患者ID" style="min-width:160px;">
</div>
<div class="filter-item">
<label>研究实例ID</label>
<input class="filter-input" type="text" id="f-study" placeholder="请输入研究实例ID" style="min-width:220px;">
</div>
<div style="margin-left:auto;display:flex;gap:12px;">
<button class="btn btn-primary" onclick="doQuery()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4.5" stroke="#fff" stroke-width="1.4"/><path d="M9.5 9.5l3 3" stroke="#fff" stroke-width="1.4" stroke-linecap="round"/></svg>
查询
</button>
<button class="btn btn-default" onclick="doReset()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M12 7a5 5 0 1 1-1.5-3.6M12 1.5V4h-2.5" stroke="#666" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
重置
</button>
</div>
</div>
<div style="display:flex;gap:12px;margin-bottom:16px;align-items:center;">
<button class="btn btn-primary" onclick="openForm(null)">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 2v10M2 7h10" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/></svg>
新增
</button>
<button class="btn btn-default" onclick="exportList()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 2v7M4 6l3 3 3-3M2 12.5h10" stroke="#666" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
导出
</button>
<button class="btn btn-default" onclick="importList()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 9V2M4 5l3-3 3 3M2 12.5h10" stroke="#666" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
导入
</button>
<button class="btn btn-default" onclick="batchDelete()" style="margin-left:auto;">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 3.5h10M5.5 3.5V2h3v1.5M3.5 3.5l.7 8.5h5.6l.7-8.5" stroke="#666" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>
批量删除
</button>
</div>
<div class="table-card">
<div class="table-card__content">
<table id="infoTable">
<thead>
<tr>
<th style="width:48px;"><input type="checkbox" id="checkAll" onclick="toggleCheckAll(this)"></th>
<th style="width:56px;">序号</th>
<th>文件类型</th>
<th>影像序号</th>
<th>错误信息</th>
<th>文件地址</th>
<th>dcm实例ID</th>
<th>研究实例ID</th>
<th>患者ID</th>
<th>研究ID</th>
<th>创建日期</th>
<th>操作</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
</table>
</div>
<div class="pagination" id="pagination"></div>
</div>
</main>
<div class="modal-mask" id="formMask" onclick="if(event.target===this)closeForm()">
<div class="modal">
<div class="modal__header">
<span class="modal__close" onclick="closeForm()">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
</span>
<span class="modal__title" id="formTitle">新增用户影像文件详细</span>
</div>
<div class="modal__body">
<div class="form-item">
<div class="form-label">用户影像文件分类ID</div>
<input class="form-input" id="formMuicid" type="text" placeholder="请输入用户影像文件分类ID">
</div>
<div class="form-item">
<div class="form-label"><span class="required">*</span> 文件类型</div>
<select class="form-select" id="formFiletype">
<option value="影像">影像</option>
<option value="报告">报告</option>
</select>
</div>
<div class="form-item">
<div class="form-label">影像序号</div>
<input class="form-input" id="formImageno" type="text" placeholder="请输入影像序号">
</div>
<div class="form-item">
<div class="form-label">错误信息</div>
<textarea class="form-textarea" id="formMsg" placeholder="请输入错误信息"></textarea>
</div>
<div class="form-item">
<div class="form-label"><span class="required">*</span> 文件地址</div>
<input class="form-input" id="formFileurl" type="text" placeholder="请输入文件地址">
</div>
<div class="form-item">
<div class="form-label">dcm实例ID</div>
<input class="form-input" id="formInsid" type="text" placeholder="请输入dcm实例ID">
</div>
<div class="form-item">
<div class="form-label">研究实例ID</div>
<input class="form-input" id="formStudyins" type="text" placeholder="请输入研究实例ID">
</div>
<div class="form-item">
<div class="form-label">患者ID</div>
<input class="form-input" id="formPatient" type="text" placeholder="请输入患者ID">
</div>
<div class="form-item">
<div class="form-label">研究ID</div>
<input class="form-input" id="formStudyid" type="text" placeholder="请输入研究ID">
</div>
<div class="form-item">
<div class="form-label">创建日期</div>
<input class="form-input" id="formCreatetime" type="text" placeholder="如 2026-08-15 11:30:00">
</div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeForm()">取消</button>
<button class="btn btn-primary" onclick="saveForm()">保存</button>
</div>
</div>
</div>
<div class="modal-mask" id="detailMask" onclick="if(event.target===this)closeDetail()">
<div class="modal">
<div class="modal__header">
<span class="modal__close" onclick="closeDetail()">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
</span>
<span class="modal__title">用户影像文件详细</span>
</div>
<div class="modal__body" id="detailBody"></div>
</div>
</div>
<script>
var rows=[
{fileType:'影像',imageNo:'1',msg:'',fileUrl:'/files/imc/PE20260001/chest/1.dcm',insId:'1.2.840.113619.6.55.201.1',studyInsId:'1.2.840.113619.6.55.201',patientId:'PAT0001',studyId:'1.2.840.113619.6.55.201',createTime:'2026-08-15 11:30:00',muicId:'MC1001'},
{fileType:'影像',imageNo:'2',msg:'',fileUrl:'/files/imc/PE20260001/chest/2.dcm',insId:'1.2.840.113619.6.55.202.1',studyInsId:'1.2.840.113619.6.55.202',patientId:'PAT0001',studyId:'1.2.840.113619.6.55.202',createTime:'2026-08-15 11:30:10',muicId:'MC1001'},
{fileType:'报告',imageNo:'1',msg:'',fileUrl:'/files/imc/PE20260001/chest/report.pdf',insId:'',studyInsId:'1.2.840.113619.6.55.201',patientId:'PAT0001',studyId:'1.2.840.113619.6.55.201',createTime:'2026-08-15 12:00:00',muicId:'MC1001'},
{fileType:'影像',imageNo:'1',msg:'',fileUrl:'/files/imc/PE20260001/mri/1.dcm',insId:'1.2.840.113619.6.55.203.1',studyInsId:'1.2.840.113619.6.55.203',patientId:'PAT0001',studyId:'1.2.840.113619.6.55.203',createTime:'2026-08-15 12:30:00',muicId:'MC1002'},
{fileType:'报告',imageNo:'1',msg:'',fileUrl:'/files/imc/PE20260001/mri/report.pdf',insId:'',studyInsId:'1.2.840.113619.6.55.203',patientId:'PAT0001',studyId:'1.2.840.113619.6.55.203',createTime:'2026-08-15 13:10:00',muicId:'MC1002'},
{fileType:'报告',imageNo:'1',msg:'',fileUrl:'/files/imc/PE20260002/chest/report.pdf',insId:'',studyInsId:'1.2.840.113619.6.55.301',patientId:'PAT0002',studyId:'1.2.840.113619.6.55.301',createTime:'2026-08-15 15:20:00',muicId:'MC1004'},
{fileType:'影像',imageNo:'1',msg:'DICOM标签解析失败:缺少 PatientID 标签',fileUrl:'/files/imc/PE20260003/xray/1.dcm',insId:'1.2.840.113619.6.55.401.1',studyInsId:'1.2.840.113619.6.55.401',patientId:'',studyId:'1.2.840.113619.6.55.401',createTime:'2026-08-15 16:00:00',muicId:'MC1006'},
{fileType:'报告',imageNo:'1',msg:'',fileUrl:'/files/imc/PE20260002/ecg/report.jpg',insId:'',studyInsId:'1.2.840.113619.6.55.302',patientId:'PAT0002',studyId:'1.2.840.113619.6.55.302',createTime:'2026-08-15 15:40:00',muicId:'MC1005'},
{fileType:'影像',imageNo:'1',msg:'',fileUrl:'/files/imc/PE20250018/chest/1.dcm',insId:'1.2.840.113619.6.55.501.1',studyInsId:'1.2.840.113619.6.55.501',patientId:'PAT0005',studyId:'1.2.840.113619.6.55.501',createTime:'2025-08-20 15:30:00',muicId:'MC1009'},
{fileType:'报告',imageNo:'1',msg:'',fileUrl:'/files/imc/PE20250018/chest/report.pdf',insId:'',studyInsId:'1.2.840.113619.6.55.501',patientId:'PAT0005',studyId:'1.2.840.113619.6.55.501',createTime:'2025-08-20 16:00:00',muicId:'MC1009'},
{fileType:'报告',imageNo:'1',msg:'报告文件格式与声明类型不一致',fileUrl:'/files/imc/PE20250021/us/report.png',insId:'',studyInsId:'1.2.840.113619.6.55.602',patientId:'PAT0006',studyId:'1.2.840.113619.6.55.602',createTime:'2025-08-21 11:00:00',muicId:'MC1012'}
];
var state={filetype:'',patient:'',study:'',page:1,pageSize:10};
function fileTypeTag(t){
return t==='影像'?'<span class="tag tag-blue">影像</span>':'<span class="tag tag-purple">报告</span>';
}
function doQuery(){
state.filetype=document.getElementById('f-filetype').value;
state.patient=document.getElementById('f-patient').value.trim();
state.study=document.getElementById('f-study').value.trim();
state.page=1;
renderTable();
}
function doReset(){
var ft=document.getElementById('f-filetype');ft.value='';ft.classList.add('placeholder');
document.getElementById('f-patient').value='';
document.getElementById('f-study').value='';
state.filetype='';state.patient='';state.study='';
state.page=1;
renderTable();
}
function filteredRows(){
return rows.filter(function(r){
if(state.filetype&&r.fileType!==state.filetype)return false;
if(state.patient&&r.patientId.indexOf(state.patient)===-1)return false;
if(state.study&&r.studyInsId.indexOf(state.study)===-1)return false;
return true;
});
}
function renderTable(){
var list=filteredRows();
var totalPages=Math.max(1,Math.ceil(list.length/state.pageSize));
if(state.page>totalPages)state.page=totalPages;
var start=(state.page-1)*state.pageSize;
var pageRows=list.slice(start,start+state.pageSize);
var tbody=document.getElementById('tbody');
tbody.innerHTML=pageRows.length?pageRows.map(function(r,pos){
var idx=rows.indexOf(r);
var msg=r.msg?r.msg:'<span class="muted"></span>';
if(r.msg&&r.msg.length>18)msg='<span title="'+r.msg+'">'+r.msg.slice(0,18)+'…</span>';
return '<tr>'
+'<td><input type="checkbox" class="row-check" data-idx="'+idx+'"></td>'
+'<td>'+(start+pos+1)+'</td>'
+'<td>'+fileTypeTag(r.fileType)+'</td><td>'+r.imageNo+'</td><td style="text-align:left;">'+msg+'</td>'
+'<td><span class="link" onclick="downloadFile(\''+r.fileUrl+'\')">下载</span></td>'
+'<td>'+(r.insId||'<span class="muted"></span>')+'</td><td>'+r.studyInsId+'</td><td>'+(r.patientId||'<span class="muted"></span>')+'</td>'
+'<td>'+r.studyId+'</td><td>'+r.createTime+'</td>'
+'<td><span class="link" onclick="openForm('+idx+')">编辑</span> <span class="link" style="margin-left:8px;" onclick="openDetail('+idx+')">详情</span> <span class="link-danger" style="margin-left:8px;" onclick="deleteRow('+idx+')">删除</span></td>'
+'</tr>';
}).join(''):'<tr><td colspan="12"><div class="empty-tip">暂无数据</div></td></tr>';
var all=tbody.querySelectorAll('.row-check');
var checked=tbody.querySelectorAll('.row-check:checked');
document.getElementById('checkAll').checked=all.length>0&&all.length===checked.length;
renderPagination(list.length,totalPages);
}
function pageSeq(p,last){
var seq=[];
if(last<=7){for(var i=1;i<=last;i++)seq.push(i);return seq;}
if(p<=4){seq=[1,2,3,4,5,'...',last];}
else if(p>=last-3){seq=[1,'...',last-4,last-3,last-2,last-1,last];}
else{seq=[1,'...',p-1,p,p+1,'...',last];}
return seq;
}
function renderPagination(total,totalPages){
var html='<span>共 '+total+' 条数据</span>';
if(totalPages>1){
html+='<button class="page-btn"'+(state.page===1?' disabled':'')+' onclick="goPage('+(state.page-1)+')">&lt;</button>';
pageSeq(state.page,totalPages).forEach(function(p){
if(p==='...')html+='<span class="page-ellipsis">...</span>';
else html+='<button class="page-btn'+(p===state.page?' active':'')+'" onclick="goPage('+p+')">'+p+'</button>';
});
html+='<button class="page-btn"'+(state.page===totalPages?' disabled':'')+' onclick="goPage('+(state.page+1)+')">&gt;</button>';
}
html+='<select class="page-size" onchange="changeSize(this.value)">'
+[10,20,50].map(function(n){return '<option value="'+n+'"'+(n===state.pageSize?' selected':'')+'>'+n+'条/页</option>';}).join('')
+'</select>';
html+='<span class="page-jump">跳至<input type="number" id="jumpInput" min="1" value="'+state.page+'" onkeydown="if(event.key===\'Enter\')jumpTo('+totalPages+')"><button class="page-btn" onclick="jumpTo('+totalPages+')"></button></span>';
document.getElementById('pagination').innerHTML=html;
}
function goPage(p){state.page=p;renderTable();}
function changeSize(v){state.pageSize=parseInt(v,10);state.page=1;renderTable();}
function jumpTo(totalPages){
var input=document.getElementById('jumpInput');
var p=parseInt(input.value,10);
if(isNaN(p)||p<1)p=1;
if(p>totalPages)p=totalPages;
state.page=p;
renderTable();
}
function downloadFile(url){
alert('下载文件:'+url);
}
function toggleCheckAll(cb){
document.querySelectorAll('.row-check').forEach(function(c){c.checked=cb.checked;});
}
function checkedIdx(){
var arr=[];
document.querySelectorAll('.row-check:checked').forEach(function(c){arr.push(parseInt(c.dataset.idx,10));});
return arr;
}
function batchDelete(){
var idxs=checkedIdx();
if(!idxs.length){alert('请先勾选要删除的记录');return;}
if(!confirm('确认删除选中的 '+idxs.length+' 条记录?'))return;
idxs.sort(function(a,b){return b-a;}).forEach(function(i){rows.splice(i,1);});
renderTable();
}
function deleteRow(idx){
if(!confirm('确认删除该影像文件记录?'))return;
rows.splice(idx,1);
renderTable();
}
function exportList(){alert('导出当前筛选结果(Excel');}
function importList(){alert('导入影像文件详细数据(Excel');}
var editingIdx=null;
function openForm(idx){
editingIdx=idx;
document.getElementById('formTitle').textContent=idx===null?'新增用户影像文件详细':'编辑用户影像文件详细';
if(idx===null){
document.getElementById('formMuicid').value='';
document.getElementById('formFiletype').value='影像';
document.getElementById('formImageno').value='';
document.getElementById('formMsg').value='';
document.getElementById('formFileurl').value='';
document.getElementById('formInsid').value='';
document.getElementById('formStudyins').value='';
document.getElementById('formPatient').value='';
document.getElementById('formStudyid').value='';
document.getElementById('formCreatetime').value='';
}else{
var r=rows[idx];
document.getElementById('formMuicid').value=r.muicId;
document.getElementById('formFiletype').value=r.fileType;
document.getElementById('formImageno').value=r.imageNo;
document.getElementById('formMsg').value=r.msg;
document.getElementById('formFileurl').value=r.fileUrl;
document.getElementById('formInsid').value=r.insId;
document.getElementById('formStudyins').value=r.studyInsId;
document.getElementById('formPatient').value=r.patientId;
document.getElementById('formStudyid').value=r.studyId;
document.getElementById('formCreatetime').value=r.createTime;
}
document.getElementById('formMask').classList.add('open');
}
function closeForm(){document.getElementById('formMask').classList.remove('open');}
function saveForm(){
var fileUrl=document.getElementById('formFileurl').value.trim();
if(!fileUrl){alert('请输入文件地址');return;}
var data={
muicId:document.getElementById('formMuicid').value.trim(),
fileType:document.getElementById('formFiletype').value,
imageNo:document.getElementById('formImageno').value.trim(),
msg:document.getElementById('formMsg').value.trim(),
fileUrl:fileUrl,
insId:document.getElementById('formInsid').value.trim(),
studyInsId:document.getElementById('formStudyins').value.trim(),
patientId:document.getElementById('formPatient').value.trim(),
studyId:document.getElementById('formStudyid').value.trim(),
createTime:document.getElementById('formCreatetime').value.trim()
};
if(editingIdx===null){
rows.unshift(data);
}else{
rows[editingIdx]=data;
}
closeForm();
renderTable();
}
function openDetail(idx){
var r=rows[idx];
document.getElementById('detailBody').innerHTML=
'<table class="detail-table"><tbody>'
+'<tr><th>用户影像文件分类ID</th><td>'+r.muicId+'</td></tr>'
+'<tr><th>文件类型</th><td>'+r.fileType+'</td></tr>'
+'<tr><th>影像序号</th><td>'+r.imageNo+'</td></tr>'
+'<tr><th>错误信息</th><td>'+(r.msg||'<span class="muted"></span>')+'</td></tr>'
+'<tr><th>文件地址</th><td>'+r.fileUrl+'</td></tr>'
+'<tr><th>dcm实例ID</th><td>'+(r.insId||'<span class="muted"></span>')+'</td></tr>'
+'<tr><th>研究实例ID</th><td>'+r.studyInsId+'</td></tr>'
+'<tr><th>患者ID</th><td>'+(r.patientId||'<span class="muted"></span>')+'</td></tr>'
+'<tr><th>研究ID</th><td>'+r.studyId+'</td></tr>'
+'<tr><th>创建日期</th><td>'+r.createTime+'</td></tr>'
+'</tbody></table>';
document.getElementById('detailMask').classList.add('open');
}
function closeDetail(){document.getElementById('detailMask').classList.remove('open');}
document.addEventListener('keydown',function(e){
if(e.key==='Escape'){closeForm();closeDetail();}
});
function toggleSubgroup(header){
header.parentElement.classList.toggle('open');
}
(function(){
renderTable();
var active=document.querySelector('.sidebar-item.active');
if(!active)return;
var sub=active.closest('.sidebar-subgroup');
while(sub){ sub.classList.add('open'); sub=sub.parentElement.closest('.sidebar-subgroup'); }
})();
</script>
</body>
</html>
+520
View File
@@ -0,0 +1,520 @@
<!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>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
:root{
--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;
--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--background:#F5F7FA;
--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;
--radius-sm:4px;--radius-md:8px;--radius-lg:12px;
--shadow-sm:0 1px 4px rgba(0,0,0,0.08);--shadow-md:0 4px 12px rgba(0,0,0,0.12);
}
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
.top-bar__logo svg{width:28px;height:28px;}
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
.top-bar__nav a{color:rgba(255,255,255,0.65);text-decoration:none;padding:6px 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,0.08);}
.top-bar__nav a.active{color:#fff;background:var(--primary);}
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);}
.top-bar__right .btn-back{color:rgba(255,255,255,0.65);cursor:pointer;display:flex;align-items:center;gap:4px;text-decoration:none;}
.top-bar__right .btn-back:hover{color:#fff;}
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);cursor:pointer;}
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);background:rgba(255,255,255,0.04);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;transition:transform .2s;flex-shrink:0;}
.sidebar-subgroup__header .arrow path{stroke:rgba(255,255,255,0.45);}
.sidebar-subgroup.open>.sidebar-subgroup__header{color:rgba(255,255,255,0.85);}
.sidebar-subgroup.open>.sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;padding-left:16px;}
.sidebar-subgroup.open>.sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:36px;font-size:12px;}
.sidebar-subgroup .sidebar-subgroup{padding-left:12px;}
.sidebar-subgroup .sidebar-subgroup .sidebar-item{padding-left:40px;}
.sidebar-subgroup .sidebar-subgroup>.sidebar-subgroup__header{font-size:12px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.filter-bar{background:#fff;border-radius:var(--radius-md);padding:20px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:16px;flex-wrap:wrap;}
.filter-item{display:flex;align-items:center;gap:8px;}
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
.filter-input{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;min-width:160px;}
.filter-input:focus{border-color:var(--primary);}
.filter-input::placeholder{color:var(--text-placeholder);}
.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;cursor:pointer;min-width:130px;}
.filter-select:focus{border-color:var(--primary);}
.filter-select.placeholder{color:var(--text-placeholder);}
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
.year-picker{position:relative;}
.year-picker .filter-input{padding-right:30px;cursor:pointer;min-width:100px;}
.year-picker>svg{position:absolute;right:9px;top:50%;transform:translateY(-50%);pointer-events:none;}
.year-picker__dropdown{display:none;position:absolute;top:40px;left:0;width:100px;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);z-index:50;max-height:180px;overflow-y:auto;}
.year-picker__dropdown.open{display:block;}
.year-picker__dropdown div{padding:8px 14px;font-size:var(--font-sm);cursor:pointer;}
.year-picker__dropdown div:hover{background:var(--primary-light);}
.year-picker__dropdown div.selected{color:var(--primary);font-weight:600;}
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.table-card__content{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;}
.table-card__content table{min-width:100%;white-space:nowrap;}
table{width:100%;border-collapse:collapse;}
thead th{background:#FAFAFA;padding:12px 16px;text-align:center;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;text-align:center;}
tbody tr:last-child td{border-bottom:none;}
tbody tr:hover{background:#F8FBFF;}
#logTable thead th:last-child{position:sticky;right:0;background:#FAFAFA;border-left:1px solid var(--border);}
#logTable tbody td:last-child{position:sticky;right:0;background:#fff;border-left:1px solid var(--border);}
#logTable tbody tr:hover td:last-child{background:#F8FBFF;}
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);}.link:hover{color:#40a9ff;}
.link-danger{color:var(--danger);cursor:pointer;font-size:var(--font-sm);}.link-danger:hover{color:#ff7875;}
.empty-tip{padding:48px 0;text-align:center;font-size:var(--font-sm);color:var(--text-placeholder);}
.muted{color:var(--text-placeholder);}
.mono{font-family:Consolas,Menlo,monospace;font-size:var(--font-xs);color:var(--text-secondary);}
.tag{display:inline-flex;align-items:center;padding:2px 10px;border-radius:10px;font-size:var(--font-xs);}
.tag-blue{background:#E6F7FF;color:#1890FF;}
.tag-green{background:#F6FFED;color:#52C41A;}
.tag-orange{background:#FFF7E6;color:#FA8C16;}
.tag-red{background:#FFF1F0;color:#FF4D4F;}
.tag-gray{background:#FAFAFA;color:#999;}
.pagination{display:flex;align-items:center;justify-content:flex-end;padding:16px 24px;gap:8px;border-top:1px solid var(--border);flex-wrap:wrap;}
.pagination span{font-size:var(--font-sm);color:var(--text-secondary);}
.page-btn{width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-sm);display:inline-flex;align-items:center;justify-content:center;transition:all .2s;color:var(--text-primary);}
.page-btn:hover:not(:disabled),.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.page-btn:disabled{color:var(--text-placeholder);cursor:not-allowed;background:#FAFAFA;}
.page-ellipsis{width:24px;text-align:center;color:var(--text-placeholder);font-size:var(--font-sm);}
.page-size{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 24px 0 10px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;appearance:none;cursor:pointer;outline:none;}
.page-jump input{width:48px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);text-align:center;font-size:var(--font-sm);outline:none;margin:0 4px;}
.page-jump input:focus{border-color:var(--primary);}
.modal-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:200;overflow-y:auto;}
.modal-mask.open{display:flex;align-items:flex-start;justify-content:center;padding:60px 24px;}
.modal{width:680px;max-width:100%;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-md);display:flex;flex-direction:column;max-height:calc(100vh - 120px);}
.modal__header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--border);flex-shrink:0;}
.modal__close{cursor:pointer;color:var(--text-secondary);display:flex;align-items:center;transition:color .2s;}
.modal__close:hover{color:var(--danger);}
.modal__title{font-size:var(--font-base);font-weight:600;}
.modal__body{padding:20px;overflow-y:auto;}
.detail-table{width:100%;border-collapse:collapse;border:1px solid var(--border);}
.detail-table th,.detail-table td{border-bottom:1px solid var(--border);text-align:left;padding:12px 16px;font-size:var(--font-sm);word-break:break-all;}
.detail-table tr:last-child td{border-bottom:none;}
.detail-table th{width:130px;background:#FAFAFA;color:var(--text-secondary);font-weight:500;}
.json-box{background:#FAFAFA;border:1px solid var(--border);border-radius:var(--radius-sm);padding:12px;font-family:Consolas,Menlo,monospace;font-size:var(--font-xs);color:var(--text-secondary);white-space:pre-wrap;word-break:break-all;max-height:200px;overflow-y:auto;}
</style>
</head>
<body>
<div class="top-bar">
<div class="top-bar__logo">
<svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
健康体检
</div>
<nav class="top-bar__nav">
<a href="archive-personal.html">档案</a>
<a href="config-material-notice.html">基础配置</a>
<a href="pre-hospital-scope.html">检前</a>
<a href="mid-appointment-track.html">检中</a>
<a href="post-unit-count.html" class="active">检后</a>
<a href="exception-dirty.html">异常事件</a>
</nav>
<div class="top-bar__right">
<a class="btn-back" href="../../index.html">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M10 3L5 8l5 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
返回首页
</a>
<div class="avatar"></div>
<span style="color:rgba(255,255,255,0.65);">系统管理员 ▾</span>
</div>
</div>
<aside class="sidebar">
<div class="sidebar-menu__title">检后</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
<a href="post-indicator-notify.html" class="sidebar-item">指标异常通知</a>
<a href="post-report-unmatched.html" class="sidebar-item">体检报告未匹配项目</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检统计汇总
</div>
<div class="sidebar-subgroup__body">
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
<a href="post-cancer-screening.html" class="sidebar-item">防癌筛查人数统计</a>
<a href="post-standard-stat.html" class="sidebar-item">系统标准化项目统计</a>
<a href="post-contracted-hospital.html" class="sidebar-item">签约医院人数统计</a>
<a href="post-key-indicator-stat.html" class="sidebar-item">重点指标异常统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
病种信息管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-disease-info.html" class="sidebar-item">病种信息配置</a>
<a href="post-conclusion-disease.html" class="sidebar-item">体检结论关联病种</a>
<a href="post-auto-select-disease.html" class="sidebar-item">员工病种信息</a>
</div>
</div>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item active">文件处理日志</a>
</div>
</div>
</aside>
<main class="main">
<div class="filter-bar">
<div class="filter-item">
<label>身份证号:</label>
<input class="filter-input" type="text" id="f-idcard" placeholder="请输入身份证号" style="min-width:170px;">
</div>
<div class="filter-item">
<label>体检年份:</label>
<div class="year-picker">
<input class="filter-input" type="text" readonly value="2026" id="f-year">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="2.5" width="11" height="10" rx="1.5" stroke="#999" stroke-width="1.2"/><path d="M1.5 5.5h11M4.5 1v3M9.5 1v3" stroke="#999" stroke-width="1.2" stroke-linecap="round"/></svg>
<div class="year-picker__dropdown" id="yearDropdown">
<div class="selected" onclick="pickYear(this)">2026</div>
<div onclick="pickYear(this)">2025</div>
<div onclick="pickYear(this)">2024</div>
</div>
</div>
</div>
<div class="filter-item">
<label>体检编号:</label>
<input class="filter-input" type="text" id="f-peid" placeholder="请输入体检编号" style="min-width:160px;">
</div>
<div class="filter-item">
<label>影像分类:</label>
<select class="filter-select placeholder" id="f-imgclass" onchange="this.classList.toggle('placeholder',!this.value)">
<option value="">请选择影像分类</option>
<option value="超声">超声</option>
<option value="CT">CT</option>
<option value="MRI">MRI</option>
<option value="X射线">X射线</option>
<option value="心电">心电</option>
</select>
</div>
<div class="filter-item">
<label>处理状态:</label>
<select class="filter-select placeholder" id="f-status" onchange="this.classList.toggle('placeholder',!this.value)">
<option value="">请选择处理状态</option>
<option value="1">新建</option>
<option value="2">待解析</option>
<option value="4">解析中</option>
<option value="5">成功</option>
<option value="6">失败待重试</option>
<option value="3">失败</option>
<option value="7">最终失败</option>
</select>
</div>
<div style="margin-left:auto;display:flex;gap:12px;">
<button class="btn btn-primary" onclick="doQuery()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4.5" stroke="#fff" stroke-width="1.4"/><path d="M9.5 9.5l3 3" stroke="#fff" stroke-width="1.4" stroke-linecap="round"/></svg>
查询
</button>
<button class="btn btn-default" onclick="doReset()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M12 7a5 5 0 1 1-1.5-3.6M12 1.5V4h-2.5" stroke="#666" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
重置
</button>
</div>
</div>
<!-- 操作栏 -->
<div style="display:flex;gap:12px;margin-bottom:16px;align-items:center;">
<button class="btn btn-default" onclick="batchDelete()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 3.5h10M5.5 3.5V2h3v1.5M3.5 3.5l.7 8.5h5.6l.7-8.5" stroke="#666" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>
批量删除
</button>
</div>
<div class="table-card">
<div class="table-card__content">
<table id="logTable">
<thead>
<tr>
<th style="width:48px;"><input type="checkbox" id="checkAll" onclick="toggleCheckAll(this)"></th>
<th style="width:56px;">序号</th>
<th>医院名称</th>
<th>姓名</th>
<th>身份证号</th>
<th>体检年份</th>
<th>体检编号</th>
<th>影像分类</th>
<th>json数据</th>
<th>文件地址</th>
<th>文件大小(MB</th>
<th>处理状态</th>
<th>错误信息</th>
<th>传输开始时间</th>
<th>传输完成时间</th>
<th>解析开始时间</th>
<th>解析完成时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
</table>
</div>
<div class="pagination" id="pagination"></div>
</div>
</main>
<div class="modal-mask" id="detailMask" onclick="if(event.target===this)closeDetail()">
<div class="modal">
<div class="modal__header">
<span class="modal__close" onclick="closeDetail()">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
</span>
<span class="modal__title">文件处理日志详情</span>
</div>
<div class="modal__body" id="detailBody"></div>
</div>
</div>
<script>
var handleStatusMap={1:'新建',2:'待解析',3:'失败',4:'解析中',5:'成功',6:'失败待重试',7:'最终失败'};
function statusTag(s){
var cls='tag-gray';
if(s===5)cls='tag-green';
else if(s===4||s===6)cls='tag-orange';
else if(s===3||s===7)cls='tag-red';
else if(s===1)cls='tag-blue';
return '<span class="tag '+cls+'">'+handleStatusMap[s]+'</span>';
}
/* 按传输开始时间倒序 */
var rows=[
{hospitalName:'重庆市人民医院',realname:'张伟',idCard:'500103197805121234',medicalYear:'2026',peId:'PE20260001',imgClass:'CT',jsonContent:'{"hospitalId":"H001","hospitalName":"重庆市人民医院","peId":"PE20260001","items":[{"name":"胸部CT平扫","type":"CT"},{"name":"头颅MRI平扫","type":"MRI"},{"name":"腹部超声","type":"超声"}]}',zipFileUrl:'/files/imc/PE20260001.zip',zipSize:128.5,status:5,msg:'',startTime:'2026-08-15 10:00:00',endTime:'2026-08-15 10:02:15',parseStartTime:'2026-08-15 10:05:00',parseEndTime:'2026-08-15 12:00:00'},
{hospitalName:'重庆医科大学附属第一医院',realname:'李芳',idCard:'500107198812053467',medicalYear:'2026',peId:'PE20260002',imgClass:'CT',jsonContent:'{"hospitalId":"H002","hospitalName":"重庆医科大学附属第一医院","peId":"PE20260002","items":[{"name":"胸部CT平扫","type":"CT"},{"name":"心电图","type":"心电"}]}',zipFileUrl:'/files/imc/PE20260002.zip',zipSize:96.2,status:5,msg:'',startTime:'2026-08-15 11:00:00',endTime:'2026-08-15 11:01:30',parseStartTime:'2026-08-15 11:05:00',parseEndTime:'2026-08-15 13:00:00'},
{hospitalName:'陆军军医大学西南医院',realname:'王强',idCard:'500105199203154567',medicalYear:'2026',peId:'PE20260003',imgClass:'X射线',jsonContent:'{"hospitalId":"H003","hospitalName":"陆军军医大学西南医院","peId":"PE20260003","items":[{"name":"胸部X射线","type":"X射线"},{"name":"腹部超声","type":"超声"}]}',zipFileUrl:'/files/imc/PE20260003.zip',zipSize:64.8,status:7,msg:'DICOM标签解析失败:缺少 PatientID 标签',startTime:'2026-08-15 13:40:00',endTime:'2026-08-15 13:42:10',parseStartTime:'2026-08-15 13:45:00',parseEndTime:'2026-08-15 14:10:00'},
{hospitalName:'陆军特色医学中心',realname:'赵敏',idCard:'500104199001011234',medicalYear:'2026',peId:'PE20260004',imgClass:'CT',jsonContent:'{"hospitalId":"H004","hospitalName":"陆军特色医学中心","peId":"PE20260004","items":[{"name":"冠脉CTA","type":"CT"}]}',zipFileUrl:'/files/imc/PE20260004.zip',zipSize:210.3,status:2,msg:'',startTime:'2026-08-16 09:10:00',endTime:'2026-08-16 09:12:00',parseStartTime:'',parseEndTime:''},
{hospitalName:'重庆市人民医院',realname:'陈刚',idCard:'500106198503094567',medicalYear:'2025',peId:'PE20250018',imgClass:'CT',jsonContent:'{"hospitalId":"H001","hospitalName":"重庆市人民医院","peId":"PE20250018","items":[{"name":"胸部CT平扫","type":"CT"},{"name":"头颅MRI平扫","type":"MRI"},{"name":"腹部超声","type":"超声"},{"name":"心电图","type":"心电"}]}',zipFileUrl:'/files/imc/PE20250018.zip',zipSize:152.7,status:5,msg:'',startTime:'2025-08-20 15:20:00',endTime:'2025-08-20 15:23:00',parseStartTime:'2025-08-20 15:30:00',parseEndTime:'2025-08-20 17:00:00'},
{hospitalName:'重庆医科大学附属第一医院',realname:'刘洋',idCard:'500102199511223456',medicalYear:'2025',peId:'PE20250021',imgClass:'超声',jsonContent:'{"hospitalId":"H002","hospitalName":"重庆医科大学附属第一医院","peId":"PE20250021","items":[{"name":"胸部CT平扫","type":"CT"},{"name":"腹部超声","type":"超声"}]}',zipFileUrl:'/files/imc/PE20250021.zip',zipSize:88.9,status:7,msg:'报告文件格式与声明类型不一致',startTime:'2025-08-21 10:00:00',endTime:'2025-08-21 10:02:00',parseStartTime:'2025-08-21 10:10:00',parseEndTime:'2025-08-21 11:00:00'}
];
var state={idcard:'',year:'2026',peid:'',imgclass:'',status:'',page:1,pageSize:10};
function toggleYear(){document.getElementById('yearDropdown').classList.toggle('open');}
function pickYear(el){
document.getElementById('f-year').value=el.textContent.trim();
document.getElementById('yearDropdown').classList.remove('open');
document.querySelectorAll('#yearDropdown div').forEach(function(d){d.classList.remove('selected');});
el.classList.add('selected');
}
document.getElementById('f-year').addEventListener('click',toggleYear);
document.addEventListener('click',function(e){
if(!e.target.closest('.year-picker'))document.getElementById('yearDropdown').classList.remove('open');
});
function doQuery(){
state.idcard=document.getElementById('f-idcard').value.trim();
state.year=document.getElementById('f-year').value.trim();
state.peid=document.getElementById('f-peid').value.trim();
state.imgclass=document.getElementById('f-imgclass').value;
state.status=document.getElementById('f-status').value;
state.page=1;
renderTable();
}
function doReset(){
document.getElementById('f-idcard').value='';
document.getElementById('f-year').value='2026';
document.querySelectorAll('#yearDropdown div').forEach(function(d){
d.classList.toggle('selected',d.textContent.trim()==='2026');
});
document.getElementById('f-peid').value='';
var ic=document.getElementById('f-imgclass');ic.value='';ic.classList.add('placeholder');
var st=document.getElementById('f-status');st.value='';st.classList.add('placeholder');
state.idcard='';state.year='2026';state.peid='';state.imgclass='';state.status='';
state.page=1;
renderTable();
}
function filteredRows(){
return rows.filter(function(r){
if(state.idcard&&r.idCard.indexOf(state.idcard)===-1)return false;
if(state.year&&r.medicalYear!==state.year)return false;
if(state.peid&&r.peId.indexOf(state.peid)===-1)return false;
if(state.imgclass&&r.imgClass!==state.imgclass)return false;
if(state.status&&String(r.status)!==state.status)return false;
return true;
});
}
function renderTable(){
var list=filteredRows();
var totalPages=Math.max(1,Math.ceil(list.length/state.pageSize));
if(state.page>totalPages)state.page=totalPages;
var start=(state.page-1)*state.pageSize;
var pageRows=list.slice(start,start+state.pageSize);
var tbody=document.getElementById('tbody');
tbody.innerHTML=pageRows.length?pageRows.map(function(r,pos){
var idx=rows.indexOf(r);
var json=r.jsonContent;
if(json.length>24)json='<span class="mono" title="'+r.jsonContent.replace(/"/g,'&quot;')+'">'+r.jsonContent.slice(0,24)+'…</span>';
else json='<span class="mono">'+json+'</span>';
var msg=r.msg?r.msg:'<span class="muted"></span>';
if(r.msg&&r.msg.length>18)msg='<span title="'+r.msg+'">'+r.msg.slice(0,18)+'…</span>';
return '<tr>'
+'<td><input type="checkbox" class="row-check" data-idx="'+idx+'"></td>'
+'<td>'+(start+pos+1)+'</td>'
+'<td>'+r.hospitalName+'</td><td>'+r.realname+'</td><td>'+r.idCard+'</td><td>'+r.medicalYear+'</td>'
+'<td>'+r.peId+'</td><td>'+r.imgClass+'</td><td>'+json+'</td>'
+'<td><span class="link" onclick="downloadFile(\''+r.zipFileUrl+'\')">下载</span></td>'
+'<td>'+r.zipSize+'</td><td>'+statusTag(r.status)+'</td><td style="text-align:left;">'+msg+'</td>'
+'<td>'+r.startTime+'</td><td>'+r.endTime+'</td><td>'+(r.parseStartTime||'<span class="muted"></span>')+'</td><td>'+(r.parseEndTime||'<span class="muted"></span>')+'</td>'
+'<td><span class="link" onclick="openDetail('+idx+')">详情</span> <span class="link-danger" style="margin-left:8px;" onclick="deleteRow('+idx+')">删除</span></td>'
+'</tr>';
}).join(''):'<tr><td colspan="18"><div class="empty-tip">暂无数据</div></td></tr>';
var all=tbody.querySelectorAll('.row-check');
var checked=tbody.querySelectorAll('.row-check:checked');
document.getElementById('checkAll').checked=all.length>0&&all.length===checked.length;
renderPagination(list.length,totalPages);
}
function pageSeq(p,last){
var seq=[];
if(last<=7){for(var i=1;i<=last;i++)seq.push(i);return seq;}
if(p<=4){seq=[1,2,3,4,5,'...',last];}
else if(p>=last-3){seq=[1,'...',last-4,last-3,last-2,last-1,last];}
else{seq=[1,'...',p-1,p,p+1,'...',last];}
return seq;
}
function renderPagination(total,totalPages){
var html='<span>共 '+total+' 条数据</span>';
if(totalPages>1){
html+='<button class="page-btn"'+(state.page===1?' disabled':'')+' onclick="goPage('+(state.page-1)+')">&lt;</button>';
pageSeq(state.page,totalPages).forEach(function(p){
if(p==='...')html+='<span class="page-ellipsis">...</span>';
else html+='<button class="page-btn'+(p===state.page?' active':'')+'" onclick="goPage('+p+')">'+p+'</button>';
});
html+='<button class="page-btn"'+(state.page===totalPages?' disabled':'')+' onclick="goPage('+(state.page+1)+')">&gt;</button>';
}
html+='<select class="page-size" onchange="changeSize(this.value)">'
+[10,20,50].map(function(n){return '<option value="'+n+'"'+(n===state.pageSize?' selected':'')+'>'+n+'条/页</option>';}).join('')
+'</select>';
html+='<span class="page-jump">跳至<input type="number" id="jumpInput" min="1" value="'+state.page+'" onkeydown="if(event.key===\'Enter\')jumpTo('+totalPages+')"><button class="page-btn" onclick="jumpTo('+totalPages+')"></button></span>';
document.getElementById('pagination').innerHTML=html;
}
function goPage(p){state.page=p;renderTable();}
function changeSize(v){state.pageSize=parseInt(v,10);state.page=1;renderTable();}
function jumpTo(totalPages){
var input=document.getElementById('jumpInput');
var p=parseInt(input.value,10);
if(isNaN(p)||p<1)p=1;
if(p>totalPages)p=totalPages;
state.page=p;
renderTable();
}
function downloadFile(url){
alert('下载文件:'+url);
}
function deleteRow(idx){
if(!confirm('确认删除该文件处理日志记录?'))return;
rows.splice(idx,1);
renderTable();
}
function toggleCheckAll(cb){
document.querySelectorAll('.row-check').forEach(function(c){c.checked=cb.checked;});
}
function checkedIdx(){
var arr=[];
document.querySelectorAll('.row-check:checked').forEach(function(c){arr.push(parseInt(c.dataset.idx,10));});
return arr;
}
function batchDelete(){
var idxs=checkedIdx();
if(!idxs.length){alert('请先勾选要删除的记录');return;}
if(!confirm('确认删除选中的 '+idxs.length+' 条记录?'))return;
idxs.sort(function(a,b){return b-a;}).forEach(function(i){rows.splice(i,1);});
renderTable();
}
function openDetail(idx){
var r=rows[idx];
document.getElementById('detailBody').innerHTML=
'<table class="detail-table"><tbody>'
+'<tr><th>医院名称</th><td>'+r.hospitalName+'</td></tr>'
+'<tr><th>姓名</th><td>'+r.realname+'</td></tr>'
+'<tr><th>身份证号</th><td>'+r.idCard+'</td></tr>'
+'<tr><th>体检年份</th><td>'+r.medicalYear+'</td></tr>'
+'<tr><th>体检编号</th><td>'+r.peId+'</td></tr>'
+'<tr><th>影像分类</th><td>'+r.imgClass+'</td></tr>'
+'<tr><th>json数据</th><td><div class="json-box">'+r.jsonContent+'</div></td></tr>'
+'<tr><th>文件地址</th><td>'+r.zipFileUrl+'</td></tr>'
+'<tr><th>文件大小(MB</th><td>'+r.zipSize+'</td></tr>'
+'<tr><th>处理状态</th><td>'+handleStatusMap[r.status]+'</td></tr>'
+'<tr><th>错误信息</th><td>'+(r.msg||'<span class="muted"></span>')+'</td></tr>'
+'<tr><th>传输开始时间</th><td>'+r.startTime+'</td></tr>'
+'<tr><th>传输完成时间</th><td>'+r.endTime+'</td></tr>'
+'<tr><th>解析开始时间</th><td>'+(r.parseStartTime||'<span class="muted"></span>')+'</td></tr>'
+'<tr><th>解析完成时间</th><td>'+(r.parseEndTime||'<span class="muted"></span>')+'</td></tr>'
+'</tbody></table>';
document.getElementById('detailMask').classList.add('open');
}
function closeDetail(){document.getElementById('detailMask').classList.remove('open');}
document.addEventListener('keydown',function(e){
if(e.key==='Escape')closeDetail();
});
function toggleSubgroup(header){
header.parentElement.classList.toggle('open');
}
(function(){
renderTable();
var active=document.querySelector('.sidebar-item.active');
if(!active)return;
var sub=active.closest('.sidebar-subgroup');
while(sub){ sub.classList.add('open'); sub=sub.parentElement.closest('.sidebar-subgroup'); }
})();
</script>
</body>
</html>
+658
View File
@@ -0,0 +1,658 @@
<!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>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
:root{
--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;
--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--background:#F5F7FA;
--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;
--radius-sm:4px;--radius-md:8px;--radius-lg:12px;
--shadow-sm:0 1px 4px rgba(0,0,0,0.08);--shadow-md:0 4px 12px rgba(0,0,0,0.12);
}
/* 顶部导航 */
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
.top-bar__logo svg{width:28px;height:28px;}
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
.top-bar__nav a{color:rgba(255,255,255,0.65);text-decoration:none;padding:6px 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,0.08);}
.top-bar__nav a.active{color:#fff;background:var(--primary);}
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);}
.top-bar__right .btn-back{color:rgba(255,255,255,0.65);cursor:pointer;display:flex;align-items:center;gap:4px;text-decoration:none;}
.top-bar__right .btn-back:hover{color:#fff;}
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);cursor:pointer;}
/* 侧边栏 */
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);background:rgba(255,255,255,0.04);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;transition:transform .2s;flex-shrink:0;}
.sidebar-subgroup__header .arrow path{stroke:rgba(255,255,255,0.45);}
.sidebar-subgroup.open>.sidebar-subgroup__header{color:rgba(255,255,255,0.85);}
.sidebar-subgroup.open>.sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;padding-left:16px;}
.sidebar-subgroup.open>.sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:36px;font-size:12px;}
.sidebar-subgroup .sidebar-subgroup{padding-left:12px;}
.sidebar-subgroup .sidebar-subgroup .sidebar-item{padding-left:40px;}
.sidebar-subgroup .sidebar-subgroup>.sidebar-subgroup__header{font-size:12px;}
/* 主内容 */
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
/* 筛选栏 */
.filter-bar{background:#fff;border-radius:var(--radius-md);padding:20px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:16px;flex-wrap:wrap;}
.filter-item{display:flex;align-items:center;gap:8px;}
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
.filter-input{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;min-width:200px;}
.filter-input:focus{border-color:var(--primary);}
.filter-input::placeholder{color:var(--text-placeholder);}
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
/* 体检年份选择控件 */
.year-picker{position:relative;}
.year-picker .filter-input{padding-right:30px;cursor:pointer;min-width:110px;}
.year-picker>svg{position:absolute;right:9px;top:50%;transform:translateY(-50%);pointer-events:none;}
.year-picker__dropdown{display:none;position:absolute;top:40px;left:0;width:110px;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);z-index:50;max-height:180px;overflow-y:auto;}
.year-picker__dropdown.open{display:block;}
.year-picker__dropdown div{padding:8px 14px;font-size:var(--font-sm);cursor:pointer;}
.year-picker__dropdown div:hover{background:var(--primary-light);}
.year-picker__dropdown div.selected{color:var(--primary);font-weight:600;}
/* 表格 */
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.table-card__content{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;}
.table-card__content table{min-width:100%;white-space:nowrap;}
table{width:100%;border-collapse:collapse;}
thead th{background:#FAFAFA;padding:12px 16px;text-align:center;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;text-align:center;}
tbody tr:last-child td{border-bottom:none;}
tbody tr:hover{background:#F8FBFF;}
#userTable thead th:last-child{position:sticky;right:0;background:#FAFAFA;border-left:1px solid var(--border);}
#userTable tbody td:last-child{position:sticky;right:0;background:#fff;border-left:1px solid var(--border);}
#userTable tbody tr:hover td:last-child{background:#F8FBFF;}
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);}.link:hover{color:#40a9ff;}
.link-danger{color:var(--danger);cursor:pointer;font-size:var(--font-sm);}.link-danger:hover{color:#ff7875;}
.empty-tip{padding:48px 0;text-align:center;font-size:var(--font-sm);color:var(--text-placeholder);}
/* 展开列 */
.expand-toggle{display:inline-flex;width:20px;height:20px;align-items:center;justify-content:center;cursor:pointer;color:var(--text-placeholder);transition:transform .2s,color .2s;}
.expand-toggle svg{transition:transform .2s;}
.expand-toggle.open{color:var(--primary);}
.expand-toggle.open svg{transform:rotate(90deg);}
/* 展开子表 */
.expand-row td{background:#FAFBFC;padding:0 !important;text-align:left !important;}
.expand-row:hover td{background:#FAFBFC !important;}
.inner-wrap{padding:12px 20px 16px 52px;}
.inner-table{width:100%;border:1px solid var(--border);border-collapse:collapse;background:#fff;}
.inner-table th{background:#F5F7FA;padding:9px 14px;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border:1px solid var(--border);text-align:center;white-space:nowrap;}
.inner-table td{padding:10px 14px;font-size:var(--font-sm);color:var(--text-primary);border:1px solid var(--border);text-align:center;white-space:nowrap;}
.inner-empty{color:var(--text-placeholder);font-style:italic;font-size:var(--font-xs);}
/* 状态标签 */
.tag{display:inline-flex;align-items:center;padding:2px 10px;border-radius:10px;font-size:var(--font-xs);}
.tag-blue{background:#E6F7FF;color:#1890FF;}
.tag-green{background:#F6FFED;color:#52C41A;}
.tag-orange{background:#FFF7E6;color:#FA8C16;}
.tag-red{background:#FFF1F0;color:#FF4D4F;}
.tag-gray{background:#FAFAFA;color:#999;}
/* 分页 */
.pagination{display:flex;align-items:center;justify-content:flex-end;padding:16px 24px;gap:8px;border-top:1px solid var(--border);flex-wrap:wrap;}
.pagination span{font-size:var(--font-sm);color:var(--text-secondary);}
.page-btn{width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-sm);display:inline-flex;align-items:center;justify-content:center;transition:all .2s;color:var(--text-primary);}
.page-btn:hover:not(:disabled),.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.page-btn:disabled{color:var(--text-placeholder);cursor:not-allowed;background:#FAFAFA;}
.page-ellipsis{width:24px;text-align:center;color:var(--text-placeholder);font-size:var(--font-sm);}
.page-size{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 24px 0 10px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;appearance:none;cursor:pointer;outline:none;}
.page-jump input{width:48px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);text-align:center;font-size:var(--font-sm);outline:none;margin:0 4px;}
.page-jump input:focus{border-color:var(--primary);}
/* 弹窗 */
.modal-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:200;overflow-y:auto;}
.modal-mask.open{display:flex;align-items:flex-start;justify-content:center;padding:60px 24px;}
.modal{width:560px;max-width:100%;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-md);display:flex;flex-direction:column;max-height:calc(100vh - 120px);}
.modal__header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--border);flex-shrink:0;}
.modal__close{cursor:pointer;color:var(--text-secondary);display:flex;align-items:center;transition:color .2s;}
.modal__close:hover{color:var(--danger);}
.modal__title{font-size:var(--font-base);font-weight:600;}
.modal__body{padding:20px;overflow-y:auto;}
/* 详情 */
.detail-table{width:100%;border-collapse:collapse;border:1px solid var(--border);}
.detail-table th,.detail-table td{border-bottom:1px solid var(--border);text-align:left;padding:12px 16px;font-size:var(--font-sm);}
.detail-table tr:last-child td{border-bottom:none;}
.detail-table th{width:120px;background:#FAFAFA;color:var(--text-secondary);font-weight:500;}
/* 表单 */
.form-item{margin-bottom:16px;}
.form-item:last-child{margin-bottom:0;}
.form-label{font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:6px;display:flex;align-items:center;gap:4px;font-weight:500;}
.form-label .required{color:var(--danger);}
.form-input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;}
.form-input:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.2);}
.form-input::placeholder{color:var(--text-placeholder);}
.modal__footer{display:flex;justify-content:flex-end;gap:12px;padding:14px 20px;border-top:1px solid var(--border);flex-shrink:0;}
</style>
</head>
<body>
<!-- 顶部导航 -->
<div class="top-bar">
<div class="top-bar__logo">
<svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
健康体检
</div>
<nav class="top-bar__nav">
<a href="archive-personal.html">档案</a>
<a href="config-material-notice.html">基础配置</a>
<a href="pre-hospital-scope.html">检前</a>
<a href="mid-appointment-track.html">检中</a>
<a href="post-unit-count.html" class="active">检后</a>
<a href="exception-dirty.html">异常事件</a>
</nav>
<div class="top-bar__right">
<a class="btn-back" href="../../index.html">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M10 3L5 8l5 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
返回首页
</a>
<div class="avatar"></div>
<span style="color:rgba(255,255,255,0.65);">系统管理员 ▾</span>
</div>
</div>
<!-- 侧边栏 -->
<aside class="sidebar">
<div class="sidebar-menu__title">检后</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
<a href="post-indicator-notify.html" class="sidebar-item">指标异常通知</a>
<a href="post-report-unmatched.html" class="sidebar-item">体检报告未匹配项目</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检统计汇总
</div>
<div class="sidebar-subgroup__body">
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
<a href="post-cancer-screening.html" class="sidebar-item">防癌筛查人数统计</a>
<a href="post-standard-stat.html" class="sidebar-item">系统标准化项目统计</a>
<a href="post-contracted-hospital.html" class="sidebar-item">签约医院人数统计</a>
<a href="post-key-indicator-stat.html" class="sidebar-item">重点指标异常统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
病种信息管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-disease-info.html" class="sidebar-item">病种信息配置</a>
<a href="post-conclusion-disease.html" class="sidebar-item">体检结论关联病种</a>
<a href="post-auto-select-disease.html" class="sidebar-item">员工病种信息</a>
</div>
</div>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item active">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
<main class="main">
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-item">
<label>姓名:</label>
<input class="filter-input" type="text" id="f-name" placeholder="请输入姓名" style="min-width:140px;">
</div>
<div class="filter-item">
<label>身份证号:</label>
<input class="filter-input" type="text" id="f-idcard" placeholder="请输入身份证号" style="min-width:180px;">
</div>
<div class="filter-item">
<label>体检年份:</label>
<div class="year-picker">
<input class="filter-input" type="text" readonly value="2026" id="f-year">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="2.5" width="11" height="10" rx="1.5" stroke="#999" stroke-width="1.2"/><path d="M1.5 5.5h11M4.5 1v3M9.5 1v3" stroke="#999" stroke-width="1.2" stroke-linecap="round"/></svg>
<div class="year-picker__dropdown" id="yearDropdown">
<div class="selected" onclick="pickYear(this)">2026</div>
<div onclick="pickYear(this)">2025</div>
<div onclick="pickYear(this)">2024</div>
</div>
</div>
</div>
<div class="filter-item">
<label>医院名称:</label>
<input class="filter-input" type="text" id="f-hospital" placeholder="请输入医院名称" style="min-width:180px;">
</div>
<div style="margin-left:auto;display:flex;gap:12px;">
<button class="btn btn-primary" onclick="doQuery()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4.5" stroke="#fff" stroke-width="1.4"/><path d="M9.5 9.5l3 3" stroke="#fff" stroke-width="1.4" stroke-linecap="round"/></svg>
查询
</button>
<button class="btn btn-default" onclick="doReset()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M12 7a5 5 0 1 1-1.5-3.6M12 1.5V4h-2.5" stroke="#666" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
重置
</button>
</div>
</div>
<!-- 操作栏 -->
<div style="display:flex;gap:12px;margin-bottom:16px;align-items:center;">
<button class="btn btn-default" onclick="batchDelete()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 3.5h10M5.5 3.5V2h3v1.5M3.5 3.5l.7 8.5h5.6l.7-8.5" stroke="#666" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>
批量删除
</button>
</div>
<!-- 数据表格 -->
<div class="table-card">
<div class="table-card__content">
<table id="userTable">
<thead>
<tr>
<th style="width:48px;"><input type="checkbox" id="checkAll" onclick="toggleCheckAll(this)"></th>
<th style="width:56px;">序号</th>
<th style="width:48px;"></th>
<th>单位信息</th>
<th>姓名</th>
<th>身份证号</th>
<th>体检年份</th>
<th>医院名称</th>
<th>体检编号</th>
<th>创建日期</th>
<th>操作</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
</table>
</div>
<div class="pagination" id="pagination"></div>
</div>
</main>
<!-- 编辑弹窗 -->
<div class="modal-mask" id="formMask" onclick="if(event.target===this)closeForm()">
<div class="modal">
<div class="modal__header">
<span class="modal__close" onclick="closeForm()">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
</span>
<span class="modal__title" id="formTitle">编辑用户体检信息</span>
</div>
<div class="modal__body">
<div class="form-item">
<div class="form-label">单位信息</div>
<input class="form-input" id="formUnit" type="text" placeholder="请输入单位信息">
</div>
<div class="form-item">
<div class="form-label"><span class="required">*</span> 姓名</div>
<input class="form-input" id="formName" type="text" placeholder="请输入姓名">
</div>
<div class="form-item">
<div class="form-label"><span class="required">*</span> 身份证号</div>
<input class="form-input" id="formIdcard" type="text" placeholder="请输入身份证号">
</div>
<div class="form-item">
<div class="form-label">体检年份</div>
<input class="form-input" id="formYear" type="text" placeholder="请输入体检年份">
</div>
<div class="form-item">
<div class="form-label">体检编号</div>
<input class="form-input" id="formPeid" type="text" placeholder="请输入体检编号">
</div>
<div class="form-item">
<div class="form-label">医院ID</div>
<input class="form-input" id="formHospid" type="text" placeholder="请输入医院ID">
</div>
<div class="form-item">
<div class="form-label">医院名称</div>
<input class="form-input" id="formHospital" type="text" placeholder="请输入医院名称">
</div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeForm()">取消</button>
<button class="btn btn-primary" onclick="saveForm()">保存</button>
</div>
</div>
</div>
<!-- 详情弹窗 -->
<div class="modal-mask" id="detailMask" onclick="if(event.target===this)closeDetail()">
<div class="modal">
<div class="modal__header">
<span class="modal__close" onclick="closeDetail()">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
</span>
<span class="modal__title">用户体检信息详情</span>
</div>
<div class="modal__body" id="detailBody"></div>
</div>
</div>
<script>
/* ============ 数据 ============ */
var handleStatusMap={1:'新建',2:'待解析',3:'失败',4:'解析中',5:'成功',6:'失败待重试',7:'最终失败'};
function statusTag(s){
var cls='tag-gray';
if(s===5)cls='tag-green';
else if(s===4||s===6)cls='tag-orange';
else if(s===3||s===7)cls='tag-red';
else if(s===1)cls='tag-blue';
return '<span class="tag '+cls+'">'+handleStatusMap[s]+'</span>';
}
var users=[
{unit:'公司机关',realname:'张伟',idCard:'500103197805121234',medicalYear:'2026',hospitalName:'重庆市人民医院',hospitalId:'H001',peId:'PE20260001',createTime:'2026-08-15 10:23:45',children:[
{name:'胸部CT平扫',status:5,studies:['1.2.840.113619.6.55.201','1.2.840.113619.6.55.202'],files:['report_chest_001.pdf']},
{name:'头颅MRI平扫',status:5,studies:['1.2.840.113619.6.55.203'],files:['report_mri_001.pdf']},
{name:'腹部超声',status:4,studies:['1.2.840.113619.6.55.204'],files:[]}
]},
{unit:'第一采油厂',realname:'李芳',idCard:'500107198812053467',medicalYear:'2026',hospitalName:'重庆医科大学附属第一医院',hospitalId:'H002',peId:'PE20260002',createTime:'2026-08-15 11:02:18',children:[
{name:'胸部CT平扫',status:5,studies:['1.2.840.113619.6.55.301'],files:['report_chest_002.pdf']},
{name:'心电图',status:5,studies:['1.2.840.113619.6.55.302'],files:['report_ecg_002.pdf']}
]},
{unit:'第二采油厂',realname:'王强',idCard:'500105199203154567',medicalYear:'2026',hospitalName:'陆军军医大学西南医院',hospitalId:'H003',peId:'PE20260003',createTime:'2026-08-15 13:45:09',children:[
{name:'胸部X射线',status:7,studies:['1.2.840.113619.6.55.401'],files:[]},
{name:'腹部超声',status:6,studies:['1.2.840.113619.6.55.402'],files:[]}
]},
{unit:'公共事务中心',realname:'赵敏',idCard:'500104199001011234',medicalYear:'2026',hospitalName:'陆军特色医学中心',hospitalId:'H004',peId:'PE20260004',createTime:'2026-08-16 09:12:33',children:[
{name:'冠脉CTA',status:2,studies:[],files:[]}
]},
{unit:'公司机关',realname:'陈刚',idCard:'500106198503094567',medicalYear:'2025',hospitalName:'重庆市人民医院',hospitalId:'H001',peId:'PE20250018',createTime:'2025-08-20 15:30:00',children:[
{name:'胸部CT平扫',status:5,studies:['1.2.840.113619.6.55.501'],files:['report_chest_018.pdf']},
{name:'头颅MRI平扫',status:5,studies:['1.2.840.113619.6.55.502'],files:['report_mri_018.pdf']},
{name:'腹部超声',status:5,studies:['1.2.840.113619.6.55.503'],files:['report_us_018.pdf']},
{name:'心电图',status:5,studies:['1.2.840.113619.6.55.504'],files:['report_ecg_018.pdf']}
]},
{unit:'第一采油厂',realname:'刘洋',idCard:'500102199511223456',medicalYear:'2025',hospitalName:'重庆医科大学附属第一医院',hospitalId:'H002',peId:'PE20250021',createTime:'2025-08-21 10:05:52',children:[
{name:'胸部CT平扫',status:5,studies:['1.2.840.113619.6.55.601'],files:['report_chest_021.pdf']},
{name:'腹部超声',status:7,studies:['1.2.840.113619.6.55.602'],files:[]}
]}
];
var state={name:'',idcard:'',year:'2026',hospital:'',page:1,pageSize:10};
var expanded={};
/* ============ 年份选择 ============ */
function toggleYear(){document.getElementById('yearDropdown').classList.toggle('open');}
function pickYear(el){
document.getElementById('f-year').value=el.textContent.trim();
document.getElementById('yearDropdown').classList.remove('open');
document.querySelectorAll('#yearDropdown div').forEach(function(d){d.classList.remove('selected');});
el.classList.add('selected');
}
document.getElementById('f-year').addEventListener('click',toggleYear);
document.addEventListener('click',function(e){
if(!e.target.closest('.year-picker'))document.getElementById('yearDropdown').classList.remove('open');
});
/* ============ 查询/重置 ============ */
function doQuery(){
state.name=document.getElementById('f-name').value.trim();
state.idcard=document.getElementById('f-idcard').value.trim();
state.year=document.getElementById('f-year').value.trim();
state.hospital=document.getElementById('f-hospital').value.trim();
state.page=1;
expanded={};
renderTable();
}
function doReset(){
document.getElementById('f-name').value='';
document.getElementById('f-idcard').value='';
document.getElementById('f-year').value='2026';
document.querySelectorAll('#yearDropdown div').forEach(function(d){
d.classList.toggle('selected',d.textContent.trim()==='2026');
});
document.getElementById('f-hospital').value='';
state.name='';state.idcard='';state.year='2026';state.hospital='';
state.page=1;
expanded={};
renderTable();
}
/* ============ 表格渲染 ============ */
function filteredIdx(){
var list=[];
users.forEach(function(u,i){
if(state.name&&u.realname.indexOf(state.name)===-1)return;
if(state.idcard&&u.idCard.indexOf(state.idcard)===-1)return;
if(state.year&&u.medicalYear!==state.year)return;
if(state.hospital&&u.hospitalName.indexOf(state.hospital)===-1)return;
list.push(i);
});
return list;
}
function renderTable(){
var list=filteredIdx();
var totalPages=Math.max(1,Math.ceil(list.length/state.pageSize));
if(state.page>totalPages)state.page=totalPages;
var start=(state.page-1)*state.pageSize;
var pageIdx=list.slice(start,start+state.pageSize);
var tbody=document.getElementById('tbody');
tbody.innerHTML=pageIdx.length?pageIdx.map(function(i,pos){
var u=users[i];
var isOpen=!!expanded[i];
var html='<tr>'
+'<td><input type="checkbox" class="row-check" data-idx="'+i+'"></td>'
+'<td>'+(start+pos+1)+'</td>'
+'<td><span class="expand-toggle'+(isOpen?' open':'')+'" onclick="toggleExpand('+i+')">'
+'<svg width="12" height="12" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>'
+'</span></td>'
+'<td>'+u.unit+'</td><td>'+u.realname+'</td><td>'+u.idCard+'</td><td>'+u.medicalYear+'</td>'
+'<td>'+u.hospitalName+'</td><td>'+u.peId+'</td><td>'+u.createTime+'</td>'
+'<td><span class="link" onclick="openForm('+i+')">编辑</span> <span class="link" style="margin-left:8px;" onclick="openDetail('+i+')">详情</span> <span class="link-danger" style="margin-left:8px;" onclick="deleteRow('+i+')">删除</span></td>'
+'</tr>';
if(isOpen)html+=renderExpandRow(i);
return html;
}).join(''):'<tr><td colspan="11"><div class="empty-tip">暂无数据</div></td></tr>';
var all=tbody.querySelectorAll('.row-check');
var checked=tbody.querySelectorAll('.row-check:checked');
document.getElementById('checkAll').checked=all.length>0&&all.length===checked.length;
renderPagination(list.length,totalPages);
}
function renderExpandRow(i){
var u=users[i];
var body='';
if(!u.children.length){
body='<tr><td colspan="4"><div class="inner-empty" style="padding:16px 0;text-align:center;">暂无检查项目数据</div></td></tr>';
}else{
body=u.children.map(function(c){
var studiesHtml=c.studies.length?c.studies.map(function(s){
return '<span class="link" style="margin-right:6px;" onclick="previewStudy(\''+s+'\')">预览</span>';
}).join(''):'<span class="inner-empty">暂无</span>';
var filesHtml=c.files.length?c.files.map(function(f){
return '<span class="link" style="margin-right:6px;" onclick="viewReport(\''+f+'\')">查看</span>';
}).join(''):'<span class="inner-empty">无文件</span>';
return '<tr><td>'+c.name+'</td><td>'+statusTag(c.status)+'</td><td>'+studiesHtml+'</td><td>'+filesHtml+'</td></tr>';
}).join('');
}
return '<tr class="expand-row"><td colspan="11"><div class="inner-wrap">'
+'<table class="inner-table"><thead><tr>'
+'<th>检查项目名称</th><th>处理状态</th><th>影像信息</th><th>影像报告</th>'
+'</tr></thead><tbody>'+body+'</tbody></table>'
+'</div></td></tr>';
}
function toggleExpand(i){
if(expanded[i])delete expanded[i];else expanded[i]=true;
renderTable();
}
/* ============ 分页 ============ */
function pageSeq(p,last){
var seq=[];
if(last<=7){for(var i=1;i<=last;i++)seq.push(i);return seq;}
if(p<=4){seq=[1,2,3,4,5,'...',last];}
else if(p>=last-3){seq=[1,'...',last-4,last-3,last-2,last-1,last];}
else{seq=[1,'...',p-1,p,p+1,'...',last];}
return seq;
}
function renderPagination(total,totalPages){
var html='<span>共 '+total+' 条数据</span>';
if(totalPages>1){
html+='<button class="page-btn"'+(state.page===1?' disabled':'')+' onclick="goPage('+(state.page-1)+')">&lt;</button>';
pageSeq(state.page,totalPages).forEach(function(p){
if(p==='...')html+='<span class="page-ellipsis">...</span>';
else html+='<button class="page-btn'+(p===state.page?' active':'')+'" onclick="goPage('+p+')">'+p+'</button>';
});
html+='<button class="page-btn"'+(state.page===totalPages?' disabled':'')+' onclick="goPage('+(state.page+1)+')">&gt;</button>';
}
html+='<select class="page-size" onchange="changeSize(this.value)">'
+[10,20,50].map(function(n){return '<option value="'+n+'"'+(n===state.pageSize?' selected':'')+'>'+n+'条/页</option>';}).join('')
+'</select>';
html+='<span class="page-jump">跳至<input type="number" id="jumpInput" min="1" value="'+state.page+'" onkeydown="if(event.key===\'Enter\')jumpTo('+totalPages+')"><button class="page-btn" onclick="jumpTo('+totalPages+')"></button></span>';
document.getElementById('pagination').innerHTML=html;
}
function goPage(p){state.page=p;renderTable();}
function changeSize(v){state.pageSize=parseInt(v,10);state.page=1;renderTable();}
function jumpTo(totalPages){
var input=document.getElementById('jumpInput');
var p=parseInt(input.value,10);
if(isNaN(p)||p<1)p=1;
if(p>totalPages)p=totalPages;
state.page=p;
renderTable();
}
/* ============ 影像预览/报告 ============ */
function previewStudy(sid){
alert('影像预览(Orthanc Web Viewer\n研究实例ID'+sid);
}
function viewReport(furl){
alert('下载影像报告\n文件:'+furl);
}
/* ============ 删除 ============ */
function deleteRow(idx){
var u=users[idx];
if(!confirm('确认删除「'+u.realname+'」的体检影像信息?'))return;
users.splice(idx,1);
delete expanded[idx];
renderTable();
}
/* ============ 勾选 / 批量删除 ============ */
function toggleCheckAll(cb){
document.querySelectorAll('.row-check').forEach(function(c){c.checked=cb.checked;});
}
function checkedIdx(){
var arr=[];
document.querySelectorAll('.row-check:checked').forEach(function(c){arr.push(parseInt(c.dataset.idx,10));});
return arr;
}
function batchDelete(){
var idxs=checkedIdx();
if(!idxs.length){alert('请先勾选要删除的记录');return;}
if(!confirm('确认删除选中的 '+idxs.length+' 条体检影像信息?'))return;
idxs.sort(function(a,b){return b-a;}).forEach(function(i){users.splice(i,1);delete expanded[i];});
renderTable();
}
/* ============ 详情 ============ */
function openDetail(idx){
var u=users[idx];
document.getElementById('detailBody').innerHTML=
'<table class="detail-table"><tbody>'
+'<tr><th>单位信息</th><td>'+u.unit+'</td></tr>'
+'<tr><th>姓名</th><td>'+u.realname+'</td></tr>'
+'<tr><th>身份证号</th><td>'+u.idCard+'</td></tr>'
+'<tr><th>体检年份</th><td>'+u.medicalYear+'</td></tr>'
+'<tr><th>体检编号</th><td>'+u.peId+'</td></tr>'
+'<tr><th>医院ID</th><td>'+u.hospitalId+'</td></tr>'
+'<tr><th>医院名称</th><td>'+u.hospitalName+'</td></tr>'
+'</tbody></table>';
document.getElementById('detailMask').classList.add('open');
}
function closeDetail(){document.getElementById('detailMask').classList.remove('open');}
/* ============ 编辑 ============ */
var editingIdx=null;
function openForm(idx){
editingIdx=idx;
document.getElementById('formTitle').textContent='编辑用户体检信息';
var u=users[idx];
document.getElementById('formUnit').value=u.unit;
document.getElementById('formName').value=u.realname;
document.getElementById('formIdcard').value=u.idCard;
document.getElementById('formYear').value=u.medicalYear;
document.getElementById('formPeid').value=u.peId;
document.getElementById('formHospid').value=u.hospitalId;
document.getElementById('formHospital').value=u.hospitalName;
document.getElementById('formMask').classList.add('open');
}
function closeForm(){document.getElementById('formMask').classList.remove('open');}
function saveForm(){
var name=document.getElementById('formName').value.trim();
var idcard=document.getElementById('formIdcard').value.trim();
if(!name){alert('请输入姓名');return;}
if(!idcard){alert('请输入身份证号');return;}
var u=users[editingIdx];
u.unit=document.getElementById('formUnit').value.trim();
u.realname=name;
u.idCard=idcard;
u.medicalYear=document.getElementById('formYear').value.trim();
u.peId=document.getElementById('formPeid').value.trim();
u.hospitalId=document.getElementById('formHospid').value.trim();
u.hospitalName=document.getElementById('formHospital').value.trim();
closeForm();
renderTable();
}
document.addEventListener('keydown',function(e){
if(e.key==='Escape'){closeForm();closeDetail();}
});
/* ============ 通用 ============ */
function toggleSubgroup(header){
header.parentElement.classList.toggle('open');
}
/* 页面加载时自动展开当前页面所在的子分组 */
(function(){
renderTable();
var active=document.querySelector('.sidebar-item.active');
if(!active)return;
var sub=active.closest('.sidebar-subgroup');
while(sub){ sub.classList.add('open'); sub=sub.parentElement.closest('.sidebar-subgroup'); }
})();
</script>
</body>
</html>
@@ -180,6 +180,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -172,6 +172,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -205,6 +205,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -207,6 +207,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -258,6 +258,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -191,6 +191,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -181,6 +181,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -202,6 +202,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -211,6 +211,18 @@
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->