fix:完善PRD文档

This commit is contained in:
冯普
2026-04-20 15:02:43 +08:00
parent 907801e45b
commit f4a368ecc8
5 changed files with 212 additions and 195 deletions
+47 -96
View File
@@ -2,16 +2,16 @@
## 模块概述
小助手角色是后台管理系统中面向健康咨询小助手的工作台,用于实时处理员工咨询、管理历史记录、内部即时通讯、专家管理与排班、体检标准查阅、通知短信发送、常用语管理及专家结算。
后台管理系统中面向健康咨询小助手的工作台,用于实时处理员工咨询、管理历史记录、内部即时通讯、专家管理与排班、体检标准查阅、通知短信发送、常用语管理及专家结算。
### 页面导航结构
```
小助手工作台
├── 当前咨询IM对话)
├── 当前咨询
│ ├── 咨询人档案弹窗
│ └── 选择专家弹窗
├── 对话消息(内部IM
├── 对话消息
│ └── 通讯录弹窗
├── 咨询记录(列表)
│ └── 对话详情弹窗
@@ -136,7 +136,7 @@ stateDiagram-v2
- 操作按钮:「咨询人档案」「结束咨询」(红色)
**消息类型:**
- 系统消息(居中灰色标签
- 专家消息(左侧白色气泡
- 员工消息(左侧白色气泡)
- 小助手消息(右侧绿色气泡)
- 咨询信息卡片:咨询人头像、基本信息、咨询标题、描述、持续时间、附件图片
@@ -152,13 +152,14 @@ stateDiagram-v2
- 对话列表按最新消息时间倒序排列
- 未读消息显示红色角标
- 仅咨询状态为"咨询中"的对话出现在此列表
- 已结束(已完成、已超时)的咨询不在此列表中显示
**交互说明:**
- 点击左侧对话项 → 右侧切换为该员工的聊天内容
- 点击「咨询人档案」→ 打开咨询人档案弹窗(680×720)
- 点击「咨询人档案」→ 打开当前咨询人档案弹窗(680×720
- 点击「发送专家」→ 打开选择专家弹窗(720×600)
- 选中专家确认后 → 专家卡片以消息形式发送到对话中
- 点击「结束咨询」→ 结束当前咨询会话
- 点击「结束咨询」→ 结束本次咨询会话
---
@@ -166,18 +167,19 @@ stateDiagram-v2
![页面截图](screenshots/employee-profile-dialog.jpg)
**功能说明:** 展示当前咨询员工的个人信息和健康档案,帮助小助手了解员工背景。以 iframe 弹窗形式打开。
**功能说明:** 当咨询会话类型是员工咨询专家时,需要展示当前咨询员工的个人信息和健康档案,帮助小助手了解员工背景。以 iframe 弹窗形式打开,主体信息分为员工信息/咨询人信息/咨询人档案三部分
**员工信息(两列网格):** 姓名、员工编号、性别、年龄、单位、部门
**咨询人信息区Tab 切换):** 支持多个咨询人(本人/配偶/子女等),默认隐藏 Tab 栏(仅一个咨询人时)
**当前咨询人信息区**
- 基本信息:咨询人姓名、性别、出生日期、与本人关系
- 生活习惯:是否抽烟、是否饮酒、饮食喜好、睡眠质量
- 健康档案:既往病史(标签)、过敏史(标签)、家族病史(标签)、体检报告(缩略图卡片)、检查报告(缩略图卡片)
- 身体情况:身高、体重、过敏史...
**本次咨询档案:** 档案名称、问题描述、持续时间、检查资料、是否前往就医(是则显示医院、科室、疾病)、期望帮助
**业务规则:**
- 健康指标来源于最近一次体检数据
- 指标按正常/偏高/偏低三色标识
- 当咨询会话类型是员工咨询专家时才有健康档案按钮,若员工未完善档案信息(咨询人信息、咨询档案)则仅显示员工信息
---
@@ -194,7 +196,7 @@ stateDiagram-v2
**交互说明:**
- 单选模式,点击专家行选中高亮
- 底部显示已选专家信息
- 点击「确认选择」→ 将专家卡片发送到对话框,关闭弹窗
- 点击「确认选择」→ 将专家卡片发送到对话框,自动关闭弹窗
---
@@ -204,7 +206,7 @@ stateDiagram-v2
![页面截图](screenshots/chat-message.jpg)
**功能说明:** IM 风格左右分栏布局,用于小助手与内部人员(咨询专家、操作人员、专业人员、驻场人员、健康管理员)之间的即时通讯。
**功能说明:** IM 风格左右分栏布局,用于小助手与内部人员(咨询专家、操作人员、专业人员、驻场人员、健康管理员)之间的点对点即时通讯。
**左侧 — 消息列表区:**
- 搜索框:按联系人姓名搜索
@@ -257,7 +259,7 @@ stateDiagram-v2
| 健康管理员 | 单位、部门 |
**业务规则:**
- 单次只能选择一位联系人
- 单次只能选择一位联系人,暂不支持手动创建群聊模式
- 已存在于消息列表中的联系人不会重复添加
**交互说明:**
@@ -273,7 +275,7 @@ stateDiagram-v2
![页面截图](screenshots/history-consult.jpg)
**功能说明:** 查看所有咨询记录,分为「咨询专家」和「咨询小助手」两个 Tab。
**功能说明:** 小助手视角查看所有咨询记录,分为「咨询专家」和「咨询小助手」两个 Tab。
**Tab — 咨询专家:**
@@ -300,13 +302,13 @@ stateDiagram-v2
表格列:序号、咨询单号、姓名、员工编号、所属单位、所属部门、咨询小助手、咨询状态、发起咨询时间、首次回复时间、咨询完成时间、咨询时长、操作
**业务规则:**
- 咨询状态随咨询类型联动:图文咨询状态为"待回复/进行中/已完成";视频咨询状态为"待确认/待开始/已完成/已取消/已拒绝"
- 咨询专家列表中咨询状态随咨询类型联动:图文咨询状态为"待回复/进行中/已完成";视频咨询状态为"待确认/待开始/已完成/已取消/已拒绝"
- 序号、咨询单号、姓名固定在表格左侧,操作列固定在右侧
**交互说明:**
- 点击「更多条件」展开/收起高级筛选区
- 点击「查看对话」→ 打开对话详情弹窗(720×680)
- 支持导出列表数据、查看导出任务
- 支持异步导出列表数据、查看导出任务
---
@@ -319,7 +321,7 @@ stateDiagram-v2
**咨询信息摘要(三列网格):** 咨询单号、员工姓名(含工号)、所属单位、发起时间、咨询时长、咨询状态(标签)
**对话内容:**
- 系统消息(居中灰色标签
- 专家消息(左侧灰色气泡
- 员工消息(左侧灰色气泡)
- 小助手消息(右侧蓝色浅底气泡)
- 转接专家通知(黄色背景卡片,显示转接目标专家信息)
@@ -367,28 +369,28 @@ stateDiagram-v2
**表单字段 — 基本信息:**
| 字段名 | 类型 | 必填 | 规则 |
|--------|------|------|------|
| 头像 | 图片上传 | 否 | 支持 jpg/png |
| 账号 | 文本输入 | 是 | — |
| 字段名 | 类型 | 必填 | 规则 |
|--------|------|------|--------------------------|
| 头像 | 图片上传 | 否 | 支持 jpg/png |
| 账号 | 文本输入 | 是 | — |
| 密码 | 密码输入 | 是 | 不少于12位,必须包含大小写字母、数字、特殊符号 |
| 姓名 | 文本输入 | 是 | — |
| 性别 | 单选 | 是 | 男/女 |
| 身份证号 | 文本输入 | 否 | 18位 |
| 手机号 | 文本输入 | 否 | 11位手机号 |
| 医院 | 下拉选择 | 是 | 预设医院列表 |
| 科室 | 下拉选择 | 是 | 心脏内科/内分泌科/神经内科/骨科/消化内科/呼吸内科 |
| 职称 | 下拉选择 | 是 | 主任医师/副主任医师/主治医师/住院医师 |
| 职务 | 下拉选择 | 否 | 科室主任/副主任/无 |
| 专家类型 | 单选 | 是 | 收费专家/免费专家/其他专家 |
| 专家状态 | 单选 | 是 | 有效/无效 |
| 专家编号 | 文本输入 | 否 | — |
| 擅长疾病 | 文本输入 | 否 | 逗号分隔 |
| 注册日期 | 日期选择 | 否 | — |
| 出库日期 | 日期选择 | 否 | — |
| 是否推荐 | 单选 | 否 | 是/否,默认否 |
| 排序 | 数字输入 | 否 | — |
| 邮箱 | 文本输入 | 否 | — |
| 姓名 | 文本输入 | 是 | — |
| 性别 | 单选 | 是 | 男/女 |
| 身份证号 | 文本输入 | 否 | 18位 |
| 手机号 | 文本输入 | 否 | 11位手机号 |
| 医院 | 下拉选择 | 是 | 预设医院列表 |
| 科室 | 下拉选择 | 是 | 医院下的科室列表,选医院后联动 |
| 职称 | 下拉选择 | 是 | 主任医师/副主任医师/主治医师/住院医师 |
| 职务 | 下拉选择 | 否 | 科室主任/副主任/无 |
| 专家类型 | 单选 | 是 | 收费专家/免费专家/其他专家 |
| 专家状态 | 单选 | 是 | 有效/无效 |
| 专家编号 | 文本输入 | 否 | — |
| 擅长疾病 | 文本输入 | 否 | 选医院和科室后联动显示科室下疾病 |
| 注册日期 | 日期选择 | 否 | — |
| 出库日期 | 日期选择 | 否 | — |
| 是否推荐 | 单选 | 否 | 是/否,默认否 |
| 排序 | 数字输入 | 否 | — |
| 邮箱 | 文本输入 | 否 | — |
**表单字段 — 扩展信息:**
@@ -409,7 +411,7 @@ stateDiagram-v2
- 编辑模式回填已有数据,可修改
**交互说明:**
- 底部按钮:取消(返回专家管理列表)、保存
- 底部按钮:取消(返回专家管理列表)、保存返回并刷新专家列表
---
@@ -417,7 +419,7 @@ stateDiagram-v2
![页面截图](screenshots/expert-schedule-dialog.jpg)
**功能说明:** 配置某位专家的每周排班和视频咨询开关。以 iframe 弹窗形式嵌入。
**功能说明:** 配置某位专家排班和视频咨询开关。以 iframe 弹窗形式嵌入。
**专家信息卡片(只读):** 头像(姓氏首字)、姓名、职称标签、专家编号、医院、科室、收费类型
@@ -434,7 +436,8 @@ stateDiagram-v2
- 接诊人数数字输入框(52px 宽,休诊时禁用)
**业务规则:**
- 工作日(周一至周五)默认开诊,上午每时段 5 人,下午每时段 4 人
- 排班设置根据自然周循环重复使用。默认自动跳过法定节假日
- 工作日(周一至周五)默认开诊,上午每时段 n 人,下午每时段 n 人
- 周末(周六、周日)默认休诊,人数为 0
- 休诊时人数输入框禁用(灰色背景),开诊时可编辑
- 从休诊切换为开诊时,若人数为 0 则自动填充为 5
@@ -490,7 +493,7 @@ stateDiagram-v2
![页面截图](screenshots/checkup-standard.jpg)
**功能说明:** 左侧体检分类树 + 右侧项目列表,供小助手查阅体检项目明细以回答员工相关问题。
**功能说明:** 节选自健康体检业务中的体检标准化项目查询业务,左侧体检分类树 + 右侧项目列表,供小助手查阅体检项目明细以回答员工相关问题。
**左侧 — 分类树:**
- 搜索框:搜索大类/小类名称
@@ -514,28 +517,13 @@ stateDiagram-v2
---
### 15. 体检项目明细弹窗(item-detail-dialog.html
![页面截图](screenshots/item-detail-dialog.jpg)
**功能说明:** 展示单个体检项目的详细信息,包括参考范围、临床意义、关联套餐和注意事项。以新窗口弹窗形式打开。
**展示区域:**
- 基本信息:项目名称、项目编码(等宽字体)、所属分类、检测单位
- 参考范围:正常范围、偏低(蓝色)、超重(橙色)、肥胖(红色),辅以可视化条形图
- 临床意义:文字描述
- 关联体检套餐:标签列表(基础套餐 A/标准套餐 B/高端套餐 C/入职体检/年度体检)
- 注意事项:编号列表
---
## 六、短信发送
### 16. 短信发送记录列表(sms-send.html
![页面截图](screenshots/sms-send.jpg)
**功能说明:** 展示短信发送历史记录,支持新建短信。
**功能说明:** 同系统管理-消息通知-短息管理业务中功能,展示短信发送历史记录,支持新建短信。
**筛选条件:** 消息标题(文本输入)、发送状态(下拉:请选择/已发布/发送中/发送失败)
@@ -549,43 +537,6 @@ stateDiagram-v2
---
### 17. 新建短信弹窗(send-dialog.html
![页面截图](screenshots/send-dialog.jpg)
**功能说明:** 创建并发送通知短信,支持选择发送对象和定时发送。以 iframe 弹窗形式打开。
**表单字段表:**
| 字段名 | 类型 | 必填 | 规则 |
|--------|------|------|------|
| 发送对象 | 选择(4种模式) | 是 | 按单位/按部门/指定人员/全部员工,选择后下方联动对应选择器 |
| 短信签名 | 下拉选择 | 是 | 预设签名列表 |
| 短信内容 | 多行文本 | 是 | 不超过 70 字,实时字数统计,超 70 字按多条计费提示 |
| 定时发送 | 开关 + 日期时间 | 否 | 开启后必填时间,关闭时时间选择器禁用 |
**交互说明:**
- 点击对象类型切换 → 下方选择器联动变化
- 点击「确认发送」→ 发送短信,通过 postMessage 关闭弹窗
---
### 18. 短信发送详情弹窗(sms-detail-dialog.html
![页面截图](screenshots/sms-detail-dialog.jpg)
**功能说明:** 展示某批次短信的发送结果,包括基本信息、短信内容预览、发送统计和失败记录。以新窗口弹窗形式打开。
**基本信息:** 发送批次(等宽字体)、发送状态(标签)、发送对象、发送时间、操作人、短信签名
**短信内容:** 灰色预览卡片
**发送统计(4 个统计块):** 发送总数(蓝色)、发送成功(绿色)、发送失败(红色)、成功率
**失败记录列表:** 姓名(脱敏手机号)+ 失败原因(号码已停机/号码不存在/运营商拦截/发送超时),红色背景行
---
## 七、常用语
### 19. 常用语管理(config-phrase.html
+148 -88
View File
@@ -50,11 +50,9 @@
tbody td{padding:14px 16px;font-size:var(--font-sm);border-bottom:1px solid var(--border);white-space:nowrap;}
.col-fixed{position:sticky;z-index:2;background:#fff;}
thead .col-fixed{background:#FAFAFA;z-index:3;}
.col-fixed-1{left:0;min-width:60px;width:60px;}
.col-fixed-2{left:60px;}
.col-fixed-3{left:160px;}
.col-fixed-name{left:0;min-width:200px;}
.col-fixed-end{right:0;min-width:120px;box-shadow:-2px 0 4px rgba(0,0,0,0.04);}
.col-fixed-3::after{content:'';position:absolute;top:0;right:-4px;bottom:0;width:4px;background:linear-gradient(to right,rgba(0,0,0,0.04),transparent);pointer-events:none;}
.col-fixed-name::after{content:'';position:absolute;top:0;right:-4px;bottom:0;width:4px;background:linear-gradient(to right,rgba(0,0,0,0.04),transparent);pointer-events:none;}
tbody tr:last-child td{border-bottom:none;}
tbody tr:hover{background:#F8FBFF;}
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
@@ -72,6 +70,14 @@
.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;}
.page-btn:hover,.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.expert-count{display:inline-flex;align-items:center;gap:4px;font-size:var(--font-sm);color:var(--primary);font-weight:500;}
/* 树状表格 */
.tree-toggle{width:20px;height:20px;border:1px solid var(--border);background:#FAFAFA;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;color:var(--text-secondary);border-radius:var(--radius-sm);flex-shrink:0;transition:all .15s;font-size:14px;line-height:1;font-weight:600;font-family:monospace;}
.tree-toggle:hover{background:var(--primary-light);color:var(--primary);border-color:var(--primary);}
.tree-name{display:inline-flex;align-items:center;gap:6px;}
.tree-indent-1{padding-left:26px;}
tr.tree-child{transition:all .2s;}
tr.tree-hidden{display:none;}
.dept-desc{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-secondary);font-size:var(--font-xs);}
/* 抽屉弹窗 */
.drawer-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0);z-index:999;transition:background .3s;pointer-events:none;}
.drawer-overlay.open{background:rgba(0,0,0,0.45);pointer-events:auto;}
@@ -161,15 +167,14 @@
<input class="form-input" type="text" placeholder="请输入科室名称">
</div>
<div class="form-item">
<div class="form-label">科室分类</div>
<select class="form-select">
<option value="">请选择科室分类</option>
<option>内科</option>
<option>外科</option>
<option>妇产科</option>
<option></option>
<option>五官</option>
<option>其他</option>
<div class="form-label">上级科室</div>
<select class="form-select" id="parentDeptSelect">
<option value="">无(作为一级科室)</option>
<option value="1">内科</option>
<option value="2">外科</option>
<option value="3">妇产科</option>
<option value="4">五官</option>
<option value="5"></option>
</select>
</div>
<div class="form-item">
@@ -204,17 +209,6 @@
<label>科室名称</label>
<input class="filter-input" type="text" placeholder="请输入科室名称">
</div>
<div class="filter-item">
<label>科室分类</label>
<select class="filter-select">
<option>全部分类</option>
<option>内科</option>
<option>外科</option>
<option>妇产科</option>
<option>儿科</option>
<option>五官科</option>
</select>
</div>
<button class="btn btn-primary">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4" stroke="currentColor" stroke-width="1.4"/><path d="M10 10l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
查询
@@ -242,9 +236,8 @@
<table>
<thead>
<tr>
<th class="col-fixed col-fixed-1">序号</th>
<th class="col-fixed col-fixed-2">科室名称</th>
<th class="col-fixed col-fixed-3">科室分类</th>
<th class="col-fixed col-fixed-name">科室名称</th>
<th>科室介绍</th>
<th>关联专家数</th>
<th>置顶</th>
<th>排序</th>
@@ -252,78 +245,131 @@
</tr>
</thead>
<tbody>
<tr>
<td class="col-fixed col-fixed-1">1</td>
<td class="col-fixed col-fixed-2" style="font-weight:500;">心血管内科</td>
<td class="col-fixed col-fixed-3">内科</td>
<td><span class="expert-count">12 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')" title="点击切换置顶状态"></div></td>
<!-- 一级:内科 -->
<tr data-id="1" data-level="0">
<td class="col-fixed col-fixed-name" style="font-weight:500;"><div class="tree-name"><button class="tree-toggle collapsed" onclick="toggleTree(this)" data-parent="1">+</button>内科</div></td>
<td><div class="dept-desc">内科系统各亚专科的综合科室</div></td>
<td><span class="expert-count">34 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')"></div></td>
<td>1</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">2</td>
<td class="col-fixed col-fixed-2" style="font-weight:500;">神经内科</td>
<td class="col-fixed col-fixed-3">内科</td>
<tr data-id="1-1" data-parent-id="1" data-level="1" class="tree-child tree-hidden">
<td class="col-fixed col-fixed-name"><div class="tree-name tree-indent-1">心血管内科</div></td>
<td><div class="dept-desc">诊治心脏及血管相关疾病</div></td>
<td><span class="expert-count">12 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')"></div></td>
<td>1</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr data-id="1-2" data-parent-id="1" data-level="1" class="tree-child tree-hidden">
<td class="col-fixed col-fixed-name"><div class="tree-name tree-indent-1">神经内科</div></td>
<td><div class="dept-desc">诊治脑与神经系统疾病</div></td>
<td><span class="expert-count">8 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')"></div></td>
<td>2</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">3</td>
<td class="col-fixed col-fixed-2" style="font-weight:500;">骨科</td>
<td class="col-fixed col-fixed-3">外科</td>
<td><span class="expert-count">6 位专家</span></td>
<td><div class="switch" onclick="this.classList.toggle('on')"></div></td>
<tr data-id="1-3" data-parent-id="1" data-level="1" class="tree-child tree-hidden">
<td class="col-fixed col-fixed-name"><div class="tree-name tree-indent-1">消化内科</div></td>
<td><div class="dept-desc">诊治消化系统相关疾病</div></td>
<td><span class="expert-count">9 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')"></div></td>
<td>3</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">4</td>
<td class="col-fixed col-fixed-2" style="font-weight:500;">消化内科</td>
<td class="col-fixed col-fixed-3">内科</td>
<td><span class="expert-count">9 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')"></div></td>
<tr data-id="1-4" data-parent-id="1" data-level="1" class="tree-child tree-hidden">
<td class="col-fixed col-fixed-name"><div class="tree-name tree-indent-1">内分泌科</div></td>
<td><div class="dept-desc">诊治内分泌及代谢性疾病</div></td>
<td><span class="expert-count">5 位专家</span></td>
<td><div class="switch" onclick="this.classList.toggle('on')"></div></td>
<td>4</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">5</td>
<td class="col-fixed col-fixed-2" style="font-weight:500;">内分泌科</td>
<td class="col-fixed col-fixed-3">内科</td>
<!-- 一级:外科 -->
<tr data-id="2" data-level="0">
<td class="col-fixed col-fixed-name" style="font-weight:500;"><div class="tree-name"><button class="tree-toggle collapsed" onclick="toggleTree(this)" data-parent="2">+</button>外科</div></td>
<td><div class="dept-desc">外科系统各亚专科的综合科室</div></td>
<td><span class="expert-count">9 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')"></div></td>
<td>2</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr data-id="2-1" data-parent-id="2" data-level="1" class="tree-child tree-hidden">
<td class="col-fixed col-fixed-name"><div class="tree-name tree-indent-1">骨科</div></td>
<td><div class="dept-desc">诊治骨骼、关节及运动系统疾病</div></td>
<td><span class="expert-count">6 位专家</span></td>
<td><div class="switch" onclick="this.classList.toggle('on')"></div></td>
<td>1</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr data-id="2-2" data-parent-id="2" data-level="1" class="tree-child tree-hidden">
<td class="col-fixed col-fixed-name"><div class="tree-name tree-indent-1">皮肤科</div></td>
<td><div class="dept-desc">诊治皮肤及皮下组织疾病</div></td>
<td><span class="expert-count">3 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')"></div></td>
<td>2</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<!-- 一级:妇产科 -->
<tr data-id="3" data-level="0">
<td class="col-fixed col-fixed-name" style="font-weight:500;"><div class="tree-name"><button class="tree-toggle collapsed" onclick="toggleTree(this)" data-parent="3">+</button>妇产科</div></td>
<td><div class="dept-desc">妇科与产科综合诊疗科室</div></td>
<td><span class="expert-count">7 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')"></div></td>
<td>3</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr data-id="3-1" data-parent-id="3" data-level="1" class="tree-child tree-hidden">
<td class="col-fixed col-fixed-name"><div class="tree-name tree-indent-1">妇科</div></td>
<td><div class="dept-desc">诊治女性生殖系统疾病</div></td>
<td><span class="expert-count">4 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')"></div></td>
<td>1</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr data-id="3-2" data-parent-id="3" data-level="1" class="tree-child tree-hidden">
<td class="col-fixed col-fixed-name"><div class="tree-name tree-indent-1">产科</div></td>
<td><div class="dept-desc">孕产期保健及分娩诊疗</div></td>
<td><span class="expert-count">3 位专家</span></td>
<td><div class="switch" onclick="this.classList.toggle('on')"></div></td>
<td>2</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<!-- 一级:五官科 -->
<tr data-id="4" data-level="0">
<td class="col-fixed col-fixed-name" style="font-weight:500;"><div class="tree-name"><button class="tree-toggle collapsed" onclick="toggleTree(this)" data-parent="4">+</button>五官科</div></td>
<td><div class="dept-desc">眼、耳、鼻、喉综合诊疗</div></td>
<td><span class="expert-count">7 位专家</span></td>
<td><div class="switch" onclick="this.classList.toggle('on')"></div></td>
<td>4</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr data-id="4-1" data-parent-id="4" data-level="1" class="tree-child tree-hidden">
<td class="col-fixed col-fixed-name"><div class="tree-name tree-indent-1">眼科</div></td>
<td><div class="dept-desc">诊治眼部及视觉系统疾病</div></td>
<td><span class="expert-count">4 位专家</span></td>
<td><div class="switch" onclick="this.classList.toggle('on')"></div></td>
<td>1</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr data-id="4-2" data-parent-id="4" data-level="1" class="tree-child tree-hidden">
<td class="col-fixed col-fixed-name"><div class="tree-name tree-indent-1">耳鼻喉科</div></td>
<td><div class="dept-desc">诊治耳鼻咽喉相关疾病</div></td>
<td><span class="expert-count">3 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')"></div></td>
<td>2</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<!-- 一级:儿科(无子科室,叶子节点) -->
<tr data-id="5" data-level="0">
<td class="col-fixed col-fixed-name" style="font-weight:500;"><div class="tree-name" style="padding-left:26px;">儿科</div></td>
<td><div class="dept-desc">诊治儿童各类常见疾病</div></td>
<td><span class="expert-count">5 位专家</span></td>
<td><div class="switch" onclick="this.classList.toggle('on')"></div></td>
<td>5</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">6</td>
<td class="col-fixed col-fixed-2" style="font-weight:500;">眼科</td>
<td class="col-fixed col-fixed-3">五官科</td>
<td><span class="expert-count">4 位专家</span></td>
<td><div class="switch" onclick="this.classList.toggle('on')"></div></td>
<td>6</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">7</td>
<td class="col-fixed col-fixed-2" style="font-weight:500;">妇科</td>
<td class="col-fixed col-fixed-3">妇产科</td>
<td><span class="expert-count">7 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')"></div></td>
<td>7</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">8</td>
<td class="col-fixed col-fixed-2" style="font-weight:500;">皮肤科</td>
<td class="col-fixed col-fixed-3">外科</td>
<td><span class="expert-count">3 位专家</span></td>
<td><div class="switch on" onclick="this.classList.toggle('on')"></div></td>
<td>8</td>
<td class="col-fixed col-fixed-end"><div style="display:flex;gap:8px;"><span class="link" onclick="openDrawer('edit',this)">编辑</span><span class="link-danger">删除</span></div></td>
</tr>
</tbody>
</table>
</div>
@@ -341,6 +387,18 @@
</main>
<script>
/* 树状展开/收起 */
function toggleTree(btn){
var parentId=btn.getAttribute('data-parent');
var collapsed=btn.classList.toggle('collapsed');
btn.textContent=collapsed?'+':'';
var children=document.querySelectorAll('tr[data-parent-id="'+parentId+'"]');
children.forEach(function(tr){
if(collapsed){tr.classList.add('tree-hidden');}
else{tr.classList.remove('tree-hidden');}
});
}
function openDrawer(mode,triggerEl){
const drawer=document.getElementById('dept-drawer');
const overlay=document.getElementById('dept-drawer-overlay');
@@ -348,23 +406,25 @@ function openDrawer(mode,triggerEl){
drawer.classList.add('open');
overlay.classList.add('open');
var nameInput=drawer.querySelector('.form-input[type="text"]');
var catSelect=drawer.querySelector('.form-select');
var parentSelect=document.getElementById('parentDeptSelect');
var textarea=drawer.querySelector('.form-textarea');
var sortInput=drawer.querySelector('.num-input__val');
if(mode==='edit'&&triggerEl){
title.textContent='编辑科室';
var tr=triggerEl.closest('tr');
var cells=tr.querySelectorAll('td');
nameInput.value=cells[1].textContent.trim();
var cat=cells[2].textContent.trim();
for(var i=0;i<catSelect.options.length;i++){
if(catSelect.options[i].text===cat){catSelect.selectedIndex=i;break;}
}
sortInput.value=cells[5].textContent.trim();
/* 科室名称在第1列的 .tree-name 文本 */
var nameEl=cells[0].querySelector('.tree-name');
nameInput.value=nameEl?nameEl.textContent.trim():cells[0].textContent.trim();
/* 上级科室:根据 data-parent-id 回填 */
var pid=tr.getAttribute('data-parent-id')||'';
parentSelect.value=pid;
/* 排序 */
sortInput.value=cells[4].textContent.trim();
}else{
title.textContent='新增科室';
nameInput.value='';
catSelect.selectedIndex=0;
parentSelect.value='';
if(textarea)textarea.value='';
sortInput.value='1';
}
+17 -11
View File
@@ -457,34 +457,40 @@
![页面截图](screenshots/config-department.jpg)
**功能说明:** 管理咨询平台的科室字典,支持新增、编辑、删除。
**功能说明:** 管理咨询平台的科室字典,科室具有层级关系,以树状列表展示,支持新增、编辑、删除。
**筛选条件:** 科室名称、科室分类(全部/内科/外科/妇产科/儿科/五官科....)
**筛选条件:** 科室名称
**表格列:** 序号、科室名称、科室分类、关联专家数("X位专家")、置顶(开关)、排序、操作(编辑/删除)
**表格列:** 科室名称(树状层级展示,带 +/− 展开收起按钮)、科室介绍(单行截断)、关联专家数("X位专家")、置顶(开关)、排序、操作(编辑/删除)
**业务规则:**
- 科室支持层级嵌套,一级科室(如内科、外科)下可包含二级子科室(如心血管内科、神经内科)
- 列表默认收起,仅展示一级科室;点击 `+` 展开子科室,点击 `` 收起
- 无子科室的一级科室不显示展开/收起按钮
**交互说明:**
- 点击"新增科室"打开右侧抽屉表单
- 抽屉表单:科室名称、科室分类(下拉)、科室简介(文本域)、置顶(开关)、排序(默认9999
- 科室信息支持层级嵌套,新增的科室不选分类则认为是一级科室分类
- 抽屉表单:科室名称(必填)、上级科室(下拉,选"无"则为一级科室)、科室简介(文本域)、置顶(开关)、排序(数字步进器,默认1
- 点击"编辑"打开编辑抽屉,自动回填科室名称、上级科室、排序等信息
- 切换置顶开关即时生效
---
### 23. 科室配置弹窗(config-department-dialog.html
### 23. 科室配置弹窗(config-department.html 内嵌抽屉
![页面截图](screenshots/config-department-dialog.png)
**功能说明:** 新增或编辑科室信息的表单弹窗
**功能说明:** 新增或编辑科室信息的右侧抽屉表单。
**表单字段表:**
| 字段名 | 类型 | 必填 | 规则 |
|--------|------|------|------------|
| 科室名称 | 文本输入 | 是 | — |
| 科室分类 | 下拉选择 | | 不选则是一级科室分类 |
| 列表排序序号 | 数字步进器 | 否 | — |
| 移动端显示 | 开关 | 否 | 默认开启 |
| 列表置顶 | 开关 | 否 | 默认关闭 |
| 上级科室 | 下拉选择 | | 选"无"则为一级科室 |
| 科室简介 | 多行文本 | 否 | — |
| 置顶 | 开关 | 否 | 默认关闭 |
| 排序 | 数字步进器 | 否 | 默认1 |
---
Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 71 KiB