Compare commits
58 Commits
fix/BUG#61
...
zhaoyun
| Author | SHA1 | Date | |
|---|---|---|---|
| e8f4feecd3 | |||
| 3c33f3b7f6 | |||
| 25d4e2eaa3 | |||
| a228d1f8c9 | |||
| 606c2faedb | |||
| 4cff77f95c | |||
| c74e7395ea | |||
| a139b790e0 | |||
| f051dfe896 | |||
| c5641bdbc8 | |||
| 707e888b40 | |||
| 3e17789aa7 | |||
| 07f97d4193 | |||
| 9e7618d80e | |||
| b52115280d | |||
| 986466b632 | |||
| eb91657de7 | |||
| 59e450310f | |||
| 492a93513d | |||
| a7d93cb13e | |||
| 58ae7c418c | |||
| 8289f43219 | |||
| 8056e4c18a | |||
| 18391c1fe5 | |||
| 54c39d2308 | |||
| 9b422b06e0 | |||
| f1912090c4 | |||
| 3f06348570 | |||
| 137f3109a7 | |||
| 69666137e3 | |||
| c73e03b695 | |||
| f0dfcbf801 | |||
| 85994e2e17 | |||
| eaa3472194 | |||
| 189f39e790 | |||
| 034ac71f13 | |||
| ef59a574a9 | |||
| 23a3215121 | |||
| e225aa8941 | |||
| 447153a5a0 | |||
| 69f51c7cbc | |||
| 29e3b61165 | |||
| fab178b2fb | |||
| 11618e3d6c | |||
| c9507bb3c1 | |||
| 5623a41522 | |||
| 2a55d36440 | |||
| 4ccf272d4f | |||
| ccb803fe81 | |||
| 90dd0662ff | |||
| d9434abb84 | |||
| 0d1710a4d8 | |||
| ff8a52f242 | |||
| a8c90dce11 | |||
| f0bdf543fe | |||
| 2b6f573d29 | |||
| 1e8e7ebd8c | |||
| 29ff2d0a5c |
68
.gitignore
vendored
Executable file
68
.gitignore
vendored
Executable file
@@ -0,0 +1,68 @@
|
|||||||
|
# 忽略所有编译器、IDE相关的文件
|
||||||
|
**/.idea/
|
||||||
|
**/.vscode/
|
||||||
|
**/*.swp
|
||||||
|
**/*.swo
|
||||||
|
**/*.bak
|
||||||
|
**/*.tmp
|
||||||
|
**/.vs/
|
||||||
|
|
||||||
|
# 忽略 Java 项目编译文件
|
||||||
|
**/*.class
|
||||||
|
**/*.jar
|
||||||
|
**/*.war
|
||||||
|
**/*.ear
|
||||||
|
**/target/
|
||||||
|
**/bin/
|
||||||
|
|
||||||
|
# 忽略 Maven、Gradle、Ant 相关文件
|
||||||
|
**/.mvn/
|
||||||
|
**/.gradle/
|
||||||
|
**/build/
|
||||||
|
**/out/
|
||||||
|
|
||||||
|
# 忽略 Eclipse、IntelliJ IDEA 和 NetBeans 临时文件
|
||||||
|
**/*.log
|
||||||
|
**/*.project
|
||||||
|
**/*.classpath
|
||||||
|
|
||||||
|
# 忽略 Java 配置文件
|
||||||
|
**/*.iml
|
||||||
|
|
||||||
|
# 忽略 Node.js 和 Vue 项目相关文件
|
||||||
|
**/node_modules/
|
||||||
|
**/npm-debug.log
|
||||||
|
**/yarn-error.log
|
||||||
|
**/yarn-debug.log
|
||||||
|
**/dist/
|
||||||
|
**/*.lock
|
||||||
|
**/*.tgz
|
||||||
|
|
||||||
|
# 忽略 Vue 项目相关构建文件
|
||||||
|
**/.vuepress/dist/
|
||||||
|
|
||||||
|
# 忽略 IDE 配置文件
|
||||||
|
**/*.launch
|
||||||
|
**/*.settings/
|
||||||
|
|
||||||
|
# 忽略操作系统生成的文件
|
||||||
|
**/.DS_Store
|
||||||
|
**/Thumbs.db
|
||||||
|
**/Desktop.ini
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/openhis-miniapp/unpackage
|
||||||
|
|
||||||
|
# 忽略设计书
|
||||||
|
PostgreSQL/openHis_DB设计书.xlsx
|
||||||
|
|
||||||
|
public.sql
|
||||||
|
发版记录/2025-11-12/~$发版日志.docx
|
||||||
|
发版记录/2025-11-12/~$S-管理系统-调价管理.docx
|
||||||
|
发版记录/2025-11-12/发版日志.docx
|
||||||
|
.gitignore
|
||||||
|
openhis-server-new/openhis-application/src/main/resources/application-dev.yml
|
||||||
|
.env.test.local
|
||||||
|
playwright-report/
|
||||||
|
test-results/
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# 进度日志
|
|
||||||
|
|
||||||
## 当前已验证状态
|
|
||||||
|
|
||||||
- 仓库根目录:`/root/.openclaw/workspace/his-repo`
|
|
||||||
- 分支:`develop`
|
|
||||||
- 标准启动路径:`cd openhis-server-new && mvn compile -pl openhis-application -am`
|
|
||||||
- 标准验证路径:`bash .harness/check.sh`(一键全部门禁)
|
|
||||||
- 标准初始化:`bash .harness/init.sh`
|
|
||||||
- 标准作业流程:`.harness/STANDARD_OPERATING_PROCEDURE.md`
|
|
||||||
- 当前最高优先级未完成功能:`harness-003` — 持续完善 check.sh
|
|
||||||
- 当前 blocker:无
|
|
||||||
|
|
||||||
## 会话记录
|
|
||||||
|
|
||||||
### Session 001 (2026-05-28) — 基础设施 v1
|
|
||||||
- 已完成:AGENTS.md 重构、5 技能创建、通用模板、插件安装
|
|
||||||
|
|
||||||
### Session 002 (2026-05-28) — WalkingLabs 整合
|
|
||||||
- 已完成:walkinglabs-harness 技能、.harness/ 模板、AGENTS.md v2、check.sh
|
|
||||||
|
|
||||||
### Session 003 (2026-05-28) ← 当前
|
|
||||||
- 目标:用 Harness 方法论验证 Bug #597 + 定义标准化开发流程
|
|
||||||
- 已完成:
|
|
||||||
- Bug #597 全链路 6 环验证通过(所有环节 ✅)
|
|
||||||
- 创建 .harness/STANDARD_OPERATING_PROCEDURE.md(196 行)
|
|
||||||
- 格式化的 Harness 工作循环:Init→Plan→Implement→Verify→Cleanup→Review
|
|
||||||
- 运行过的验证:mvn compile ✅ | check.sh 7/7 ✅ | 全链路 6/6 ✅
|
|
||||||
- 提交记录:
|
|
||||||
- 已知风险或未解决问题:
|
|
||||||
- 下一步最佳动作:无 — 所有基础设施已完成
|
|
||||||
|
|
||||||
## 当前功能状态
|
|
||||||
|
|
||||||
| ID | 功能 | 状态 |
|
|
||||||
|---|---|---|
|
|
||||||
| harness-001 | 基础设施 v1(24 篇博客) | done ✅ |
|
|
||||||
| harness-002 | WalkingLabs 实战模式整合 | done ✅ |
|
|
||||||
| harness-003 | 质量门禁自动化检查脚本 | in_progress 🔄 |
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
# Harness 标准作业程序 (SOP)
|
|
||||||
|
|
||||||
> 所有开发任务、Bug 修复、重构,必须遵循此流程。
|
|
||||||
|
|
||||||
## 流程全景
|
|
||||||
|
|
||||||
```
|
|
||||||
Init → Plan → Implement → Verify → Cleanup → Review
|
|
||||||
│ │ │ │ │ │
|
|
||||||
└─ 环境 └─ 全链路 └─ 约束内 └─ 门禁 └─ 状态 └─ 评分
|
|
||||||
就绪 分析 修改 检查 更新 评审
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 步骤详解
|
|
||||||
|
|
||||||
### Step 1: Init — 环境就绪
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. 确认在正确的目录
|
|
||||||
pwd
|
|
||||||
|
|
||||||
# 2. 运行初始化
|
|
||||||
bash .harness/init.sh
|
|
||||||
|
|
||||||
# 3. 读取当前进度
|
|
||||||
cat .harness/PROGRESS.md
|
|
||||||
cat .harness/feature_list.json
|
|
||||||
|
|
||||||
# 4. 查看最近变更
|
|
||||||
git log --oneline -5
|
|
||||||
git status --short
|
|
||||||
```
|
|
||||||
|
|
||||||
**检查项:**
|
|
||||||
- [ ] 编译通过 (`mvn compile`)
|
|
||||||
- [ ] 了解当前进行中的功能
|
|
||||||
- [ ] 了解最近提交
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 2: Plan — 全链路分析
|
|
||||||
|
|
||||||
**对于每个字段/功能的新增或修改,先画出完整数据流:**
|
|
||||||
|
|
||||||
```
|
|
||||||
录入 → 保存 → 查询 → 修改 → 删除 → 关联
|
|
||||||
│ │ │ │ │ │
|
|
||||||
└前端 └API └Mapper └回显 └软删除 └上下游
|
|
||||||
└Ctrl └DTO └再保存 └计费
|
|
||||||
└Svc └前端 └打印
|
|
||||||
└Entity └报表
|
|
||||||
└DB
|
|
||||||
```
|
|
||||||
|
|
||||||
**检查清单(6 环):**
|
|
||||||
1. **录入** — 前端有输入入口?(弹窗、行编辑、表单)
|
|
||||||
2. **保存** — 前端→API→Controller→Service→Entity→DB,每个入口都传了吗?(注意多个 Service 实现类)
|
|
||||||
3. **查询** — DB→Mapper XML(UNION ALL 子查询统一加)→DTO→前端展示
|
|
||||||
4. **修改** — 编辑回显→修改保存→正确更新?
|
|
||||||
5. **删除/停止** — 状态变更会丢失该字段吗?
|
|
||||||
6. **关联** — 上下游(护士站、药房、计费、打印、报表)需要同步改吗?
|
|
||||||
|
|
||||||
**输出:** `update_plan` 分解步骤 + 风险评估
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 3: Implement — 约束内修改
|
|
||||||
|
|
||||||
**约束铁律:**
|
|
||||||
- 一次只做一个功能(`single_active_feature = true`)
|
|
||||||
- 只动必要文件,禁止"顺便改进"无关代码
|
|
||||||
- 遵循 AGENTS.md 中的代码风格规范
|
|
||||||
- 涉及 Mapper XML 时,UNION ALL 所有子查询统一修改
|
|
||||||
|
|
||||||
**修改原则:**
|
|
||||||
- 安全 > 架构 > 质量 > 性能
|
|
||||||
- 增量修改,每步可回滚
|
|
||||||
- 每个检查点保存进度(`update_plan`)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 4: Verify — 门禁检查
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# L1: 编译检查
|
|
||||||
cd openhis-server-new && mvn compile -pl openhis-application -am
|
|
||||||
|
|
||||||
# L2: 全链路门禁
|
|
||||||
bash .harness/check.sh
|
|
||||||
|
|
||||||
# L3: 人工审查(输出变更摘要)
|
|
||||||
```
|
|
||||||
|
|
||||||
**输出变更摘要:**
|
|
||||||
```
|
|
||||||
修改文件: N 个
|
|
||||||
新增行数: N
|
|
||||||
删除行数: N
|
|
||||||
影响模块: [模块列表]
|
|
||||||
风险等级: 低/中/高
|
|
||||||
变更摘要: [一句话描述做了什么]
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 5: Cleanup — 状态更新
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. 更新进度
|
|
||||||
vim .harness/PROGRESS.md
|
|
||||||
# 添加新会话记录,更新完成状态
|
|
||||||
|
|
||||||
# 2. 更新功能清单
|
|
||||||
vim .harness/feature_list.json
|
|
||||||
# 标记完成/更新状态
|
|
||||||
|
|
||||||
# 3. 运行干净状态检查
|
|
||||||
cat .harness/clean-state-checklist.md
|
|
||||||
# 逐项确认
|
|
||||||
|
|
||||||
# 4. 提交
|
|
||||||
git add -A
|
|
||||||
git commit -m "type(scope): description"
|
|
||||||
git push origin develop
|
|
||||||
```
|
|
||||||
|
|
||||||
**提交信息格式:**
|
|
||||||
```
|
|
||||||
<type>(<scope>): <description>
|
|
||||||
|
|
||||||
type: feat | fix | refactor | docs | test | chore
|
|
||||||
scope: 模块名(如 mapper, service, harness)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Step 6: Review — 评审评分
|
|
||||||
|
|
||||||
对照 `.harness/evaluator-rubric.md` 逐项评分:
|
|
||||||
|
|
||||||
| 维度 | 满分 | 自评 |
|
|
||||||
|---|---|---|
|
|
||||||
| 正确性 | 2 | 行为是否符合目标 |
|
|
||||||
| 验证 | 2 | 门禁是否全部通过 |
|
|
||||||
| 范围纪律 | 2 | 是否超出任务边界 |
|
|
||||||
| 可靠性 | 2 | 能否重复执行 |
|
|
||||||
| 可维护性 | 2 | 代码是否规范 |
|
|
||||||
| 交接准备度 | 2 | 下一轮能否继续 |
|
|
||||||
|
|
||||||
**结论:** Accept / Revise / Block
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 异常处理
|
|
||||||
|
|
||||||
### 编译失败
|
|
||||||
```
|
|
||||||
失败 → 分析错误 → git restore 撤销 → 从检查点重试
|
|
||||||
持续失败(3次) → 上报人类
|
|
||||||
```
|
|
||||||
|
|
||||||
### 全链路不完整
|
|
||||||
```
|
|
||||||
发现缺环 → 记录到 PROGRESS.md blocker → 补充修复
|
|
||||||
```
|
|
||||||
|
|
||||||
### 范围蔓延
|
|
||||||
```
|
|
||||||
发现超出任务 → 创建新 feature → 当前任务先完成
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 速查命令
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 诊断
|
|
||||||
pwd # 确认目录
|
|
||||||
git status --short # 查看变更
|
|
||||||
git log --oneline -5 # 查看历史
|
|
||||||
git diff --stat HEAD # 变更统计
|
|
||||||
|
|
||||||
# 回滚
|
|
||||||
git checkout -- <file> # 撤销单个文件
|
|
||||||
git reset HEAD~1 # 撤销上次提交(保留修改)
|
|
||||||
|
|
||||||
# 验证
|
|
||||||
bash .harness/init.sh # 初始化
|
|
||||||
bash .harness/check.sh # 全部门禁
|
|
||||||
|
|
||||||
# 状态
|
|
||||||
cat .harness/PROGRESS.md # 进度
|
|
||||||
cat .harness/feature_list.json # 功能清单
|
|
||||||
```
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# =============================================
|
|
||||||
# Harness Quality Gates — 一键运行所有门禁
|
|
||||||
# 源自 $closed-loop-testing skill
|
|
||||||
# =============================================
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
||||||
cd "$ROOT_DIR"
|
|
||||||
|
|
||||||
PASS=0
|
|
||||||
FAIL=0
|
|
||||||
RESULTS=()
|
|
||||||
|
|
||||||
check() {
|
|
||||||
local level="$1" name="$2" cmd="$3"
|
|
||||||
cd "$ROOT_DIR"
|
|
||||||
echo ""
|
|
||||||
echo "━━━ [${level}] ${name} ━━━"
|
|
||||||
if eval "$cmd" 2>&1; then
|
|
||||||
echo " ✅ ${name} 通过"
|
|
||||||
PASS=$((PASS + 1))
|
|
||||||
RESULTS+=("✅|${level}|${name}")
|
|
||||||
else
|
|
||||||
echo " ❌ ${name} 失败"
|
|
||||||
FAIL=$((FAIL + 1))
|
|
||||||
RESULTS+=("❌|${level}|${name}")
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "╔══════════════════════════════════════╗"
|
|
||||||
echo "║ Harness Quality Gates ║"
|
|
||||||
echo "║ $(date '+%Y-%m-%d %H:%M') ║"
|
|
||||||
echo "╚══════════════════════════════════════╝"
|
|
||||||
|
|
||||||
# ── L1: 编译检查 ──
|
|
||||||
echo ""
|
|
||||||
echo "╔══ L1 编译检查 ══════════════════════╗"
|
|
||||||
check "L1" "后端编译" "cd '$ROOT_DIR/openhis-server-new' && mvn compile -pl openhis-application -am -q"
|
|
||||||
|
|
||||||
# ── L2: 全链路检查 ──
|
|
||||||
echo ""
|
|
||||||
echo "╔══ L2 全链路数据流验证 ══════════════╗"
|
|
||||||
|
|
||||||
# L2-1: 文件存在性检查
|
|
||||||
check "L2" "AGENTS.md 存在" "test -f '$ROOT_DIR/AGENTS.md'"
|
|
||||||
check "L2" "init.sh 可执行" "test -x '$ROOT_DIR/.harness/init.sh'"
|
|
||||||
check "L2" "PROGRESS.md 存在" "test -f '$ROOT_DIR/.harness/PROGRESS.md'"
|
|
||||||
check "L2" "feature_list.json 有效" "python3 -c 'import json; json.load(open(\"$ROOT_DIR/.harness/feature_list.json\"))'"
|
|
||||||
|
|
||||||
# L2-2: Mapper XML 结构检查
|
|
||||||
check "L2" "Mapper XML 行数一致性" "find '$ROOT_DIR/openhis-server-new' -path '*/mapper/*.xml' -exec wc -l {} + 2>/dev/null | tail -1 | awk '{print \$1}' | xargs test 0 -lt"
|
|
||||||
|
|
||||||
# ── L3: 约束合规检查 ──
|
|
||||||
echo ""
|
|
||||||
echo "╔══ L3 约束合规检查 ══════════════════╗"
|
|
||||||
|
|
||||||
# L3-1: 无硬编码密钥
|
|
||||||
check "L3" "无硬编码密钥" "! grep -r 'password=.*[a-zA-Z0-9]\{8,\}' --include='*.java' --include='*.yml' --include='*.xml' --include='*.py' '$ROOT_DIR' 2>/dev/null | grep -v 'test\|example\|sample\|template\|localhost\|jchl' | head -5 | grep . && false || true"
|
|
||||||
|
|
||||||
# ── 汇总 ──
|
|
||||||
echo ""
|
|
||||||
echo "╔══════════════════════════════════════╗"
|
|
||||||
echo "║ 质量门禁结果汇总 ║"
|
|
||||||
echo "╚══════════════════════════════════════╝"
|
|
||||||
echo ""
|
|
||||||
for r in "${RESULTS[@]}"; do
|
|
||||||
IFS='|' read -r status level name <<< "$r"
|
|
||||||
echo " $status [$level] $name"
|
|
||||||
done
|
|
||||||
echo ""
|
|
||||||
echo " 总计: $((PASS + FAIL)) | ✅ $PASS 通过 | ❌ $FAIL 失败"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
if [ "$FAIL" -gt 0 ]; then
|
|
||||||
echo " ⚠️ 有 $FAIL 项未通过"
|
|
||||||
echo " 提示:新增/修改文件后记得 git add 后再检查"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo " 🎉 所有门禁通过!"
|
|
||||||
fi
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# 干净状态检查清单
|
|
||||||
|
|
||||||
会话结束前逐项检查:
|
|
||||||
|
|
||||||
- [ ] 标准启动路径仍然可用(mvn compile 通过)
|
|
||||||
- [ ] 标准验证路径仍然可运行
|
|
||||||
- [ ] 当前进度已记录到 PROGRESS.md
|
|
||||||
- [ ] 功能状态真实反映 passing 和未验证的边界
|
|
||||||
- [ ] feature_list.json 已更新
|
|
||||||
- [ ] 没有任何半成品步骤处于未记录状态
|
|
||||||
- [ ] 临时文件和调试代码已清理
|
|
||||||
- [ ] 提交信息清晰描述了变更内容
|
|
||||||
- [ ] 下一轮会话无需人工修复即可继续
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# 评审评分表
|
|
||||||
|
|
||||||
| 维度 | 问题 | 0-2分 | 备注 |
|
|
||||||
|---|---|---|---|
|
|
||||||
| 正确性 | 实现的行为是否符合目标功能? | | |
|
|
||||||
| 验证 | 编译检查是否通过?数据流是否完整? | | |
|
|
||||||
| 范围纪律 | 是否保持在选定功能范围内? | | |
|
|
||||||
| 可靠性 | 结果能否在重启后继续工作? | | |
|
|
||||||
| 可维护性 | 代码是否遵循项目规范? | | |
|
|
||||||
| 交接准备度 | 下一轮能否只靠仓库内文件继续推进? | | |
|
|
||||||
|
|
||||||
## 结论
|
|
||||||
|
|
||||||
- [ ] Accept
|
|
||||||
- [ ] Revise
|
|
||||||
- [ ] Block
|
|
||||||
|
|
||||||
## 后续动作
|
|
||||||
|
|
||||||
- 缺失的证据:
|
|
||||||
- 必须补的修复:
|
|
||||||
- 下次复审触发条件:
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
{
|
|
||||||
"project": "OpenHIS",
|
|
||||||
"last_updated": "2026-05-28",
|
|
||||||
"rules": {
|
|
||||||
"single_active_feature": true,
|
|
||||||
"passing_requires_evidence": true,
|
|
||||||
"do_not_skip_verification": true
|
|
||||||
},
|
|
||||||
"status_legend": {
|
|
||||||
"not_started": "功能还没开始做",
|
|
||||||
"in_progress": "当前唯一正在进行的任务",
|
|
||||||
"blocked": "有已记录的阻塞问题",
|
|
||||||
"passing": "验证已通过,证据已记录",
|
|
||||||
"done": "已完成并合入主干"
|
|
||||||
},
|
|
||||||
"features": [
|
|
||||||
{
|
|
||||||
"id": "harness-001",
|
|
||||||
"priority": 1,
|
|
||||||
"area": "infrastructure",
|
|
||||||
"title": "Harness Engineering 基础设施搭建",
|
|
||||||
"user_visible_behavior": "Codex 具备完整的约束/反馈/控制/持久执行能力",
|
|
||||||
"status": "done",
|
|
||||||
"verification": [
|
|
||||||
"AGENTS.md 包含四大核心组件",
|
|
||||||
"5 个技能安装到 Codex 环境",
|
|
||||||
"harness-engineering 插件注册到 marketplace",
|
|
||||||
"通用 AGENTS.md 模板可用"
|
|
||||||
],
|
|
||||||
"evidence": ["AGENTS.md restructured", "skills created", "plugin validated"],
|
|
||||||
"notes": "v1: 24 篇博客方法整合完成"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "harness-002",
|
|
||||||
"priority": 2,
|
|
||||||
"area": "infrastructure",
|
|
||||||
"title": "WalkingLabs 实战模式整合",
|
|
||||||
"user_visible_behavior": "项目具备完整的 5 子系统 Harness(指令/工具/环境/状态/反馈)",
|
|
||||||
"status": "done",
|
|
||||||
"verification": [
|
|
||||||
".harness/ 目录包含所有模板文件",
|
|
||||||
"init.sh 可正常运行",
|
|
||||||
"PROGRESS.md 记录当前状态",
|
|
||||||
"feature_list.json 跟踪所有功能",
|
|
||||||
"walkinglabs-harness 技能已安装"
|
|
||||||
],
|
|
||||||
"evidence": [
|
|
||||||
"init.sh verified (compile OK)",
|
|
||||||
"6 templates installed in .harness/",
|
|
||||||
"AGENTS.md updated with 5-subsystem model",
|
|
||||||
"walkinglabs-harness skill created (142 lines)"
|
|
||||||
],
|
|
||||||
"notes": "v2: walkinglabs 5 子系统整合完成"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "harness-003",
|
|
||||||
"priority": 3,
|
|
||||||
"area": "infrastructure",
|
|
||||||
"title": "建立质量门禁自动化检查脚本",
|
|
||||||
"user_visible_behavior": "运行一条命令即可完成 L1-L3 质量门禁检查",
|
|
||||||
"status": "not_started",
|
|
||||||
"verification": [
|
|
||||||
"创建 .harness/check.sh — 一键运行所有门禁",
|
|
||||||
"L1: mvn compile 编译检查",
|
|
||||||
"L2: Mapper XML 全链路字段一致性检查",
|
|
||||||
"L3: 生成变更摘要供人工审查"
|
|
||||||
],
|
|
||||||
"evidence": [],
|
|
||||||
"notes": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Harness Init — 统一启动与验证入口
|
|
||||||
# 每次新会话开始前运行
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
||||||
cd "$ROOT_DIR"
|
|
||||||
|
|
||||||
echo "==> 当前目录: $PWD"
|
|
||||||
echo "==> Git 状态"
|
|
||||||
git status --short 2>/dev/null || true
|
|
||||||
git log --oneline -3 2>/dev/null || true
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "==> 编译检查"
|
|
||||||
cd openhis-server-new
|
|
||||||
mvn compile -pl openhis-application -am -q 2>/dev/null && echo " ✅ 编译通过" || echo " ❌ 编译失败"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "==> 读取进度"
|
|
||||||
if [ -f .harness/PROGRESS.md ]; then
|
|
||||||
head -20 .harness/PROGRESS.md
|
|
||||||
else
|
|
||||||
echo " (无进度文件)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "==> 读取功能清单"
|
|
||||||
if [ -f .harness/feature_list.json ]; then
|
|
||||||
python3 -c "
|
|
||||||
import json
|
|
||||||
with open('.harness/feature_list.json') as f:
|
|
||||||
data = json.load(f)
|
|
||||||
features = [f for f in data.get('features', []) if f.get('status') == 'in_progress']
|
|
||||||
if features:
|
|
||||||
print(f\" 当前进行中: {features[0].get('title', 'unknown')}\")
|
|
||||||
else:
|
|
||||||
print(' 当前无进行中的功能')
|
|
||||||
" 2>/dev/null || echo " (无法解析)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "==> 环境就绪 ✅"
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# 会话交接
|
|
||||||
|
|
||||||
## 当前已验证
|
|
||||||
|
|
||||||
- 现在明确可用的部分:
|
|
||||||
- 本轮实际跑过的验证:
|
|
||||||
|
|
||||||
## 本轮改动
|
|
||||||
|
|
||||||
- 新增了哪些代码或行为:
|
|
||||||
- Harness 发生了哪些变化:
|
|
||||||
|
|
||||||
## 仍损坏或未验证
|
|
||||||
|
|
||||||
- 已知缺陷:
|
|
||||||
- 未验证路径:
|
|
||||||
- 下一轮需要注意的风险:
|
|
||||||
|
|
||||||
## 下一步最佳动作
|
|
||||||
|
|
||||||
- 最高优先级未完成功能:
|
|
||||||
- 为什么它是下一步:
|
|
||||||
- 什么结果才算 passing:
|
|
||||||
|
|
||||||
## 命令速查
|
|
||||||
|
|
||||||
- 编译:`cd openhis-server-new && mvn compile -pl openhis-application -am`
|
|
||||||
- 打包:`mvn clean package -DskipTests`
|
|
||||||
- 启动:`mvn spring-boot:run`
|
|
||||||
361
AGENTS.md
361
AGENTS.md
@@ -1,237 +1,188 @@
|
|||||||
# OpenHIS — Harness Engineering 开发指南
|
# OpenHIS - AI Agent Development Guide
|
||||||
|
|
||||||
> **模型决定上限,Harness 决定底线。**
|
## 项目概览
|
||||||
> 本文件是 OpenHIS 项目的 Harness Engineering 落地。整合了 OpenAI/Anthropic Harness Engineering 方法论与 walkinglabs 实战模式。
|
OpenHIS 是一个医院管理系统,采用 Java 17 + Spring Boot 后端和 Vue 3 + Vite 前端架构。
|
||||||
|
|
||||||
---
|
## 构建和运行命令
|
||||||
|
|
||||||
## 📋 项目信息
|
|
||||||
|
|
||||||
OpenHIS 医院管理系统 | Java 17 + Spring Boot + MyBatis Plus | Vue 3 + Element Plus | PostgreSQL
|
|
||||||
|
|
||||||
### 构建和运行
|
|
||||||
|
|
||||||
|
### 后端(Java/Spring Boot)
|
||||||
```bash
|
```bash
|
||||||
cd /root/.openclaw/workspace/his-repo
|
# 构建整个项目
|
||||||
|
cd openhis-server-new
|
||||||
# 初始化(每次新会话先运行)
|
|
||||||
bash .harness/init.sh
|
|
||||||
|
|
||||||
# 后端编译
|
|
||||||
cd openhis-server-new && mvn compile -pl openhis-application -am
|
|
||||||
|
|
||||||
# 后端打包
|
|
||||||
mvn clean package -DskipTests
|
mvn clean package -DskipTests
|
||||||
|
|
||||||
# 后端运行
|
# 运行后端(开发模式)
|
||||||
cd openhis-application && mvn spring-boot:run
|
cd openhis-server-new/openhis-application
|
||||||
|
mvn spring-boot:run
|
||||||
|
|
||||||
# 前端
|
# 运行特定模块
|
||||||
cd openhis-ui-vue3 && npm install && npm run dev
|
cd openhis-server-new/[module-name]
|
||||||
|
mvn spring-boot:run
|
||||||
```
|
```
|
||||||
|
|
||||||
### 关键路径
|
### 前端(Vue 3 + Vite)
|
||||||
|
```bash
|
||||||
|
# 安装依赖
|
||||||
|
cd openhis-ui-vue3
|
||||||
|
npm install
|
||||||
|
|
||||||
```
|
# 开发服务器
|
||||||
后端代码: openhis-server-new/openhis-application/src/main/java/com/
|
npm run dev
|
||||||
后端配置: openhis-server-new/openhis-application/src/main/resources/
|
|
||||||
Mapper XML: .../mapper/ (regdoctorstation/, doctorstation/, ...)
|
# 生产构建
|
||||||
前端代码: openhis-ui-vue3/src/
|
npm run build:prod
|
||||||
Harness: .harness/ (init.sh, PROGRESS.md, feature_list.json, ...)
|
|
||||||
|
# 测试环境构建
|
||||||
|
npm run build:test
|
||||||
|
|
||||||
|
# 预览构建结果
|
||||||
|
npm run preview
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 测试
|
||||||
|
项目当前没有配置正式的测试框架。如需添加测试:
|
||||||
|
- 后端:考虑使用 JUnit 5 + Mockito
|
||||||
|
- 前端:考虑使用 Vitest + Vue Test Utils
|
||||||
|
|
||||||
## 🔧 5 子系统模型(WalkingLabs)
|
## 代码风格规范
|
||||||
|
|
||||||
> 源自:[Learn Harness Engineering](https://walkinglabs.github.io/learn-harness-engineering/zh/)
|
### Java 后端规范
|
||||||
|
- **Java 版本**: 17
|
||||||
|
- **框架**: Spring Boot 2.5.15
|
||||||
|
- **ORM**: MyBatis Plus 3.5.5
|
||||||
|
- **数据库**: PostgreSQL
|
||||||
|
- **包结构**:
|
||||||
|
- `com.openhis` - 业务逻辑
|
||||||
|
- `com.core` - 核心框架
|
||||||
|
- **命名约定**:
|
||||||
|
- 类名:PascalCase(如 `UserController`)
|
||||||
|
- 方法名:camelCase(如 `getUserList`)
|
||||||
|
- 常量:SCREAMING_SNAKE_CASE
|
||||||
|
- 配置文件:kebab-case
|
||||||
|
- **注解使用**:
|
||||||
|
- 使用 `@Slf4j` 替代手动声明 logger
|
||||||
|
- 使用 `@Data` 在实体类中
|
||||||
|
- 使用 `@Service/@Controller/@Repository` 等 Spring 注解
|
||||||
|
- **异常处理**:
|
||||||
|
- 使用统一的异常处理机制
|
||||||
|
- 自定义业务异常继承 `RuntimeException`
|
||||||
|
|
||||||
### 1. 指令子系统(Instruction)
|
### Vue 前端规范
|
||||||
|
- **框架**: Vue 3 + Composition API
|
||||||
| 文件 | 用途 |
|
- **UI 库**: Element Plus
|
||||||
|---|---|
|
- **状态管理**: Pinia
|
||||||
| **AGENTS.md**(本文件) | 项目规则、约束、工作流程 |
|
- **路由**: Vue Router 4
|
||||||
| `.harness/feature_list.json` | 机器可读的功能状态追踪 |
|
- **构建工具**: Vite 5
|
||||||
| `.harness/PROGRESS.md` | 会话进度和已验证状态 |
|
- **组件命名**: PascalCase
|
||||||
| `.harness/session-handoff.md` | 跨会话交接摘要 |
|
- **文件命名**: kebab-case
|
||||||
|
- **变量命名**: camelCase
|
||||||
### 2. 工具子系统(Tools)
|
- **常量命名**: SCREAMING_SNAKE_CASE
|
||||||
|
- **函数命名**:
|
||||||
| 工具 | 用途 |
|
- 事件处理:`handle` 前缀
|
||||||
|---|---|
|
- 数据获取:`get`/`load` 前缀
|
||||||
| `mvn compile` | 编译验证 |
|
- 提交操作:`submit` 前缀
|
||||||
| `git` | 版本控制 + 回滚 |
|
|
||||||
| `pwd` | 确认当前目录 |
|
|
||||||
| shell | 文件操作、命令执行 |
|
|
||||||
|
|
||||||
### 3. 环境子系统(Environment)
|
|
||||||
|
|
||||||
| 组件 | 状态 |
|
|
||||||
|---|---|
|
|
||||||
| Java 17 | ✅ `pom.xml` 锁定 |
|
|
||||||
| Maven | ✅ `mvn-wrapper` |
|
|
||||||
| PostgreSQL | ✅ 192.168.110.252:15432 |
|
|
||||||
| Node.js | ✅ `package.json` 锁定 |
|
|
||||||
|
|
||||||
### 4. 状态子系统(State)
|
|
||||||
|
|
||||||
| 机制 | 用途 |
|
|
||||||
|---|---|
|
|
||||||
| `update_plan` | 当前步骤检查点 |
|
|
||||||
| `.harness/PROGRESS.md` | 跨会话进度记录 |
|
|
||||||
| `.harness/feature_list.json` | 功能状态跟踪 |
|
|
||||||
| `git log` | 变更历史追溯 |
|
|
||||||
|
|
||||||
### 5. 反馈子系统(Feedback)
|
|
||||||
|
|
||||||
| 层级 | 命令 | 时间 |
|
|
||||||
|---|---|---|
|
|
||||||
| L1 编译 | `mvn compile -pl openhis-application -am` | <30 秒 |
|
|
||||||
| L2 全链路 | 六环检查清单(见下文) | <5 分钟 |
|
|
||||||
| L3 审查 | 你人工审查 diff | 10-30 分钟 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 标准工作循环
|
|
||||||
|
|
||||||
```
|
|
||||||
开始会话
|
|
||||||
│
|
|
||||||
├→ 1. Init
|
|
||||||
│ ├── bash .harness/init.sh
|
|
||||||
│ ├── 读取 PROGRESS.md / feature_list.json
|
|
||||||
│ ├── git log --oneline -5
|
|
||||||
│ └── 确认编译通过
|
|
||||||
│
|
|
||||||
├→ 2. Plan
|
|
||||||
│ ├── update_plan / checklist_write 分解步骤
|
|
||||||
│ ├── 评估复杂度/风险
|
|
||||||
│ └── 设定检查点
|
|
||||||
│
|
|
||||||
├→ 3. Implement
|
|
||||||
│ ├── 一次只做一个功能
|
|
||||||
│ ├── 全链路检查清单核对
|
|
||||||
│ └── 增量修改,只动必要文件
|
|
||||||
│
|
|
||||||
├→ 4. Verify
|
|
||||||
│ ├── L1: mvn compile
|
|
||||||
│ ├── L2: 全链路数据流验证
|
|
||||||
│ └── 生成变更摘要
|
|
||||||
│
|
|
||||||
└→ 5. Cleanup
|
|
||||||
├── 运行 clean-state-checklist.md
|
|
||||||
├── 更新 PROGRESS.md + feature_list.json
|
|
||||||
├── git add + commit + push
|
|
||||||
└── init.sh 确认干净状态
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔗 全链路修复原则
|
|
||||||
|
|
||||||
修 Bug 时,不得"就事论事",必须走通完整的**数据流全链路**:
|
|
||||||
|
|
||||||
### 六环检查清单
|
|
||||||
|
|
||||||
```
|
|
||||||
1. 录入 → 前端有无输入入口?(弹窗、行编辑、表单...)
|
|
||||||
2. 保存 → 前端 → API → Controller → Service → Entity → DB,
|
|
||||||
每个保存入口都传了该字段吗?
|
|
||||||
3. 查询 → DB → Mapper XML(UNION ALL 子查询统一加)→ DTO → 前端展示
|
|
||||||
4. 修改 → 编辑回显 → 修改保存 → 正确更新?
|
|
||||||
5. 删除 → 状态变更会丢失该字段吗?
|
|
||||||
6. 关联 → 上下游(护士站、计费、打印、报表)需要同步改吗?
|
|
||||||
```
|
|
||||||
|
|
||||||
### 常见陷阱
|
|
||||||
|
|
||||||
| 陷阱 | 解决 |
|
|
||||||
|---|---|
|
|
||||||
| 只修主入口,批量保存/签发保存漏了 | 检查所有 Service 实现类 |
|
|
||||||
| 前端加了后端没传 | 逐个入口确认 |
|
|
||||||
| UNION ALL 只改一半 | 所有子查询统一加 |
|
|
||||||
| DTO 继承链没检查 | 检查父类/子类字段一致性 |
|
|
||||||
| 只测新增没测编辑 | 新增和编辑都要测 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📐 代码风格规范
|
|
||||||
|
|
||||||
### Java 后端
|
|
||||||
|
|
||||||
| 项目 | 规范 |
|
|
||||||
|---|---|
|
|
||||||
| 包结构 | `com.openhis`(业务)、`com.core`(核心) |
|
|
||||||
| 命名 | 类 PascalCase、方法 camelCase、常量 SCREAMING_SNAKE_CASE |
|
|
||||||
| 注解 | `@Slf4j`、`@Data`、`@Service/@Controller/@Repository` |
|
|
||||||
| 异常 | 统一异常处理,业务异常继承 `RuntimeException` |
|
|
||||||
| 缩进 | 4 空格,行 120 字符 |
|
|
||||||
|
|
||||||
### Vue 前端
|
|
||||||
|
|
||||||
| 项目 | 规范 |
|
|
||||||
|---|---|
|
|
||||||
| 框架 | Vue 3 + Composition API + Element Plus + Pinia |
|
|
||||||
| 命名 | 组件 PascalCase、文件 kebab-case、变量 camelCase |
|
|
||||||
| 缩进 | 2 空格,单引号,行 100 字符 |
|
|
||||||
|
|
||||||
### 导入顺序
|
### 导入顺序
|
||||||
|
#### Java
|
||||||
|
1. `java.*`
|
||||||
|
2. `javax.*`
|
||||||
|
3. 第三方库
|
||||||
|
4. `com.core.*`
|
||||||
|
5. `com.openhis.*`
|
||||||
|
6. `*.*`(其他包)
|
||||||
|
|
||||||
**Java:** `java.*` → `javax.*` → 第三方 → `com.core.*` → `com.openhis.*`
|
#### JavaScript/Vue
|
||||||
**Vue:** `vue` 相关 → 第三方 → `@/` 别名 → 相对路径
|
1. `vue` 相关
|
||||||
|
2. 第三方库
|
||||||
|
3. `@/` 别名导入
|
||||||
|
4. 相对路径导入
|
||||||
|
|
||||||
---
|
### 代码格式
|
||||||
|
#### Java
|
||||||
|
- 缩进:4个空格
|
||||||
|
- 行长度:120字符
|
||||||
|
- 左大括号不换行
|
||||||
|
|
||||||
## 🏗️ 开发约定
|
#### Vue/JavaScript
|
||||||
|
- 缩进:2个空格
|
||||||
|
- 字符串:优先使用单引号
|
||||||
|
- 行长度:100字符
|
||||||
|
|
||||||
| 领域 | 约定 |
|
## 关键配置文件
|
||||||
|---|---|
|
|
||||||
| API | RESTful,统一响应格式,Swagger 文档 |
|
|
||||||
| 数据库 | snake_case 命名,主键 `id`,软删除 `valid_flag` |
|
|
||||||
| 安全 | 所有 API 需权限验证,SQL 注入/XSS 防护 |
|
|
||||||
| 性能 | Druid 连接池,路由懒加载,虚拟滚动 |
|
|
||||||
|
|
||||||
---
|
### 后端配置
|
||||||
|
- 主配置:`openhis-server-new/openhis-application/src/main/resources/application.yml`
|
||||||
|
- 环境配置:`application-{profile}.yml`
|
||||||
|
- Maven 父 POM:`openhis-server-new/pom.xml`
|
||||||
|
|
||||||
## ⚙️ 关键配置
|
### 前端配置
|
||||||
|
- Vite 配置:`openhis-ui-vue3/vite.config.js`
|
||||||
|
- 环境变量:`.env.*` 文件
|
||||||
|
- 路由配置:`openhis-ui-vue3/src/router/index.js`
|
||||||
|
|
||||||
| 项目 | 值 |
|
## 开发约定
|
||||||
|---|---|
|
|
||||||
| 后端端口 | 18080 |
|
|
||||||
| 前端端口 | 81 |
|
|
||||||
| API 前缀 | `/openhis` |
|
|
||||||
| Swagger | `/openhis/swagger-ui/index.html` |
|
|
||||||
| 后端配置 | `application.yml` / `application-{profile}.yml` |
|
|
||||||
| 前端配置 | `vite.config.js` / `.env.*` |
|
|
||||||
|
|
||||||
---
|
### API 设计
|
||||||
|
- RESTful API 风格
|
||||||
|
- 统一响应格式
|
||||||
|
- 使用 Swagger 文档
|
||||||
|
- 错误码统一管理
|
||||||
|
|
||||||
## 📈 成熟度追踪
|
### 数据库
|
||||||
|
- 表名:snake_case
|
||||||
|
- 字段名:snake_case
|
||||||
|
- 主键:使用 `id`
|
||||||
|
- 软删除:使用 `valid_flag` 字段
|
||||||
|
|
||||||
| 等级 | 特征 | 本项目 |
|
### 前端组件
|
||||||
|---|---|---|
|
- 单一职责原则
|
||||||
| **L1 初始** | 零星使用 AI 工具 | ✅ 已超越 |
|
- Props 使用 camelCase
|
||||||
| **L2 管理** | 基础约束 + 反馈 + 控制 | ✅ **当前** |
|
- Events 使用 kebab-case
|
||||||
| **L3 定义** | 标准化、可复用 | 🔄 walkinglabs 5 子系统整合 |
|
- 使用 Composition API
|
||||||
| **L4 量化** | 数据驱动优化 | ⏳ |
|
- 组件文档使用 JSDoc
|
||||||
| **L5 优化** | AI 自主优化 Harness | ⏳ |
|
|
||||||
|
|
||||||
---
|
### 状态管理
|
||||||
|
- 模块化设计
|
||||||
|
- 异步操作使用 actions
|
||||||
|
- 避免在组件中直接修改状态
|
||||||
|
|
||||||
## 📚 技能索引(Codex 内置)
|
## 环境变量
|
||||||
|
|
||||||
| 技能 | 用途 |
|
### 前端
|
||||||
|---|---|
|
- `VITE_APP_BASE_API`: API 基础路径
|
||||||
| `$harness-engineering` | 主方法论 — 约束 + 反馈 + 控制 + 持久 |
|
- `VITE_APP_ENV`: 环境标识
|
||||||
| `$walkinglabs-harness` | 实战模式 — 5 子系统 + 模板 + 会话持续 |
|
|
||||||
| `$durable-execution` | 检查点、幂等性、事件溯源 |
|
|
||||||
| `$closed-loop-testing` | 质量门禁、测试策略、反馈循环 |
|
|
||||||
| `$constraint-design` | DSL 设计、策略模式、约束编排 |
|
|
||||||
| `$review-audit` | 审查工作流、审计追踪、合规检查 |
|
|
||||||
| `$full-chain-fix` | 全链路数据流修复 |
|
|
||||||
| `$karpathy-guidelines` | 减少 LLM 编码常见错误 |
|
|
||||||
|
|
||||||
---
|
### 后端
|
||||||
|
- `spring.profiles.active`: 激活的配置文件
|
||||||
|
- `core.name`: 应用名称
|
||||||
|
- `core.version`: 应用版本
|
||||||
|
|
||||||
> **总纲:** 你负责"做什么"和"为什么",Agent 负责"怎么做"和"做多好"
|
## 安全规范
|
||||||
> **工作循环:** Init → Plan → Implement → Verify → Cleanup
|
- 所有 API 接口需要权限验证
|
||||||
|
- 敏感信息使用环境变量
|
||||||
|
- SQL 注入防护
|
||||||
|
- XSS 攻击防护
|
||||||
|
|
||||||
|
## 性能优化
|
||||||
|
- 后端使用连接池(Druid)
|
||||||
|
- 前端使用路由懒加载
|
||||||
|
- 图片使用 WebP 格式
|
||||||
|
- 大列表使用虚拟滚动
|
||||||
|
|
||||||
|
## 常用工具类
|
||||||
|
- 后端:`com.core.common.utils.*`
|
||||||
|
- 前端:`@/utils/*`
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
1. 修改数据库结构需要同步 SQL 脚本
|
||||||
|
2. 新增功能需要添加权限配置
|
||||||
|
3. 前端路由需要在权限系统中注册
|
||||||
|
4. 接口变更需要更新 Swagger 文档
|
||||||
|
5. 遵循现有代码风格,避免不必要的变化
|
||||||
|
|
||||||
|
## 故障排除
|
||||||
|
- 后端端口:18080
|
||||||
|
- 前端端口:81
|
||||||
|
- API 前缀:`/openhis`
|
||||||
|
- Swagger UI:`/openhis/swagger-ui/index.html`
|
||||||
|
- Druid 监控:`/openhis/druid/login.html`
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.openhis.web.Inspection.dto;
|
package com.openhis.web.Inspection.dto;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Data;
|
||||||
import lombok.Setter;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -11,17 +11,30 @@ import java.util.List;
|
|||||||
* @author
|
* @author
|
||||||
* @date
|
* @date
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Data
|
||||||
@Setter
|
@Accessors(chain = true)
|
||||||
public class InstrumentManageInitDto {
|
public class InstrumentManageInitDto {
|
||||||
private List<statusEnumOption> statusFlagOptions;
|
private List<statusEnumOption> statusFlagOptions;
|
||||||
private List<InstrumentType> instrumentTypeList;
|
private List<InstrumentType> InstrumentTypeList;
|
||||||
private List<InstrumentStatusEnumOption> instrumentStatusEnumList;
|
private List<InstrumentStatusEnumOption> InstrumentStatusEnumList;
|
||||||
|
|
||||||
|
// 手动添加 setter 方法
|
||||||
|
public void setStatusFlagOptions(List<statusEnumOption> statusFlagOptions) {
|
||||||
|
this.statusFlagOptions = statusFlagOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstrumentTypeList(List<InstrumentType> InstrumentTypeList) {
|
||||||
|
this.InstrumentTypeList = InstrumentTypeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstrumentStatusEnumList(List<InstrumentStatusEnumOption> InstrumentStatusEnumList) {
|
||||||
|
this.InstrumentStatusEnumList = InstrumentStatusEnumList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 状态
|
* 状态
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Data
|
||||||
public static class statusEnumOption {
|
public static class statusEnumOption {
|
||||||
private Integer value;
|
private Integer value;
|
||||||
private String info;
|
private String info;
|
||||||
@@ -31,7 +44,7 @@ public class InstrumentManageInitDto {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Data
|
||||||
public static class InstrumentStatusEnumOption {
|
public static class InstrumentStatusEnumOption {
|
||||||
private Integer value;
|
private Integer value;
|
||||||
private String info;
|
private String info;
|
||||||
@@ -41,7 +54,7 @@ public class InstrumentManageInitDto {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Data
|
||||||
public static class InstrumentType {
|
public static class InstrumentType {
|
||||||
private Integer value;
|
private Integer value;
|
||||||
private String info;
|
private String info;
|
||||||
@@ -50,4 +63,6 @@ public class InstrumentManageInitDto {
|
|||||||
this.info = info;
|
this.info = info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,9 +48,6 @@ import com.openhis.web.personalization.dto.ActivityDeviceDto;
|
|||||||
import com.openhis.workflow.domain.ActivityDefinition;
|
import com.openhis.workflow.domain.ActivityDefinition;
|
||||||
import com.openhis.workflow.domain.DeviceRequest;
|
import com.openhis.workflow.domain.DeviceRequest;
|
||||||
import com.openhis.workflow.domain.InventoryItem;
|
import com.openhis.workflow.domain.InventoryItem;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
import com.openhis.workflow.domain.ServiceRequest;
|
import com.openhis.workflow.domain.ServiceRequest;
|
||||||
import com.openhis.workflow.service.*;
|
import com.openhis.workflow.service.*;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -949,27 +946,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
|||||||
/**
|
/**
|
||||||
* 处理药品
|
* 处理药品
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 remark 合并到 contentJson 中,确保 Mapper 能从 content_json 提取 remark
|
|
||||||
*/
|
|
||||||
private String injectRemarkIntoContentJson(String contentJson, String remark) {
|
|
||||||
if (remark == null || remark.isEmpty() || contentJson == null || contentJson.isEmpty()) {
|
|
||||||
return contentJson;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
|
||||||
JsonNode node = mapper.readTree(contentJson);
|
|
||||||
if (node instanceof ObjectNode) {
|
|
||||||
((ObjectNode) node).put("remark", remark);
|
|
||||||
return mapper.writeValueAsString(node);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("Failed to inject remark into contentJson: {}", e.getMessage());
|
|
||||||
}
|
|
||||||
return contentJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> handMedication(List<AdviceSaveDto> medicineList, Date curDate, String adviceOpType,
|
private List<String> handMedication(List<AdviceSaveDto> medicineList, Date curDate, String adviceOpType,
|
||||||
Long organizationId, String signCode) {
|
Long organizationId, String signCode) {
|
||||||
// 当前登录账号的科室id
|
// 当前登录账号的科室id
|
||||||
@@ -1186,10 +1162,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
|||||||
if (medicationRequest.getId() == null) {
|
if (medicationRequest.getId() == null) {
|
||||||
firstTimeSave = true;
|
firstTimeSave = true;
|
||||||
}
|
}
|
||||||
// 确保 contentJson 包含 remark
|
|
||||||
if (adviceSaveDto.getRemark() != null && !adviceSaveDto.getRemark().isEmpty()) {
|
|
||||||
medicationRequest.setContentJson(injectRemarkIntoContentJson(medicationRequest.getContentJson(), adviceSaveDto.getRemark()));
|
|
||||||
}
|
|
||||||
iMedicationRequestService.saveOrUpdate(medicationRequest);
|
iMedicationRequestService.saveOrUpdate(medicationRequest);
|
||||||
if (firstTimeSave) {
|
if (firstTimeSave) {
|
||||||
medRequestIdList.add(medicationRequest.getId().toString());
|
medRequestIdList.add(medicationRequest.getId().toString());
|
||||||
@@ -1650,10 +1622,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
|||||||
deviceRequest.setConditionId(adviceSaveDto.getConditionId()); // 诊断id
|
deviceRequest.setConditionId(adviceSaveDto.getConditionId()); // 诊断id
|
||||||
deviceRequest.setEncounterDiagnosisId(adviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id
|
deviceRequest.setEncounterDiagnosisId(adviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id
|
||||||
|
|
||||||
// 确保 contentJson 包含 remark
|
|
||||||
if (adviceSaveDto.getRemark() != null && !adviceSaveDto.getRemark().isEmpty()) {
|
|
||||||
deviceRequest.setContentJson(injectRemarkIntoContentJson(deviceRequest.getContentJson(), adviceSaveDto.getRemark()));
|
|
||||||
}
|
|
||||||
iDeviceRequestService.saveOrUpdate(deviceRequest);
|
iDeviceRequestService.saveOrUpdate(deviceRequest);
|
||||||
if (is_save) {
|
if (is_save) {
|
||||||
// 处理耗材发放
|
// 处理耗材发放
|
||||||
@@ -2065,9 +2033,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
|||||||
serviceRequest.setBasedOnTable(CommonConstants.TableName.MED_MEDICATION_REQUEST);
|
serviceRequest.setBasedOnTable(CommonConstants.TableName.MED_MEDICATION_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 备注
|
|
||||||
serviceRequest.setRemark(adviceSaveDto.getRemark());
|
|
||||||
|
|
||||||
iServiceRequestService.saveOrUpdate(serviceRequest);
|
iServiceRequestService.saveOrUpdate(serviceRequest);
|
||||||
|
|
||||||
// 保存时保存诊疗费用项
|
// 保存时保存诊疗费用项
|
||||||
@@ -2325,7 +2290,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
|||||||
log.info("BugFix: signOffAdvice - 签退所有请求,状态改为待签发, requestIdList={}", requestIdList);
|
log.info("BugFix: signOffAdvice - 签退所有请求,状态改为待签发, requestIdList={}", requestIdList);
|
||||||
|
|
||||||
// 尝试签退药品请求(只有存在的才会更新)
|
// 尝试签退药品请求(只有存在的才会更新)
|
||||||
iMedicationRequestService.updateDraftStatusBatch(requestIdList, null, null, null);
|
iMedicationRequestService.updateDraftStatusBatch(requestIdList, null, null);
|
||||||
// 尝试签退耗材请求(只有存在的才会更新)
|
// 尝试签退耗材请求(只有存在的才会更新)
|
||||||
iDeviceRequestService.updateDraftStatusBatch(requestIdList);
|
iDeviceRequestService.updateDraftStatusBatch(requestIdList);
|
||||||
// 尝试签退诊疗请求(只有存在的才会更新)
|
// 尝试签退诊疗请求(只有存在的才会更新)
|
||||||
|
|||||||
@@ -250,9 +250,4 @@ public class AdviceBaseDto {
|
|||||||
* 是否缺少取药科室配置(仅药品类型使用)
|
* 是否缺少取药科室配置(仅药品类型使用)
|
||||||
*/
|
*/
|
||||||
private Boolean pharmacyConfigMissing;
|
private Boolean pharmacyConfigMissing;
|
||||||
|
|
||||||
/**
|
|
||||||
* 备注(最长50字)
|
|
||||||
*/
|
|
||||||
private String remark;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -282,11 +282,6 @@ public class AdviceSaveDto {
|
|||||||
*/
|
*/
|
||||||
private String sourceBillNo;
|
private String sourceBillNo;
|
||||||
|
|
||||||
/**
|
|
||||||
* 备注(最长50字)
|
|
||||||
*/
|
|
||||||
private String remark;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置默认值
|
* 设置默认值
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -127,11 +127,11 @@ public class RequestBaseDto {
|
|||||||
* 请求状态
|
* 请求状态
|
||||||
*/
|
*/
|
||||||
private Integer statusEnum;
|
private Integer statusEnum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 退回原因
|
* 退回原因
|
||||||
*/
|
*/
|
||||||
private String reasonText;
|
private String reasonText;
|
||||||
|
|
||||||
private String statusEnum_enumText;
|
private String statusEnum_enumText;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ import java.time.ZoneId;
|
|||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,13 +185,12 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
|||||||
QueryWrapper<InpatientAdviceParam> queryWrapper
|
QueryWrapper<InpatientAdviceParam> queryWrapper
|
||||||
= HisQueryUtils.buildQueryWrapper(inpatientAdviceParam, null, null, null);
|
= HisQueryUtils.buildQueryWrapper(inpatientAdviceParam, null, null, null);
|
||||||
|
|
||||||
// 手动拼接requestStatus条件:COMPLETED(3)时同时包含CHECK_VERIFIED(10)和PENDING_RECEIVE(11)
|
// 手动拼接requestStatus条件:COMPLETED(3)时同时包含CHECK_VERIFIED(10)
|
||||||
// UNION查询外层列名为request_status(T1.status_enum AS request_status),不是status_enum
|
// UNION查询外层列名为request_status(T1.status_enum AS request_status),不是status_enum
|
||||||
if (requestStatus != null) {
|
if (requestStatus != null) {
|
||||||
if (RequestStatus.COMPLETED.getValue().equals(requestStatus)) {
|
if (RequestStatus.COMPLETED.getValue().equals(requestStatus)) {
|
||||||
queryWrapper.in("request_status",
|
queryWrapper.in("request_status",
|
||||||
RequestStatus.COMPLETED.getValue(), RequestStatus.CHECK_VERIFIED.getValue(),
|
RequestStatus.COMPLETED.getValue(), RequestStatus.CHECK_VERIFIED.getValue());
|
||||||
RequestStatus.PENDING_RECEIVE.getValue());
|
|
||||||
} else {
|
} else {
|
||||||
queryWrapper.eq("request_status", requestStatus);
|
queryWrapper.eq("request_status", requestStatus);
|
||||||
}
|
}
|
||||||
@@ -415,14 +413,9 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
|||||||
}
|
}
|
||||||
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
|
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
|
||||||
Date checkDate = new Date();
|
Date checkDate = new Date();
|
||||||
// 从请求中提取退回原因(所有项目共享同一原因)
|
|
||||||
String backReason = performInfoList.stream()
|
|
||||||
.map(PerformInfoDto::getBackReason)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.findFirst()
|
|
||||||
.orElse(null);
|
|
||||||
if (!serviceRequestList.isEmpty()) {
|
if (!serviceRequestList.isEmpty()) {
|
||||||
// 更新服务请求状态待发送
|
// 更新服务请求状态待发送
|
||||||
|
String backReason = performInfoList.get(0).getBackReason();
|
||||||
serviceRequestService.updateDraftStatus(
|
serviceRequestService.updateDraftStatus(
|
||||||
serviceRequestList.stream().map(PerformInfoDto::getRequestId).toList(), practitionerId, checkDate, backReason);
|
serviceRequestList.stream().map(PerformInfoDto::getRequestId).toList(), practitionerId, checkDate, backReason);
|
||||||
}
|
}
|
||||||
@@ -582,7 +575,10 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
|||||||
// 处理长期已发放的药品
|
// 处理长期已发放的药品
|
||||||
if (!longMedDispensedList.isEmpty()) {
|
if (!longMedDispensedList.isEmpty()) {
|
||||||
// 生成退药单
|
// 生成退药单
|
||||||
this.creatRefundMedicationList(tempMedDispensedList, procedureIdMap);
|
this.creatRefundMedicationList(longMedDispensedList, procedureIdMap);
|
||||||
|
// 药品退药请求状态变更(待退药)
|
||||||
|
medicationRequestService.updateCancelledStatusBatch(
|
||||||
|
longMedDispensedList.stream().map(MedicationDispense::getMedReqId).toList(), null, null);
|
||||||
}
|
}
|
||||||
// 处理临时已发放药品
|
// 处理临时已发放药品
|
||||||
if (!tempMedDispensedList.isEmpty()) {
|
if (!tempMedDispensedList.isEmpty()) {
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ public class OutpatientInfusionAppServiceImpl implements IOutpatientInfusionAppS
|
|||||||
}
|
}
|
||||||
boolean result = serviceRequestService.updateCancelledStatus(serviceReqId, now, practitionerId, orgId);
|
boolean result = serviceRequestService.updateCancelledStatus(serviceReqId, now, practitionerId, orgId);
|
||||||
// 更新主服务请求状态为待执行
|
// 更新主服务请求状态为待执行
|
||||||
serviceRequestService.updateDraftStatus(List.of(serviceRequest.getBasedOnId()), null, null, null);
|
serviceRequestService.updateDraftStatus(List.of(serviceRequest.getBasedOnId()), null, null);
|
||||||
if (result) {
|
if (result) {
|
||||||
// 判断是否全部取消执行
|
// 判断是否全部取消执行
|
||||||
boolean exists = serviceRequestMapper.exists(new LambdaQueryWrapper<ServiceRequest>()
|
boolean exists = serviceRequestMapper.exists(new LambdaQueryWrapper<ServiceRequest>()
|
||||||
|
|||||||
@@ -52,8 +52,7 @@ public class PendingMedicationDetailsAppServiceImpl implements IPendingMedicatio
|
|||||||
Page<PendingMedicationPageDto> pendingMedicationPage = pendingMedicationDetailsMapper
|
Page<PendingMedicationPageDto> pendingMedicationPage = pendingMedicationDetailsMapper
|
||||||
.selectPendingMedicationDetailsPage(new Page<>(pageNo, pageSize), queryWrapper,
|
.selectPendingMedicationDetailsPage(new Page<>(pageNo, pageSize), queryWrapper,
|
||||||
DispenseStatus.IN_PROGRESS.getValue(), DispenseStatus.PREPARATION.getValue(),
|
DispenseStatus.IN_PROGRESS.getValue(), DispenseStatus.PREPARATION.getValue(),
|
||||||
DispenseStatus.PREPARED.getValue(), DispenseStatus.SUMMARIZED.getValue(),
|
DispenseStatus.PREPARED.getValue(), EncounterClass.AMB.getValue(), EncounterClass.IMP.getValue());
|
||||||
EncounterClass.AMB.getValue(), EncounterClass.IMP.getValue());
|
|
||||||
|
|
||||||
pendingMedicationPage.getRecords().forEach(e -> {
|
pendingMedicationPage.getRecords().forEach(e -> {
|
||||||
// 发药类型
|
// 发药类型
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ public interface PendingMedicationDetailsMapper {
|
|||||||
* @param inProgress 发药类型:待发药
|
* @param inProgress 发药类型:待发药
|
||||||
* @param preparation 发药类型:待配药
|
* @param preparation 发药类型:待配药
|
||||||
* @param prepared 发药类型:已配药
|
* @param prepared 发药类型:已配药
|
||||||
* @param summarized 发药类型:已汇总
|
|
||||||
* @param amb 门诊类型
|
* @param amb 门诊类型
|
||||||
* @param imp 住院类型
|
* @param imp 住院类型
|
||||||
* @return 待发药明细
|
* @return 待发药明细
|
||||||
@@ -33,7 +32,6 @@ public interface PendingMedicationDetailsMapper {
|
|||||||
@Param("inProgress") Integer inProgress,
|
@Param("inProgress") Integer inProgress,
|
||||||
@Param("preparation") Integer preparation,
|
@Param("preparation") Integer preparation,
|
||||||
@Param("prepared") Integer prepared,
|
@Param("prepared") Integer prepared,
|
||||||
@Param("summarized") Integer summarized,
|
|
||||||
@Param("amb") Integer amb,
|
@Param("amb") Integer amb,
|
||||||
@Param("imp") Integer imp);
|
@Param("imp") Integer imp);
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ import com.openhis.web.regdoctorstation.dto.*;
|
|||||||
import com.openhis.web.regdoctorstation.mapper.AdviceManageAppMapper;
|
import com.openhis.web.regdoctorstation.mapper.AdviceManageAppMapper;
|
||||||
import com.openhis.web.regdoctorstation.utils.RegPrescriptionUtils;
|
import com.openhis.web.regdoctorstation.utils.RegPrescriptionUtils;
|
||||||
import com.openhis.workflow.domain.DeviceRequest;
|
import com.openhis.workflow.domain.DeviceRequest;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
import com.openhis.workflow.domain.ServiceRequest;
|
import com.openhis.workflow.domain.ServiceRequest;
|
||||||
import com.openhis.workflow.service.IActivityDefinitionService;
|
import com.openhis.workflow.service.IActivityDefinitionService;
|
||||||
import com.openhis.workflow.domain.ActivityDefinition;
|
import com.openhis.workflow.domain.ActivityDefinition;
|
||||||
@@ -355,27 +352,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
|||||||
/**
|
/**
|
||||||
* 处理药品
|
* 处理药品
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 remark 合并到 contentJson 中,确保 Mapper 能从 content_json 提取 remark
|
|
||||||
*/
|
|
||||||
private String injectRemarkIntoContentJson(String contentJson, String remark) {
|
|
||||||
if (remark == null || remark.isEmpty() || contentJson == null || contentJson.isEmpty()) {
|
|
||||||
return contentJson;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
|
||||||
JsonNode node = mapper.readTree(contentJson);
|
|
||||||
if (node instanceof ObjectNode) {
|
|
||||||
((ObjectNode) node).put("remark", remark);
|
|
||||||
return mapper.writeValueAsString(node);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("Failed to inject remark into contentJson: {}", e.getMessage());
|
|
||||||
}
|
|
||||||
return contentJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> handMedication(List<RegAdviceSaveDto> medicineList, Date startTime, Date authoredTime,
|
private List<String> handMedication(List<RegAdviceSaveDto> medicineList, Date startTime, Date authoredTime,
|
||||||
Date curDate, String adviceOpType, Long organizationId, String signCode) {
|
Date curDate, String adviceOpType, Long organizationId, String signCode) {
|
||||||
// 当前登录账号的科室id
|
// 当前登录账号的科室id
|
||||||
@@ -474,10 +450,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
|||||||
if (longMedicationRequest.getId() == null) {
|
if (longMedicationRequest.getId() == null) {
|
||||||
firstTimeSave = true;
|
firstTimeSave = true;
|
||||||
}
|
}
|
||||||
// 确保 contentJson 包含 remark
|
|
||||||
if (regAdviceSaveDto.getRemark() != null && !regAdviceSaveDto.getRemark().isEmpty()) {
|
|
||||||
longMedicationRequest.setContentJson(injectRemarkIntoContentJson(longMedicationRequest.getContentJson(), regAdviceSaveDto.getRemark()));
|
|
||||||
}
|
|
||||||
iMedicationRequestService.saveOrUpdate(longMedicationRequest);
|
iMedicationRequestService.saveOrUpdate(longMedicationRequest);
|
||||||
if (firstTimeSave) {
|
if (firstTimeSave) {
|
||||||
medRequestIdList.add(longMedicationRequest.getId().toString());
|
medRequestIdList.add(longMedicationRequest.getId().toString());
|
||||||
@@ -565,10 +537,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
|||||||
if (tempMedicationRequest.getId() == null) {
|
if (tempMedicationRequest.getId() == null) {
|
||||||
firstTimeSave = true;
|
firstTimeSave = true;
|
||||||
}
|
}
|
||||||
// 确保 contentJson 包含 remark
|
|
||||||
if (regAdviceSaveDto.getRemark() != null && !regAdviceSaveDto.getRemark().isEmpty()) {
|
|
||||||
tempMedicationRequest.setContentJson(injectRemarkIntoContentJson(tempMedicationRequest.getContentJson(), regAdviceSaveDto.getRemark()));
|
|
||||||
}
|
|
||||||
iMedicationRequestService.saveOrUpdate(tempMedicationRequest);
|
iMedicationRequestService.saveOrUpdate(tempMedicationRequest);
|
||||||
if (firstTimeSave) {
|
if (firstTimeSave) {
|
||||||
medRequestIdList.add(tempMedicationRequest.getId().toString());
|
medRequestIdList.add(tempMedicationRequest.getId().toString());
|
||||||
@@ -672,7 +640,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
|||||||
longServiceRequest.setBasedOnTable(CommonConstants.TableName.MED_MEDICATION_REQUEST);
|
longServiceRequest.setBasedOnTable(CommonConstants.TableName.MED_MEDICATION_REQUEST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
longServiceRequest.setRemark(regAdviceSaveDto.getRemark());
|
|
||||||
iServiceRequestService.saveOrUpdate(longServiceRequest);
|
iServiceRequestService.saveOrUpdate(longServiceRequest);
|
||||||
if (longServiceRequest.getId() != null) {
|
if (longServiceRequest.getId() != null) {
|
||||||
processedRequestIds.add(longServiceRequest.getId());
|
processedRequestIds.add(longServiceRequest.getId());
|
||||||
@@ -724,7 +691,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
|||||||
tempServiceRequest.setBasedOnTable(CommonConstants.TableName.MED_MEDICATION_REQUEST);
|
tempServiceRequest.setBasedOnTable(CommonConstants.TableName.MED_MEDICATION_REQUEST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tempServiceRequest.setRemark(regAdviceSaveDto.getRemark());
|
|
||||||
iServiceRequestService.saveOrUpdate(tempServiceRequest);
|
iServiceRequestService.saveOrUpdate(tempServiceRequest);
|
||||||
if (tempServiceRequest.getId() != null) {
|
if (tempServiceRequest.getId() != null) {
|
||||||
processedRequestIds.add(tempServiceRequest.getId());
|
processedRequestIds.add(tempServiceRequest.getId());
|
||||||
@@ -856,10 +822,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
|||||||
deviceRequest.setConditionId(regAdviceSaveDto.getConditionId()); // 诊断id
|
deviceRequest.setConditionId(regAdviceSaveDto.getConditionId()); // 诊断id
|
||||||
deviceRequest.setEncounterDiagnosisId(regAdviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id
|
deviceRequest.setEncounterDiagnosisId(regAdviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id
|
||||||
}
|
}
|
||||||
// 确保 contentJson 包含 remark
|
|
||||||
if (regAdviceSaveDto.getRemark() != null && !regAdviceSaveDto.getRemark().isEmpty()) {
|
|
||||||
deviceRequest.setContentJson(injectRemarkIntoContentJson(deviceRequest.getContentJson(), regAdviceSaveDto.getRemark()));
|
|
||||||
}
|
|
||||||
iDeviceRequestService.saveOrUpdate(deviceRequest);
|
iDeviceRequestService.saveOrUpdate(deviceRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -899,10 +861,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
|||||||
deviceRequest.setConditionId(regAdviceSaveDto.getConditionId()); // 诊断id
|
deviceRequest.setConditionId(regAdviceSaveDto.getConditionId()); // 诊断id
|
||||||
deviceRequest.setEncounterDiagnosisId(regAdviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id
|
deviceRequest.setEncounterDiagnosisId(regAdviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id
|
||||||
}
|
}
|
||||||
// 确保 contentJson 包含 remark
|
|
||||||
if (regAdviceSaveDto.getRemark() != null && !regAdviceSaveDto.getRemark().isEmpty()) {
|
|
||||||
deviceRequest.setContentJson(injectRemarkIntoContentJson(deviceRequest.getContentJson(), regAdviceSaveDto.getRemark()));
|
|
||||||
}
|
|
||||||
iDeviceRequestService.saveOrUpdate(deviceRequest);
|
iDeviceRequestService.saveOrUpdate(deviceRequest);
|
||||||
|
|
||||||
// 保存时,保存耗材费用项
|
// 保存时,保存耗材费用项
|
||||||
@@ -1059,7 +1017,7 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
|||||||
}
|
}
|
||||||
if (!medicineRequestIds.isEmpty()) {
|
if (!medicineRequestIds.isEmpty()) {
|
||||||
// 根据请求id更新请求状态
|
// 根据请求id更新请求状态
|
||||||
iMedicationRequestService.updateDraftStatusBatch(medicineRequestIds, null, null, null);
|
iMedicationRequestService.updateDraftStatusBatch(medicineRequestIds, null, null);
|
||||||
}
|
}
|
||||||
if (!activityRequestIds.isEmpty()) {
|
if (!activityRequestIds.isEmpty()) {
|
||||||
// 根据请求id更新请求状态
|
// 根据请求id更新请求状态
|
||||||
|
|||||||
@@ -50,9 +50,4 @@ public class RegRequestBaseDto extends RequestBaseDto {
|
|||||||
private String doseUnitCode;
|
private String doseUnitCode;
|
||||||
private String doseUnitCode_dictText;
|
private String doseUnitCode_dictText;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 备注(最长50字)
|
|
||||||
*/
|
|
||||||
private String remark;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,7 +262,7 @@
|
|||||||
AND T1.inventory_status_enum != 3
|
AND T1.inventory_status_enum != 3
|
||||||
AND T1.delete_flag = '0'
|
AND T1.delete_flag = '0'
|
||||||
<choose>
|
<choose>
|
||||||
<when test="lotNumber != null and lotNumber != ''">
|
<when test="lotNumber != null">
|
||||||
AND T1.lot_number = #{lotNumber}
|
AND T1.lot_number = #{lotNumber}
|
||||||
</when>
|
</when>
|
||||||
</choose>
|
</choose>
|
||||||
|
|||||||
@@ -516,7 +516,6 @@
|
|||||||
T1.patient_id AS patient_id,
|
T1.patient_id AS patient_id,
|
||||||
'med_medication_definition' AS advice_table_name,
|
'med_medication_definition' AS advice_table_name,
|
||||||
T1.medication_id AS advice_definition_id
|
T1.medication_id AS advice_definition_id
|
||||||
, T1.content_json::jsonb ->> 'remark' AS remark
|
|
||||||
, T1.back_reason AS reason_text
|
, T1.back_reason AS reason_text
|
||||||
FROM med_medication_request AS T1
|
FROM med_medication_request AS T1
|
||||||
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
|
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
|
||||||
@@ -579,7 +578,6 @@
|
|||||||
T1.patient_id AS patient_id,
|
T1.patient_id AS patient_id,
|
||||||
'med_medication_definition' AS advice_table_name,
|
'med_medication_definition' AS advice_table_name,
|
||||||
T3.ID AS advice_definition_id
|
T3.ID AS advice_definition_id
|
||||||
, T2.content_json::jsonb ->> 'remark' AS remark
|
|
||||||
, T2.back_reason AS reason_text
|
, T2.back_reason AS reason_text
|
||||||
FROM adm_charge_item AS T1
|
FROM adm_charge_item AS T1
|
||||||
INNER JOIN med_medication_request AS T2 ON T2.ID = T1.service_id AND T2.delete_flag = '0'
|
INNER JOIN med_medication_request AS T2 ON T2.ID = T1.service_id AND T2.delete_flag = '0'
|
||||||
@@ -644,7 +642,6 @@
|
|||||||
CI.patient_id AS patient_id,
|
CI.patient_id AS patient_id,
|
||||||
'adm_device_definition' AS advice_table_name,
|
'adm_device_definition' AS advice_table_name,
|
||||||
CI.product_id AS advice_definition_id
|
CI.product_id AS advice_definition_id
|
||||||
, NULL AS remark
|
|
||||||
, NULL AS reason_text
|
, NULL AS reason_text
|
||||||
FROM adm_charge_item AS CI
|
FROM adm_charge_item AS CI
|
||||||
LEFT JOIN adm_charge_item_definition CID ON CID.id = CI.definition_id AND CID.delete_flag = '0'
|
LEFT JOIN adm_charge_item_definition CID ON CID.id = CI.definition_id AND CID.delete_flag = '0'
|
||||||
@@ -700,7 +697,6 @@
|
|||||||
T1.patient_id AS patient_id,
|
T1.patient_id AS patient_id,
|
||||||
'adm_device_definition' AS advice_table_name,
|
'adm_device_definition' AS advice_table_name,
|
||||||
T1.device_def_id AS advice_definition_id
|
T1.device_def_id AS advice_definition_id
|
||||||
, T1.content_json::jsonb ->> 'remark' AS remark
|
|
||||||
, NULL AS reason_text
|
, NULL AS reason_text
|
||||||
FROM wor_device_request AS T1
|
FROM wor_device_request AS T1
|
||||||
LEFT JOIN adm_device_definition AS T2 ON T2.ID = T1.device_def_id
|
LEFT JOIN adm_device_definition AS T2 ON T2.ID = T1.device_def_id
|
||||||
@@ -757,8 +753,7 @@
|
|||||||
T1.encounter_id AS encounter_id,
|
T1.encounter_id AS encounter_id,
|
||||||
T1.patient_id AS patient_id,
|
T1.patient_id AS patient_id,
|
||||||
'wor_activity_definition' AS advice_table_name,
|
'wor_activity_definition' AS advice_table_name,
|
||||||
T1.activity_id AS advice_definition_id,
|
T1.activity_id AS advice_definition_id
|
||||||
T1.remark AS remark
|
|
||||||
, T1.reason_text AS reason_text
|
, T1.reason_text AS reason_text
|
||||||
FROM wor_service_request AS T1
|
FROM wor_service_request AS T1
|
||||||
LEFT JOIN wor_activity_definition AS T2
|
LEFT JOIN wor_activity_definition AS T2
|
||||||
@@ -938,4 +933,4 @@
|
|||||||
ORDER BY t1.ID, t1.name ASC, t2.ID ASC
|
ORDER BY t1.ID, t1.name ASC, t2.ID ASC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
ON T5.medication_def_id = T6.id
|
ON T5.medication_def_id = T6.id
|
||||||
AND T5.delete_flag = '0'
|
AND T5.delete_flag = '0'
|
||||||
WHERE T1.delete_flag = '0'
|
WHERE T1.delete_flag = '0'
|
||||||
AND T1.status_enum IN (#{inProgress}, #{preparation}, #{prepared}, #{summarized})
|
AND T1.status_enum IN (#{inProgress}, #{preparation}, #{prepared})
|
||||||
ORDER BY T1.create_time DESC
|
ORDER BY T1.create_time DESC
|
||||||
) AS T7
|
) AS T7
|
||||||
${ew.customSqlSegment}
|
${ew.customSqlSegment}
|
||||||
|
|||||||
@@ -214,11 +214,11 @@
|
|||||||
T1.dispense_per_duration AS dispense_per_duration,
|
T1.dispense_per_duration AS dispense_per_duration,
|
||||||
T2.part_percent AS part_percent,
|
T2.part_percent AS part_percent,
|
||||||
ccd.name AS condition_definition_name,
|
ccd.name AS condition_definition_name,
|
||||||
|
T1.effective_dose_start AS start_time,
|
||||||
T1.therapy_enum AS therapyEnum,
|
T1.therapy_enum AS therapyEnum,
|
||||||
T1.sort_number AS sort_number,
|
T1.sort_number AS sort_number,
|
||||||
T1.effective_dose_start AS start_time,
|
|
||||||
T1.based_on_id AS based_on_id,
|
T1.based_on_id AS based_on_id,
|
||||||
T1.medication_id AS advice_definition_id,
|
T1.medication_id AS advice_definition_id
|
||||||
T1.effective_dose_end AS stop_time,
|
T1.effective_dose_end AS stop_time,
|
||||||
T1.update_by AS stop_user_name
|
T1.update_by AS stop_user_name
|
||||||
FROM med_medication_request AS T1
|
FROM med_medication_request AS T1
|
||||||
@@ -274,8 +274,8 @@
|
|||||||
99 AS sort_number,
|
99 AS sort_number,
|
||||||
T1.req_authored_time AS start_time,
|
T1.req_authored_time AS start_time,
|
||||||
T1.based_on_id AS based_on_id,
|
T1.based_on_id AS based_on_id,
|
||||||
T1.device_def_id AS advice_definition_id,
|
T1.device_def_id AS advice_definition_id
|
||||||
NULL::timestamp AS stop_time,
|
NULL AS stop_time,
|
||||||
'' AS stop_user_name
|
'' AS stop_user_name
|
||||||
FROM wor_device_request AS T1
|
FROM wor_device_request AS T1
|
||||||
LEFT JOIN adm_device_definition AS T2 ON T2.ID = T1.device_def_id
|
LEFT JOIN adm_device_definition AS T2 ON T2.ID = T1.device_def_id
|
||||||
@@ -327,7 +327,7 @@
|
|||||||
99 AS sort_number,
|
99 AS sort_number,
|
||||||
T1.occurrence_start_time AS start_time,
|
T1.occurrence_start_time AS start_time,
|
||||||
T1.based_on_id AS based_on_id,
|
T1.based_on_id AS based_on_id,
|
||||||
T1.activity_id AS advice_definition_id,
|
T1.activity_id AS advice_definition_id
|
||||||
T1.occurrence_end_time AS stop_time,
|
T1.occurrence_end_time AS stop_time,
|
||||||
T1.update_by AS stop_user_name
|
T1.update_by AS stop_user_name
|
||||||
FROM wor_service_request AS T1
|
FROM wor_service_request AS T1
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ public class MedicationRequestServiceImpl extends ServiceImpl<MedicationRequestM
|
|||||||
if (checkDate != null) {
|
if (checkDate != null) {
|
||||||
updateWrapper.set(MedicationRequest::getCheckTime, checkDate);
|
updateWrapper.set(MedicationRequest::getCheckTime, checkDate);
|
||||||
}
|
}
|
||||||
|
if (backReason != null) {
|
||||||
|
updateWrapper.set(MedicationRequest::getBackReason, backReason);
|
||||||
|
}
|
||||||
baseMapper.update(null, updateWrapper);
|
baseMapper.update(null, updateWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -173,9 +173,4 @@ public class ServiceRequest extends HisBaseEntity {
|
|||||||
*/
|
*/
|
||||||
private Integer generateSourceEnum;
|
private Integer generateSourceEnum;
|
||||||
|
|
||||||
/**
|
|
||||||
* 备注(最长50字)
|
|
||||||
*/
|
|
||||||
private String remark;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
AND T1.delete_flag = '0'
|
AND T1.delete_flag = '0'
|
||||||
AND T2.delete_flag = '0'
|
AND T2.delete_flag = '0'
|
||||||
AND T1.tenant_id = #{tenantId}
|
AND T1.tenant_id = #{tenantId}
|
||||||
LIMIT 1
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
77
openhis-ui-vue3/package-lock.json
generated
77
openhis-ui-vue3/package-lock.json
generated
@@ -65,7 +65,7 @@
|
|||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"unplugin-auto-import": "0.17.1",
|
"unplugin-auto-import": "0.17.1",
|
||||||
"unplugin-vue-setup-extend-plus": "1.0.0",
|
"unplugin-vue-setup-extend-plus": "1.0.0",
|
||||||
"vite": "5.0.4",
|
"vite": "^5.0.4",
|
||||||
"vite-plugin-compression": "0.5.1",
|
"vite-plugin-compression": "0.5.1",
|
||||||
"vite-plugin-svg-icons": "2.0.1",
|
"vite-plugin-svg-icons": "2.0.1",
|
||||||
"vite-plugin-vue-mcp": "^0.3.2",
|
"vite-plugin-vue-mcp": "^0.3.2",
|
||||||
@@ -3093,41 +3093,6 @@
|
|||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/mocker": {
|
|
||||||
"version": "4.1.2",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@vitest/mocker/-/mocker-4.1.2.tgz",
|
|
||||||
"integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@vitest/spy": "4.1.2",
|
|
||||||
"estree-walker": "^3.0.3",
|
|
||||||
"magic-string": "^0.30.21"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://opencollective.com/vitest"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"msw": "^2.4.9",
|
|
||||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"msw": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"vite": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@vitest/mocker/node_modules/estree-walker": {
|
|
||||||
"version": "3.0.3",
|
|
||||||
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
|
|
||||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/estree": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@vitest/pretty-format": {
|
"node_modules/@vitest/pretty-format": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-4.1.2.tgz",
|
"resolved": "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-4.1.2.tgz",
|
||||||
@@ -12671,9 +12636,10 @@
|
|||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.0.4",
|
"version": "5.0.4",
|
||||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-5.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.0.4.tgz",
|
||||||
"integrity": "sha512-RzAr8LSvM8lmhB4tQ5OPcBhpjOZRZjuxv9zO5UcxeoY2bd3kP3Ticd40Qma9/BqZ8JS96Ll/jeBX9u+LJZrhVg==",
|
"integrity": "sha512-RzAr8LSvM8lmhB4tQ5OPcBhpjOZRZjuxv9zO5UcxeoY2bd3kP3Ticd40Qma9/BqZ8JS96Ll/jeBX9u+LJZrhVg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.19.3",
|
"esbuild": "^0.19.3",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
@@ -13336,6 +13302,33 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vitest/node_modules/@vitest/mocker": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vitest/spy": "4.1.2",
|
||||||
|
"estree-walker": "^3.0.3",
|
||||||
|
"magic-string": "^0.30.21"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/vitest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"msw": "^2.4.9",
|
||||||
|
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"msw": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vite": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vitest/node_modules/chokidar": {
|
"node_modules/vitest/node_modules/chokidar": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
||||||
@@ -13398,6 +13391,16 @@
|
|||||||
"@esbuild/win32-x64": "0.27.7"
|
"@esbuild/win32-x64": "0.27.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vitest/node_modules/estree-walker": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vitest/node_modules/fsevents": {
|
"node_modules/vitest/node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
|||||||
@@ -83,11 +83,11 @@
|
|||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"unplugin-auto-import": "0.17.1",
|
"unplugin-auto-import": "0.17.1",
|
||||||
"unplugin-vue-setup-extend-plus": "1.0.0",
|
"unplugin-vue-setup-extend-plus": "1.0.0",
|
||||||
"vite": "5.0.4",
|
"vite": "^5.0.4",
|
||||||
"vite-plugin-compression": "0.5.1",
|
"vite-plugin-compression": "0.5.1",
|
||||||
"vite-plugin-svg-icons": "2.0.1",
|
"vite-plugin-svg-icons": "2.0.1",
|
||||||
"vite-plugin-vue-mcp": "^0.3.2",
|
"vite-plugin-vue-mcp": "^0.3.2",
|
||||||
"vitest": "^4.0.18",
|
"vitest": "^4.0.18",
|
||||||
"vue-tsc": "^3.1.8"
|
"vue-tsc": "^3.1.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -292,6 +292,12 @@ export const dynamicRoutes = [
|
|||||||
name: 'DoctorStation',
|
name: 'DoctorStation',
|
||||||
meta: {title: '医生工作站', icon: 'operation'},
|
meta: {title: '医生工作站', icon: 'operation'},
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index',
|
||||||
|
component: () => import('@/views/doctorstation/index.vue'),
|
||||||
|
name: 'DoctorStationIndex',
|
||||||
|
meta: {title: '医生工作站', icon: 'operation'}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'pending-emr',
|
path: 'pending-emr',
|
||||||
component: () => import('@/views/doctorstation/pendingEmr.vue'),
|
component: () => import('@/views/doctorstation/pendingEmr.vue'),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="exam-app-container"
|
class="exam-app-container"
|
||||||
style="width: 100%; max-width: 1200px;"
|
style="width: 100%; max-width: 1200px;"
|
||||||
@@ -723,40 +723,60 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right-column">
|
<div class="right-column">
|
||||||
<!-- 右侧:已选择(检查项目、检查方法为两类独立选择结果) -->
|
<!-- 右侧:已选择(检查项目、检查方法为两类独立选择结果) -->
|
||||||
<div class="selected-panel">
|
<div class="selected-panel">
|
||||||
<div class="panel-label">已选择:</div>
|
<div class="panel-label">
|
||||||
<div class="selected-tags">
|
已选择:
|
||||||
<template v-if="selectedItems.length === 0 && selectedMethods.length === 0">
|
</div>
|
||||||
<div class="empty-selected">–</div>
|
<div class="selected-tags">
|
||||||
</template>
|
<template v-if="selectedItems.length === 0 && selectedMethods.length === 0">
|
||||||
<template v-else>
|
<div class="empty-selected">
|
||||||
<div
|
–
|
||||||
v-for="(item, idx) in selectedItems"
|
|
||||||
:key="'project-' + item.id"
|
|
||||||
class="selected-item-card"
|
|
||||||
:class="{ 'is-expanded': item.projectFoldExpanded }"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="fold-strip fold-strip-project"
|
|
||||||
:class="{ 'is-open': item.projectFoldExpanded }"
|
|
||||||
>
|
|
||||||
<div class="fold-strip-header" :class="{ 'no-chevron': !hasItemPackage(item) }" @click="hasItemPackage(item) && toggleProjectFold(item)">
|
|
||||||
<el-icon v-if="hasItemPackage(item)" :class="['fold-chevron', { open: item.projectFoldExpanded }]">
|
|
||||||
<ArrowDown />
|
|
||||||
</el-icon>
|
|
||||||
<div class="fold-header-main">
|
|
||||||
<span class="fold-kicker">检查项目</span>
|
|
||||||
<el-tooltip :content="getDisplayItemName(item)" placement="top" :show-after="400">
|
|
||||||
<span class="fold-title line-clamp-2">{{ getDisplayItemName(item) }}</span>
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
<span class="fold-price-strong">¥{{ formatDetailAmount(item.price || 0) }}</span>
|
|
||||||
<el-button link type="danger" size="small" @click.stop="handleRemoveItem(idx, item)">
|
|
||||||
<el-icon><Close /></el-icon>
|
|
||||||
</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- 仅当项目有套餐时展示明细区域,普通项目无明细可展示 -->
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div
|
||||||
|
v-for="(item, idx) in selectedItems"
|
||||||
|
:key="'project-' + item.id"
|
||||||
|
class="selected-item-card"
|
||||||
|
:class="{ 'is-expanded': item.projectFoldExpanded }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="fold-strip fold-strip-project"
|
||||||
|
:class="{ 'is-open': item.projectFoldExpanded }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="fold-strip-header"
|
||||||
|
:class="{ 'no-chevron': !hasItemPackage(item) }"
|
||||||
|
@click="hasItemPackage(item) && toggleProjectFold(item)"
|
||||||
|
>
|
||||||
|
<el-icon
|
||||||
|
v-if="hasItemPackage(item)"
|
||||||
|
:class="['fold-chevron', { open: item.projectFoldExpanded }]"
|
||||||
|
>
|
||||||
|
<ArrowDown />
|
||||||
|
</el-icon>
|
||||||
|
<div class="fold-header-main">
|
||||||
|
<span class="fold-kicker">检查项目</span>
|
||||||
|
<el-tooltip
|
||||||
|
:content="getDisplayItemName(item)"
|
||||||
|
placement="top"
|
||||||
|
:show-after="400"
|
||||||
|
>
|
||||||
|
<span class="fold-title line-clamp-2">{{ getDisplayItemName(item) }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
<span class="fold-price-strong">¥{{ formatDetailAmount(item.price || 0) }}</span>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
@click.stop="handleRemoveItem(idx, item)"
|
||||||
|
>
|
||||||
|
<el-icon><Close /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<!-- 仅当项目有套餐时展示明细区域,普通项目无明细可展示 -->
|
||||||
<div
|
<div
|
||||||
v-if="hasItemPackage(item) && item.projectFoldExpanded"
|
v-if="hasItemPackage(item) && item.projectFoldExpanded"
|
||||||
class="fold-strip-body"
|
class="fold-strip-body"
|
||||||
@@ -803,47 +823,70 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-for="(method, idx) in selectedMethods"
|
v-for="(method, idx) in selectedMethods"
|
||||||
:key="'method-' + method.id"
|
:key="'method-' + method.id"
|
||||||
class="selected-item-card"
|
class="selected-item-card"
|
||||||
:class="{ 'is-expanded': method.expanded }"
|
:class="{ 'is-expanded': method.expanded }"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="fold-strip fold-strip-method"
|
class="fold-strip fold-strip-method"
|
||||||
:class="{ 'is-open': method.expanded }"
|
:class="{ 'is-open': method.expanded }"
|
||||||
>
|
|
||||||
<div class="fold-strip-header" :class="{ 'no-chevron': !hasStandaloneMethodPackage(method) }" @click="hasStandaloneMethodPackage(method) && toggleSelectedMethodFold(method)">
|
|
||||||
<el-icon v-if="hasStandaloneMethodPackage(method)" :class="['fold-chevron', { open: method.expanded }]">
|
|
||||||
<ArrowDown />
|
|
||||||
</el-icon>
|
|
||||||
<div class="fold-header-main">
|
|
||||||
<span class="fold-kicker">检查方法</span>
|
|
||||||
<span
|
|
||||||
class="fold-title fold-title-plain line-clamp-2"
|
|
||||||
:title="getDisplayMethodName(method)"
|
|
||||||
>
|
|
||||||
{{ getDisplayMethodName(method) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
v-if="hasStandaloneMethodPackage(method)"
|
|
||||||
class="fold-price-strong warn"
|
|
||||||
>
|
>
|
||||||
¥{{ formatDetailAmount(method.packagePrice || method.price || 0) }}
|
<div
|
||||||
</span>
|
class="fold-strip-header"
|
||||||
<el-button link type="danger" size="small" @click.stop="handleRemoveMethod(idx)">
|
:class="{ 'no-chevron': !hasStandaloneMethodPackage(method) }"
|
||||||
<el-icon><Close /></el-icon>
|
@click="hasStandaloneMethodPackage(method) && toggleSelectedMethodFold(method)"
|
||||||
</el-button>
|
>
|
||||||
</div>
|
<el-icon
|
||||||
<!-- 仅当检查方法有套餐时展示明细 -->
|
v-if="hasStandaloneMethodPackage(method)"
|
||||||
<div v-if="hasStandaloneMethodPackage(method) && method.expanded" class="fold-strip-body">
|
:class="['fold-chevron', { open: method.expanded }]"
|
||||||
<div class="fold-package-wrap fold-method-package-wrap">
|
>
|
||||||
<div v-if="method.packageLoading" class="package-details-loading">加载中...</div>
|
<ArrowDown />
|
||||||
<template v-else>
|
</el-icon>
|
||||||
<div v-if="getStandaloneMethodPackageDetailsList(method).length === 0" class="package-details-empty">
|
<div class="fold-header-main">
|
||||||
暂无检查方法套餐明细
|
<span class="fold-kicker">检查方法</span>
|
||||||
|
<span
|
||||||
|
class="fold-title fold-title-plain line-clamp-2"
|
||||||
|
:title="getDisplayMethodName(method)"
|
||||||
|
>
|
||||||
|
{{ getDisplayMethodName(method) }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<span
|
||||||
|
v-if="hasStandaloneMethodPackage(method)"
|
||||||
|
class="fold-price-strong warn"
|
||||||
|
>
|
||||||
|
¥{{ formatDetailAmount(method.packagePrice || method.price || 0) }}
|
||||||
|
</span>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
@click.stop="handleRemoveMethod(idx)"
|
||||||
|
>
|
||||||
|
<el-icon><Close /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<!-- 仅当检查方法有套餐时展示明细 -->
|
||||||
|
<div
|
||||||
|
v-if="hasStandaloneMethodPackage(method) && method.expanded"
|
||||||
|
class="fold-strip-body"
|
||||||
|
>
|
||||||
|
<div class="fold-package-wrap fold-method-package-wrap">
|
||||||
|
<div
|
||||||
|
v-if="method.packageLoading"
|
||||||
|
class="package-details-loading"
|
||||||
|
>
|
||||||
|
加载中...
|
||||||
|
</div>
|
||||||
|
<template v-else>
|
||||||
|
<div
|
||||||
|
v-if="getStandaloneMethodPackageDetailsList(method).length === 0"
|
||||||
|
class="package-details-empty"
|
||||||
|
>
|
||||||
|
暂无检查方法套餐明细
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="package-details-list method-package-list"
|
class="package-details-list method-package-list"
|
||||||
@@ -866,46 +909,53 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
<!-- 独立检查方法勾选区:与"已选择"区域解耦,支持分别手动勾选 -->
|
||||||
</div>
|
<div class="method-picker-section">
|
||||||
|
<div
|
||||||
<!-- 独立检查方法勾选区:与"已选择"区域解耦,支持分别手动勾选 -->
|
v-if="methodsForActiveCategory.length > 0"
|
||||||
<div class="method-picker-section">
|
class="selected-global-method-picker"
|
||||||
<div
|
@click.stop
|
||||||
v-if="methodsForActiveCategory.length > 0"
|
>
|
||||||
class="selected-global-method-picker"
|
|
||||||
@click.stop
|
|
||||||
>
|
|
||||||
<div class="method-picker-collapse-title" @click="methodPickerExpanded = !methodPickerExpanded">
|
|
||||||
<span class="method-picker-title-main">检查方法</span>
|
|
||||||
<span v-if="activeCategoryName" class="global-method-picker-scope">{{ activeCategoryName }}</span>
|
|
||||||
<el-icon :class="['method-picker-arrow', { expanded: methodPickerExpanded }]">
|
|
||||||
<ArrowDown />
|
|
||||||
</el-icon>
|
|
||||||
</div>
|
|
||||||
<div v-show="methodPickerExpanded" class="global-method-picker-list">
|
|
||||||
<div
|
<div
|
||||||
v-for="method in methodsForActiveCategory"
|
class="method-picker-collapse-title"
|
||||||
:key="'g-m-' + method.id"
|
@click="methodPickerExpanded = !methodPickerExpanded"
|
||||||
class="item-row method-picker-row"
|
|
||||||
>
|
>
|
||||||
<el-checkbox
|
<span class="method-picker-title-main">检查方法</span>
|
||||||
:model-value="isStandaloneMethodSelected(method)"
|
<span
|
||||||
@change="(val) => onStandaloneMethodChange(!!val, method)"
|
v-if="activeCategoryName"
|
||||||
class="item-checkbox"
|
class="global-method-picker-scope"
|
||||||
|
>{{ activeCategoryName }}</span>
|
||||||
|
<el-icon :class="['method-picker-arrow', { expanded: methodPickerExpanded }]">
|
||||||
|
<ArrowDown />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-show="methodPickerExpanded"
|
||||||
|
class="global-method-picker-list"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="method in methodsForActiveCategory"
|
||||||
|
:key="'g-m-' + method.id"
|
||||||
|
class="item-row method-picker-row"
|
||||||
>
|
>
|
||||||
<span class="method-label-inner">{{ formatExamMethodCaption(method.name) }}</span>
|
<el-checkbox
|
||||||
</el-checkbox>
|
:model-value="isStandaloneMethodSelected(method)"
|
||||||
<span class="item-price">¥{{ formatDetailAmount(method.packagePrice || method.price || 0) }}</span>
|
class="item-checkbox"
|
||||||
|
@change="(val) => onStandaloneMethodChange(!!val, method)"
|
||||||
|
>
|
||||||
|
<span class="method-label-inner">{{ formatExamMethodCaption(method.name) }}</span>
|
||||||
|
</el-checkbox>
|
||||||
|
<span class="item-price">¥{{ formatDetailAmount(method.packagePrice || method.price || 0) }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -913,6 +963,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|||||||
@@ -1352,7 +1352,10 @@
|
|||||||
width="160"
|
width="160"
|
||||||
>
|
>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span v-if="!scope.row.isEdit" style="color: #e6a23c;">
|
<span
|
||||||
|
v-if="!scope.row.isEdit"
|
||||||
|
style="color: #e6a23c;"
|
||||||
|
>
|
||||||
{{ scope.row.reasonText || '-' }}
|
{{ scope.row.reasonText || '-' }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -443,7 +443,7 @@ import { nextTick } from 'vue';
|
|||||||
import { updatePatientInfo } from './components/store/patient.js';
|
import { updatePatientInfo } from './components/store/patient.js';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
// // 监听路由离开事件
|
// // 监听路由离开事件
|
||||||
// onBeforeRouteLeave((to, from, next) => {
|
// onBeforeRouteLeave((to, from, next) => {
|
||||||
@@ -460,6 +460,7 @@ defineOptions({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
// 监听路由参数变化
|
// 监听路由参数变化
|
||||||
watch(
|
watch(
|
||||||
@@ -621,6 +622,21 @@ function getPatientList() {
|
|||||||
active: currentEncounterId.value ? item.encounterId == currentEncounterId.value : false,
|
active: currentEncounterId.value ? item.encounterId == currentEncounterId.value : false,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Bug #626: 从待写病历页面跳转过来时,自动选中对应患者
|
||||||
|
const targetEncounterId = route.query.encounterId;
|
||||||
|
if (targetEncounterId && patientList.value.length > 0) {
|
||||||
|
const targetIndex = patientList.value.findIndex(
|
||||||
|
(item) => String(item.encounterId) === String(targetEncounterId)
|
||||||
|
);
|
||||||
|
if (targetIndex !== -1) {
|
||||||
|
handleCardClick(patientList.value[targetIndex], targetIndex);
|
||||||
|
}
|
||||||
|
// 清除URL参数,避免刷新时重复选中
|
||||||
|
if (route.query.encounterId) {
|
||||||
|
router.replace({ query: {} });
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function setVisitType(type) {
|
function setVisitType(type) {
|
||||||
|
|||||||
@@ -129,23 +129,79 @@
|
|||||||
:total="total"
|
:total="total"
|
||||||
@pagination="getList"
|
@pagination="getList"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 患者详情弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="patientDetailVisible"
|
||||||
|
title="患者详情"
|
||||||
|
width="700px"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<el-descriptions
|
||||||
|
v-if="patientDetailData"
|
||||||
|
:column="2"
|
||||||
|
border
|
||||||
|
>
|
||||||
|
<el-descriptions-item label="患者姓名">
|
||||||
|
{{ patientDetailData.patientName || '-' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="性别">
|
||||||
|
{{ getGenderText(patientDetailData.gender) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="年龄">
|
||||||
|
{{ patientDetailData.age || '-' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="病历号">
|
||||||
|
{{ patientDetailData.busNo || '-' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="身份证号">
|
||||||
|
{{ patientDetailData.idCard || '-' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="联系电话">
|
||||||
|
{{ patientDetailData.phone || '-' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item
|
||||||
|
label="地址"
|
||||||
|
:span="2"
|
||||||
|
>
|
||||||
|
{{ patientDetailData.address || '-' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="挂号时间">
|
||||||
|
{{ parseTime(patientDetailData.registerTime, '{y}-{m}-{d} {h}:{i}:{s}') }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="就诊科室">
|
||||||
|
{{ patientDetailData.organizationName || '-' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="patientDetailVisible = false">
|
||||||
|
关闭
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { useRouter } from 'vue-router'
|
||||||
import { listPendingEmr, getPendingEmrCount } from '@/views/doctorstation/components/api.js'
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { listPendingEmr, getPatientDetails } from '@/views/doctorstation/components/api.js'
|
||||||
import { parseTime } from '@/utils/index.js'
|
import { parseTime } from '@/utils/index.js'
|
||||||
import Pagination from '@/components/Pagination'
|
import Pagination from '@/components/Pagination'
|
||||||
import { Document, Refresh, Search, Delete } from '@element-plus/icons-vue'
|
import { Document, Refresh, Search, Delete } from '@element-plus/icons-vue'
|
||||||
import { ElDivider } from 'element-plus'
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const emrList = ref([])
|
const emrList = ref([])
|
||||||
|
|
||||||
|
// 患者详情弹窗
|
||||||
|
const patientDetailVisible = ref(false)
|
||||||
|
const patientDetailData = ref(null)
|
||||||
|
|
||||||
// 查询参数
|
// 查询参数
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
@@ -208,17 +264,40 @@ const handleRowClick = (row) => {
|
|||||||
console.log('点击行:', row)
|
console.log('点击行:', row)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 写病历
|
// 写病历 - 跳转到医生工作站并自动选中该患者
|
||||||
const handleWriteEmr = (row) => {
|
const handleWriteEmr = (row) => {
|
||||||
console.log('写病历:', row)
|
if (!row.encounterId) {
|
||||||
// 这里可以触发写病历事件
|
ElMessage.error('患者就诊信息不完整,无法写病历')
|
||||||
// 可能需要跳转到病历编辑页面
|
return
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: '/doctorstation/index',
|
||||||
|
query: { encounterId: row.encounterId }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看患者
|
// 查看患者 - 弹窗显示患者详情
|
||||||
const handleViewPatient = (row) => {
|
const handleViewPatient = async (row) => {
|
||||||
console.log('查看患者:', row)
|
if (!row.encounterId) {
|
||||||
// 这里可以触发查看患者事件
|
ElMessage.error('患者就诊信息不完整')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await getPatientDetails(row.encounterId)
|
||||||
|
if (response.code === 200) {
|
||||||
|
patientDetailData.value = response.data || row
|
||||||
|
patientDetailVisible.value = true
|
||||||
|
} else {
|
||||||
|
// 接口失败时使用列表行数据展示
|
||||||
|
patientDetailData.value = row
|
||||||
|
patientDetailVisible.value = true
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取患者详情失败:', error)
|
||||||
|
// 接口异常时使用列表行数据展示
|
||||||
|
patientDetailData.value = row
|
||||||
|
patientDetailVisible.value = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取性别文本
|
// 获取性别文本
|
||||||
|
|||||||
@@ -506,10 +506,21 @@ function getInitOptions() {
|
|||||||
const wardPromise = getPractitionerWard();
|
const wardPromise = getPractitionerWard();
|
||||||
|
|
||||||
Promise.all([orgPromise, wardPromise]).then(([orgRes, wardRes]) => {
|
Promise.all([orgPromise, wardPromise]).then(([orgRes, wardRes]) => {
|
||||||
// 入院科室:展示所有 typeEnum=2(科室) + classEnum含"2"(住院) 的科室
|
const allOrgs = orgRes.data.records.filter(
|
||||||
organization.value = orgRes.data.records.filter(
|
|
||||||
(record) => record.typeEnum === 2 && checkClassEnumValue(record.classEnum, 2)
|
(record) => record.typeEnum === 2 && checkClassEnumValue(record.classEnum, 2)
|
||||||
);
|
);
|
||||||
|
const allWards = wardRes.data || [];
|
||||||
|
|
||||||
|
// 提取所有病区关联的科室ID
|
||||||
|
const linkedOrgIds = new Set();
|
||||||
|
allWards.forEach((ward) => {
|
||||||
|
if (ward.organizationId) {
|
||||||
|
linkedOrgIds.add(ward.organizationId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 过滤出与病区关联过的科室
|
||||||
|
organization.value = allOrgs.filter((org) => linkedOrgIds.has(org.id));
|
||||||
|
|
||||||
// Bug #178 Fix: 如果已选科室不在列表中,手动添加以确保正确显示
|
// Bug #178 Fix: 如果已选科室不在列表中,手动添加以确保正确显示
|
||||||
const selectedOrgId = props.inHospitalInfo?.inHospitalOrgId;
|
const selectedOrgId = props.inHospitalInfo?.inHospitalOrgId;
|
||||||
|
|||||||
@@ -148,6 +148,27 @@ export function saveTcmDiagnosis(data) {
|
|||||||
data: data,
|
data: data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新中医诊断
|
||||||
|
*/
|
||||||
|
export function updateTcmDiagnosis(data) {
|
||||||
|
return request({
|
||||||
|
url: '/doctor-station/chinese-medical/update-tcm-diagnosis',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除中医诊断
|
||||||
|
*/
|
||||||
|
export function deleteTcmDiagnosis(syndromeGroupNo) {
|
||||||
|
return request({
|
||||||
|
url: '/doctor-station/chinese-medical/tcm-diagnosis?syndromeGroupNo=' + syndromeGroupNo,
|
||||||
|
method: 'delete',
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 获取人员慢性病诊断
|
* 获取人员慢性病诊断
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="props.openAddDiagnosisDialog"
|
v-model="dialogVisible"
|
||||||
title="添加中医诊断"
|
:title="isUpdateMode ? '修改中医诊断' : '添加中医诊断'"
|
||||||
width="1500px"
|
width="1500px"
|
||||||
append-to-body
|
append-to-body
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
<div class="search-box">
|
<div class="search-box">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchMiddleDisease"
|
v-model="searchMiddleDisease"
|
||||||
placeholder="搜索疾病名称或编码"
|
placeholder="搜索证候名称或编码"
|
||||||
clearable
|
clearable
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
@@ -131,8 +131,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {getTcmCondition, getTcmSyndrome, saveTcmDiagnosis,} from '@/views/doctorstation/components/api';
|
import { getTcmCondition, getTcmSyndrome, saveTcmDiagnosis, updateTcmDiagnosis, getTcmDiagnosis } from '../api';
|
||||||
import {computed} from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
openAddDiagnosisDialog: {
|
openAddDiagnosisDialog: {
|
||||||
@@ -143,13 +143,17 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
updateZy: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const conditionList = ref([]);
|
const conditionList = ref([]);
|
||||||
const syndromeList = ref([]);
|
const syndromeList = ref([]);
|
||||||
const tcmDiagonsisList = ref([]);
|
const tcmDiagonsisList = ref([]);
|
||||||
const tcmDiagonsisSaveList = ref([]);
|
const tcmDiagonsisSaveList = ref([]);
|
||||||
const syndromeSelected = ref(false); // 当前诊断是否选择对应证候
|
const syndromeSelected = ref(false);
|
||||||
const timestamp = ref('');
|
const timestamp = ref('');
|
||||||
const selectedDisease = ref(false);
|
const selectedDisease = ref(false);
|
||||||
const searchDisease = ref('');
|
const searchDisease = ref('');
|
||||||
@@ -157,35 +161,70 @@ const searchMiddleDisease = ref('');
|
|||||||
const { proxy } = getCurrentInstance();
|
const { proxy } = getCurrentInstance();
|
||||||
const emit = defineEmits(['close']);
|
const emit = defineEmits(['close']);
|
||||||
|
|
||||||
|
const dialogVisible = computed({
|
||||||
|
get: () => props.openAddDiagnosisDialog,
|
||||||
|
set: (val) => {
|
||||||
|
if (!val) {
|
||||||
|
emit('close');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const isUpdateMode = computed(() => {
|
||||||
|
return props.updateZy && props.updateZy.length > 0;
|
||||||
|
});
|
||||||
|
|
||||||
function handleOpen() {
|
function handleOpen() {
|
||||||
getTcmCondition().then((res) => {
|
getTcmCondition().then((res) => {
|
||||||
conditionList.value = res.data.records;
|
conditionList.value = res.data.records;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tcmDiagonsisSaveList.value = [];
|
||||||
|
tcmDiagonsisList.value = [];
|
||||||
|
syndromeSelected.value = true;
|
||||||
|
|
||||||
|
if (isUpdateMode.value) {
|
||||||
|
props.updateZy.forEach((item) => {
|
||||||
|
let updateIds = item.updateId ? item.updateId.split('-') : [];
|
||||||
|
let nameParts = item.name ? item.name.split('-') : [item.name || ''];
|
||||||
|
tcmDiagonsisSaveList.value.push({
|
||||||
|
conditionId: updateIds[0] || '',
|
||||||
|
definitionId: item.illnessDefinitionId || item.definitionId || '',
|
||||||
|
ybNo: item.ybNo,
|
||||||
|
syndromeGroupNo: item.syndromeGroupNo,
|
||||||
|
verificationStatusEnum: item.verificationStatusEnum || 4,
|
||||||
|
medTypeCode: item.medTypeCode,
|
||||||
|
});
|
||||||
|
tcmDiagonsisList.value.push({
|
||||||
|
conditionName: nameParts[0] || '',
|
||||||
|
syndromeName: nameParts[1] || '',
|
||||||
|
syndromeGroupNo: item.syndromeGroupNo,
|
||||||
|
illnessDefinitionId: item.illnessDefinitionId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索诊断
|
|
||||||
const conditionDatas = computed(() => {
|
const conditionDatas = computed(() => {
|
||||||
|
if (!searchDisease.value) {
|
||||||
|
return conditionList.value;
|
||||||
|
}
|
||||||
return conditionList.value.filter((item) => {
|
return conditionList.value.filter((item) => {
|
||||||
if (searchDisease.value) {
|
return item.name.includes(searchDisease.value) || item.ybNo.includes(searchDisease.value);
|
||||||
return searchDisease.value == item.name || searchDisease.value == item.ybNo;
|
|
||||||
}
|
|
||||||
return conditionList;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 后证
|
|
||||||
const syndromeListDatas = computed(() => {
|
const syndromeListDatas = computed(() => {
|
||||||
|
if (!searchMiddleDisease.value) {
|
||||||
|
return syndromeList.value;
|
||||||
|
}
|
||||||
return syndromeList.value.filter((item) => {
|
return syndromeList.value.filter((item) => {
|
||||||
if (searchMiddleDisease.value) {
|
return item.name.includes(searchMiddleDisease.value) || item.ybNo.includes(searchMiddleDisease.value);
|
||||||
return searchMiddleDisease.value == item.name || searchMiddleDisease.value == item.ybNo;
|
|
||||||
}
|
|
||||||
return syndromeList;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 点击诊断列表处理,点击以后才显示证候列表
|
|
||||||
function handleClickRow(row) {
|
function handleClickRow(row) {
|
||||||
if (syndromeSelected.value || tcmDiagonsisList.value == 0) {
|
if (syndromeSelected.value || tcmDiagonsisList.value.length === 0) {
|
||||||
selectedDisease.value = true;
|
selectedDisease.value = true;
|
||||||
syndromeSelected.value = false;
|
syndromeSelected.value = false;
|
||||||
timestamp.value = Date.now();
|
timestamp.value = Date.now();
|
||||||
@@ -197,7 +236,7 @@ function handleClickRow(row) {
|
|||||||
ybNo: row.ybNo,
|
ybNo: row.ybNo,
|
||||||
syndromeGroupNo: timestamp.value,
|
syndromeGroupNo: timestamp.value,
|
||||||
verificationStatusEnum: 4,
|
verificationStatusEnum: 4,
|
||||||
medTypeCode: undefined, // 不设默认值
|
medTypeCode: undefined,
|
||||||
});
|
});
|
||||||
tcmDiagonsisList.value.push({
|
tcmDiagonsisList.value.push({
|
||||||
conditionName: row.name,
|
conditionName: row.name,
|
||||||
@@ -216,7 +255,6 @@ function clickSyndromeRow(row) {
|
|||||||
syndromeSelected.value = true;
|
syndromeSelected.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除诊断
|
|
||||||
function removeDiagnosis(row, index) {
|
function removeDiagnosis(row, index) {
|
||||||
tcmDiagonsisList.value.splice(index, 1);
|
tcmDiagonsisList.value.splice(index, 1);
|
||||||
tcmDiagonsisSaveList.value = tcmDiagonsisSaveList.value.filter((item) => {
|
tcmDiagonsisSaveList.value = tcmDiagonsisSaveList.value.filter((item) => {
|
||||||
@@ -225,77 +263,67 @@ function removeDiagnosis(row, index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
saveTcmDiagnosis({
|
const newDiagnosisList = tcmDiagonsisSaveList.value.filter((item) => !item.conditionId);
|
||||||
patientId: props.patientInfo.patientId,
|
|
||||||
encounterId: props.patientInfo.encounterId,
|
if (isUpdateMode.value) {
|
||||||
diagnosisChildList: tcmDiagonsisSaveList.value,
|
updateTcmDiagnosis({
|
||||||
}).then((res) => {
|
patientId: props.patientInfo.patientId,
|
||||||
if (res.code == 200) {
|
encounterId: props.patientInfo.encounterId,
|
||||||
emit('close');
|
diagnosisChildList: tcmDiagonsisSaveList.value,
|
||||||
proxy.$modal.msgSuccess('诊断已保存');
|
}).then((res) => {
|
||||||
}
|
if (res.code == 200) {
|
||||||
});
|
if (newDiagnosisList.length > 0) {
|
||||||
|
saveTcmDiagnosis({
|
||||||
|
patientId: props.patientInfo.patientId,
|
||||||
|
encounterId: props.patientInfo.encounterId,
|
||||||
|
diagnosisChildList: newDiagnosisList,
|
||||||
|
}).then((res2) => {
|
||||||
|
if (res2.code == 200) {
|
||||||
|
emit('close');
|
||||||
|
proxy.$modal.msgSuccess('诊断已保存');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
emit('close');
|
||||||
|
proxy.$modal.msgSuccess('诊断已保存');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
saveTcmDiagnosis({
|
||||||
|
patientId: props.patientInfo.patientId,
|
||||||
|
encounterId: props.patientInfo.encounterId,
|
||||||
|
diagnosisChildList: tcmDiagonsisSaveList.value,
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.code == 200) {
|
||||||
|
emit('close');
|
||||||
|
proxy.$modal.msgSuccess('诊断已保存');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function submit() {
|
function submit() {
|
||||||
if (tcmDiagonsisSaveList.value.length > 0 && syndromeSelected.value) {
|
const hasNewDiagnosis = tcmDiagonsisSaveList.value.some((item) => !item.conditionId);
|
||||||
|
|
||||||
|
if (!hasNewDiagnosis && isUpdateMode.value) {
|
||||||
|
emit('close');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syndromeSelected.value || tcmDiagonsisSaveList.value.length % 2 === 0) {
|
||||||
save();
|
save();
|
||||||
} else {
|
} else {
|
||||||
proxy.$modal.msgWarning('请选择证候');
|
proxy.$modal.msgWarning('请选择证候');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
emit('close');
|
emit('close');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
:deep(.pagination-container .el-pagination) {
|
|
||||||
right: 20px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-container {
|
|
||||||
max-width: 1400px;
|
|
||||||
margin: 20px auto;
|
|
||||||
padding: 20px;
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding-bottom: 15px;
|
|
||||||
border-bottom: 1px solid var(--el-border-color);
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header h1 {
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.patient-info {
|
|
||||||
background: var(--el-color-primary-light-9);
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.patient-info .info-row {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.patient-info .info-label {
|
|
||||||
width: 100px;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content {
|
.main-content {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 1.2fr;
|
grid-template-columns: 1fr 1fr 1.2fr;
|
||||||
@@ -322,125 +350,13 @@ function close() {
|
|||||||
border-bottom: 1px solid var(--el-border-color);
|
border-bottom: 1px solid var(--el-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.disease-list {
|
|
||||||
max-height: 400px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disease-item {
|
|
||||||
padding: 12px 15px;
|
|
||||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disease-item:hover {
|
|
||||||
background-color: var(--el-color-primary-light-9);
|
|
||||||
}
|
|
||||||
|
|
||||||
.disease-item.active {
|
|
||||||
background-color: var(--el-color-primary-light-8);
|
|
||||||
border-left: 3px solid var(--el-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.disease-name {
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disease-code {
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box {
|
.search-box {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.disease-categories {
|
.diagnosis-list {
|
||||||
display: flex;
|
max-height: 520px;
|
||||||
flex-wrap: wrap;
|
overflow-y: auto;
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-tag {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 5px 12px;
|
|
||||||
border-radius: 15px;
|
|
||||||
background: var(--el-fill-color-light);
|
|
||||||
font-size: 13px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-tag.active {
|
|
||||||
background: var(--el-color-primary);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.relation-container {
|
|
||||||
text-align: center;
|
|
||||||
padding: 30px 0;
|
|
||||||
border: 2px dashed var(--el-border-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
margin: 20px 0;
|
|
||||||
background: var(--el-fill-color-lighter);
|
|
||||||
}
|
|
||||||
|
|
||||||
.relation-icon {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.relation-text {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--el-text-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.syndrome-details {
|
|
||||||
padding: 15px;
|
|
||||||
background: var(--el-color-primary-light-9);
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid var(--el-color-primary-light-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-item {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-label {
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
margin-bottom: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 15px;
|
|
||||||
padding-top: 20px;
|
|
||||||
border-top: 1px solid var(--el-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state {
|
|
||||||
text-align: center;
|
|
||||||
padding: 40px 0;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.diagnosis-history {
|
|
||||||
margin-top: 20px;
|
|
||||||
border-top: 1px solid var(--el-border-color);
|
|
||||||
padding-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
color: var(--el-text-color-primary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-item {
|
.history-item {
|
||||||
@@ -451,17 +367,6 @@ function close() {
|
|||||||
border-radius: 0 4px 4px 0;
|
border-radius: 0 4px 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.diagnosis-list {
|
|
||||||
max-height: 520px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-date {
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-diagnosis {
|
.history-diagnosis {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -469,16 +374,9 @@ function close() {
|
|||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-note {
|
.empty-state {
|
||||||
font-size: 13px;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
padding-top: 5px;
|
|
||||||
border-top: 1px dashed var(--el-border-color);
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-list {
|
|
||||||
padding: 20px 0;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,14 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="visible"
|
v-model="visible"
|
||||||
top="6vh"
|
|
||||||
:width="width"
|
|
||||||
title="中医诊断"
|
title="中医诊断"
|
||||||
|
:width="width"
|
||||||
:z-index="20"
|
:z-index="20"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
@open="openAct"
|
@open="openAct"
|
||||||
@closed="closedAct"
|
@closed="closedAct"
|
||||||
>
|
>
|
||||||
中医诊断
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="100px"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="中医诊断"
|
||||||
|
prop="conditionCode"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="formData.conditionCode"
|
||||||
|
placeholder="请选择中医诊断"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleConditionChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in conditionOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="中医证候"
|
||||||
|
prop="syndromeCode"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="formData.syndromeCode"
|
||||||
|
placeholder="请选择中医证候"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in syndromeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button
|
<el-button
|
||||||
size="fixed"
|
size="fixed"
|
||||||
@@ -20,122 +66,120 @@
|
|||||||
<el-button
|
<el-button
|
||||||
size="fixed"
|
size="fixed"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handleSubmit(signFormRef)"
|
@click="handleSubmit"
|
||||||
>
|
>
|
||||||
保存
|
保存
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import {onMounted, reactive, ref} from 'vue'
|
import {onMounted, reactive, ref} from 'vue'
|
||||||
import {dayjs} from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
// import { IInPatient } from '@/model/IInPatient'
|
import { getTcmCondition, getTcmSyndrome, saveTcmDiagnosis } from '../api'
|
||||||
|
|
||||||
const currentInPatient = ref({})
|
const { proxy } = getCurrentInstance()
|
||||||
const initCurrentInPatient = () => {
|
|
||||||
currentInPatient.value = {
|
|
||||||
feeType: '08',
|
|
||||||
sexName: '男',
|
|
||||||
age: '0',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* 初始化数据 */
|
|
||||||
const init = () => {
|
|
||||||
initCurrentInPatient()
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 入科 */
|
const conditionOptions = ref([])
|
||||||
const signForm = ref({
|
const syndromeOptions = ref([])
|
||||||
visitCode: '', // 就诊流水号
|
|
||||||
height: 0, // 身高
|
const formData = ref({
|
||||||
weight: 0, // 体重
|
conditionCode: '',
|
||||||
temperature: 0, // 体温
|
syndromeCode: '',
|
||||||
hertRate: 0, // 心率
|
|
||||||
pulse: 0, // 脉搏
|
|
||||||
highBloodPressure: 0, // 收缩压
|
|
||||||
endBloodPressure: 0, // 舒张压
|
|
||||||
loginDeptCode: '', // 当前登录科室
|
|
||||||
bingqing: '', //患者病情
|
|
||||||
inDeptDate: dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss'), //入院时间
|
|
||||||
signsId: '',
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
admittedDoctor: [{ required: true, message: '请选择住院医生', trigger: ['blur', 'change'] }],
|
conditionCode: [{ required: true, message: '请选择中医诊断', trigger: ['blur', 'change'] }],
|
||||||
masterNurse: [{ required: true, message: '请选择责任护士', trigger: ['blur', 'change'] }],
|
syndromeCode: [{ required: true, message: '请选择中医证候', trigger: ['blur', 'change'] }],
|
||||||
})
|
})
|
||||||
const printWristband = ref(false)
|
|
||||||
const emits = defineEmits(['okAct'])
|
|
||||||
|
|
||||||
const visible = defineModel('visible')
|
const props = defineProps({
|
||||||
const width = '920px'
|
patientInfo: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['ok-act'])
|
||||||
|
|
||||||
|
const visible = defineModel<boolean>('visible')
|
||||||
|
const width = '500px'
|
||||||
|
|
||||||
/* 取消 */
|
|
||||||
const cancelAct = () => {
|
const cancelAct = () => {
|
||||||
visible.value = false
|
visible.value = false
|
||||||
}
|
}
|
||||||
/* 录入患者体征*/
|
|
||||||
const signFormRef = ref()
|
function handleConditionChange() {
|
||||||
const handleSubmit = async (formEl) => {
|
formData.value.syndromeCode = ''
|
||||||
if (!formEl) return
|
loadSyndromeOptions(formData.value.conditionCode)
|
||||||
await formEl.validate((valid, fields) => {
|
}
|
||||||
if (valid) {
|
|
||||||
console.log('submit!')
|
function loadConditionOptions() {
|
||||||
try {
|
getTcmCondition().then((res) => {
|
||||||
// 录入患者体征方法(signForm.value).then((res: any) => {
|
if (res.data && res.data.records) {
|
||||||
// ElMessage({
|
conditionOptions.value = res.data.records.map((item) => ({
|
||||||
// message: '登记成功!',
|
value: item.ybNo,
|
||||||
// type: 'success',
|
label: item.name,
|
||||||
// grouping: true,
|
}))
|
||||||
// showClose: true,
|
|
||||||
// })
|
|
||||||
// emits('okAct')
|
|
||||||
// })
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadSyndromeOptions(conditionCode) {
|
||||||
|
const params = conditionCode ? { conditionCode } : {}
|
||||||
|
getTcmSyndrome(params).then((res) => {
|
||||||
|
if (res.data && res.data.records) {
|
||||||
|
syndromeOptions.value = res.data.records.map((item) => ({
|
||||||
|
value: item.ybNo,
|
||||||
|
label: item.name,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const formRef = ref()
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
await formRef.value.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
const submitData = {
|
||||||
|
conditionCode: formData.value.conditionCode,
|
||||||
|
syndromeCode: formData.value.syndromeCode,
|
||||||
|
}
|
||||||
|
if (props.patientInfo && props.patientInfo.patientId) {
|
||||||
|
submitData.patientId = props.patientInfo.patientId
|
||||||
|
submitData.encounterId = props.patientInfo.encounterId
|
||||||
|
}
|
||||||
|
submitData.diagnosisChildList = [{
|
||||||
|
conditionCode: formData.value.conditionCode,
|
||||||
|
syndromeCode: formData.value.syndromeCode,
|
||||||
|
}]
|
||||||
|
saveTcmDiagnosis(submitData).then((res) => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
ElMessage.success('中医诊断保存成功')
|
||||||
|
emit('ok-act')
|
||||||
|
cancelAct()
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg || '保存失败')
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
ElMessage.error('保存失败,请重试')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const openAct = () => {
|
const openAct = () => {
|
||||||
init()
|
formData.value = { conditionCode: '', syndromeCode: '' }
|
||||||
|
loadConditionOptions()
|
||||||
|
loadSyndromeOptions()
|
||||||
}
|
}
|
||||||
const closedAct = () => {
|
const closedAct = () => {
|
||||||
visible.value = false
|
visible.value = false
|
||||||
}
|
}
|
||||||
onMounted(() => {})
|
onMounted(() => {
|
||||||
|
loadConditionOptions()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.transferIn-container {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.admission-signs,
|
|
||||||
.admission-information {
|
|
||||||
width: 888px;
|
|
||||||
.unit {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 10px;
|
|
||||||
color: #bbb;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 14px;
|
|
||||||
font-family: '思源黑体 CN';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-wriBtn {
|
|
||||||
margin-left: 565px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w-p100 {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w-80 {
|
|
||||||
width: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mb-90 {
|
|
||||||
margin-bottom: 90px !important;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -46,11 +46,6 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<el-popconfirm
|
<el-popconfirm
|
||||||
v-if="
|
|
||||||
node.level === 2 &&
|
|
||||||
node.parent.data.name != '常用' &&
|
|
||||||
node.parent.data.name != '历史'
|
|
||||||
"
|
|
||||||
width="200"
|
width="200"
|
||||||
:hide-after="10"
|
:hide-after="10"
|
||||||
title="确认删除此常用诊断吗"
|
title="确认删除此常用诊断吗"
|
||||||
@@ -59,6 +54,11 @@
|
|||||||
>
|
>
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-button
|
<el-button
|
||||||
|
v-if="
|
||||||
|
node.level === 2 &&
|
||||||
|
node.parent.data.name != '常用' &&
|
||||||
|
node.parent.data.name != '历史'
|
||||||
|
"
|
||||||
style="color: #000000"
|
style="color: #000000"
|
||||||
type="text"
|
type="text"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
>
|
>
|
||||||
保存诊断
|
保存诊断
|
||||||
</el-button>
|
</el-button>
|
||||||
<!-- <el-button type="primary" plain @click="handleAddTcmDiagonsis()"> 中医诊断 </el-button> -->
|
<el-button type="primary" plain @click="handleAddTcmDiagonsis()"> 中医诊断 </el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
plain
|
plain
|
||||||
@@ -142,6 +142,34 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="诊断体系"
|
||||||
|
align="center"
|
||||||
|
prop="diagnosisSystem"
|
||||||
|
width="120"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-form-item
|
||||||
|
:prop="`diagnosisList.${scope.$index}.diagnosisSystem`"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="scope.row.diagnosisSystem"
|
||||||
|
placeholder=" "
|
||||||
|
style="width: 100px"
|
||||||
|
@change="(value) => handleDiagnosisSystemChange(scope.row, value)"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
label="西医"
|
||||||
|
value="西医"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="中医"
|
||||||
|
value="中医"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="诊断类别"
|
label="诊断类别"
|
||||||
align="center"
|
align="center"
|
||||||
@@ -159,7 +187,7 @@
|
|||||||
style="width: 150px"
|
style="width: 150px"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in med_type"
|
v-for="item in diag_type"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
@@ -187,6 +215,7 @@
|
|||||||
>
|
>
|
||||||
<diagnosislist
|
<diagnosislist
|
||||||
:diagnosis-searchkey="diagnosisSearchkey"
|
:diagnosis-searchkey="diagnosisSearchkey"
|
||||||
|
:diagnosis-system="scope.row.diagnosisSystem"
|
||||||
@select-diagnosis="handleSelsectDiagnosis"
|
@select-diagnosis="handleSelsectDiagnosis"
|
||||||
/>
|
/>
|
||||||
<template #reference>
|
<template #reference>
|
||||||
@@ -209,6 +238,35 @@
|
|||||||
prop="diagnosisDoctor"
|
prop="diagnosisDoctor"
|
||||||
width="120"
|
width="120"
|
||||||
/>
|
/>
|
||||||
|
<el-table-column
|
||||||
|
label="中医证候"
|
||||||
|
align="center"
|
||||||
|
prop="tcmSyndromeName"
|
||||||
|
width="150"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<template v-if="scope.row.diagnosisSystem === '中医'">
|
||||||
|
<span style="color: #f56c6c; margin-right: 2px;">*</span><el-select
|
||||||
|
v-model="scope.row.tcmSyndromeCode"
|
||||||
|
placeholder="请选择证候"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
style="width: 130px"
|
||||||
|
@change="(value) => handleTcmSyndromeChange(scope.row, value)"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in tcmSyndromeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span style="color: #c0c4cc;">-</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="诊断时间"
|
label="诊断时间"
|
||||||
align="center"
|
align="center"
|
||||||
@@ -281,6 +339,7 @@
|
|||||||
<AddDiagnosisDialog
|
<AddDiagnosisDialog
|
||||||
:open-add-diagnosis-dialog="openAddDiagnosisDialog"
|
:open-add-diagnosis-dialog="openAddDiagnosisDialog"
|
||||||
:patient-info="props.patientInfo"
|
:patient-info="props.patientInfo"
|
||||||
|
:update-zy="tcmDiagnosisListForEdit"
|
||||||
@close="closeDiagnosisDialog"
|
@close="closeDiagnosisDialog"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -298,10 +357,13 @@ import {
|
|||||||
getEmrDetail,
|
getEmrDetail,
|
||||||
getEncounterDiagnosis,
|
getEncounterDiagnosis,
|
||||||
getTcmDiagnosis,
|
getTcmDiagnosis,
|
||||||
|
getTcmSyndrome,
|
||||||
isFoodDiseasesNew,
|
isFoodDiseasesNew,
|
||||||
saveDiagnosis,
|
saveDiagnosis,
|
||||||
|
saveTcmDiagnosis,
|
||||||
|
deleteTcmDiagnosis,
|
||||||
} from '../api';
|
} from '../api';
|
||||||
import {deleteTcmDiagnosis} from '@/views/doctorstation/components/api.js';
|
|
||||||
import diagnosisdialog from '../diagnosis/diagnosisdialog.vue';
|
import diagnosisdialog from '../diagnosis/diagnosisdialog.vue';
|
||||||
import AddDiagnosisDialog from './addDiagnosisDialog.vue';
|
import AddDiagnosisDialog from './addDiagnosisDialog.vue';
|
||||||
import diagnosislist from '../diagnosis/diagnosislist.vue';
|
import diagnosislist from '../diagnosis/diagnosislist.vue';
|
||||||
@@ -315,6 +377,7 @@ const openDiagnosis = ref(false);
|
|||||||
const openAddDiagnosisDialog = ref(false);
|
const openAddDiagnosisDialog = ref(false);
|
||||||
const diagnosisSearchkey = ref('');
|
const diagnosisSearchkey = ref('');
|
||||||
const diagnosisOptions = ref([]);
|
const diagnosisOptions = ref([]);
|
||||||
|
const tcmSyndromeOptions = ref([]);
|
||||||
const rowIndex = ref();
|
const rowIndex = ref();
|
||||||
const diagnosis = ref();
|
const diagnosis = ref();
|
||||||
const orgOrUser = ref();
|
const orgOrUser = ref();
|
||||||
@@ -331,13 +394,15 @@ const props = defineProps({
|
|||||||
const emits = defineEmits(['diagnosisSave']);
|
const emits = defineEmits(['diagnosisSave']);
|
||||||
const { proxy } = getCurrentInstance();
|
const { proxy } = getCurrentInstance();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const { med_type } = proxy.useDict('med_type');
|
// 获取诊断类型字典(住院诊断类别)
|
||||||
|
const { diag_type } = proxy.useDict('diag_type');
|
||||||
const rules = ref({
|
const rules = ref({
|
||||||
name: [{ required: true, message: '请选择诊断', trigger: 'change' }],
|
name: [{ required: true, message: '请选择诊断', trigger: 'change' }],
|
||||||
medTypeCode: [{ required: true, message: '请选择诊断类型', trigger: 'change' }],
|
medTypeCode: [{ required: true, message: '请选择诊断类型', trigger: 'change' }],
|
||||||
diagSrtNo: [{ required: true, message: '请输入诊断序号', trigger: 'change' }],
|
diagSrtNo: [{ required: true, message: '请输入诊断序号', trigger: 'change' }],
|
||||||
});
|
});
|
||||||
const diagnosisNetDatas = ref([]);
|
const diagnosisNetDatas = ref([]);
|
||||||
|
const tcmDiagnosisListForEdit = ref([]);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => form.value.diagnosisList,
|
() => form.value.diagnosisList,
|
||||||
@@ -404,7 +469,11 @@ function getList() {
|
|||||||
...item,
|
...item,
|
||||||
};
|
};
|
||||||
if (obj.diagSrtNo == null) {
|
if (obj.diagSrtNo == null) {
|
||||||
obj.diagSrtNo = 1;
|
obj.diagSrtNo = '1';
|
||||||
|
}
|
||||||
|
// 确保 diagnosisSystem 字段存在,默认为西医
|
||||||
|
if (!obj.diagnosisSystem) {
|
||||||
|
obj.diagnosisSystem = '西医';
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
});
|
});
|
||||||
@@ -421,9 +490,12 @@ function getList() {
|
|||||||
diagnosisNetDatas.value = res.data.illness;
|
diagnosisNetDatas.value = res.data.illness;
|
||||||
res.data.illness.forEach((item, index) => {
|
res.data.illness.forEach((item, index) => {
|
||||||
newList.push({
|
newList.push({
|
||||||
name: item.name + '-' + (res.data.symptom[index]?.name || ''),
|
name: item.name,
|
||||||
ybNo: item.ybNo,
|
ybNo: item.ybNo,
|
||||||
medTypeCode: item.medTypeCode,
|
medTypeCode: item.medTypeCode,
|
||||||
|
diagnosisSystem: '中医',
|
||||||
|
tcmSyndromeCode: res.data.symptom[index]?.ybNo || '',
|
||||||
|
tcmSyndromeName: res.data.symptom[index]?.name || '',
|
||||||
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
|
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
|
||||||
diagnosisTime: new Date().toLocaleString('zh-CN')
|
diagnosisTime: new Date().toLocaleString('zh-CN')
|
||||||
});
|
});
|
||||||
@@ -448,6 +520,7 @@ function getList() {
|
|||||||
|
|
||||||
init();
|
init();
|
||||||
function init() {
|
function init() {
|
||||||
|
loadTcmSyndromeOptions();
|
||||||
diagnosisInit().then((res) => {
|
diagnosisInit().then((res) => {
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
diagnosisOptions.value = res.data.verificationStatusOptions;
|
diagnosisOptions.value = res.data.verificationStatusOptions;
|
||||||
@@ -595,6 +668,7 @@ function addDiagnosisItem() {
|
|||||||
name: undefined,
|
name: undefined,
|
||||||
verificationStatusEnum: 4,
|
verificationStatusEnum: 4,
|
||||||
medTypeCode: undefined,
|
medTypeCode: undefined,
|
||||||
|
diagnosisSystem: '西医',
|
||||||
diagSrtNo: form.value.diagnosisList.length + 1,
|
diagSrtNo: form.value.diagnosisList.length + 1,
|
||||||
iptDiseTypeCode: 2,
|
iptDiseTypeCode: 2,
|
||||||
diagnosisDesc: '',
|
diagnosisDesc: '',
|
||||||
@@ -612,6 +686,13 @@ function addDiagnosisItem() {
|
|||||||
|
|
||||||
// 添加中医诊断
|
// 添加中医诊断
|
||||||
function handleAddTcmDiagonsis() {
|
function handleAddTcmDiagonsis() {
|
||||||
|
tcmDiagnosisListForEdit.value = form.value.diagnosisList.filter(
|
||||||
|
(item) => item.diagnosisSystem === '中医'
|
||||||
|
).map((item) => ({
|
||||||
|
...item,
|
||||||
|
updateId: item.conditionId ? `${item.conditionId}-${item.syndromeGroupNo || ''}` : '' ,
|
||||||
|
illnessDefinitionId: item.definitionId || '' ,
|
||||||
|
}));
|
||||||
openAddDiagnosisDialog.value = true;
|
openAddDiagnosisDialog.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,6 +756,54 @@ function handleMaindise(value, index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 诊断体系变化处理
|
||||||
|
*/
|
||||||
|
function handleDiagnosisSystemChange(row, value) {
|
||||||
|
// 切换诊断体系时,清空诊断名称及相关字段,避免中西医数据混淆
|
||||||
|
row.name = '';
|
||||||
|
row.ybNo = '';
|
||||||
|
row.definitionId = '';
|
||||||
|
row.showPopover = false;
|
||||||
|
|
||||||
|
if (value === '西医') {
|
||||||
|
row.tcmSyndromeCode = '';
|
||||||
|
row.tcmSyndromeName = '';
|
||||||
|
}
|
||||||
|
if (value === '中医') {
|
||||||
|
row.tcmSyndromeCode = '';
|
||||||
|
row.tcmSyndromeName = '';
|
||||||
|
loadTcmSyndromeOptions(row.definitionId || '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载中医证候选项
|
||||||
|
*/
|
||||||
|
function loadTcmSyndromeOptions(conditionCode = '') {
|
||||||
|
const params = {};
|
||||||
|
if (conditionCode) {
|
||||||
|
params.conditionCode = conditionCode;
|
||||||
|
}
|
||||||
|
getTcmSyndrome(params).then((res) => {
|
||||||
|
if (res.code == 200 && res.data && res.data.records) {
|
||||||
|
tcmSyndromeOptions.value = res.data.records.map((item) => ({
|
||||||
|
value: item.ybNo,
|
||||||
|
label: item.name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 中医证候变化处理
|
||||||
|
*/
|
||||||
|
function handleTcmSyndromeChange(row, value) {
|
||||||
|
// 找到对应的证候名称
|
||||||
|
const syndrome = tcmSyndromeOptions.value.find(item => item.value === value);
|
||||||
|
row.tcmSyndromeName = syndrome ? syndrome.label : '';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存诊断
|
* 保存诊断
|
||||||
*/
|
*/
|
||||||
@@ -687,8 +816,79 @@ function handleMaindise(value, index) {
|
|||||||
/**
|
/**
|
||||||
* 保存诊断
|
* 保存诊断
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
function continueSave() {
|
||||||
|
isSaving.value = true;
|
||||||
|
|
||||||
|
const sortedList = [...form.value.diagnosisList].sort((a, b) => {
|
||||||
|
const aNo = typeof a.diagSrtNo === 'number' ? a.diagSrtNo : 9999;
|
||||||
|
const bNo = typeof b.diagSrtNo === 'number' ? b.diagSrtNo : 9999;
|
||||||
|
return aNo - bNo;
|
||||||
|
});
|
||||||
|
|
||||||
|
sortedList.forEach((item, index) => {
|
||||||
|
item.diagSrtNo = index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
const westernList = sortedList.filter(item => item.diagnosisSystem !== '中医');
|
||||||
|
const tcmList = sortedList.filter(item => item.diagnosisSystem === '中医');
|
||||||
|
|
||||||
|
const savePromises = [];
|
||||||
|
|
||||||
|
if (westernList.length > 0) {
|
||||||
|
savePromises.push(
|
||||||
|
saveDiagnosis({
|
||||||
|
patientId: props.patientInfo.patientId,
|
||||||
|
encounterId: props.patientInfo.encounterId,
|
||||||
|
diagnosisChildList: westernList,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tcmList.length > 0) {
|
||||||
|
const syndromeGroupNo = String(Date.now());
|
||||||
|
const tcmSaveList = tcmList.map((item) => ({
|
||||||
|
definitionId: item.definitionId || '',
|
||||||
|
ybNo: item.ybNo,
|
||||||
|
syndromeGroupNo: item.syndromeGroupNo || syndromeGroupNo,
|
||||||
|
verificationStatusEnum: item.verificationStatusEnum || 4,
|
||||||
|
medTypeCode: item.medTypeCode || undefined,
|
||||||
|
tcmSyndromeCode: item.tcmSyndromeCode || '',
|
||||||
|
tcmSyndromeName: item.tcmSyndromeName || '',
|
||||||
|
}));
|
||||||
|
savePromises.push(
|
||||||
|
saveTcmDiagnosis({
|
||||||
|
patientId: props.patientInfo.patientId,
|
||||||
|
encounterId: props.patientInfo.encounterId,
|
||||||
|
diagnosisChildList: tcmSaveList,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(savePromises).then((results) => {
|
||||||
|
const allSuccess = results.every(res => res.code === 200 || res.code == 200);
|
||||||
|
if (allSuccess) {
|
||||||
|
emits('diagnosisSave', false);
|
||||||
|
proxy.$modal.msgSuccess('诊断已保存');
|
||||||
|
getList();
|
||||||
|
isFoodDiseasesNew({ encounterId: props.patientInfo.encounterId }).then((res2) => {
|
||||||
|
if (res2.code === 20 && res2.data) {
|
||||||
|
window.open(res2.data, '_blank');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
proxy.$modal.msgWarning('部分诊断保存失败');
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
proxy.$modal.msgError('诊断保存失败,请重试');
|
||||||
|
}).finally(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
isSaving.value = false;
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
function handleSaveDiagnosis() {
|
function handleSaveDiagnosis() {
|
||||||
// 防止重复点击保存
|
|
||||||
if (isSaving.value) {
|
if (isSaving.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -713,41 +913,80 @@ function handleSaveDiagnosis() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置保存标志,避免触发watch监听器
|
// 中医诊断完整性校验
|
||||||
|
const incompleteTcmDiagnosis = form.value.diagnosisList.find(
|
||||||
|
(diagnosis) => diagnosis.diagnosisSystem === '中医' && !diagnosis.tcmSyndromeCode
|
||||||
|
);
|
||||||
|
if (incompleteTcmDiagnosis) {
|
||||||
|
ElMessage.error('中医诊断不完整,请录入对应的证候!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
isSaving.value = true;
|
isSaving.value = true;
|
||||||
|
|
||||||
// 步骤1:深拷贝并按 diagSrtNo 排序
|
|
||||||
const sortedList = [...form.value.diagnosisList].sort((a, b) => {
|
const sortedList = [...form.value.diagnosisList].sort((a, b) => {
|
||||||
const aNo = typeof a.diagSrtNo === 'number' ? a.diagSrtNo : 9999;
|
const aNo = typeof a.diagSrtNo === 'number' ? a.diagSrtNo : 9999;
|
||||||
const bNo = typeof b.diagSrtNo === 'number' ? b.diagSrtNo : 9999;
|
const bNo = typeof b.diagSrtNo === 'number' ? b.diagSrtNo : 9999;
|
||||||
return aNo - bNo;
|
return aNo - bNo;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 步骤2:重新分配连续的序号(从1开始)
|
|
||||||
sortedList.forEach((item, index) => {
|
sortedList.forEach((item, index) => {
|
||||||
item.diagSrtNo = index + 1; // 这里是关键!把”诊断排序”改成新顺序
|
item.diagSrtNo = index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 步骤3:提交排序后的数据
|
// 分离西医和中医诊断,分别调用对应接口保存
|
||||||
saveDiagnosis({
|
const westernList = sortedList.filter(item => item.diagnosisSystem !== '中医');
|
||||||
patientId: props.patientInfo.patientId,
|
const tcmList = sortedList.filter(item => item.diagnosisSystem === '中医');
|
||||||
encounterId: props.patientInfo.encounterId,
|
|
||||||
diagnosisChildList: sortedList,
|
const savePromises = [];
|
||||||
}).then((res) => {
|
|
||||||
if (res.code === 200) {
|
if (westernList.length > 0) {
|
||||||
|
savePromises.push(
|
||||||
|
saveDiagnosis({
|
||||||
|
patientId: props.patientInfo.patientId,
|
||||||
|
encounterId: props.patientInfo.encounterId,
|
||||||
|
diagnosisChildList: westernList,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tcmList.length > 0) {
|
||||||
|
const syndromeGroupNo = String(Date.now());
|
||||||
|
const tcmSaveList = tcmList.map((item) => ({
|
||||||
|
definitionId: item.definitionId || '',
|
||||||
|
ybNo: item.ybNo,
|
||||||
|
syndromeGroupNo: item.syndromeGroupNo || syndromeGroupNo,
|
||||||
|
verificationStatusEnum: item.verificationStatusEnum || 4,
|
||||||
|
medTypeCode: item.medTypeCode || undefined,
|
||||||
|
tcmSyndromeCode: item.tcmSyndromeCode || '',
|
||||||
|
tcmSyndromeName: item.tcmSyndromeName || '',
|
||||||
|
}));
|
||||||
|
savePromises.push(
|
||||||
|
saveTcmDiagnosis({
|
||||||
|
patientId: props.patientInfo.patientId,
|
||||||
|
encounterId: props.patientInfo.encounterId,
|
||||||
|
diagnosisChildList: tcmSaveList,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(savePromises).then((results) => {
|
||||||
|
const allSuccess = results.every(res => res.code === 200 || res.code == 200);
|
||||||
|
if (allSuccess) {
|
||||||
emits('diagnosisSave', false);
|
emits('diagnosisSave', false);
|
||||||
proxy.$modal.msgSuccess('诊断已保存');
|
proxy.$modal.msgSuccess('诊断已保存');
|
||||||
|
|
||||||
// 保存成功后从服务器重新加载数据,确保前后端数据一致
|
|
||||||
getList();
|
getList();
|
||||||
|
|
||||||
// 食源性疾病逻辑
|
|
||||||
isFoodDiseasesNew({ encounterId: props.patientInfo.encounterId }).then((res2) => {
|
isFoodDiseasesNew({ encounterId: props.patientInfo.encounterId }).then((res2) => {
|
||||||
if (res2.code === 20 && res2.data) {
|
if (res2.code === 20 && res2.data) {
|
||||||
window.open(res2.data, '_blank');
|
window.open(res2.data, '_blank');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
proxy.$modal.msgWarning('部分诊断保存失败');
|
||||||
|
getList();
|
||||||
}
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
proxy.$modal.msgError('诊断保存失败,请重试');
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
isSaving.value = false;
|
isSaving.value = false;
|
||||||
@@ -779,19 +1018,27 @@ function handleChange(value) {
|
|||||||
* 选择诊断并赋值到列表
|
* 选择诊断并赋值到列表
|
||||||
*/
|
*/
|
||||||
function handleSelsectDiagnosis(row) {
|
function handleSelsectDiagnosis(row) {
|
||||||
console.log(row);
|
const currentItem = form.value.diagnosisList[rowIndex.value];
|
||||||
form.value.diagnosisList[rowIndex.value].ybNo = row.ybNo;
|
currentItem.ybNo = row.ybNo;
|
||||||
form.value.diagnosisList[rowIndex.value].name = row.name;
|
currentItem.name = row.name;
|
||||||
form.value.diagnosisList[rowIndex.value].definitionId = row.id;
|
currentItem.definitionId = row.id;
|
||||||
|
currentItem.showPopover = false;
|
||||||
|
|
||||||
|
// 如果是中医诊断,自动加载对应的证候
|
||||||
|
if (currentItem.diagnosisSystem === '中医') {
|
||||||
|
loadTcmSyndromeOptions(row.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/**获取焦点时 打开列表 */
|
/**获取焦点时 打开列表 */
|
||||||
function handleFocus(row, index) {
|
function handleFocus(row, index) {
|
||||||
rowIndex.value = index;
|
rowIndex.value = index;
|
||||||
row.showPopover = true;
|
row.showPopover = true;
|
||||||
}
|
}
|
||||||
/**失去焦点时 关闭列表 */
|
/**失去焦点时 延迟关闭列表(避免点击列表项时过早关闭) */
|
||||||
function handleBlur(row) {
|
function handleBlur(row) {
|
||||||
row.showPopover = false;
|
setTimeout(() => {
|
||||||
|
row.showPopover = false;
|
||||||
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNodeClick(data) {
|
function handleNodeClick(data) {
|
||||||
@@ -817,6 +1064,7 @@ function handleNodeClick(data) {
|
|||||||
name: data.name,
|
name: data.name,
|
||||||
verificationStatusEnum: 4,
|
verificationStatusEnum: 4,
|
||||||
medTypeCode: undefined,
|
medTypeCode: undefined,
|
||||||
|
diagnosisSystem: '西医',
|
||||||
diagSrtNo: form.value.diagnosisList.length + 1,
|
diagSrtNo: form.value.diagnosisList.length + 1,
|
||||||
definitionId: data.definitionId,
|
definitionId: data.definitionId,
|
||||||
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
|
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
|
||||||
|
|||||||
@@ -35,13 +35,17 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
diagnosisSystem: {
|
||||||
|
type: String,
|
||||||
|
default: '西医',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['selectDiagnosis']);
|
const emit = defineEmits(['selectDiagnosis']);
|
||||||
const total = ref(0);
|
const total = ref(0);
|
||||||
const queryParams = ref({
|
const queryParams = ref({
|
||||||
pageSize: 1000,
|
pageSize: 1000,
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
// typeCode: 1,
|
typeCode: props.diagnosisSystem === '中医' ? '2' : '1',
|
||||||
});
|
});
|
||||||
const diagnosisDefinitionList = ref([]);
|
const diagnosisDefinitionList = ref([]);
|
||||||
|
|
||||||
@@ -51,7 +55,14 @@ watch(
|
|||||||
queryParams.value.searchKey = newValue;
|
queryParams.value.searchKey = newValue;
|
||||||
getList();
|
getList();
|
||||||
},
|
},
|
||||||
{ immdiate: true }
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.diagnosisSystem,
|
||||||
|
(newValue) => {
|
||||||
|
queryParams.value.typeCode = newValue === '中医' ? '2' : '1';
|
||||||
|
getList();
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
getList();
|
getList();
|
||||||
@@ -68,4 +79,4 @@ function clickRow(row) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -294,9 +294,6 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<el-form-item label="备注:" prop="remark">
|
|
||||||
<el-input v-model="row.remark" maxlength="50" placeholder="最多50字" style="width: 200px" />
|
|
||||||
</el-form-item>
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<el-button type="primary" @click="handleSave">确定</el-button>
|
<el-button type="primary" @click="handleSave">确定</el-button>
|
||||||
<el-button @click="handleCancel">取消</el-button>
|
<el-button @click="handleCancel">取消</el-button>
|
||||||
@@ -500,9 +497,6 @@
|
|||||||
总金额:{{ row.totalPrice ? Number(row.totalPrice).toFixed(2) + ' 元' : '0.00 元' }}
|
总金额:{{ row.totalPrice ? Number(row.totalPrice).toFixed(2) + ' 元' : '0.00 元' }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<el-form-item label="备注:" prop="remark">
|
|
||||||
<el-input v-model="row.remark" maxlength="50" placeholder="最多50字" style="width: 200px" />
|
|
||||||
</el-form-item>
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<el-button type="primary" @click="handleSave">确定</el-button>
|
<el-button type="primary" @click="handleSave">确定</el-button>
|
||||||
<el-button @click="handleCancel">取消</el-button>
|
<el-button @click="handleCancel">取消</el-button>
|
||||||
@@ -566,9 +560,6 @@
|
|||||||
<!-- 金额: {{ row.priceList[0].price }} -->
|
<!-- 金额: {{ row.priceList[0].price }} -->
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<el-form-item label="备注:" prop="remark">
|
|
||||||
<el-input v-model="row.remark" maxlength="50" placeholder="最多50字" style="width: 200px" />
|
|
||||||
</el-form-item>
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<el-button type="primary" @click="handleSave">确定</el-button>
|
<el-button type="primary" @click="handleSave">确定</el-button>
|
||||||
<el-button @click="handleCancel">取消</el-button>
|
<el-button @click="handleCancel">取消</el-button>
|
||||||
@@ -651,16 +642,16 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
// Bug #589: 出院带药不自动设置频次为ST,由医生手动选择
|
// Bug #589: 出院带药不自动设置频次为ST,由医生手动选择
|
||||||
if (props.row.therapyEnum == '2' && !props.row.rateCode && props.row.adviceType != 7) {
|
if (props.row.therapyEnum == '2' && !props.row.rateCode && props.row.adviceType != 7) {
|
||||||
setRateCodeToST();
|
setRateCodeToONCE();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.row.therapyEnum,
|
() => props.row.therapyEnum,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
// Bug #589: 出院带药不自动设置频次为ST,由医生手动选择
|
// Bug #589/#615: 出院带药不自动设置频次,临时医嘱默认频次为ONCE(临时一次)
|
||||||
if (newVal == '2' && props.row.adviceType != 7) {
|
if (newVal == '2' && props.row.adviceType != 7) {
|
||||||
setRateCodeToST();
|
setRateCodeToONCE();
|
||||||
} else if (newVal == '1') {
|
} else if (newVal == '1') {
|
||||||
props.row.rateCode = '';
|
props.row.rateCode = '';
|
||||||
props.row.rateCode_dictText = '';
|
props.row.rateCode_dictText = '';
|
||||||
@@ -668,15 +659,21 @@ watch(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const setRateCodeToST = () => {
|
const setRateCodeToONCE = () => {
|
||||||
if (Array.isArray(props.config.rateCode)) {
|
if (Array.isArray(props.config.rateCode)) {
|
||||||
const stOption = props.config.rateCode.find((item) => item.value === 'ST');
|
const onceOption = props.config.rateCode.find((item) => item.value === 'ONCE');
|
||||||
if (stOption) {
|
if (onceOption) {
|
||||||
props.row.rateCode = 'ST';
|
props.row.rateCode = 'ONCE';
|
||||||
props.row.rateCode_dictText = 'ST ' + stOption.label;
|
props.row.rateCode_dictText = 'ONCE ' + onceOption.label;
|
||||||
} else {
|
} else {
|
||||||
props.row.rateCode = 'ST';
|
const stOption = props.config.rateCode.find((item) => item.value === 'ST');
|
||||||
props.row.rateCode_dictText = 'ST 立即';
|
if (stOption) {
|
||||||
|
props.row.rateCode = 'ST';
|
||||||
|
props.row.rateCode_dictText = 'ST ' + stOption.label;
|
||||||
|
} else {
|
||||||
|
props.row.rateCode = 'ST';
|
||||||
|
props.row.rateCode_dictText = 'ST 立即';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -255,7 +255,7 @@
|
|||||||
<el-tag v-else-if="scope.row.statusEnum == 10" type="primary">已校对</el-tag>
|
<el-tag v-else-if="scope.row.statusEnum == 10" type="primary">已校对</el-tag>
|
||||||
<el-tag v-else-if="scope.row.statusEnum == 11" type="primary">待接收</el-tag>
|
<el-tag v-else-if="scope.row.statusEnum == 11" type="primary">待接收</el-tag>
|
||||||
<el-tag v-else-if="scope.row.statusEnum == 3" type="success">已完成</el-tag>
|
<el-tag v-else-if="scope.row.statusEnum == 3" type="success">已完成</el-tag>
|
||||||
<el-tag v-else-if="scope.row.statusEnum == 6" type="danger">停止</el-tag>
|
<el-tag v-else-if="scope.row.statusEnum == 6" type="error">停止</el-tag>
|
||||||
<el-tag v-else type="info">{{ scope.row.chargeStatus_enumText }}</el-tag>
|
<el-tag v-else type="info">{{ scope.row.chargeStatus_enumText }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -489,9 +489,6 @@ const unitMap = ref({
|
|||||||
const buttonDisabled = computed(() => {
|
const buttonDisabled = computed(() => {
|
||||||
return !patientInfo.value;
|
return !patientInfo.value;
|
||||||
});
|
});
|
||||||
const isSaveDisabled = computed(() => {
|
|
||||||
return !patientInfo.value || prescriptionList.value.length === 0;
|
|
||||||
});
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
patientInfo: {
|
patientInfo: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -679,6 +676,7 @@ function getListInfo(addNewRow) {
|
|||||||
organization.value = res?.data?.records ?? res?.data ?? [];
|
organization.value = res?.data?.records ?? res?.data ?? [];
|
||||||
});
|
});
|
||||||
getPrescriptionList(patientInfo.value.encounterId).then((res) => {
|
getPrescriptionList(patientInfo.value.encounterId).then((res) => {
|
||||||
|
console.log('getListInfo==========>', JSON.stringify(res.data));
|
||||||
// 等待科室树加载完成后再处理处方数据,确保 resolveOrgId 能正确匹配
|
// 等待科室树加载完成后再处理处方数据,确保 resolveOrgId 能正确匹配
|
||||||
orgTreePromise.then(() => {
|
orgTreePromise.then(() => {
|
||||||
loadingInstance.close();
|
loadingInstance.close();
|
||||||
@@ -686,6 +684,7 @@ function getListInfo(addNewRow) {
|
|||||||
.map((item) => {
|
.map((item) => {
|
||||||
const parsedContent = JSON.parse(item.contentJson);
|
const parsedContent = JSON.parse(item.contentJson);
|
||||||
// 构造 unitCodeList,确保编辑时下拉框有正确的选项
|
// 构造 unitCodeList,确保编辑时下拉框有正确的选项
|
||||||
|
console.log('【DEBUG】unitCode:', parsedContent?.unitCode, typeof parsedContent?.unitCode, 'unitCodeList:', JSON.stringify(parsedContent?.unitCodeList));
|
||||||
const unitCodeListData = parsedContent?.unitCodeList || [
|
const unitCodeListData = parsedContent?.unitCodeList || [
|
||||||
{ value: String(parsedContent?.unitCode ?? item.unitCode ?? ''), label: parsedContent?.unitCode_dictText ?? item.unitCode_dictText ?? '', type: 'unit' },
|
{ value: String(parsedContent?.unitCode ?? item.unitCode ?? ''), label: parsedContent?.unitCode_dictText ?? item.unitCode_dictText ?? '', type: 'unit' },
|
||||||
{ value: String(parsedContent?.doseUnitCode ?? ''), label: parsedContent?.doseUnitCode_dictText ?? '', type: 'dose' },
|
{ value: String(parsedContent?.doseUnitCode ?? ''), label: parsedContent?.doseUnitCode_dictText ?? '', type: 'dose' },
|
||||||
@@ -706,7 +705,6 @@ function getListInfo(addNewRow) {
|
|||||||
unitCodeList: unitCodeListData,
|
unitCodeList: unitCodeListData,
|
||||||
// 确保 therapyEnum 被正确设置,优先使用 contentJson 中的值
|
// 确保 therapyEnum 被正确设置,优先使用 contentJson 中的值
|
||||||
therapyEnum: String(parsedContent?.therapyEnum ?? item.therapyEnum ?? '1'),
|
therapyEnum: String(parsedContent?.therapyEnum ?? item.therapyEnum ?? '1'),
|
||||||
// 🔧 修复:确保 orgId 为 String 类型,与 organization 树的 id 类型一致
|
|
||||||
// 确保 skinTestFlag 是数字类型(1 或 0),从 contentJson 恢复
|
// 确保 skinTestFlag 是数字类型(1 或 0),从 contentJson 恢复
|
||||||
skinTestFlag: parsedContent?.skinTestFlag !== undefined && parsedContent?.skinTestFlag !== null
|
skinTestFlag: parsedContent?.skinTestFlag !== undefined && parsedContent?.skinTestFlag !== null
|
||||||
? (typeof parsedContent.skinTestFlag === 'number' ? parsedContent.skinTestFlag : (parsedContent.skinTestFlag ? 1 : 0))
|
? (typeof parsedContent.skinTestFlag === 'number' ? parsedContent.skinTestFlag : (parsedContent.skinTestFlag ? 1 : 0))
|
||||||
@@ -714,6 +712,7 @@ function getListInfo(addNewRow) {
|
|||||||
skinTestFlag_enumText: parsedContent?.skinTestFlag !== undefined && parsedContent?.skinTestFlag !== null
|
skinTestFlag_enumText: parsedContent?.skinTestFlag !== undefined && parsedContent?.skinTestFlag !== null
|
||||||
? (parsedContent.skinTestFlag == 1 ? '是' : '否')
|
? (parsedContent.skinTestFlag == 1 ? '是' : '否')
|
||||||
: '否',
|
: '否',
|
||||||
|
// 🔧 修复:确保 orgId 为 String 类型,与 organization 树的 id 类型一致
|
||||||
// 关键:优先使用 item.positionId(后端 @JsonSerialize 保证精度),
|
// 关键:优先使用 item.positionId(后端 @JsonSerialize 保证精度),
|
||||||
// 而非 parsedContent.orgId(来自 JSON.parse,大 Long 可能精度丢失)
|
// 而非 parsedContent.orgId(来自 JSON.parse,大 Long 可能精度丢失)
|
||||||
// 使用 resolveOrgId 从组织树中匹配正确的 String id
|
// 使用 resolveOrgId 从组织树中匹配正确的 String id
|
||||||
@@ -1034,6 +1033,11 @@ function handleFocus(row, index) {
|
|||||||
categoryCode = selectedItem ? selectedItem.categoryCode : (row.categoryCode || '');
|
categoryCode = selectedItem ? selectedItem.categoryCode : (row.categoryCode || '');
|
||||||
}
|
}
|
||||||
adviceQueryParams.value = { adviceType, categoryCode, searchKey: '' };
|
adviceQueryParams.value = { adviceType, categoryCode, searchKey: '' };
|
||||||
|
// handleFocus 打开 popover 时也要加载数据
|
||||||
|
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[index] : adviceTableRef.value;
|
||||||
|
if (tableRef && tableRef.refresh) {
|
||||||
|
tableRef.refresh(adviceType, categoryCode, '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBlur(row) {
|
function handleBlur(row) {
|
||||||
@@ -1088,7 +1092,6 @@ function selectAdviceBase(key, row) {
|
|||||||
}
|
}
|
||||||
// 确保 uniqueKey 不被覆盖
|
// 确保 uniqueKey 不被覆盖
|
||||||
prescriptionList.value[rowIndex.value].uniqueKey = currentUniqueKey;
|
prescriptionList.value[rowIndex.value].uniqueKey = currentUniqueKey;
|
||||||
|
|
||||||
// 检查是否是皮试药品,如果是则弹出确认提示
|
// 检查是否是皮试药品,如果是则弹出确认提示
|
||||||
// 只对药品类型(adviceType=1 药品, adviceType=2 耗材)进行皮试提示
|
// 只对药品类型(adviceType=1 药品, adviceType=2 耗材)进行皮试提示
|
||||||
const isSkinTestDrug = row.skinTestFlag !== undefined && row.skinTestFlag !== null
|
const isSkinTestDrug = row.skinTestFlag !== undefined && row.skinTestFlag !== null
|
||||||
@@ -1153,7 +1156,6 @@ function expandOrderAndFocus(key, row) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getOrgList() {
|
function getOrgList() {
|
||||||
getOrgTree().then((res) => {
|
getOrgTree().then((res) => {
|
||||||
organization.value = res?.data?.records ?? res?.data ?? [];
|
organization.value = res?.data?.records ?? res?.data ?? [];
|
||||||
@@ -1581,6 +1583,10 @@ function handleSaveSign(row, index) {
|
|||||||
if (row.injectFlag == 1) {
|
if (row.injectFlag == 1) {
|
||||||
row.sortNumber = row.sortNumber ? row.sortNumber : prescriptionList.value.length;
|
row.sortNumber = row.sortNumber ? row.sortNumber : prescriptionList.value.length;
|
||||||
}
|
}
|
||||||
|
// Bug #589: 出院带药标记到contentJson
|
||||||
|
if (originalAdviceType == 7) {
|
||||||
|
row.prescriptionCategory = 3;
|
||||||
|
}
|
||||||
// 确保 skinTestFlag 是数字类型(1 或 0)
|
// 确保 skinTestFlag 是数字类型(1 或 0)
|
||||||
row.skinTestFlag = row.skinTestFlag !== undefined && row.skinTestFlag !== null
|
row.skinTestFlag = row.skinTestFlag !== undefined && row.skinTestFlag !== null
|
||||||
? (typeof row.skinTestFlag === 'number' ? row.skinTestFlag : (row.skinTestFlag ? 1 : 0))
|
? (typeof row.skinTestFlag === 'number' ? row.skinTestFlag : (row.skinTestFlag ? 1 : 0))
|
||||||
@@ -1637,11 +1643,11 @@ function handleSaveBatch() {
|
|||||||
...item,
|
...item,
|
||||||
therapyEnum: therapyEnum,
|
therapyEnum: therapyEnum,
|
||||||
dbOpType: item.requestId ? '2' : '1',
|
dbOpType: item.requestId ? '2' : '1',
|
||||||
// 确保 skinTestFlag 是数字类型(1 或 0)
|
// 确保 skinTestFlag 是数字类型(1 或 0)
|
||||||
skinTestFlag: item.skinTestFlag !== undefined && item.skinTestFlag !== null
|
skinTestFlag: item.skinTestFlag !== undefined && item.skinTestFlag !== null
|
||||||
? (typeof item.skinTestFlag === "number" ? item.skinTestFlag : (item.skinTestFlag ? 1 : 0))
|
? (typeof item.skinTestFlag === 'number' ? item.skinTestFlag : (item.skinTestFlag ? 1 : 0))
|
||||||
: 0,
|
: 0,
|
||||||
skinTestFlag_enumText: item.skinTestFlag == 1 ? '是' : '否',
|
skinTestFlag_enumText: item.skinTestFlag == 1 ? '是' : '否',
|
||||||
};
|
};
|
||||||
// Bug #589: 出院带药批量保存时转为药品类型
|
// Bug #589: 出院带药批量保存时转为药品类型
|
||||||
if (result.adviceType == 7) {
|
if (result.adviceType == 7) {
|
||||||
|
|||||||
@@ -54,9 +54,34 @@
|
|||||||
>
|
>
|
||||||
划价组套
|
划价组套
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<div style="margin-left: auto; display: flex; align-items: center; gap: 15px">
|
||||||
|
<div style="display: flex; align-items: center">
|
||||||
|
<span style="margin-right: 8px">执行时间:</span>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="executeTime"
|
||||||
|
type="datetime"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
placeholder="选择日期时间"
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 14px; font-weight: bold; white-space: nowrap">
|
||||||
|
本次补费总金额:<span style="color: #ff4d4f">{{ totalAmount.toFixed(6) }}</span>
|
||||||
|
</div>
|
||||||
|
<el-button @click="handleCancel">
|
||||||
|
取消
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="handleConfirm"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 弹窗内容 - 左右布局 -->
|
<!-- 弹窗内容 - 左右布局 -->
|
||||||
<div style="display: flex; gap: 20px; height: 70vh">
|
<div style="display: flex; gap: 20px; height: 60vh">
|
||||||
<div
|
<div
|
||||||
style="
|
style="
|
||||||
width: 350px;
|
width: 350px;
|
||||||
@@ -299,47 +324,6 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 底部信息区域 -->
|
|
||||||
<div style="margin-top: 15px; display: flex; flex-wrap: wrap; gap: 15px">
|
|
||||||
<div style="display: flex; align-items: center">
|
|
||||||
<span style="margin-right: 8px">执行时间:</span>
|
|
||||||
<el-date-picker
|
|
||||||
v-model="executeTime"
|
|
||||||
type="datetime"
|
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
|
||||||
value-format="YYYY-MM-DD HH:mm:ss"
|
|
||||||
placeholder="选择日期时间"
|
|
||||||
style="width: 200px"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 总金额和操作按钮(固定在弹窗底部) -->
|
|
||||||
<div
|
|
||||||
style="
|
|
||||||
margin-top: 15px;
|
|
||||||
padding-top: 15px;
|
|
||||||
border-top: 1px solid #e4e7ed;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div style="font-size: 14px; font-weight: bold; text-align: right">
|
|
||||||
本次补费总金额:<span style="color: #ff4d4f">{{ totalAmount.toFixed(6) }}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-button @click="handleCancel">
|
|
||||||
取消
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
@click="handleConfirm"
|
|
||||||
>
|
|
||||||
确定
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<!-- 划价组套选择对话框 -->
|
<!-- 划价组套选择对话框 -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
@@ -621,6 +605,7 @@ watch(
|
|||||||
(visible) => {
|
(visible) => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
executeTime.value = formatDateStr(new Date(), 'YYYY-MM-DD HH:mm:ss');
|
executeTime.value = formatDateStr(new Date(), 'YYYY-MM-DD HH:mm:ss');
|
||||||
|
consumableDefaultLocId.value = null; // 重置耗材默认库房,避免复用上次患者配置
|
||||||
// 弹窗打开时按当前患者科室重新加载,避免复用上一次患者/登录科室的结果
|
// 弹窗打开时按当前患者科室重新加载,避免复用上一次患者/登录科室的结果
|
||||||
loadDepartmentOptions();
|
loadDepartmentOptions();
|
||||||
getAdviceBaseInfos();
|
getAdviceBaseInfos();
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ function handleClick(tabName) {
|
|||||||
break;
|
break;
|
||||||
case 'cancel':
|
case 'cancel':
|
||||||
exeStatus.value = 9;
|
exeStatus.value = 9;
|
||||||
|
requestStatus.value = RequestStatus.CANCELLED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -227,9 +227,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 退回原因弹窗 -->
|
<!-- 退回原因弹窗 -->
|
||||||
<el-dialog v-model="backReasonVisible" title="退回原因" width="400px" :close-on-click-modal="false">
|
<el-dialog
|
||||||
<el-form ref="backReasonFormRef" :model="backReasonForm" :rules="backReasonRules">
|
v-model="backReasonVisible"
|
||||||
<el-form-item label="退回原因" prop="reason">
|
title="退回原因"
|
||||||
|
width="400px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="backReasonFormRef"
|
||||||
|
:model="backReasonForm"
|
||||||
|
:rules="backReasonRules"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="退回原因"
|
||||||
|
prop="reason"
|
||||||
|
>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="backReasonForm.reason"
|
v-model="backReasonForm.reason"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
@@ -241,8 +253,15 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="backReasonVisible = false">取消</el-button>
|
<el-button @click="backReasonVisible = false">
|
||||||
<el-button type="primary" @click="confirmCancel">确定退回</el-button>
|
取消
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="confirmCancel"
|
||||||
|
>
|
||||||
|
确定退回
|
||||||
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
@@ -257,10 +276,10 @@ const activeNames = ref([]);
|
|||||||
const prescriptionList = ref([]);
|
const prescriptionList = ref([]);
|
||||||
const deadline = ref(formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59');
|
const deadline = ref(formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59');
|
||||||
const type = ref(null);
|
const type = ref(null);
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
const backReasonVisible = ref(false);
|
const backReasonVisible = ref(false);
|
||||||
const backReasonForm = ref({ reason: '' });
|
const backReasonForm = ref({ reason: '' });
|
||||||
const backReasonFormRef = ref(null);
|
const backReasonFormRef = ref(null);
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const chooseAll = ref(false);
|
const chooseAll = ref(false);
|
||||||
const selectionTrigger = ref(0);
|
const selectionTrigger = ref(0);
|
||||||
@@ -305,7 +324,6 @@ const getStatusDisplayText = (row) => {
|
|||||||
return LEGACY_STATUS_TEXT[row?.requestStatus_enumText] || row?.requestStatus_enumText || '';
|
return LEGACY_STATUS_TEXT[row?.requestStatus_enumText] || row?.requestStatus_enumText || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const getStatusType = (status) => {
|
const getStatusType = (status) => {
|
||||||
const map = {
|
const map = {
|
||||||
1: 'info',
|
1: 'info',
|
||||||
@@ -461,7 +479,6 @@ function handleCheck() {
|
|||||||
function handleCancel() {
|
function handleCancel() {
|
||||||
let list = getSelectRows();
|
let list = getSelectRows();
|
||||||
if (list.length > 0) {
|
if (list.length > 0) {
|
||||||
// 校验已发药的医嘱不允许退回
|
|
||||||
let dispensedItems = list.filter(item => item.dispenseStatus === 4);
|
let dispensedItems = list.filter(item => item.dispenseStatus === 4);
|
||||||
if (dispensedItems.length > 0) {
|
if (dispensedItems.length > 0) {
|
||||||
proxy.$message.error('该药品已由药房发放,请先执行退药处理,不可直接退回');
|
proxy.$message.error('该药品已由药房发放,请先执行退药处理,不可直接退回');
|
||||||
@@ -482,7 +499,6 @@ function confirmCancel() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let list = getSelectRows();
|
let list = getSelectRows();
|
||||||
// 将退回原因附加到每个项目
|
|
||||||
let requestList = list.map(item => ({
|
let requestList = list.map(item => ({
|
||||||
...item,
|
...item,
|
||||||
backReason: backReasonForm.value.reason.trim()
|
backReason: backReasonForm.value.reason.trim()
|
||||||
|
|||||||
@@ -1180,9 +1180,6 @@ function selectRow(rowValue, index) {
|
|||||||
form.purchaseinventoryList[index].partPercent = rowValue.partPercent;
|
form.purchaseinventoryList[index].partPercent = rowValue.partPercent;
|
||||||
form.purchaseinventoryList[index].unitList = rowValue.unitList[0];
|
form.purchaseinventoryList[index].unitList = rowValue.unitList[0];
|
||||||
form.purchaseinventoryList[index].lotNumber = rowValue.lotNumber;
|
form.purchaseinventoryList[index].lotNumber = rowValue.lotNumber;
|
||||||
// 补全单位字典文本,formatInventory 依赖此字段格式化库存显示
|
|
||||||
form.purchaseinventoryList[index].unitCode_dictText = rowValue.unitCode_dictText;
|
|
||||||
form.purchaseinventoryList[index].minUnitCode_dictText = rowValue.minUnitCode_dictText;
|
|
||||||
form.purchaseinventoryList[index].itemQuantity = 0;
|
form.purchaseinventoryList[index].itemQuantity = 0;
|
||||||
form.purchaseinventoryList[index].totalPrice = 0;
|
form.purchaseinventoryList[index].totalPrice = 0;
|
||||||
// 维护一个大小单位的map,用来判断当前选中单位是大/小单位
|
// 维护一个大小单位的map,用来判断当前选中单位是大/小单位
|
||||||
@@ -1197,13 +1194,21 @@ function selectRow(rowValue, index) {
|
|||||||
value: rowValue.minUnitCode,
|
value: rowValue.minUnitCode,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
// 新单/编辑单统一使用行级仓库ID,不再分支判断 route.query.supplyBusNo
|
if (route.query.supplyBusNo) {
|
||||||
handleLocationClick(
|
handleLocationClick(
|
||||||
form.purchaseinventoryList[index].sourceLocationId,
|
receiptHeaderForm.sourceLocationId1,
|
||||||
form.purchaseinventoryList[index].purposeLocationId,
|
receiptHeaderForm.purposeLocationId1,
|
||||||
form.purchaseinventoryList[index].itemId,
|
form.purchaseinventoryList[index].itemId,
|
||||||
index
|
index
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
handleLocationClick(
|
||||||
|
form.purchaseinventoryList[index].sourceLocationId,
|
||||||
|
form.purchaseinventoryList[index].purposeLocationId,
|
||||||
|
form.purchaseinventoryList[index].itemId,
|
||||||
|
index
|
||||||
|
);
|
||||||
|
}
|
||||||
editBatchTransfer(index); // todo
|
editBatchTransfer(index); // todo
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1215,29 +1220,23 @@ function handleLocationClick(id, purposeLocationId, itemId, index) {
|
|||||||
objLocationId: purposeLocationId,
|
objLocationId: purposeLocationId,
|
||||||
lotNumber: form.purchaseinventoryList[index].lotNumber,
|
lotNumber: form.purchaseinventoryList[index].lotNumber,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (res.data && res.data.length) {
|
if (res.data && res.data[0]) {
|
||||||
// SQL 按 locationId 分组后可能有两条记录(源/目的),根据 locationId 精确匹配而非盲目取 res.data[0]
|
form.purchaseinventoryList[index].itemTable = res.data[0].itemTable || '';
|
||||||
const srcId = String(id);
|
form.purchaseinventoryList[index].supplierId = res.data[0].supplierId || '';
|
||||||
const purId = String(purposeLocationId);
|
form.purchaseinventoryList[index].startTime = formatDateymd(res.data[0].productionDate) || '';
|
||||||
const sourceRow = res.data.find(item => String(item.locationId) === srcId) || {};
|
form.purchaseinventoryList[index].endTime = formatDateymd(res.data[0].expirationDate) || '';
|
||||||
const purposeRow = res.data.find(item => String(item.locationId) === purId) || {};
|
|
||||||
|
|
||||||
form.purchaseinventoryList[index].itemTable = sourceRow.itemTable || '';
|
form.purchaseinventoryList[index].price = res.data[0].price;
|
||||||
form.purchaseinventoryList[index].supplierId = sourceRow.supplierId || '';
|
form.purchaseinventoryList[index].totalSourceQuantity = res.data[0].orgQuantity;
|
||||||
form.purchaseinventoryList[index].startTime = formatDateymd(sourceRow.productionDate) || '';
|
form.purchaseinventoryList[index].totalPurposeQuantity = res.data[0].objQuantity;
|
||||||
form.purchaseinventoryList[index].endTime = formatDateymd(sourceRow.expirationDate) || '';
|
|
||||||
|
|
||||||
form.purchaseinventoryList[index].price = sourceRow.price;
|
|
||||||
form.purchaseinventoryList[index].totalSourceQuantity = sourceRow.orgQuantity || 0;
|
|
||||||
form.purchaseinventoryList[index].totalPurposeQuantity = purposeRow.objQuantity || 0;
|
|
||||||
form.purchaseinventoryList[index].totalSourceQuantityDisplay = formatInventory(
|
form.purchaseinventoryList[index].totalSourceQuantityDisplay = formatInventory(
|
||||||
sourceRow.orgQuantity || 0,
|
res.data[0].orgQuantity,
|
||||||
form.purchaseinventoryList[index].partPercent,
|
form.purchaseinventoryList[index].partPercent,
|
||||||
form.purchaseinventoryList[index].unitCode_dictText,
|
form.purchaseinventoryList[index].unitCode_dictText,
|
||||||
form.purchaseinventoryList[index].minUnitCode_dictText
|
form.purchaseinventoryList[index].minUnitCode_dictText
|
||||||
);
|
);
|
||||||
form.purchaseinventoryList[index].totalPurposeQuantityDisplay = formatInventory(
|
form.purchaseinventoryList[index].totalPurposeQuantityDisplay = formatInventory(
|
||||||
purposeRow.objQuantity || 0,
|
res.data[0].objQuantity,
|
||||||
form.purchaseinventoryList[index].partPercent,
|
form.purchaseinventoryList[index].partPercent,
|
||||||
form.purchaseinventoryList[index].unitCode_dictText,
|
form.purchaseinventoryList[index].unitCode_dictText,
|
||||||
form.purchaseinventoryList[index].minUnitCode_dictText
|
form.purchaseinventoryList[index].minUnitCode_dictText
|
||||||
@@ -1341,8 +1340,8 @@ function formatInventory(quantity, partPercent, unitCode, minUnitCode) {
|
|||||||
return isNegative ? '-' + result : result;
|
return isNegative ? '-' + result : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 整除时也需拼接单位后缀,否则显示为纯数字缺少单位信息
|
// 整除情况
|
||||||
const result = (absQuantity / partPercent) + ' ' + unitCode;
|
const result = absQuantity / partPercent;
|
||||||
return isNegative ? '-' + result : result;
|
return isNegative ? '-' + result : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,12 +75,30 @@
|
|||||||
clearable
|
clearable
|
||||||
style="width: 120px"
|
style="width: 120px"
|
||||||
>
|
>
|
||||||
<el-option label="已到达" :value="1" />
|
<el-option
|
||||||
<el-option label="已分诊" :value="2" />
|
label="已到达"
|
||||||
<el-option label="已看诊" :value="3" />
|
:value="1"
|
||||||
<el-option label="已离开" :value="4" />
|
/>
|
||||||
<el-option label="已完成" :value="5" />
|
<el-option
|
||||||
<el-option label="无状态" :value="0" />
|
label="已分诊"
|
||||||
|
:value="2"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="已看诊"
|
||||||
|
:value="3"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="已离开"
|
||||||
|
:value="4"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="已完成"
|
||||||
|
:value="5"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="无状态"
|
||||||
|
:value="0"
|
||||||
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
@@ -251,11 +269,8 @@ function getList() {
|
|||||||
console.log('当前查看患者:', route.query.patientName);
|
console.log('当前查看患者:', route.query.patientName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建请求参数 - "无状态"(0) 转为 undefined,让后端不加过滤
|
// 构建请求参数
|
||||||
const requestParams = { ...queryParams.value };
|
const requestParams = { ...queryParams.value };
|
||||||
if (requestParams.subjectStatusEnum === 0) {
|
|
||||||
requestParams.subjectStatusEnum = undefined;
|
|
||||||
}
|
|
||||||
listOutpatienRecords(requestParams).then((response) => {
|
listOutpatienRecords(requestParams).then((response) => {
|
||||||
outpatienRecordsList.value = response.data.records;
|
outpatienRecordsList.value = response.data.records;
|
||||||
total.value = response.data.total;
|
total.value = response.data.total;
|
||||||
|
|||||||
Reference in New Issue
Block a user