Browse Source

2.0接口联调

dev2.0
kola-web 2 weeks ago
parent
commit
5cdcbf8624
  1. 1398
      api.md
  2. 572
      api_doctor_msg_task.md
  3. 18
      project.private.config.json
  4. 28
      src/components/toast/index.scss
  5. 2
      src/components/toast/index.ts
  6. 12
      src/components/toast/index.wxml
  7. 19
      src/doctor/pages/d_createTask/index.scss
  8. 170
      src/doctor/pages/d_createTask/index.ts
  9. 16
      src/doctor/pages/d_createTask/index.wxml
  10. 2
      src/doctor/pages/d_patientDetail/index.wxml
  11. 87
      src/doctor/pages/d_taskDetail/index.ts
  12. 97
      src/doctor/pages/d_taskDetail/index.wxml
  13. 3
      src/doctor/pages/d_taskList/index.json
  14. 4
      src/doctor/pages/d_taskList/index.scss
  15. 71
      src/doctor/pages/d_taskList/index.ts
  16. 92
      src/doctor/pages/d_taskList/index.wxml
  17. 46
      src/gift/pages/record/index.scss
  18. 453
      src/gift/pages/record/index.ts
  19. 275
      src/gift/pages/record/index.wxml
  20. 1
      src/gift/pages/recordDetail/index.json
  21. 153
      src/gift/pages/recordDetail/index.ts
  22. 42
      src/gift/pages/recordDetail/index.wxml
  23. 3
      src/gift/pages/recordList/index.json
  24. 7
      src/gift/pages/recordList/index.scss
  25. 281
      src/gift/pages/recordList/index.ts
  26. 75
      src/gift/pages/recordList/index.wxml
  27. BIN
      src/images/bg43.png
  28. BIN
      src/images/bg44.png
  29. BIN
      src/images/icon145.png
  30. BIN
      src/images/icon146.png
  31. BIN
      src/images/logo.png
  32. 6
      src/pages/caseReport/index.json
  33. 24
      src/pages/caseReport/index.scss
  34. 70
      src/pages/caseReport/index.ts
  35. 19
      src/pages/caseReport/index.wxml
  36. 22
      src/pages/index/index.scss
  37. 23
      src/pages/index/index.ts
  38. 9
      src/pages/index/index.wxml
  39. 13
      src/pages/qaFormDetail/index.ts
  40. 2
      src/pages/qaFormDetail/index.wxml

1398
api.md

File diff suppressed because it is too large Load Diff

572
api_doctor_msg_task.md

@ -0,0 +1,572 @@
# 医生消息任务接口文档
## 通用说明
### 响应格式
所有接口返回 JSON,严格遵循以下格式:
```json
{
"code": 0,
"message": "success",
"data": {}
}
```
- `code`:0 表示成功,非 0 表示失败
- `message`:成功时为 "success",失败时为错误描述
- `data`:业务数据,失败时为 null
### 数据命名规则
`data` 中所有字段使用**小驼峰**命名法(如 `patientNum`、`sendTime`)。
### 登录校验
所有接口需通过请求头 `loginState` 或请求参数 `loginState` 传递登录态,未登录返回 code=403。本模块所有接口需医生身份登录。
### 错误码
| code | 说明 |
|------|------|
| 0 | 成功 |
| 1 | 参数缺失或业务异常 |
| 403 | 需要登录 |
---
## 一、筛选患者人数
- **路由**:`?r=igg4/doctor/task/filter-patient-num`
- **请求方式**:GET
- **说明**:根据筛选条件获取符合条件的患者人数
### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| SelectAll | int | 否 | 全选:0-否,1-是;为1时跳过其余筛选条件 |
| BindStartDate | string | 否 | 绑定日期开始,格式 YYYY-MM-DD |
| BindEndDate | string | 否 | 绑定日期结束,格式 YYYY-MM-DD |
| ConfirmedIgg4 | int | 否 | 确诊IGG4-RD:0-全部,1-是,2-否 |
| MedicationType | string | 否 | 正在使用的药物,逗号分隔选项值(如"2,3"),空或0表示全部。可选值见下面枚举表 |
#### ConfirmedIgg4 枚举
| 值 | 说明 |
|----|------|
| 0 | 全部 |
| 1 | 是(确诊患者) |
| 2 | 否(非确诊患者,含未填写和疑似) |
#### MedicationType 枚举
| 值 | 说明 |
|----|------|
| 0 | 全部 |
| 2 | 激素 |
| 3 | 免疫抑制剂 |
| 4 | 靶向CD19生物制剂(伊奈利珠单抗) |
| 5 | 靶向CD20生物制剂(利妥昔单抗等) |
### 请求示例
```
?r=igg4/doctor/task/filter-patient-num&SelectAll=0&BindStartDate=2026-01-01&BindEndDate=2026-06-30&ConfirmedIgg4=1&MedicationType=2,3
```
### 响应示例
```json
{
"code": 0,
"data": {
"PatientNum": "1"
}
}
```
### 业务逻辑说明
1. 基础条件:患者状态有效、属于当前医生、有绑定用户
2. SelectAll=1 时跳过所有筛选直接返回
3. 绑定时间范围:按 `InviteTime` 字段筛选,使用 BETWEEN 逻辑
4. 确诊IGG4-RD:选"是"时匹配 `IsSuspectedPatient=2`,选"否"时排除 `IsSuspectedPatient=2`(保留未填写和疑似患者)
5. 药物筛选:关联 `t_patient_health_questionnaire_latest` 表(QuestionNo=4),使用 `FIND_IN_SET` 匹配多选答案,多个选项值之间为 OR 关系
---
## 二、获取任务模板
- **路由**:`?r=igg4/doctor/task/get-template`
- **请求方式**:GET
- **说明**:获取所有消息推送模板,按模板类型分组
### 请求参数
### 响应示例
```json
{
"code": 0,
"data": [
{
"templateType": 1,
"templateTypeText": "复诊提醒",
"templateList": [
{
"id": 1,
"templateType": 1,
"interactMsgContent": "互动消息内容",
"templateIndex": "模板index",
"templateContent": "模板内容JSON"
}
]
}
]
}
```
#### TemplateType 枚举
| 值 | 说明 |
|----|------|
| 1 | 复诊提醒 |
| 2 | 激素风险提醒 |
### 业务逻辑说明
1. 查询所有模板记录,按 `TemplateType` 升序、`Id` 升序排列
2. 按 `TemplateType` 分组返回,每组包含类型编号、类型文本和模板列表
---
## 三、创建任务
- **路由**:`?r=igg4/doctor/task/create`
- **请求方式**:POST
- **说明**:创建消息推送任务,使用数据库事务
### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| FilterCondition | object | 否 | 筛选条件JSON对象,结构同"筛选患者人数"接口参数 |
| PushTemplateId | int | 是 | 推送模板ID |
| IsSendNow | int | 是 | 是否立即发送:0-否,1-是 |
| SendTime | string | 条件必填 | 发送时间(IsSendNow=0时必填),格式 YYYY-MM-DD HH:mm:ss |
| PatientNum | int | 是 | 患者人数 |
#### FilterCondition 结构
| 字段 | 类型 | 说明 |
|------|------|------|
| SelectAll | int | 全选:0-否,1-是 |
| BindStartDate | string | 绑定日期开始 |
| BindEndDate | string | 绑定日期结束 |
| ConfirmedIgg4 | int | 确诊IGG4-RD:0-全部,1-是,2-否 |
| MedicationType | string | 正在使用的药物,逗号分隔 |
### 请求示例
```json
{
"FilterCondition": {
"SelectAll": 0,
"BindStartDate": "2026-01-01",
"BindEndDate": "2026-06-30",
"ConfirmedIgg4": 1,
"MedicationType": "2"
},
"PushTemplateId": 2,
"IsSendNow": 1,
"SendTime": "",
"PatientNum": 25
}
```
### 响应示例(成功)
```json
{
"code": 0,
"data": "创建成功"
}
```
### 响应示例(失败)
```json
{
"code": 1,
"data": "每个医生每天只能创建一次任务"
}
```
### 业务逻辑说明
1. 校验医生存在(逻辑删除标记 `DeleteTime` 为空)
2. 校验模板存在
3. 每个医生每天只能创建一次任务(当天已有状态为待发送/发送中/暂停/已发送/已停止的任务则不可再创建)
4. IsSendNow=1 时,SendTime 自动设为当前时间
5. 自动生成 IdNumber(`YmdHis` + 3位随机数)
6. IsDoctorCreate=1(医生端创建),CreateWorkerId 为空
7. 生成模板消息内容(调用 `transformTemplateContent` 转化模板变量)
8. 保存任务后,自动调用 `generateUserRecord` 生成用户发送记录
9. 更新任务的 ActualPatientNum 和 IsInsertUser 字段
---
## 四、获取任务列表
- **路由**:`?r=igg4/doctor/task/list`
- **请求方式**:GET
- **说明**:获取当前医生的任务列表,支持分页,按创建时间降序排列
### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| count | int | 否 | 每页条数,默认10 |
| page | int | 否 | 页码,从1开始 |
### 请求示例
```
?r=igg4/doctor/task/list&count=10&page=1
```
### 响应示例
```json
{
"code": 0,
"data": {
"list": [
{
"Id": 1,
"IdNumber": "20260702143025123",
"DoctorId": 5,
"FilterCondition": {
"SelectAll": 0,
"BindStartDate": "2026-01-01",
"BindEndDate": "2026-06-30",
"ConfirmedIgg4": 1,
"MedicationType": "2"
},
"TemplateType": 1,
"CreateTime": "2026-07-02 14:30:25",
"IsSendNow": 1,
"SendTime": "2026-07-02 14:30:25",
"Status": 4,
"PatientNum": 25,
"ActualPatientNum": 23
}
],
"pages": 3,
"count": 28,
"page": 1
}
}
```
### 响应字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| Id | int | 任务ID |
| IdNumber | string | 任务编号 |
| DoctorId | int | 医生ID |
| FilterCondition | object | 筛选条件JSON对象 |
| TemplateType | int | 模板类型 |
| CreateTime | string | 创建时间 |
| IsSendNow | int | 是否立即发送:0-否,1-是 |
| SendTime | string | 发送时间 |
| Status | int | 任务状态 |
| PatientNum | int | 患者人数 |
| ActualPatientNum | int | 实际发送患者人数 |
#### FilterCondition 结构说明
| 字段 | 类型 | 说明 |
|------|------|------|
| SelectAll | int | 全选:0-否,1-是 |
| BindStartDate | string | 绑定日期开始 |
| BindEndDate | string | 绑定日期结束 |
| ConfirmedIgg4 | int | 确诊IGG4-RD:0-全部,1-是,2-否 |
| MedicationType | string | 正在使用的药物,逗号分隔 |
#### TemplateType 枚举
| 值 | 说明 |
|----|------|
| 1 | 复诊提醒 |
| 2 | 激素风险提醒 |
#### Status 枚举
| 值 | 说明 |
|----|------|
| 1 | 待发送 |
| 2 | 发送中 |
| 3 | 暂停 |
| 4 | 已发送 |
| 5 | 已停止 |
| 6 | 已取消 |
### 业务逻辑说明
1. 仅查询 `DeleteTime` 为空的任务
2. FilterCondition 字段从 JSON 字符串解码为数组返回
3. 返回字段包含:Id, IdNumber, DoctorId, FilterCondition, TemplateType, CreateTime, IsSendNow, SendTime, Status, PatientNum, ActualPatientNum
4. 分页参数:pages 为总页数,count 为总记录数,page 为当前页码(从1开始)
---
## 五、获取任务详情
- **路由**:`?r=igg4/doctor/task/detail`
- **请求方式**:GET
- **说明**:获取单个任务的详细信息,包含发送统计
### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| TaskId | int | 是 | 任务ID |
### 请求示例
```
?r=igg4/doctor/task/detail&TaskId=1
```
### 响应示例
```json
{
"code": 0,
"data": {
"Id": 1,
"IdNumber": "20260702143025123",
"DoctorId": 5,
"FilterCondition": {
"SelectAll": 0,
"BindStartDate": "2026-01-01",
"BindEndDate": "2026-06-30",
"ConfirmedIgg4": 1,
"MedicationType": "2"
},
"TemplateType": 1,
"PushTemplateId": 2,
"TemplateMsgContent": "{...}",
"InteractMsgContent": "互动消息内容",
"IsSendNow": 1,
"SendTime": "2026-07-02 14:30:25",
"PatientNum": 25,
"ActualPatientNum": 23,
"IsInsertUser": 1,
"Status": 4,
"IsDoctorCreate": 1,
"WorkerId": "",
"DeleteTime": null,
"CreateTime": "2026-07-02 14:30:25",
"UpdateTime": "2026-07-02 14:30:30",
"SendDetail": {
"TaskId": 1,
"SumSendCount": 23,
"SendCount": 20,
"UnSendCount": 3,
"InteractSendSuccessCount": 18,
"InteractSendFailCount": 2,
"PushSendSuccessCount": 16,
"PushSendFailCount": 4,
"PushClickCount": 10,
"InteractReadCount": 15,
"InteractUnReadCount": 5,
"PushUnClickCount": 10
}
}
}
```
#### SendDetail 字段说明
| 字段 | 说明 |
|------|------|
| sumSendCount | 总人数(去重) |
| sendCount | 已发送人数 |
| unSendCount | 未发送人数 |
| InteractSendSuccessCount | 互动消息发送成功人数 |
| InteractSendFailCount | 互动消息发送失败人数(派生值:SendCount - InteractSendSuccessCount) |
| PushSendSuccessCount | Push消息发送成功人数 |
| PushSendFailCount | Push消息发送失败人数 |
| PushClickCount | Push消息点击人数 |
| InteractReadCount | 互动消息已读人数 |
| InteractUnReadCount | 互动消息未读人数(派生值:sendCount - InteractReadCount) |
| PushUnClickCount | Push消息未点击人数(派生值:sendCount - PushClickCount) |
### 业务逻辑说明
1. 校验任务存在且 `DeleteTime` 为空,否则抛出"任务不存在"
2. FilterCondition 从 JSON 字符串解码为数组
3. 根据 PushTemplateId 查询模板,补充 TemplateType 和 InteractMsgContent 字段
4. 通过 `TZdDoctorMsgTaskUserMessage::taskDetail` 获取发送统计数据,包含派生的失败/未读/未点击人数
---
## 六、删除任务
- **路由**:`?r=igg4/doctor/task/delete`
- **请求方式**:POST
- **说明**:逻辑删除已取消的任务,使用数据库事务
### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| TaskId | int | 是 | 任务ID |
### 请求示例
```json
{
"TaskId": 1
}
```
### 响应示例(成功)
```json
{
"code": 0,
"data": "删除成功"
}
```
### 响应示例(失败)
```json
{
"code": 1,
"msg": "当前任务状态不能删除",
}
```
### 业务逻辑说明
1. 校验任务存在、属于当前医生且 `DeleteTime` 为空,否则抛出"任务不存在"
2. 只有状态为"已取消"(Status=6)的任务才能删除
3. 设置 `DeleteTime` 为当前时间(软删除)
---
## 七、停止任务
- **路由**:`?r=igg4/doctor/task/stop`
- **请求方式**:POST
- **说明**:停止正在发送中或暂停的任务,使用数据库事务
### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| TaskId | int | 是 | 任务ID |
### 请求示例
```json
{
"TaskId": 1
}
```
### 响应示例(成功)
```json
{
"code": 0,
"data": "停止成功"
}
```
### 响应示例(失败)
```json
{
"code": 1,
"msg": "当前任务状态不能停止",
}
```
### 业务逻辑说明
1. 校验任务存在、属于当前医生且 `DeleteTime` 为空,否则抛出"任务不存在"
2. 只有状态为"发送中"(Status=2)或"暂停"(Status=3)的任务才能停止
3. 将状态更新为"已停止"(Status=5)
---
## 八、取消任务
- **路由**:`?r=igg4/doctor/task/cancel`
- **请求方式**:POST
- **说明**:取消待发送的任务,使用数据库事务
### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| TaskId | int | 是 | 任务ID |
### 请求示例
```json
{
"TaskId": 1
}
```
### 响应示例(成功)
```json
{
"code": 0,
"data": "取消成功"
}
```
### 响应示例(失败)
```json
{
"code": 1,
"msg": "当前任务状态不能取消",
}
```
### 业务逻辑说明
1. 校验任务存在、属于当前医生且 `DeleteTime` 为空,否则抛出"任务不存在"
2. 只有状态为"待发送"(Status=1)的任务才能取消
3. 将状态更新为"已取消"(Status=6)
---
## 附录
### 附录A:任务状态流转
```
待发送(1) ──发送调度──> 发送中(2) ──发送完成──> 已发送(4)
│ │ ↑
│取消 │ │暂停
↓ ↓ │
已取消(6) <────────── 暂停(3)─┘
│ │
│删除(软删) │停止
↓ ↓
(DeleteTime非空) 已停止(5)
```

18
project.private.config.json

@ -22,11 +22,18 @@
"miniprogram": { "miniprogram": {
"list": [ "list": [
{ {
"name": "就诊记录",
"pathName": "gift/pages/record/index",
"query": "id=15",
"scene": null,
"launchMode": "default"
},
{
"name": "医生-患者详情", "name": "医生-患者详情",
"pathName": "doctor/pages/d_patientDetail/index", "pathName": "doctor/pages/d_patientDetail/index",
"query": "id=67", "query": "id=67",
"scene": null, "launchMode": "default",
"launchMode": "default" "scene": null
}, },
{ {
"name": "病例报告", "name": "病例报告",
@ -50,13 +57,6 @@
"scene": null "scene": null
}, },
{ {
"name": "就诊记录",
"pathName": "gift/pages/record/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生-新建消息", "name": "医生-新建消息",
"pathName": "doctor/pages/d_createTask/index", "pathName": "doctor/pages/d_createTask/index",
"query": "", "query": "",

28
src/components/toast/index.scss

@ -2477,21 +2477,29 @@
} }
.popup-public-toast { .popup-public-toast {
.popup-container { .popup-container {
position: relative; padding: 226rpx 0;
padding: 360rpx 146rpx 0; width: 630rpx;
width: 590rpx; height: 864rpx;
height: 880rpx;
box-sizing: border-box; box-sizing: border-box;
.title {
font-size: 40rpx;
color: rgba(34, 34, 34, 1);
text-align: center;
}
.code { .code {
width: 286rpx; display: block;
height: 286rpx; width: 310rpx;
height: 310rpx;
margin: 20rpx auto 0;
border: 1px solid rgba(0, 0, 0, 0.03);
border-radius: 22rpx;
} }
} }
.close { .clsoe{
margin: 68rpx auto 0;
display: block; display: block;
width: 64rpx; margin: 52rpx auto 0;
height: 64rpx; width: 84rpx;
height: 84rpx;
} }
} }

2
src/components/toast/index.ts

@ -42,7 +42,7 @@ Component({
if (['storyLead', 'storyStar', 'storyShare'].includes(this.data.type)) { if (['storyLead', 'storyStar', 'storyShare'].includes(this.data.type)) {
this.getSettingInfo() this.getSettingInfo()
} }
if (this.data.type === 'feedback' || this.data.type === 'doctor-feedback') { if (this.data.type === 'feedback' || this.data.type === 'doctor-feedback' || this.data.type === 'public-toast') {
this.getFeedbackCode() this.getFeedbackCode()
} }
if (this.data.type === 'follow') { if (this.data.type === 'follow') {

12
src/components/toast/index.wxml

@ -718,16 +718,20 @@
bind:tap="handleCancel" bind:tap="handleCancel"
></image> ></image>
</view> </view>
<view class="popup-public-toast" wx:if="{{type == 'public-toast'}}"> <view class="popup-public-toast" wx:if="{{type==='public-toast'}}">
<view <view
class="popup-container" class="popup-container"
style="background: url({{imageUrl}}/5/public-bg1.png?t={{Timestamp}}) no-repeat top center/100%" style="background:url('{{imageUrl}}{{theme === 'DRUG' ? 'bg44' : 'bg43'}}.png?t={{Timestamp}}') no-repeat top center/100%"
> >
<image class="code" src="{{imageUrl}}5/code.png?t={{Timestamp}}" show-menu-by-longpress></image> <view class="title">
关注「IgG4愈见昕生」公众号
<view></view>
重要提醒不丢失
</view>
<image class="code" src="{{feedbackQrCode}}" show-menu-by-longpress></image>
</view> </view>
<image <image
class="close" class="close"
bind:tap="handleCancel"
src="{{imageUrl}}2/close.png?t={{Timestamp}}" src="{{imageUrl}}2/close.png?t={{Timestamp}}"
bind:tap="handleCancel" bind:tap="handleCancel"
></image> ></image>

19
src/doctor/pages/d_createTask/index.scss

@ -99,22 +99,29 @@ page {
} }
.select { .select {
margin-top: 24rpx; margin-top: 24rpx;
display: grid; display: flex;
grid-template-columns: repeat(3, 1fr); flex-wrap: wrap;
gap: 16rpx; gap: 16rpx;
.s-item { .s-item {
padding: 22rpx; flex: 0 0 auto;
min-width: calc((100% - 44rpx) / 3);
padding: 22rpx 12rpx;
text-align: center; text-align: center;
border-radius: 12rpx; border-radius: 12rpx;
font-size: 32rpx; font-size: 32rpx;
color: rgba(34, 34, 34, 0.6); color: rgba(34, 34, 34, 0.6);
background: rgba(248, 248, 248, 1); background: rgba(248, 248, 248, 1);
border: 1px solid rgba(248, 248, 248, 1); border: 1px solid rgba(248, 248, 248, 1);
box-sizing: border-box;
&.active { &.active {
color: rgba(22, 121, 203, 1); color: rgba(22, 121, 203, 1);
background-color: rgba(22, 121, 203, 0.1); background-color: rgba(22, 121, 203, 0.1);
border-color: rgba(22, 121, 203, 1); border-color: rgba(22, 121, 203, 1);
} }
&:nth-of-type(4),
&:nth-of-type(5) {
width: 100%;
}
} }
} }
.custom { .custom {
@ -162,7 +169,7 @@ page {
.content { .content {
font-size: 32rpx; font-size: 32rpx;
color: rgba(34, 34, 34, 0.2); color: rgba(34, 34, 34, 1);
&:empty::after { &:empty::after {
content: attr(data-place); content: attr(data-place);
@ -250,7 +257,7 @@ page {
flex: 1; flex: 1;
padding: 18rpx; padding: 18rpx;
font-size: 32rpx; font-size: 32rpx;
color: rgba(34, 34, 34, 0.60); color: rgba(34, 34, 34, 0.6);
text-align: center; text-align: center;
background: rgba(248, 248, 248, 1); background: rgba(248, 248, 248, 1);
border-radius: 12rpx 12rpx 12rpx 12rpx; border-radius: 12rpx 12rpx 12rpx 12rpx;
@ -259,7 +266,7 @@ page {
&.active { &.active {
color: rgba(22, 121, 203, 1); color: rgba(22, 121, 203, 1);
border-color: rgba(22, 121, 203, 1); border-color: rgba(22, 121, 203, 1);
background: rgba(22, 121, 203, 0.10); background: rgba(22, 121, 203, 0.1);
} }
} }
} }

170
src/doctor/pages/d_createTask/index.ts

@ -1,5 +1,4 @@
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { filterColumns } from '@/utils/doctorPatientTask'
const licia = require('miniprogram-licia') const licia = require('miniprogram-licia')
const app = getApp<IAppOption>() const app = getApp<IAppOption>()
@ -7,7 +6,7 @@ Page({
data: { data: {
background: 'transparent', background: 'transparent',
step: 2, step: 1,
TemplateType: 1, TemplateType: 1,
templates: [] as any, templates: [] as any,
@ -25,28 +24,24 @@ Page({
PatientNum: 0, //患者人数 PatientNum: 0, //患者人数
// form // form
Search: '', SelectAll: 0,
HasAdl: 0, ConfirmedIgg4: 0,
DoubleStandard: 0, MedicationType: [] as number[],
AdlTime: 0,
AdlScore: [] as any,
AdlScoreChange: [],
AdlScoreChangeType: 0,
AdlScoreChangeMax: '',
AdlScoreChangeMin: '',
SteroidDailyDose: [] as any,
SteroidDailyDoseChange: [] as any,
IsThisWeekAdl: 0,
PharyngealSymptoms: 0,
AntibodyType: 0,
ThyroidAbnormal: 0,
PreviousConvulsion: 0,
TraditionalImmunosuppressant: 0,
MedicalInsuranceType: 0,
BindStartDate: '', BindStartDate: '',
BindEndDate: '', BindEndDate: '',
...filterColumns, ConfirmedIgg4Column: [
{ id: 0, value: '全部' },
{ id: 1, value: '是' },
{ id: 2, value: '否' },
],
MedicationTypeColumn: [
{ id: 0, value: '全部' },
{ id: 2, value: '激素' },
{ id: 3, value: '免疫抑制剂' },
{ id: 4, value: '靶向CD19生物制剂(伊奈利珠单抗)' },
{ id: 5, value: '靶向CD20生物制剂(利妥昔单抗等)' },
],
}, },
onLoad() { onLoad() {
const SystemInfo = app.globalSystemInfo const SystemInfo = app.globalSystemInfo
@ -114,26 +109,11 @@ Page({
method: 'GET', method: 'GET',
url: '?r=igg4/doctor/task/filter-patient-num', url: '?r=igg4/doctor/task/filter-patient-num',
data: { data: {
Search: this.data.Search, SelectAll: this.data.SelectAll,
HasAdl: this.data.HasAdl,
DoubleStandard: this.data.DoubleStandard,
AdlTime: this.data.AdlTime,
AdlScore: this.data.AdlScore.join('-'),
AdlScoreChange: this.data.AdlScoreChange.join('-'),
AdlScoreChangeType: this.data.AdlScoreChangeType,
AdlScoreChangeMax: this.data.AdlScoreChangeMax,
AdlScoreChangeMin: this.data.AdlScoreChangeMin,
SteroidDailyDose: this.data.SteroidDailyDose.join('-'),
SteroidDailyDoseChange: this.data.SteroidDailyDoseChange.join('-'),
IsThisWeekAdl: this.data.IsThisWeekAdl,
PharyngealSymptoms: this.data.PharyngealSymptoms,
AntibodyType: this.data.AntibodyType,
ThyroidAbnormal: this.data.ThyroidAbnormal,
PreviousConvulsion: this.data.PreviousConvulsion,
TraditionalImmunosuppressant: this.data.TraditionalImmunosuppressant,
MedicalInsuranceType: this.data.MedicalInsuranceType,
BindStartDate: this.data.BindStartDate, BindStartDate: this.data.BindStartDate,
BindEndDate: this.data.BindEndDate, BindEndDate: this.data.BindEndDate,
ConfirmedIgg4: this.data.ConfirmedIgg4,
MedicationType: this.data.MedicationType.join(','),
}, },
loading: true, loading: true,
}) })
@ -150,24 +130,9 @@ Page({
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
this.setData({ this.setData({
Search: '', SelectAll: 0,
HasAdl: 0, ConfirmedIgg4: 0,
DoubleStandard: 0, MedicationType: [],
AdlTime: 0,
AdlScore: [] as any,
AdlScoreChange: [],
AdlScoreChangeType: 0,
AdlScoreChangeMax: '',
AdlScoreChangeMin: '',
SteroidDailyDose: [] as any,
SteroidDailyDoseChange: [] as any,
IsThisWeekAdl: 0,
PharyngealSymptoms: 0,
AntibodyType: 0,
ThyroidAbnormal: 0,
PreviousConvulsion: 0,
TraditionalImmunosuppressant: 0,
MedicalInsuranceType: 0,
BindStartDate: '', BindStartDate: '',
BindEndDate: '', BindEndDate: '',
}) })
@ -186,14 +151,24 @@ Page({
handleCheckbox(e: any) { handleCheckbox(e: any) {
const { index, key } = e.currentTarget.dataset const { index, key } = e.currentTarget.dataset
const data = this.data[`${key}Column`] const data = this.data[`${key}Column`]
const clickedId = data[index].id
const oldValue = this.data[`${key}`] const oldValue = this.data[`${key}`]
const value = oldValue.includes(data[index].id)
? oldValue.filter((item: any) => item !== data[index].id)
: [...oldValue, data[index].id]
this.setData({ if (!clickedId) {
[`${key}`]: data[index].id ? value : [], // 点击"全部":清空其他选择
}) this.setData({ [`${key}`]: [] })
return
}
const value = oldValue.includes(clickedId)
? oldValue.filter((item: any) => item !== clickedId)
: [...oldValue.filter((item: any) => item !== 0), clickedId] // 选具体选项时移除"全部"
this.setData({ [`${key}`]: value })
},
handleSelectAll() {
this.setData({ SelectAll: this.data.SelectAll === 1 ? 0 : 1 })
this.getTaskNum()
}, },
handleDay(e) { handleDay(e) {
const { value } = e.currentTarget.dataset const { value } = e.currentTarget.dataset
@ -236,33 +211,12 @@ Page({
wx.navigateBack() wx.navigateBack()
}, },
async handleNext() { async handleNext() {
const anyValueKeys = [ const anyValueKeys = ['SelectAll', 'ConfirmedIgg4', 'MedicationType', 'BindStartDate', 'BindEndDate']
'Search',
'HasAdl',
'DoubleStandard',
'AdlTime',
'AdlScore',
'AdlScoreChange',
'AdlScoreChangeType',
'AdlScoreChangeMax',
'AdlScoreChangeMin',
'SteroidDailyDose',
'SteroidDailyDoseChange',
'IsThisWeekAdl',
'PharyngealSymptoms',
'AntibodyType',
'ThyroidAbnormal',
'PreviousConvulsion',
'TraditionalImmunosuppressant',
'MedicalInsuranceType',
'BindStartDate',
'BindEndDate',
]
const empty = anyValueKeys.every((item) => const empty = anyValueKeys.every((item) =>
Array.isArray(this.data[item]) ? licia.isEmpty(this.data[item]) : !this.data[item], Array.isArray(this.data[item]) ? licia.isEmpty(this.data[item]) : !this.data[item],
) )
const hasSelectAll = this.data.SelectAll === 1
if (empty) { if (empty && !hasSelectAll) {
wx.showToast({ wx.showToast({
icon: 'none', icon: 'none',
title: '请至少选择一个筛选条件', title: '请至少选择一个筛选条件',
@ -294,24 +248,9 @@ Page({
}, },
handleSubmit() { handleSubmit() {
const { const {
Search, SelectAll,
HasAdl, ConfirmedIgg4,
DoubleStandard, MedicationType,
AdlTime,
AdlScore,
AdlScoreChange,
AdlScoreChangeType,
AdlScoreChangeMax,
AdlScoreChangeMin,
SteroidDailyDose,
SteroidDailyDoseChange,
IsThisWeekAdl,
PharyngealSymptoms,
AntibodyType,
ThyroidAbnormal,
PreviousConvulsion,
TraditionalImmunosuppressant,
MedicalInsuranceType,
BindStartDate, BindStartDate,
BindEndDate, BindEndDate,
@ -356,26 +295,11 @@ Page({
if (res.confirm) { if (res.confirm) {
const params = { const params = {
FilterCondition: { FilterCondition: {
Search, SelectAll,
HasAdl,
DoubleStandard,
AdlTime,
AdlScore: AdlScore.join('-'),
AdlScoreChange: AdlScoreChange.join('-'),
AdlScoreChangeType,
AdlScoreChangeMax,
AdlScoreChangeMin,
SteroidDailyDose: SteroidDailyDose.join('-'),
SteroidDailyDoseChange: SteroidDailyDoseChange.join('-'),
IsThisWeekAdl,
PharyngealSymptoms,
AntibodyType,
ThyroidAbnormal,
PreviousConvulsion,
TraditionalImmunosuppressant,
MedicalInsuranceType,
BindStartDate, BindStartDate,
BindEndDate, BindEndDate,
ConfirmedIgg4,
MedicationType: MedicationType.join(','),
}, },
PushTemplateId, PushTemplateId,
IsSendNow, IsSendNow,

16
src/doctor/pages/d_createTask/index.wxml

@ -29,7 +29,7 @@
<view class="form"> <view class="form">
<view class="form-item vertical"> <view class="form-item vertical">
<view class="f-title">选择全员</view> <view class="f-title">选择全员</view>
<checkbox class="checkbox"></checkbox> <checkbox class="checkbox" checked="{{SelectAll === 1}}" bind:tap="handleSelectAll"></checkbox>
</view> </view>
</view> </view>
<view class="form"> <view class="form">
@ -53,13 +53,13 @@
</view> </view>
<view class="form"> <view class="form">
<view class="form-item"> <view class="form-item">
<view class="f-title">确诊IgG4-R</view> <view class="f-title">确诊IgG4-RD</view>
<view class="select"> <view class="select">
<view <view
class="s-item {{DoubleStandard === item.id && 'active'}}" class="s-item {{ConfirmedIgg4 === item.id && 'active'}}"
wx:for="{{DoubleStandardColumn}}" wx:for="{{ConfirmedIgg4Column}}"
data-index="{{index}}" data-index="{{index}}"
data-key="DoubleStandard" data-key="ConfirmedIgg4"
bind:tap="handleRadio" bind:tap="handleRadio"
wx:key="id" wx:key="id"
> >
@ -73,9 +73,9 @@
<view class="f-title">正在使用的药物</view> <view class="f-title">正在使用的药物</view>
<view class="select"> <view class="select">
<view <view
class="s-item {{(tools.include(item.id,AdlScore) || AdlScore.length===0 && !item.id) && 'active'}}" class="s-item {{(item.id === 0 ? !MedicationType.length : tools.include(item.id, MedicationType)) && 'active'}}"
wx:for="{{AdlScoreColumn}}" wx:for="{{MedicationTypeColumn}}"
data-key="AdlScore" data-key="MedicationType"
data-index="{{index}}" data-index="{{index}}"
wx:key="id" wx:key="id"
bind:tap="handleCheckbox" bind:tap="handleCheckbox"

2
src/doctor/pages/d_patientDetail/index.wxml

@ -3,7 +3,7 @@
<view class="banner"> <view class="banner">
<view class="generate-pdf"> <view class="generate-pdf">
一键生成病历 一键生成病历
<image class="icon" src="/images/icon144.png"></image> <image class="icon" src="{{imageUrl}}icon144.png?t={{Timestamp}}"></image>
</view> </view>
<view class="avatar"> <view class="avatar">
<image class="avatar-img" src="{{detail.UserImg}}"></image> <image class="avatar-img" src="{{detail.UserImg}}"></image>

87
src/doctor/pages/d_taskDetail/index.ts

@ -1,5 +1,4 @@
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { filterObjs } from '@/utils/doctorPatientTask'
const app = getApp<IAppOption>() const app = getApp<IAppOption>()
@ -12,39 +11,37 @@ Page({
Status: { Status: {
1: '待发送', 1: '待发送',
2: '发送中', 2: '发送中',
3: '暂停',
4: '已发送', 4: '已发送',
5: '已停止', 5: '已停止',
6: '已取消', 6: '已取消',
}, },
templateObj: {}, ConfirmedIgg4Obj: {
0: '全部',
...filterObjs, 1: '是',
2: '否',
},
MedicationTypeObj: {
0: '全部',
2: '激素',
3: '免疫抑制剂',
4: '靶向CD19生物制剂(伊奈利珠单抗)',
5: '靶向CD20生物制剂(利妥昔单抗等)',
},
TemplateTypeObj: {
1: '复诊提醒',
2: '激素风险提醒',
},
}, },
onLoad(options) { onLoad(options) {
this.setData({ this.setData({
id: options.id, id: options.id,
}) })
app.waitLogin().then(() => { app.waitLogin().then(() => {
this.getTemplate()
this.getDetail() this.getDetail()
}) })
}, },
getTemplate() {
wx.ajax({
method: 'GET',
url: '?r=igg4/doctor/task/get-template',
data: {},
}).then((res) => {
const templateObj = {}
res.forEach((item) => {
templateObj[item.TemplateType] = item.TemplateTypeText
})
this.setData({
templateObj,
})
})
},
getDetail() { getDetail() {
wx.ajax({ wx.ajax({
method: 'GET', method: 'GET',
@ -53,33 +50,10 @@ Page({
TaskId: this.data.id, TaskId: this.data.id,
}, },
}).then((res) => { }).then((res) => {
if (typeof res.FilterCondition.AdlScore === 'string') { if (typeof res.FilterCondition.MedicationType === 'string' && res.FilterCondition.MedicationType) {
res.FilterCondition.AdlScore = res.FilterCondition.AdlScore?.split('-') res.FilterCondition.MedicationTypeArr = res.FilterCondition.MedicationType.split(',').map(Number)
.map((item) => Number(item))
.filter((item) => Boolean(item))
} else {
res.FilterCondition.AdlScore = []
}
if (typeof res.FilterCondition.AdlScoreChange === 'string') {
res.FilterCondition.AdlScoreChange = res.FilterCondition.AdlScoreChange?.split('-')
.map((item) => Number(item))
.filter((item) => Boolean(item))
} else {
res.FilterCondition.AdlScoreChange = []
}
if (typeof res.FilterCondition.SteroidDailyDose === 'string') {
res.FilterCondition.SteroidDailyDose = res.FilterCondition.SteroidDailyDose?.split('-')
.map((item) => Number(item))
.filter((item) => Boolean(item))
} else {
res.FilterCondition.SteroidDailyDose = []
}
if (typeof res.FilterCondition.SteroidDailyDoseChange === 'string') {
res.FilterCondition.SteroidDailyDoseChange = res.FilterCondition.SteroidDailyDoseChange?.split('-')
.map((item) => Number(item))
.filter((item) => Boolean(item))
} else { } else {
res.FilterCondition.SteroidDailyDoseChange = [] res.FilterCondition.MedicationTypeArr = []
} }
if (res.CreateTime) { if (res.CreateTime) {
res.CreateTime = dayjs(res.CreateTime).format('YYYY-MM-DD HH:mm') res.CreateTime = dayjs(res.CreateTime).format('YYYY-MM-DD HH:mm')
@ -98,26 +72,11 @@ Page({
url: '/doctor/pages/d_createTask/index', url: '/doctor/pages/d_createTask/index',
success: (res) => { success: (res) => {
res.eventChannel.emit('FilterCondition', { res.eventChannel.emit('FilterCondition', {
Search: FilterCondition.Search, SelectAll: FilterCondition.SelectAll,
HasAdl: FilterCondition.HasAdl,
DoubleStandard: FilterCondition.DoubleStandard,
AdlTime: FilterCondition.AdlTime,
AdlScore: FilterCondition.AdlScore.filter((item) => Boolean(item)),
AdlScoreChange: FilterCondition.AdlScoreChange.filter((item) => Boolean(item)),
AdlScoreChangeType: FilterCondition.AdlScoreChangeType,
AdlScoreChangeMax: FilterCondition.AdlScoreChangeMax,
AdlScoreChangeMin: FilterCondition.AdlScoreChangeMin,
SteroidDailyDose: FilterCondition.SteroidDailyDose.filter((item) => Boolean(item)),
SteroidDailyDoseChange: FilterCondition.SteroidDailyDoseChange.filter((item) => Boolean(item)),
IsThisWeekAdl: FilterCondition.IsThisWeekAdl,
PharyngealSymptoms: FilterCondition.PharyngealSymptoms,
AntibodyType: FilterCondition.AntibodyType,
ThyroidAbnormal: FilterCondition.ThyroidAbnormal,
PreviousConvulsion: FilterCondition.PreviousConvulsion,
TraditionalImmunosuppressant: FilterCondition.TraditionalImmunosuppressant,
MedicalInsuranceType: FilterCondition.MedicalInsuranceType,
BindStartDate: FilterCondition.BindStartDate, BindStartDate: FilterCondition.BindStartDate,
BindEndDate: FilterCondition.BindEndDate, BindEndDate: FilterCondition.BindEndDate,
ConfirmedIgg4: FilterCondition.ConfirmedIgg4,
MedicationType: FilterCondition.MedicationType,
}) })
}, },
}) })

97
src/doctor/pages/d_taskDetail/index.wxml

@ -21,97 +21,26 @@
筛选患者 筛选患者
</view> </view>
<view class="c-wrap"> <view class="c-wrap">
<view class="row" wx:if="{{detail.FilterCondition.Search}}"> <view class="row" wx:if="{{detail.FilterCondition.SelectAll === 1}}">
<view class="label">搜索</view> <view class="label">选择范围</view>
<view class="content">{{detail.FilterCondition.Search}}</view> <view class="content">全部患者</view>
</view> </view>
<view class="row" wx:if="{{detail.FilterCondition.BindStartDate && detail.FilterCondition.BindEndDate}}"> <view class="row" wx:if="{{detail.FilterCondition.BindStartDate || detail.FilterCondition.BindEndDate}}">
<view class="label">绑定时间</view> <view class="label">绑定时间</view>
<view class="content">{{detail.FilterCondition.BindStartDate}}~{{detail.FilterCondition.BindEndDate}}</view> <view class="content">{{detail.FilterCondition.BindStartDate || '---'}}~{{detail.FilterCondition.BindEndDate || '---'}}</view>
</view> </view>
<view class="row" wx:if="{{detail.FilterCondition.IsThisWeekAdl}}"> <view class="row" wx:if="{{detail.FilterCondition.ConfirmedIgg4}}">
<view class="label">本周ADL测评</view> <view class="label">确诊IgG4-RD</view>
<view class="content">{{IsThisWeekAdlObj[detail.FilterCondition.IsThisWeekAdl]}}</view> <view class="content">{{ConfirmedIgg4Obj[detail.FilterCondition.ConfirmedIgg4]}}</view>
</view> </view>
<view class="row" wx:if="{{detail.FilterCondition.HasAdl}}"> <view class="row" wx:if="{{detail.FilterCondition.MedicationTypeArr.length}}">
<view class="label">是否有ADL测评</view> <view class="label">正在使用的药物</view>
<view class="content">{{HasAdlObj[detail.FilterCondition.HasAdl]}}</view>
</view>
<view class="row" wx:if="{{detail.FilterCondition.AdlTime}}">
<view class="label">ADL测评间隔时长</view>
<view class="content">{{AdlTimeObj[detail.FilterCondition.AdlTime]}}</view>
</view>
<view class="row" wx:if="{{detail.FilterCondition.DoubleStandard}}">
<view class="label">是否双达标</view>
<view class="content">{{DoubleStandardObj[detail.FilterCondition.DoubleStandard]}}</view>
</view>
<view class="row" wx:if="{{detail.FilterCondition.AdlScore.length}}">
<view class="label">ADL评分(多选)</view>
<view class="content">
<block wx:for="{{detail.FilterCondition.AdlScore}}" wx:for-item="subItem" wx:key="subItem">
{{AdlScorceObj[subItem]}}{{index < detail.FilterCondition.AdlScore.length - 1 ? ',' : ''}}
</block>
</view>
</view>
<view class="row" wx:if="{{detail.FilterCondition.AdlScoreChange.length}}">
<view class="label">ADL分数变化(多选)</view>
<view class="content">
<block wx:for="{{detail.FilterCondition.AdlScoreChange}}" wx:for-item="subItem" wx:key="subItem">
{{AdlScoreChangeObj[subItem]}}{{index < detail.FilterCondition.AdlScoreChange.length - 1 ? ',' : ''}}
</block>
<view>
<block wx:if="{{detail.FilterCondition.AdlScoreChangeType}}">
{{AdlScoreChangeTypeObj[detail.FilterCondition.AdlScoreChangeType]}}
</block>
<block wx:if="{{detail.FilterCondition.AdlScoreChangeType===2}}">
({{detail.FilterCondition.AdlScoreChangeMin}}-{{detail.FilterCondition.AdlScoreChangeMax}})
</block>
</view>
</view>
</view>
<view class="row" wx:if="{{detail.FilterCondition.SteroidDailyDose.length}}">
<view class="label">激素用量(多选)</view>
<view class="content"> <view class="content">
<block wx:for="{{detail.FilterCondition.SteroidDailyDose}}" wx:for-item="subItem" wx:key="subItem"> <block wx:for="{{detail.FilterCondition.MedicationTypeArr}}" wx:for-item="subItem" wx:key="subItem">
{{SteroidDailyDoseObj[subItem]}}{{index < detail.FilterCondition.SteroidDailyDose.length - 1 ? ',' : ''}} {{MedicationTypeObj[subItem]}}{{index < detail.FilterCondition.MedicationTypeArr.length - 1 ? '、' : ''}}
</block> </block>
</view> </view>
</view> </view>
<view class="row" wx:if="{{detail.FilterCondition.SteroidDailyDoseChange.length}}">
<view class="label">激素变化(多选)</view>
<view class="content">
<block wx:for="{{detail.FilterCondition.SteroidDailyDoseChange}}" wx:for-item="subItem" wx:key="subItem">
{{SteroidDailyDoseChangeObj[subItem]}}{{index < detail.FilterCondition.SteroidDailyDoseChange.length - 1 ?
',' : ''}}
</block>
</view>
</view>
<view class="row" wx:if="{{detail.FilterCondition.PharyngealSymptoms}}">
<view class="label">延髓症状</view>
<view class="content">{{PharyngealSymptomsObj[detail.FilterCondition.PharyngealSymptoms]}}</view>
</view>
<view class="row" wx:if="{{detail.FilterCondition.AntibodyType}}">
<view class="label">抗体类型</view>
<view class="content">{{AntibodyTypeObj[detail.FilterCondition.AntibodyType]}}</view>
</view>
<view class="row" wx:if="{{detail.FilterCondition.ThyroidAbnormal}}">
<view class="label">胸腺异常</view>
<view class="content">{{ThyroidAbnormalObj[detail.FilterCondition.ThyroidAbnormal]}}</view>
</view>
<view class="row" wx:if="{{detail.FilterCondition.PreviousConvulsion}}">
<view class="label">既往发生危象</view>
<view class="content">{{PreviousConvulsionObj[detail.FilterCondition.PreviousConvulsion]}}</view>
</view>
<view class="row" wx:if="{{detail.FilterCondition.TraditionalImmunosuppressant}}">
<view class="label">传统免疫抑制剂</view>
<view class="content">
{{TraditionalImmunosuppressantObj[detail.FilterCondition.TraditionalImmunosuppressant]}}
</view>
</view>
<view class="row" wx:if="{{detail.FilterCondition.MedicalInsuranceType}}">
<view class="label">医保类型</view>
<view class="content">{{MedicalInsuranceTypeObj[detail.FilterCondition.MedicalInsuranceType]}}</view>
</view>
</view> </view>
<view class="c-title"> <view class="c-title">
<image class="icon" src="{{imageUrl}}/doctor/icon-title-lable.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}/doctor/icon-title-lable.png?t={{Timestamp}}"></image>
@ -120,7 +49,7 @@
<view class="c-wrap"> <view class="c-wrap">
<view class="row"> <view class="row">
<view class="label">消息标题</view> <view class="label">消息标题</view>
<view class="content">{{templateObj[detail.TemplateType]}}</view> <view class="content">{{TemplateTypeObj[detail.TemplateType]}}</view>
</view> </view>
<view class="row"> <view class="row">
<view class="label">消息内容</view> <view class="label">消息内容</view>

3
src/doctor/pages/d_taskList/index.json

@ -1,6 +1,9 @@
{ {
"navigationBarTitleText": "任务列表", "navigationBarTitleText": "任务列表",
"usingComponents": { "usingComponents": {
"pagination": "/components/pagination/index",
"van-divider": "@vant/weapp/divider/index",
"van-loading": "@vant/weapp/loading/index",
"navBar": "/components/navBar/navBar" "navBar": "/components/navBar/navBar"
} }
} }

4
src/doctor/pages/d_taskList/index.scss

@ -38,6 +38,10 @@ page {
color: rgba(229, 154, 41, 1); color: rgba(229, 154, 41, 1);
} }
&.status3 {
color: rgba(229, 154, 41, 1);
}
&.status4 { &.status4 {
color: rgba(103, 186, 202, 1); color: rgba(103, 186, 202, 1);
} }

71
src/doctor/pages/d_taskList/index.ts

@ -1,5 +1,4 @@
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { filterObjs } from '@/utils/doctorPatientTask'
const app = getApp<IAppOption>() const app = getApp<IAppOption>()
@ -14,11 +13,27 @@ Page({
Status: { Status: {
1: '待发送', 1: '待发送',
2: '发送中', 2: '发送中',
3: '暂停',
4: '已发送', 4: '已发送',
5: '已停止', 5: '已停止',
6: '已取消', 6: '已取消',
}, },
...filterObjs, ConfirmedIgg4Obj: {
0: '全部',
1: '是',
2: '否',
},
MedicationTypeObj: {
0: '全部',
2: '激素',
3: '免疫抑制剂',
4: '靶向CD19生物制剂(伊奈利珠单抗)',
5: '靶向CD20生物制剂(利妥昔单抗等)',
},
TemplateTypeObj: {
1: '复诊提醒',
2: '激素风险提醒',
},
}, },
onLoad() { onLoad() {
const SystemInfo = app.globalSystemInfo const SystemInfo = app.globalSystemInfo
@ -32,7 +47,7 @@ Page({
}, },
onShow() { onShow() {
app.waitLogin().then(() => { app.waitLogin().then(() => {
// this.getList() this.getList()
}) })
}, },
getList(newPage = 1) { getList(newPage = 1) {
@ -44,33 +59,10 @@ Page({
}, },
}).then((res) => { }).then((res) => {
res.list.forEach((item) => { res.list.forEach((item) => {
if (typeof item.FilterCondition.AdlScore === 'string') { if (typeof item.FilterCondition.MedicationType === 'string' && item.FilterCondition.MedicationType) {
item.FilterCondition.AdlScore = item.FilterCondition.AdlScore?.split('-') item.FilterCondition.MedicationTypeArr = item.FilterCondition.MedicationType.split(',').map(Number)
.map((item) => Number(item))
.filter((item) => Boolean(item))
} else {
item.FilterCondition.AdlScore = []
}
if (typeof item.FilterCondition.AdlScoreChange === 'string') {
item.FilterCondition.AdlScoreChange = item.FilterCondition.AdlScoreChange?.split('-')
.map((item) => Number(item))
.filter((item) => Boolean(item))
} else {
item.FilterCondition.AdlScoreChange = []
}
if (typeof item.FilterCondition.SteroidDailyDose === 'string') {
item.FilterCondition.SteroidDailyDose = item.FilterCondition.SteroidDailyDose?.split('-')
.map((item) => Number(item))
.filter((item) => Boolean(item))
} else {
item.FilterCondition.SteroidDailyDose = []
}
if (typeof item.FilterCondition.SteroidDailyDoseChange === 'string') {
item.FilterCondition.SteroidDailyDoseChange = item.FilterCondition.SteroidDailyDoseChange?.split('-')
.map((item) => Number(item))
.filter((item) => Boolean(item))
} else { } else {
item.FilterCondition.SteroidDailyDoseChange = [] item.FilterCondition.MedicationTypeArr = []
} }
if (item.CreateTime) { if (item.CreateTime) {
@ -123,26 +115,11 @@ Page({
url: '/doctor/pages/d_createTask/index', url: '/doctor/pages/d_createTask/index',
success: (res) => { success: (res) => {
res.eventChannel.emit('FilterCondition', { res.eventChannel.emit('FilterCondition', {
Search: FilterCondition.Search, SelectAll: FilterCondition.SelectAll,
HasAdl: FilterCondition.HasAdl,
DoubleStandard: FilterCondition.DoubleStandard,
AdlTime: FilterCondition.AdlTime,
AdlScore: FilterCondition.AdlScore.filter((item) => Boolean(item)),
AdlScoreChange: FilterCondition.AdlScoreChange.filter((item) => Boolean(item)),
AdlScoreChangeType: FilterCondition.AdlScoreChangeType,
AdlScoreChangeMax: FilterCondition.AdlScoreChangeMax,
AdlScoreChangeMin: FilterCondition.AdlScoreChangeMin,
SteroidDailyDose: FilterCondition.SteroidDailyDose.filter((item) => Boolean(item)),
SteroidDailyDoseChange: FilterCondition.SteroidDailyDoseChange.filter((item) => Boolean(item)),
IsThisWeekAdl: FilterCondition.IsThisWeekAdl,
PharyngealSymptoms: FilterCondition.PharyngealSymptoms,
AntibodyType: FilterCondition.AntibodyType,
ThyroidAbnormal: FilterCondition.ThyroidAbnormal,
PreviousConvulsion: FilterCondition.PreviousConvulsion,
TraditionalImmunosuppressant: FilterCondition.TraditionalImmunosuppressant,
MedicalInsuranceType: FilterCondition.MedicalInsuranceType,
BindStartDate: FilterCondition.BindStartDate, BindStartDate: FilterCondition.BindStartDate,
BindEndDate: FilterCondition.BindEndDate, BindEndDate: FilterCondition.BindEndDate,
ConfirmedIgg4: FilterCondition.ConfirmedIgg4,
MedicationType: FilterCondition.MedicationType,
}) })
}, },
}) })

92
src/doctor/pages/d_taskList/index.wxml

@ -24,9 +24,9 @@
<view class="row"> <view class="row">
<view class="label">筛选条件</view> <view class="label">筛选条件</view>
<view class="content"> <view class="content">
<view class="f-row" wx:if="{{item.FilterCondition.Search}}"> <view class="f-row" wx:if="{{item.FilterCondition.SelectAll === 1}}">
<view class="f-label">搜索:</view> <view class="f-label">选择范围:</view>
<view class="f-content">{{item.FilterCondition.Search}}</view> <view class="f-content">全部患者</view>
</view> </view>
<view class="f-row" wx:if="{{item.FilterCondition.BindStartDate || item.FilterCondition.BindEndDate}}"> <view class="f-row" wx:if="{{item.FilterCondition.BindStartDate || item.FilterCondition.BindEndDate}}">
<view class="f-label">绑定时间:</view> <view class="f-label">绑定时间:</view>
@ -34,89 +34,23 @@
{{item.FilterCondition.BindStartDate || '---'}}~{{item.FilterCondition.BindEndDate || '---'}} {{item.FilterCondition.BindStartDate || '---'}}~{{item.FilterCondition.BindEndDate || '---'}}
</view> </view>
</view> </view>
<view class="f-row" wx:if="{{item.FilterCondition.IsThisWeekAdl}}"> <view class="f-row" wx:if="{{item.FilterCondition.ConfirmedIgg4}}">
<view class="f-label">本周ADL测评:</view> <view class="f-label">确诊IgG4-RD:</view>
<view class="f-content">{{IsThisWeekAdlObj[item.FilterCondition.IsThisWeekAdl]}}</view> <view class="f-content">{{ConfirmedIgg4Obj[item.FilterCondition.ConfirmedIgg4]}}</view>
</view> </view>
<view class="f-row" wx:if="{{item.FilterCondition.HasAdl}}"> <view class="f-row" wx:if="{{item.FilterCondition.MedicationTypeArr.length}}">
<view class="f-label">是否有ADL测评:</view> <view class="f-label">正在使用的药物:</view>
<view class="f-content">{{HasAdlObj[item.FilterCondition.HasAdl]}}</view>
</view>
<view class="f-row" wx:if="{{item.FilterCondition.AdlTime}}">
<view class="f-label">ADL测评间隔时长:</view>
<view class="f-content">{{AdlTimeObj[item.FilterCondition.AdlTime]}}</view>
</view>
<view class="f-row" wx:if="{{item.FilterCondition.DoubleStandard}}">
<view class="f-label">是否双达标:</view>
<view class="f-content">{{DoubleStandardObj[item.FilterCondition.DoubleStandard]}}</view>
</view>
<view class="f-row" wx:if="{{item.FilterCondition.AdlScore.length}}">
<view class="f-label">ADL评分(多选):</view>
<view class="f-content"> <view class="f-content">
<block wx:for="{{item.FilterCondition.AdlScore}}" wx:for-item="subItem" wx:key="subItem"> <block wx:for="{{item.FilterCondition.MedicationTypeArr}}" wx:for-item="subItem" wx:key="subItem">
{{AdlScorceObj[subItem]}}{{index < item.FilterCondition.AdlScore.length - 1 ? ',' : ''}} {{MedicationTypeObj[subItem]}}{{index < item.FilterCondition.MedicationTypeArr.length - 1 ? '、' : ''}}
</block> </block>
</view> </view>
</view> </view>
<view class="f-row" wx:if="{{item.FilterCondition.AdlScoreChange.length}}">
<view class="f-label">ADL分数变化(多选):</view>
<view class="f-content">
<block wx:for="{{item.FilterCondition.AdlScoreChange}}" wx:for-item="subItem" wx:key="subItem">
{{AdlScoreChangeObj[subItem]}}{{index < item.FilterCondition.AdlScoreChange.length - 1 ? ',' : ''}}
</block>
<block wx:if="{{item.FilterCondition.AdlScoreChangeType}}">
{{AdlScoreChangeTypeObj[item.FilterCondition.AdlScoreChangeType]}}
</block>
<block wx:if="{{item.FilterCondition.AdlScoreChangeType===2}}">
({{item.FilterCondition.AdlScoreChangeMin}}-{{item.FilterCondition.AdlScoreChangeMax}})
</block>
</view>
</view>
<view class="f-row" wx:if="{{item.FilterCondition.SteroidDailyDose.length}}">
<view class="f-label">激素用量(多选):</view>
<view class="f-content">
<block wx:for="{{item.FilterCondition.SteroidDailyDose}}" wx:for-item="subItem" wx:key="subItem">
{{SteroidDailyDoseObj[subItem]}}{{index < item.FilterCondition.SteroidDailyDose.length - 1 ? ',' :
''}}
</block>
</view>
</view>
<view class="f-row" wx:if="{{item.FilterCondition.SteroidDailyDoseChange.length}}">
<view class="f-label">激素变化(多选):</view>
<view class="f-content">
<block wx:for="{{item.FilterCondition.SteroidDailyDoseChange}}" wx:for-item="subItem" wx:key="subItem">
{{SteroidDailyDoseChangeObj[subItem]}}{{index < item.FilterCondition.SteroidDailyDoseChange.length - 1
? ',' : ''}}
</block>
</view>
</view>
<view class="f-row" wx:if="{{item.FilterCondition.PharyngealSymptoms}}">
<view class="f-label">延髓症状:</view>
<view class="f-content">{{PharyngealSymptomsObj[item.FilterCondition.PharyngealSymptoms]}}</view>
</view>
<view class="f-row" wx:if="{{item.FilterCondition.AntibodyType}}">
<view class="f-label">抗体类型:</view>
<view class="f-content">{{AntibodyTypeObj[item.FilterCondition.AntibodyType]}}</view>
</view>
<view class="f-row" wx:if="{{item.FilterCondition.ThyroidAbnormal}}">
<view class="f-label">胸腺异常:</view>
<view class="f-content">{{ThyroidAbnormalObj[item.FilterCondition.ThyroidAbnormal]}}</view>
</view>
<view class="f-row" wx:if="{{item.FilterCondition.PreviousConvulsion}}">
<view class="f-label">既往发生危象:</view>
<view class="f-content">{{PreviousConvulsionObj[item.FilterCondition.PreviousConvulsion]}}</view>
</view>
<view class="f-row" wx:if="{{item.FilterCondition.TraditionalImmunosuppressant}}">
<view class="f-label">传统免疫抑制剂:</view>
<view class="f-content">
{{TraditionalImmunosuppressantObj[item.FilterCondition.TraditionalImmunosuppressant]}}
</view>
</view>
<view class="f-row" wx:if="{{item.FilterCondition.MedicalInsuranceType}}">
<view class="f-label">医保类型:</view>
<view class="f-content">{{MedicalInsuranceTypeObj[item.FilterCondition.MedicalInsuranceType]}}</view>
</view> </view>
</view> </view>
<view class="row">
<view class="label">模板类型</view>
<view class="content">{{TemplateTypeObj[item.TemplateType]}}</view>
</view> </view>
</view> </view>
</view> </view>

46
src/gift/pages/record/index.scss

@ -87,6 +87,9 @@ page {
margin-top: 32rpx; margin-top: 32rpx;
padding-bottom: 32rpx; padding-bottom: 32rpx;
border-bottom: 1px dashed rgba(34, 34, 34, 0.1); border-bottom: 1px dashed rgba(34, 34, 34, 0.1);
&:last-of-type {
border: none;
}
.label { .label {
font-size: 36rpx; font-size: 36rpx;
color: rgba(34, 34, 34, 1); color: rgba(34, 34, 34, 1);
@ -125,11 +128,10 @@ page {
.content { .content {
flex: 1; flex: 1;
font-size: 36rpx; font-size: 36rpx;
color: rgba(34, 34, 34, 1); color: rgba(34, 34, 34, 0.4);
&:empty::after { &.active {
content: attr(data-place); color: rgba(34, 34, 34, 1);
color: rgba(34, 34, 34, 0.7);
} }
} }
} }
@ -213,19 +215,25 @@ page {
font-size: 32rpx; font-size: 32rpx;
color: rgba(34, 34, 34, 0.5); color: rgba(34, 34, 34, 0.5);
} }
.upload-container { .upload-wrap {
margin-top: 32rpx; margin-top: 32rpx;
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
.upload-container {
flex-shrink: 0;
padding: 0; padding: 0;
width: 300rpx; width: 300rpx;
height: 168rpx; height: 168rpx;
box-sizing: border-box; box-sizing: border-box;
background: #f3fffa; background: #f3fffa;
border-radius: 16rpx 16rpx 16rpx 16rpx; border-radius: 16rpx;
border: 2rpx dashed #0eb66d; border: 2rpx dashed #0eb66d;
overflow: hidden; overflow: hidden;
.icon { .icon {
display: block; display: block;
margin: 28rpx auto 0; margin: 34rpx auto 0;
width: 48rpx; width: 48rpx;
height: 48rpx; height: 48rpx;
} }
@ -238,25 +246,26 @@ page {
} }
.upload-preview { .upload-preview {
position: relative; position: relative;
margin-top: 32rpx; flex-shrink: 0;
width: 300rpx; width: 300rpx;
height: 168rpx; height: 168rpx;
border-radius: 24rpx; border-radius: 16rpx;
overflow: hidden;
.upload-img { .upload-img {
display: block; display: block;
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 24rpx; border-radius: 16rpx;
} }
.del { .del {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
color: #fff; color: #fff;
font-size: 28rpx; font-size: 24rpx;
padding: 4rpx 12rpx; padding: 4rpx 12rpx;
background-color: rgba(0, 0, 0, 0.8); background-color: rgba(0, 0, 0, 0.8);
border-radius: 0 24rpx 0 24rpx; border-radius: 0 16rpx 0 16rpx;
} }
} }
} }
@ -377,23 +386,22 @@ page {
display: grid; display: grid;
gap: 24rpx; gap: 24rpx;
&.col1 { &.col1 {
grid-template-columns: repeat(1, 1fr); grid-template-columns: repeat(auto-fill, minmax(300rpx, 1fr));
} }
&.col2 { &.col2 {
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
} }
.check-item { .check-item {
display: flex; display: flex;
align-items: center;
gap: 24rpx; gap: 24rpx;
padding: 0 32rpx; padding: 32rpx 32rpx;
height: 96rpx;
border-radius: 16rpx; border-radius: 16rpx;
background-color: rgba(248, 248, 248, 1); background-color: rgba(248, 248, 248, 1);
border: 1px solid rgba(248, 248, 248, 1); border: 1px solid rgba(248, 248, 248, 1);
box-sizing: border-box; box-sizing: border-box;
.check { .check {
flex-shrink: 0; flex-shrink: 0;
margin-top: 6rpx;
width: 40rpx; width: 40rpx;
height: 40rpx; height: 40rpx;
border-radius: 8rpx; border-radius: 8rpx;
@ -577,6 +585,12 @@ page {
} }
} }
} }
.num-select {
.n-item.active {
border-color: rgba(89, 86, 233, 1);
background-color: rgba(89, 86, 233, 0.08);
}
}
} }
.step3-footer { .step3-footer {
.prev { .prev {

453
src/gift/pages/record/index.ts

@ -1,37 +1,466 @@
import { debounce } from 'miniprogram-licia'
const _app = getApp<IAppOption>() const _app = getApp<IAppOption>()
const MEDICATIONS_OPTIONS = [
{ id: 1, label: '没有使用激素', active: false },
{ id: 2, label: '激素', active: false },
{ id: 3, label: '免疫抑制剂', active: false },
{ id: 4, label: '靶向CD19生物制剂(伊奈利珠单抗)', active: false },
{ id: 5, label: '靶向CD20生物制剂(利妥昔单抗等)', active: false },
{ id: 99, label: '其他', active: false },
]
const IMMUNOSUPPRESSANT_OPTIONS = [
{ id: 1, label: '吗替麦考酚酯(骁悉、麦考芬)', active: false },
{ id: 2, label: '替唑嘌呤', active: false },
{ id: 3, label: '环磷酰胺', active: false },
{ id: 4, label: '来氟米特', active: false },
{ id: 5, label: '甲氨蝶呤', active: false },
{ id: 6, label: '环孢素 A', active: false },
{ id: 7, label: '他克莫司', active: false },
{ id: 8, label: '艾拉莫德', active: false },
{ id: 99, label: '其他', active: false },
]
const HORMONE_DOSAGE_OPTIONS = [1, 2, 4, 6, 8]
const IMAGE_TYPE_MAP: Record<string, string> = {
outpatientRecord: 'outpatientRecord',
labReport: 'labReport',
imagingExam: 'imagingExam',
pathology: 'pathology',
immuneFunction: 'immuneFunction',
}
Page({ Page({
data: { data: {
step: 3, step: 1,
draftId: 0,
recordStatus: 0,
// step1
visitDate: '',
nextVisitDate: '',
// step2 - 每种图片类型的文件列表
outpatientRecordFiles: [] as any[],
labReportFiles: [] as any[],
imagingExamFiles: [] as any[],
pathologyFiles: [] as any[],
immuneFunctionFiles: [] as any[],
// step3
igG4Value: '',
medicationsUsed: [] as number[],
immunosuppressantName: [] as number[],
oralHormoneDosage: 0,
customDosage: '',
medicationsOptions: MEDICATIONS_OPTIONS,
immunosuppressantOptions: IMMUNOSUPPRESSANT_OPTIONS,
hormoneDosageOptions: HORMONE_DOSAGE_OPTIONS,
// 其他输入
medicationsUsedOtherText: '',
immunosuppressantNameOtherText: '',
medicationsOtherShow: false,
immunosuppressantOtherShow: false,
// UI
toastShow: false, toastShow: false,
toastType: '', toastType: '',
// toastType: 'oneWrite',
// toastType: 'noWrite', // 用户信息未填全的提醒弹窗
toastParams: {} as any, toastParams: {} as any,
saving: false,
submitted: false,
},
_saveDraftDebounced: null as any,
onLoad(options: any) {
if (options.id) {
this.setData({ draftId: Number(options.id) })
_app.waitLogin().then(() => {
this.getDetail()
})
}
this._saveDraftDebounced = debounce(() => {
this.saveDraft()
}, 800)
},
onUnload() {
wx.disableAlertBeforeUnload()
},
_updateAlertBeforeUnload() {
if (this.data.submitted || !this._hasFilledData()) {
wx.disableAlertBeforeUnload()
} else {
wx.enableAlertBeforeUnload({ message: '您有填写信息还未提交' })
}
},
_hasFilledData() {
if (this.data.visitDate || this.data.nextVisitDate) return true
const hasImage = Object.keys(IMAGE_TYPE_MAP).some(
(key) => (this.data[`${key}Files` as any] as any[]).length > 0,
)
if (hasImage) return true
if (this.data.igG4Value || this.data.oralHormoneDosage || this.data.customDosage) return true
if (this.data.medicationsUsed.length || this.data.immunosuppressantName.length) return true
return false
},
// ========== API ==========
getDetail() {
wx.ajax({
method: 'GET',
url: '?r=igg4/medical-visit/detail',
data: { id: this.data.draftId },
}).then((res: any) => {
if (!res) return
const images = res.images || {}
const setData: Record<string, any> = {
visitDate: res.visitDate || '',
nextVisitDate: res.nextVisitDate || '',
igG4Value: res.manualIgG4Value != null ? String(res.manualIgG4Value) : '',
medicationsUsed: res.medicationsUsed || [],
immunosuppressantName: res.immunosuppressantName || [],
medicationsOptions: MEDICATIONS_OPTIONS.map((item) => ({
...item,
active: (res.medicationsUsed || []).includes(item.id),
})),
immunosuppressantOptions: IMMUNOSUPPRESSANT_OPTIONS.map((item) => ({
...item,
active: (res.immunosuppressantName || []).includes(item.id),
})),
oralHormoneDosage: res.oralHormoneDosage || 0,
recordStatus: res.recordStatus,
customDosage:
res.oralHormoneDosage && !HORMONE_DOSAGE_OPTIONS.includes(res.oralHormoneDosage)
? String(res.oralHormoneDosage)
: '',
medicationsUsedOtherText: res.medicationsUsedOtherText || '',
immunosuppressantNameOtherText: res.immunosuppressantNameOtherText || '',
medicationsOtherShow: (res.medicationsUsed || []).includes(99),
immunosuppressantOtherShow: (res.immunosuppressantName || []).includes(99),
}
// 将按类型分组的图片转为 fileList
for (const typeKey of Object.keys(IMAGE_TYPE_MAP)) {
const typeImages = images[typeKey] || []
setData[`${typeKey}Files`] = typeImages.map((img: any) => ({
imgUrl: img.maskedImageUrl || img.imageUrl,
fileUrl: img.imageUrl,
type: 'image',
imageId: img.id,
imageType: typeKey,
}))
}
this.setData(setData)
})
},
saveDraft(): Promise<any> {
if (this.data.saving) return Promise.resolve()
if (!this.data.visitDate) return Promise.resolve()
this.setData({ saving: true })
// 汇总所有图片
const images: any[] = []
const typeKeys = Object.keys(IMAGE_TYPE_MAP)
for (const typeKey of typeKeys) {
const files = this.data[`${typeKey}Files` as any] as any[]
if (files && files.length) {
for (const file of files) {
images.push({
imageUrl: file.fileUrl || file.imgUrl,
imageType: typeKey,
})
}
}
}
const data: Record<string, any> = {
visitDate: this.data.visitDate,
nextVisitDate: this.data.nextVisitDate || undefined,
manualIgG4Value: this.data.igG4Value ? Number(this.data.igG4Value) : undefined,
medicationsUsed: this.data.medicationsUsed.length ? this.data.medicationsUsed : undefined,
medicationsUsedOtherText: this.data.medicationsUsedOtherText || undefined,
immunosuppressantName: this.data.immunosuppressantName.length
? this.data.immunosuppressantName
: undefined,
immunosuppressantNameOtherText: this.data.immunosuppressantNameOtherText || undefined,
oralHormoneDosage:
this.data.oralHormoneDosage || this.data.customDosage
? Number(this.data.oralHormoneDosage || this.data.customDosage)
: undefined,
images,
}
if (this.data.draftId) {
data.id = this.data.draftId
}
return wx
.ajax({
method: 'POST',
url: '?r=igg4/medical-visit/save-draft',
data,
})
.then((res: any) => {
if (res && res.id && !this.data.draftId) {
this.setData({ draftId: res.id })
}
if (res && res.recordStatus) {
this.setData({ recordStatus: res.recordStatus })
}
})
.catch(() => {})
.finally(() => {
this.setData({ saving: false })
})
},
autoSave() {
this._updateAlertBeforeUnload()
if (this._saveDraftDebounced) {
this._saveDraftDebounced()
}
}, },
onLoad() {},
submit() {
if (!this.data.draftId) {
wx.showToast({ title: '请先保存草稿', icon: 'none' })
return
}
wx.ajax({
method: 'POST',
url: '?r=igg4/medical-visit/submit',
data: { id: this.data.draftId },
loading: true,
}).then(() => {
this.setData({ submitted: true })
wx.disableAlertBeforeUnload()
wx.showToast({ title: '提交成功', icon: 'success' })
setTimeout(() => {
wx.navigateBack()
}, 1500)
})
},
// ========== Step1 事件 ==========
handleVisitDateChange(e: WechatMiniprogram.PickerChange) {
this.setData({ visitDate: e.detail.value as string })
this.autoSave()
},
handleNextVisitDateChange(e: WechatMiniprogram.PickerChange) {
this.setData({ nextVisitDate: e.detail.value as string })
this.autoSave()
},
handleStep1Next() {
if (!this.data.visitDate && !this.data.nextVisitDate) {
this.setData({ toastShow: true, toastType: 'oneWrite', toastParams: {} })
return
}
this.saveDraft().then(() => {
this.setData({ step: 2 })
})
},
// ========== Step2 事件 ==========
handleSetData(e: WechatMiniprogram.CustomEvent) { handleSetData(e: WechatMiniprogram.CustomEvent) {
const imgUrl = e.detail[0].imgUrl const imageType = e.currentTarget.dataset.type as string
const file = e.detail[0]
const filesKey = `${imageType}Files`
const currentFiles = this.data[filesKey as any] as any[] || []
const newFile = {
imgUrl: file.imgUrl,
fileUrl: file.imgUrl,
type: 'image',
imageType,
}
this.setData({ this.setData({
uploadImg: imgUrl, [filesKey]: [...currentFiles, newFile],
audit: true,
}) })
this.autoSave()
},
handleDeleteFile(e: WechatMiniprogram.CustomEvent) {
const imageType = e.currentTarget.dataset.type as string
const index = e.detail.index
this._removeFile(imageType, index)
}, },
handleExample(e) {
handleDeleteImage(e: WechatMiniprogram.CustomEvent) {
const imageType = e.currentTarget.dataset.type as string
const index = e.currentTarget.dataset.index as number
this._removeFile(imageType, index)
},
_removeFile(imageType: string, index: number) {
const filesKey = `${imageType}Files`
const currentFiles = this.data[filesKey as any] as any[]
currentFiles.splice(index, 1)
this.setData({
[filesKey]: [...currentFiles],
})
this.autoSave()
},
handlePreviewImage(e: WechatMiniprogram.CustomEvent) {
const imageType = e.currentTarget.dataset.type as string
const filesKey = `${imageType}Files`
const files = this.data[filesKey as any] as any[]
const urls = files.map((f) => f.fileUrl || f.imgUrl)
wx.previewImage({ urls })
},
handleExample(e: WechatMiniprogram.CustomEvent) {
wx.previewImage({ wx.previewImage({
urls: [e.currentTarget.dataset.url], urls: [e.currentTarget.dataset.url],
}) })
}, },
handleStep2Prev() {
this.saveDraft().then(() => {
this.setData({ step: 1 })
})
},
handleStep2Next() {
const hasImage = Object.keys(IMAGE_TYPE_MAP).some(
(key) => (this.data[`${key}Files` as any] as any[]).length > 0,
)
if (!hasImage) {
this.setData({ toastShow: true, toastType: 'oneWrite', toastParams: {} })
return
}
this.saveDraft().then(() => {
this.setData({ step: 3 })
})
},
// ========== Step3 事件 ==========
handleIgG4Input(e: WechatMiniprogram.Input) {
this.setData({ igG4Value: e.detail.value })
this.autoSave()
},
handleMedicationToggle(e: WechatMiniprogram.CustomEvent) {
const id = e.currentTarget.dataset.id as number
const options = this.data.medicationsOptions.map((item) => {
if (item.id === id) return { ...item, active: !item.active }
return item
})
const usedIds = options.filter((item) => item.active).map((item) => item.id)
const isOtherActive = usedIds.includes(99)
this.setData({
medicationsOptions: options,
medicationsUsed: usedIds,
medicationsOtherShow: isOtherActive,
medicationsUsedOtherText: isOtherActive ? this.data.medicationsUsedOtherText : '',
})
this.autoSave()
},
handleMedicationsUsedOtherInput(e: WechatMiniprogram.Input) {
this.setData({ medicationsUsedOtherText: e.detail.value })
this.autoSave()
},
handleImmunosuppressantToggle(e: WechatMiniprogram.CustomEvent) {
const id = e.currentTarget.dataset.id as number
const options = this.data.immunosuppressantOptions.map((item) => {
if (item.id === id) return { ...item, active: !item.active }
return item
})
const usedIds = options.filter((item) => item.active).map((item) => item.id)
const isOtherActive = usedIds.includes(99)
this.setData({
immunosuppressantOptions: options,
immunosuppressantName: usedIds,
immunosuppressantOtherShow: isOtherActive,
immunosuppressantNameOtherText: isOtherActive ? this.data.immunosuppressantNameOtherText : '',
})
this.autoSave()
},
handleImmunosuppressantNameOtherInput(e: WechatMiniprogram.Input) {
this.setData({ immunosuppressantNameOtherText: e.detail.value })
this.autoSave()
},
handleDosageSelect(e: WechatMiniprogram.CustomEvent) {
const val = e.currentTarget.dataset.val as number
this.setData({
oralHormoneDosage: this.data.oralHormoneDosage === val ? 0 : val,
customDosage: '',
})
this.autoSave()
},
handleCustomDosageInput(e: WechatMiniprogram.Input) {
this.setData({
customDosage: e.detail.value,
oralHormoneDosage: 0,
})
this.autoSave()
},
handleStep3Prev() {
this.saveDraft().then(() => {
this.setData({ step: 2 })
})
},
handleSubmit() {
const hasData =
this.data.igG4Value ||
this.data.medicationsUsed.length ||
this.data.immunosuppressantName.length ||
this.data.oralHormoneDosage ||
this.data.customDosage
if (!hasData) {
this.setData({ toastShow: true, toastType: 'oneWrite', toastParams: {} })
return
}
this.saveDraft().then(() => {
// 编辑已提交的记录,无需再调用提交接口
if (this.data.recordStatus === 2) {
this.setData({ submitted: true })
wx.disableAlertBeforeUnload()
wx.navigateBack()
return
}
this.submit()
})
},
// ========== Toast 事件 ==========
handleToastOk() { handleToastOk() {
const { toastType } = this.data this.setData({
toastShow: false,
toastType: '',
toastParams: {},
})
}, },
handleToastCancel(_e = null) {
const { toastType } = this.data handleToastCancel() {
this.setData({ this.setData({
toastShow: false, toastShow: false,
toastType: '', toastType: '',
toastParams: '', toastParams: {},
}) })
}, },
}) })

275
src/gift/pages/record/index.wxml

@ -21,6 +21,8 @@
<view class="name {{step==3 && 'current'}}">核对指标</view> <view class="name {{step==3 && 'current'}}">核对指标</view>
</view> </view>
</view> </view>
<!-- Step1: 时间记录 -->
<block wx:if="{{step==1}}"> <block wx:if="{{step==1}}">
<view class="step1"> <view class="step1">
<view class="s-header"> <view class="s-header">
@ -30,117 +32,204 @@
<view class="s-form"> <view class="s-form">
<view class="form-item"> <view class="form-item">
<view class="label">我的本次就诊时间</view> <view class="label">我的本次就诊时间</view>
<picker> <picker mode="date" value="{{visitDate}}" end="{{today}}" bind:change="handleVisitDateChange">
<view class="date"> <view class="date">
<image class="icon1" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon134' : 'icon114'}}.png?t={{Timestamp}}"></image> <image
<view class="content" data-place="选择开始时间"></view> class="icon1"
src="{{imageUrl}}{{theme === 'DRUG' ? 'icon134' : 'icon114'}}.png?t={{Timestamp}}"
></image>
<view class="content {{visitDate && 'active'}}">{{visitDate || '选择就诊时间'}}</view>
<image class="icon2" src="{{imageUrl}}icon115.png?t={{Timestamp}}"></image> <image class="icon2" src="{{imageUrl}}icon115.png?t={{Timestamp}}"></image>
</view> </view>
</picker> </picker>
</view> </view>
<view class="form-item"> <view class="form-item">
<view class="label">我的下次就诊时间</view> <view class="label">我的下次就诊时间</view>
<picker> <picker mode="date" value="{{nextVisitDate}}" start="{{visitDate}}" bind:change="handleNextVisitDateChange">
<view class="date"> <view class="date">
<image class="icon1" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon134' : 'icon114'}}.png?t={{Timestamp}}"></image> <image
<view class="content" data-place="选择开始时间"></view> class="icon1"
src="{{imageUrl}}{{theme === 'DRUG' ? 'icon134' : 'icon114'}}.png?t={{Timestamp}}"
></image>
<view class="content {{nextVisitDate && 'active'}}">{{nextVisitDate || '选择复诊时间'}}</view>
<image class="icon2" src="{{imageUrl}}icon115.png?t={{Timestamp}}"></image> <image class="icon2" src="{{imageUrl}}icon115.png?t={{Timestamp}}"></image>
</view> </view>
</picker> </picker>
</view> </view>
</view> </view>
</view> </view>
<view class="step1-btn">下一步</view> <view class="step1-btn" bind:tap="handleStep1Next">下一步</view>
</block> </block>
<!-- Step2: 上传资料 -->
<view class="step2" wx:if="{{step==2}}"> <view class="step2" wx:if="{{step==2}}">
<view class="tip-card">门诊材料会分类存储到 "我的-健康档案",方便您查看对比。</view> <view class="tip-card">门诊材料会分类存储到 "我的-健康档案",方便您查看对比。</view>
<!-- 门诊病历及处方 -->
<view class="form-card"> <view class="form-card">
<view class="f-header"> <view class="f-header">
<view class="label required">门诊病历及处方</view> <view class="label required">门诊病历及处方</view>
<view class="example" bind:tap="handleExample" data-url="{{imageUrl}}{{theme === 'DRUG' ? 'bg42' : 'bg40'}}.png?t={{Timestamp}}">示意</view> <view
class="example"
bind:tap="handleExample"
data-url="{{imageUrl}}{{theme === 'DRUG' ? 'bg42' : 'bg40'}}.png?t={{Timestamp}}"
>
示意
</view>
</view> </view>
<view class="content">医生手写或电脑打印的病历、处方</view> <view class="content">医生手写或电脑打印的病历、处方</view>
<uploadFile fileTypes="{{['image']}}" bind:setData="handleSetData" maxNum="{{1}}" isSlot="{{true}}"> <view class="upload-wrap">
<view class="upload-container" wx:if="{{!uploadImg}}"> <view class="upload-preview" wx:for="{{outpatientRecordFiles}}" wx:key="index" data-type="outpatientRecord" data-index="{{index}}" bind:tap="handlePreviewImage">
<image class="icon" wx:if="{{!uploadImg}}" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon135' : 'icon48'}}.png?t={{Timestamp}}"></image> <image class="upload-img" mode="aspectFill" src="{{item.imgUrl}}"></image>
<view class="name" wx:if="{{!uploadImg}}">添加文件照片</view> <view class="del" catch:tap="handleDeleteImage" data-type="outpatientRecord" data-index="{{index}}">
</view>
<view class="upload-preview" wx:else>
<image class="upload-img" mode="aspectFill" src="{{uploadImg}}"></image>
<view class="del">
<van-icon name="cross" /> <van-icon name="cross" />
</view> </view>
</view> </view>
<uploadFile
fileTypes="{{['image']}}"
bind:setData="handleSetData"
data-type="outpatientRecord"
maxNum="{{10}}"
isSlot="{{true}}"
>
<view class="upload-container">
<image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon135' : 'icon48'}}.png?t={{Timestamp}}"></image>
<view class="name">添加文件照片</view>
</view>
</uploadFile> </uploadFile>
</view> </view>
</view>
<!-- 检验报告 -->
<view class="form-card"> <view class="form-card">
<view class="f-header"> <view class="f-header">
<view class="label required">检验报告(IgG4化验单等)</view> <view class="label required">检验报告(IgG4化验单等)</view>
<view class="example" bind:tap="handleExample" data-url="{{imageUrl}}{{theme === 'DRUG' ? 'bg41' : 'bg39'}}.png?t={{Timestamp}}">示意</view> <view
class="example"
bind:tap="handleExample"
data-url="{{imageUrl}}{{theme === 'DRUG' ? 'bg41' : 'bg39'}}.png?t={{Timestamp}}"
>
示意
</view>
</view> </view>
<view class="content">抽血检测的IgG4、总免疫球蛋白、ESR等</view> <view class="content">抽血检测的IgG4、总免疫球蛋白、ESR等</view>
<uploadFile fileTypes="{{['image']}}" bind:setData="handleSetData" maxNum="{{1}}" isSlot="{{true}}"> <view class="upload-wrap">
<view class="upload-container" wx:if="{{!uploadImg}}"> <view class="upload-preview" wx:for="{{labReportFiles}}" wx:key="index" data-type="labReport" data-index="{{index}}" bind:tap="handlePreviewImage">
<image class="icon" wx:if="{{!uploadImg}}" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon135' : 'icon48'}}.png?t={{Timestamp}}"></image> <image class="upload-img" mode="aspectFill" src="{{item.imgUrl}}"></image>
<view class="name" wx:if="{{!uploadImg}}">添加文件照片</view> <view class="del" catch:tap="handleDeleteImage" data-type="labReport" data-index="{{index}}">
<van-icon name="cross" />
</view>
</view> </view>
<view class="upload-preview" wx:else> <uploadFile
<image class="upload-img" mode="aspectFill" src="{{uploadImg}}"></image> fileTypes="{{['image']}}"
bind:setData="handleSetData"
data-type="labReport"
maxNum="{{10}}"
isSlot="{{true}}"
>
<view class="upload-container">
<image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon135' : 'icon48'}}.png?t={{Timestamp}}"></image>
<view class="name">添加文件照片</view>
</view> </view>
</uploadFile> </uploadFile>
</view> </view>
</view>
<!-- 影像学检查 -->
<view class="form-card"> <view class="form-card">
<view class="f-header"> <view class="f-header">
<view class="label">影像学检查(CT/超声/核磁)</view> <view class="label">影像学检查(CT/超声/核磁)</view>
</view> </view>
<view class="content">超声、CT、核磁共振的描述结论、电子胶片</view> <view class="content">超声、CT、核磁共振的描述结论、电子胶片</view>
<uploadFile fileTypes="{{['image']}}" bind:setData="handleSetData" maxNum="{{1}}" isSlot="{{true}}"> <view class="upload-wrap">
<view class="upload-container" wx:if="{{!uploadImg}}"> <view class="upload-preview" wx:for="{{imagingExamFiles}}" wx:key="index" data-type="imagingExam" data-index="{{index}}" bind:tap="handlePreviewImage">
<image class="icon" wx:if="{{!uploadImg}}" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon135' : 'icon48'}}.png?t={{Timestamp}}"></image> <image class="upload-img" mode="aspectFill" src="{{item.imgUrl}}"></image>
<view class="name" wx:if="{{!uploadImg}}">添加文件照片</view> <view class="del" catch:tap="handleDeleteImage" data-type="imagingExam" data-index="{{index}}">
<van-icon name="cross" />
</view> </view>
<view class="upload-preview" wx:else> </view>
<image class="upload-img" mode="aspectFill" src="{{uploadImg}}"></image> <uploadFile
fileTypes="{{['image']}}"
bind:setData="handleSetData"
data-type="imagingExam"
maxNum="{{10}}"
isSlot="{{true}}"
>
<view class="upload-container">
<image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon135' : 'icon48'}}.png?t={{Timestamp}}"></image>
<view class="name">添加文件照片</view>
</view> </view>
</uploadFile> </uploadFile>
</view> </view>
</view>
<!-- 病理诊断及活检 -->
<view class="form-card"> <view class="form-card">
<view class="f-header"> <view class="f-header">
<view class="label">病理诊断及活检</view> <view class="label">病理诊断及活检</view>
</view> </view>
<view class="content">受累组织穿刺或切除免疫组化报告</view> <view class="content">受累组织穿刺或切除免疫组化报告</view>
<uploadFile fileTypes="{{['image']}}" bind:setData="handleSetData" maxNum="{{1}}" isSlot="{{true}}"> <view class="upload-wrap">
<view class="upload-container" wx:if="{{!uploadImg}}"> <view class="upload-preview" wx:for="{{pathologyFiles}}" wx:key="index" data-type="pathology" data-index="{{index}}" bind:tap="handlePreviewImage">
<image class="icon" wx:if="{{!uploadImg}}" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon135' : 'icon48'}}.png?t={{Timestamp}}"></image> <image class="upload-img" mode="aspectFill" src="{{item.imgUrl}}"></image>
<view class="name" wx:if="{{!uploadImg}}">添加文件照片</view> <view class="del" catch:tap="handleDeleteImage" data-type="pathology" data-index="{{index}}">
<van-icon name="cross" />
</view>
</view> </view>
<view class="upload-preview" wx:else> <uploadFile
<image class="upload-img" mode="aspectFill" src="{{uploadImg}}"></image> fileTypes="{{['image']}}"
bind:setData="handleSetData"
data-type="pathology"
maxNum="{{10}}"
isSlot="{{true}}"
>
<view class="upload-container">
<image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon135' : 'icon48'}}.png?t={{Timestamp}}"></image>
<view class="name">添加文件照片</view>
</view> </view>
</uploadFile> </uploadFile>
</view> </view>
</view>
<!-- 外周血免疫功能评估 -->
<view class="form-card"> <view class="form-card">
<view class="f-header"> <view class="f-header">
<view class="label">外周血免疫功能评估</view> <view class="label">外周血免疫功能评估</view>
</view> </view>
<view class="content">免疫细胞亚群、淋巴细胞比例测定</view> <view class="content">免疫细胞亚群、淋巴细胞比例测定</view>
<uploadFile fileTypes="{{['image']}}" bind:setData="handleSetData" maxNum="{{1}}" isSlot="{{true}}"> <view class="upload-wrap">
<view class="upload-container" wx:if="{{!uploadImg}}"> <view class="upload-preview" wx:for="{{immuneFunctionFiles}}" wx:key="index" data-type="immuneFunction" data-index="{{index}}" bind:tap="handlePreviewImage">
<image class="icon" wx:if="{{!uploadImg}}" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon135' : 'icon48'}}.png?t={{Timestamp}}"></image> <image class="upload-img" mode="aspectFill" src="{{item.imgUrl}}"></image>
<view class="name" wx:if="{{!uploadImg}}">添加文件照片</view> <view class="del" catch:tap="handleDeleteImage" data-type="immuneFunction" data-index="{{index}}">
<van-icon name="cross" />
</view>
</view> </view>
<view class="upload-preview" wx:else> <uploadFile
<image class="upload-img" mode="aspectFill" src="{{uploadImg}}"></image> fileTypes="{{['image']}}"
bind:setData="handleSetData"
data-type="immuneFunction"
maxNum="{{10}}"
isSlot="{{true}}"
>
<view class="upload-container">
<image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon135' : 'icon48'}}.png?t={{Timestamp}}"></image>
<view class="name">添加文件照片</view>
</view> </view>
</uploadFile> </uploadFile>
</view> </view>
</view>
<view class="step2-footer"> <view class="step2-footer">
<view class="prev">上一页</view> <view class="prev" bind:tap="handleStep2Prev">上一页</view>
<view class="next">下一页</view> <view class="next" bind:tap="handleStep2Next">下一页</view>
</view> </view>
</view> </view>
<!-- Step3: 核对指标 -->
<view class="step3" wx:if="{{step==3}}"> <view class="step3" wx:if="{{step==3}}">
<view class="tip-card">请填写本次就诊的以下信息,方便医生关注您的健康情况</view> <view class="tip-card">请填写本次就诊的以下信息,方便医生关注您的健康情况</view>
<!-- IgG4 数值 -->
<view class="form-card"> <view class="form-card">
<view class="f-header"> <view class="f-header">
<view class="label doboule"> <view class="label doboule">
@ -153,67 +242,119 @@
</view> </view>
<view class="num"> <view class="num">
<view class="wrap"> <view class="wrap">
<input class="input" placeholder-class="place-input" type="number" placeholder="请输入" maxlength="{{5}}" /> <input
<image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon136' : 'icon117'}}.png?t={{Timestamp}}"></image> class="input"
placeholder-class="place-input"
type="digit"
placeholder="请输入"
maxlength="{{8}}"
value="{{igG4Value}}"
bindinput="handleIgG4Input"
/>
<image
class="icon"
src="{{imageUrl}}{{theme === 'DRUG' ? 'icon136' : 'icon117'}}.png?t={{Timestamp}}"
></image>
</view> </view>
<view class="sub">mg/dL</view> <view class="sub">mg/dL</view>
</view> </view>
</view> </view>
<!-- 本次就诊已使用的药物 -->
<view class="form-card"> <view class="form-card">
<view class="f-header"> <view class="f-header">
<view class="label">本次就诊已使用的药物</view> <view class="label">本次就诊已使用的药物</view>
</view> </view>
<view class="check-group col1">
<view class="check-group"> <view
<view class="check-item active"> class="check-item {{item.active && 'active'}}"
wx:for="{{medicationsOptions}}"
wx:key="id"
bind:tap="handleMedicationToggle"
data-id="{{item.id}}"
>
<view class="check"> <view class="check">
<van-icon name="success" class="icon" /> <van-icon name="success" class="icon" />
</view> </view>
<view class="content">没有使用激素</view> <view class="content">{{item.label}}</view>
</view> </view>
<view class="check-item">
<view class="check">
<van-icon name="success" class="icon" />
</view>
<view class="content">没有使用激素</view>
</view> </view>
<view class="custom-num" wx:if="{{medicationsOtherShow}}" style="margin-top: 20rpx;">
<input
class="input"
placeholder-class="place-input"
maxlength="{{20}}"
placeholder="请输入其他药物名称"
value="{{medicationsUsedOtherText}}"
bindinput="handleMedicationsUsedOtherInput"
/>
</view> </view>
</view> </view>
<!-- 口服激素每日用量 -->
<view class="form-card"> <view class="form-card">
<view class="f-header"> <view class="f-header">
<view class="label">最近一周,口服激素的每日用量</view> <view class="label">最近一周,口服激素的每日用量</view>
</view> </view>
<view class="num-select"> <view class="num-select">
<view class="n-item" wx:for="{{6}}" wx:key="index">8片</view> <view
class="n-item {{oralHormoneDosage === item && 'active'}}"
wx:for="{{hormoneDosageOptions}}"
wx:key="*this"
bind:tap="handleDosageSelect"
data-val="{{item}}"
>
{{item}}片
</view>
</view> </view>
<view class="custom-num"> <view class="custom-num">
<input class="input" placeholder-class="place-input" type="number" maxlength="{{5}}" placeholder="请输入" /> <input
class="input"
placeholder-class="place-input"
type="digit"
maxlength="{{5}}"
placeholder="自定义用量"
value="{{customDosage}}"
bindinput="handleCustomDosageInput"
/>
<view class="sub">片</view> <view class="sub">片</view>
</view> </view>
</view> </view>
<!-- 免疫抑制剂名称 -->
<view class="form-card"> <view class="form-card">
<view class="f-header"> <view class="f-header">
<view class="label">正在使用的免疫抑制剂名称</view> <view class="label">正在使用的免疫抑制剂名称</view>
</view> </view>
<view class="check-group col1">
<view class="check-group"> <view
<view class="check-item active"> class="check-item {{item.active && 'active'}}"
wx:for="{{immunosuppressantOptions}}"
wx:key="id"
bind:tap="handleImmunosuppressantToggle"
data-id="{{item.id}}"
>
<view class="check"> <view class="check">
<van-icon name="success" class="icon" /> <van-icon name="success" class="icon" />
</view> </view>
<view class="content">没有使用激素</view> <view class="content">{{item.label}}</view>
</view> </view>
<view class="check-item">
<view class="check">
<van-icon name="success" class="icon" />
</view>
<view class="content">没有使用激素</view>
</view> </view>
<view class="custom-num" wx:if="{{immunosuppressantOtherShow}}" style="margin-top: 20rpx;">
<input
class="input"
placeholder-class="place-input"
maxlength="{{20}}"
placeholder="请输入其他免疫抑制剂名称"
value="{{immunosuppressantNameOtherText}}"
bindinput="handleImmunosuppressantNameOtherInput"
/>
</view> </view>
</view> </view>
<view class="step3-footer"> <view class="step3-footer">
<view class="prev">上一页</view> <view class="prev" bind:tap="handleStep3Prev">上一页</view>
<view class="submit">提交</view> <view class="submit" bind:tap="handleSubmit">提交</view>
</view> </view>
</view> </view>
</view> </view>

1
src/gift/pages/recordDetail/index.json

@ -2,7 +2,6 @@
"navigationBarTitleText": "就诊记录详情页", "navigationBarTitleText": "就诊记录详情页",
"navigationBarBackgroundColor": "#F6F6F6", "navigationBarBackgroundColor": "#F6F6F6",
"usingComponents": { "usingComponents": {
"uploadFile": "/components/uploadFile/index",
"van-icon": "@vant/weapp/icon/index" "van-icon": "@vant/weapp/icon/index"
} }
} }

153
src/gift/pages/recordDetail/index.ts

@ -1,8 +1,153 @@
const _app = getApp<IAppOption>(); const MEDICATIONS_MAP: Record<number, string> = {
1: '没有使用激素',
2: '激素',
3: '免疫抑制剂',
4: '靶向CD19生物制剂',
5: '靶向CD20生物制剂',
99: '其他',
}
const IMMUNOSUPPRESSANT_MAP: Record<number, string> = {
1: '吗替麦考酚酯',
2: '替唑嘌呤',
3: '环磷酰胺',
4: '来氟米特',
5: '甲氨蝶呤',
6: '环孢素A',
7: '他克莫司',
8: '艾拉莫德',
99: '其他',
}
const IMAGE_TYPE_LABELS: Record<string, string> = {
outpatientRecord: '门诊病历及处方',
labReport: '检验报告',
imagingExam: '影像学检查',
pathology: '病理诊断及活检',
immuneFunction: '外周血免疫功能评估',
}
Page({ Page({
data: {}, data: {
onLoad() {}, id: 0,
}); visitDate: '',
nextVisitDate: '',
displayIgG4Value: '' as string | number | null,
medicationsLabel: '',
oralHormoneDosage: '' as string | number | null,
immunosuppressantLabel: '',
reminderEnabled: 2,
imageGroups: [] as { type: string; label: string; list: string[] }[],
},
onLoad(options: any) {
if (options.id) {
this.setData({ id: Number(options.id) })
this.getDetail()
this.getReminderConfig()
}
},
// ========== API ==========
getDetail() {
wx.ajax({
method: 'GET',
url: '?r=igg4/medical-visit/detail',
data: { id: this.data.id },
}).then((res: any) => {
if (!res) return
const medicationsLabel = (res.medicationsUsed || [])
.map((id: number) => {
if (id === 99 && res.medicationsUsedOtherText) return `其他:${res.medicationsUsedOtherText}`
return MEDICATIONS_MAP[id]
})
.filter(Boolean)
.join('、')
const immunosuppressantLabel = (res.immunosuppressantName || [])
.map((id: number) => {
if (id === 99 && res.immunosuppressantNameOtherText) return `其他:${res.immunosuppressantNameOtherText}`
return IMMUNOSUPPRESSANT_MAP[id]
})
.filter(Boolean)
.join('、')
const imageGroups: { type: string; label: string; list: string[] }[] = []
const images = res.images || {}
for (const type of Object.keys(IMAGE_TYPE_LABELS)) {
const list = (images[type] || []).map((img: any) => img.maskedImageUrl || img.imageUrl)
if (list.length) {
imageGroups.push({ type, label: IMAGE_TYPE_LABELS[type], list })
}
}
this.setData({
visitDate: res.visitDate || '',
nextVisitDate: res.nextVisitDate || '',
displayIgG4Value: res.displayIgG4Value,
medicationsLabel,
oralHormoneDosage: res.oralHormoneDosage,
immunosuppressantLabel,
imageGroups,
})
})
},
getReminderConfig() {
wx.ajax({
method: 'GET',
url: '?r=igg4/medical-visit/get-reminder-config',
}).then((res: any) => {
if (!res) return
this.setData({ reminderEnabled: res.reminderEnabled || 2 })
})
},
saveReminderConfig(e: any) {
const val = e.detail.value ? 1 : 2
this.setData({ reminderEnabled: val })
wx.ajax({
method: 'POST',
url: '?r=igg4/medical-visit/save-reminder-config',
data: { reminderEnabled: val },
})
},
// ========== 操作 ==========
handleDelete() {
wx.showModal({
title: '确认删除记录?',
content: `删除${this.data.visitDate || ''}记录,此操作不可逆,相关照片将永久删除`,
confirmText: '确认删除',
confirmColor: '#ee0a24',
success: (res) => {
if (!res.confirm) return
wx.ajax({
method: 'POST',
url: '?r=igg4/medical-visit/delete',
data: { id: this.data.id },
}).then((res: any) => {
if (!res) return
wx.navigateBack()
})
},
})
},
handleEdit() {
wx.navigateTo({ url: `/gift/pages/record/index?id=${this.data.id}` })
},
handlePreviewImage(e: any) {
const { url, type } = e.currentTarget.dataset
const group = this.data.imageGroups.find((g) => g.type === type)
if (!group) return
wx.previewImage({
current: url,
urls: group.list,
})
},
})
export {} export {}

42
src/gift/pages/recordDetail/index.wxml

@ -4,52 +4,62 @@
<image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon137' : 'icon121'}}.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon137' : 'icon121'}}.png?t={{Timestamp}}"></image>
<view class="col"> <view class="col">
<view class="name">我的本次就诊时间</view> <view class="name">我的本次就诊时间</view>
<view class="date">2026-06-23</view> <view class="date">{{visitDate || '--'}}</view>
</view> </view>
</view> </view>
<view class="row"> <view class="row">
<image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon138' : 'icon122'}}.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon138' : 'icon122'}}.png?t={{Timestamp}}"></image>
<view class="col"> <view class="col">
<view class="name">我的下次就诊时间</view> <view class="name">我的下次就诊时间</view>
<view class="date">2026-07-23</view> <view class="date">{{nextVisitDate || '--'}}</view>
</view> </view>
</view> </view>
</view> </view>
<view class="switch-wrap"> <view class="switch-wrap">
<view class="name">复诊提醒</view> <view class="name">复诊提醒</view>
<switch class="switch" color="{{theme === 'DRUG' ? 'rgba(89, 86, 233, 1)' : 'rgba(14, 182, 109, 1)'}}"></switch> <switch class="switch" checked="{{reminderEnabled === 1}}" bind:change="saveReminderConfig" color="{{theme === 'DRUG' ? 'rgba(89, 86, 233, 1)' : 'rgba(14, 182, 109, 1)'}}" />
</view> </view>
<view class="photos"> <view class="photos" wx:for="{{imageGroups}}" wx:key="type">
<view class="p-header">门诊病历及处方</view> <view class="p-header">{{item.label}}</view>
<view class="p-container"> <view class="p-container">
<image class="photo" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> <image
class="photo"
wx:for="{{item.list}}"
wx:key="*this"
wx:for-item="imgUrl"
src="{{imgUrl}}"
mode="aspectFill"
bind:tap="handlePreviewImage"
data-url="{{imgUrl}}"
data-type="{{item.type}}"
></image>
</view> </view>
</view> </view>
<view class="container"> <view class="container">
<view class="row"> <view class="row" wx:if="{{displayIgG4Value !== null && displayIgG4Value !== ''}}">
<view class="name">IgG4值</view> <view class="name">IgG4值</view>
<view class="content">115 mg/dL</view> <view class="content">{{displayIgG4Value}} mg/dL</view>
</view> </view>
<view class="row"> <view class="row" wx:if="{{medicationsLabel}}">
<view class="name">本次就诊已使用的药物</view> <view class="name">本次就诊已使用的药物</view>
<view class="content">激素免疫抑制剂</view> <view class="content">{{medicationsLabel}}</view>
</view> </view>
<view class="row"> <view class="row" wx:if="{{oralHormoneDosage !== null && oralHormoneDosage !== ''}}">
<view class="name">最近一周,每日激素使用剂量</view> <view class="name">最近一周,每日激素使用剂量</view>
<view class="content">5mg/天</view> <view class="content">{{oralHormoneDosage}}mg/天</view>
</view> </view>
<view class="row"> <view class="row" wx:if="{{immunosuppressantLabel}}">
<view class="name">正在使用的免疫抑制剂名称</view> <view class="name">正在使用的免疫抑制剂名称</view>
<view class="content">吗替麦考酚酯(骁悉、麦考芬)</view> <view class="content">{{immunosuppressantLabel}}</view>
</view> </view>
</view> </view>
<view class="footer"> <view class="footer">
<view class="col"> <view class="col" bind:tap="handleDelete">
<image class="icon" src="{{imageUrl}}icon123.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon123.png?t={{Timestamp}}"></image>
<view class="name">删除</view> <view class="name">删除</view>
</view> </view>
<view class="line"></view> <view class="line"></view>
<view class="col"> <view class="col" bind:tap="handleEdit">
<image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon139' : 'icon124'}}.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon139' : 'icon124'}}.png?t={{Timestamp}}"></image>
<view class="name name1">编辑</view> <view class="name name1">编辑</view>
</view> </view>

3
src/gift/pages/recordList/index.json

@ -4,6 +4,7 @@
"usingComponents": { "usingComponents": {
"uploadFile": "/components/uploadFile/index", "uploadFile": "/components/uploadFile/index",
"van-icon": "@vant/weapp/icon/index", "van-icon": "@vant/weapp/icon/index",
"ec-canvas": "/components/ec-canvas/ec-canvas" "ec-canvas": "/components/ec-canvas/ec-canvas",
"pagination": "/components/pagination/index"
} }
} }

7
src/gift/pages/recordList/index.scss

@ -55,6 +55,13 @@ page {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 14rpx; gap: 14rpx;
.start,
.end {
color: rgba(105, 104, 110, 1);
&.active {
color: rgba(34, 34, 34, 1);
}
}
.line { .line {
margin: 0 10rpx; margin: 0 10rpx;
} }

281
src/gift/pages/recordList/index.ts

@ -2,28 +2,181 @@ import dayjs from 'dayjs'
const _app = getApp<IAppOption>() const _app = getApp<IAppOption>()
let echarts: any = null let echarts: any = null
const MEDICATIONS_MAP: Record<number, string> = {
1: '没有使用激素',
2: '激素',
3: '免疫抑制剂',
4: '靶向CD19生物制剂',
5: '靶向CD20生物制剂',
99: '其他',
}
const IMMUNOSUPPRESSANT_MAP: Record<number, string> = {
1: '吗替麦考酚酯',
2: '替唑嘌呤',
3: '环磷酰胺',
4: '来氟米特',
5: '甲氨蝶呤',
6: '环孢素A',
7: '他克莫司',
8: '艾拉莫德',
99: '其他',
}
Page({ Page({
data: {}, data: {
list: [] as any[],
pagination: { count: 0, page: 1, pages: 1 },
year: 0 as number,
yearOptions: [] as { label: string; value: number }[],
beginDate: '',
endDate: '',
// 用户头部
latestIgG4: '' as string | number,
// 图表
chartUnit: '',
chartList: [] as any[],
},
ecDataTrendComponent: null as any, ecDataTrendComponent: null as any,
async onLoad() {
echarts = await require.async('../../../resource/components/echart/echarts.js') onLoad() {
this.ecDataTrendComponent = this.selectComponent('#chart1') echarts = require.async('../../../resource/components/echart/echarts.js').then((mod: any) => {
this.initChart() echarts = mod
})
// 默认按年,筛选前一年
const lastYear = dayjs().subtract(1, 'year').year()
this.setData({ selectedYear: lastYear, selectedYearLabel: `${lastYear}` })
_app.waitLogin().then(() => {
this.getLatestRecord()
this.getList()
this.getIndicatorHistory()
})
},
onShow() {
if (this.data.list.length) {
this.getLatestRecord()
this.getList(true)
}
},
// ========== API ==========
getList(reset = false) {
const page = reset ? 1 : this.data.pagination.page
const data: Record<string, any> = { page, pageSize: 10 }
wx.ajax({
method: 'GET',
url: '?r=igg4/medical-visit/list',
data,
}).then((res: any) => {
if (!res) return
const list = (res.list || []).map((item: any) => ({
...item,
medicationLabels: (item.medicationsUsed || []).map((id: number) => MEDICATIONS_MAP[id]).filter(Boolean),
immunosuppressantLabels: (item.immunosuppressantName || [])
.map((id: number) => IMMUNOSUPPRESSANT_MAP[id])
.filter(Boolean),
statusText: item.recordStatus === 1 ? '草稿' : '已提交',
}))
// 提取年份选项
const yearSet = new Set<number>()
for (const item of res.list || []) {
if (item.visitDate) yearSet.add(Number(item.visitDate.substring(0, 4)))
}
const yearOptions = [...yearSet].sort((a, b) => b - a).map((y) => ({ label: `${y}`, value: y }))
this.setData({
list: reset ? list : [...this.data.list, ...list],
pagination: {
count: res.totalCount || 0,
page: res.page || 1,
pages: res.pages || 1,
}, },
yearOptions: this.data.yearOptions.length ? this.data.yearOptions : yearOptions,
})
})
},
getLatestRecord() {
wx.ajax({
method: 'GET',
url: '?r=igg4/medical-visit/get-latest-record',
}).then((res: any) => {
if (!res) {
this.setData({ latestIgG4: '' })
return
}
this.setData({ latestIgG4: res.displayIgG4Value ?? '' })
})
},
getIndicatorHistory() {
const data: Record<string, any> = { indicatorField: 'igG4' }
if (this.data.filterMode === 'year') {
data.year = this.data.selectedYear
} else {
data.count = this.data.filterCount || 10
}
wx.ajax({
method: 'GET',
url: '?r=igg4/medical-visit/indicator-history',
data,
}).then((res: any) => {
if (!res) return
this.setData({
chartUnit: res.unit || 'mg/dL',
chartList: res.list || [],
})
this.initChart(res.list || [])
})
},
handleDelete(e: any) {
const { id, date } = e.currentTarget.dataset
wx.showModal({
title: '确认删除记录?',
content: `删除${date}记录,此操作不可逆,相关照片将永久删除`,
confirmText: '确认删除',
confirmColor: '#cf5375',
success: (res) => {
if (!res.confirm) return
wx.ajax({
method: 'POST',
url: '?r=igg4/medical-visit/delete',
data: { id },
}).then((res: any) => {
if (!res) return
const list = this.data.list.filter((item: any) => item.id !== id)
this.setData({ list })
})
},
})
},
// ========== 图表 ==========
initChart(defaultList: any[] = []) {
if (!defaultList.length) return
if (!echarts || !this.ecDataTrendComponent) {
this.ecDataTrendComponent = this.selectComponent('#chart1')
if (!echarts || !this.ecDataTrendComponent) return
}
const list = defaultList
initChart(defaultList = []) {
const list: any = [
...defaultList,
{ Date: '2026-7-7', TotalScore: 100, InjectionBottles: false },
{ Date: '2026-7-8', TotalScore: 1000, InjectionBottles: false },
]
return new Promise((reslove) => {
const { theme } = this.data const { theme } = this.data
this.ecDataTrendComponent.init((canvas, width, height, dpr) => { this.ecDataTrendComponent.init((canvas: any, width: number, height: number, dpr: number) => {
const chart = echarts.init(canvas, null, { const chart = echarts.init(canvas, null, {
width, width,
height, height,
devicePixelRatio: dpr, // new devicePixelRatio: dpr,
}) })
canvas.setChart(chart) canvas.setChart(chart)
@ -31,15 +184,10 @@ Page({
tooltip: { tooltip: {
show: false, show: false,
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: { type: 'shadow' },
type: 'shadow',
},
confine: true, confine: true,
backgroundColor: 'rgba(0, 0, 0, 0.5)', backgroundColor: 'rgba(0, 0, 0, 0.5)',
textStyle: { textStyle: { color: '#fff', fontSize: 10 },
color: '#fff',
fontSize: 10,
},
order: 'seriesDesc', order: 'seriesDesc',
}, },
grid: { grid: {
@ -52,37 +200,28 @@ Page({
xAxis: [ xAxis: [
{ {
type: 'category', type: 'category',
axisTick: { axisTick: { alignWithLabel: true },
alignWithLabel: true,
},
axisLine: { axisLine: {
lineStyle: { lineStyle: { color: 'rgba(34, 34, 34, 0.20)', type: 'dashed' },
color: 'rgba(34, 34, 34, 0.20)',
type: 'dashed',
},
}, },
data: list.map((item) => dayjs(item.Date).format('MM-DD')), data: list.map((item: any) => dayjs(item.visitDate).format('MM-DD')),
}, },
], ],
yAxis: [ yAxis: [
{ {
type: 'value', type: 'value',
min: 200,
minInterval: 1, minInterval: 1,
axisLabel: { axisLabel: { color: 'rgba(34, 34, 34, 0.40)' },
color: 'rgba(34, 34, 34, 0.40)',
},
splitLine: { splitLine: {
lineStyle: { lineStyle: { color: 'rgba(34, 34, 34, 0.20)', type: 'dashed' },
color: 'rgba(34, 34, 34, 0.20)',
type: 'dashed',
},
}, },
}, },
], ],
series: [ series: [
{ {
name: '总分', name: 'IgG4',
data: list.length ? list.map((item) => item.TotalScore) : [25], data: list.length ? list.map((item: any) => item.value) : [0],
barWidth: '16', barWidth: '16',
label: { label: {
show: true, show: true,
@ -122,21 +261,12 @@ Page({
itemStyle: { itemStyle: {
color: theme === 'DRUG' ? 'rgba(89, 86, 233, 0.06)' : 'rgba(14, 182, 109, 0.06)', color: theme === 'DRUG' ? 'rgba(89, 86, 233, 0.06)' : 'rgba(14, 182, 109, 0.06)',
}, },
data: [ data: [[{ yAxis: 0 }, { yAxis: 135 }]],
[
{
yAxis: 0,
},
{
yAxis: 135,
},
],
],
}, },
}, },
], ],
dataZoom: { dataZoom: {
type: 'inside', // 有type这个属性,滚动条在最下面,也可以不行,写y:36,这表示距离顶端36px,一般就是在图上面。 type: 'inside',
startValue: 0, startValue: 0,
endValue: 4, endValue: 4,
filterMode: 'none', filterMode: 'none',
@ -144,20 +274,57 @@ Page({
} }
chart.setOption(option) chart.setOption(option)
reslove(chart)
return chart return chart
}) })
})
}, },
handleDetail() {
wx.navigateTo({ // ========== 筛选 ==========
url: '/gift/pages/recordDetail/index',
handleFilterModeChange(e: any) {
const mode = e.currentTarget.dataset.mode
this.setData({ filterMode: mode })
this.getIndicatorHistory()
},
handleYearChange(e: any) {
const idx = Number(e.detail.value)
const selected = this.data.yearOptions[idx]
if (!selected) return
this.setData({
selectedYear: selected.value,
selectedYearLabel: selected.label,
}) })
this.getIndicatorHistory()
},
handleCountChange(e: any) {
const idx = Number(e.detail.value)
const count = this.data.countOptions[idx]
this.setData({ filterCount: count })
this.getIndicatorHistory()
}, },
// ========== 导航 ==========
handleDetail(e: any) {
const { id, status } = e.currentTarget.dataset
if (status === 1) {
wx.navigateTo({ url: `/gift/pages/record/index?id=${id}` })
} else {
wx.navigateTo({ url: `/gift/pages/recordDetail/index?id=${id}` })
}
},
handleAdd() { handleAdd() {
wx.navigateTo({ wx.navigateTo({ url: '/gift/pages/record/index' })
url: '/gift/pages/record/index', },
})
onReachBottom() {
const { page, pages } = this.data.pagination
if (page < pages) {
this.setData({ 'pagination.page': page + 1 })
this.getList()
}
}, },
}) })

75
src/gift/pages/recordList/index.wxml

@ -4,40 +4,26 @@
张先生,您好 张先生,您好
<image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon131' : 'icon119'}}.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon131' : 'icon119'}}.png?t={{Timestamp}}"></image>
</view> </view>
<view class="content"> <view class="content" wx:if="{{latestIgG4 !== ''}}">
您的最近一次IgG4数值为: 您的最近一次IgG4数值为:
<text class="high">115 mg/dL</text> <text class="high">{{latestIgG4}} mg/dL</text>
</view> </view>
</view> </view>
<view class="chart"> <view class="chart" wx:if="{{chartList.length}}">
<view class="chart-header"> <view class="chart-header">
<picker> <picker range="{{yearOptions}}" range-key="label" bindchange="handleYearChange">
<view class="type"> <view class="type">
按年 {{year || '按年'}}
<van-icon class="icon" name="play" /> <van-icon class="icon" name="play" />
</view> </view>
</picker> </picker>
<view class="date"> <view class="date">
<picker <picker mode="date" fields="month" bindchange="handleBeginDateChange" value="{{beginDate}}" end="{{endDate}}">
bindchange="handleChange" <view class="start {{beginDate && 'active'}}">{{beginDate ? beginDate : '开始时间'}}</view>
class="start"
end="{{EndMonth}}"
fields="day"
mode="date"
model:value="{{BeginMonth}}"
>
2025.08
</picker> </picker>
<view class="line">-</view> <view class="line">-</view>
<picker <picker mode="date" fields="month" bindchange="handleEndDateChange" value="{{endDate}}" start="{{beginDate}}">
bindchange="handleChange" <view class="end {{endDate && 'active'}}">{{endDate ? endDate : '结束时间'}}</view>
class="end"
fields="day"
mode="date"
model:value="{{EndMonth}}"
start="{{BeginMonth}}"
>
2025.09
</picker> </picker>
<van-icon class="icon" name="play" /> <van-icon class="icon" name="play" />
</view> </view>
@ -45,7 +31,7 @@
<view class="chart-container"> <view class="chart-container">
<view class="c-header"> <view class="c-header">
<view class="title">血清IgG4趋势监测</view> <view class="title">血清IgG4趋势监测</view>
<view class="sub">单位mg/dL</view> <view class="sub">单位{{chartUnit}}</view>
</view> </view>
<view class="w-chart"> <view class="w-chart">
<ec-canvas id="chart1" ec="{{ ec }}"></ec-canvas> <ec-canvas id="chart1" ec="{{ ec }}"></ec-canvas>
@ -56,40 +42,55 @@
<view class="l-header"> <view class="l-header">
<view class="title">我的就诊历史总轴(按日期)</view> <view class="title">我的就诊历史总轴(按日期)</view>
</view> </view>
<view class="card" wx:for="{{10}}" wx:key="index" bind:tap="handleDetail"> <view
class="card"
wx:for="{{list}}"
wx:key="id"
bind:tap="handleDetail"
data-id="{{item.id}}"
data-status="{{item.recordStatus}}"
>
<view class="aside"> <view class="aside">
<view class="line-top"></view> <view class="line-top"></view>
<view class="circle"></view> <view class="circle"></view>
<view class="line-bottom"></view> <view class="line-bottom"></view>
</view> </view>
<view class="wrap"> <view class="wrap">
<view class="date">2025-06-11</view> <view class="date">{{item.visitDate}}</view>
<view class="container"> <view class="container">
<view class="del"><van-icon name="cross" /></view> <view class="del" catch:tap="handleDelete" data-id="{{item.id}}" data-date="{{item.visitDate}}">
<view class="name">lgG4值</view> <van-icon name="cross" />
</view>
<view class="name">IgG4值</view>
<view class="content"> <view class="content">
<view class="c-left"> <view class="c-left">
<view class="num">385</view> <view class="num">{{item.displayIgG4Value || '--'}}</view>
<view class="sub">mg/dL</view> <view class="sub">mg/dL</view>
<image class="icon" src="{{imageUrl}}icon118.png?t={{Timestamp}}"></image> <image class="icon" wx:if="{{item.change > 0}}" src="{{imageUrl}}icon120.png?t={{Timestamp}}"></image>
<image class="icon" src="{{imageUrl}}icon132.png?t={{Timestamp}}"></image> <image
<image class="icon" src="{{imageUrl}}icon120.png?t={{Timestamp}}"></image> class="icon"
wx:elif="{{item.change < 0}}"
src="{{imageUrl}}{{theme === 'DRUG' ? 'icon132' : 'icon118'}}.png?t={{Timestamp}}"
></image>
</view> </view>
<view class="status">草稿</view> <view wx:if="{{item.recordStatus === 1}}" class="status">{{item.statusText}}</view>
</view> </view>
<view class="row"> <view class="row">
<view class="col"> <view class="col" wx:if="{{item.oralHormoneDosage}}">
<view class="c-name">激素用量</view> <view class="c-name">激素用量</view>
<view class="c-content">10mg/天</view> <view class="c-content">{{item.oralHormoneDosage}}mg/天</view>
</view> </view>
<view class="col"> <view class="col" wx:if="{{item.nextVisitDate}}">
<view class="c-name">下次复诊时间</view> <view class="c-name">下次复诊时间</view>
<view class="c-content">2025.06.11</view> <view class="c-content">{{item.nextVisitDate}}</view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<pagination pagination="{{pagination}}" customEmpty emptyTitle="暂无就诊记录">
<view class="empty-text" slot>暂无就诊记录</view>
</pagination>
</view> </view>
<view class="page-footer" bind:tap="handleAdd"> <view class="page-footer" bind:tap="handleAdd">
<view class="btn">新增就诊记录</view> <view class="btn">新增就诊记录</view>

BIN
src/images/bg43.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

BIN
src/images/bg44.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

BIN
src/images/icon145.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/images/icon146.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
src/images/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

6
src/pages/caseReport/index.json

@ -1,4 +1,8 @@
{ {
"navigationBarTitleText": "病例报告", "navigationBarTitleText": "病例报告",
"usingComponents": {} "usingComponents": {
"pagination": "/components/pagination/index",
"van-divider": "@vant/weapp/divider/index",
"van-loading": "@vant/weapp/loading/index"
}
} }

24
src/pages/caseReport/index.scss

@ -60,24 +60,12 @@ page {
} }
} }
} }
.page-footer { .empty {
position: fixed; padding: 200rpx 0;
bottom: 0; text-align: center;
left: 0; .text {
width: 100%; font-size: 28rpx;
box-sizing: border-box; color: rgba(34, 34, 34, 0.4);
padding: 32rpx 32rpx calc(env(safe-area-inset-bottom) + 32rpx);
background-color: #fff;
box-shadow: 0rpx -10rpx 30rpx 0rpx rgba(0, 0, 0, 0.01);
.btn {
font-size: 36rpx;
color: rgba(255, 255, 255, 1);
height: 96rpx;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(86deg, #0eb66d 0%, #00d277 100%);
border-radius: 16rpx 16rpx 16rpx 16rpx;
} }
} }
} }

70
src/pages/caseReport/index.ts

@ -1,13 +1,69 @@
const _app = getApp<IAppOption>()
Page({ Page({
data: {}, data: {
onLoad(options) { imageField: '',
list: [] as { visitDate: string; images: string[] }[],
pagination: { count: 0, page: 1, pages: 1 },
},
onLoad(options: any) {
if (options.name) { if (options.name) {
wx.setNavigationBarTitle({ wx.setNavigationBarTitle({ title: options.name })
title: options.name, }
}) if (options.imageField) {
this.setData({ imageField: options.imageField })
this.getImageHistory(true)
}
},
onReachBottom() {
const { page, pages } = this.data.pagination
if (page < pages) {
this.setData({ 'pagination.page': page + 1 })
this.getImageHistory()
}
},
getImageHistory(reset = false) {
const page = reset ? 1 : this.data.pagination.page
wx.ajax({
method: 'GET',
url: '?r=igg4/medical-visit/image-history',
data: { imageField: this.data.imageField, page, pageSize: 10 },
}).then((res: any) => {
if (!res || !res.list) return
// 按就诊日期分组
const groupMap: Record<string, string[]> = {}
for (const item of res.list) {
if (!groupMap[item.visitDate]) {
groupMap[item.visitDate] = []
}
groupMap[item.visitDate].push(item.imageUrl)
} }
const newList = Object.keys(groupMap)
.sort((a, b) => b.localeCompare(a))
.map((visitDate) => ({
visitDate,
images: groupMap[visitDate],
}))
this.setData({
list: reset ? newList : [...this.data.list, ...newList],
pagination: {
count: res.totalCount || 0,
page: res.page || 1,
pages: res.pages || 1,
},
})
})
},
handlePreviewImage(e: WechatMiniprogram.CustomEvent) {
const { url, date } = e.currentTarget.dataset
const group = this.data.list.find((item) => item.visitDate === date)
if (!group) return
wx.previewImage({
current: url,
urls: group.images,
})
}, },
}) })

19
src/pages/caseReport/index.wxml

@ -1,17 +1,30 @@
<view class="page" id="{{theme === 'DRUG' && 'drug-page'}}"> <view class="page" id="{{theme === 'DRUG' && 'drug-page'}}">
<view class="list"> <view class="list">
<view class="card" wx:for="{{10}}" wx:key="index" bind:tap="handleDetail"> <view class="card" wx:for="{{list}}" wx:key="visitDate">
<view class="aside"> <view class="aside">
<view class="line-top"></view> <view class="line-top"></view>
<view class="circle"></view> <view class="circle"></view>
<view class="line-bottom"></view> <view class="line-bottom"></view>
</view> </view>
<view class="wrap"> <view class="wrap">
<view class="date">2025-06-11</view> <view class="date">{{item.visitDate}}</view>
<view class="container"> <view class="container">
<image class="photo" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> <image
class="photo"
wx:for="{{item.images}}"
wx:key="*this"
wx:for-item="imgUrl"
src="{{imgUrl}}"
mode="aspectFill"
bind:tap="handlePreviewImage"
data-url="{{imgUrl}}"
data-date="{{item.visitDate}}"
></image>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<pagination pagination="{{pagination}}" customEmpty emptyTitle="暂无记录">
<view class="empty-text" slot>暂无记录</view>
</pagination>
</view> </view>

22
src/pages/index/index.scss

@ -481,7 +481,7 @@ page {
.reg { .reg {
position: fixed; position: fixed;
left: 64rpx; left: 64rpx;
bottom: calc(env(safe-area-inset-bottom) + 120rpx); bottom: calc(env(safe-area-inset-bottom) + 180rpx);
width: 622rpx; width: 622rpx;
background-color: rgba(39, 58, 101, 0.8); background-color: rgba(39, 58, 101, 0.8);
box-shadow: 0 8rpx 24rpx rgba(92, 144, 220, 0.25); box-shadow: 0 8rpx 24rpx rgba(92, 144, 220, 0.25);
@ -505,7 +505,7 @@ page {
.reg-audit { .reg-audit {
position: fixed; position: fixed;
left: 34rpx; left: 34rpx;
bottom: calc(env(safe-area-inset-bottom) + 120rpx); bottom: calc(env(safe-area-inset-bottom) + 180rpx);
width: 682rpx; width: 682rpx;
background-color: rgba(39, 58, 101, 0.8); background-color: rgba(39, 58, 101, 0.8);
box-shadow: 0 8rpx 24rpx rgba(92, 144, 220, 0.25); box-shadow: 0 8rpx 24rpx rgba(92, 144, 220, 0.25);
@ -522,12 +522,12 @@ page {
.public { .public {
position: fixed; position: fixed;
left: 30rpx; left: 30rpx;
bottom: calc(env(safe-area-inset-bottom) + 120rpx); bottom: calc(env(safe-area-inset-bottom) + 180rpx);
width: 690rpx; width: 690rpx;
height: 152rpx; height: 152rpx;
box-shadow: 0rpx 2rpx 16rpx 0rpx #390f1b2d; box-shadow: 0rpx 4rpx 32rpx 0rpx rgba(0,0,0,0.08);
border-radius: 24rpx; border-radius: 24rpx;
background: linear-gradient(346deg, #ffffff 0%, #ffe2eb 100%); background: linear-gradient(358deg, #ffffff 0%, #e1f4ec 100%);
border: 2rpx solid #ffffff; border: 2rpx solid #ffffff;
display: flex; display: flex;
align-items: center; align-items: center;
@ -561,15 +561,15 @@ page {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background: #cf5375; background: linear-gradient(188deg, #50e8a6 0%, #00d277 100%);
border-radius: 12rpx 12rpx 12rpx 12rpx; border-radius: 12rpx 12rpx 12rpx 12rpx;
} }
.close { .close {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
width: 48rpx; width: 40rpx;
height: 48rpx; height: 40rpx;
} }
} }
} }
@ -601,6 +601,12 @@ page {
.action-btn.active { .action-btn.active {
color: #5956e9; color: #5956e9;
} }
.public {
background: linear-gradient(358deg, #ffffff 0%, #ece9ff 100%);
.btn {
background: linear-gradient(274deg, #5956e9 0%, #b384f4 100%);
}
}
} }
.slidearea { .slidearea {

23
src/pages/index/index.ts

@ -26,6 +26,8 @@ Page({
detail: {}, detail: {},
publicCard: false,
toastShow: false, toastShow: false,
toastType: '', toastType: '',
// toastType: 'dedicatedDoctor', // 绑定医生弹窗 // toastType: 'dedicatedDoctor', // 绑定医生弹窗
@ -64,6 +66,7 @@ Page({
app.getUserInfo(this, true, (userInfo) => { app.getUserInfo(this, true, (userInfo) => {
this.setData({ this.setData({
userInfo, userInfo,
publicCard: userInfo.isShowFollowGuide == 1,
}) })
this.updateGreeting() this.updateGreeting()
this.updateNextVisitDays(userInfo) this.updateNextVisitDays(userInfo)
@ -337,7 +340,7 @@ Page({
handleNextVisit() { handleNextVisit() {
app.permissionVerification(3, 0, '').then(() => { app.permissionVerification(3, 0, '').then(() => {
wx.navigateTo({ wx.navigateTo({
url: '/pages/signIn/index', url: '/gift/pages/record/index',
}) })
}) })
}, },
@ -437,4 +440,22 @@ Page({
}) })
}) })
}, },
handlePublic() {
this.setData({
toastShow: true,
toastType: 'public-toast',
toastParams: {},
})
this.handlePublicClose()
},
handlePublicClose() {
this.setData({
publicCard: false,
})
wx.ajax({
method: 'POST',
url: '?r=igg4/account/add-user-subscribe-guide-record',
data: {},
})
},
}) })

9
src/pages/index/index.wxml

@ -224,6 +224,15 @@
<van-icon name="arrow" /> <van-icon name="arrow" />
<image catch:tap="handleRegClose" class="reg-close" src="{{imageUrl}}5/reg-close.png?t={{Timestamp}}"></image> <image catch:tap="handleRegClose" class="reg-close" src="{{imageUrl}}5/reg-close.png?t={{Timestamp}}"></image>
</view> </view>
<view wx:elif="{{!anyWhere && publicCard}}" class="public" bind:tap="handlePublic">
<image class="logo" src="{{imageUrl}}logo.png?t={{Timestamp}}"></image>
<view class="wrap">
<view class="title">关注公众号</view>
<view class="content">重要提醒不丢失~</view>
</view>
<view class="btn">去关注</view>
<image catch:tap="handlePublicClose" class="close" src="{{imageUrl}}{{theme === 'DRUG' ? 'icon146' : 'icon145'}}.png?t={{Timestamp}}"></image>
</view>
</view> </view>
<toast <toast

13
src/pages/qaFormDetail/index.ts

@ -39,11 +39,12 @@ Page({
drugIcon: 'icon141.png', drugIcon: 'icon141.png',
name: '门诊病历及处方', name: '门诊病历及处方',
content: '医生手写或电脑打印的病历、处方', content: '医生手写或电脑打印的病历、处方',
imageField: 'outpatientRecord',
}, },
{ icon: 'icon127.png', name: '检验报告', content: '抽血检测的IgG4、IgG、ESR等' }, { icon: 'icon127.png', name: '检验报告', content: '抽血检测的IgG4、IgG、ESR等', imageField: 'labReport' },
{ icon: 'icon128.png', name: '影像学检查', content: '超声/CT/MRI的描述结论、电子胶片' }, { icon: 'icon128.png', name: '影像学检查', content: '超声/CT/MRI的描述结论、电子胶片', imageField: 'imagingExam' },
{ icon: 'icon129.png', name: '病理诊断及活检', content: '受累组织穿刺或切除免疫组化报告' }, { icon: 'icon129.png', name: '病理诊断及活检', content: '受累组织穿刺或切除免疫组化报告', imageField: 'pathology' },
{ icon: 'icon130.png', name: '外周血免疫功能评估', content: '免疫细胞亚群、淋巴细胞比例测定' }, { icon: 'icon130.png', name: '外周血免疫功能评估', content: '免疫细胞亚群、淋巴细胞比例测定', imageField: 'immuneFunction' },
], ],
}, },
@ -212,9 +213,9 @@ Page({
}, },
handleDetail(e) { handleDetail(e) {
const { name } = e.currentTarget.dataset const { name, imagefield } = e.currentTarget.dataset
wx.navigateTo({ wx.navigateTo({
url: `/pages/caseReport/index?name=${name}`, url: `/pages/caseReport/index?name=${name}&imageField=${imagefield}`,
}) })
}, },
}) })

2
src/pages/qaFormDetail/index.wxml

@ -60,7 +60,7 @@
<view class="right"></view> <view class="right"></view>
</view> </view>
<view class="list"> <view class="list">
<view class="row" wx:for="{{caseReportList}}" wx:key="icon" bind:tap="handleDetail" data-name="{{item.name}}"> <view class="row" wx:for="{{caseReportList}}" wx:key="icon" bind:tap="handleDetail" data-name="{{item.name}}" data-imagefield="{{item.imageField}}">
<image wx:if="{{item.drugIcon && theme === 'DRUG'}}" class="icon" src="{{imageUrl}}{{item.drugIcon}}?t={{Timestamp}}"></image> <image wx:if="{{item.drugIcon && theme === 'DRUG'}}" class="icon" src="{{imageUrl}}{{item.drugIcon}}?t={{Timestamp}}"></image>
<image wx:else class="icon" src="{{imageUrl}}{{item.icon}}?t={{Timestamp}}"></image> <image wx:else class="icon" src="{{imageUrl}}{{item.icon}}?t={{Timestamp}}"></image>
<view class="col"> <view class="col">

Loading…
Cancel
Save