Compare commits

153 Commits

Author SHA1 Message Date
Auora
0610ba7cb5 解决门诊挂号退号费用性质不一致的bug 2025-11-14 15:41:30 +08:00
叶锦涛
8b848f787a 修改科室管理主界面科室分类和编辑科室的字典 2025-11-14 09:25:37 +08:00
wzk
a794cd0ce3 门诊医生站开立医嘱中成药的开医嘱录入界面和西药的开医嘱录入界面保持一致 2025-11-14 09:01:48 +08:00
叶锦涛
d97fd0bed1 修复采购入库时选择不同仓库新增药品时仓库的错误 2025-11-13 17:16:12 +08:00
叶锦涛
c0e67722d9 修复门诊医生站诊断删除按钮的错误 2025-11-13 15:40:53 +08:00
qk123
aca7fea69f 新增门诊医生站取消接诊功能 2025-11-13 13:41:48 +08:00
wzk
ce9344f9ce 门诊医生站-》开立医嘱系统根据选中的药品名称
判断医嘱类型。
2025-11-13 10:17:35 +08:00
wzk
991ad61655 1 2025-11-13 08:59:09 +08:00
Auora
e4ac857461 【bug】剂量单位bug 2025-11-12 14:18:51 +08:00
叶锦涛
24ab98ae89 发票号码维护页面 2025-11-12 13:51:34 +08:00
wzk
e79ab9ac4f 医嘱:修改用药天数时,系统自动通过公式自动换算总量 2025-11-12 13:31:11 +08:00
b66c2027d1 换卡处理的分页 2025-11-12 12:08:52 +08:00
09bf895711 维护换卡处理的查询 2025-11-12 10:58:12 +08:00
Auora
825cdd58ae 【bug】 2025-11-12 10:12:12 +08:00
0977eb8145 维护换卡处理的查询bug 2025-11-12 09:59:03 +08:00
Auora
73efeecfc1 解决当日已挂号页面费用性质不一致问题 2025-11-12 09:47:28 +08:00
fe8fb3d321 维护换卡处理的查询 2025-11-12 09:38:47 +08:00
叶锦涛
618fb1e340 修改新增患者的信息样式 2025-11-12 09:15:08 +08:00
叶锦涛
215fe8b889 修改科室管理和新增科室分类的字典 2025-11-11 16:46:36 +08:00
Auora
3e32458b7f 解决医嘱类型选中开单后变成数字问题。 2025-11-11 16:05:18 +08:00
wzk
a8e170ea45 门诊号码管理维护界面-》优化 2025-11-11 13:58:34 +08:00
wzk
1856ae50fd Merge remote-tracking branch 'origin/develop' into develop 2025-11-11 13:58:20 +08:00
wzk
1711e6c115 门诊号码管理维护界面-》优化 2025-11-11 13:57:34 +08:00
叶锦涛
b3f226feab 修改新增患者 2025-11-11 13:28:50 +08:00
wzk
c086dc8c77 修改报表管理-》院内库房情况查询报表-》库存商品明细查询报表,选择库存范围条件检索报错补充 2025-11-11 11:51:52 +08:00
wzk
6762341fbd 修改报表管理-》院内库房情况查询报表-》库存商品明细查询报表,选择库存范围条件检索报错。 2025-11-11 11:36:15 +08:00
d5f8b0f23b 医嘱类型勾选全部之后项目报错的bug 2025-11-11 10:12:40 +08:00
叶锦涛
38233110f2 1、监护人关系要取原来的联系人关系的下拉内容,取值于字典管理-》与患者关系。 2025-11-10 17:11:15 +08:00
6ebb59bc5e 、增加挂号单补打功能(差就诊号查询自动填充) 2025-11-10 17:09:08 +08:00
wzk
69aefab280 库房/药房管理:添加的耗材库 2025-11-10 16:57:05 +08:00
3d3ff82e24 解决医嘱类别选择全部项目报错的问题 2025-11-10 16:53:14 +08:00
69780d204f 1、收费工作站-》门诊挂号:将门诊挂号界面的功能按钮统一摆放在标题页,依次摆放整齐。
2、增加挂号单补打功能(差就诊号查询自动填充)
3.患者档案管理:将【修改】和【查询】按钮调出的界面统一修改成图2的新增患者界面。
2025-11-10 16:35:47 +08:00
Auora
ef52f290fe 解决医嘱类型选中开单后变成数字问题。 2025-11-10 16:17:44 +08:00
wzk
e9d1119777 门诊号码管理维护界面-》优化 2025-11-10 14:41:22 +08:00
qk123
cf182f0e34 修改:初复诊标识系统重新判断->从数据库中读取 2025-11-10 14:32:59 +08:00
qk123
626ae1a459 修改:初复诊标识系统重新判断->从数据库中读取 2025-11-10 10:12:29 +08:00
wzk
3d6977328f 解决系统管理-》基础数据-》库房/药房管理无法添加的耗材库问题的补充 2025-11-07 16:42:19 +08:00
wzk
2dcd7ba1d5 解决系统管理-》基础数据-》库房/药房管理无法添加的耗材库问题 2025-11-07 16:27:56 +08:00
qk123
16a4d38113 修复疾病目录模块下新增病种异常提示请勿重复提交问题 2025-11-07 15:25:28 +08:00
57c98ea39d 解决诊断目录数据展示不全问题 2025-11-07 15:13:41 +08:00
08f7e35042 解决诊断目录数据展示不全问题 2025-11-07 14:08:28 +08:00
wzk
43f3d1ba94 立医嘱的执行科室默认获取诊疗项目维护的所属科室,如果诊疗项目未维护所属科室,默认执行科室为开单科室,然后开单医生又权限操作修改。 2025-11-07 13:41:57 +08:00
wzk
2e45c6c029 开单科室维护的取药药房药在医生开处方药做好过滤限制,没有维护的药房/药库或者药品类型过滤掉。 2025-11-07 13:35:06 +08:00
wzk
945182c6f8 解决报表管理-》院内库房情况查询报表-》库存商品明细查询报表bug 2025-11-07 09:56:50 +08:00
Auora
f7de87860b 1增加挂号收费系统参数维护界面挂号处理的相关参数
2在打印设置tba页面增加是否打印挂号单维护参数
2025-11-06 17:20:42 +08:00
wzk
67e4de0d68 Merge remote-tracking branch 'origin/develop' into develop 2025-11-06 17:13:11 +08:00
qk123
602d521424 修改完诊后再接诊初复诊标识不对问题 2025-11-06 17:12:57 +08:00
叶锦涛
01e14ee084 修改登录界面测试医院的信息 2025-11-06 16:56:41 +08:00
qk123
4a5572de26 修改完诊后再接诊初复诊标识不对问题 2025-11-06 14:00:45 +08:00
88a516d1be 门诊挂号-》【档案】档管理的【新增患者】-患者界面(图1)的内容要与图2的内容一致(门诊挂号-》【新建】-》新增患者界面) 2025-11-06 13:34:59 +08:00
wzk
9ce967002a Merge remote-tracking branch 'origin/develop' into develop 2025-11-06 13:25:41 +08:00
wzk
fd536a035e 修改 2025-11-06 13:25:25 +08:00
wzk
df84a7eefa Merge remote-tracking branch 'origin/develop' into develop 2025-11-06 13:23:55 +08:00
2804703eaa 门诊号码管理维护界面 2025-11-06 13:23:33 +08:00
wzk
e14a0e3d13 Merge remote-tracking branch 'origin/develop' into develop 2025-11-06 13:15:49 +08:00
5a99fe8234 门诊号码管理维护界面 2025-11-06 13:15:22 +08:00
3ad5c5533f 在门诊挂号页面增加档案按钮完成患者档案管理的跳转 2025-11-06 09:11:26 +08:00
Auora
9997f4f7c9 修改医嘱字典 2025-11-06 08:53:57 +08:00
Auora
3d4e4a8119 修改病历不能编辑问题 2025-11-05 16:15:23 +08:00
叶锦涛
3deac74898 在处方打印处增加患者信息 2025-11-05 16:07:37 +08:00
wzk
7bbc50ef47 Merge remote-tracking branch 'origin/develop' into develop 2025-11-05 15:39:48 +08:00
wzk
4ae6765a15 门诊号码管理维护界面 2025-11-05 15:39:39 +08:00
wzk
1292853c5d Merge remote-tracking branch 'origin/develop' into develop 2025-11-05 14:53:46 +08:00
Auora
41791c9ccc 修改门诊挂号和费用支付上的费用性质不一致的问题,并且将解决无法收费的问题 2025-11-05 14:53:34 +08:00
Auora
0674215b53 修改门诊挂号和费用支付上的费用性质不一致的问题,并且将解决无法收费的问题 2025-11-05 14:09:06 +08:00
e42d990304 收费工作站-》门诊挂号:将门诊挂号界面的功能按钮统一摆放在标题页,依次摆放整齐。 2025-11-05 13:32:48 +08:00
0dbe9a57c8 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	openhis-ui-vue3/src/views/charge/outpatientregistration/index.vue
2025-11-05 11:45:36 +08:00
85ea831a1c 收费工作站-》门诊挂号:将门诊挂号界面的功能按钮统一摆放在标题页,依次摆放整齐。 2025-11-05 11:43:52 +08:00
7ac26cf781 增加监护人信息限制,在门诊挂号页面增加档案按钮完成患者档案管理的跳转 2025-11-05 11:38:47 +08:00
b7412648b4 修改报表格式2 2025-11-04 16:45:41 +08:00
叶锦涛
97571652e5 把新增患者中的联系人替换成监护人 2025-11-04 16:36:26 +08:00
b5d4da97f9 修改报表格式 2025-11-04 16:10:16 +08:00
叶锦涛
e5edb6bda2 跳过了处方打印页面的构建 2025-11-04 15:45:12 +08:00
叶锦涛
0e1ae53194 修改了标题undefined 2025-11-04 14:55:32 +08:00
a1efd3f91b 修复完诊后的初诊复诊错乱 2025-11-04 13:24:07 +08:00
3211553d0d 修复门诊收费报表报错,修复采购入库历史遗留数据问题 2025-11-04 13:10:47 +08:00
wzk
a57a326b83 Merge remote-tracking branch 'origin/develop' into develop 2025-11-04 10:44:14 +08:00
wzk
f77d0a2567 新增用户用户密码系统可以自动赋值一个初始密码,123456。 2025-11-04 10:43:52 +08:00
wzk
72581466aa 新增用户用户密码系统可以自动赋值一个初始密码,123456。 2025-11-03 17:39:05 +08:00
Auora
4b3471df06 门诊挂号的费用性质取值改成取值字典管理-》医疗费用支付方式代码的字典数据
将门诊医生站开立医嘱的医嘱类型字段下拉列表内容做成前台维护
2025-11-03 17:15:00 +08:00
叶锦涛
68c65ae8bd 给登录界面的标题加上了信息管理系统 2025-11-03 17:10:10 +08:00
wzk
1b879addc7 门诊医生开处方单,系统把药品无论是药库或药房有的库存都显示出来了。解决显示只有药房的问题。 2025-11-03 16:14:52 +08:00
叶锦涛
3888859b2b 修复了处方打印不能打印的错误 2025-11-03 13:48:20 +08:00
Auora
f03d2e1633 修改病历格式 2025-11-03 13:38:47 +08:00
bc91eb7cdc 按照采购退货的申请审批流程,完成药品采购申请退货的审批确认, 2025-11-03 11:56:34 +08:00
Auora
93120e973a [bug]解决选中药库或药房对应的源仓库或目的仓库无下拉数据 2025-11-03 11:12:19 +08:00
wzk
c6a27f6276 Merge remote-tracking branch 'origin/develop' into develop 2025-11-03 09:41:36 +08:00
wzk
778704c9dc 药品类别下拉别表内容错误,药品类别应该取值如下图字典管理的药品分类编码 2025-11-03 09:41:28 +08:00
wzk
ff227b40c1 Merge remote-tracking branch 'origin/develop' into develop 2025-11-03 09:38:43 +08:00
wzk
c7d8ab5b48 Merge remote-tracking branch 'origin/develop' into develop 2025-11-03 09:38:36 +08:00
wzk
9c27b2a134 Merge remote-tracking branch 'origin/develop' into develop 2025-11-03 09:31:58 +08:00
叶锦涛
3f919188d2 药品类别下拉别表内容错误,药品类别应该取值如下图字典管理的药品分类编码 2025-11-03 09:31:45 +08:00
wzk
8942058972 Merge remote-tracking branch 'origin/develop' into develop 2025-11-03 09:29:49 +08:00
叶锦涛
6c2dbf0418 药品类别下拉别表内容错误,药品类别应该取值字典管理的药品分类编码 2025-11-03 09:29:30 +08:00
叶锦涛
85e185bab8 修复了全选框 2025-10-31 16:35:47 +08:00
叶锦涛
88aa1517ef Merge branch 'develop' of https://gitea.gentronhealth.com/wangjunping/his into develop 2025-10-31 16:32:53 +08:00
叶锦涛
c05118c427 修复了全选框 2025-10-31 16:32:13 +08:00
Auora
2a7f1326b9 [bug]修改就诊时,就诊时间逻辑 2025-10-31 16:23:12 +08:00
叶锦涛
c07255fe5b 修改了登入界面的xxx医院信息管理系统 2025-10-31 16:13:53 +08:00
叶锦涛
749bfc89dd 修复了打印单据没有响应的错误,修复了新增加诊断诊断类别自动获取11的错误 2025-10-31 16:06:12 +08:00
52cc5e3aae 实现采购入库的供货商字段数据存放到数据库,并且系统在采购入库界面能读取对应的采购入库单的对应供货商名称显示 2025-10-31 13:52:33 +08:00
wzk
642c4a0941 Merge remote-tracking branch 'origin/develop' into develop 2025-10-31 12:38:34 +08:00
wzk
30953d5771 开立医嘱的执行科室默认获取诊疗项目维护的所属科室 2025-10-31 12:38:22 +08:00
wzk
83e0c663c9 在开立好的医嘱名称前增加医嘱类别的显示 2025-10-31 12:32:41 +08:00
b19337e76a 修复初诊和就诊的bug 2025-10-31 12:30:11 +08:00
ba607346bd 修复初诊和就诊的bug 2025-10-31 11:05:40 +08:00
Auora
433c452a40 解决库房/药房管理,操作编辑弹窗问题 2025-10-31 10:08:59 +08:00
Auora
a1be7fdbfd [bug]修改仓库字段下拉别表无内容bug 2025-10-31 09:01:35 +08:00
叶锦涛
87409d0c93 修复了药房/库房的删除的报错 2025-10-30 15:38:51 +08:00
d5c8ae8d45 修复初诊和就诊的bug 2025-10-30 15:28:05 +08:00
bc45e9c8c6 首页页面优化 2025-10-30 11:14:25 +08:00
Auora
42992382c0 增加isFoodDiseasesNew 函数 2025-10-30 10:43:54 +08:00
Auora
22d7eba510 增加getEmrDetail 函数 2025-10-30 09:56:41 +08:00
9728c8a6dd 优化格式问题 2025-10-30 09:45:50 +08:00
c951144ac6 首页页面进行了初步优化 2025-10-30 09:15:27 +08:00
叶锦涛
0b179fffd6 诊断排序 2025-10-29 16:50:39 +08:00
Auora
75374ac5d3 更改.env.production 2025-10-29 15:15:26 +08:00
Auora
2a83719b87 更改prod配置 2025-10-29 14:54:55 +08:00
Auora
e58f2d807b 更改prod配置 2025-10-29 14:03:43 +08:00
Auora
b2884def17 Merge branch 'develop' of https://gitea.gentronhealth.com/wangjunping/his into develop
# Conflicts:
#	openhis-ui-vue3/.env.spug
2025-10-29 13:53:14 +08:00
Auora
064840dd42 删除VITE_OUT_DIR=dist-spug 2025-10-29 13:52:22 +08:00
1aa814c766 更新 openhis-ui-vue3/.env.spug
删除VITE_OUT_DIR=dist-spug
2025-10-29 05:49:44 +00:00
Auora
b9719a51d1 增加build:spug 2025-10-29 13:46:48 +08:00
Auora
03b83aaf7c 增加VITE_APP_ENV = 'spug' 2025-10-29 13:30:25 +08:00
Auora
d93976483b 增加VITE_APP_BASE_API = '/admin-api' 2025-10-29 13:28:13 +08:00
dd0a3a915c 身份证号改为不是必须项只有输入身份证号的时候才会校验格式问题 2025-10-29 09:47:32 +08:00
2f3994c575 身份证号改为不是必须项只有输入身份证号的时候才会校验格式问题 2025-10-29 09:43:54 +08:00
wzk
daa78e128f 修复医嘱请选择项目选完选项框不消失 2025-10-29 09:40:55 +08:00
wzk
2ca594cb39 Merge remote-tracking branch 'origin/develop' into develop 2025-10-29 09:36:01 +08:00
Auora
7805c26f4a Merge remote-tracking branch 'origin/develop' into develop 2025-10-29 09:33:01 +08:00
Auora
b7d34537c2 修改login 2025-10-29 09:32:29 +08:00
wzk
c1213fcf59 Revert "修复医嘱请选择项目选完选项框不消失"
This reverts commit 4afe0d107c.
2025-10-29 09:21:55 +08:00
wzk
a64edace55 Merge remote-tracking branch 'origin/develop' into develop 2025-10-29 09:04:25 +08:00
wzk
4afe0d107c 修复医嘱请选择项目选完选项框不消失 2025-10-29 09:04:01 +08:00
Auora
d8af11412f 修改格式 2025-10-29 08:55:14 +08:00
Auora
00816c9834 修改位置:applicationFormBottomBtn.vue 中的 import 语句。 2025-10-28 17:28:25 +08:00
Auora
4fb7bea80a 修改位置:prescriptionlist.vue 中的 import 语句 2025-10-28 17:21:20 +08:00
Auora
0fc72cb270 修改位置:prescriptionlist.vue 中的 import 语句 2025-10-28 17:14:23 +08:00
Auora
eadf521903 Merge branch 'develop' of https://gitea.gentronhealth.com/wangjunping/his into develop 2025-10-28 17:04:08 +08:00
f786fdbc3f 更新 openhis-ui-vue3/.env.spug
删除NODE_ENV=spug
2025-10-28 09:03:17 +00:00
92c4c938a3 更新 openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionMedicineList.vue 2025-10-28 08:57:06 +00:00
Auora
a94a1b7b69 Merge branch 'develop' of https://gitea.gentronhealth.com/wangjunping/his into develop 2025-10-28 16:34:06 +08:00
Auora
48755c2d9e 删掉多余逗号 2025-10-28 16:32:42 +08:00
叶锦涛
f99f8eb560 系统登入界面的设计 2025-10-28 16:27:42 +08:00
Auora
ddc7ce2fe7 spug测试 2025-10-28 15:56:24 +08:00
Auora
c9899c62d2 Merge branch 'develop' of https://gitea.gentronhealth.com/wangjunping/his 2025-10-28 15:55:04 +08:00
wzk
8b9837d7dc Merge remote-tracking branch 'origin/develop' into develop 2025-10-28 13:35:22 +08:00
wzk
2cba41331a 诊断类别:字典管理中找不到对应的字典数据,字典类别在显示出来的数据和医生开立诊断的数据不一致。 2025-10-28 13:34:56 +08:00
4da5ca427b 在租户管理-》所属用户界面的关闭按钮改为关闭当前的界面,并返回上一级界面, 2025-10-28 13:33:50 +08:00
wzk
f4605b1af7 下拉列表的刷选功能失效,按照下拉选中的医嘱类型能过滤出对应收费项目医生进行开单 2025-10-27 15:08:28 +08:00
叶锦涛
d2babdc9ed 修复已保存病例模板无数据的显示的问题 2025-10-27 14:02:01 +08:00
4d0599eac1 将历史病例中对病例选择的bug修复,现在是单选选中 2025-10-27 13:22:25 +08:00
Auora
35d99df274 test 2025-10-24 17:17:31 +08:00
96 changed files with 8969 additions and 1849 deletions

View File

@@ -8,7 +8,7 @@
天天开源的前⾝是新致开源最早于2022年6⽉发布开源医疗软件平台OpenHIS.org.cn于2023年6⽉发布开源企业软件平台OpenCOM.com.cn。2025年7⽉新致开源品牌更新为天天开源我们始终秉持开源、专业、协作的理念致⼒于为医疗、教育、中⼩企业等⾏业提供优质的开源解决⽅案。 天天开源的前⾝是新致开源最早于2022年6⽉发布开源医疗软件平台OpenHIS.org.cn于2023年6⽉发布开源企业软件平台OpenCOM.com.cn。2025年7⽉新致开源品牌更新为天天开源我们始终秉持开源、专业、协作的理念致⼒于为医疗、教育、中⼩企业等⾏业提供优质的开源解决⽅案。
了解我们https://open.tntlinking.com/about?site=gitee 了解我们ahttps://open.tntlinking.com/about?site=gitee
## 💾【部署包下载】 ## 💾【部署包下载】

View File

@@ -37,6 +37,10 @@ public final class AgeCalculatorUtil {
* 当前年龄取得(床位列表表示年龄用) * 当前年龄取得(床位列表表示年龄用)
*/ */
public static String getAge(Date date) { public static String getAge(Date date) {
// 添加空值检查
if (date == null) {
return "";
}
// 将 Date 转换为 LocalDateTime // 将 Date 转换为 LocalDateTime
LocalDateTime dateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); LocalDateTime dateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();

View File

@@ -22,10 +22,11 @@ import com.core.common.utils.StringUtils;
*/ */
@Configuration @Configuration
public class FilterConfig { public class FilterConfig {
@Value("${xss.excludes}") // 添加默认值,避免配置不存在时启动失败
@Value("${xss.excludes:/system/notice}")
private String excludes; private String excludes;
@Value("${xss.urlPatterns}") @Value("${xss.urlPatterns:/system/*,/monitor/*,/tool/*}")
private String urlPatterns; private String urlPatterns;
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})

View File

@@ -0,0 +1,53 @@
package com.openhis.web.charge.patientcardrenewal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.core.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
/**
* 患者换卡控制器
*
* @author system
* @date 2024-01-01
*/
@RestController
@RequestMapping("/charge/patientCardRenewal")
@Slf4j
public class PatientCardRenewalController {
@Autowired
private PatientCardRenewalService patientCardRenewalService;
/**
* 执行患者换卡操作
*
* @param request 换卡请求参数
* @return 换卡结果
*/
@PostMapping("/renewCard")
public R<?> renewCard(@RequestBody RenewalRequest request) {
try {
log.info("患者换卡请求: 旧卡号={}, 新卡号={}, 患者ID={}",
request.getOldCardNo(), request.getNewCardNo(), request.getPatientId());
// 执行换卡操作
boolean success = patientCardRenewalService.renewCard(request);
if (success) {
log.info("患者换卡成功: 旧卡号={} -> 新卡号={}",
request.getOldCardNo(), request.getNewCardNo());
return R.ok("换卡成功");
} else {
return R.fail("换卡失败");
}
} catch (Exception e) {
log.error("患者换卡异常: ", e);
return R.fail("换卡操作异常: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,18 @@
package com.openhis.web.charge.patientcardrenewal;
/**
* 患者换卡服务接口
*
* @author system
* @date 2024-01-01
*/
public interface PatientCardRenewalService {
/**
* 执行患者换卡操作
*
* @param request 换卡请求参数
* @return 是否换卡成功
*/
boolean renewCard(RenewalRequest request);
}

View File

@@ -0,0 +1,43 @@
package com.openhis.web.charge.patientcardrenewal;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
/**
* 患者换卡服务实现类
*
* @author system
* @date 2024-01-01
*/
@Service
@Slf4j
public class PatientCardRenewalServiceImpl implements PatientCardRenewalService {
@Override
public boolean renewCard(RenewalRequest request) {
// TODO: 这里应该实现真实的换卡业务逻辑
// 1. 验证参数合法性
// 2. 检查新卡号是否已被使用
// 3. 更新患者主表中的卡号信息
// 4. 记录换卡日志
// 5. 处理相关业务系统的卡号更新
// 目前返回模拟结果
log.info("模拟执行患者换卡操作: 旧卡号={}, 新卡号={}, 原因={}",
request.getOldCardNo(), request.getNewCardNo(), request.getReason());
// 简单验证:确保旧卡号和新卡号不为空且不相同
if (request.getOldCardNo() == null || request.getNewCardNo() == null ||
request.getOldCardNo().isEmpty() || request.getNewCardNo().isEmpty()) {
throw new IllegalArgumentException("卡号不能为空");
}
if (request.getOldCardNo().equals(request.getNewCardNo())) {
throw new IllegalArgumentException("新卡号不能与旧卡号相同");
}
// 模拟成功结果
return true;
}
}

View File

@@ -0,0 +1,38 @@
package com.openhis.web.charge.patientcardrenewal;
import lombok.Data;
/**
* 换卡请求参数类
*
* @author system
* @date 2024-01-01
*/
@Data
public class RenewalRequest {
/**
* 旧门诊号码
*/
private String oldCardNo;
/**
* 新门诊号码
*/
private String newCardNo;
/**
* 患者ID
*/
private String patientId;
/**
* 换卡原因
*/
private String reason;
/**
* 备注信息
*/
private String remark;
}

View File

@@ -91,9 +91,9 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
*/ */
@Override @Override
public Page<PatientMetadata> getPatientMetadataBySearchKey(String searchKey, Integer pageNo, Integer pageSize) { public Page<PatientMetadata> getPatientMetadataBySearchKey(String searchKey, Integer pageNo, Integer pageSize) {
// 构建查询条件 // 构建查询条件添加phone字段支持手机号搜索
QueryWrapper<Patient> queryWrapper = HisQueryUtils.buildQueryWrapper(null, searchKey, QueryWrapper<Patient> queryWrapper = HisQueryUtils.buildQueryWrapper(null, searchKey,
new HashSet<>(Arrays.asList("id_card", "name", "py_str", "wb_str")), null); new HashSet<>(Arrays.asList("id_card", "name", "py_str", "wb_str", "phone")), null);
// 设置排序 // 设置排序
queryWrapper.orderByDesc("update_time"); queryWrapper.orderByDesc("update_time");
// 通过证件号匹配 patient // 通过证件号匹配 patient

View File

@@ -81,5 +81,11 @@ public class RefundItemDto {
/** 项目名 */ /** 项目名 */
private String itemName; private String itemName;
/** 费用支付方式编码 */
private String medfeePaymtdCode;
/** 费用类型 */
private String feeType;
} }

View File

@@ -29,7 +29,7 @@ public interface ICommonService {
/** /**
* 药房列表(库房用) * 药房列表(库房用)
* *
* @return 药房列表 * @return 药房列表1
*/ */
List<LocationDto> getInventoryPharmacyList(); List<LocationDto> getInventoryPharmacyList();

View File

@@ -91,7 +91,7 @@ public class CommonAppController {
/** /**
* 病区列表 * 病区列表
* *
* @return 病区列表 * @return 病区列表
*/ */
@GetMapping(value = "/ward-list") @GetMapping(value = "/ward-list")
public R<?> getWardList(@RequestParam(value = "orgId", required = false) Long orgId) { public R<?> getWardList(@RequestParam(value = "orgId", required = false) Long orgId) {
@@ -105,7 +105,7 @@ public class CommonAppController {
*/ */
@GetMapping(value = "/department-list") @GetMapping(value = "/department-list")
public R<?> getDepartmentList() { public R<?> getDepartmentList() {
return commonService.getDepartmentList(); return commonService.getDepartmentList();
} }
/** /**

View File

@@ -52,6 +52,14 @@ public interface IDoctorStationMainAppService {
*/ */
R<?> completeEncounter(Long encounterId); R<?> completeEncounter(Long encounterId);
/**
* 取消完成
*
* @param encounterId 就诊id
* @return 结果
*/
R<?> cancelEncounter(Long encounterId);
/** /**
* 查询处方号列表信息 * 查询处方号列表信息
* *

View File

@@ -140,6 +140,17 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
adviceUtils.subtractInventory(adviceInventoryList, adviceDraftInventoryList); adviceUtils.subtractInventory(adviceInventoryList, adviceDraftInventoryList);
// 查询取药科室配置 // 查询取药科室配置
List<AdviceInventoryDto> medLocationConfig = doctorStationAdviceAppMapper.getMedLocationConfig(organizationId); List<AdviceInventoryDto> medLocationConfig = doctorStationAdviceAppMapper.getMedLocationConfig(organizationId);
// 将配置转为 {categoryCode -> 允许的locationId集合}
Map<String, Set<Long>> allowedLocByCategory = new HashMap<>();
if (medLocationConfig != null && !medLocationConfig.isEmpty()) {
for (AdviceInventoryDto cfg : medLocationConfig) {
if (cfg.getCategoryCode() == null || cfg.getLocationId() == null) {
continue;
}
allowedLocByCategory.computeIfAbsent(String.valueOf(cfg.getCategoryCode()), k -> new HashSet<>())
.add(cfg.getLocationId());
}
}
// 费用定价子表信息 // 费用定价子表信息
List<AdvicePriceDto> childCharge = doctorStationAdviceAppMapper List<AdvicePriceDto> childCharge = doctorStationAdviceAppMapper
.getChildCharge(ConditionCode.LOT_NUMBER_COST.getCode(), chargeItemDefinitionIdList); .getChildCharge(ConditionCode.LOT_NUMBER_COST.getCode(), chargeItemDefinitionIdList);
@@ -163,6 +174,19 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
.filter(e -> baseDto.getAdviceDefinitionId().equals(e.getItemId()) .filter(e -> baseDto.getAdviceDefinitionId().equals(e.getItemId())
&& baseDto.getAdviceTableName().equals(e.getItemTable())) && baseDto.getAdviceTableName().equals(e.getItemTable()))
.collect(Collectors.toList()); .collect(Collectors.toList());
// 当存在按科室配置时:仅保留被允许的药房/药库的库存;
// 若该药品类别未在配置中出现,则视为不可开立(清空库存以便前端过滤掉)
if (!allowedLocByCategory.isEmpty()) {
Set<Long> allowedLoc =
allowedLocByCategory.get(String.valueOf(baseDto.getCategoryCode()));
if (allowedLoc == null || allowedLoc.isEmpty()) {
inventoryList = Collections.emptyList();
} else {
inventoryList = inventoryList.stream()
.filter(inv -> allowedLoc.contains(inv.getLocationId()))
.collect(Collectors.toList());
}
}
// 库存信息 // 库存信息
baseDto.setInventoryList(inventoryList); baseDto.setInventoryList(inventoryList);
// 设置默认产品批号 // 设置默认产品批号
@@ -174,18 +198,15 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
baseDto.setDefaultLotNumber(hasInventoryList.get(0).getLotNumber()); baseDto.setDefaultLotNumber(hasInventoryList.get(0).getLotNumber());
} }
} }
if (!inventoryList.isEmpty() && !medLocationConfig.isEmpty()) { if (!inventoryList.isEmpty() && !allowedLocByCategory.isEmpty()) {
// 第一步在medLocationConfig中匹配categoryCode Set<Long> allowedLoc =
AdviceInventoryDto result1 = medLocationConfig.stream() allowedLocByCategory.get(String.valueOf(baseDto.getCategoryCode()));
.filter(dto -> baseDto.getCategoryCode().equals(dto.getCategoryCode())).findFirst() if (allowedLoc != null && !allowedLoc.isEmpty()) {
.orElse(null); AdviceInventoryDto hit = inventoryList.stream()
if (result1 != null) { .filter(inv -> allowedLoc.contains(inv.getLocationId()))
// 第二步在inventoryList中匹配locationId .findFirst().orElse(null);
AdviceInventoryDto result2 = inventoryList.stream() if (hit != null && hit.getLotNumber() != null) {
.filter(dto -> result1.getLocationId().equals(dto.getLocationId())).findFirst() baseDto.setDefaultLotNumber(hit.getLotNumber());
.orElse(null);
if (result2 != null && result2.getLotNumber() != null) {
baseDto.setDefaultLotNumber(result2.getLotNumber());
} }
} }
} }

View File

@@ -1,14 +1,13 @@
package com.openhis.web.doctorstation.appservice.impl; package com.openhis.web.doctorstation.appservice.impl;
import java.util.Arrays; import java.util.*;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import cn.hutool.core.util.ObjectUtil;
import com.openhis.web.doctorstation.appservice.*;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -25,7 +24,6 @@ import com.openhis.administration.service.IEncounterParticipantService;
import com.openhis.common.enums.*; import com.openhis.common.enums.*;
import com.openhis.common.utils.EnumUtils; import com.openhis.common.utils.EnumUtils;
import com.openhis.common.utils.HisQueryUtils; import com.openhis.common.utils.HisQueryUtils;
import com.openhis.web.doctorstation.appservice.IDoctorStationMainAppService;
import com.openhis.web.doctorstation.dto.PatientInfoDto; import com.openhis.web.doctorstation.dto.PatientInfoDto;
import com.openhis.web.doctorstation.dto.PrescriptionInfoBaseDto; import com.openhis.web.doctorstation.dto.PrescriptionInfoBaseDto;
import com.openhis.web.doctorstation.dto.PrescriptionInfoDetailDto; import com.openhis.web.doctorstation.dto.PrescriptionInfoDetailDto;
@@ -46,6 +44,17 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
@Resource @Resource
IEncounterParticipantService iEncounterParticipantService; IEncounterParticipantService iEncounterParticipantService;
@Resource
IDoctorStationAdviceAppService iDoctorStationAdviceAppService;
@Resource
IDoctorStationEmrAppService iDoctorStationEmrAppService;
@Resource
IDoctorStationDiagnosisAppService iDoctorStationDiagnosisAppService;
@Resource
IDoctorStationChineseMedicalAppService iDoctorStationChineseMedicalAppService;
/** /**
* 查询就诊患者信息 * 查询就诊患者信息
* *
@@ -154,6 +163,45 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
return update > 0 ? R.ok() : R.fail(); return update > 0 ? R.ok() : R.fail();
} }
/**
* 取消接诊
*
* @param encounterId 就诊id
* @return 结果
*/
@Override
public R<?> cancelEncounter(Long encounterId) {
//1.判断是否已经产生业务,如医生已经开有病历、处方、诊断、检验检查或相关项目已收费、执行等,
//如果有则提示:需要医生删除、作废、退费才能【取消接诊】。
//1.1病历
Object emrDetailResult = iDoctorStationEmrAppService.getEmrDetail(encounterId).getData();
//1.2诊断
Object diagnosisResult = iDoctorStationDiagnosisAppService.getEncounterDiagnosis(encounterId).getData();
//1.3处方
Object adviceResult = iDoctorStationAdviceAppService.getRequestBaseInfo(encounterId).getData();
//1.4中医诊断、处方
Map<?,?> tcmDiagnosisResult = (Map<?,?>) iDoctorStationChineseMedicalAppService.getTcmEncounterDiagnosis(encounterId).getData();
Object symptom = tcmDiagnosisResult.get("symptom");
Object illness = tcmDiagnosisResult.get("illness");
Object tcmPrescriptionResult = iDoctorStationChineseMedicalAppService.getTcmRequestBaseInfo(encounterId).getData();
boolean isEmpty = ObjectUtil.isAllEmpty(emrDetailResult, diagnosisResult, adviceResult, symptom,illness, tcmPrescriptionResult);
if (!isEmpty) {
return R.fail("已产生业务数据,无法取消,请删除相关业务数据!");
}
//2.取消接诊,患者重新回到患者队列待诊中
int update = encounterMapper.update(null,
new LambdaUpdateWrapper<Encounter>().eq(Encounter::getId, encounterId)
.set(Encounter::getStatusEnum, EncounterStatus.PLANNED.getValue())
.set(Encounter::getSubjectStatusEnum, EncounterSubjectStatus.TRIAGED.getValue()));
return update > 0 ? R.ok() : R.fail();
}
/** /**
* 查询处方号列表信息 * 查询处方号列表信息
* *

View File

@@ -92,6 +92,17 @@ public class DoctorStationMainController {
return iDoctorStationMainAppService.completeEncounter(encounterId); return iDoctorStationMainAppService.completeEncounter(encounterId);
} }
/**
* 取消接诊
*
* @param encounterId 就诊id
* @return 结果
*/
@GetMapping(value = "/cancel-encounter")
public R<?> cancelEncounter(@RequestParam Long encounterId) {
return iDoctorStationMainAppService.cancelEncounter(encounterId);
}
/** /**
* 查询处方号列表信息 * 查询处方号列表信息
* *

View File

@@ -161,6 +161,27 @@ public class PurchaseInventoryAppServiceImpl implements IPurchaseInventoryAppSer
if (receiptDetailList.isEmpty()) { if (receiptDetailList.isEmpty()) {
return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00006, null)); return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00006, null));
} }
// 获取所有供应商ID
Set<Long> supplierIds = receiptDetailList.stream()
.filter(dto -> dto.getSupplierId() != null)
.map(ReceiptDetailDto::getSupplierId)
.collect(Collectors.toSet());
// 查询供应商信息并设置供应商名称
if (!supplierIds.isEmpty()) {
List<Supplier> suppliers = supplierService.listByIds(supplierIds);
Map<Long, String> supplierNameMap = suppliers.stream()
.collect(Collectors.toMap(Supplier::getId, Supplier::getName));
// 设置供应商名称
receiptDetailList.forEach(dto -> {
if (dto.getSupplierId() != null) {
dto.setSupplierName(supplierNameMap.getOrDefault(dto.getSupplierId(), ""));
}
});
}
return R.ok(receiptDetailList); return R.ok(receiptDetailList);
} }
@@ -212,7 +233,7 @@ public class PurchaseInventoryAppServiceImpl implements IPurchaseInventoryAppSer
// 申请时间 // 申请时间
.setApplyTime(DateUtils.getNowDate()); .setApplyTime(DateUtils.getNowDate());
supplyRequestList.add(supplyRequest); supplyRequestList.add(supplyRequest);
} }
// 保存 // 保存
@@ -224,7 +245,7 @@ public class PurchaseInventoryAppServiceImpl implements IPurchaseInventoryAppSer
List<Long> requestIdList = supplyRequestIdList.stream().map(SupplyRequest::getId).collect(Collectors.toList()); List<Long> requestIdList = supplyRequestIdList.stream().map(SupplyRequest::getId).collect(Collectors.toList());
for (Long list : requestIdList) { for (Long list : requestIdList) {
idList.add(list.toString()); idList.add(list.toString());
} }
// 返回请求id // 返回请求id
return R.ok(idList, null); return R.ok(idList, null);

View File

@@ -128,10 +128,11 @@ public class PatientInformationServiceImpl implements IPatientInformationService
public IPage<PatientInformationDto> getPatientInfo(PatientInfoSearchParam patientInfoSearchParam, String searchKey, public IPage<PatientInformationDto> getPatientInfo(PatientInfoSearchParam patientInfoSearchParam, String searchKey,
Integer pageNo, Integer pageSize, HttpServletRequest request) { Integer pageNo, Integer pageSize, HttpServletRequest request) {
// 构建查询条件 // 构建查询条件 - 添加phone字段到搜索条件中
QueryWrapper<PatientInformationDto> queryWrapper = HisQueryUtils.buildQueryWrapper( QueryWrapper<PatientInformationDto> queryWrapper = HisQueryUtils.buildQueryWrapper(
patientInfoSearchParam, searchKey, new HashSet<>(Arrays.asList(CommonConstants.FieldName.Name, patientInfoSearchParam, searchKey, new HashSet<>(Arrays.asList(CommonConstants.FieldName.Name,
CommonConstants.FieldName.BusNo, CommonConstants.FieldName.PyStr, CommonConstants.FieldName.WbStr)), CommonConstants.FieldName.BusNo, CommonConstants.FieldName.PyStr, CommonConstants.FieldName.WbStr,
CommonConstants.FieldName.Phone)), // 添加phone字段支持手机号搜索
request); request);
IPage<PatientInformationDto> patientInformationPage = IPage<PatientInformationDto> patientInformationPage =

View File

@@ -172,13 +172,13 @@ public class PaymentReconciliationController {
/** /**
* 挂号收费(挂号收费先医保挂号,收费成功后再本系统挂号) * 挂号收费(挂号收费先医保挂号,收费成功后再本系统挂号)
* *
* @param outpatientRegistrationAddParam 挂号信息 * @param outpatientRegistrationSettleParam 挂号信息
* @return 操做结果 * @return 操做结果
*/ */
@PostMapping("/reg-pay") @PostMapping("/reg-pay")
public R<?> regPay(@Valid @RequestBody OutpatientRegistrationSettleParam outpatientRegistrationAddParam) { public R<?> regPay(@Valid @RequestBody OutpatientRegistrationSettleParam outpatientRegistrationSettleParam) {
R<?> result = paymentReconciliationService.regPay(outpatientRegistrationAddParam, R<?> result = paymentReconciliationService.regPay(outpatientRegistrationSettleParam,
outpatientRegistrationAddParam.getChrgBchno(), outpatientRegistrationAddParam.getPaymentDetails()); outpatientRegistrationSettleParam.getChrgBchno(), outpatientRegistrationSettleParam.getPaymentDetails());
// 付款成功后,开具发票 // 付款成功后,开具发票
if (result.getCode() == 200) { if (result.getCode() == 200) {
PaymentReconciliation paymentRecon = null; PaymentReconciliation paymentRecon = null;

View File

@@ -68,6 +68,11 @@ public class InventoryProductReportAppServiceImpl implements IInventoryProductRe
public R<?> getPage(InventoryProductReportSearchParam inventoryProductReportSearchParam, Integer pageNo, public R<?> getPage(InventoryProductReportSearchParam inventoryProductReportSearchParam, Integer pageNo,
Integer pageSize, String searchKey, HttpServletRequest request) { Integer pageSize, String searchKey, HttpServletRequest request) {
// 数据初始化不使用eq条件拼接
// 库存范围
Integer inventoryScope = inventoryProductReportSearchParam.getInventoryScope();
inventoryProductReportSearchParam.setInventoryScope(null);
// 设置模糊查询的字段名 // 设置模糊查询的字段名
HashSet<String> searchFields = new HashSet<>(); HashSet<String> searchFields = new HashSet<>();
searchFields.add(CommonConstants.FieldName.BusNo); searchFields.add(CommonConstants.FieldName.BusNo);
@@ -78,7 +83,8 @@ public class InventoryProductReportAppServiceImpl implements IInventoryProductRe
// 查询库存商品明细分页列表 // 查询库存商品明细分页列表
Page<InventoryProductReportPageDto> productReportPage = inventoryProductReportMapper.selectProductReportPage( Page<InventoryProductReportPageDto> productReportPage = inventoryProductReportMapper.selectProductReportPage(
new Page<>(pageNo, pageSize), queryWrapper, ConditionCode.LOT_NUMBER_COST.getValue().toString()); new Page<>(pageNo, pageSize), queryWrapper, ConditionCode.LOT_NUMBER_COST.getValue().toString(),
inventoryScope);
productReportPage.getRecords().forEach(e -> { productReportPage.getRecords().forEach(e -> {
// 药品类型 // 药品类型
@@ -104,6 +110,11 @@ public class InventoryProductReportAppServiceImpl implements IInventoryProductRe
Integer pageSize, String searchKey, HttpServletRequest request, HttpServletResponse response) { Integer pageSize, String searchKey, HttpServletRequest request, HttpServletResponse response) {
pageNo = 1; pageNo = 1;
pageSize = 10000; pageSize = 10000;
// 数据初始化不使用eq条件拼接
// 库存范围
Integer inventoryScope = inventoryProductReportSearchParam.getInventoryScope();
inventoryProductReportSearchParam.setInventoryScope(null);
// 设置模糊查询的字段名 // 设置模糊查询的字段名
HashSet<String> searchFields = new HashSet<>(); HashSet<String> searchFields = new HashSet<>();
searchFields.add(CommonConstants.FieldName.BusNo); searchFields.add(CommonConstants.FieldName.BusNo);
@@ -114,7 +125,8 @@ public class InventoryProductReportAppServiceImpl implements IInventoryProductRe
// 查询库存商品明细分页列表 // 查询库存商品明细分页列表
Page<InventoryProductReportPageDto> productReportPage = inventoryProductReportMapper.selectProductReportPage( Page<InventoryProductReportPageDto> productReportPage = inventoryProductReportMapper.selectProductReportPage(
new Page<>(pageNo, pageSize), queryWrapper, ConditionCode.LOT_NUMBER_COST.getValue().toString()); new Page<>(pageNo, pageSize), queryWrapper, ConditionCode.LOT_NUMBER_COST.getValue().toString(),
inventoryScope);
productReportPage.getRecords().forEach(e -> { productReportPage.getRecords().forEach(e -> {
// 药品类型 // 药品类型

View File

@@ -38,4 +38,10 @@ public class InventoryProductReportSearchParam {
/** 供应商 */ /** 供应商 */
private Long supplierId; private Long supplierId;
/** 厂家/产地(供应商名称) */
private String manufacturerText;
/** 仓库ID药房ID */
private Long purposeLocationId;
} }

View File

@@ -27,9 +27,11 @@ public interface InventoryProductReportMapper {
* @param page 分页 * @param page 分页
* @param queryWrapper 查询条件 * @param queryWrapper 查询条件
* @param lotNumber 命中条件枚举类型:产品批号 * @param lotNumber 命中条件枚举类型:产品批号
* @param inventoryScope 库存范围无限制1、数量等于02、数量大于03、数量小于等于204、数量小于等于505
* @return 库存商品明细 * @return 库存商品明细
*/ */
Page<InventoryProductReportPageDto> selectProductReportPage(@Param("page") Page<InventoryProductReportPageDto> page, Page<InventoryProductReportPageDto> selectProductReportPage(@Param("page") Page<InventoryProductReportPageDto> page,
@Param(Constants.WRAPPER) QueryWrapper<InventoryProductReportSearchParam> queryWrapper, @Param(Constants.WRAPPER) QueryWrapper<InventoryProductReportSearchParam> queryWrapper,
@Param("lotNumber") String lotNumber); @Param("lotNumber") String lotNumber,
@Param("inventoryScope") Integer inventoryScope);
} }

View File

@@ -6,9 +6,9 @@ spring:
druid: druid:
# 主库数据源 # 主库数据源
master: master:
url: jdbc:postgresql://localhost:5432/openhis?currentSchema=public&characterEncoding=UTF-8&client_encoding=UTF-8 url: jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=public&characterEncoding=UTF-8&client_encoding=UTF-8
username: postgres username: postgresql
password: root password: Jchl1528
# 从库数据源 # 从库数据源
slave: slave:
# 从数据源开关/默认关闭 # 从数据源开关/默认关闭
@@ -62,13 +62,13 @@ spring:
# redis 配置 # redis 配置
redis: redis:
# 地址 # 地址
host: 127.0.0.1 host: 192.168.110.252
# 端口默认为6379 # 端口默认为6379
port: 6379 port: 6379
# 数据库索引 # 数据库索引
database: 1 database: 1
# 密码 # 密码
password: redis password: Jchl1528
# 连接超时时间 # 连接超时时间
timeout: 10s timeout: 10s
lettuce: lettuce:

View File

@@ -74,12 +74,14 @@
T2."name" AS item_name, T2."name" AS item_name,
T2.id AS item_id, T2.id AS item_id,
T2.part_percent, T2.part_percent,
T2.manufacturer_text AS supplier_name, T2.manufacturer_text AS manufacturer_text,
T3.total_volume, T3.total_volume,
T5."name" AS practitioner_name, T5."name" AS practitioner_name,
T6."name" AS purpose_location_name, T6."name" AS purpose_location_name,
T6."id" AS purpose_location_id, T6."id" AS purpose_location_id,
T7."name" AS purpose_location_store_name, T7."name" AS purpose_location_store_name,
T1.supplier_id AS supplierId,
T10."name" AS supplier_name,
T1.occurrence_time, T1.occurrence_time,
(SELECT SUM(T9.quantity) (SELECT SUM(T9.quantity)
FROM wor_inventory_item T9 FROM wor_inventory_item T9
@@ -100,6 +102,8 @@
LEFT JOIN wor_inventory_item T9 LEFT JOIN wor_inventory_item T9
ON T1.item_id = T9.item_id ON T1.item_id = T9.item_id
AND T1.purpose_location_id = T9.location_id AND T1.purpose_location_id = T9.location_id
LEFT JOIN adm_supplier T10
ON T1.supplier_id = T10.id
WHERE T1.bus_no = #{busNo} WHERE T1.bus_no = #{busNo}
AND T1.delete_flag = '0' AND T1.delete_flag = '0'
UNION UNION
@@ -121,12 +125,14 @@
T8."name" AS item_name, T8."name" AS item_name,
T8.id AS item_id, T8.id AS item_id,
T8.part_percent, T8.part_percent,
T8.manufacturer_text AS supplier_name, T8.manufacturer_text AS manufacturer_text,
T8."size" AS total_volume, T8."size" AS total_volume,
T5."name" AS practitioner_name, T5."name" AS practitioner_name,
T6."name" AS purpose_location_name, T6."name" AS purpose_location_name,
T6."id" AS purpose_location_id, T6."id" AS purpose_location_id,
T7."name" AS purpose_location_store_name, T7."name" AS purpose_location_store_name,
T1.supplier_id AS supplierId,
T10."name" AS supplier_name,
T1.occurrence_time, T1.occurrence_time,
(SELECT SUM(T9.quantity) (SELECT SUM(T9.quantity)
FROM wor_inventory_item T9 FROM wor_inventory_item T9
@@ -145,6 +151,8 @@
LEFT JOIN wor_inventory_item T9 LEFT JOIN wor_inventory_item T9
ON T1.item_id = T9.item_id ON T1.item_id = T9.item_id
AND T1.purpose_location_id = T9.location_id AND T1.purpose_location_id = T9.location_id
LEFT JOIN adm_supplier T10
ON T1.supplier_id = T10.id
WHERE T1.bus_no = #{busNo} WHERE T1.bus_no = #{busNo}
AND T1.delete_flag = '0' AND T1.delete_flag = '0'
</select> </select>
@@ -167,12 +175,14 @@
T2."name" AS item_name, T2."name" AS item_name,
T2.id AS item_id, T2.id AS item_id,
T2.part_percent, T2.part_percent,
T2.manufacturer_text AS supplier_name, T2.manufacturer_text AS manufacturer_text,
T3.total_volume, T3.total_volume,
T5."name" AS practitioner_name, T5."name" AS practitioner_name,
T6."name" AS purpose_location_name, T6."name" AS purpose_location_name,
T6."id" AS purpose_location_id, T6."id" AS purpose_location_id,
T7."name" AS purpose_location_store_name, T7."name" AS purpose_location_store_name,
T1.supplier_id AS supplierId,
T10."name" AS supplier_name,
(SELECT SUM(T9.quantity) (SELECT SUM(T9.quantity)
FROM wor_inventory_item T9 FROM wor_inventory_item T9
WHERE T9.item_id = T1.item_id WHERE T9.item_id = T1.item_id
@@ -192,6 +202,8 @@
LEFT JOIN wor_inventory_item T9 LEFT JOIN wor_inventory_item T9
ON T1.item_id = T9.item_id ON T1.item_id = T9.item_id
AND T1.purpose_location_id = T9.location_id AND T1.purpose_location_id = T9.location_id
LEFT JOIN adm_supplier T10
ON T1.supplier_id = T10.id
WHERE T1.purpose_location_id = #{locationId} WHERE T1.purpose_location_id = #{locationId}
AND T1.status_enum = #{statusEnum} AND T1.status_enum = #{statusEnum}
AND T1.type_enum IN AND T1.type_enum IN
@@ -220,12 +232,14 @@
T8."name" AS item_name, T8."name" AS item_name,
T8.id AS item_id, T8.id AS item_id,
T8.part_percent, T8.part_percent,
T8.manufacturer_text AS supplier_name, T8.manufacturer_text AS manufacturer_text,
T8."size" AS total_volume, T8."size" AS total_volume,
T5."name" AS practitioner_name, T5."name" AS practitioner_name,
T6."name" AS purpose_location_name, T6."name" AS purpose_location_name,
T6."id" AS purpose_location_id, T6."id" AS purpose_location_id,
T7."name" AS purpose_location_store_name, T7."name" AS purpose_location_store_name,
T1.supplier_id AS supplierId,
T10."name" AS supplier_name,
(SELECT SUM(T9.quantity) (SELECT SUM(T9.quantity)
FROM wor_inventory_item T9 FROM wor_inventory_item T9
WHERE T9.item_id = T1.item_id WHERE T9.item_id = T1.item_id
@@ -243,6 +257,8 @@
LEFT JOIN wor_inventory_item T9 LEFT JOIN wor_inventory_item T9
ON T1.item_id = T9.item_id ON T1.item_id = T9.item_id
AND T1.purpose_location_id = T9.location_id AND T1.purpose_location_id = T9.location_id
LEFT JOIN adm_supplier T10
ON T1.supplier_id = T10.id
WHERE T1.purpose_location_id = #{locationId} WHERE T1.purpose_location_id = #{locationId}
AND T1.status_enum = #{statusEnum} AND T1.status_enum = #{statusEnum}
AND T1.type_enum IN AND T1.type_enum IN

View File

@@ -41,188 +41,366 @@
abi.restricted_flag, abi.restricted_flag,
abi.restricted_scope abi.restricted_scope
from ( from (
<if test="adviceTypes == null or adviceTypes.contains(1)"> <!-- 改进SQL逻辑确保所有情况都能正确处理避免空的FROM子查询 -->
SELECT T1.tenant_id, <!-- 当adviceTypes包含4或不在1-3范围内时或者adviceTypes为空/null时查询所有类型 -->
1 AS advice_type, <choose>
T1.category_code AS category_code, <!-- 检查adviceTypes是否为null、空列表或者包含4、5或其他不在1-3范围内的值 -->
T1.pharmacology_category_code AS pharmacology_category_code, <when test="adviceTypes == null or adviceTypes.isEmpty() or adviceTypes.contains(4) or adviceTypes.contains(5)">
T1.part_percent AS part_percent, <!-- 查询所有类型 -->
T1.unit_conversion_ratio AS unit_conversion_ratio, SELECT T1.tenant_id,
T1.part_attribute_enum AS part_attribute_enum, 1 AS advice_type,
T1.tho_part_attribute_enum AS tho_part_attribute_enum, T1.category_code AS category_code,
T1.skin_test_flag AS skin_test_flag, T1.pharmacology_category_code AS pharmacology_category_code,
T1.inject_flag AS inject_flag, T1.part_percent AS part_percent,
T1.ID AS advice_definition_id, T1.unit_conversion_ratio AS unit_conversion_ratio,
T1.NAME AS advice_name, T1.part_attribute_enum AS part_attribute_enum,
T1.bus_no AS advice_bus_no, T1.tho_part_attribute_enum AS tho_part_attribute_enum,
T1.py_str AS py_str, T1.skin_test_flag AS skin_test_flag,
T1.wb_str AS wb_str, T1.inject_flag AS inject_flag,
T1.yb_no AS yb_no, T1.ID AS advice_definition_id,
T1.merchandise_name AS product_name, T1.NAME AS advice_name,
0 AS activity_type, T1.bus_no AS advice_bus_no,
T1.unit_code AS unit_code, T1.py_str AS py_str,
T1.min_unit_code AS min_unit_code, T1.wb_str AS wb_str,
T2.total_volume AS volume, T1.yb_no AS yb_no,
T2.method_code AS method_code, T1.merchandise_name AS product_name,
T2.rate_code AS rate_code, 0 AS activity_type,
T2.org_id AS org_id, T1.unit_code AS unit_code,
T2.location_id AS location_id, T1.min_unit_code AS min_unit_code,
CAST(T2.dose AS TEXT) AS dose, T2.total_volume AS volume,
T2.dose_unit_code AS dose_unit_code, T2.method_code AS method_code,
T3.NAME AS supplier, T2.rate_code AS rate_code,
T3.id AS supplier_id, T2.org_id AS org_id,
T1.manufacturer_text AS manufacturer, T2.location_id AS location_id,
T5.id AS charge_item_definition_id, CAST(T2.dose AS TEXT) AS dose,
T5.instance_table AS advice_table_name, T2.dose_unit_code AS dose_unit_code,
T6.def_location_id AS position_id, T3.NAME AS supplier,
t1.restricted_flag AS restricted_flag, T3.id AS supplier_id,
t1.restricted_scope AS restricted_scope T1.manufacturer_text AS manufacturer,
FROM med_medication_definition AS t1 T5.id AS charge_item_definition_id,
INNER JOIN med_medication AS T2 ON T2.medication_def_id = T1.ID T5.instance_table AS advice_table_name,
AND T2.delete_flag = '0' AND T2.status_enum = #{statusEnum} T6.def_location_id AS position_id,
LEFT JOIN adm_supplier AS T3 t1.restricted_flag AS restricted_flag,
ON T3.ID = T1.supply_id t1.restricted_scope AS restricted_scope
AND T3.delete_flag = '0' FROM med_medication_definition AS t1
LEFT JOIN adm_charge_item_definition AS T5 ON T5.instance_id = T1.ID INNER JOIN med_medication AS T2 ON T2.medication_def_id = T1.ID
AND T5.delete_flag = '0' AND T2.delete_flag = '0' AND T2.status_enum = #{statusEnum}
LEFT JOIN adm_organization_location AS T6 LEFT JOIN adm_supplier AS T3
ON T6.distribution_category_code = T1.category_code ON T3.ID = T1.supply_id
AND T6.delete_flag = '0' AND T6.organization_id = #{organizationId} AND AND T3.delete_flag = '0'
(CURRENT_TIME :: time (6) BETWEEN T6.start_time AND T6.end_time) LEFT JOIN adm_charge_item_definition AS T5 ON T5.instance_id = T1.ID
WHERE T1.delete_flag = '0' AND T5.delete_flag = '0'
AND T2.status_enum = #{statusEnum} LEFT JOIN adm_organization_location AS T6
<if test="pricingFlag ==1"> ON T6.distribution_category_code = T1.category_code
AND 1 = 2 AND T6.delete_flag = '0' AND T6.organization_id = #{organizationId} AND
</if> (CURRENT_TIME :: time (6) BETWEEN T6.start_time AND T6.end_time)
<if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()"> WHERE T1.delete_flag = '0'
AND T1.id IN AND T2.status_enum = #{statusEnum}
<foreach collection="adviceDefinitionIdParamList" item="itemId" open="(" separator="," close=")"> <if test="pricingFlag ==1">
#{itemId} AND 1 = 2
</foreach> </if>
</if> <if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()">
AND T5.instance_table = #{medicationTableName} AND T1.id IN
</if> <foreach collection="adviceDefinitionIdParamList" item="itemId" open="(" separator="," close=")">
#{itemId}
</foreach>
</if>
AND T5.instance_table = #{medicationTableName}
UNION ALL
SELECT T1.tenant_id,
2 AS advice_type,
T1.category_code AS category_code,
'' AS pharmacology_category_code,
T1.part_percent AS part_percent,
0 AS unit_conversion_ratio,
null AS part_attribute_enum,
null AS tho_part_attribute_enum,
null AS skin_test_flag,
null AS inject_flag,
T1.ID AS advice_definition_id,
T1.NAME AS advice_name,
T1.bus_no AS advice_bus_no,
T1.py_str AS py_str,
T1.wb_str AS wb_str,
T1.yb_no AS yb_no,
'' AS product_name,
0 AS activity_type,
T1.unit_code AS unit_code,
T1.min_unit_code AS min_unit_code,
T1.SIZE AS volume,
'' AS method_code,
'' AS rate_code,
T1.org_id AS org_id,
T1.location_id AS location_id,
'' AS dose,
'' AS dose_unit_code,
T2.NAME AS supplier,
T2.id AS supplier_id,
T1.manufacturer_text AS manufacturer,
T4.id AS charge_item_definition_id,
T4.instance_table AS advice_table_name,
T5.def_location_id AS position_id,
0 AS restricted_flag,
'' AS restricted_scope
FROM adm_device_definition AS T1
LEFT JOIN adm_supplier AS T2
ON T2.ID = T1.supply_id
AND T2.delete_flag = '0'
LEFT JOIN adm_charge_item_definition AS T4 ON T4.instance_id = T1.ID
AND T4.delete_flag = '0'
LEFT JOIN adm_organization_location AS T5 ON T5.distribution_category_code = T1.category_code
AND T5.delete_flag = '0' AND T5.organization_id = #{organizationId} AND
(CURRENT_TIME :: time (6) BETWEEN T5.start_time AND T5.end_time)
WHERE T1.delete_flag = '0'
<if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()">
AND T1.id IN
<foreach collection="adviceDefinitionIdParamList" item="itemId" open="(" separator="," close=")">
#{itemId}
</foreach>
</if>
AND T4.instance_table = #{deviceTableName}
AND T1.category_code = #{singleUse}
AND T1.status_enum = #{statusEnum}
UNION ALL
SELECT T1.tenant_id,
3 AS advice_type,
T1.category_code AS category_code,
'' AS pharmacology_category_code,
1 AS part_percent,
0 AS unit_conversion_ratio,
null AS part_attribute_enum,
null AS tho_part_attribute_enum,
null AS skin_test_flag,
null AS inject_flag,
T1.ID AS advice_definition_id,
T1.NAME AS advice_name,
T1.bus_no AS advice_bus_no,
T1.py_str AS py_str,
T1.wb_str AS wb_str,
T1.yb_no AS yb_no,
'' AS product_name,
T1.type_enum AS activity_type,
'' AS unit_code,
'' AS min_unit_code,
'' AS volume,
'' AS method_code,
'' AS rate_code,
T1.org_id AS org_id,
T1.location_id AS location_id,
'' AS dose,
'' AS dose_unit_code,
'' AS supplier,
null AS supplier_id,
'' AS manufacturer,
T2.ID AS charge_item_definition_id,
T2.instance_table AS advice_table_name,
COALESCE(T3.organization_id, T1.org_id) AS position_id,
0 AS restricted_flag,
'' AS restricted_scope
FROM wor_activity_definition AS T1
LEFT JOIN adm_charge_item_definition AS T2
ON T2.instance_id = T1.ID
AND T2.delete_flag = '0'
LEFT JOIN adm_organization_location AS T3 ON T3.activity_definition_id = T1.ID
AND T3.delete_flag = '0' AND (CURRENT_TIME :: time (6) BETWEEN T3.start_time AND T3.end_time)
WHERE T1.delete_flag = '0'
<if test="pricingFlag ==1">
AND T1.pricing_flag = #{pricingFlag}
</if>
<if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()">
AND T1.id IN
<foreach collection="adviceDefinitionIdParamList" item="itemId" open="(" separator="," close=")">
#{itemId}
</foreach>
</if>
AND T1.status_enum = #{statusEnum}
AND T2.instance_table = #{activityTableName}
</when>
<otherwise>
<!-- 当adviceTypes不为null且不为空且只包含1、2、3时根据选中的类型查询 -->
<!-- 确保至少有一个类型被选中,避免空查询 -->
<if test="adviceTypes.contains(1) or adviceTypes.contains(2) or adviceTypes.contains(3)">
<if test="adviceTypes.contains(1)">
SELECT T1.tenant_id,
1 AS advice_type,
T1.category_code AS category_code,
T1.pharmacology_category_code AS pharmacology_category_code,
T1.part_percent AS part_percent,
T1.unit_conversion_ratio AS unit_conversion_ratio,
T1.part_attribute_enum AS part_attribute_enum,
T1.tho_part_attribute_enum AS tho_part_attribute_enum,
T1.skin_test_flag AS skin_test_flag,
T1.inject_flag AS inject_flag,
T1.ID AS advice_definition_id,
T1.NAME AS advice_name,
T1.bus_no AS advice_bus_no,
T1.py_str AS py_str,
T1.wb_str AS wb_str,
T1.yb_no AS yb_no,
T1.merchandise_name AS product_name,
0 AS activity_type,
T1.unit_code AS unit_code,
T1.min_unit_code AS min_unit_code,
T2.total_volume AS volume,
T2.method_code AS method_code,
T2.rate_code AS rate_code,
T2.org_id AS org_id,
T2.location_id AS location_id,
CAST(T2.dose AS TEXT) AS dose,
T2.dose_unit_code AS dose_unit_code,
T3.NAME AS supplier,
T3.id AS supplier_id,
T1.manufacturer_text AS manufacturer,
T5.id AS charge_item_definition_id,
T5.instance_table AS advice_table_name,
T6.def_location_id AS position_id,
t1.restricted_flag AS restricted_flag,
t1.restricted_scope AS restricted_scope
FROM med_medication_definition AS t1
INNER JOIN med_medication AS T2 ON T2.medication_def_id = T1.ID
AND T2.delete_flag = '0' AND T2.status_enum = #{statusEnum}
LEFT JOIN adm_supplier AS T3
ON T3.ID = T1.supply_id
AND T3.delete_flag = '0'
LEFT JOIN adm_charge_item_definition AS T5 ON T5.instance_id = T1.ID
AND T5.delete_flag = '0'
LEFT JOIN adm_organization_location AS T6
ON T6.distribution_category_code = T1.category_code
AND T6.delete_flag = '0' AND T6.organization_id = #{organizationId} AND
(CURRENT_TIME :: time (6) BETWEEN T6.start_time AND T6.end_time)
WHERE T1.delete_flag = '0'
AND T2.status_enum = #{statusEnum}
<if test="pricingFlag ==1">
AND 1 = 2
</if>
<if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()">
AND T1.id IN
<foreach collection="adviceDefinitionIdParamList" item="itemId" open="(" separator="," close=")">
#{itemId}
</foreach>
</if>
AND T5.instance_table = #{medicationTableName}
</if>
<if test="adviceTypes == null or adviceTypes.contains(1)"> <if test="adviceTypes.contains(1) and (adviceTypes.contains(2) or adviceTypes.contains(3))">UNION ALL</if>
<if test="adviceTypes == null or adviceTypes.contains(2) or adviceTypes.contains(3)">UNION ALL</if>
</if>
<if test="adviceTypes == null or adviceTypes.contains(2)"> <if test="adviceTypes.contains(2)">
SELECT T1.tenant_id, SELECT T1.tenant_id,
2 AS advice_type, 2 AS advice_type,
T1.category_code AS category_code, T1.category_code AS category_code,
'' AS pharmacology_category_code, '' AS pharmacology_category_code,
T1.part_percent AS part_percent, T1.part_percent AS part_percent,
0 AS unit_conversion_ratio, 0 AS unit_conversion_ratio,
null AS part_attribute_enum, null AS part_attribute_enum,
null AS tho_part_attribute_enum, null AS tho_part_attribute_enum,
null AS skin_test_flag, null AS skin_test_flag,
null AS inject_flag, null AS inject_flag,
T1.ID AS advice_definition_id, T1.ID AS advice_definition_id,
T1.NAME AS advice_name, T1.NAME AS advice_name,
T1.bus_no AS advice_bus_no, T1.bus_no AS advice_bus_no,
T1.py_str AS py_str, T1.py_str AS py_str,
T1.wb_str AS wb_str, T1.wb_str AS wb_str,
T1.yb_no AS yb_no, T1.yb_no AS yb_no,
'' AS product_name, '' AS product_name,
0 AS activity_type, 0 AS activity_type,
T1.unit_code AS unit_code, T1.unit_code AS unit_code,
T1.min_unit_code AS min_unit_code, T1.min_unit_code AS min_unit_code,
T1.SIZE AS volume, T1.SIZE AS volume,
'' AS method_code, '' AS method_code,
'' AS rate_code, '' AS rate_code,
T1.org_id AS org_id, T1.org_id AS org_id,
T1.location_id AS location_id, T1.location_id AS location_id,
'' AS dose, '' AS dose,
'' AS dose_unit_code, '' AS dose_unit_code,
T2.NAME AS supplier, T2.NAME AS supplier,
T2.id AS supplier_id, T2.id AS supplier_id,
T1.manufacturer_text AS manufacturer, T1.manufacturer_text AS manufacturer,
T4.id AS charge_item_definition_id, T4.id AS charge_item_definition_id,
T4.instance_table AS advice_table_name, T4.instance_table AS advice_table_name,
T5.def_location_id AS position_id, T5.def_location_id AS position_id,
0 AS restricted_flag, 0 AS restricted_flag,
'' AS restricted_scope '' AS restricted_scope
FROM adm_device_definition AS T1 FROM adm_device_definition AS T1
LEFT JOIN adm_supplier AS T2 LEFT JOIN adm_supplier AS T2
ON T2.ID = T1.supply_id ON T2.ID = T1.supply_id
AND T2.delete_flag = '0' AND T2.delete_flag = '0'
LEFT JOIN adm_charge_item_definition AS T4 ON T4.instance_id = T1.ID LEFT JOIN adm_charge_item_definition AS T4 ON T4.instance_id = T1.ID
AND T4.delete_flag = '0' AND T4.delete_flag = '0'
LEFT JOIN adm_organization_location AS T5 ON T5.distribution_category_code = T1.category_code LEFT JOIN adm_organization_location AS T5 ON T5.distribution_category_code = T1.category_code
AND T5.delete_flag = '0' AND T5.organization_id = #{organizationId} AND AND T5.delete_flag = '0' AND T5.organization_id = #{organizationId} AND
(CURRENT_TIME :: time (6) BETWEEN T5.start_time AND T5.end_time) (CURRENT_TIME :: time (6) BETWEEN T5.start_time AND T5.end_time)
WHERE T1.delete_flag = '0' WHERE T1.delete_flag = '0'
<if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()"> <if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()">
AND T1.id IN AND T1.id IN
<foreach collection="adviceDefinitionIdParamList" item="itemId" open="(" separator="," close=")"> <foreach collection="adviceDefinitionIdParamList" item="itemId" open="(" separator="," close=")">
#{itemId} #{itemId}
</foreach> </foreach>
</if> </if>
AND T4.instance_table = #{deviceTableName} AND T4.instance_table = #{deviceTableName}
AND T1.category_code = #{singleUse} AND T1.category_code = #{singleUse}
AND T1.status_enum = #{statusEnum} AND T1.status_enum = #{statusEnum}
</if> </if>
<if test="adviceTypes == null or adviceTypes.contains(2)"> <if test="adviceTypes.contains(2) and adviceTypes.contains(3)">UNION ALL</if>
<if test="adviceTypes == null or adviceTypes.contains(3)">UNION ALL</if>
</if>
<if test="adviceTypes == null or adviceTypes.contains(3)"> <if test="adviceTypes.contains(3)">
SELECT T1.tenant_id, SELECT T1.tenant_id,
3 AS advice_type, 3 AS advice_type,
T1.category_code AS category_code, T1.category_code AS category_code,
'' AS pharmacology_category_code, '' AS pharmacology_category_code,
1 AS part_percent, 1 AS part_percent,
0 AS unit_conversion_ratio, 0 AS unit_conversion_ratio,
null AS part_attribute_enum, null AS part_attribute_enum,
null AS tho_part_attribute_enum, null AS tho_part_attribute_enum,
null AS skin_test_flag, null AS skin_test_flag,
null AS inject_flag, null AS inject_flag,
T1.ID AS advice_definition_id, T1.ID AS advice_definition_id,
T1.NAME AS advice_name, T1.NAME AS advice_name,
T1.bus_no AS advice_bus_no, T1.bus_no AS advice_bus_no,
T1.py_str AS py_str, T1.py_str AS py_str,
T1.wb_str AS wb_str, T1.wb_str AS wb_str,
T1.yb_no AS yb_no, T1.yb_no AS yb_no,
'' AS product_name, '' AS product_name,
T1.type_enum AS activity_type, T1.type_enum AS activity_type,
'' AS unit_code, '' AS unit_code,
'' AS min_unit_code, '' AS min_unit_code,
'' AS volume, '' AS volume,
'' AS method_code, '' AS method_code,
'' AS rate_code, '' AS rate_code,
T1.org_id AS org_id, T1.org_id AS org_id,
T1.location_id AS location_id, T1.location_id AS location_id,
'' AS dose, '' AS dose,
'' AS dose_unit_code, '' AS dose_unit_code,
'' AS supplier, '' AS supplier,
null AS supplier_id, null AS supplier_id,
'' AS manufacturer, '' AS manufacturer,
T2.ID AS charge_item_definition_id, T2.ID AS charge_item_definition_id,
T2.instance_table AS advice_table_name, T2.instance_table AS advice_table_name,
T3.organization_id AS position_id, COALESCE(T3.organization_id, T1.org_id) AS position_id,
0 AS restricted_flag, 0 AS restricted_flag,
'' AS restricted_scope '' AS restricted_scope
FROM wor_activity_definition AS T1 FROM wor_activity_definition AS T1
LEFT JOIN adm_charge_item_definition AS T2 LEFT JOIN adm_charge_item_definition AS T2
ON T2.instance_id = T1.ID ON T2.instance_id = T1.ID
AND T2.delete_flag = '0' AND T2.delete_flag = '0'
LEFT JOIN adm_organization_location AS T3 ON T3.activity_definition_id = T1.ID LEFT JOIN adm_organization_location AS T3 ON T3.activity_definition_id = T1.ID
AND T3.delete_flag = '0' AND (CURRENT_TIME :: time (6) BETWEEN T3.start_time AND T3.end_time) AND T3.delete_flag = '0' AND (CURRENT_TIME :: time (6) BETWEEN T3.start_time AND T3.end_time)
WHERE T1.delete_flag = '0' WHERE T1.delete_flag = '0'
<if test="pricingFlag ==1"> <if test="pricingFlag ==1">
AND T1.pricing_flag = #{pricingFlag} AND T1.pricing_flag = #{pricingFlag}
</if> </if>
<if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()"> <if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()">
AND T1.id IN AND T1.id IN
<foreach collection="adviceDefinitionIdParamList" item="itemId" open="(" separator="," close=")"> <foreach collection="adviceDefinitionIdParamList" item="itemId" open="(" separator="," close=")">
#{itemId} #{itemId}
</foreach> </foreach>
</if> </if>
AND T1.status_enum = #{statusEnum} AND T1.status_enum = #{statusEnum}
AND T2.instance_table = #{activityTableName} AND T2.instance_table = #{activityTableName}
</if> </if>
</if>
</otherwise>
</choose>
) AS abi ) AS abi
${ew.customSqlSegment} ${ew.customSqlSegment}
</select> </select>

View File

@@ -4,7 +4,9 @@
<select id="selectProductReportPage" <select id="selectProductReportPage"
resultType="com.openhis.web.reportmanage.dto.InventoryProductReportPageDto"> resultType="com.openhis.web.reportmanage.dto.InventoryProductReportPageDto">
SELECT T8.id, --ID SELECT *
FROM (
SELECT T8.id, --ID
T8.bus_no, --药品编码 T8.bus_no, --药品编码
T8.name, --药品名称 T8.name, --药品名称
T8.lot_number, --批次号 T8.lot_number, --批次号
@@ -23,9 +25,11 @@
T8.purpose_type_enum, --仓库类型 T8.purpose_type_enum, --仓库类型
T8.location_name, --仓库名称 T8.location_name, --仓库名称
T8.location_store_name, --货位名称 T8.location_store_name, --货位名称
T8.purpose_location_id, --仓库ID
T8.expiration_date, --有效期 T8.expiration_date, --有效期
T8.yb_no, --医保编码 T8.yb_no, --医保编码
T8.tenant_id -- 租户ID T8.tenant_id, -- 租户ID
CAST(T8.item_table AS INTEGER) AS category_type -- 项目类型(按分类编码,整型)
FROM (SELECT T1.id, --ID FROM (SELECT T1.id, --ID
T2.bus_no, --药品编码 T2.bus_no, --药品编码
T2.name, --药品名称 T2.name, --药品名称
@@ -46,6 +50,7 @@
T6.form_enum AS purpose_type_enum, --仓库类型 T6.form_enum AS purpose_type_enum, --仓库类型
T6.name AS location_name, --仓库名称 T6.name AS location_name, --仓库名称
T7.name AS location_store_name, --货位名称 T7.name AS location_store_name, --货位名称
T1.location_id AS purpose_location_id, --仓库ID
T1.expiration_date, --有效期 T1.expiration_date, --有效期
T2.yb_no, --医保编码 T2.yb_no, --医保编码
T1.tenant_id -- 租户ID T1.tenant_id -- 租户ID
@@ -86,9 +91,11 @@
T10.purpose_type_enum, --仓库类型 T10.purpose_type_enum, --仓库类型
T10.location_name, --仓库名称 T10.location_name, --仓库名称
T10.location_store_name, --货位名称 T10.location_store_name, --货位名称
T10.purpose_location_id, --仓库ID
T10.expiration_date, --有效期 T10.expiration_date, --有效期
T10.yb_no, --医保编码 T10.yb_no, --医保编码
T10.tenant_id -- 租户ID T10.tenant_id, -- 租户ID
CAST(T10.item_table AS INTEGER) AS category_type -- 项目类型(按分类编码,整型)
FROM (SELECT T1.id, --ID FROM (SELECT T1.id, --ID
T9.bus_no, --药品编码 T9.bus_no, --药品编码
T9.name, --药品名称 T9.name, --药品名称
@@ -109,6 +116,7 @@
T6.form_enum AS purpose_type_enum, --仓库类型 T6.form_enum AS purpose_type_enum, --仓库类型
T6.name AS location_name, --仓库名称 T6.name AS location_name, --仓库名称
T7.name AS location_store_name, --货位名称 T7.name AS location_store_name, --货位名称
T1.location_id AS purpose_location_id, --仓库ID
T1.expiration_date, --有效期 T1.expiration_date, --有效期
T9.yb_no, --医保编码 T9.yb_no, --医保编码
T1.tenant_id -- 租户ID T1.tenant_id -- 租户ID
@@ -128,7 +136,24 @@
LEFT JOIN adm_location T7 LEFT JOIN adm_location T7
ON T1.location_store_id = T7.id ON T1.location_store_id = T7.id
AND T7.delete_flag = '0' AND T7.delete_flag = '0'
WHERE T1.delete_flag = '0') AS T10 WHERE T1.delete_flag = '0') AS T10
) AS T
${ew.customSqlSegment} ${ew.customSqlSegment}
<if test="inventoryScope != null">
<choose>
<when test="inventoryScope == 2">
AND item_quantity = 0
</when>
<when test="inventoryScope == 3">
AND item_quantity > 0
</when>
<when test="inventoryScope == 4">
AND item_quantity <![CDATA[ <= ]]> 20
</when>
<when test="inventoryScope == 5">
AND item_quantity <![CDATA[ <= ]]> 50
</when>
</choose>
</if>
</select> </select>
</mapper> </mapper>

View File

@@ -142,6 +142,11 @@ public class CommonConstants {
*/ */
public interface FieldName { public interface FieldName {
/**
* 手机号
*/
String Phone = "phone";
/** /**
* 单据号 * 单据号
*/ */

View File

@@ -39,7 +39,7 @@ public enum LocationForm implements HisEnumInterface {
PHARMACY(16, "ph", "药房"), PHARMACY(16, "ph", "药房"),
WAREHOUSE (17, "wa", ""), WAREHOUSE (17, "wa", "耗材"),
DEPARTMENT (18, "de", "科室"); DEPARTMENT (18, "de", "科室");

View File

@@ -63,7 +63,7 @@ public class LocationServiceImpl extends ServiceImpl<LocationMapper, Location> i
@Override @Override
public List<Location> getPharmacyCabinetList() { public List<Location> getPharmacyCabinetList() {
return baseMapper.selectList(new LambdaQueryWrapper<Location>().in(Location::getFormEnum, return baseMapper.selectList(new LambdaQueryWrapper<Location>().in(Location::getFormEnum,
LocationForm.CABINET.getValue(), LocationForm.PHARMACY.getValue())); LocationForm.CABINET.getValue(), LocationForm.PHARMACY.getValue(), LocationForm.WAREHOUSE.getValue()));
} }
/** /**

View File

@@ -45,9 +45,13 @@ public class ConditionDefinitionServiceImpl extends ServiceImpl<ConditionDefinit
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean addDisease(ConditionDefinition conditionDefinition) { public boolean addDisease(ConditionDefinition conditionDefinition) {
// 根据病种编码判断病种是否存在 // 根据病种编码判断病种是否存在
// List<ConditionDefinition> conditionDefinitions =
// conditionDefinitionMapper.selectList(new LambdaQueryWrapper<ConditionDefinition>()
// .eq(ConditionDefinition::getConditionCode, conditionDefinition.getConditionCode()));
// 根据医保编码判断病种是否存在
List<ConditionDefinition> conditionDefinitions = List<ConditionDefinition> conditionDefinitions =
conditionDefinitionMapper.selectList(new LambdaQueryWrapper<ConditionDefinition>() conditionDefinitionMapper.selectList(new LambdaQueryWrapper<ConditionDefinition>()
.eq(ConditionDefinition::getConditionCode, conditionDefinition.getConditionCode())); .eq(ConditionDefinition::getYbNo,conditionDefinition.getYbNo()));
if (conditionDefinitions.size() > 0) { if (conditionDefinitions.size() > 0) {
return false; return false;
} }

View File

@@ -131,13 +131,16 @@ public class ContractServiceImpl extends ServiceImpl<ContractMapper, Contract> i
*/ */
@Override @Override
public Contract getContract(String contractNo) { public Contract getContract(String contractNo) {
// 先从缓存中查找
List<Contract> contractList = getRedisContractList(); List<Contract> contractList = getRedisContractList();
for (Contract contract : contractList) { for (Contract contract : contractList) {
if (contractNo.equals(contract.getBusNo())) { if (contractNo.equals(contract.getBusNo())) {
return contract; return contract;
} }
} }
return null;
// 缓存中找不到时直接从数据库查询支持contractNo动态变化
return getByContractNo(contractNo);
} }
/** /**

View File

@@ -85,33 +85,33 @@
</dependencies> </dependencies>
<build> <!-- <build>-->
<plugins> <!-- <plugins>-->
<plugin> <!-- <plugin>-->
<groupId>org.springframework.boot</groupId> <!-- <groupId>org.springframework.boot</groupId>-->
<artifactId>spring-boot-maven-plugin</artifactId> <!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
<configuration> <!-- <configuration>-->
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 --> <!-- <fork>true</fork> &lt;!&ndash; 如果没有该配置devtools不会生效 &ndash;&gt;-->
</configuration> <!-- </configuration>-->
<executions> <!-- <executions>-->
<execution> <!-- <execution>-->
<goals> <!-- <goals>-->
<goal>repackage</goal> <!-- <goal>repackage</goal>-->
</goals> <!-- </goals>-->
</execution> <!-- </execution>-->
</executions> <!-- </executions>-->
</plugin> <!-- </plugin>-->
<plugin> <!-- <plugin>-->
<groupId>org.apache.maven.plugins</groupId> <!-- <groupId>org.apache.maven.plugins</groupId>-->
<artifactId>maven-war-plugin</artifactId> <!-- <artifactId>maven-war-plugin</artifactId>-->
<version>${maven-war-plugin.version}</version> <!-- <version>${maven-war-plugin.version}</version>-->
<configuration> <!-- <configuration>-->
<failOnMissingWebXml>false</failOnMissingWebXml> <!-- <failOnMissingWebXml>false</failOnMissingWebXml>-->
<warName>${project.artifactId}</warName> <!-- <warName>${project.artifactId}</warName>-->
</configuration> <!-- </configuration>-->
</plugin> <!-- </plugin>-->
</plugins> <!-- </plugins>-->
<finalName>${project.artifactId}</finalName> <!-- <finalName>${project.artifactId}</finalName>-->
</build> <!-- </build>-->
</project> </project>

View File

@@ -1,11 +1,11 @@
# 页面标题 # 页面标题
VITE_APP_TITLE = 医院信息管理系统 VITE_APP_TITLE=医院信息管理系统
# 生产环境配置 # 生产环境配置
VITE_APP_ENV = 'production' VITE_APP_ENV=production
# OpenHIS管理系统/生产环境 # OpenHIS管理系统/生产环境
VITE_APP_BASE_API = '/prod-api' VITE_APP_BASE_API=/prod-api
# 是否在打包时开启压缩,支持 gzip 和 brotli # 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip VITE_BUILD_COMPRESS=gzip

35
openhis-ui-vue3/.env.spug Normal file
View File

@@ -0,0 +1,35 @@
# 开发环境本地只启动前端项目依赖开发环境后端、APP
# 生产环境配置
VITE_APP_ENV = 'spug'
VITE_DEV=true
# 请求路径
VITE_BASE_URL='http://192.168.110.252'
# 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务
VITE_UPLOAD_TYPE=server
# OpenHIS管理系统/SPUG环境
VITE_APP_BASE_API = '/admin-api'
# 是否删除debugger
VITE_DROP_DEBUGGER=false
# 是否删除console.log
VITE_DROP_CONSOLE=false
# 是否sourcemap
VITE_SOURCEMAP=true
# 打包路径
VITE_BASE_PATH=/
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=false
# GoView域名
VITE_GOVIEW_URL='http://127.0.0.1:3000'

View File

@@ -9,7 +9,7 @@
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/> />
<!-- <link rel="icon" href="src/assets/images/ccu.png" /> --> <!-- <link rel="icon" href="src/assets/images/ccu.png" /> -->
<link rel="stylesheet" type="text/css" media="print" href="/public/print-lock.css"> <link rel="stylesheet" type="text/css" media="print" href="/print-lock.css">
<title>医院信息管理系统</title> <title>医院信息管理系统</title>
<!--[if lt IE 11 <!--[if lt IE 11
]><script> ]><script>

View File

@@ -13,9 +13,10 @@
"@vueup/vue-quill": "1.2.0", "@vueup/vue-quill": "1.2.0",
"@vueuse/core": "10.6.1", "@vueuse/core": "10.6.1",
"axios": "0.27.2", "axios": "0.27.2",
"chart.js": "^4.5.1",
"d3": "^7.9.0", "d3": "^7.9.0",
"decimal.js": "^10.5.0", "decimal.js": "^10.5.0",
"echarts": "5.4.3", "echarts": "^5.4.3",
"element-china-area-data": "^6.1.0", "element-china-area-data": "^6.1.0",
"element-plus": "2.9.11", "element-plus": "2.9.11",
"file-saver": "2.0.5", "file-saver": "2.0.5",
@@ -544,6 +545,11 @@
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
}, },
"node_modules/@kurkle/color": {
"version": "0.3.4",
"resolved": "https://registry.npmmirror.com/@kurkle/color/-/color-0.3.4.tgz",
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1686,6 +1692,18 @@
"url": "https://github.com/chalk/chalk?sponsor=1" "url": "https://github.com/chalk/chalk?sponsor=1"
} }
}, },
"node_modules/chart.js": {
"version": "4.5.1",
"resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-4.5.1.tgz",
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
"license": "MIT",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"engines": {
"pnpm": ">=8"
}
},
"node_modules/china-division": { "node_modules/china-division": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmmirror.com/china-division/-/china-division-2.7.0.tgz", "resolved": "https://registry.npmmirror.com/china-division/-/china-division-2.7.0.tgz",
@@ -1837,20 +1855,6 @@
"integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
"dev": true "dev": true
}, },
"node_modules/copy-anything": {
"version": "2.0.6",
"resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz",
"integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"is-what": "^3.14.1"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/copy-descriptor": { "node_modules/copy-descriptor": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmmirror.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "resolved": "https://registry.npmmirror.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
@@ -2823,20 +2827,6 @@
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
"dev": true "dev": true
}, },
"node_modules/errno": {
"version": "0.1.8",
"resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz",
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"prr": "~1.0.1"
},
"bin": {
"errno": "cli.js"
}
},
"node_modules/error-ex": { "node_modules/error-ex": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz", "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz",
@@ -4195,14 +4185,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-what": {
"version": "3.14.1",
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz",
"integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
"dev": true,
"optional": true,
"peer": true
},
"node_modules/is-windows": { "node_modules/is-windows": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmmirror.com/is-windows/-/is-windows-1.0.2.tgz", "resolved": "https://registry.npmmirror.com/is-windows/-/is-windows-1.0.2.tgz",
@@ -4325,34 +4307,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/less": {
"version": "4.2.2",
"resolved": "https://registry.npmmirror.com/less/-/less-4.2.2.tgz",
"integrity": "sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"copy-anything": "^2.0.1",
"parse-node-version": "^1.0.1",
"tslib": "^2.3.0"
},
"bin": {
"lessc": "bin/lessc"
},
"engines": {
"node": ">=6"
},
"optionalDependencies": {
"errno": "^0.1.1",
"graceful-fs": "^4.1.2",
"image-size": "~0.5.0",
"make-dir": "^2.1.0",
"mime": "^1.4.1",
"needle": "^3.1.0",
"source-map": "~0.6.0"
}
},
"node_modules/lines-and-columns": { "node_modules/lines-and-columns": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -4432,21 +4386,6 @@
"@jridgewell/sourcemap-codec": "^1.5.0" "@jridgewell/sourcemap-codec": "^1.5.0"
} }
}, },
"node_modules/make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"pify": "^4.0.1",
"semver": "^5.6.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/map-cache": { "node_modules/map-cache": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmmirror.com/map-cache/-/map-cache-0.2.2.tgz", "resolved": "https://registry.npmmirror.com/map-cache/-/map-cache-0.2.2.tgz",
@@ -4521,20 +4460,6 @@
"node": ">=8.6" "node": ">=8.6"
} }
}, },
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"dev": true,
"optional": true,
"peer": true,
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": { "node_modules/mime-db": {
"version": "1.52.0", "version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
@@ -4723,24 +4648,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/needle": {
"version": "3.3.1",
"resolved": "https://registry.npmmirror.com/needle/-/needle-3.3.1.tgz",
"integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"iconv-lite": "^0.6.3",
"sax": "^1.2.4"
},
"bin": {
"needle": "bin/needle"
},
"engines": {
"node": ">= 4.4.x"
}
},
"node_modules/normalize-path": { "node_modules/normalize-path": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -4984,17 +4891,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/parse-node-version": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">= 0.10"
}
},
"node_modules/pascalcase": { "node_modules/pascalcase": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmmirror.com/pascalcase/-/pascalcase-0.1.1.tgz", "resolved": "https://registry.npmmirror.com/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -5045,17 +4941,6 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/pify": {
"version": "4.0.1",
"resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=6"
}
},
"node_modules/pinia": { "node_modules/pinia": {
"version": "2.1.7", "version": "2.1.7",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz", "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz",
@@ -5290,14 +5175,6 @@
"@province-city-china/types": "8.5.8" "@province-city-china/types": "8.5.8"
} }
}, },
"node_modules/prr": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz",
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
"dev": true,
"optional": true,
"peer": true
},
"node_modules/quansync": { "node_modules/quansync": {
"version": "0.2.8", "version": "0.2.8",
"resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.8.tgz", "resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.8.tgz",
@@ -5779,14 +5656,6 @@
"node": ">=14.0.0" "node": ">=14.0.0"
} }
}, },
"node_modules/sax": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz",
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
"dev": true,
"optional": true,
"peer": true
},
"node_modules/scule": { "node_modules/scule": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz", "resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
@@ -5801,17 +5670,6 @@
"preval.macro": "^4.0.0" "preval.macro": "^4.0.0"
} }
}, },
"node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"optional": true,
"peer": true,
"bin": {
"semver": "bin/semver"
}
},
"node_modules/set-function-length": { "node_modules/set-function-length": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -7817,6 +7675,11 @@
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
}, },
"@kurkle/color": {
"version": "0.3.4",
"resolved": "https://registry.npmmirror.com/@kurkle/color/-/color-0.3.4.tgz",
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="
},
"@nodelib/fs.scandir": { "@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -8634,6 +8497,14 @@
"supports-color": "^7.1.0" "supports-color": "^7.1.0"
} }
}, },
"chart.js": {
"version": "4.5.1",
"resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-4.5.1.tgz",
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
"requires": {
"@kurkle/color": "^0.3.0"
}
},
"china-division": { "china-division": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmmirror.com/china-division/-/china-division-2.7.0.tgz", "resolved": "https://registry.npmmirror.com/china-division/-/china-division-2.7.0.tgz",
@@ -8749,17 +8620,6 @@
"integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
"dev": true "dev": true
}, },
"copy-anything": {
"version": "2.0.6",
"resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz",
"integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
"dev": true,
"optional": true,
"peer": true,
"requires": {
"is-what": "^3.14.1"
}
},
"copy-descriptor": { "copy-descriptor": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmmirror.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "resolved": "https://registry.npmmirror.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
@@ -9458,17 +9318,6 @@
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
"dev": true "dev": true
}, },
"errno": {
"version": "0.1.8",
"resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz",
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
"dev": true,
"optional": true,
"peer": true,
"requires": {
"prr": "~1.0.1"
}
},
"error-ex": { "error-ex": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz", "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz",
@@ -10443,14 +10292,6 @@
"get-intrinsic": "^1.2.6" "get-intrinsic": "^1.2.6"
} }
}, },
"is-what": {
"version": "3.14.1",
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz",
"integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
"dev": true,
"optional": true,
"peer": true
},
"is-windows": { "is-windows": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmmirror.com/is-windows/-/is-windows-1.0.2.tgz", "resolved": "https://registry.npmmirror.com/is-windows/-/is-windows-1.0.2.tgz",
@@ -10554,26 +10395,6 @@
"integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
"dev": true "dev": true
}, },
"less": {
"version": "4.2.2",
"resolved": "https://registry.npmmirror.com/less/-/less-4.2.2.tgz",
"integrity": "sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==",
"dev": true,
"optional": true,
"peer": true,
"requires": {
"copy-anything": "^2.0.1",
"errno": "^0.1.1",
"graceful-fs": "^4.1.2",
"image-size": "~0.5.0",
"make-dir": "^2.1.0",
"mime": "^1.4.1",
"needle": "^3.1.0",
"parse-node-version": "^1.0.1",
"source-map": "~0.6.0",
"tslib": "^2.3.0"
}
},
"lines-and-columns": { "lines-and-columns": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -10639,18 +10460,6 @@
"@jridgewell/sourcemap-codec": "^1.5.0" "@jridgewell/sourcemap-codec": "^1.5.0"
} }
}, },
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"optional": true,
"peer": true,
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
}
},
"map-cache": { "map-cache": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmmirror.com/map-cache/-/map-cache-0.2.2.tgz", "resolved": "https://registry.npmmirror.com/map-cache/-/map-cache-0.2.2.tgz",
@@ -10707,14 +10516,6 @@
"picomatch": "^2.3.1" "picomatch": "^2.3.1"
} }
}, },
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"dev": true,
"optional": true,
"peer": true
},
"mime-db": { "mime-db": {
"version": "1.52.0", "version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
@@ -10853,18 +10654,6 @@
} }
} }
}, },
"needle": {
"version": "3.3.1",
"resolved": "https://registry.npmmirror.com/needle/-/needle-3.3.1.tgz",
"integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==",
"dev": true,
"optional": true,
"peer": true,
"requires": {
"iconv-lite": "^0.6.3",
"sax": "^1.2.4"
}
},
"normalize-path": { "normalize-path": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -11045,14 +10834,6 @@
"lines-and-columns": "^1.1.6" "lines-and-columns": "^1.1.6"
} }
}, },
"parse-node-version": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
"dev": true,
"optional": true,
"peer": true
},
"pascalcase": { "pascalcase": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmmirror.com/pascalcase/-/pascalcase-0.1.1.tgz", "resolved": "https://registry.npmmirror.com/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -11091,14 +10872,6 @@
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true "dev": true
}, },
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true,
"optional": true,
"peer": true
},
"pinia": { "pinia": {
"version": "2.1.7", "version": "2.1.7",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz", "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz",
@@ -11245,14 +11018,6 @@
"@province-city-china/types": "8.5.8" "@province-city-china/types": "8.5.8"
} }
}, },
"prr": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz",
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
"dev": true,
"optional": true,
"peer": true
},
"quansync": { "quansync": {
"version": "0.2.8", "version": "0.2.8",
"resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.8.tgz", "resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.8.tgz",
@@ -11592,14 +11357,6 @@
"source-map-js": ">=0.6.2 <2.0.0" "source-map-js": ">=0.6.2 <2.0.0"
} }
}, },
"sax": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz",
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
"dev": true,
"optional": true,
"peer": true
},
"scule": { "scule": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz", "resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
@@ -11614,14 +11371,6 @@
"preval.macro": "^4.0.0" "preval.macro": "^4.0.0"
} }
}, },
"semver": {
"version": "5.7.2",
"resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"optional": true,
"peer": true
},
"set-function-length": { "set-function-length": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",

View File

@@ -7,9 +7,10 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build:prod": "vite build", "build:prod": "vite build --mode production",
"build:stage": "vite build --mode staging", "build:stage": "vite build --mode staging",
"preview": "vite preview" "preview": "vite preview",
"build:spug": "vite build --mode spug"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -20,9 +21,10 @@
"@vueup/vue-quill": "1.2.0", "@vueup/vue-quill": "1.2.0",
"@vueuse/core": "10.6.1", "@vueuse/core": "10.6.1",
"axios": "0.27.2", "axios": "0.27.2",
"chart.js": "^4.5.1",
"d3": "^7.9.0", "d3": "^7.9.0",
"decimal.js": "^10.5.0", "decimal.js": "^10.5.0",
"echarts": "5.4.3", "echarts": "^5.4.3",
"element-china-area-data": "^6.1.0", "element-china-area-data": "^6.1.0",
"element-plus": "2.9.11", "element-plus": "2.9.11",
"file-saver": "2.0.5", "file-saver": "2.0.5",

View File

@@ -0,0 +1,61 @@
import request from '@/utils/request';
import { parseStrEmpty } from "@/utils/openhis";
/**
* 查询患者列表
* 完全复用门诊挂号查询患者的逻辑和API
* @param {Object} query - 查询参数
* @returns {Promise} 请求结果
*/
export function getPatientList(query) {
// 打印日志便于调试
console.log('调用患者查询API参数:', query);
// 直接复用门诊挂号模块完全相同的实现方式
// 不做额外的参数处理直接将query传递给后端
return request({
url: '/charge-manage/register/patient-metadata',
method: 'get',
params: query
});
};
/**
* 更新患者换卡信息
* @param {Object} params - 换卡参数
* @returns {Promise} 请求结果
*/
export const renewPatientCard = (params) => {
return request({
url: '/cardRenewal/card/renewal',
method: 'post',
data: params
});
};
/**
* 获取患者详情信息
* @param {string} patientId - 患者ID
* @returns {Promise} 请求结果
*/
export const getPatientInfo = (patientId) => {
return request({
url: `/cardRenewal/patient/info/${patientId}`,
method: 'get'
});
};
// 获取患者详细信息
/* export function getPatientInfo(patientId) {
return request({
url: '/cardRenewal/patient/info/' + patientId,
method: 'get'
}).catch(error => {
console.error('获取患者详细信息API调用失败:', error);
return {
code: 500,
msg: 'API调用失败',
data: {}
};
});
} */

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -87,7 +87,119 @@ export const constantRoutes = [
] ]
// 动态路由,基于用户权限动态去加载 // 动态路由,基于用户权限动态去加载
export const dynamicRoutes = [ export const dynamicRoutes = [
{
path: '/basicmanage',
component: Layout,
redirect: '/basicmanage/invoice-management',
name: 'BasicManage',
meta: { title: '基础管理', icon: 'component' },
children: [
{
path: 'invoice-management',
component: () => import('@/views/basicmanage/InvoiceManagement/index.vue'),
name: 'invoice-management',
meta: { title: '发票管理' }
}
]
},
// 兼容系统业务管理路径
{
path: '/system/ywgz',
component: Layout,
redirect: '/system/ywgz/InvoiceManagement',
hidden: true,
children: [
{
path: 'InvoiceManagement',
component: () => import('@/views/basicmanage/InvoiceManagement/index.vue'),
name: 'SystemInvoiceManagement',
meta: { title: '发票管理' }
}
]
},
{
path: '/maintainSystem',
component: Layout,
redirect: '/maintainSystem/chargeConfig',
name: 'MaintainSystem',
meta: { title: '维护系统', icon: 'system' },
children: [
{
path: '',
redirect: 'chargeConfig'
},
{
path: 'chargeConfig',
component: () => import('@/views/maintainSystem/chargeConfig/index.vue'),
name: 'ChargeConfig',
meta: { title: '挂号收费系统参数维护', icon: 'config', permissions: ['maintainSystem:chargeConfig:list'] }
}
]
},
{
path: '/system',
component: Layout,
redirect: '/system/user',
name: 'System',
meta: { title: '系统管理', icon: 'system' },
children: [
{
path: 'user',
component: () => import('@/views/system/user/index.vue'),
name: 'User',
meta: { title: '用户管理', icon: 'user', permissions: ['system:user:list'] }
},
{
path: 'role',
component: () => import('@/views/system/role/index.vue'),
name: 'Role',
meta: { title: '角色管理', icon: 'role', permissions: ['system:role:list'] }
},
{
path: 'menu',
component: () => import('@/views/system/menu/index.vue'),
name: 'Menu',
meta: { title: '菜单管理', icon: 'menu', permissions: ['system:menu:list'] }
},
{
path: 'dept',
component: () => import('@/views/system/dept/index.vue'),
name: 'Dept',
meta: { title: '部门管理', icon: 'dept', permissions: ['system:dept:list'] }
},
{
path: 'post',
component: () => import('@/views/system/post/index.vue'),
name: 'Post',
meta: { title: '岗位管理', icon: 'post', permissions: ['system:post:list'] }
},
{
path: 'dict',
component: () => import('@/views/system/dict/index.vue'),
name: 'Dict',
meta: { title: '字典管理', icon: 'dict', permissions: ['system:dict:list'] }
},
{
path: 'config',
component: () => import('@/views/system/config/index.vue'),
name: 'Config',
meta: { title: '参数配置', icon: 'config', permissions: ['system:config:list'] }
},
{
path: 'notice',
component: () => import('@/views/system/notice/index.vue'),
name: 'Notice',
meta: { title: '通知公告', icon: 'notice', permissions: ['system:notice:list'] }
},
{
path: 'tenant',
component: () => import('@/views/system/tenant/index.vue'),
name: 'Tenant',
meta: { title: '租户管理', icon: 'tenant', permissions: ['system:tenant:list'] }
}
]
},
{ {
path: '/system/tenant-user', path: '/system/tenant-user',
component: Layout, component: Layout,
@@ -98,7 +210,7 @@ export const dynamicRoutes = [
path: 'set/:tenantId(\\d+)', path: 'set/:tenantId(\\d+)',
component: () => import('@/views/system/tenant/setUser'), component: () => import('@/views/system/tenant/setUser'),
name: 'SetUser', name: 'SetUser',
meta: { title: '所属用户', activeMenu: '/system/basicmanage/tenant' } meta: { title: '所属用户', activeMenu: '/system/tenant' }
} }
] ]
}, },
@@ -112,7 +224,7 @@ export const dynamicRoutes = [
path: 'set/:tenantId(\\d+)', path: 'set/:tenantId(\\d+)',
component: () => import('@/views/system/tenant/setContract'), component: () => import('@/views/system/tenant/setContract'),
name: 'SetContract', name: 'SetContract',
meta: { title: '合同管理', activeMenu: '/system/basicmanage/tenant' } meta: { title: '合同管理', activeMenu: '/system/tenant' }
} }
] ]
}, },
@@ -158,6 +270,48 @@ export const dynamicRoutes = [
} }
] ]
}, },
{
path: '/monitor',
component: Layout,
redirect: '/monitor/operlog',
name: 'Monitor',
meta: { title: '系统监控', icon: 'monitor' },
children: [
{
path: 'operlog',
component: () => import('@/views/monitor/operlog/index.vue'),
name: 'Operlog',
meta: { title: '操作日志', icon: 'operlog', permissions: ['monitor:operlog:list'] }
},
{
path: 'logininfor',
component: () => import('@/views/monitor/logininfor/index.vue'),
name: 'Logininfor',
meta: { title: '登录日志', icon: 'logininfor', permissions: ['monitor:logininfor:list'] }
},
{
path: 'job',
component: () => import('@/views/monitor/job/index.vue'),
name: 'Job',
meta: { title: '定时任务', icon: 'job', permissions: ['monitor:job:list'] }
}
]
},
{
path: '/tool',
component: Layout,
redirect: '/tool/gen',
name: 'Tool',
meta: { title: '系统工具', icon: 'tool' },
children: [
{
path: 'gen',
component: () => import('@/views/tool/gen/index.vue'),
name: 'Gen',
meta: { title: '代码生成', icon: 'gen', permissions: ['tool:gen:list'] }
}
]
},
{ {
path: '/monitor/job-log', path: '/monitor/job-log',
component: Layout, component: Layout,
@@ -188,9 +342,12 @@ export const dynamicRoutes = [
} }
] ]
// 合并常量路由和动态路由,确保所有路由都能被访问
const allRoutes = [...constantRoutes, ...dynamicRoutes];
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
routes: constantRoutes, routes: allRoutes,
scrollBehavior(to, from, savedPosition) { scrollBehavior(to, from, savedPosition) {
if (savedPosition) { if (savedPosition) {
return savedPosition return savedPosition

View File

@@ -3,6 +3,10 @@ export default {
* 网页标题 * 网页标题
*/ */
title: import.meta.env.VITE_APP_TITLE, title: import.meta.env.VITE_APP_TITLE,
/**
* 系统名称
*/
systemName: (import.meta.env.VITE_APP_SYSTEM_NAME ? import.meta.env.VITE_APP_SYSTEM_NAME : '测试医院'),
/** /**
* 侧边栏主题 深色主题theme-dark浅色主题theme-light * 侧边栏主题 深色主题theme-dark浅色主题theme-light
*/ */

File diff suppressed because it is too large Load Diff

View File

@@ -41,7 +41,7 @@ export function getOrgDetail(id) {
export function initOrgTypeOption() { export function initOrgTypeOption() {
return request({ return request({
url: '/base-data-manage/organization/init', url: '/base-data-manage/organization/init',
method: 'get', method: 'get'
}) })
} }

View File

@@ -141,15 +141,7 @@
</template> </template>
<script setup name="Organization"> <script setup name="Organization">
import { import { getList, deleteOrganization, addOrganization, updateOrganization, disableOrg, initOrgTypeOption, enableOrg } from './components/api';
getList,
deleteOrganization,
addOrganization,
updateOrganization,
disableOrg,
initOrgTypeOption,
enableOrg,
} from './components/api';
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const loading = ref(true); const loading = ref(true);
@@ -181,11 +173,57 @@ const rules = ref({
getPageList(); getPageList();
initOption(); initOption();
// 使用系统标准字典获取方法
const { organization_class } = proxy.useDict('organization_class');
// 统一的科室分类字典处理函数
function processOrganizationClassDict(dictData) {
return dictData.map(item => ({
value: String(item.value), // 将值转换为字符串类型,确保与表单值类型一致
info: item.label // 使用dict_label的值作为显示文本
}));
}
// 监听字典数据变化
watch(() => organization_class.value, (newVal) => {
if (newVal && newVal.length > 0) {
// 转换为组件需要的格式
classEnumOption.value = processOrganizationClassDict(newVal);
// 同步更新表格中显示的科室分类文本,确保主界面显示与字典一致
if (organization.value && organization.value.length > 0) {
organization.value = organization.value.map(item => {
// 保留原有显示文本作为基础
const originalText = item.classEnum_dictText || '';
// 获取字典中的对应文本
const dictLabel = getDictLabel(item.classEnum);
// 只有在字典中找到匹配值时才替换,否则保留原有文本
return {
...item,
classEnum_dictText: dictLabel || originalText
};
});
}
}
}, { immediate: true });
function initOption() { function initOption() {
if (orgTypeOption.value.length == 0) { if (orgTypeOption.value.length == 0) {
initOrgTypeOption().then((res) => { initOrgTypeOption().then((res) => {
orgTypeOption.value = res.data.organizationTypeOptions; orgTypeOption.value = res.data.organizationTypeOptions;
classEnumOption.value = res.data.organizationClassOptions;
// 优先使用系统标准字典数据,确保编辑和新增科室使用相同的分类字典
if (organization_class.value && organization_class.value.length > 0) {
classEnumOption.value = processOrganizationClassDict(organization_class.value);
} else if (res.data.organizationClassOptions && res.data.organizationClassOptions.length > 0) {
// 只有在字典数据不存在时才使用接口返回的数据作为备选
// 将接口返回的科室分类选项值也转换为字符串类型,保持一致性
classEnumOption.value = res.data.organizationClassOptions.map(item => ({
...item,
value: String(item.value)
}));
}
}); });
} }
} }
@@ -195,17 +233,48 @@ function reset() {
orgRef.value.resetFields(); orgRef.value.resetFields();
} }
// 从字典数据中查找对应的值,处理类型转换
function getDictLabel(value) {
if (!value || !organization_class.value || organization_class.value.length === 0) return '';
// 尝试进行类型转换比较,处理可能的字符串/数字不匹配问题
const stringValue = String(value);
const dict = organization_class.value.find(item => {
// 比较转换后的字符串值
return String(item.value) === stringValue;
});
return dict ? dict.label : '';
}
function getPageList() { function getPageList() {
loading.value = false; loading.value = false;
getList(queryParams.value).then((res) => { getList(queryParams.value).then((res) => {
organization.value = res.data.records; // 处理返回的科室数据,确保科室分类显示与系统标准字典一致
const processedData = res.data.records.map(item => {
// 保留原有显示文本作为基础
const originalText = item.classEnum_dictText || '';
// 如果系统标准字典存在,尝试使用字典中的文本覆盖原有文本
if (organization_class.value && organization_class.value.length > 0) {
const dictLabel = getDictLabel(item.classEnum);
// 只有在字典中找到匹配值时才替换,否则保留原有文本
return {
...item,
classEnum_dictText: dictLabel || originalText
};
}
return item;
});
organization.value = processedData;
total.value = res.data.total; total.value = res.data.total;
loading.value = false; loading.value = false;
}); });
} }
function handleAdd() { function handleAdd() {
title.value = '添加药库药房'; title.value = '添加科室';
open.value = true; open.value = true;
reset(); reset();
console.log(form.value); console.log(form.value);
@@ -223,7 +292,8 @@ function handelEdit(row) {
form.value.ybNo = row.ybNo; form.value.ybNo = row.ybNo;
form.value.ybName = row.ybName; form.value.ybName = row.ybName;
form.value.typeEnum = row.typeEnum; form.value.typeEnum = row.typeEnum;
form.value.classEnum = row.classEnum; // 确保科室分类值的类型正确,使其能正确匹配下拉选项中的值
form.value.classEnum = row.classEnum !== undefined ? String(row.classEnum) : undefined;
form.value.busNoParent = row.busNo.split('.').length > 1 ? row.busNo.split('.')[0] : undefined; form.value.busNoParent = row.busNo.split('.').length > 1 ? row.busNo.split('.')[0] : undefined;
}, 50); }, 50);
} }

View File

@@ -0,0 +1,105 @@
/**
* 操作日志工具
* 所有操作必须有操作日志
*/
import { addOperationLog } from './outpatientNumber'
/**
* 记录操作日志
* @param {Object} params
* @param {string} params.operation - 操作类型(新增/修改/删除/查询)
* @param {string} params.details - 操作详情
* @param {boolean} params.success - 操作是否成功
* @param {string} params.errorMessage - 错误信息
* @param {Object} params.userInfo - 用户信息
*/
export async function logOperation({ operation, details, success, errorMessage, userInfo }) {
try {
const logData = {
operation,
details,
success,
errorMessage: errorMessage || null,
timestamp: new Date().toISOString(),
userId: userInfo?.id || null,
userName: userInfo?.name || null,
}
// 控制台输出(便于调试)
console.log('[门诊号码管理] 操作日志:', logData)
// 调用后端接口记录日志
try {
await addOperationLog(logData)
} catch (apiError) {
console.warn('[门诊号码管理] 日志接口调用失败,仅记录到控制台')
}
} catch (error) {
console.error('[门诊号码管理] 记录日志失败:', error)
}
}
/**
* 记录查询操作
*/
export function logQuery(recordCount, userInfo) {
return logOperation({
operation: '查询',
details: `查询门诊号码段列表,共 ${recordCount} 条记录`,
success: true,
userInfo
})
}
/**
* 记录新增操作
*/
export function logCreate(record, success, errorMessage, userInfo) {
const details = success
? `新增门诊号码段:${record.startNo} - ${record.endNo}(操作员:${record.operatorName}`
: `尝试新增门诊号码段:${record.startNo} - ${record.endNo},失败原因:${errorMessage}`
return logOperation({
operation: '新增',
details,
success,
errorMessage,
userInfo
})
}
/**
* 记录修改操作
*/
export function logUpdate(record, success, errorMessage, userInfo) {
const details = success
? `修改门诊号码段:${record.startNo} - ${record.endNo}ID${record.id}`
: `尝试修改门诊号码段 ID${record.id},失败原因:${errorMessage}`
return logOperation({
operation: '修改',
details,
success,
errorMessage,
userInfo
})
}
/**
* 记录删除操作
*/
export function logDelete(records, success, errorMessage, userInfo) {
const recordsInfo = records.map(r => `${r.startNo}-${r.endNo}`).join('、')
const details = success
? `删除门诊号码段(共 ${records.length} 条):${recordsInfo}`
: `尝试删除门诊号码段(共 ${records.length} 条),失败原因:${errorMessage}`
return logOperation({
operation: '删除',
details,
success,
errorMessage,
userInfo
})
}

View File

@@ -0,0 +1,64 @@
/**
* 门诊号码管理 API 接口
* 严格按照要求实现
*/
import request from '@/utils/request'
/**
* 分页查询门诊号码段列表
* 要求:普通用户只能查看自己的,管理员可以查看所有
*/
export function listOutpatientNo(query) {
return request({
url: '/business-rule/outpatient-no/page',
method: 'get',
params: query,
})
}
/**
* 新增门诊号码段
* 要求:必须校验前缀一致性、长度一致性、重复检查
*/
export function addOutpatientNo(data) {
return request({
url: '/business-rule/outpatient-no',
method: 'post',
data,
})
}
/**
* 更新门诊号码段
*/
export function updateOutpatientNo(data) {
return request({
url: '/business-rule/outpatient-no',
method: 'put',
data,
})
}
/**
* 删除门诊号码段
*要求:双重校验(归属权+使用状态)
*/
export function deleteOutpatientNo(params) {
return request({
url: '/business-rule/outpatient-no',
method: 'delete',
params,
})
}
/**
* 记录操作日志
* PRD要求所有操作必须有操作日志
*/
export function addOperationLog(data) {
return request({
url: '/business-rule/outpatient-no/log',
method: 'post',
data,
})
}

View File

@@ -0,0 +1,823 @@
<template>
<!-- Windows XP风格窗口布局600px固定宽度 -->
<div class="outpatient-no-management-wrapper">
<div class="outpatient-no-management">
<!--标题栏32px高 -->
<div class="title-bar">
<span class="title-text">门诊号码管理</span>
</div>
<!-- 功能按钮区40px高 -->
<div class="button-bar">
<el-row :gutter="10">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="onAdd">新设(A)</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="onDelete">删除(D)</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Check" @click="() => onSave()">保存(S)</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Close" @click="onClose">关闭(X)</el-button>
</el-col>
<el-col v-if="canToggleViewAll" :span="4">
<el-switch
v-model="viewAll"
active-text="查看全部"
inactive-text="仅本人"
@change="getList"
/>
</el-col>
</el-row>
</div>
<!-- 表格内容区自适应剩余高度 -->
<div class="table-content">
<el-table
v-loading="loading"
:data="tableData"
@selection-change="handleSelectionChange"
:row-class-name="tableRowClassName"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="操作员" prop="operatorName" min-width="120" />
<el-table-column label="员工工号" prop="staffNo" min-width="120" />
<el-table-column label="领用日期" prop="receiveDate" min-width="140">
<template #default="{ row }">
<el-date-picker
v-if="row._editing"
v-model="row.receiveDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择日期"
style="width: 100%"
/>
<span v-else>{{ row.receiveDate }}</span>
</template>
</el-table-column>
<el-table-column label="起始号码" prop="startNo" min-width="140">
<template #default="{ row }">
<el-input
v-if="row._editing"
v-model.trim="row.startNo"
@input="() => onStartNoChange(row)"
@blur="() => validateNumField(row, 'startNo')"
/>
<span v-else>{{ row.startNo }}</span>
</template>
</el-table-column>
<el-table-column label="终止号码" prop="endNo" min-width="140">
<template #default="{ row }">
<el-input
v-if="row._editing"
v-model.trim="row.endNo"
@blur="() => validateNumField(row, 'endNo')"
/>
<span v-else>{{ row.endNo }}</span>
</template>
</el-table-column>
<el-table-column label="使用号码" prop="usedNo" min-width="140">
<template #default="{ row }">
<el-input
v-if="row._editing"
v-model.trim="row.usedNo"
@blur="() => validateNumField(row, 'usedNo')"
/>
<span v-else>{{ row.usedNo }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="100" fixed="right">
<template #default="scope">
<el-button type="primary" link icon="Edit" @click="() => openEdit(scope.row, scope.$index)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</div>
</div>
</div>
<!-- 编辑弹窗 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px" append-to-body>
<el-form label-width="100px">
<el-form-item label="操作员">
<el-input v-model="editForm.operatorName" disabled />
</el-form-item>
<el-form-item label="员工工号">
<el-input v-model="editForm.staffNo" disabled />
</el-form-item>
<el-form-item label="领用日期">
<el-date-picker v-model="editForm.receiveDate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
<el-form-item label="起始号码">
<el-input v-model.trim="editForm.startNo" @blur="() => validateNumField(editForm, 'startNo')" />
</el-form-item>
<el-form-item label="终止号码">
<el-input v-model.trim="editForm.endNo" @blur="() => validateNumField(editForm, 'endNo')" />
</el-form-item>
<el-form-item label="使用号码">
<el-input v-model.trim="editForm.usedNo" @blur="() => validateNumField(editForm, 'usedNo')" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="confirmEdit"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="outpatientNoManagement">
import useUserStore from '@/store/modules/user'
import { getConfigKey } from '@/api/system/config'
import { logQuery, logCreate, logUpdate, logDelete } from './components/operationLog'
const { proxy } = getCurrentInstance()
const userStore = useUserStore()
// 获取当前用户信息(用于日志记录)
const getUserInfo = () => ({
id: userStore.id,
name: userStore.name || userStore.nickName
})
const loading = ref(false)
const tableData = ref([])
const total = ref(0)
const ids = ref([])
const multiple = ref(true)
const viewAll = ref(false)
const canToggleViewAll = ref(false)
const dialogVisible = ref(false)
const dialogTitle = ref('编辑门诊号码段')
const editIndex = ref(-1)
const editForm = reactive({
receiveDate: '',
startNo: '',
endNo: '',
usedNo: '',
operatorName: '',
staffNo: '',
})
const data = reactive({
queryParams: {
pageNo: 1,
pageSize: 10,
onlySelf: true,
},
})
const { queryParams } = toRefs(data)
initConfig()
getList()
// 解决从标签页关闭后再次进入页面空白的问题:
// 当页面被 keep-alive 缓存后再次激活,主动刷新列表
onActivated(() => {
getList()
})
async function initConfig() {
try {
const res = await getConfigKey('outpatient_no_view_all')
canToggleViewAll.value = (res?.msg === 'Y' || res?.data === 'Y')
} catch (e) {
canToggleViewAll.value = false
}
}
function handleSelectionChange(selection) {
ids.value = selection.map((item) => item.id)
multiple.value = !selection.length
}
function onAdd() {
const now = new Date()
const yyyy = now.getFullYear()
const mm = String(now.getMonth() + 1).padStart(2, '0')
const dd = String(now.getDate()).padStart(2, '0')
tableData.value.push({
id: undefined,
operatorId: userStore.id,
operatorName: userStore.name || userStore.nickName,
staffNo: userStore.id,
receiveDate: `${yyyy}-${mm}-${dd}`,
startNo: '',
endNo: '',
usedNo: '',
_editing: true,
_error: false,
})
}
// 新增时,起始号码变化时自动设置使用号码为起始号码
function onStartNoChange(row) {
if (!row.id && row._editing) {
row.usedNo = row.startNo
}
}
function onClose() {
proxy.$tab.closePage()
}
function tableRowClassName({ row }) {
return row._error ? 'error-row' : ''
}
function openEdit(row, index) {
editIndex.value = index
dialogTitle.value = '编辑门诊号码段'
editForm.receiveDate = row.receiveDate
editForm.startNo = row.startNo
editForm.endNo = row.endNo
editForm.usedNo = row.usedNo
editForm.operatorName = row.operatorName
editForm.staffNo = row.staffNo
dialogVisible.value = true
}
function confirmEdit() {
const tmp = { ...tableData.value[editIndex.value], ...editForm }
if (!validateRow(tmp, editIndex.value)) return
tableData.value[editIndex.value] = {
...tableData.value[editIndex.value],
...editForm,
_dirty: true, // 标记为已修改,顶部保存时提交
}
dialogVisible.value = false
}
// 字母前缀识别规则 - 从末位往前找到第一个字母
function extractPrefix(value) {
if (!value) return ''
const chars = value.split('')
for (let i = chars.length - 1; i >= 0; i--) {
if (/[A-Za-z]/.test(chars[i])) {
return value.slice(0, i + 1) // 包含找到的字母
}
}
return ''
}
function extractTailNumber(value) {
if (!value) return NaN
const m = value.match(/(\d+)$/)
if (!m) return NaN
return parseInt(m[1], 10)
}
function lengthWithinLimit(value) {
if (!value) return false
const m = value.match(/(\d+)$/)
if (!m) return false
return m[1].length <= 12
}
function rangesOverlap(aStart, aEnd, bStart, bEnd) {
return Math.max(aStart, bStart) <= Math.min(aEnd, bEnd)
}
function alertWarn(msg) {
if (proxy.$modal && proxy.$modal.alertWarning) {
proxy.$modal.alertWarning(msg)
} else if (proxy.$message) {
proxy.$message.warning(msg)
}
}
// 校验必须以数字结尾且尾部数字长度≤12
function isTailDigitsValid(value) {
const m = String(value || '').match(/(\d+)$/)
return !!m && m[1].length <= 12
}
function validateNumField(row, field, rowIndex) {
if (!isTailDigitsValid(row[field])) {
const idxInTable = typeof rowIndex === 'number' ? rowIndex : tableData.value.indexOf(row)
const lineNo = idxInTable >= 0 ? idxInTable + 1 : (editIndex.value >= 0 ? editIndex.value + 1 : undefined)
const msg = lineNo ? `第【${lineNo}】行数据中最大位数为12位且必须以数字结尾` : '最大位数为12位且必须以数字结尾'
alertWarn(msg)
row._error = true
row._warned = row._warned || {}
row._warned[field] = true
return false
}
row._error = false
row._warned = row._warned || {}
row._warned[field] = false
return true
}
function onNumberInput(row, field) {
row._warned = row._warned || {}
const valid = isTailDigitsValid(row[field])
if (!valid && !row._warned[field]) {
alertWarn('最大位数为12位且必须以数字结尾')
row._warned[field] = true
}
if (valid) {
row._warned[field] = false
}
}
function validateRow(row, rowIndex) {
row._error = false
if (!lengthWithinLimit(row.startNo) || !lengthWithinLimit(row.endNo) || !lengthWithinLimit(row.usedNo)) {
const idxInTable = typeof rowIndex === 'number' ? rowIndex : tableData.value.indexOf(row)
const lineNo = idxInTable >= 0 ? idxInTable + 1 : (editIndex.value >= 0 ? editIndex.value + 1 : undefined)
const msg = lineNo ? `第【${lineNo}】行数据中最大位数为12位且必须以数字结尾` : '最大位数为12位且必须以数字结尾'
alertWarn(msg)
row._error = true
return false
}
if ((row.startNo?.length || 0) !== (row.endNo?.length || 0)) {
const idxInTable = typeof rowIndex === 'number' ? rowIndex : tableData.value.indexOf(row)
const lineNo = idxInTable >= 0 ? idxInTable + 1 : (editIndex.value >= 0 ? editIndex.value + 1 : undefined)
const msg = lineNo ? `第【${lineNo}】行数据中,起始号码与终止号码长度必须一致,请修改!` : '起始号码与终止号码长度必须一致'
alertWarn(msg)
row._error = true
return false
}
const p1 = extractPrefix(row.startNo)
const p2 = extractPrefix(row.endNo)
const p3 = extractPrefix(row.usedNo)
if (!(p1 === p2 && p2 === p3)) {
const idxInTable = typeof rowIndex === 'number' ? rowIndex : tableData.value.indexOf(row)
const lineNo = idxInTable >= 0 ? idxInTable + 1 : (editIndex.value >= 0 ? editIndex.value + 1 : undefined)
const msg = lineNo ? `第【${lineNo}】行数据中,门诊号码的字母前缀必须相同,请修改!` : '行数据中,门诊号码的字母前缀必须相同,请修改!'
alertWarn(msg)
row._error = true
return false
}
const sNum = extractTailNumber(row.startNo)
const eNum = extractTailNumber(row.endNo)
if (Number.isNaN(sNum) || Number.isNaN(eNum) || sNum > eNum) {
const idxInTable = typeof rowIndex === 'number' ? rowIndex : tableData.value.indexOf(row)
const lineNo = idxInTable >= 0 ? idxInTable + 1 : (editIndex.value >= 0 ? editIndex.value + 1 : undefined)
const msg = lineNo ? `第【${lineNo}】行数据中,起始/终止号码不合法` : '起始/终止号码不合法'
alertWarn(msg)
row._error = true
return false
}
// 放宽:不再强制“使用号码”必须处于起始与终止范围内
const prefix = p1
for (let i = 0; i < tableData.value.length; i++) {
const other = tableData.value[i]
// 跳过自身:当从弹窗校验时 row 为临时对象,需用下标判断
if ((typeof rowIndex === 'number' && i === rowIndex) || other === row || !other.startNo || !other.endNo) continue
if (extractPrefix(other.startNo) !== prefix) continue
const os = extractTailNumber(other.startNo)
const oe = extractTailNumber(other.endNo)
if (!Number.isNaN(os) && !Number.isNaN(oe)) {
if (rangesOverlap(sNum, eNum, os, oe)) {
const idxInTable = typeof rowIndex === 'number' ? rowIndex : tableData.value.indexOf(row)
const lineNo = idxInTable >= 0 ? idxInTable + 1 : (editIndex.value >= 0 ? editIndex.value + 1 : undefined)
const msg = lineNo ? `第【${lineNo}】行数据中,门诊号码和【${i + 1}】行的门诊号码有冲突,请修改!` : '门诊号码设置重复!'
alertWarn(msg)
row._error = true
return false
}
}
}
return true
}
function onSave(row) {
const rows = row ? [row] : tableData.value.filter(r => ids.value.includes(r.id) || r._dirty || r._editing)
if (!rows.length) return
for (const r of rows) {
const idx = tableData.value.indexOf(r)
if (!validateRow(r, idx)) return
}
// 准备保存的数据
const saveData = rows.map((r) => ({
id: r.id,
operatorId: r.operatorId,
operatorName: r.operatorName,
staffNo: r.staffNo,
receiveDate: r.receiveDate,
startNo: r.startNo,
endNo: r.endNo,
usedNo: r.usedNo,
}))
const ok = lcUpsertMany(saveData)
if (!ok) {
// 记录失败的操作日志
for (const record of saveData) {
if (record.id) {
logUpdate(record, false, '门诊号码设置重复', getUserInfo())
} else {
logCreate(record, false, '门诊号码设置重复', getUserInfo())
}
}
return
}
// 记录成功的操作日志
for (const record of saveData) {
if (record.id) {
logUpdate(record, true, null, getUserInfo())
} else {
logCreate(record, true, null, getUserInfo())
}
}
if (proxy.$modal?.alertSuccess) {
proxy.$modal.alertSuccess('保存成功!')
} else {
proxy.$message.success('保存成功')
}
getList()
}
function onDelete() {
const rows = tableData.value.filter((r) => ids.value.includes(r.id))
if (!rows.length) return
// 双重校验(归属权+使用状态)
for (const r of rows) {
const canDeleteSelf = String(r.operatorId) === String(userStore.id)
const neverUsed = r.usedNo === r.startNo
if (!canDeleteSelf) {
// 权限不足提示
alertWarn('只能删除自己维护的门诊号码段')
logDelete(rows, false, '只能删除自己维护的门诊号码段', getUserInfo())
return
}
if (!neverUsed) {
// 已使用提示
alertWarn('已有门诊号码段已有使用的门诊号码,请核对!')
logDelete(rows, false, '已有门诊号码段已有使用的门诊号码', getUserInfo())
return
}
}
const doRealDelete = () => {
lcDeleteByIds(rows.map((r) => r.id))
//记录成功的删除操作日志
logDelete(rows, true, null, getUserInfo())
if (proxy.$modal?.alertSuccess) {
proxy.$modal.alertSuccess('删除成功')
} else {
proxy.$message.success('删除成功')
}
getList()
}
if (proxy.$modal?.confirm) {
proxy.$modal.confirm('是否确认删除选中数据项?').then(doRealDelete).catch(() => {
// 用户取消删除,不记录日志
})
} else {
doRealDelete()
}
}
function getList() {
loading.value = true
queryParams.value.onlySelf = !viewAll.value
const res = lcList({ ...queryParams.value })
tableData.value = res.records.map((it) => ({
...it,
_editing: false,
_error: false,
_dirty: false,
}))
total.value = res.total
loading.value = false
// 记录查询操作日志
logQuery(total.value, getUserInfo())
}
// 纯前端本地持久化方法localStorage
const STORAGE_KEY = 'ohis_outpatient_no_segments'
function lcReadAll() {
try {
const raw = localStorage.getItem(STORAGE_KEY)
const arr = raw ? JSON.parse(raw) : []
return Array.isArray(arr) ? arr : []
} catch (e) {
return []
}
}
function lcWriteAll(list) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(list || []))
}
function lcList({ pageNo = 1, pageSize = 10, onlySelf = true }) {
const all = lcReadAll()
const filtered = onlySelf ? all.filter((x) => String(x.operatorId) === String(userStore.id)) : all
const start = (pageNo - 1) * pageSize
const end = start + pageSize
return { records: filtered.slice(start, end), total: filtered.length, all }
}
function checkOverlapAll(row, all) {
const prefix = extractPrefix(row.startNo)
const sNum = extractTailNumber(row.startNo)
const eNum = extractTailNumber(row.endNo)
for (const it of all) {
if (row.id && it.id === row.id) continue
if (!it.startNo || !it.endNo) continue
if (extractPrefix(it.startNo) !== prefix) continue
const os = extractTailNumber(it.startNo)
const oe = extractTailNumber(it.endNo)
if (!Number.isNaN(os) && !Number.isNaN(oe)) {
if (rangesOverlap(sNum, eNum, os, oe)) return true
}
}
return false
}
function lcUpsertMany(rows) {
const all = lcReadAll()
for (const r of rows) {
if (checkOverlapAll(r, all)) {
alertWarn('门诊号码设置重复!')
return false
}
if (!r.id) {
r.id = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
}
const idx = all.findIndex((x) => x.id === r.id)
if (idx >= 0) all[idx] = { ...all[idx], ...r }
else all.push({ ...r })
}
lcWriteAll(all)
return true
}
function lcDeleteByIds(idList) {
const all = lcReadAll()
const remain = all.filter((x) => !idList.includes(x.id))
lcWriteAll(remain)
}
</script>
<style scoped>
/*Windows XP风格布局 */
/* 外层容器 - 居中显示 */
.outpatient-no-management-wrapper {
display: flex;
justify-content: center;
align-items: flex-start;
min-height: calc(100vh - 100px);
padding: 20px;
background-color: #f0f0f0;
}
/* 主容器 - 600px固定宽度 */
.outpatient-no-management {
width: 600px;
background-color: #D4D0C8;
border: 1px solid #000000;
box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.3);
display: flex;
flex-direction: column;
min-height: 500px;
}
/* 标题栏32px高背景色#D4D0C8 */
.title-bar {
height: 32px;
background: linear-gradient(to bottom, #0055E5 0%, #0F3D8C 100%);
border-bottom: 1px solid #000000;
display: flex;
align-items: center;
padding: 0 8px;
}
/* 标题文本14px/700左对齐 */
.title-text {
font-size: 14px;
font-weight: 700;
color: #FFFFFF;
letter-spacing: 0.5px;
}
/* 功能按钮区40px高 */
.button-bar {
height: 40px;
background-color: #D4D0C8;
border-bottom: 1px solid #808080;
display: flex;
align-items: center;
padding: 0 8px;
}
.button-bar .el-row {
width: 100%;
}
/* 按钮样式90x32px1px边框圆角0背景色#EFEFEF */
.button-bar :deep(.el-button) {
width: 90px;
height: 32px;
border-radius: 0;
border: 1px solid #808080;
font-size: 13px;
padding: 0;
}
/* 新设按钮 - 主要操作 */
.button-bar :deep(.el-button--primary) {
background: linear-gradient(to bottom, #FFFFFF 0%, #EFEFEF 50%, #DFDFDF 100%);
color: #000000;
border-top: 1px solid #FFFFFF;
border-left: 1px solid #FFFFFF;
border-right: 1px solid #808080;
border-bottom: 1px solid #808080;
}
.button-bar :deep(.el-button--primary:hover) {
background: linear-gradient(to bottom, #FFFEF8 0%, #F5F4EF 50%, #E5E4DF 100%);
}
/* 删除按钮 */
.button-bar :deep(.el-button--danger) {
background: linear-gradient(to bottom, #FFFFFF 0%, #EFEFEF 50%, #DFDFDF 100%);
color: #000000;
border-top: 1px solid #FFFFFF;
border-left: 1px solid #FFFFFF;
border-right: 1px solid #808080;
border-bottom: 1px solid #808080;
}
/* 保存按钮 */
.button-bar :deep(.el-button--success) {
background: linear-gradient(to bottom, #FFFFFF 0%, #EFEFEF 50%, #DFDFDF 100%);
color: #000000;
border-top: 1px solid #FFFFFF;
border-left: 1px solid #FFFFFF;
border-right: 1px solid #808080;
border-bottom: 1px solid #808080;
}
/* 关闭按钮(红色背景,白色文字) */
.button-bar :deep(.el-button--warning) {
background: linear-gradient(to bottom, #FF6B6B 0%, #EE5A5A 50%, #DD4949 100%);
color: #FFFFFF;
font-weight: 600;
border-top: 1px solid #FF9999;
border-left: 1px solid #FF9999;
border-right: 1px solid #AA3333;
border-bottom: 1px solid #AA3333;
}
.button-bar :deep(.el-button--warning:hover) {
background: linear-gradient(to bottom, #FF7B7B 0%, #FE6A6A 50%, #ED5959 100%);
}
/* 按钮禁用状态 */
.button-bar :deep(.el-button:disabled) {
background: #D4D0C8;
color: #808080;
cursor: not-allowed;
}
/*表格内容区(自适应剩余高度) */
.table-content {
flex: 1;
background-color: #FFFFFF;
padding: 8px;
overflow: auto;
min-height: 400px;
}
/* 表格样式1px实线边框#CCCCCC表头背景#F0F0F0 */
.table-content :deep(.el-table) {
border: 1px solid #CCCCCC;
font-size: 13px;
}
.table-content :deep(.el-table th) {
background: linear-gradient(to bottom, #FFFFFF 0%, #F0F0F0 100%);
border: 1px solid #CCCCCC;
color: #000000;
font-weight: 600;
font-size: 13px;
padding: 8px 4px;
}
.table-content :deep(.el-table td) {
border: 1px solid #CCCCCC;
padding: 6px 4px;
font-size: 13px;
}
.table-content :deep(.el-table__body tr:hover > td) {
background-color: #E5F3FF !important;
}
/* 错误行样式 */
:deep(.error-row) {
--el-table-tr-bg-color: #fff7e6;
}
/* 分页样式 */
.table-content :deep(.pagination-container) {
margin-top: 10px;
padding: 10px 0;
border-top: 1px solid #CCCCCC;
}
/* 输入框样式 */
.table-content :deep(.el-input__inner) {
border: 1px solid #7FB4FF;
border-radius: 0;
font-size: 13px;
}
.table-content :deep(.el-input__inner:focus) {
border: 2px solid #0055E5;
}
/* 日期选择器样式 */
.table-content :deep(.el-date-editor) {
width: 100%;
}
/* 开关样式 */
.button-bar :deep(.el-switch) {
height: 24px;
}
/* 滚动条样式Windows XP风格 */
.table-content::-webkit-scrollbar {
width: 16px;
height: 16px;
}
.table-content::-webkit-scrollbar-track {
background-color: #D4D0C8;
border: 1px solid #808080;
}
.table-content::-webkit-scrollbar-thumb {
background: linear-gradient(to bottom, #FFFFFF 0%, #EFEFEF 50%, #DFDFDF 100%);
border: 1px solid #808080;
}
.table-content::-webkit-scrollbar-thumb:hover {
background: linear-gradient(to bottom, #FFFEF8 0%, #F5F4EF 50%, #E5E4DF 100%);
}
/* 编辑弹窗样式 */
:deep(.el-dialog) {
border-radius: 0;
border: 2px solid #0055E5;
}
:deep(.el-dialog__header) {
background: linear-gradient(to bottom, #0055E5 0%, #0F3D8C 100%);
padding: 10px 15px;
margin: 0;
}
:deep(.el-dialog__title) {
color: #FFFFFF;
font-weight: 700;
font-size: 14px;
}
:deep(.el-dialog__close) {
color: #FFFFFF;
}
/* 响应式调整 */
@media (max-width: 650px) {
.outpatient-no-management {
width: 100%;
min-height: 100vh;
}
.outpatient-no-management-wrapper {
padding: 0;
}
}
</style>

View File

@@ -82,7 +82,7 @@
:class="{ 'error-border': scope.row.error }" :class="{ 'error-border': scope.row.error }"
> >
<el-option <el-option
v-for="dict in distribution_category_code" v-for="dict in med_category_code"
:key="dict.value" :key="dict.value"
:label="dict.label" :label="dict.label"
:value="dict.value" :value="dict.value"
@@ -184,7 +184,7 @@ import {
deletePharmacyDepartment, deletePharmacyDepartment,
} from './components/pharmacyDepartment'; } from './components/pharmacyDepartment';
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const { distribution_category_code } = proxy.useDict('distribution_category_code'); const { med_category_code } = proxy.useDict('med_category_code');
import { nextTick } from 'vue'; import { nextTick } from 'vue';
const diagnosisTreatmentList = ref([]); const diagnosisTreatmentList = ref([]);

View File

@@ -36,8 +36,12 @@ export function updateWarehouse(data) {
// 删除 // 删除
export function deleteWarehouse(data) { export function deleteWarehouse(data) {
return request({ return request({
url: '/base-data-manage/location/location?locationId=' + data.locationId, url: '/base-data-manage/location/location',
method: 'delete', method: 'delete',
params: {
locationId: data.locationId,
busNo: data.busNo
}
}) })
} }

View File

@@ -153,7 +153,7 @@ const { proxy } = getCurrentInstance();
const loading = ref(true); const loading = ref(true);
const organization = ref([]); const organization = ref([]);
const queryParams = ref({ const queryParams = ref({
locationFormList: [11, 16], locationFormList: [11, 16, 17],
}); });
const open = ref(false); const open = ref(false);
const form = ref({ const form = ref({
@@ -173,7 +173,7 @@ const { warehous_type } = proxy.useDict('warehous_type');
const rules = ref({ const rules = ref({
busNo: [{ required: false, message: '请输入科室编号', trigger: 'change' }], busNo: [{ required: false, message: '请输入科室编号', trigger: 'change' }],
name: [ name: [
{ required: true, message: '请输入仓库名称', trigger: 'change' }, { required: true, message: '请输入', trigger: 'change' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'change' }, { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'change' },
], ],
formEnum: [{ required: true, message: '请选择仓库类型', trigger: 'change' }], formEnum: [{ required: true, message: '请选择仓库类型', trigger: 'change' }],
@@ -211,7 +211,7 @@ function resetQuery() {
} }
function handleEnable(row) { function handleEnable(row) {
enableLocation([row.id]).then((res) => { enableLocation({ locationId: row.id, busNo: row.busNo }).then((res) => {
if (res.code == 200) { if (res.code == 200) {
proxy.$modal.msgSuccess('启用成功'); proxy.$modal.msgSuccess('启用成功');
handleQuery(); handleQuery();
@@ -220,7 +220,7 @@ function handleEnable(row) {
} }
function handleUnable(row) { function handleUnable(row) {
unableLocation([row.id]).then((res) => { unableLocation({ locationId: row.id, busNo: row.busNo }).then((res) => {
if (res.code == 200) { if (res.code == 200) {
proxy.$modal.msgSuccess('停用成功'); proxy.$modal.msgSuccess('停用成功');
handleQuery(); handleQuery();
@@ -243,15 +243,14 @@ function getPageList() {
} }
function handleAdd() { function handleAdd() {
title.value = '添加药库药房'; title.value = '添加库房/药房/耗材库';
open.value = true; open.value = true;
editShow.value = false; editShow.value = false;
reset(); reset();
} }
function handelEdit(row) { function handelEdit(row) {
console.log(warehous_type); title.value = '编辑库房/药房/耗材库';
title.value = '编辑药库药房';
open.value = true; open.value = true;
editShow.value = true; editShow.value = true;
setTimeout(() => { setTimeout(() => {
@@ -281,7 +280,6 @@ function submitForm() {
getPageList(); getPageList();
}); });
} else { } else {
alert('456789');
updateWarehouse(form.value).then((res) => { updateWarehouse(form.value).then((res) => {
proxy.$modal.msgSuccess('操作成功'); proxy.$modal.msgSuccess('操作成功');
open.value = false; open.value = false;
@@ -294,11 +292,29 @@ function submitForm() {
// 删除 // 删除
function handelDelete(data) { function handelDelete(data) {
loading.value = true; proxy.$modal.confirm('是否确认删除该仓库位置?', '提示', {
deleteWarehouse({ locationId: data.id }).then((res) => { confirmButtonText: '确定',
proxy.$modal.msgSuccess('操作成功'); cancelButtonText: '取消',
loading.value = false; type: 'warning'
getPageList(); }).then(() => {
loading.value = true;
deleteWarehouse({ locationId: data.id, busNo: data.busNo }).then((res) => {
if (res.code === 200) {
proxy.$modal.msgSuccess('删除成功');
getPageList();
} else {
// 检查错误信息是否与药品数据关联有关
if (res.msg && res.msg.includes('药品')) {
proxy.$modal.msgError('该仓库名称已有药品信息请核对确认');
} else {
proxy.$modal.msgError('删除失败:' + (res.msg || '未知错误'));
}
}
}).catch(() => {
proxy.$modal.msgError('删除失败');
}).finally(() => {
loading.value = false;
});
}); });
} }
// // 停用 // // 停用

View File

@@ -10,6 +10,16 @@ export function getRegistrationfeeList(query) {
}) })
} }
// 根据位置id筛选医生
export function getPractitionerMetadata(query) {
return request({
url: '/charge-manage/register/practitioner-metadata',
method: 'get',
params: query
})
}
// 查询服务管理详细 // 查询服务管理详细
export function getRegistrationfeeOne(id) { export function getRegistrationfeeOne(id) {
return request({ return request({

View File

@@ -102,7 +102,7 @@
/> />
<el-table-column <el-table-column
label="地点" label="地点"
align="center" align="center"
key="locationId_dictText" key="locationId_dictText"
prop="locationId_dictText" prop="locationId_dictText"
/> />
@@ -228,6 +228,7 @@
placeholder="请选择提供部门" placeholder="请选择提供部门"
check-strictly check-strictly
clearable clearable
@change="handleDeptChange"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
@@ -279,6 +280,25 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8">
<el-form-item label="医生:" prop="practitionerId">
<el-select
v-model="form.practitionerId"
placeholder="医生"
clearable
style="width: 240px"
@change="setInfo"
ref="doctorRef"
>
<el-option
v-for="doctor in doctorList"
:key="doctor.id"
:label="`${doctor.name}${doctor.qualification ? ' - '+doctor.qualification : ''}${doctor.department ? ' - '+doctor.department : ''}`"
:value="doctor.id"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row> </el-row>
<div class="title">费用管理</div> <div class="title">费用管理</div>
<el-row> <el-row>
@@ -368,28 +388,13 @@ import {
locationTreeSelect, locationTreeSelect,
delRegistrationfee, delRegistrationfee,
getInitOption, getInitOption,
getPractitionerMetadata,
} from './components/registrationfee'; } from './components/registrationfee';
const router = useRouter(); const router = useRouter();
const { proxy } = getCurrentInstance();
const registrationfeeRef = ref(null); // 初始化 ref const registrationfeeRef = ref(null); // 初始化 ref
const { const { proxy } = getCurrentInstance();
adm_location, const { adm_location, category_code, service_type_code, specialty_code, med_chrgitm_type, fin_type_code, yb_type,} = proxy.useDict( 'adm_location', 'category_code', 'service_type_code', 'specialty_code', 'med_chrgitm_type', 'fin_type_code', 'yb_type');
category_code,
service_type_code,
specialty_code,
med_chrgitm_type,
fin_type_code,
yb_type,
} = proxy.useDict(
'adm_location',
'category_code',
'service_type_code',
'specialty_code',
'med_chrgitm_type',
'fin_type_code',
'yb_type'
);
const registrationfeeList = ref([]); const registrationfeeList = ref([]);
const open = ref(false); const open = ref(false);
@@ -403,12 +408,19 @@ const total = ref(0);
const title = ref(''); const title = ref('');
const activeFlagOptions = ref(undefined); const activeFlagOptions = ref(undefined);
const appointmentRequiredFlagOptions = ref(undefined); const appointmentRequiredFlagOptions = ref(undefined);
const doctorList = ref([]);
const deptOptions = ref(undefined); // 部门树选项 const deptOptions = ref(undefined); // 部门树选项
const locationOptions = ref(undefined); // 地点树选项 const locationOptions = ref(undefined); // 地点树选项
// 是否停用 // 是否停用
const statusFlagOptions = ref(undefined); const statusFlagOptions = ref(undefined);
// 获取选中的医生信息
const getSelectedDoctorInfo = computed(() => {
if (!form.value.practitionerId) return null;
return doctorList.value.find(doctor => doctor.id === form.value.practitionerId) || null;
});
const data = reactive({ const data = reactive({
form: {}, form: {},
queryParams: { queryParams: {
@@ -427,7 +439,7 @@ const data = reactive({
// locationId: [{ required: true, message: "地点不能为空", trigger: "blur" }], // locationId: [{ required: true, message: "地点不能为空", trigger: "blur" }],
name: [{ required: true, message: '服务名称不能为空', trigger: 'blur' }], name: [{ required: true, message: '服务名称不能为空', trigger: 'blur' }],
contact: [{ required: true, message: '联系人电话不能为空', trigger: 'blur' }], contact: [{ required: true, message: '联系人电话不能为空', trigger: 'blur' }],
appointmentRequiredFlag: [{ required: true, message: '预约要求不能为空', trigger: 'blur' }], practitionerId: [{ required: true, message: '医生不能为空', trigger: 'blur' }],
activeFlag: [{ required: true, message: '活动标识不能为空', trigger: 'blur' }], activeFlag: [{ required: true, message: '活动标识不能为空', trigger: 'blur' }],
chargeName: [{ required: true, message: '名称不能为空', trigger: 'blur' }], chargeName: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
description: [{ required: true, message: '描述不能为空', trigger: 'blur' }], description: [{ required: true, message: '描述不能为空', trigger: 'blur' }],
@@ -452,6 +464,10 @@ function getRegistrationfeeTypeList() {
console.log(response, 'response'); console.log(response, 'response');
activeFlagOptions.value = response.data.activeFlagOptions; // 活动标记 activeFlagOptions.value = response.data.activeFlagOptions; // 活动标记
appointmentRequiredFlagOptions.value = response.data.appointmentRequiredFlagOptions; // 预约必填标记 appointmentRequiredFlagOptions.value = response.data.appointmentRequiredFlagOptions; // 预约必填标记
// 获取医生列表
if (response.data.doctorList) {
doctorList.value = response.data.doctorList;
}
}); });
} }
@@ -531,9 +547,60 @@ function reset() {
ybType: undefined, ybType: undefined,
title: undefined, title: undefined,
comment: undefined, comment: undefined,
practitionerId: undefined,
doctorName: undefined
}; };
proxy.resetForm('registrationfeeRef'); proxy.resetForm('registrationfeeRef');
} }
/** 设置医生信息 */
function setInfo() {
const doctor = doctorList.value.find(item => item.id === form.value.practitionerId);
if (doctor) {
form.value.doctorName = doctor.name;
} else {
form.value.doctorName = '';
}
}
/** 科室变化时,加载对应医生 */
function handleDeptChange(orgId) {
if (!orgId) {
doctorList.value = []; // 清空医生
form.value.practitionerId = null;
return;
}
// 调用接口获取该科室下的医生
getPractitionerByOrgId(orgId);
}
/** 根据科室ID获取医生列表 */
function getPractitionerByOrgId(orgId) {
// 假设你已有类似 outpatientregistration 中的 getPractitionerMetadata 接口
// 如果 registrationfee/components/registrationfee.js 中没有,需要补充或复用
getPractitionerMetadata({ orgId }).then((response) => {
doctorList.value = response.data.records || [];
// 如果当前选中的医生不在新列表中,清空选择
if (form.value.practitionerId && !doctorList.value.some(d => d.id === form.value.practitionerId)) {
form.value.practitionerId = null;
form.value.doctorName = '';
}
// 如果医生列表不为空且没有选中的医生,默认选中第一个医生
if (doctorList.value.length > 0 && !form.value.practitionerId) {
form.value.practitionerId = doctorList.value[0].id;
form.value.doctorName = doctorList.value[0].name;
}
}).catch(() => {
doctorList.value = [];
form.value.practitionerId = null;
});
}
/** 取消按钮 */ /** 取消按钮 */
function cancel() { function cancel() {
open.value = false; open.value = false;
@@ -555,6 +622,11 @@ function handleUpdate(row) {
form.value.cwTypeCode = '1011'; form.value.cwTypeCode = '1011';
open.value = true; open.value = true;
title.value = '编辑'; title.value = '编辑';
// 当编辑时如果有科室ID自动加载对应科室的医生
if (form.value.offeredOrgId) {
getPractitionerByOrgId(form.value.offeredOrgId);
}
} }
/** 提交按钮 */ /** 提交按钮 */
function submitForm() { function submitForm() {
@@ -639,6 +711,11 @@ function handleView(row) {
getRegistrationfeeOne(row.id).then((response) => { getRegistrationfeeOne(row.id).then((response) => {
console.log(response, 'responsebbbb', row.id); console.log(response, 'responsebbbb', row.id);
form.value = response.data; form.value = response.data;
// 当查看详情时如果有科室ID自动加载对应科室的医生
if (form.value.offeredOrgId) {
getPractitionerByOrgId(form.value.offeredOrgId);
}
}); });
} }
@@ -766,4 +843,25 @@ init();
font-size: large; font-size: large;
margin-bottom: 10px; margin-bottom: 10px;
} }
/* 医生信息显示样式 */
.selected-info {
margin-top: 8px;
padding: 10px;
background-color: #f5f7fa;
border-radius: 4px;
border: 1px solid #e4e7ed;
}
.info-item {
display: inline-block;
margin-right: 16px;
color: #606266;
font-size: 14px;
}
.info-item:first-child {
color: #303133;
font-weight: 500;
}
</style> </style>

View File

@@ -86,84 +86,84 @@
</el-col> --> </el-col> -->
</el-row> </el-row>
<el-table v-loading="loading" :data="diseaseList" @selection-change="handleSelectionChange"> <!-- 添加外层滚动容器确保表格可以水平滚动 -->
<el-table-column type="selection" width="50" align="center" /> <div class="table-scroll-container">
<el-table-column label="编码" align="center" key="conditionCode" prop="conditionCode" /> <!-- 移除style="width: 100%"让Element UI表格根据内容自动调整 -->
<el-table-column <el-table v-loading="loading" :data="diseaseList" @selection-change="handleSelectionChange" border resizable>
label="名称" <el-table-column type="selection" width="50" align="center" />
align="center" <!-- 使用prop属性并设置合适的最小宽度启用列宽调整 -->
key="name" <el-table-column label="编码" align="center" prop="conditionCode" min-width="150" />
prop="name" <!-- 名称列使用标准配置 -->
:show-overflow-tooltip="true" <el-table-column
/> label="名称"
<el-table-column align="center"
label="疾病分类" prop="name"
align="center" min-width="280"
key="sourceEnum_enumText" />
prop="sourceEnum_enumText" <!-- 其他列使用标准配置 -->
:show-overflow-tooltip="true" <el-table-column
/> label="疾病分类"
<el-table-column align="center"
label="拼音助记码" prop="sourceEnum_enumText"
align="center" min-width="180"
key="pyStr" />
prop="pyStr" <el-table-column
:show-overflow-tooltip="true" label="拼音助记码"
/> align="center"
<el-table-column prop="pyStr"
label="类型" min-width="220"
align="center" />
key="typeCode_dictText" <el-table-column
prop="typeCode_dictText" label="类型"
:show-overflow-tooltip="true" align="center"
/> prop="typeCode_dictText"
<el-table-column min-width="120"
label="医保编码 " />
align="center" <el-table-column
key="ybNo" label="医保编码"
prop="ybNo" align="center"
:show-overflow-tooltip="true" prop="ybNo"
/> min-width="180"
<el-table-column />
label="医保标记" <el-table-column
align="center" label="医保标记"
key="ybMatchFlag" align="center"
prop="ybMatchFlag_enumText" prop="ybMatchFlag_enumText"
/> min-width="150"
<el-table-column />
label="医保对码标志" <el-table-column
align="center" label="医保对码标志"
key="ybMatchFlag" align="center"
prop="ybMatchFlag_enumText" prop="ybMatchFlag_enumText"
/> min-width="150"
<el-table-column />
label="状态" <el-table-column
align="center" label="状态"
key="statusEnum_enumText" align="center"
prop="statusEnum_enumText" prop="statusEnum_enumText"
width="160" min-width="150"
/> />
<el-table-column <el-table-column
label="描述" label="描述"
align="center" align="center"
key="description" prop="description"
prop="description" min-width="250"
width="160" />
/> <el-table-column
<el-table-column label="操作"
label="操作" align="center"
align="center" min-width="120"
width="150" class-name="small-padding"
class-name="small-padding fixed-width" >
> <template #default="scope">
<template #default="scope"> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" >编辑</el-button
>编辑</el-button >
> </template>
</template> </el-table-column>
</el-table-column> </el-table>
</el-table> </div>
<pagination <pagination
v-show="total > 0" v-show="total > 0"
:total="total" :total="total"
v-model:page="queryParams.pageNo" v-model:page="queryParams.pageNo"
@@ -504,4 +504,40 @@ getList();
display: flex; display: flex;
align-items: center; align-items: center;
} }
/* 表格外层滚动容器,确保表格整体可以水平滚动 */
.table-scroll-container {
width: 100%;
overflow-x: auto;
margin-bottom: 10px;
}
/* 表格样式调整,移除默认的最大宽度限制 */
.table-scroll-container >>> .el-table {
min-width: 100%;
width: auto;
}
/* 表格外层容器的滚动条样式 */
.table-scroll-container::-webkit-scrollbar {
height: 12px;
}
.table-scroll-container::-webkit-scrollbar-thumb {
background: rgba(0, 120, 215, 0.7);
border-radius: 6px;
border: 2px solid rgba(255, 255, 255, 0.3);
transition: background-color 0.2s ease;
}
.table-scroll-container::-webkit-scrollbar-thumb:hover {
background: rgba(0, 120, 215, 0.9);
}
.table-scroll-container::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.15);
border-radius: 6px;
border: 2px solid rgba(0, 0, 0, 0.05);
}
</style> </style>

View File

@@ -182,6 +182,8 @@
:paymentId="paymentId" :paymentId="paymentId"
:details="details" :details="details"
:chargedItems="chargedItems" :chargedItems="chargedItems"
:feeType="patientInfo.medfeePaymtdCode"
:medfee_paymtd_code="medfee_paymtd_code"
@refresh="getPatientList" @refresh="getPatientList"
/> />
</div> </div>
@@ -203,11 +205,13 @@ import useUserStore from '@/store/modules/user';
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const userStore = useUserStore(); const userStore = useUserStore();
const { medfee_paymtd_code } = proxy.useDict('medfee_paymtd_code');
const queryParams = ref({ const queryParams = ref({
pageNum: 1, pageNum: 1,
pageSize: 50, pageSize: 50,
statusEnum: 1, statusEnum: 1,
}); });
const totalAmounts = ref(0); const totalAmounts = ref(0);
const selectedRows = ref([]); const selectedRows = ref([]);
const patientList = ref([]); const patientList = ref([]);

View File

@@ -11,7 +11,7 @@
<el-text size="large" style="display: block; margin-bottom: 15px"> <el-text size="large" style="display: block; margin-bottom: 15px">
退费日期{{ currentDate }} 退费日期{{ currentDate }}
</el-text> </el-text>
<el-text size="large">费用性质{{ '自费' }}</el-text> <el-text size="large">费用性质{{ getFeeTypeText }}</el-text>
<div class="amount-row"> <div class="amount-row">
<el-text size="large">应退金额</el-text> <el-text size="large">应退金额</el-text>
<el-text size="large" type="primary" class="amount"> <el-text size="large" type="primary" class="amount">
@@ -160,6 +160,14 @@ const props = defineProps({
type: Object, type: Object,
default: undefined, default: undefined,
}, },
medfee_paymtd_code: {
type: Array,
default: () => [],
},
feeType: {
type: String,
default: '',
},
}); });
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
@@ -324,6 +332,23 @@ const returnedAmount = computed(() => {
function close() { function close() {
emit('close'); emit('close');
} }
// 获取费用性质文本
const getFeeTypeText = computed(() => {
if (!props.medfee_paymtd_code || !Array.isArray(props.medfee_paymtd_code)) {
return '自费';
}
// 如果有feeType根据feeType查找对应的文本
if (props.feeType) {
const dict = props.medfee_paymtd_code.find(item => item.value === props.feeType);
return dict ? dict.label : '自费';
}
// 如果只有一个选项,直接返回第一个选项的文本
if (props.medfee_paymtd_code.length === 1) {
return props.medfee_paymtd_code[0].label || '自费';
}
return '自费';
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -178,6 +178,8 @@
:paymentId="paymentId" :paymentId="paymentId"
:chargeItemIds="chargeItemIdList" :chargeItemIds="chargeItemIdList"
:details="details" :details="details"
:medfee_paymtd_code="medfee_paymtd_code"
:feeType="patientInfo.medfeePaymtdCode"
/> />
</div> </div>
</template> </template>
@@ -197,6 +199,8 @@ import RefundDialog from './components/refundDialog.vue';
import Decimal from 'decimal.js'; import Decimal from 'decimal.js';
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
// 获取费用性质字典
const { medfee_paymtd_code } = proxy.useDict('medfee_paymtd_code');
const queryParams = ref({ const queryParams = ref({
pageNum: 1, pageNum: 1,
pageSize: 50, pageSize: 50,

View File

@@ -4,7 +4,7 @@
<el-text size="large" style="display: block; margin-bottom: 15px"> <el-text size="large" style="display: block; margin-bottom: 15px">
收费日期{{ currentDate }} 收费日期{{ currentDate }}
</el-text> </el-text>
<el-text size="large">费用性质{{ '自费' }}</el-text> <el-text size="large">费用性质{{ getFeeTypeText }}</el-text>
<div class="amount-row"> <div class="amount-row">
<el-text size="large">应收金额</el-text> <el-text size="large">应收金额</el-text>
<el-text size="large" type="primary" class="amount"> <el-text size="large" type="primary" class="amount">
@@ -97,6 +97,23 @@ import { computed, watch, reactive, ref, getCurrentInstance, nextTick } from 'vu
import { Delete } from '@element-plus/icons-vue'; import { Delete } from '@element-plus/icons-vue';
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
// 获取费用性质文本
const getFeeTypeText = computed(() => {
if (!props.medfee_paymtd_code || !Array.isArray(props.medfee_paymtd_code)) {
return '';
}
// 如果有feeType根据feeType查找对应的文本
if (props.feeType) {
const dict = props.medfee_paymtd_code.find(item => item.value === props.feeType);
return dict ? dict.label : '';
}
// 如果只有一个选项,直接返回第一个选项的文本
if (props.medfee_paymtd_code.length === 1) {
return props.medfee_paymtd_code[0].label || '';
}
return '';
});
const props = defineProps({ const props = defineProps({
open: { open: {
type: Boolean, type: Boolean,
@@ -130,6 +147,14 @@ const props = defineProps({
type: Object, type: Object,
default: undefined, default: undefined,
}, },
medfee_paymtd_code: {
type: Array,
default: () => [],
},
feeType: {
type: String,
default: '',
}
}); });
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
@@ -271,19 +296,17 @@ function submit() {
return; return;
} }
dialogLoading.value = true; dialogLoading.value = true;
// 根据费用性质动态设置合同编号
if (props.transformedData && props.transformedData.accountFormData) {
// 直接使用传入的feeType作为contractNo
// 如果feeType存在就使用否则使用patientInfo中的medfeePaymtdCode最后默认使用'0000'
props.transformedData.accountFormData.contractNo = props.feeType || props.patientInfo?.medfeePaymtdCode || '0000';
}
savePayment({ savePayment({
// paymentEnum: 0,
// kindEnum: 1,
// patientId: props.patientInfo.patientId,
// encounterId: props.patientInfo.encounterId,
// chargeItemIds: props.chargeItemIds,
outpatientRegistrationAddParam: props.transformedData, outpatientRegistrationAddParam: props.transformedData,
chrgBchno: props.chrgBchno, chrgBchno: props.chrgBchno,
busNo: props.registerBusNo, busNo: props.registerBusNo,
paymentDetails: formData.selfPay, paymentDetails: formData.selfPay,
// ybFlag: '0',
// eleFlag: '0',
// returnedAmount: parseFloat(returnedAmount.value),
}) })
.then((res) => { .then((res) => {
if (res.code == 200) { if (res.code == 200) {

View File

@@ -213,4 +213,31 @@ export function gerPreInfo(userMaessage) {
method: 'post', method: 'post',
params: userMaessage params: userMaessage
}) })
}
/**
* 根据就诊卡号查询挂号记录
* 注意此接口可能不存在实际使用getOutpatientRegistrationCurrent接口
* 保留此函数以便将来使用目前使用getOutpatientRegistrationCurrent替代
*/
export function getRegistrationByCardNo(query) {
// 如果后端提供了专门的接口,可以取消注释使用
// return request({
// url: '/charge-manage/register/encounter-by-card',
// method: 'get',
// params: query
// })
// 暂时使用现有的接口
return getOutpatientRegistrationCurrent(query);
}
/**
* 补打挂号
*/
export function reprintRegistration(data) {
return request({
url: '/charge-manage/register/reprint',
method: 'post',
data: data
})
} }

View File

@@ -1,48 +1,17 @@
<template> <template>
<!-- <div class="app-container"> --> <!-- <div class="app-container"> -->
<!-- 添加或修改对话框 --> <!-- 添加或修改对话框 -->
<el-dialog :title="title" v-model="visible" width="980px" append-to-body> <el-dialog :title="title" v-model="visible" width="1020px" append-to-body>
<el-form ref="patientRef" :model="form" :rules="rules" label-width="100px"> <el-form ref="patientRef" :model="form" :rules="rules" label-width="120px" label-position="left">
<el-row> <!-- 第一行姓名民族性别 -->
<el-row :gutter="10">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="姓名" prop="name"> <el-form-item label="姓名" prop="name" label-width="80px">
<el-input v-model="form.name" clearable :disabled="isViewMode" /> <el-input v-model="form.name" clearable :disabled="isViewMode" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="性别" prop="genderEnum"> <el-form-item label="民族" prop="nationalityCode" label-width="80px">
<el-radio-group v-model="form.genderEnum" :disabled="isViewMode">
<el-radio
v-for="item in administrativegenderList"
:key="item.value"
:label="item.value"
>
{{ item.info }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<!-- <el-col :span="8">
<el-form-item label="活动标识" prop="tempFlag">
<el-radio-group v-model="form.tempFlag" :disabled="isViewMode">
<el-radio v-for="dict in patient_temp_flag" :key="dict.value" :label="dict.value">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col> -->
<el-col :span="8">
<el-form-item label="联系方式" prop="phone">
<el-input v-model="form.phone" clearable :disabled="isViewMode" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="证件号码" prop="idCard">
<el-input v-model="form.idCard" clearable :disabled="isViewMode" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="民族" prop="nationalityCode">
<el-select v-model="form.nationalityCode" clearable filterable :disabled="isViewMode"> <el-select v-model="form.nationalityCode" clearable filterable :disabled="isViewMode">
<el-option <el-option
v-for="item in nationality_code" v-for="item in nationality_code"
@@ -54,10 +23,22 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="卡类别" prop="typeCode"> <el-form-item label="别" prop="genderEnum" label-width="80px">
<el-radio-group v-model="form.genderEnum" :disabled="isViewMode">
<el-radio :label="0">男性</el-radio>
<el-radio :label="1">女性</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行证件类别证件号码*年龄 -->
<el-row :gutter="10">
<el-col :span="8">
<el-form-item label="证件类别" prop="typeCode" label-width="80px">
<el-select <el-select
v-model="form.typeCode" v-model="form.typeCode"
placeholder="卡类别" placeholder="就诊卡"
clearable clearable
:disabled="isViewMode" :disabled="isViewMode"
> >
@@ -71,15 +52,36 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="就诊卡号" prop="identifierNo"> <el-form-item label="证件号码" prop="idCard" label-width="80px">
<el-input v-model="form.identifierNo" clearable :disabled="isViewMode" /> <el-input v-model="form.idCard" clearable :disabled="isViewMode" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="国家编码" prop="countryCode"> <el-form-item label="年龄" prop="age" label-width="80px">
<el-input
v-model="form.age"
:disabled="isViewMode"
@input="handleAgeInput"
placeholder="请输入龄"
>
<template #suffix></template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行国家编码*联系方式工作单位 -->
<el-row :gutter="10">
<el-col :span="8">
<el-form-item label="国家编码" prop="countryCode" label-width="80px">
<el-input v-model="form.countryCode" clearable :disabled="isViewMode" /> <el-input v-model="form.countryCode" clearable :disabled="isViewMode" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8">
<el-form-item label="联系方式" prop="phone" label-width="80px">
<el-input v-model="form.phone" clearable :disabled="isViewMode" />
</el-form-item>
</el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="出生日期" prop="birthDate"> <el-form-item label="出生日期" prop="birthDate">
<el-date-picker <el-date-picker
@@ -94,12 +96,14 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- <el-col :span="6">
<el-form-item label="年龄" prop="age"> <!-- 第三行就诊卡号国家编码出生日期 -->
<el-input v-model="form.age" clearable :disabled="isViewMode"/>
</el-form-item>
</el-col> -->
<el-row> <el-row>
<el-col :span="8">
<el-form-item label="就诊卡号" prop="identifierNo">
<el-input v-model="form.identifierNo" clearable :disabled="isViewMode" />
</el-form-item>
</el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="职业" prop="prfsEnum"> <el-form-item label="职业" prop="prfsEnum">
<el-select v-model="form.prfsEnum" placeholder="职业" clearable :disabled="isViewMode"> <el-select v-model="form.prfsEnum" placeholder="职业" clearable :disabled="isViewMode">
@@ -117,48 +121,8 @@
<el-input v-model="form.workCompany" clearable :disabled="isViewMode" /> <el-input v-model="form.workCompany" clearable :disabled="isViewMode" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8">
<el-form-item label="年龄" prop="age">
<el-input
v-model="form.age"
:disabled="isViewMode"
@input="handleAgeInput"
placeholder="请输入年龄"
>
<template #suffix></template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="联系人" prop="linkName">
<el-input v-model="form.linkName" clearable :disabled="isViewMode" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系人关系" prop="linkRelationCode">
<el-select
v-model="form.linkRelationCode"
placeholder="联系人关系"
clearable
:disabled="isViewMode"
>
<el-option
v-for="item in familyrelationshiptypeList"
:key="item.value"
:label="item.info"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系人电话" prop="linkRelationCode">
<el-input v-model="form.linkTelcom" clearable :disabled="isViewMode" />
</el-form-item>
</el-col>
</el-row> </el-row>
<!-- 地址选择详细地址 -->
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="地址选择" prop="addressSelect"> <el-form-item label="地址选择" prop="addressSelect">
@@ -176,12 +140,14 @@
</el-cascader> </el-cascader>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="16">
<el-form-item label="详细地址" prop="address"> <el-form-item label="详细地址" prop="address">
<el-input v-model="form.address" clearable :disabled="isViewMode" /> <el-input v-model="form.address" clearable :disabled="isViewMode" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- 第六行血型ABO血型RH -->
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="血型ABO" prop="bloodAbo"> <el-form-item label="血型ABO" prop="bloodAbo">
@@ -213,6 +179,8 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- 第七行婚姻状态死亡时间 -->
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="婚姻状态" prop="maritalStatusEnum"> <el-form-item label="婚姻状态" prop="maritalStatusEnum">
@@ -231,7 +199,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="16">
<el-form-item label="死亡时间" prop="deceasedDate"> <el-form-item label="死亡时间" prop="deceasedDate">
<el-date-picker <el-date-picker
v-model="form.deceasedDate" v-model="form.deceasedDate"
@@ -244,6 +212,59 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- 第八行监护人监护人关系监护人电话 -->
<el-row>
<el-col :span="8">
<el-form-item label="监护人" prop="guardianName">
<el-input v-model="form.guardianName" clearable :disabled="isViewMode" placeholder="请输入监护人" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="监护人关系" prop="guardianRelation">
<el-select v-model="form.guardianRelation" placeholder="监护人关系" clearable :disabled="isViewMode">
<el-option
v-for="item in familyrelationshiptypeList"
:key="item.value"
:label="item.info"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="监护人电话" prop="guardianPhone">
<el-input v-model="form.guardianPhone" clearable :disabled="isViewMode" placeholder="请输入监护人电话" />
</el-form-item>
</el-col>
</el-row>
<!-- 第九行监护人证件类型监护人证件号码 -->
<el-row>
<el-col :span="8">
<el-form-item label="监护人证件类型" prop="guardianIdType">
<el-select v-model="form.guardianIdType" placeholder="请选择" clearable :disabled="isViewMode">
<el-option label="身份证" value="id_card" />
<el-option label="护照" value="passport" />
<el-option label="其他" value="other" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="16">
<el-form-item label="监护人证件号码" prop="guardianIdNo">
<el-input v-model="form.guardianIdNo" clearable :disabled="isViewMode" placeholder="请输入监护人证件号码" />
</el-form-item>
</el-col>
</el-row>
<!-- 第十行监护人地址 -->
<el-row>
<el-col :span="24">
<el-form-item label="监护人地址" prop="guardianAddress">
<el-input v-model="form.guardianAddress" clearable :disabled="isViewMode" placeholder="请输入监护人详细地址" />
</el-form-item>
</el-col>
</el-row>
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
@@ -361,6 +382,23 @@ const validateIdCard = (rule, value, callback) => {
callback(); // 校验通过 callback(); // 校验通过
} }
// 监护人信息条件验证函数
const validateGuardianInfo = (rule, value, callback) => {
// 只有当年龄小于18岁时才验证监护人信息
if (form.value.age) {
// 提取年龄数字部分
const ageMatch = form.value.age.toString().match(/\d+/);
if (ageMatch) {
const age = parseInt(ageMatch[0]);
// 如果年龄小于18岁监护人信息必须填写
if (age < 18 && !value) {
return callback(new Error('年龄小于18岁的患者必须填写监护人信息'));
}
}
}
// 18岁及以上患者或年龄未填写时跳过验证
callback();
}
const data = reactive({ const data = reactive({
isViewMode: false, isViewMode: false,
@@ -378,11 +416,16 @@ const data = reactive({
phone: [{ required: true, message: '联系方式不能为空', trigger: 'change' }], phone: [{ required: true, message: '联系方式不能为空', trigger: 'change' }],
identifierNo: [{ required: true, message: '就诊卡号不能为空', trigger: 'change' }], identifierNo: [{ required: true, message: '就诊卡号不能为空', trigger: 'change' }],
idCard: [ idCard: [
{ required: true, message: '证件号码不能为空', trigger: 'change' }, { required: false, message: '证件号码不能为空', trigger: 'change' },
{ validator: validateIdCard, trigger: 'blur' }, { validator: validateIdCard, trigger: 'blur' },
{ validator: validateUniquePatient, trigger: 'blur' } { validator: validateUniquePatient, trigger: 'blur' }
], ],
birthDate: [{ required: false, message: '请选择出生日期', trigger: 'change' }], birthDate: [{ required: false, message: '请选择出生日期', trigger: 'change' }],
// 监护人信息条件验证规则
guardianName: [{ validator: validateGuardianInfo, trigger: 'blur' }],
guardianRelation: [{ validator: validateGuardianInfo, trigger: 'blur' }],
guardianPhone: [{ validator: validateGuardianInfo, trigger: 'blur' }],
guardianIdNo: [{ validator: validateGuardianInfo, trigger: 'blur' }],
}, },
}); });
@@ -572,13 +615,16 @@ function submitForm() {
form.value.idCard.toString().substring(12, 14); form.value.idCard.toString().substring(12, 14);
console.log(form.value.birthDate, 123); console.log(form.value.birthDate, 123);
} }
// 进行表单验证
proxy.$refs['patientRef'].validate((valid) => { proxy.$refs['patientRef'].validate((valid) => {
if (valid) { if (valid) {
// 使用 // 提交表单前的处理
if (!form.value.identifierNo) { if (!form.value.identifierNo) {
form.value.typeCode = undefined; form.value.typeCode = undefined;
} }
form.value.address = getAddress(form); form.value.address = getAddress(form);
// 提交新增患者请求
addPatient(form.value).then((response) => { addPatient(form.value).then((response) => {
proxy.$modal.msgSuccess('新增成功'); proxy.$modal.msgSuccess('新增成功');
getPatientInfo(response.data.idCard); getPatientInfo(response.data.idCard);
@@ -624,4 +670,12 @@ defineExpose({
line-height: 1.2; /* 调整行间距 */ line-height: 1.2; /* 调整行间距 */
margin-bottom: 4px; /* 调整 label 和输入框之间的间距 */ margin-bottom: 4px; /* 调整 label 和输入框之间的间距 */
} }
/* 防止表单标签换行 */
:deep(.el-form-item__label) {
white-space: nowrap;
overflow: visible;
text-overflow: clip;
width: 120px;
}
</style> </style>

View File

@@ -11,7 +11,7 @@
<el-text size="large" style="display: block; margin-bottom: 15px"> <el-text size="large" style="display: block; margin-bottom: 15px">
退费日期{{ currentDate }} 退费日期{{ currentDate }}
</el-text> </el-text>
<el-text size="large">费用性质{{ '自费' }}</el-text> <el-text size="large">费用性质{{ getFeeTypeText }}</el-text>
<div class="amount-row"> <div class="amount-row">
<el-text size="large">应退金额</el-text> <el-text size="large">应退金额</el-text>
<el-text size="large" type="primary" class="amount"> <el-text size="large" type="primary" class="amount">
@@ -112,6 +112,22 @@ import { cancelRegister } from './outpatientregistration';
import { computed, watch, reactive, ref, getCurrentInstance } from 'vue'; import { computed, watch, reactive, ref, getCurrentInstance } from 'vue';
import { Delete } from '@element-plus/icons-vue'; import { Delete } from '@element-plus/icons-vue';
// 获取费用性质文本
const getFeeTypeText = computed(() => {
// 优先使用从挂号记录传递过来的费用性质名称
if (props.contractName) {
return props.contractName;
}
// 如果没有传递名称,则根据费用性质代码查找
if (props.feeType && props.medfee_paymtd_code && Array.isArray(props.medfee_paymtd_code)) {
const dictItem = props.medfee_paymtd_code.find(item => item.value === props.feeType);
return dictItem ? dictItem.label : '自费';
}
return '自费'; // 默认值
});
const props = defineProps({ const props = defineProps({
open: { open: {
type: Boolean, type: Boolean,
@@ -135,6 +151,18 @@ const props = defineProps({
type: [], type: [],
default: [], default: [],
}, },
medfee_paymtd_code: {
type: Array,
default: () => [],
},
feeType: {
type: String,
default: '',
},
contractName: {
type: String,
default: '', // 新增:接收费用性质名称
}
}); });
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();

View File

@@ -0,0 +1,400 @@
<template>
<el-dialog title="补打挂号" v-model="dialogVisible" width="900px" append-to-body destroy-on-close>
<div v-loading="loading">
<!-- 搜索区域 -->
<el-form :model="searchForm" :inline="true" style="margin-bottom: 20px">
<el-form-item label="就诊卡号:">
<el-input
v-model="searchForm.cardNo"
placeholder="请输入就诊卡号"
style="width: 240px"
clearable
@keyup.enter="handleSearch"
>
<template #append>
<el-button icon="Search" @click="handleSearch" />
</template>
</el-input>
</el-form-item>
</el-form>
<!-- 挂号记录列表 -->
<el-table
v-if="registrationList.length > 0"
:data="registrationList"
border
max-height="300"
highlight-current-row
@row-click="handleRowClick"
style="margin-bottom: 20px"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="患者姓名" prop="patientName" width="120" align="center" />
<el-table-column label="就诊卡号" prop="identifierNo" width="150" align="center">
<template #default="scope">
<!-- 优化就诊卡号显示尝试多种可能的字段 -->
{{ getCardNoDisplay(scope.row) }}
</template>
</el-table-column>
<el-table-column label="就诊科室" prop="organizationName" align="center" />
<el-table-column label="医生" prop="practitionerName" width="120" align="center" />
<el-table-column label="挂号费" prop="price" width="100" align="center">
<template #default="scope">
{{ (scope.row.price || scope.row.registrationFee) ? parseFloat(scope.row.price || scope.row.registrationFee).toFixed(2) + ' 元' : '0.00 元' }}
</template>
</el-table-column>
<el-table-column label="诊疗费" prop="activityPrice" width="100" align="center">
<template #default="scope">
{{ (scope.row.activityPrice || scope.row.diagnosisFee) ? parseFloat(scope.row.activityPrice || scope.row.diagnosisFee).toFixed(2) + ' 元' : '0.00 元' }}
</template>
</el-table-column>
<el-table-column label="总金额" prop="totalPrice" width="100" align="center">
<template #default="scope">
{{ scope.row.totalPrice ? scope.row.totalPrice.toFixed(2) + ' 元' : '0.00 元' }}
</template>
</el-table-column>
<el-table-column label="挂号时间" prop="registerTime" width="180" align="center">
<template #default="scope">
<span>{{ parseTime(scope.row.registerTime) }}</span>
</template>
</el-table-column>
</el-table>
<el-empty v-if="!loading && registrationList.length === 0 && hasSearched" description="未找到挂号记录" />
<!-- 补打挂号表单 -->
<el-divider>补打信息</el-divider>
<el-form :model="form" :rules="rules" ref="reprintFormRef" label-width="120px">
<el-row :gutter="24">
<el-col :span="12">
<el-form-item label="就诊卡号:" prop="cardNo">
<el-input v-model="form.cardNo" placeholder="就诊卡号" :disabled="true" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="姓名:" prop="name">
<el-input v-model="form.name" placeholder="姓名" :disabled="true" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item label="就诊科室:" prop="organizationName">
<el-input v-model="form.organizationName" placeholder="就诊科室" :disabled="true" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="医生:" prop="practitionerName">
<el-input v-model="form.practitionerName" placeholder="医生" :disabled="true" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="8">
<el-form-item label="挂号费:" prop="price">
<el-input v-model="form.price" placeholder="挂号费" :disabled="true" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="诊疗费:" prop="activityPrice">
<el-input v-model="form.activityPrice" placeholder="诊疗费" :disabled="true" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="总金额:" prop="totalPrice">
<el-input v-model="form.totalPrice" placeholder="总金额" :disabled="true" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item label="就诊时间:" prop="visitTime">
<el-date-picker
v-model="form.visitTime"
type="datetime"
placeholder="就诊时间"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
:disabled="true"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleSubmit" :disabled="loading || !form.cardNo"> </el-button>
<el-button @click="handleCancel" :disabled="loading"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive, getCurrentInstance, watch } from 'vue';
import { getOutpatientRegistrationCurrent, getOutpatientRegistrationList, reprintRegistration } from './outpatientregistration';
import { parseTime } from '@/utils/his';
import { formatDateStr } from '@/utils/index';
const { proxy } = getCurrentInstance();
const props = defineProps({
open: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['close']);
const dialogVisible = ref(false);
const loading = ref(false);
const hasSearched = ref(false);
const registrationList = ref([]);
const reprintFormRef = ref(null);
const searchForm = reactive({
cardNo: '',
});
const form = reactive({
cardNo: '',
name: '',
organizationName: '',
practitionerName: '',
price: '',
activityPrice: '',
totalPrice: '',
visitTime: '',
encounterId: '',
});
const rules = {
cardNo: [{ required: true, message: '就诊卡号不能为空', trigger: 'blur' }],
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
organizationName: [{ required: true, message: '就诊科室不能为空', trigger: 'blur' }],
practitionerName: [{ required: true, message: '医生不能为空', trigger: 'blur' }],
};
// 监听open属性变化
watch(
() => props.open,
(newVal) => {
dialogVisible.value = newVal;
if (newVal) {
// 打开对话框时重置表单
resetForm();
resetSearch();
}
}
);
// 监听dialogVisible变化同步到父组件
watch(dialogVisible, (newVal) => {
if (!newVal) {
emit('close');
}
});
// 获取就诊卡号用于显示
function getCardNoDisplay(row) {
// 调试信息,打印所有可能的卡号字段
console.log('解析卡号字段:', {
identifierNo: row.identifierNo,
cardNo: row.cardNo,
patientId: row.patientId,
encounterNo: row.encounterNo,
visitNo: row.visitNo,
patient: row.patient ? {
identifierNo: row.patient.identifierNo,
cardNo: row.patient.cardNo
} : null
});
// 尝试所有可能的卡号字段
const cardNo =
row.identifierNo ||
row.cardNo ||
(row.patient && (row.patient.identifierNo || row.patient.cardNo)) ||
row.encounterNo ||
row.visitNo ||
'-';
return cardNo;
}
// 搜索挂号记录
async function handleSearch() {
if (!searchForm.cardNo) {
proxy.$modal.msgWarning('请输入就诊卡号');
return;
}
loading.value = true;
hasSearched.value = true;
registrationList.value = [];
try {
// 简化搜索逻辑直接使用就诊卡号作为searchKey查询
const endDate = new Date();
const startDate = new Date();
startDate.setFullYear(startDate.getFullYear() - 1);
const queryParams = {
pageNo: 1,
pageSize: 100,
registerTimeSTime: formatDateStr(startDate, 'YYYY-MM-DD') + ' 00:00:00',
registerTimeETime: formatDateStr(endDate, 'YYYY-MM-DD') + ' 23:59:59',
searchKey: searchForm.cardNo // 直接使用就诊卡号作为搜索关键字
};
console.log('搜索参数:', queryParams);
// 调用API查询挂号记录
const response = await getOutpatientRegistrationCurrent(queryParams);
if (response.code === 200) {
let records = response.data.records || response.data || [];
console.log('查询到的挂号记录数量:', records.length);
// 记录原始数据用于调试
if (records.length > 0) {
console.log('查询到的记录详情:', records.map(r => ({
id: r.id || r.encounterId,
identifierNo: r.identifierNo,
cardNo: r.cardNo,
patientName: r.patientName || r.name,
patient: r.patient
})));
}
// 显示所有查询到的记录,不进行严格过滤
registrationList.value = records;
if (records.length === 0) {
proxy.$modal.msgWarning('未查询到相关挂号记录,请检查就诊卡号是否正确');
}
} else {
proxy.$modal.msgWarning('查询失败: ' + (response.msg || '接口返回异常'));
}
} catch (error) {
console.error('查询挂号记录异常:', error);
proxy.$modal.msgError('查询出错: ' + (error.message || '网络异常'));
} finally {
loading.value = false;
}
}
// 点击表格行,自动填充表单
function handleRowClick(row) {
console.log('点击的记录:', row);
// 使用getCardNoDisplay函数获取就诊卡号确保与表格显示一致
const cardNo = getCardNoDisplay(row);
form.cardNo = cardNo !== '-' ? cardNo : '';
// 其他表单字段填充
form.name = row.patientName || row.name || (row.patient ? row.patient.name : '') || '';
form.organizationName = row.organizationName || '';
form.practitionerName = row.practitionerName || '';
// 价格相关字段
form.price = row.price ? parseFloat(row.price).toFixed(2) : (row.registrationFee ? parseFloat(row.registrationFee).toFixed(2) : '0.00');
form.activityPrice = row.activityPrice ? parseFloat(row.activityPrice).toFixed(2) : (row.diagnosisFee ? parseFloat(row.diagnosisFee).toFixed(2) : '0.00');
form.totalPrice = row.totalPrice ? parseFloat(row.totalPrice).toFixed(2) : '0.00';
// 就诊时间设置为当前时间
const now = new Date();
form.visitTime = formatDateTime(now);
form.encounterId = row.encounterId || row.id || '';
console.log('填充的表单数据:', form);
}
// 格式化日期时间
function formatDateTime(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// 提交补打挂号
function handleSubmit() {
reprintFormRef.value.validate((valid) => {
if (valid) {
loading.value = true;
const submitData = {
encounterId: form.encounterId,
cardNo: form.cardNo,
name: form.name,
organizationName: form.organizationName,
practitionerName: form.practitionerName,
price: parseFloat(form.price),
activityPrice: parseFloat(form.activityPrice),
totalPrice: parseFloat(form.totalPrice),
visitTime: form.visitTime,
};
reprintRegistration(submitData)
.then((res) => {
if (res.code === 200) {
proxy.$modal.msgSuccess('补打挂号成功');
emit('close', 'success');
dialogVisible.value = false;
} else {
proxy.$modal.msgError(res.msg || '补打挂号失败');
}
})
.catch((error) => {
console.error('补打挂号失败:', error);
proxy.$modal.msgError('补打挂号失败');
})
.finally(() => {
loading.value = false;
});
}
});
}
// 取消
function handleCancel() {
dialogVisible.value = false;
}
// 重置表单
function resetForm() {
Object.assign(form, {
cardNo: '',
name: '',
organizationName: '',
practitionerName: '',
price: '',
activityPrice: '',
totalPrice: '',
visitTime: '',
encounterId: '',
});
if (reprintFormRef.value) {
reprintFormRef.value.clearValidate();
}
}
// 重置搜索
function resetSearch() {
searchForm.cardNo = '';
registrationList.value = [];
hasSearched.value = false;
}
</script>
<style scoped>
.dialog-footer {
text-align: right;
}
</style>

View File

@@ -3,7 +3,22 @@
<el-row> <el-row>
<el-col :span="24" class="card-box"> <el-col :span="24" class="card-box">
<el-card> <el-card>
<template #header> <span style="vertical-align: middle">门诊挂号</span></template> <template #header>
<div style="display: flex; align-items: center; justify-content: space-between; width: 100%">
<span style="vertical-align: middle; font-size: 16px; font-weight: bold;">门诊挂号</span>
<div class="header-buttons">
<el-button type="primary" icon="Document" @click="goToPatientRecord" size="small">档案</el-button>
<el-button type="primary" icon="Plus" @click="handleAddPatient" size="small">新建</el-button>
<el-button type="primary" plain icon="Search" @click="handleSearch" size="small">查询</el-button>
<el-button type="primary" plain @click="handleReadCard('01')" size="small">电子凭证</el-button>
<el-button type="primary" plain @click="handleReadCard('02')" size="small" :disabled="true">身份证</el-button>
<el-button type="primary" plain @click="handleReadCard('03')" size="small">医保卡</el-button>
<el-button type="warning" plain icon="CircleClose" @click="handleClear" size="small">清空</el-button>
<el-button type="primary" icon="Plus" @click="handleAdd" size="small">保存挂号</el-button>
<el-button type="success" icon="Printer" @click="handleReprint" size="small">补打挂号</el-button>
</div>
</div>
</template>
<el-form :model="form" :rules="rules" ref="outpatientRegistrationRef" label-width="110px"> <el-form :model="form" :rules="rules" ref="outpatientRegistrationRef" label-width="110px">
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="5"> <el-col :span="5">
@@ -28,44 +43,6 @@
</el-popover> </el-popover>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6" style="padding: 0">
<el-button type="primary" icon="Plus" @click="handleAddPatient" style="width: 65px">
新建
</el-button>
<el-button
type="primary"
plain
icon="Search"
@click="handleSearch"
style="width: 65px"
>
查询
</el-button>
<el-button type="primary" plain @click="handleReadCard('01')" style="width: 65px">
电子凭证
</el-button>
<el-button
type="primary"
plain
@click="handleReadCard('02')"
style="width: 65px"
:disabled="true"
>
身份证
</el-button>
<el-button type="primary" plain @click="handleReadCard('03')" style="width: 65px">
医保卡
</el-button>
<!-- <el-button
type="primary"
plain
@click="handleReadCard('99')"
style="width: 65px"
:disabled="true"
>
学生卡
</el-button> -->
</el-col>
<el-col :span="5"> <el-col :span="5">
<el-form-item label="姓名:" prop="name"> <el-form-item label="姓名:" prop="name">
<el-input v-model="form.name" placeholder="姓名" :disabled="true" /> <el-input v-model="form.name" placeholder="姓名" :disabled="true" />
@@ -162,10 +139,10 @@
ref="contractNameRef" ref="contractNameRef"
> >
<el-option <el-option
v-for="item in contractList" v-for="dict in medfee_paymtd_code"
:key="item.busNo" :key="dict.value"
:label="item.contractName" :label="dict.label"
:value="item.busNo" :value="dict.value"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -350,33 +327,12 @@
<el-input v-model="form.totalPrice" placeholder="" :disabled="true" /> <el-input v-model="form.totalPrice" placeholder="" :disabled="true" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="4" style="text-align: right"> </el-row>
<el-button type="warning" plain icon="CircleClose" @click="handleClear" <el-row :gutter="24" justify="end">
>清空</el-button <el-col :span="5" style="text-align: right">
> <!-- 功能按钮已移至标题区域 -->
<el-button type="primary" plain icon="Plus" @click="handleAdd">保存挂号</el-button>
</el-col> </el-col>
</el-row> </el-row>
<!-- <el-row :gutter="24" justify="end">
<el-col :span="5" style="text-align: right">
<el-button
type="warning"
plain
icon="CircleClose"
@click="handleClear"
v-hasPermi="['system:user:export']"
>清空</el-button
>
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['system:user:add']"
>添加</el-button
>
</el-col>
</el-row> -->
</el-form> </el-form>
</el-card> </el-card>
</el-col> </el-col>
@@ -441,7 +397,7 @@
/> />
<el-table-column label="年龄" align="center" key="age" prop="age" width="120"> <el-table-column label="年龄" align="center" key="age" prop="age" width="120">
<template #default="scope"> <template #default="scope">
{{ scope.row.age ? `${scope.row.age}` : '-' }} {{ scope.row.age ? `${scope.row.age}` : '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
@@ -482,7 +438,8 @@
align="center" align="center"
key="contractName" key="contractName"
prop="contractName" prop="contractName"
/> >
</el-table-column>
<el-table-column label="挂号金额" align="center" key="totalPrice" prop="totalPrice"> <el-table-column label="挂号金额" align="center" key="totalPrice" prop="totalPrice">
<template #default="scope"> <template #default="scope">
<span> <span>
@@ -597,6 +554,8 @@
:transformedData="transformedData" :transformedData="transformedData"
:chrgBchno="chrgBchno" :chrgBchno="chrgBchno"
:registerBusNo="registerBusNo" :registerBusNo="registerBusNo"
:feeType="(patientInfo && patientInfo.medfeePaymtdCode) || (form && form.value && form.value.contractNo) || ''"
:medfee_paymtd_code="medfee_paymtd_code"
/> />
<RefundDialog <RefundDialog
:open="openRefundDialog" :open="openRefundDialog"
@@ -613,6 +572,20 @@
:patientInfo="patientInfo" :patientInfo="patientInfo"
:paymentId="paymentId" :paymentId="paymentId"
:chargeItemIds="chargeItemIdList" :chargeItemIds="chargeItemIdList"
:feeType="patientInfo.medfeePaymtdCode || ''"
:contractName="patientInfo.contractName || ''"
:medfee_paymtd_code="medfee_paymtd_code"
/>
<ReprintDialog
:open="openReprintDialog"
@close="
(value) => {
if (value == 'success') {
getList();
}
openReprintDialog = false;
}
"
/> />
</div> </div>
</template> </template>
@@ -637,21 +610,29 @@ import { invokeYbPlugin } from '@/api/public';
import patientInfoDialog from './components/patientInfoDialog'; import patientInfoDialog from './components/patientInfoDialog';
import PatientAddDialog from './components/patientAddDialog'; import PatientAddDialog from './components/patientAddDialog';
import patientList from './components/patientList'; import patientList from './components/patientList';
import { nextTick, ref } from 'vue'; import { nextTick, ref, onMounted, onUnmounted } from 'vue';
import ChargeDialog from './components/chargeDialog.vue'; import ChargeDialog from './components/chargeDialog.vue';
import RefundDialog from './components/refundDialog.vue'; import RefundDialog from './components/refundDialog.vue';
import ReprintDialog from './components/reprintDialog.vue';
import { handleColor } from '@/utils/his'; import { handleColor } from '@/utils/his';
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
import { formatDate, formatDateStr } from '@/utils/index'; import { formatDate, formatDateStr } from '@/utils/index';
const patientInfo = ref({}); const patientInfo = ref({});
// 跳转到患者档案管理页面
const goToPatientRecord = () => {
// 使用Vue Router进行路由导航在当前页面打开新的路由层级
router.push('/system/basicmanage/patientmanagement');
};
const router = useRouter(); const router = useRouter();
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const { sys_normal_disable, sys_user_sex, med_chrgitm_type } = proxy.useDict( const { sys_normal_disable, sys_user_sex, med_chrgitm_type,medfee_paymtd_code } = proxy.useDict(
'sys_normal_disable', 'sys_normal_disable',
'sys_user_sex', 'sys_user_sex',
'med_chrgitm_type' 'med_chrgitm_type',
'medfee_paymtd_code'
); );
const outpatientRegistrationList = ref([]); const outpatientRegistrationList = ref([]);
@@ -681,6 +662,7 @@ const doctorRef = ref(null);
const ybTypeRef = ref(null); const ybTypeRef = ref(null);
const openDialog = ref(false); const openDialog = ref(false);
const openRefundDialog = ref(false); const openRefundDialog = ref(false);
const openReprintDialog = ref(false);
const totalAmount = ref(0); const totalAmount = ref(0);
const chargeItemIdList = ref([]); const chargeItemIdList = ref([]);
const chrgBchnoList = ref([]); const chrgBchnoList = ref([]);
@@ -793,6 +775,15 @@ onUnmounted(() => {
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/** 根据contractNo获取费用性质名称 */
function getFeeTypeName(contractNo) {
if (!contractNo || !medfee_paymtd_code || !Array.isArray(medfee_paymtd_code)) {
return '';
}
const dictItem = medfee_paymtd_code.find(item => item.value === contractNo);
return dictItem ? dictItem.label : '';
}
/** 初期所用数据查询 */ /** 初期所用数据查询 */
function getInitData() { function getInitData() {
getInit().then((response) => { getInit().then((response) => {
@@ -1142,6 +1133,7 @@ function handleAdd() {
console.log(transformedData, 'transformedData门诊挂号'); console.log(transformedData, 'transformedData门诊挂号');
chargeItemIdList.value = []; chargeItemIdList.value = [];
patientInfo.value.patientId = form.value.patientId; patientInfo.value.patientId = form.value.patientId;
patientInfo.value.medfeePaymtdCode = form.value.contractNo; // 设置费用性质
proxy.$refs['outpatientRegistrationRef'].validate((valid) => { proxy.$refs['outpatientRegistrationRef'].validate((valid) => {
if (valid) { if (valid) {
readCardLoading.value = true; readCardLoading.value = true;
@@ -1152,7 +1144,10 @@ function handleAdd() {
addOutpatientRegistration(transformedData.value) addOutpatientRegistration(transformedData.value)
.then((res) => { .then((res) => {
if (res.code == 200) { if (res.code == 200) {
// proxy.$modal.msgSuccess('挂号成功'); console.log('挂号成功,返回数据:', res.data);
// 立即刷新列表,确保挂号信息显示在当日已挂号列表中
getList();
chrgBchno.value = res.data.chrgBchno; chrgBchno.value = res.data.chrgBchno;
registerBusNo.value = res.data.busNo; registerBusNo.value = res.data.busNo;
totalAmount.value = res.data.psnCashPay; totalAmount.value = res.data.psnCashPay;
@@ -1214,7 +1209,11 @@ function handleReturn(row) {
totalAmount.value = row.totalPrice; totalAmount.value = row.totalPrice;
chargeItemIdList.value = row.chargeItemIds.split(','); chargeItemIdList.value = row.chargeItemIds.split(',');
paymentId.value = row.paymentId; paymentId.value = row.paymentId;
console.log(paymentId.value); // 从挂号记录中获取正确的费用性质
patientInfo.value.medfeePaymtdCode = row.contractNo; // 使用挂号记录中的费用性质代码
patientInfo.value.contractName = row.contractName; // 保存费用性质名称用于显示
console.log('退号费用性质:', row.contractNo, row.contractName);
} }
function handleReturnRegister() { function handleReturnRegister() {
@@ -1226,6 +1225,11 @@ function handleReturnRegister() {
}); });
} }
/** 打开补打挂号对话框 */
function handleReprint() {
openReprintDialog.value = true;
}
/** /**
* 点击患者列表给表单赋值 * 点击患者列表给表单赋值
*/ */
@@ -1323,4 +1327,10 @@ getLocationInfo();
margin-bottom: 0; /* 去掉默认的 margin-bottom */ margin-bottom: 0; /* 去掉默认的 margin-bottom */
margin-left: 8px; /* 图标和下拉选之间的间距 */ margin-left: 8px; /* 图标和下拉选之间的间距 */
} }
/* 标题区域按钮样式 */
.header-buttons {
display: flex;
gap: 8px;
}
</style> </style>

View File

@@ -0,0 +1,42 @@
import request from '@/utils/request'
/**
* 执行患者换卡操作
* @param {Object} params - 换卡参数
* @param {string} params.oldCardNo - 旧门诊号码
* @param {string} params.newCardNo - 新门诊号码
* @param {string} params.patientId - 患者ID
* @param {string} params.reason - 换卡原因
* @param {string} params.remark - 备注信息
* @returns {Promise} 请求结果
*/
export const doCardRenewal = (params) => {
return request({
url: '/charge/patientCardRenewal/renewCard',
method: 'post',
data: params
})
}
/**
* 查询患者信息 - 复用门诊挂号的API
* @param {Object} params - 查询参数
* @param {string} params.searchKey - 搜索关键字(姓名、身份证号、手机号等)
* @param {number} params.pageNo - 当前页码
* @param {number} params.pageSize - 每页大小
* @returns {Promise} 请求结果
*/
export const getPatientList = (params) => {
// 构建searchKey参数将姓名、身份证号、手机号组合成一个搜索关键字
const searchKey = params.patientName || params.idCard || params.phoneNumber || '';
return request({
url: '/charge-manage/register/patient-metadata',
method: 'get',
params: {
searchKey: searchKey,
pageNo: params.pageNo || 1,
pageSize: params.pageSize || 10
}
})
}

View File

@@ -0,0 +1,650 @@
<template>
<div class="card-renewal-container">
<!-- 标题栏 -->
<div class="title-bar">
<h2>换卡处理</h2>
</div>
<!-- 按钮栏 - 修改为水平排放 -->
<div class="button-bar">
<el-button
type="primary"
size="large"
@click="handlePatientSearch"
:loading="loading"
icon="el-icon-search"
style="min-width: 140px;"
>
病人查询(Q)
</el-button>
<el-button
type="success"
size="large"
@click="handleConfirm"
:loading="loading"
:disabled="!patientInfo"
style="min-width: 140px;"
>
确定 (O)
</el-button>
<el-button
type="danger"
size="large"
@click="handleClose"
:disabled="loading"
style="min-width: 140px;"
>
关闭 (C)
</el-button>
</div>
<!-- 查询条件 -->
<div class="search-section">
<el-row :gutter="20">
<el-col :span="8">
<div class="input-item">
<label>病人姓名:</label>
<el-input
v-model="searchForm.patientName"
placeholder="请输入"
@keyup.enter="handlePatientSearch"
clearable
/>
</div>
</el-col>
<el-col :span="8">
<div class="input-item">
<label>身份证号码:</label>
<el-input
v-model="searchForm.idCard"
placeholder="请输入"
@keyup.enter="handlePatientSearch"
clearable
/>
</div>
</el-col>
<el-col :span="8">
<div class="input-item">
<label>手机号码:</label>
<el-input
v-model="searchForm.phoneNumber"
placeholder="请输入"
@keyup.enter="handlePatientSearch"
clearable
/>
</div>
</el-col>
</el-row>
</div>
<!-- 患者信息显示 -->
<div class="patient-info-section">
<el-row :gutter="20">
<el-col :span="8">
<div class="info-item">
<label>门诊号码</label>
<div class="info-value">{{ patientInfo?.outpatientNo || '' }}</div>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<label>病人姓名</label>
<div class="info-value">{{ patientInfo?.patientName || '' }}</div>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<label>身份证号码</label>
<div class="info-value">{{ patientInfo?.idCard || '' }}</div>
</div>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 15px;">
<el-col :span="8">
<div class="info-item">
<label>手机号码</label>
<div class="info-value">{{ patientInfo?.phoneNumber || '' }}</div>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<label>性别</label>
<div class="info-value">{{ patientInfo?.gender || '' }}</div>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<label>年龄</label>
<div class="info-value">{{ patientInfo?.age || '' }}</div>
</div>
</el-col>
</el-row>
</div>
<!-- 新门诊号码输入 -->
<div class="new-number-section">
<div class="input-item">
<label>新门诊号码:</label>
<el-input
v-model="renewalForm.newOutpatientNo"
placeholder="请输入新门诊号码"
@keyup.enter="handleConfirm"
style="width: 250px;"
/>
</div>
</div>
<!-- 按钮区域已移除 -->
<!-- 患者列表对话框 -->
<el-dialog
v-model="showPatientList"
title="患者列表"
width="800px"
:close-on-click-modal="false"
>
<el-table
:data="patientList"
style="width: 100%"
@row-click="selectPatient"
highlight-current-row
>
<el-table-column label="序号" width="60" type="index">
</el-table-column>
<el-table-column label="病人姓名" width="120">
<template #default="scope">
{{ scope.row.patientName || scope.row.name || '-' }}
</template>
</el-table-column>
<el-table-column label="门诊号码" width="120">
<template #default="scope">
{{ scope.row.identifierNo || scope.row.cardNo || scope.row.card_number || scope.row.就诊卡号 || scope.row.outpatientNumber || scope.row.outpatientNo || scope.row.门诊号码 || scope.row.卡号 || scope.row.card || scope.row.patientNo || scope.row.patient_id || '-' }}
</template>
</el-table-column>
<el-table-column label="身份证号码" width="200">
<template #default="scope">
{{ scope.row.idCard || scope.row.id_card || scope.row.idNo || '-' }}
</template>
</el-table-column>
<el-table-column label="手机号码" width="120">
<template #default="scope">
{{ scope.row.phoneNumber || scope.row.phone || scope.row.mobile || scope.row.mobilePhone || '-' }}
</template>
</el-table-column>
<el-table-column label="性别" width="80">
<template #default="scope">
{{ scope.row.genderEnum_enumText || scope.row.gender || scope.row.sex || scope.row.性别 || scope.row.xb || scope.row.sexCode || scope.row.GENDER || scope.row.SEX || '-' }}
</template>
</el-table-column>
<el-table-column label="年龄" width="80">
<template #default="scope">
{{ scope.row.age || '-' }}
</template>
</el-table-column>
<el-table-column label="出生年月" width="120">
<template #default="scope">
{{ formatDate(scope.row.birthDate || scope.row.birthday || scope.row.出生日期) || '-' }}
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<div style="margin-top: 15px; display: flex; justify-content: flex-end;">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showPatientList = false">取消</el-button>
</span>
</template>
</el-dialog>
<!-- 换卡成功提示 -->
<el-dialog
v-model="renewalSuccessVisible"
title="换卡成功"
width="400px"
:close-on-click-modal="false"
>
<div style="text-align: center; padding: 20px;">
<div class="success-icon"></div>
<p style="margin-top: 15px; font-size: 16px;">患者换卡操作已成功完成</p>
<p style="margin-top: 10px;">新门诊号码{{ renewalForm.newOutpatientNo }}</p>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="renewalSuccessVisible = false; resetForm">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue'
import { ElMessage } from 'element-plus'
import { doCardRenewal, getPatientList } from './components/api.js';
// 搜索表单
const searchForm = reactive({
patientName: '',
idCard: '',
phoneNumber: ''
})
// 患者信息
const patientInfo = ref(null)
// 患者列表
const patientList = ref([])
const showPatientList = ref(false)
// 分页相关状态
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
// 换卡表单
const renewalForm = reactive({
newOutpatientNo: ''
})
// 加载状态
const loading = ref(false)
// 成功弹窗显示状态
const renewalSuccessVisible = ref(false)
// 分页大小变化处理
const handleSizeChange = (newSize) => {
pageSize.value = newSize
currentPage.value = 1 // 重置为第一页
if (showPatientList.value) {
handlePatientSearch() // 重新查询
}
}
// 当前页码变化处理
const handleCurrentChange = (newPage) => {
currentPage.value = newPage
if (showPatientList.value) {
handlePatientSearch() // 重新查询
}
}
// 查询患者信息
const handlePatientSearch = async () => {
// 检查是否至少填写了一个查询条件
if (!searchForm.patientName && !searchForm.idCard && !searchForm.phoneNumber) {
ElMessage.warning('请至少输入一个查询条件')
return
}
loading.value = true
try {
// 构建查询参数,包含分页信息
const queryParams = {
patientName: searchForm.patientName || '',
idCard: searchForm.idCard || '',
phoneNumber: searchForm.phoneNumber || '',
pageNo: currentPage.value,
pageSize: pageSize.value
}
// 调用复用的门诊挂号API获取患者列表
const response = await getPatientList(queryParams)
if (response && response.code === 200) {
// 检查是否有查询结果
if (response.data && response.data.records && response.data.records.length > 0) {
// 更新总条数
total.value = response.data.total || 0
// 如果只有一条记录且是第一页,直接显示
if (response.data.records.length === 1 && currentPage.value === 1) {
const patient = response.data.records[0]
// 获取门诊号码优先使用identifierNo或patientId
const outpatientNo = patient.identifierNo || patient.cardNo || patient.card_number || patient.就诊卡号 || patient.outpatientNumber || patient.outpatientNo || patient.门诊号码 || patient.卡号 || patient.card || patient.patientNo || patient.patient_id;
// 获取性别优先使用genderEnum_enumText
const gender = patient.genderEnum_enumText || patient.gender || patient.sex || patient.性别 || patient.xb || patient.sexCode || patient.GENDER || patient.SEX;
patientInfo.value = {
outpatientNo: outpatientNo,
patientName: patient.patientName || patient.name,
idCard: patient.idCard || patient.id_card || patient.idNo,
phoneNumber: patient.phoneNumber || patient.phone || patient.mobile || patient.mobilePhone,
gender: gender,
age: patient.age,
patientId: patient.patientId || outpatientNo
}
} else {
// 如果有多条记录或不是第一页,显示患者列表供选择
patientList.value = response.data.records
showPatientList.value = true
}
} else {
// 无数据时重置总条数
total.value = 0
ElMessage.warning('未查询到患者信息')
}
} else {
ElMessage.error('查询失败:' + (response?.msg || '未知错误'))
}
} catch (error) {
ElMessage.error('查询失败,请稍后重试')
// 可以在这里添加错误监控或日志记录
} finally {
loading.value = false
}
}
// 确定换卡
const handleConfirm = async () => {
if (!patientInfo.value) {
ElMessage.warning('请先查询患者信息')
return
}
if (!renewalForm.newOutpatientNo) {
ElMessage.warning('请输入新门诊号码')
return
}
if (renewalForm.newOutpatientNo === patientInfo.value.outpatientNo) {
ElMessage.warning('新门诊号码不能与原号码相同')
return
}
loading.value = true
try {
// 构建换卡请求参数注意参数名称与API接口保持一致
const params = {
oldCardNo: patientInfo.value.outpatientNo,
newCardNo: renewalForm.newOutpatientNo,
patientId: patientInfo.value.patientId,
reason: '患者换卡',
remark: `${patientInfo.value.outpatientNo}更换到${renewalForm.newOutpatientNo}`
}
// 调用真实的换卡接口
const res = await doCardRenewal(params)
if (res && res.code === 200) {
renewalSuccessVisible.value = true
ElMessage.success('换卡成功!')
} else {
ElMessage.error('换卡失败:' + (res?.msg || '未知错误'))
}
} catch (error) {
ElMessage.error('换卡失败,请稍后重试')
// 可以在这里添加错误监控或日志记录
} finally {
loading.value = false
}
}
// 关闭窗口
const handleClose = () => {
if (confirm('确定要关闭换卡窗口吗?')) {
resetForm()
// 如果是在弹窗中打开的,可以添加关闭弹窗的逻辑
// 否则可以导航回上一页
// this.$router.back()
}
}
// 键盘事件处理
const handleKeydown = (event) => {
// Alt + Q 查询
if (event.altKey && event.key.toLowerCase() === 'q') {
event.preventDefault()
handlePatientSearch()
}
// Alt + O 确定
if (event.altKey && event.key.toLowerCase() === 'o') {
event.preventDefault()
handleConfirm()
}
// Alt + C 关闭
if (event.altKey && event.key.toLowerCase() === 'c') {
event.preventDefault()
handleClose()
}
}
// 组件挂载时添加键盘事件监听
onMounted(() => {
document.addEventListener('keydown', handleKeydown)
})
// 组件卸载时移除键盘事件监听
onUnmounted(() => {
document.removeEventListener('keydown', handleKeydown)
})
// 移除了调试功能
// 选择患者
const selectPatient = (row) => {
// 获取门诊号码优先使用identifierNo
const outpatientNo = row.identifierNo || row.cardNo || row.card_number || row.就诊卡号 || row.outpatientNumber || row.outpatientNo || row.门诊号码 || row.卡号 || row.card || row.patientNo || row.patient_id;
// 获取性别优先使用genderEnum_enumText
const gender = row.genderEnum_enumText || row.gender || row.sex || row.性别 || row.xb || row.sexCode || row.GENDER || row.SEX;
patientInfo.value = {
outpatientNo: outpatientNo,
patientName: row.patientName || row.name,
idCard: row.idCard || row.id_card || row.idNo,
phoneNumber: row.phoneNumber || row.phone || row.mobile || row.mobilePhone,
gender: gender,
age: row.age,
patientId: row.patientId || outpatientNo
}
showPatientList.value = false
ElMessage.success('已选择患者:' + (row.patientName || row.name))
}
// 重置表单
const resetForm = () => {
searchForm.patientName = ''
searchForm.idCard = ''
searchForm.phoneNumber = ''
patientInfo.value = null
patientList.value = []
showPatientList.value = false
renewalForm.newOutpatientNo = ''
// 重置分页状态
currentPage.value = 1
pageSize.value = 10
total.value = 0
}
// 格式化日期为YYYY-MM-DD格式
const formatDate = (date) => {
if (!date) return ''
// 如果已经是YYYY-MM-DD格式直接返回
if (typeof date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(date)) {
return date
}
// 处理日期对象或其他格式
const d = new Date(date)
if (isNaN(d.getTime())) return ''
const year = d.getFullYear()
const month = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
</script>
<style scoped>
/* 对话框标题样式 */
.el-dialog__header {
background-color: #fff;
padding: 15px 20px;
border-bottom: 1px solid #ebeef5;
margin: 0;
}
.el-dialog__title {
font-size: 16px;
color: #303133;
font-weight: 500;
}
/* 按钮样式 */
.dialog-footer {
display: flex;
justify-content: center;
padding: 15px 20px;
background-color: #fff;
border-top: 1px solid #ebeef5;
}
.dialog-footer .el-button {
min-width: 80px;
background-color: #f56c6c;
border-color: #f56c6c;
color: white;
}
/* 表格样式 */
.el-dialog__body {
padding: 0;
overflow: hidden;
}
.el-table {
margin: 0;
}
.el-table th {
background-color: #f5f7fa;
font-weight: 500;
color: #303133;
}
.el-table td {
text-align: left;
}
/* 恢复默认表格样式 */
.el-table tr:hover > td {
background-color: #f5f7fa;
}
.el-table--striped .el-table__body tr:nth-child(2n) {
background-color: #fafafa;
}
.card-renewal-container {
width: 100%;
max-width: 800px;
margin: 0 auto;
padding: 10px;
border: 1px solid #dcdfe6;
border-radius: 4px;
background: #fff;
}
.title-bar {
background: #409eff;
color: white;
padding: 10px 15px;
margin: -10px -10px 15px -10px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.title-bar h2 {
margin: 0;
font-size: 16px;
font-weight: normal;
}
.button-bar {
display: flex;
gap: 15px;
padding: 15px;
background-color: #f5f7fa;
border: 1px solid #ebeef5;
margin-bottom: 20px;
}
.button-bar .el-button {
font-weight: 500;
transition: all 0.3s ease;
}
.button-bar .el-button:hover {
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.search-section {
margin-bottom: 20px;
}
.input-item {
margin-bottom: 15px;
}
.input-item label {
display: inline-block;
width: 100px;
text-align: right;
margin-right: 10px;
font-weight: 500;
}
.patient-info-section {
background: #f5f7fa;
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
}
.info-item label {
display: block;
color: #606266;
font-size: 14px;
margin-bottom: 5px;
}
.info-value {
color: #303133;
font-weight: 500;
}
.new-number-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #ebeef5;
}
.success-icon {
width: 50px;
height: 50px;
background: #67c23a;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 30px;
margin: 0 auto;
}
</style>

View File

@@ -0,0 +1,342 @@
<template>
<div class="patient-search-container">
<!-- 标题栏 -->
<div class="title-bar">
<h2>病人档案查询</h2>
<div class="button-bar">
<el-button
type="primary"
@click="handleQuery"
:loading="loading"
size="large"
icon="el-icon-check"
>
确认(Q)
</el-button>
<el-button
type="danger"
@click="handleClose"
size="large"
icon="el-icon-close"
>
关闭(C)
</el-button>
</div>
</div>
<!-- 查询表单 -->
<div class="search-form">
<el-row :gutter="20">
<el-col :span="8">
<div class="form-item">
<label>病人姓名:</label>
<el-input
v-model="searchForm.patientName"
placeholder="请输入病人姓名"
clearable
@keyup.enter="handleQuery"
/>
</div>
</el-col>
<el-col :span="8">
<div class="form-item">
<label>身份证号码:</label>
<el-input
v-model="searchForm.idCard"
placeholder="请输入身份证号码"
clearable
@keyup.enter="handleQuery"
/>
</div>
</el-col>
<el-col :span="8">
<div class="form-item">
<label>手机号码:</label>
<el-input
v-model="searchForm.phoneNumber"
placeholder="请输入手机号码"
clearable
@keyup.enter="handleQuery"
/>
</div>
</el-col>
</el-row>
</div>
<!-- 查询结果表格 -->
<div class="result-table">
<el-table
:data="patientList"
style="width: 100%"
stripe
@row-click="handleRowClick"
highlight-current-row
row-key="id"
:current-row-key="selectedPatient?.id"
>
<el-table-column label="序号" type="index" width="80" align="center">
</el-table-column>
<el-table-column label="病人姓名" width="120" align="center">
<template #default="scope">
{{ scope.row.patientName }}
</template>
</el-table-column>
<el-table-column label="门诊号码" width="150" align="center">
<template #default="scope">
{{ scope.row.outpatientNo || '-' }}
</template>
</el-table-column>
<el-table-column label="身份证号码" width="200" align="center">
<template #default="scope">
{{ scope.row.idCard || '-' }}
</template>
</el-table-column>
<el-table-column label="手机号码" width="120" align="center">
<template #default="scope">
{{ scope.row.phoneNumber || '-' }}
</template>
</el-table-column>
<el-table-column label="性别" width="80" align="center">
<template #default="scope">
{{ scope.row.gender }}
</template>
</el-table-column>
<el-table-column label="年龄" width="80" align="center">
<template #default="scope">
{{ scope.row.age }}
</template>
</el-table-column>
<el-table-column label="出生年月" width="120" align="center">
<template #default="scope">
{{ scope.row.birthDate }}
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted, computed, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { getOutpatientRegistrationList } from '../outpatientregistration/components/outpatientregistration'
// 搜索表单
const searchForm = reactive({
patientName: '',
idCard: '',
phoneNumber: ''
})
// 患者列表
const patientList = ref([])
// 加载状态
const loading = ref(false)
// API调用相关变量
const queryParams = ref({
pageNum: 1,
pageSize: 50
})
// 当前选中的患者
const selectedPatient = ref(null)
// 定义事件
const emit = defineEmits(['selectPatient', 'close'])
// 计算搜索关键字合并三个字段为一个searchKey
const searchKey = computed(() => {
return searchForm.patientName || searchForm.idCard || searchForm.phoneNumber
})
// 监听搜索关键字变化,自动查询
watch(searchKey, (newValue) => {
// 当搜索关键字变化时,延迟查询,避免频繁请求
const timer = setTimeout(() => {
if (newValue) {
queryParams.value.searchKey = newValue
handleQuery()
}
}, 300)
return () => clearTimeout(timer)
})
// 处理行点击
const handleRowClick = (row) => {
selectedPatient.value = row
ElMessage.success(`已选择患者:${row.patientName}`)
}
// 查询患者信息和确认选择
const handleQuery = async () => {
// 如果已经选中了患者,则确认选择
if (selectedPatient.value) {
// 发射选择患者事件
emit('selectPatient', selectedPatient.value)
ElMessage.success(`已确认选择患者:${selectedPatient.value.patientName}`)
return
}
// 检查是否至少填写了一个查询条件
if (!searchKey.value) {
ElMessage.warning('请至少输入一个查询条件')
return
}
loading.value = true
try {
// 调用门诊挂号的API获取患者列表
const response = await getOutpatientRegistrationList(queryParams.value)
if (response.data && response.data.records) {
// 映射字段,使其与表格展示需求匹配
patientList.value = response.data.records.map(record => ({
id: record.id,
patientName: record.name,
outpatientNo: record.identifierNo,
idCard: record.idCard,
phoneNumber: record.phone,
gender: record.genderEnum_enumText || '-',
age: record.age || '-',
birthDate: record.birthDate || '-' // 如果后端没有提供,需要计算或显示为'-'
}))
if (patientList.value.length === 0) {
ElMessage.warning('未找到符合条件的患者信息')
}
} else {
ElMessage.error('查询失败')
patientList.value = []
}
} catch (error) {
ElMessage.error('查询失败,请稍后重试')
console.error('查询失败:', error)
patientList.value = []
} finally {
loading.value = false
}
}
// 关闭窗口
const handleClose = () => {
// 发射关闭事件
emit('close')
}
// 键盘事件处理
const handleKeydown = (event) => {
// Alt + Q 查询
if (event.altKey && event.key.toLowerCase() === 'q') {
event.preventDefault()
handleQuery()
}
// Alt + C 关闭
if (event.altKey && event.key.toLowerCase() === 'c') {
event.preventDefault()
handleClose()
}
}
// 组件挂载时添加键盘事件监听
onMounted(() => {
document.addEventListener('keydown', handleKeydown)
})
// 组件卸载时移除键盘事件监听
onUnmounted(() => {
document.removeEventListener('keydown', handleKeydown)
})
</script>
<style scoped>
.patient-search-container {
width: 100%;
background-color: #fff;
border: 1px solid #b3d4ff;
border-radius: 4px;
overflow: hidden;
}
.title-bar {
background-color: #d6e9ff;
padding: 10px 20px;
border-bottom: 1px solid #b3d4ff;
display: flex;
justify-content: space-between;
align-items: center;
}
.title-bar h2 {
margin: 0;
font-size: 18px;
color: #303133;
font-weight: 500;
}
.button-bar {
padding: 10px 20px;
display: flex;
gap: 10px;
}
.button-bar .el-button {
padding: 12px 24px;
font-size: 16px;
}
.search-form {
padding: 20px;
border-bottom: 1px solid #dcdfe6;
}
.form-item {
margin-bottom: 15px;
display: flex;
align-items: center;
}
.form-item label {
display: inline-block;
width: 100px;
text-align: right;
margin-right: 10px;
font-weight: 500;
color: #303133;
}
.form-item .el-input {
width: 250px;
}
.result-table {
padding: 10px;
}
.el-table {
border: 1px solid #dcdfe6;
border-radius: 4px;
}
.el-table th {
background-color: #f2f6fc;
font-weight: 600;
color: #303133;
border-bottom: 1px solid #e6e8eb;
}
.el-table td {
padding: 10px 0;
font-size: 14px;
border-bottom: 1px solid #ebeef5;
}
.el-table--striped .el-table__body tr:nth-child(2n) {
background-color: #f8f9fa;
}
.el-table__row:hover {
background-color: #f5f7fa;
}
</style>

View File

@@ -0,0 +1,48 @@
import request from '@/utils/request'
/**
* 获取门诊患者信息列表
* 复用门诊挂号的API来查询患者信息
*/
export const getOutpatientRegistrationList = (params) => {
// 调用门诊挂号的API获取患者信息
return request({
url: '/api/outpatient/registration/list',
method: 'get',
params
})
}
/**
* 根据ID获取患者详情
*/
export const getPatientById = (id) => {
return request({
url: `/api/outpatient/registration/${id}`,
method: 'get'
})
}
/**
* 根据条件查询患者信息
* 复用门诊挂号API进行查询
*/
export const getPatientList = (params) => {
// 复用门诊挂号的API
return request({
url: '/api/outpatient/registration/list',
method: 'get',
params
})
}
/**
* 根据门诊号码获取患者详细信息
*/
export const getPatientByOutpatientNo = (outpatientNo) => {
return request({
url: '/api/outpatient/registration/list',
method: 'get',
params: { identifierNo: outpatientNo }
})
}

View File

@@ -67,7 +67,7 @@ import { throttle } from 'lodash-es';
const props = defineProps({ const props = defineProps({
adviceQueryParams: { adviceQueryParams: {
type: Object, type: Object,
default: '', required: true
}, },
patientInfo: { patientInfo: {
type: Object, type: Object,
@@ -97,39 +97,32 @@ const throttledGetList = throttle(
watch( watch(
() => props.adviceQueryParams, () => props.adviceQueryParams,
(newValue) => { (newValue) => {
queryParams.value.searchKey = newValue.searchKey; console.log('adviceBaseList 接收到参数变化:', newValue);
// queryParams.value.adviceType = newValue.adviceType;
if (newValue.adviceType) { // 直接更新查询参数
queryParams.value.adviceTypes = [newValue.adviceType].join(','); queryParams.value.searchKey = newValue.searchKey || '';
// 处理类型筛选
if (newValue.adviceType !== undefined && newValue.adviceType !== null && newValue.adviceType !== '') {
// 单个类型
queryParams.value.adviceTypes = newValue.adviceType.toString();
} else { } else {
// 全部类型
queryParams.value.adviceTypes = '1,2,3'; queryParams.value.adviceTypes = '1,2,3';
} }
throttledGetList();
}, // 设置categoryCode筛选条件用于筛选西药和中成药
{ deep: true } if (newValue.categoryCode) {
); queryParams.value.categoryCode = newValue.categoryCode;
} else {
getList(); queryParams.value.categoryCode = undefined;
function getList() {
queryParams.value.organizationId = props.patientInfo.orgId;
getAdviceBaseInfo(queryParams.value).then((res) => {
console.log('ssssssssss', res.data.records);
if (res.data.records.length > 0) {
adviceBaseList.value = res.data.records.filter((item) => {
if (item.adviceType == 1 || item.adviceType == 2) {
return handleQuantity(item) != 0;
} else {
return true;
}
});
total.value = res.data.total;
nextTick(() => {
currentIndex.value = 0;
adviceBaseRef.value.setCurrentRow(adviceBaseList.value[0]);
});
} }
});
} console.log('发送请求参数:', queryParams.value);
getList();
},
{ deep: true, immediate: true }
);
// 从priceList列表中获取价格 // 从priceList列表中获取价格
function getPriceFromInventory(row) { function getPriceFromInventory(row) {
if (row.priceList && row.priceList.length > 0) { if (row.priceList && row.priceList.length > 0) {
@@ -138,6 +131,55 @@ function getPriceFromInventory(row) {
} }
return '-'; return '-';
} }
function getList() {
queryParams.value.organizationId = props.patientInfo.orgId;
console.log('发送API请求:', queryParams.value);
getAdviceBaseInfo(queryParams.value).then((res) => {
if (res.data.records && res.data.records.length > 0) {
let filteredRecords = res.data.records.filter((item) => {
// 后端已经根据adviceTypes参数进行了筛选这里只需要进行前端补充筛选
// 过滤库存(药品和耗材需要检查库存,诊疗不需要检查库存)
if (item.adviceType == 1 || item.adviceType == 2) {
if (handleQuantity(item) == 0) {
return false;
}
}
// 如果设置了categoryCode筛选条件进行筛选用于区分西药和中成药
// categoryCode = '1' 表示中成药categoryCode = '2' 表示西药
// 注意:只有药品类型(adviceType=1)才需要根据categoryCode筛选
if (queryParams.value.categoryCode && item.adviceType == 1) {
// 只筛选药品类型
if (item.categoryCode != queryParams.value.categoryCode) {
return false;
}
}
return true;
});
adviceBaseList.value = filteredRecords;
console.log('过滤后数据显示:', adviceBaseList.value.length, '条');
total.value = res.data.total;
nextTick(() => {
currentIndex.value = 0;
if (adviceBaseList.value.length > 0) {
adviceBaseRef.value.setCurrentRow(adviceBaseList.value[0]);
}
});
} else {
adviceBaseList.value = [];
}
}).catch(error => {
console.error('获取数据失败:', error);
adviceBaseList.value = [];
});
}
// 处理键盘事件 // 处理键盘事件
const handleKeyDown = (event) => { const handleKeyDown = (event) => {
const key = event.key; const key = event.key;

View File

@@ -63,6 +63,16 @@ export function completeEncounter(encounterId) {
}) })
} }
/**
* 取消接诊
*/
export function cancelEncounter(encounterId) {
return request({
url: '/doctor-station/main/cancel-encounter?encounterId=' + encounterId,
method: 'get',
})
}
/** /**
* 保存病历 * 保存病历
*/ */

View File

@@ -99,7 +99,7 @@
> >
<el-select v-model="scope.row.medTypeCode" placeholder=" " style="width: 150px"> <el-select v-model="scope.row.medTypeCode" placeholder=" " 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"
@@ -235,7 +235,7 @@ 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' }],
@@ -275,6 +275,10 @@ function getList() {
diagnosisDoctor: item.diagnosisDoctor || props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name, diagnosisDoctor: item.diagnosisDoctor || props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: item.diagnosisTime || new Date().toLocaleString('zh-CN') diagnosisTime: item.diagnosisTime || new Date().toLocaleString('zh-CN')
})); }));
// 获取诊断列表后按排序号排序
form.value.diagnosisList.sort((a, b) => (a.diagSrtNo || 0) - (b.diagSrtNo || 0));
emits('diagnosisSave', false); emits('diagnosisSave', false);
console.log(form.value.diagnosisList); console.log(form.value.diagnosisList);
} }
@@ -320,6 +324,11 @@ function handleImport() {
// 获取患者慢性病信息 // 获取患者慢性病信息
getChronicDisease({ encounterId: props.patientInfo.encounterId }).then((res) => { getChronicDisease({ encounterId: props.patientInfo.encounterId }).then((res) => {
if (res.data && res.data.length > 0) { if (res.data && res.data.length > 0) {
// 计算现有最大排序号
const maxSortNo = form.value.diagnosisList.length > 0
? Math.max(...form.value.diagnosisList.map(item => item.diagSrtNo || 0))
: 0;
res.data.forEach((item, index) => { res.data.forEach((item, index) => {
form.value.diagnosisList.push({ form.value.diagnosisList.push({
...item, ...item,
@@ -327,7 +336,7 @@ function handleImport() {
medTypeCode: '140104', medTypeCode: '140104',
verificationStatusEnum: 4, verificationStatusEnum: 4,
definitionId: item.id, definitionId: item.id,
diagSrtNo: form.value.diagnosisList.length + 1, diagSrtNo: maxSortNo + index + 1,
iptDiseTypeCode: 2, iptDiseTypeCode: 2,
diagnosisDesc: '', diagnosisDesc: '',
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name, diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
@@ -335,6 +344,10 @@ function handleImport() {
}, },
}); });
}); });
// 导入完成后按排序号排序
form.value.diagnosisList.sort((a, b) => (a.diagSrtNo || 0) - (b.diagSrtNo || 0));
emits('diagnosisSave', false);
} }
}); });
} }
@@ -417,17 +430,26 @@ function handleAddDiagnosis() {
proxy.$modal.msgWarning('请先填写病历'); proxy.$modal.msgWarning('请先填写病历');
return; return;
} }
// 计算现有最大排序号
const maxSortNo = form.value.diagnosisList.length > 0
? Math.max(...form.value.diagnosisList.map(item => item.diagSrtNo || 0))
: 0;
form.value.diagnosisList.push({ form.value.diagnosisList.push({
showPopover: false, showPopover: false,
name: undefined, name: undefined,
verificationStatusEnum: 4, verificationStatusEnum: 4,
medTypeCode: '11', medTypeCode: '初诊诊断',
diagSrtNo: form.value.diagnosisList.length + 1, diagSrtNo: maxSortNo + 1,
iptDiseTypeCode: 2, iptDiseTypeCode: 2,
diagnosisDesc: '', diagnosisDesc: '',
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')
}); });
// 添加后按排序号排序
form.value.diagnosisList.sort((a, b) => (a.diagSrtNo || 0) - (b.diagSrtNo || 0));
if (form.value.diagnosisList.length == 1) { if (form.value.diagnosisList.length == 1) {
form.value.diagnosisList[0].maindiseFlag = 1; form.value.diagnosisList[0].maindiseFlag = 1;
} }
@@ -445,12 +467,17 @@ function handleAddTcmDiagonsis() {
*/ */
function handleDeleteDiagnosis(row, index) { function handleDeleteDiagnosis(row, index) {
if (row.conditionId) { if (row.conditionId) {
delEncounterDiagnosis(row.conditionId).then(() => { delEncounterDiagnosis(row.conditionId).then((res) => {
getList(); if (res.code == 200) {
getTree(); proxy.$modal.msgSuccess('删除成功');
getList();
getTree();
}
}); });
} else { } else {
form.value.diagnosisList.splice(index, 1); form.value.diagnosisList.splice(index, 1);
// 删除后不重新计算排序号,保持用户设置的排序不变
emits('diagnosisSave', false);
} }
} }
@@ -458,7 +485,6 @@ function handleMaindise(value, index) {
if (value == 1) { if (value == 1) {
let flag = 0; let flag = 0;
form.value.diagnosisList.forEach((item) => { form.value.diagnosisList.forEach((item) => {
console.log(item);
if (item.maindiseFlag == 1) { if (item.maindiseFlag == 1) {
flag++; flag++;
} }
@@ -482,6 +508,9 @@ function handleSaveDiagnosis() {
} else if (!form.value.diagnosisList.some((diagnosis) => diagnosis.maindiseFlag === 1)) { } else if (!form.value.diagnosisList.some((diagnosis) => diagnosis.maindiseFlag === 1)) {
proxy.$modal.msgWarning('至少添加一条主诊断'); proxy.$modal.msgWarning('至少添加一条主诊断');
} else { } else {
// 保存前按排序号排序
form.value.diagnosisList.sort((a, b) => (a.diagSrtNo || 0) - (b.diagSrtNo || 0));
saveDiagnosis({ saveDiagnosis({
patientId: props.patientInfo.patientId, patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId, encounterId: props.patientInfo.encounterId,
@@ -491,7 +520,7 @@ function handleSaveDiagnosis() {
getTree(); getTree();
getList(); getList();
emits('diagnosisSave', false); emits('diagnosisSave', false);
proxy.$modal.msgSuccess('诊断已保存'); proxy.$modal.msgSuccess('诊断已保存并按排序号排序');
//食源性疾病病例数据智能采集 ---START--- //食源性疾病病例数据智能采集 ---START---
isFoodDiseasesNew({ isFoodDiseasesNew({
encounterId: props.patientInfo.encounterId, encounterId: props.patientInfo.encounterId,
@@ -566,16 +595,26 @@ function handleNodeClick(data) {
proxy.$modal.msgWarning('该诊断项已存在'); proxy.$modal.msgWarning('该诊断项已存在');
return; return;
} }
// 计算现有最大排序号
const maxSortNo = form.value.diagnosisList.length > 0
? Math.max(...form.value.diagnosisList.map(item => item.diagSrtNo || 0))
: 0;
form.value.diagnosisList.push({ form.value.diagnosisList.push({
ybNo: data.ybNo, ybNo: data.ybNo,
name: data.name, name: data.name,
verificationStatusEnum: 4, verificationStatusEnum: 4,
medTypeCode: '11', medTypeCode: '11',
diagSrtNo: form.value.diagnosisList.length + 1, diagSrtNo: maxSortNo + 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,
diagnosisTime: new Date().toLocaleString('zh-CN') diagnosisTime: new Date().toLocaleString('zh-CN')
}); });
// 添加后按排序号排序
form.value.diagnosisList.sort((a, b) => (a.diagSrtNo || 0) - (b.diagSrtNo || 0));
if (form.value.diagnosisList.length == 1) { if (form.value.diagnosisList.length == 1) {
form.value.diagnosisList[0].maindiseFlag = 1; form.value.diagnosisList[0].maindiseFlag = 1;
} }

View File

@@ -10,174 +10,125 @@
<el-button type="primary" plain @click="handleEmrHistory()" style="margin-left: 20px"> <el-button type="primary" plain @click="handleEmrHistory()" style="margin-left: 20px">
历史病历 历史病历
</el-button> </el-button>
<!-- <el-button type="primary" plain @click="handleReceive()" style="margin-left: 20px"> <!-- 可选添加打印按钮 -->
<!-- <el-button type="primary" plain @click="printEmr" style="margin-left: 20px">
打印病历 打印病历
</el-button> --> </el-button> -->
</div> </div>
<div style="max-height: 650px; overflow-y: auto; overflow-x: hidden">
<div style="text-align: center; font-size: 18px; font-weight: bold; margin-bottom: 10px;"> <div
ref="printArea"
style="max-height: 650px; overflow-y: auto; overflow-x: hidden; font-family: 'SimSun', '宋体', sans-serif;"
>
<!-- 标题 -->
<div style="text-align: center; font-size: 18px; font-weight: bold; margin-bottom: 10px;">
{{ visitType === 'FIRST' ? '门诊初诊病历' : '门诊复诊病历' }} {{ visitType === 'FIRST' ? '门诊初诊病历' : '门诊复诊病历' }}
</div> </div>
<el-form ref="emrRef" :model="form" :rules="rules" label-width="80px">
<el-row :gutter="4"> <!-- 患者基本信息 -->
<el-col :span="4"> <div style="display: flex; flex-wrap: wrap; gap: 10px; padding: 10px 0; border-bottom: 1px solid #ebeef5;">
<el-form-item label="就诊卡号" prop="patientId" style="width: 100%"> <div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<el-input placeholder="" v-model="patientInfo.patientId" class="input-with-bottom-border"/> <strong>就诊卡号:</strong> {{ patientInfo.patientId || '' }}
</el-form-item> </div>
</el-col> <div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<el-col :span="4"> <strong>姓名:</strong> {{ patientInfo.patientName || '' }}
<el-form-item label="姓名" prop="patientName" style="width: 100%"> </div>
<el-input placeholder="" v-model="patientInfo.patientName" class="input-with-bottom-border"/> <div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
</el-form-item> <strong>性别:</strong> {{ patientInfo.genderEnum_enumText || '' }}
</el-col> </div>
<el-col :span="4"> <div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<el-form-item label="性别" prop="genderEnum_enumText" style="width: 100%"> <strong>年龄:</strong> {{ patientInfo.age || '' }}
<el-input placeholder="" v-model="patientInfo.genderEnum_enumText" class="input-with-bottom-border"/> </div>
</el-form-item> </div>
</el-col>
<el-col :span="4"> <!-- 就诊信息 -->
<el-form-item label="年龄" prop="age" style="width: 100%"> <div style="display: flex; flex-wrap: wrap; gap: 10px; padding: 10px 0; border-bottom: 1px solid #ebeef5;">
<el-input placeholder="" v-model="patientInfo.age" class="input-with-bottom-border"/> <div style="flex: 1; min-width: 200px; font-size: 14px; line-height: 24px;">
</el-form-item> <strong>就诊日期:</strong> {{ currentVisitDate }}
</el-col> </div>
<el-col :span="4"> <div style="flex: 1; min-width: 200px; font-size: 14px; line-height: 24px;">
<el-form-item label="就诊日期" prop="currentVisitDate" style="width: 100%"> <strong>就诊科室:</strong> {{ patientInfo.organizationName || '' }}
<el-input placeholder="" v-model="currentVisitDate" class="input-with-bottom-border"/> </div>
</el-form-item> </div>
</el-col>
<el-col :span="4"> <!-- 生命体征 -->
<el-form-item label="就诊科室" prop="organizationName" style="width: 100%"> <div style="display: flex; flex-wrap: wrap; gap: 10px; padding: 10px 0; border-bottom: 1px solid #ebeef5;">
<el-input placeholder="" v-model="patientInfo.organizationName" class="input-with-bottom-border"/> <div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
</el-form-item> <strong>身高:</strong>
</el-col> <el-input
</el-row> v-model="form.height"
<el-row :gutter="24"> style="width: 80px; margin-left: 5px;"
<el-col :span="6"> /> cm
<el-form-item label="身高" prop="height" style="width: 100%"> </div>
<el-input placeholder="" v-model="form.height" class="input-with-bottom-border"> <div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<template #suffix>cm</template> <strong>体重:</strong>
</el-input> <el-input
</el-form-item> v-model="form.weight"
</el-col> style="width: 80px; margin-left: 5px;"
<el-col :span="6"> /> kg
<el-form-item label="体重" prop="weight" style="width: 100%"> </div>
<el-input placeholder="" v-model="form.weight" class="input-with-bottom-border"> <div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<template #suffix>kg</template> <strong>体温:</strong>
</el-input> <el-input
</el-form-item> v-model="form.temperature"
</el-col> style="width: 80px; margin-left: 5px;"
<el-col :span="6"> :step="0.1"
<el-form-item label="体温" prop="temperature" style="width: 100%"> />
<el-input placeholder="" v-model="form.temperature" class="input-with-bottom-border"> </div>
<template #suffix></template> <div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
</el-input> <strong>脉搏:</strong>
</el-form-item> <el-input
</el-col> v-model="form.pulse"
<el-col :span="6"> style="width: 80px; margin-left: 5px;"
<el-form-item label="脉搏" prop="pulse" style="width: 100%"> /> /
<el-input placeholder="" v-model="form.pulse" class="input-with-bottom-border"> </div>
<template #suffix>/</template> </div>
</el-input>
</el-form-item> <!-- 主诉 + 发病日期 -->
</el-col> <div style="display: flex; flex-wrap: wrap; gap: 10px; padding: 10px 0; border-bottom: 1px solid #ebeef5;">
</el-row> <div style="flex: 1; min-width: 300px; font-size: 14px; line-height: 24px;">
<el-row :gutter="24"> <strong>主诉:</strong>
<!-- <el-col :span="6"> <el-input
<el-form-item v-model="form.chiefComplaint"
label="血糖" style="width: calc(100% - 50px); margin-left: 5px;"
prop="chiefComplaint" />
style="width: 100%" </div>
> <div style="flex: 1; min-width: 120px; font-size: 14px; line-height: 24px;">
<el-input placeholder="" class="input-with-bottom-border"> <strong>发病日期:</strong>
<template #suffix></template> <el-date-picker
</el-input> v-model="form.onsetDate"
</el-form-item> type="date"
</el-col> style="width: calc(100% - 80px); margin-left: 5px;"
<el-col :span="6"> value-format="YYYY-MM-DD"
<el-form-item />
label="血糖" </div>
prop="chiefComplaint" </div>
style="width: 100%"
> <!-- 多行文本字段 -->
<el-input placeholder="" class="input-with-bottom-border"> <div v-for="field in textFieldList" :key="field.key" style="padding: 10px 0;">
<template #suffix>mmol/L</template> <div style="font-size: 14px; margin-bottom: 5px;">
</el-input> <strong>{{ field.label }}:</strong>
</el-form-item> </div>
</el-col> <el-input
<el-col :span="6"> v-model="form[field.key]"
<el-form-item type="textarea"
label="收缩压" :rows="4"
prop="chiefComplaint" style="width: 100%; font-size: 14px; line-height: 1.5;"
style="width: 100%" />
> </div>
<el-input placeholder="" class="input-with-bottom-border">
<template #suffix>mmHg</template> <!-- 弹窗 -->
</el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item
label="舒张压"
prop="chiefComplaint"
style="width: 100%"
>
<el-input placeholder="" class="input-with-bottom-border">
<template #suffix>mmHg</template>
</el-input>
</el-form-item>
</el-col> -->
</el-row>
<el-row :gutter="24">
<el-col :span="18">
<el-form-item label="主诉" prop="chiefComplaint">
<el-input v-model="form.chiefComplaint" type="textarea" :rows="1" placeholder="" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="发病日期" prop="onsetDate">
<el-date-picker
v-model="form.onsetDate"
type="date"
size="default"
style="width: 100% !important"
placement="bottom"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="现病史" prop="currentIllnessHistory">
<el-input v-model="form.currentIllnessHistory" type="textarea" :rows="2" placeholder="" />
</el-form-item>
<el-form-item label="既往史" prop="pastMedicalHistory">
<el-input v-model="form.pastMedicalHistory" type="textarea" :rows="2" placeholder="" />
</el-form-item>
<el-form-item label="个人史" prop="menstrualHistory">
<el-input v-model="form.menstrualHistory" type="textarea" :rows="2" placeholder="" />
</el-form-item>
<el-form-item label="过敏史" prop="allergyHistory">
<el-input v-model="form.allergyHistory" type="textarea" :rows="2" placeholder="" />
</el-form-item>
<el-form-item label="查体" prop="physicalExamination">
<el-input v-model="form.physicalExamination" type="textarea" :rows="2" placeholder="" />
</el-form-item>
<el-form-item label="处理" prop="treatment">
<el-input v-model="form.treatment" type="textarea" :rows="2" placeholder="" />
</el-form-item>
<el-form-item label="辅助检查" prop="auxiliaryExamination">
<el-input v-model="form.auxiliaryExamination" type="textarea" :rows="2" placeholder="" />
</el-form-item>
</el-form>
<el-dialog <el-dialog
:title="emrTitle" :title="emrTitle"
v-model="openEmrTemplate" v-model="openEmrTemplate"
:width="showDialog == 'emrHistory'?'800px':'600px'" :width="showDialog === 'emrHistory' ? '800px' : '600px'"
append-to-body append-to-body
destroy-on-close destroy-on-close
> >
<emrTemplate v-if="showDialog == 'emrTemplate'" @selectRow="templateSelect" /> <emrTemplate v-if="showDialog === 'emrTemplate'" @selectRow="templateSelect" />
<emrhistory v-if="showDialog == 'emrHistory'" @selectRow="emrHistorySelect" /> <emrhistory v-if="showDialog === 'emrHistory'" @selectRow="emrHistorySelect" />
<div v-if="showDialog == 'saveTemplate'"> <div v-if="showDialog === 'saveTemplate'">
<span> 模板名称 </span> <span>模板名称</span>
<el-input <el-input
v-model="templateName" v-model="templateName"
style="width: 260px; margin-top: 10px; margin-right: 20px" style="width: 260px; margin-top: 10px; margin-right: 20px"
@@ -203,91 +154,114 @@ import { saveEmr, getEmrDetail, saveEmrTemplate } from '../api';
import emrTemplate from '../emr/emrtemplate.vue'; import emrTemplate from '../emr/emrtemplate.vue';
import emrhistory from '../emr/emrhistory.vue'; import emrhistory from '../emr/emrhistory.vue';
import { ref, computed } from 'vue'; import { ref, computed, watch, getCurrentInstance } from 'vue';
import { formatDate as formatDateUtil } from '@/utils/index';
// 2. 当前就诊日期(默认为当前时间) // Props
const currentVisitDate = computed(() => {
return new Date().toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
});
const form = ref({});
const emrTitle = ref('');
const radio = ref(1);
const rules = ref({
chiefComplaint: [{ required: true, message: '请输入主诉', trigger: 'blur' }],
});
const emits = defineEmits(['save']);
const { proxy } = getCurrentInstance();
const showDialog = ref('');
const openEmrTemplate = ref(false);
const templateName = ref('');
const props = defineProps({ const props = defineProps({
patientInfo: { patientInfo: {
type: Object, type: Object,
required: true, required: true,
}, },
visitType: { // ✅ 接收父组件传来的值 visitType: {
type: String,
default: '',
},
firstVisitDate: {
type: String, type: String,
default: '', default: '',
}, },
}); });
// Computed: 当前就诊日期
const currentVisitDate = computed(() => {
if (props.visitType === 'FOLLOW_UP' && props.firstVisitDate) {
return props.firstVisitDate;
}
return formatDateUtil(props.patientInfo?.registerTime || new Date());
});
// 表单数据
const form = ref({
height: '',
weight: '',
temperature: '',
pulse: '',
chiefComplaint: '',
onsetDate: '',
currentIllnessHistory: '',
pastMedicalHistory: '',
menstrualHistory: '',
allergyHistory: '',
physicalExamination: '',
treatment: '',
auxiliaryExamination: '',
});
// 文本字段配置(用于循环渲染)
const textFieldList = [
{ key: 'currentIllnessHistory', label: '现病史' },
{ key: 'pastMedicalHistory', label: '既往史' },
{ key: 'menstrualHistory', label: '个人史' },
{ key: 'allergyHistory', label: '过敏史' },
{ key: 'physicalExamination', label: '查体' },
{ key: 'treatment', label: '处理' },
{ key: 'auxiliaryExamination', label: '辅助检查' },
];
// 弹窗控制
const emrTitle = ref('');
const radio = ref(1);
const showDialog = ref('');
const openEmrTemplate = ref(false);
const templateName = ref('');
// 事件
const emits = defineEmits(['save']);
const { proxy } = getCurrentInstance();
// 监听表单变化
watch( watch(
() => form.value, () => form.value,
() => { () => {
// 如果表单数据有变化,通知父组件保存
emits('save', false); emits('save', false);
}, },
{ deep: true } { deep: true }
); );
/** // 格式化日期(用于 onsetDate
* 保存病历 function formatDate(date) {
*/ if (!date) return '';
return formatDateUtil(date);
}
// 保存病历
function addEmr() { function addEmr() {
proxy.$refs['emrRef'].validate((valid) => { // 简单校验主诉
if (valid) { if (!form.value.chiefComplaint) {
saveEmr({ proxy.$message.warning('请输入主诉');
patientId: props.patientInfo.patientId, return;
encounterId: props.patientInfo.encounterId, }
contextJson: form.value, saveEmr({
}).then((res) => { patientId: props.patientInfo.patientId,
if (res.code == 200) { encounterId: props.patientInfo.encounterId,
proxy.$modal.msgSuccess('病历已保存'); contextJson: form.value,
emits('save', true); }).then((res) => {
} if (res.code === 200) {
}); proxy.$modal.msgSuccess('病历已保存');
emits('save', true);
} }
}); });
} }
/**
* 病历模板 // 打开模板
*/
function handleEmrTemplate() { function handleEmrTemplate() {
emrTitle.value = '病历模板'; emrTitle.value = '病历模板';
showDialog.value = 'emrTemplate'; showDialog.value = 'emrTemplate';
openEmrTemplate.value = true; openEmrTemplate.value = true;
} }
/**
* 选择病历模板 // 打开历史
*/
function templateSelect(row) {
form.value = row;
}
function emrHistorySelect(row) {
form.value = row;
openEmrTemplate.value = false;
}
/**
* 历史病历
*/
function handleEmrHistory() { function handleEmrHistory() {
emrTitle.value = '历史病历'; emrTitle.value = '历史病历';
showDialog.value = 'emrHistory'; showDialog.value = 'emrHistory';
@@ -295,55 +269,93 @@ function handleEmrHistory() {
sessionStorage.setItem('patientId', props.patientInfo.patientId); sessionStorage.setItem('patientId', props.patientInfo.patientId);
} }
function getDetail(encounterId) { // 保存为模板
getEmrDetail(encounterId).then((res) => {
if (res.data) {
form.value = JSON.parse(res.data.contextJson);
// 提交父组件刷新保存装填
emits('save', true);
} else {
form.value = {};
}
});
}
/**
* 保存病历模板
*/
function handleSaveTemplate() { function handleSaveTemplate() {
emrTitle.value = '保存模板'; emrTitle.value = '保存模板';
showDialog.value = 'saveTemplate'; showDialog.value = 'saveTemplate';
openEmrTemplate.value = true; openEmrTemplate.value = true;
} }
/** // 选择模板/历史
* 弹窗确认操作,包括保存病历模板/选择病历模板/选择历史病历 function templateSelect(row) {
*/ form.value = { ...row };
function submit() { openEmrTemplate.value = false;
switch (showDialog.value) { }
case 'saveTemplate': function emrHistorySelect(row) {
saveEmrTemplate({ form.value = { ...row };
templateName: templateName.value,
useScopeCode: radio.value,
contextJson: form.value,
}).then((res) => {
if (res.code == 200) {
openEmrTemplate.value = false;
proxy.$modal.msgSuccess('保存成功');
}
});
break;
case 'emrTemplate':
openEmrTemplate.value = false;
break;
case 'emrHistory':
break;
}
}
function cancel() {
openEmrTemplate.value = false; openEmrTemplate.value = false;
// openDiagnosis.value = false;
} }
defineExpose({ getDetail, addEmr }); // 弹窗确认
</script> function submit() {
if (showDialog.value === 'saveTemplate') {
if (!templateName.value.trim()) {
proxy.$message.warning('请输入模板名称');
return;
}
saveEmrTemplate({
templateName: templateName.value,
useScopeCode: radio.value,
contextJson: form.value,
}).then((res) => {
if (res.code === 200) {
openEmrTemplate.value = false;
proxy.$modal.msgSuccess('保存成功');
}
});
} else {
openEmrTemplate.value = false;
}
}
function cancel() {
openEmrTemplate.value = false;
}
// 可选:打印功能
// function printEmr() {
// const printContent = proxy.$refs.printArea.innerHTML;
// const originalContent = document.body.innerHTML;
// document.body.innerHTML = printContent;
// window.print();
// document.body.innerHTML = originalContent;
// window.location.reload(); // 恢复组件状态
// }
// 暴露方法给父组件
defineExpose({
getDetail(encounterId) {
getEmrDetail(encounterId).then((res) => {
if (res.data) {
try {
form.value = JSON.parse(res.data.contextJson) || {};
} catch (e) {
form.value = {};
}
emits('save', true);
} else {
form.value = {};
}
});
},
addEmr
});
</script>
<style scoped>
/* 可选:增强打印样式 */
@media print {
body * {
visibility: hidden;
}
#printArea, #printArea * {
visibility: visible;
}
#printArea {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
}
</style>

View File

@@ -10,23 +10,15 @@
<el-table <el-table
ref="emrHistoryRef" ref="emrHistoryRef"
:data="emrHistory" :data="emrHistory"
row-key="patientId" row-key="id"
highlight-current-row
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<!-- @cell-click="clickRow" -->
<el-table-column type="selection" width="55" /> <el-table-column type="selection" width="55" />
<el-table-column label="主诉" align="left" prop="name" :show-overflow-tooltip="true"/> <el-table-column label="主诉" align="left" prop="name" :show-overflow-tooltip="true"/>
<el-table-column label="时间" align="center" prop="createTime" width="180" :show-overflow-tooltip="true"/> <el-table-column label="时间" align="center" prop="createTime" width="180" :show-overflow-tooltip="true"/>
<el-table-column label="操作" align="center" width="80"> <el-table-column label="操作" align="center" width="80">
<template #default="scope"> <template #default="scope">
<!-- <el-button <el-button link type="primary" @click.stop="clickRow(scope.row)">复用</el-button>
link
type="primary"
@click.stop="handelDetail(scope.row)"
>详情
</el-button> -->
<el-button link type="primary" @click.stop="clickRow(scope.row)">复用 </el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -43,6 +35,7 @@
<script setup> <script setup>
import { formatDate, formatDateymd } from '@/utils/index'; import { formatDate, formatDateymd } from '@/utils/index';
import { getEmrHistoryList } from '../api'; import { getEmrHistoryList } from '../api';
const queryParams = ref({ const queryParams = ref({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
@@ -51,13 +44,19 @@ const queryParams = ref({
const selectRow = ref({}); const selectRow = ref({});
const emrHistory = ref([]); const emrHistory = ref([]);
const emits = defineEmits(['selectRow']); const emits = defineEmits(['selectRow']);
const emrHistoryRef = ref(null);
const selectedRows = ref([]);
const total = ref(0);
getList(); getList();
function getList() { function getList() {
if (sessionStorage.getItem('patientId')) { if (sessionStorage.getItem('patientId')) {
queryParams.value.patientId = sessionStorage.getItem('patientId'); queryParams.value.patientId = sessionStorage.getItem('patientId');
getEmrHistoryList(queryParams.value).then((res) => { getEmrHistoryList(queryParams.value).then((res) => {
emrHistory.value = res.data.records; emrHistory.value = res.data.records;
total.value = res.data.total;
emrHistory.value.map((k) => { emrHistory.value.map((k) => {
k.name = JSON.parse(k.contextJson).chiefComplaint; k.name = JSON.parse(k.contextJson).chiefComplaint;
k.createTime = formatDate(k.createTime); k.createTime = formatDate(k.createTime);
@@ -66,6 +65,12 @@ function getList() {
} }
} }
function handleSelectionChange(selection) {
// 直接更新选中行数据不干涉表格UI状态
// 这样可以允许用户使用全选框功能
selectedRows.value = selection;
}
function clickRow(row) { function clickRow(row) {
selectRow.value = JSON.parse(row.contextJson); selectRow.value = JSON.parse(row.contextJson);
emits('selectRow', selectRow.value); emits('selectRow', selectRow.value);

View File

@@ -43,7 +43,7 @@ const props = defineProps({
required: true, required: true,
}, },
}); });
// getList(); getList();
function getList() { function getList() {
queryParams.value.useScopeCode = 1; queryParams.value.useScopeCode = 1;
getEmrTemplateList(queryParams.value).then((res) => { getEmrTemplateList(queryParams.value).then((res) => {

View File

@@ -108,24 +108,7 @@
v-for="item in scope.row.stockList" v-for="item in scope.row.stockList"
:key="item.inventoryId" :key="item.inventoryId"
:value="item.inventoryId" :value="item.inventoryId"
:label=" :label="item.locationName"
item.locationName +
' ' +
'批次号: ' +
item.lotNumber +
' ' +
' 库存:' +
stockFormat(
scope.row.partPercent,
scope.row.unitCode_dictText,
scope.row.minUnitCode_dictText,
item.quantity
) +
' 单价:' +
item.price.toFixed(2) +
'/' +
item.unitCode_dictText
"
@click="handleNumberClick(item, scope.$index)" @click="handleNumberClick(item, scope.$index)"
/> />
</el-select> </el-select>
@@ -157,7 +140,7 @@
<div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap"> <div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap">
<div class="form-group"> <div class="form-group">
<!-- 单次剂量 --> <!-- 单次剂量 -->
<el-form-item <el-form-item
label="单次用量:" label="单次用量:"
prop="doseQuantity" prop="doseQuantity"
class="required-field" class="required-field"
@@ -176,7 +159,7 @@
</el-form-item> </el-form-item>
<!-- 剂量单位 --> <!-- 剂量单位 -->
<el-select <el-select
v-model="scope.row.minUnitCode" v-model="scope.row.unitCode"
style="width: 70px; margin-right: 20px" style="width: 70px; margin-right: 20px"
placeholder=" " placeholder=" "
> >
@@ -316,6 +299,7 @@
@keyup.enter.prevent=" @keyup.enter.prevent="
handleEnter('dispensePerDuration', scope.row, scope.$index) handleEnter('dispensePerDuration', scope.row, scope.$index)
" "
@input="calculateTotalAmount(scope.row, scope.$index)"
> >
<template #suffix>天</template> <template #suffix>天</template>
</el-input-number> </el-input-number>
@@ -369,62 +353,256 @@
</template> </template>
<template v-else-if="scope.row.adviceType == 2"> <template v-else-if="scope.row.adviceType == 2">
<div style="display: flex; align-items: center; margin-bottom: 16px; gap: 16px"> <div style="display: flex; align-items: center; margin-bottom: 16px; gap: 16px">
<span style="font-size: 16px; font-weight: 600"> <span class="medicine-title">
{{ {{
scope.row.adviceName + scope.row.adviceName +
' ' + ' ' +
scope.row.volume + scope.row.volume +
' ' + ' [' +
scope.row.unitPrice + Number(scope.row.unitPrice).toFixed(2) +
' 元/' + ' 元' +
scope.row.unitCode_dictText '/' +
scope.row.unitCode_dictText +
']'
}} }}
</span> </span>
<div class="form-group">
<!-- <el-form-item prop="conditionDefinitionId">
<el-select <el-select
v-model="scope.row.lotNumber" v-model="scope.row.conditionDefinitionId"
style="width: 180px; margin-right: 20px" style="width: 180px; margin: 0 20px"
placeholder="诊断"
>
<el-option
v-for="item in diagnosisList"
:key="item.conditionId"
:label="item.name"
:value="item.definitionId"
@click="handleDiagnosisChange(item, scope.row)"
/>
</el-select>
</el-form-item> -->
<el-form-item prop="lotNumber" label="药房:">
<el-select
v-model="scope.row.inventoryId"
style="width: 400px; margin-right: 20px"
placeholder="药房" placeholder="药房"
> >
<el-option <el-option
v-for="item in scope.row.stockList" v-for="item in scope.row.stockList"
:key="item.lotNumber" :key="item.inventoryId"
:value="item.lotNumber" :value="item.inventoryId"
:label=" :label="item.locationName"
item.locationName +
' ' +
'批次号: ' +
item.lotNumber +
' ' +
' 库存:' +
stockFormat(
scope.row.partPercent,
scope.row.unitCode_dictText,
scope.row.minUnitCode_dictText,
item.quantity
) +
' 单价:' +
item.price.toFixed(2) +
'/' +
item.unitCode_dictText
"
@click="handleNumberClick(item, scope.$index)" @click="handleNumberClick(item, scope.$index)"
/> />
</el-select> </el-select>
</el-form-item>
<el-form-item
label="执行次数:"
prop="executeNum"
class="required-field"
data-prop="executeNum"
v-if="scope.row.injectFlag == 1"
>
<el-input-number
:min="1"
v-model="scope.row.executeNum"
controls-position="right"
:controls="false"
:ref="(el) => (inputRefs.executeNum = el)"
@keyup.enter.prevent="handleEnter('executeNum', scope.row, scope.$index)"
style="width: 70px; margin-right: 20px"
/>
</el-form-item>
<span class="medicine-info"> 诊断:{{ diagnosisName }} </span>
<span class="medicine-info"> 皮试:{{ scope.row.skinTestFlag_enumText }} </span>
<span class="medicine-info"> 注射药品:{{ scope.row.injectFlag_enumText }} </span>
<span class="total-amount">
总金额:{{ scope.row.totalPrice ? scope.row.totalPrice + ' 元' : '0.00 元' }}
</span>
</div>
<div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap">
<div class="form-group">
<!-- 单次剂量 -->
<el-form-item
label="单次用量:"
prop="doseQuantity"
class="required-field"
data-prop="doseQuantity"
>
<el-input-number
:min="0"
v-model="scope.row.doseQuantity"
controls-position="right"
:controls="false"
style="width: 70px; margin-right: 20px"
:ref="(el) => (inputRefs.doseQuantity = el)"
@input="convertValues(scope.row, scope.$index)"
@keyup.enter.prevent="handleEnter('doseQuantity', scope.row, scope.$index)"
/>
</el-form-item>
<!-- 剂量单位 -->
<el-select
v-model="scope.row.unitCode"
style="width: 70px; margin-right: 20px"
placeholder=" "
>
<template v-for="item in scope.row.unitCodeList" :key="item.value">
<el-option
v-if="
scope.row.unitCodeList.length == 3
? item.type == unitMap['minUnit']
: item.type == unitMap['unit']
"
:value="item.value"
:label="item.label"
/>
</template>
</el-select>
<span>=</span>
<!-- 单次剂量 -->
<el-form-item prop="dose" class="required-field" data-prop="dose">
<el-input-number
v-model="scope.row.dose"
controls-position="right"
:controls="false"
style="width: 70px; margin: 0 20px"
:ref="(el) => (inputRefs.dose = el)"
@input="convertDoseValues(scope.row, scope.$index)"
@keyup.enter.prevent="handleEnter('dose', scope.row, scope.$index)"
/>
</el-form-item>
<!-- 全部单位 -->
<el-select
v-model="scope.row.doseUnitCode"
style="width: 70px"
placeholder=" "
@change="convertValues(scope.row, scope.$index)"
>
<el-option
v-for="item in scope.row.unitCodeList"
:value="item.value"
:label="item.label"
:key="item.value"
/>
</el-select>
</div>
<div class="form-group">
<el-form-item <el-form-item
label="数量" label="给药途径"
prop="methodCode"
class="required-field"
data-prop="methodCode"
>
<el-select
v-model="scope.row.methodCode"
placeholder="给药途径"
clearable
filterable
:ref="(el) => (inputRefs.methodCode = el)"
@keyup.enter.prevent="
() => {
inputRefs.methodCode.blur();
}
"
@visible-change="
(value) => {
if (!value) {
handleEnter('methodCode', scope.row, scope.$index);
}
}
"
>
<el-option
v-for="dict in method_code"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item
label="用药频次:"
prop="rateCode"
class="required-field"
data-prop="rateCode"
>
<el-select
v-model="scope.row.rateCode"
placeholder="频次"
style="width: 120px"
filterable
@keyup.enter.prevent="
() => {
inputRefs.rateCode.blur();
}
"
@change="calculateTotalAmount(scope.row, scope.$index)"
@visible-change="
(value) => {
if (!value) {
handleEnter('rateCode', scope.row, scope.$index);
}
}
"
:ref="(el) => (inputRefs.rateCode = el)"
>
<el-option
v-for="dict in rate_code"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</div>
</div>
<div
style="
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
margin-top: 10px;
"
>
<div class="form-group">
<el-form-item
label="用药天数:"
prop="dispensePerDuration"
class="required-field"
data-prop="dispensePerDuration"
>
<el-input-number
v-model="scope.row.dispensePerDuration"
style="width: 80px"
:min="1"
controls-position="right"
:controls="false"
:ref="(el) => (inputRefs.dispensePerDuration = el)"
@keyup.enter.prevent="
handleEnter('dispensePerDuration', scope.row, scope.$index)
"
@input="calculateTotalAmount(scope.row, scope.$index)"
>
<template #suffix>天</template>
</el-input-number>
</el-form-item>
<el-form-item
label="总量:"
prop="quantity" prop="quantity"
class="required-field" class="required-field"
data-prop="quantity" data-prop="quantity"
> >
<el-input-number <el-input-number
placeholder="数量"
v-model="scope.row.quantity" v-model="scope.row.quantity"
style="width: 70px" style="width: 70px"
controls-position="right" controls-position="right"
:controls="false" :controls="false"
:ref="(el) => (inputRefs.quantity = el)"
@keyup.enter.prevent="handleEnter('quantity', scope.row, scope.$index)" @keyup.enter.prevent="handleEnter('quantity', scope.row, scope.$index)"
@input="calculateTotalAmount(scope.row, scope.$index)" @input="calculateTotalPrice(scope.row, scope.$index)"
/> />
</el-form-item> </el-form-item>
<el-select <el-select
@@ -440,15 +618,17 @@
:label="item.label" :label="item.label"
@click=" @click="
() => { () => {
if (item.type == unitMap['minUnit']) {
scope.row.unitPrice = scope.row.minUnitPrice;
} else {
scope.row.unitPrice = scope.row.unitTempPrice;
}
scope.row.unitCode_dictText = item.label; scope.row.unitCode_dictText = item.label;
} }
" "
/> />
</template> </template>
</el-select> </el-select>
<span class="total-amount">
总金额:{{ scope.row.totalPrice ? scope.row.totalPrice + ' 元' : '0.00 元' }}
</span>
</div> </div>
<el-button type="primary" @click="handleSaveSign(scope.row, scope.$index)"> <el-button type="primary" @click="handleSaveSign(scope.row, scope.$index)">
确定 确定
@@ -511,7 +691,7 @@
<el-button type="primary" @click="handleSaveSign(scope.row, scope.$index)"> <el-button type="primary" @click="handleSaveSign(scope.row, scope.$index)">
确定 确定
</el-button> </el-button>
</div> </div>
</template> </template>
</div> </div>
</el-form> </el-form>
@@ -550,40 +730,38 @@
</template> </template>
</el-table-column> </el-table-column>
<!-- <el-table-column label="序号" align="center" width="60" prop="sortNumber"> </el-table-column> --> <!-- <el-table-column label="序号" align="center" width="60" prop="sortNumber"> </el-table-column> -->
<el-table-column label="医嘱" align="center" prop="productName" width="400"> <!-- 医嘱类型列 -->
<el-table-column label="医嘱类型" align="center" width="120">
<template #default="scope"> <template #default="scope">
<template v-if="getRowDisabled(scope.row)"> <template v-if="getRowDisabled(scope.row)">
<el-select <el-select
style="width: 35%; margin-right: 20px"
v-model="scope.row.adviceType" v-model="scope.row.adviceType"
:ref="'adviceTypeRef' + scope.$index" :ref="'adviceTypeRef' + scope.$index"
@change=" placeholder="请选择"
(value) => { style="width: 100%"
expandOrder = []; @change="handleAdviceTypeChange(scope.row, scope.$index)">
prescriptionList[scope.$index].adviceName = undefined; <!-- 直接使用drord_doctor_type字典已包含拆分后的西药(1)和中成药(2) -->
adviceQueryParams.adviceType = value;
}
"
>
<el-option <el-option
v-for="item in adviceTypeList" v-for="dict in getSortedAdviceTypeList()"
:key="item.value" :key="dict.value"
:label="item.label" :label="dict.label"
:value="item.value" :value="dict.value"
@click="
() => {
prescriptionList[scope.$index].adviceType = item.value;
prescriptionList[scope.$index].adviceType_dictText = item.label;
}
"
/> />
</el-select> </el-select>
</template>
<!-- 非编辑模式下使用getAdviceTypeText函数确保显示正确的文本 -->
<span v-else>{{ getAdviceTypeText(scope.row.adviceType) }}</span>
</template>
</el-table-column>
<!-- 项目列 -->
<el-table-column label="项目" align="center" prop="productName" width="280">
<template #default="scope">
<template v-if="getRowDisabled(scope.row)">
<el-popover <el-popover
:popper-style="{ padding: '0' }" :popper-style="{ padding: '0' }"
placement="bottom-start" placement="bottom-start"
:visible="scope.row.showPopover" :visible="scope.row.showPopover"
:width="1200" :width="1200">
>
<adviceBaseList <adviceBaseList
ref="adviceTableRef" ref="adviceTableRef"
:popoverVisible="scope.row.showPopover" :popoverVisible="scope.row.showPopover"
@@ -594,24 +772,14 @@
<template #reference> <template #reference>
<el-input <el-input
:ref="'adviceRef' + scope.$index" :ref="'adviceRef' + scope.$index"
style="width: 50%"
v-model="scope.row.adviceName" v-model="scope.row.adviceName"
placeholder="请选择项目" placeholder="请选择项目"
@input="handleChange" @input="handleChange"
@click="handleFocus(scope.row, scope.$index)" @click="handleFocus(scope.row, scope.$index)"
@keyup.enter.stop="handleFocus(scope.row, scope.$index)" @keyup.enter.stop="handleFocus(scope.row, scope.$index)"
@keydown=" @keydown="handleInputKeyDown(scope.row, $event)"
(e) => {
if (!scope.row.showPopover) return;
// 拦截上下键和回车事件
if (['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) {
e.preventDefault();
// 传递事件到弹窗容器
adviceTableRef.handleKeyDown(e);
}
}
"
@blur="handleBlur(scope.row)" @blur="handleBlur(scope.row)"
style="width: 100%"
/> />
</template> </template>
</el-popover> </el-popover>
@@ -735,8 +903,8 @@ import {
getAdviceBaseInfo, getAdviceBaseInfo,
getActivityBindDevice, getActivityBindDevice,
} from '../api'; } from '../api';
import adviceBaseList from '../advicebaselist'; import adviceBaseList from '../adviceBaseList.vue';
import { computed, getCurrentInstance, nextTick, watch } from 'vue'; import { computed, getCurrentInstance, nextTick, watch, unref } from 'vue';
import { calculateQuantityByDays, formatNumber } from '@/utils/his'; import { calculateQuantityByDays, formatNumber } from '@/utils/his';
import OrderGroupDrawer from './orderGroupDrawer'; import OrderGroupDrawer from './orderGroupDrawer';
import PrescriptionHistory from './prescriptionHistory'; import PrescriptionHistory from './prescriptionHistory';
@@ -751,7 +919,11 @@ const prescriptionList = ref([]);
const form = ref({ const form = ref({
prescriptionList: prescriptionList.value, prescriptionList: prescriptionList.value,
}); });
const adviceQueryParams = ref({}); const adviceQueryParams = ref({
searchKey: '',
adviceType: '',
categoryCode: '' // 用于筛选西药(2)和中成药(1)
});
const rowIndex = ref(-1); const rowIndex = ref(-1);
const groupIndex = ref(1); const groupIndex = ref(1);
const groupIndexList = ref([]); const groupIndexList = ref([]);
@@ -806,25 +978,32 @@ const { proxy } = getCurrentInstance();
const inputRefs = ref({}); // 存储输入框实例 const inputRefs = ref({}); // 存储输入框实例
const requiredProps = ref([]); // 存储必填项 prop 顺序 const requiredProps = ref([]); // 存储必填项 prop 顺序
const totalAmount = ref(0); const totalAmount = ref(0);
const { method_code, unit_code, rate_code, distribution_category_code } = proxy.useDict( const { method_code, unit_code, rate_code, distribution_category_code, drord_doctor_type } = proxy.useDict(
'method_code', 'method_code',
'unit_code', 'unit_code',
'rate_code', 'rate_code',
'distribution_category_code' 'distribution_category_code',
'drord_doctor_type'
); );
// 删除硬编码的adviceTypeList直接使用drord_doctor_type字典
const adviceTypeList = ref([ const adviceTypeList = ref([
{ {
label: '西药中成药', label: '西药',
value: 1, value: 1,
},
{
label: '中成药',
value: 2,
}, },
{ {
label: '耗材', label: '耗材',
value: 2, value: 3,
}, },
{ {
label: '诊疗', label: '诊疗',
value: 3, value: 4,
}, },
{ {
label: '全部', label: '全部',
@@ -863,6 +1042,89 @@ watch(
}, },
{ immediate: true } { immediate: true }
); );
// 获取排序后的医嘱类型列表drord_doctor_type字典已包含拆分后的西药1和中成药2
function getSortedAdviceTypeList() {
if (!drord_doctor_type.value || !Array.isArray(drord_doctor_type.value)) {
return [];
}
// 按照字典排序显示优先使用dictSort如果没有则使用value排序
// 同时确保value是数字类型与adviceType匹配
return [...drord_doctor_type.value].sort((a, b) => {
// 优先使用dictSort排序
if (a.dictSort !== undefined && b.dictSort !== undefined) {
return (a.dictSort || 0) - (b.dictSort || 0);
}
// 如果没有dictSort使用value排序
const valueA = Number(a.value) || 0;
const valueB = Number(b.value) || 0;
return valueA - valueB;
}).map(item => ({
...item,
value: Number(item.value) // 确保value是数字类型与adviceType匹配
}));
}
// 获取医嘱类别显示文本
function getAdviceTypeText(adviceType) {
// 如果adviceType为空或未定义返回空字符串
if (adviceType === undefined || adviceType === null || adviceType === '') {
return '';
}
// 使用drord_doctor_type字典已包含西药1、中成药2、诊疗3、耗材4、全部5
if (drord_doctor_type.value && Array.isArray(drord_doctor_type.value) && drord_doctor_type.value.length > 0) {
// 转换为数字进行比较
const adviceTypeNum = Number(adviceType);
// 尝试匹配字典项
let dictItem = drord_doctor_type.value.find(item => {
// 先尝试数字匹配
const itemValue = Number(item.value);
if (!isNaN(itemValue) && itemValue === adviceTypeNum) {
return true;
}
// 再尝试字符串匹配
return String(item.value) === String(adviceType);
});
// 如果找到匹配项,返回标签
if (dictItem && dictItem.label) {
return dictItem.label;
}
}
// 如果字典未加载或未找到匹配项,返回空字符串
// 注意:这里不返回数字,避免显示数字
return '';
}
// 处理医嘱类型变化
function handleAdviceTypeChange(row, index) {
// 根据选择的医嘱类型设置categoryCode筛选条件
// drord_doctor_type: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=全部
if (row.adviceType == 1) {
// 西药categoryCode = 2
adviceQueryParams.value.categoryCode = '2';
adviceQueryParams.value.adviceType = 1; // 药品类型
} else if (row.adviceType == 2) {
// 中成药categoryCode = 1
adviceQueryParams.value.categoryCode = '1';
adviceQueryParams.value.adviceType = 1; // 药品类型
} else if (row.adviceType == 3) {
// 诊疗adviceType = 3
adviceQueryParams.value.categoryCode = '';
adviceQueryParams.value.adviceType = 3; // 诊疗类型
} else if (row.adviceType == 4) {
// 耗材adviceType = 2后端接口中耗材的adviceType是2
adviceQueryParams.value.categoryCode = '';
adviceQueryParams.value.adviceType = 2; // 耗材类型
} else {
// 全部(5)或其他:清除筛选
adviceQueryParams.value.categoryCode = '';
adviceQueryParams.value.adviceType = '';
}
}
function handleTotalAmount() { function handleTotalAmount() {
totalAmount.value = prescriptionList.value.reduce((accumulator, currentRow) => { totalAmount.value = prescriptionList.value.reduce((accumulator, currentRow) => {
if (currentRow.chargeStatus != 8) { if (currentRow.chargeStatus != 8) {
@@ -886,11 +1148,14 @@ function getListInfo(addNewRow) {
isAdding.value = false; isAdding.value = false;
getPrescriptionList(props.patientInfo.encounterId).then((res) => { getPrescriptionList(props.patientInfo.encounterId).then((res) => {
prescriptionList.value = res.data.map((item) => { prescriptionList.value = res.data.map((item) => {
const parsedContent = JSON.parse(item.contentJson);
return { return {
...JSON.parse(item.contentJson), ...parsedContent,
...item, ...item,
doseQuantity: JSON.parse(item.contentJson)?.doseQuantity, // 确保adviceType是数字类型以便正确显示文本
doseUnitCode_dictText: JSON.parse(item.contentJson)?.doseUnitCode_dictText, adviceType: parsedContent.adviceType !== undefined ? Number(parsedContent.adviceType) : (item.adviceType !== undefined ? Number(item.adviceType) : undefined),
doseQuantity: parsedContent?.doseQuantity,
doseUnitCode_dictText: parsedContent?.doseUnitCode_dictText,
}; };
}); });
groupMarkers.value = getGroupMarkers(prescriptionList.value); // 更新标记 groupMarkers.value = getGroupMarkers(prescriptionList.value); // 更新标记
@@ -933,6 +1198,13 @@ function handleAddPrescription() {
return; return;
} }
isAdding.value = true; isAdding.value = true;
// 重置查询参数
adviceQueryParams.value = {
searchKey: '',
adviceType: '',
categoryCode: ''
};
// 在数组最前方添加一行,让新增行显示在最上边 // 在数组最前方添加一行,让新增行显示在最上边
prescriptionList.value.unshift({ prescriptionList.value.unshift({
uniqueKey: nextId.value++, uniqueKey: nextId.value++,
@@ -979,10 +1251,45 @@ function handleDiagnosisChange(item) {
function handleFocus(row, index) { function handleFocus(row, index) {
rowIndex.value = index; rowIndex.value = index;
row.showPopover = true; row.showPopover = true;
// 确保查询参数与当前行类型一致
let categoryCode = '';
let adviceType = row.adviceType || '';
// 根据医嘱类型设置筛选条件
// drord_doctor_type: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=全部
if (row.adviceType == 1) {
// 西药
categoryCode = '2';
adviceType = 1; // 药品类型
} else if (row.adviceType == 2) {
// 中成药
categoryCode = '1';
adviceType = 1; // 药品类型
} else if (row.adviceType == 3) {
// 诊疗adviceType = 3
categoryCode = '';
adviceType = 3; // 诊疗类型
} else if (row.adviceType == 4) {
// 耗材adviceType = 2后端接口中耗材的adviceType是2
categoryCode = '';
adviceType = 2; // 耗材类型
} else {
// 全部(5)或其他:显示所有类型
categoryCode = '';
adviceType = '';
}
adviceQueryParams.value = {
adviceType: adviceType,
categoryCode: categoryCode,
searchKey: adviceQueryParams.value.searchKey || ''
};
} }
function handleBlur(row) { function handleBlur(row) {
row.showPopover = false; setTimeout(() => {
row.showPopover = false;
}, 200);
} }
function handleChange(value) { function handleChange(value) {
@@ -993,6 +1300,8 @@ function handleChange(value) {
* 选择药品回调 * 选择药品回调
*/ */
function selectAdviceBase(key, row) { function selectAdviceBase(key, row) {
// 立即关闭当前弹窗
prescriptionList.value[rowIndex.value].showPopover = false;
if (row.adviceType == 3) { if (row.adviceType == 3) {
getActivityBindDevice({ activityId: row.adviceDefinitionId }).then((res) => { getActivityBindDevice({ activityId: row.adviceDefinitionId }).then((res) => {
if (res.data.activityBindDeviceInfos?.length > 0) { if (res.data.activityBindDeviceInfos?.length > 0) {
@@ -1035,10 +1344,48 @@ function selectAdviceBase(key, row) {
type: 'minUnit', type: 'minUnit',
}); });
} }
// 保存当前已选择的医嘱类型
const currentAdviceType = prescriptionList.value[rowIndex.value].adviceType;
// 如果医嘱类型是"全部"5或未选择根据药品的categoryCode自动判断
// drord_doctor_type: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=全部
let finalAdviceType = currentAdviceType;
if ((currentAdviceType === undefined || currentAdviceType === null || currentAdviceType === '' || Number(currentAdviceType) === 5) && Number(row.adviceType) === 1) {
// 药品类型需要根据categoryCode判断是西药还是中成药
// categoryCode = '1' 表示中成药categoryCode = '2' 表示西药
if (row.categoryCode == '1') {
finalAdviceType = 2; // 中成药
} else if (row.categoryCode == '2') {
finalAdviceType = 1; // 西药
} else {
// 如果没有categoryCode使用项目默认的类型
finalAdviceType = Number(row.adviceType);
}
} else if (currentAdviceType !== undefined && currentAdviceType !== null && currentAdviceType !== '' && Number(currentAdviceType) !== 5) {
// 用户已选择医嘱类型(且不是"全部"),保留用户的选择,确保是数字类型
finalAdviceType = Number(currentAdviceType);
} else {
// 使用项目默认的类型,确保是数字类型
finalAdviceType = Number(row.adviceType);
}
prescriptionList.value[rowIndex.value] = { prescriptionList.value[rowIndex.value] = {
...prescriptionList.value[rowIndex.value], ...prescriptionList.value[rowIndex.value],
...JSON.parse(JSON.stringify(row)), ...JSON.parse(JSON.stringify(row)),
adviceType: finalAdviceType, // 确保是数字类型
}; };
// 确保字典已加载后更新显示
nextTick(() => {
// 如果字典已加载,强制更新视图以确保显示正确的文本
if (drord_doctor_type.value && drord_doctor_type.value.length > 0) {
// 触发响应式更新
const currentRow = prescriptionList.value[rowIndex.value];
prescriptionList.value[rowIndex.value] = {
...currentRow
};
}
});
prescriptionList.value[rowIndex.value].orgId = undefined; prescriptionList.value[rowIndex.value].orgId = undefined;
prescriptionList.value[rowIndex.value].dose = undefined; prescriptionList.value[rowIndex.value].dose = undefined;
prescriptionList.value[rowIndex.value].unitCodeList = unitCodeList.value; prescriptionList.value[rowIndex.value].unitCodeList = unitCodeList.value;
@@ -1093,12 +1440,14 @@ function selectAdviceBase(key, row) {
prescriptionList.value[rowIndex.value].positionName = stock.locationName; prescriptionList.value[rowIndex.value].positionName = stock.locationName;
} }
} else { } else {
prescriptionList.value[rowIndex.value].orgId = JSON.parse(JSON.stringify(row)).positionId; // 执行科室默认逻辑:优先使用诊疗项目维护的所属科室,如果没有则使用开单科室
prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price; const defaultOrgId = row.positionId || props.patientInfo.orgId;
} prescriptionList.value[rowIndex.value].orgId = defaultOrgId;
expandOrder.value = [key]; prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price;
}
expandOrder.value = [key];
nextTick(() => { nextTick(() => {
if (row.adviceType == 1) { if (row.adviceType == 1 || row.adviceType == 2) {
if (row.injectFlag == 1) { if (row.injectFlag == 1) {
inputRefs.value['executeNum']?.focus(); inputRefs.value['executeNum']?.focus();
} else { } else {
@@ -1108,6 +1457,7 @@ function selectAdviceBase(key, row) {
inputRefs.value['quantity']?.focus(); inputRefs.value['quantity']?.focus();
} }
}); });
} }
function getOrgList() { function getOrgList() {
@@ -1142,6 +1492,7 @@ function handleDelete() {
expandOrder.value = []; expandOrder.value = [];
isAdding.value = false; isAdding.value = false;
adviceQueryParams.value.adviceType = undefined; adviceQueryParams.value.adviceType = undefined;
adviceQueryParams.value.categoryCode = undefined;
if (sum == selectRow.length) { if (sum == selectRow.length) {
proxy.$modal.msgSuccess('删除成功'); proxy.$modal.msgSuccess('删除成功');
groupIndexList.value = []; groupIndexList.value = [];
@@ -1326,7 +1677,7 @@ function handleSaveSign(row, index) {
row.patientId = props.patientInfo.patientId; row.patientId = props.patientInfo.patientId;
row.encounterId = props.patientInfo.encounterId; row.encounterId = props.patientInfo.encounterId;
row.accountId = accountId.value; row.accountId = accountId.value;
if (row.adviceType == 1) { if (row.adviceType == 1 || row.adviceType == 2) {
row.minUnitQuantity = row.minUnitQuantity =
row.minUnitCode == row.unitCode ? row.quantity : row.quantity * row.partPercent; row.minUnitCode == row.unitCode ? row.quantity : row.quantity * row.partPercent;
} else { } else {
@@ -1435,6 +1786,8 @@ function setValue(row) {
prescriptionList.value[rowIndex.value] = { prescriptionList.value[rowIndex.value] = {
...prescriptionList.value[rowIndex.value], ...prescriptionList.value[rowIndex.value],
...JSON.parse(JSON.stringify(row)), ...JSON.parse(JSON.stringify(row)),
// 确保adviceType为数字类型避免类型不匹配导致的显示问题
adviceType: Number(row.adviceType),
}; };
prescriptionList.value[rowIndex.value].orgId = undefined; prescriptionList.value[rowIndex.value].orgId = undefined;
prescriptionList.value[rowIndex.value].dose = undefined; prescriptionList.value[rowIndex.value].dose = undefined;
@@ -1488,9 +1841,11 @@ function setValue(row) {
.toFixed(2); .toFixed(2);
} }
} else { } else {
prescriptionList.value[rowIndex.value].orgId = JSON.parse(JSON.stringify(row)).positionId; // 执行科室默认逻辑:优先使用诊疗项目维护的所属科室,如果没有则使用开单科室
prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price; const defaultOrgId = row.positionId || props.patientInfo.orgId;
} prescriptionList.value[rowIndex.value].orgId = defaultOrgId;
prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price;
}
} }
// 组套保存 // 组套保存
@@ -1891,20 +2246,8 @@ function convertDoseValues(row, index) {
// 总量计算,仅适用只有两种单位的情况 // 总量计算,仅适用只有两种单位的情况
function calculateTotalAmount(row, index) { function calculateTotalAmount(row, index) {
nextTick(() => { nextTick(() => {
// 项目为耗材时 // 项目为西药或中成药时,根据用药天数和用药频次自动计算总量
if (row.adviceType == 2) { if (row.adviceType == 1 || row.adviceType == 2) {
// 拆零比为1时 总价等于数量乘以单价
if (row.partPercent == 1) {
row.totalPrice = row.quantity * row.unitPrice;
} else {
// 拆零比不为1时 如果当前总量单位是大单位,总价等于数量乘以大单位价格 否则总价等于数量乘以小单位价格
if (row.unitCodeList.find((k) => k.value == row.unitCode).type == 'unit') {
row.totalPrice = row.quantity * row.unitPrice;
} else {
row.totalPrice = (row.quantity * row.minUnitPrice).toFixed(2);
}
}
} else if (row.adviceType == 1) {
if (row.rateCode && row.dispensePerDuration) { if (row.rateCode && row.dispensePerDuration) {
// 根据用药天数和用药频次计算数量 // 根据用药天数和用药频次计算数量
let count = calculateQuantityByDays(row.rateCode, row.dispensePerDuration); let count = calculateQuantityByDays(row.rateCode, row.dispensePerDuration);
@@ -1925,6 +2268,23 @@ function calculateTotalAmount(row, index) {
prescriptionList.value[index].totalPrice = (quantity * row.unitPrice).toFixed(2); prescriptionList.value[index].totalPrice = (quantity * row.unitPrice).toFixed(2);
} }
} }
} else {
// 如果没有用药频次或用药天数,则根据数量计算总价(中成药兼容旧逻辑)
if (row.adviceType == 2) {
// 拆零比为1时 总价等于数量乘以单价
if (row.partPercent == 1) {
row.totalPrice = row.quantity * row.unitPrice;
} else {
// 拆零比不为1时 如果当前总量单位是大单位,总价等于数量乘以大单位价格 否则总价等于数量乘以小单位价格
if (row.unitCodeList && row.unitCodeList.find((k) => k.value == row.unitCode)) {
if (row.unitCodeList.find((k) => k.value == row.unitCode).type == 'unit') {
row.totalPrice = row.quantity * row.unitPrice;
} else {
row.totalPrice = (row.quantity * row.minUnitPrice).toFixed(2);
}
}
}
}
} }
} }
}); });

View File

@@ -100,8 +100,8 @@
}}</el-descriptions-item> }}</el-descriptions-item>
<el-descriptions-item label="" width="350"> <el-descriptions-item label="" width="350">
<!-- 初诊 / 复诊 按钮 --> <!-- 初诊 / 复诊 按钮 -->
<el-radio v-model="visitType" label="FIRST">初诊</el-radio> <el-radio v-model="visitType" label="FIRST" :disabled="visitTypeDisabled">初诊</el-radio>
<el-radio v-model="visitType" label="FOLLOW_UP">复诊</el-radio> <el-radio v-model="visitType" label="FOLLOW_UP" :disabled="visitTypeDisabled">复诊</el-radio>
<!-- 原有按钮 --> <!-- 原有按钮 -->
<el-button type="primary" plain @click.stop="handleFinish(patientInfo.encounterId)"> <el-button type="primary" plain @click.stop="handleFinish(patientInfo.encounterId)">
@@ -127,6 +127,13 @@
> >
办理住院 办理住院
</el-button> </el-button>
<el-button
type="primary"
plain
@click.stop="handleCancelEncounter"
>
取消接诊
</el-button>
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
</div> </div>
@@ -148,6 +155,7 @@
} }
" "
:visitType="visitType" :visitType="visitType"
:firstVisitDate="firstVisitDate"
/> />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="诊断" name="diagnosis"> <el-tab-pane label="诊断" name="diagnosis">
@@ -206,6 +214,7 @@ import {
leaveEncounter, leaveEncounter,
completeEncounter, completeEncounter,
getEnPrescriptionInfo, getEnPrescriptionInfo,
cancelEncounter,
getEmrHistoryList getEmrHistoryList
} from './components/api.js'; } from './components/api.js';
import prescriptionlist from './components/prescription/prescriptionlist.vue'; import prescriptionlist from './components/prescription/prescriptionlist.vue';
@@ -253,6 +262,8 @@ const emits = defineEmits(['click']);
const activeTab = ref('emr'); const activeTab = ref('emr');
const patientList = ref([]); const patientList = ref([]);
const patientInfo = ref({}); const patientInfo = ref({});
const visitTypeDisabled = ref(false);
const prescriptionInfo = ref([]); const prescriptionInfo = ref([]);
const registerTime = ref([formatDate(new Date()), formatDate(new Date())]); const registerTime = ref([formatDate(new Date()), formatDate(new Date())]);
const patientDrawerRef = ref(); const patientDrawerRef = ref();
@@ -264,6 +275,7 @@ const waitCount = ref(0);
const loading = ref(false); const loading = ref(false);
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const visitType = ref(''); const visitType = ref('');
const firstVisitDate = ref('');
const disabled = computed(() => { const disabled = computed(() => {
return Object.keys(patientInfo.value).length === 0; return Object.keys(patientInfo.value).length === 0;
}); });
@@ -290,6 +302,10 @@ function setVisitType(type) {
visitType.value = type; visitType.value = type;
} }
function checkPatientHistory(patient) { function checkPatientHistory(patient) {
// 重置状态
visitTypeDisabled.value = false;
firstVisitDate.value = '';
// 如果患者没有身份证号,无法判断是否为初诊 // 如果患者没有身份证号,无法判断是否为初诊
if (!patient.idCard) { if (!patient.idCard) {
// 默认设置为初诊 // 默认设置为初诊
@@ -297,6 +313,16 @@ function checkPatientHistory(patient) {
return; return;
} }
// // 如果当前就诊已经有明确的初复诊标识,则使用该标识
// if (patient.visitType) {
// visitType.value = patient.visitType;
// // 如果是已完诊的记录,禁用修改
// if (patient.statusEnum && patient.statusEnum !== 2) { // 假设2是就诊中状态
// visitTypeDisabled.value = true;
// }
// return;
// }
// 查询患者历史就诊记录 // 查询患者历史就诊记录
const params = { const params = {
patientId: patient.patientId, patientId: patient.patientId,
@@ -305,20 +331,31 @@ function checkPatientHistory(patient) {
getEmrHistoryList(params).then(res => { getEmrHistoryList(params).then(res => {
if (res.code === 200) { if (res.code === 200) {
const records = res.data?.records || [];
// 如果有历史记录,则为复诊 // 如果有历史记录,则为复诊
if (res.data && res.data.total > 0) { if (res.data && res.data.total > 0) {
visitType.value = 'FOLLOW_UP'; visitType.value = 'FOLLOW_UP';
// 计算最早一次病历创建时间作为初诊日期
const earliest = records.reduce((min, cur) => {
const ct = new Date(cur.createTime).getTime();
return ct < min ? ct : min;
}, new Date(records[0].createTime).getTime());
// 使用统一格式化
firstVisitDate.value = formatDate(earliest);
} else { } else {
// 如果没有历史记录,则为初诊 // 如果没有历史记录,则为初诊
visitType.value = 'FIRST'; visitType.value = 'FIRST';
firstVisitDate.value = '';
} }
} else { } else {
// 请求失败,默认设置为初诊 // 请求失败,默认设置为初诊
visitType.value = 'FIRST'; visitType.value = 'FIRST';
firstVisitDate.value = '';
} }
}).catch(() => { }).catch(() => {
// 异常情况,默认设置为初诊 // 异常情况,默认设置为初诊
visitType.value = 'FIRST'; visitType.value = 'FIRST';
firstVisitDate.value = '';
}); });
} }
function getWaitPatient() { function getWaitPatient() {
@@ -380,19 +417,25 @@ function handleOpen() {
function handleCardClick(item, index) { function handleCardClick(item, index) {
currentEncounterId.value = ''; currentEncounterId.value = '';
// if (item.active) {
// patientList.value[index].active = false;
// return;
// }
loading.value = true; loading.value = true;
patientList.value.forEach((patient) => { patientList.value.forEach((patient) => {
patient.active = patient.encounterId === item.encounterId; patient.active = patient.encounterId === item.encounterId;
}); });
patientInfo.value = item; patientInfo.value = item;
// 检查患者历史记录以确定初诊/复诊 // 优先使用数据库中保存的初复诊
checkPatientHistory(item); if (item.visitType) {
visitType.value = item.visitType;
// 已完诊的记录禁用修改
visitTypeDisabled.value = item.statusEnum !== 2;
// 如果有初诊日期也从数据库获取
if (item.firstVisitDate) {
firstVisitDate.value = item.firstVisitDate;
}
} else {
// 对于没有初复诊记录的患者,才使用原有的判断逻辑
checkPatientHistory(item);
}
activeTab.value = 'emr'; activeTab.value = 'emr';
nextTick(() => { nextTick(() => {
prescriptionRef.value.getListInfo(); prescriptionRef.value.getListInfo();
@@ -421,6 +464,8 @@ function handleFinish(encounterId) {
if (res.code == 200) { if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功'); proxy.$modal.msgSuccess('操作成功');
patientInfo.value = {}; patientInfo.value = {};
visitType.value = ''; // 重置初复诊标识
visitTypeDisabled.value = false; // 重置禁用状态
getPatientList(); getPatientList();
} }
}); });
@@ -434,7 +479,6 @@ function handleTimeChange(value) {
// 接诊回调 // 接诊回调
function handleReceive(row) { function handleReceive(row) {
// patientInfo.value = row;
handleCardClick(row); handleCardClick(row);
currentEncounterId.value = row.encounterId; currentEncounterId.value = row.encounterId;
drawer.value = false; drawer.value = false;
@@ -445,6 +489,30 @@ function handleReceive(row) {
function openDrawer() { function openDrawer() {
drawer.value = true; drawer.value = true;
} }
function handleCancelEncounter(){
proxy.$modal.confirm('您确定要取消病人本次的就诊记录吗?','提示信息',{
confirmButtonText: '是(Y)',
cancelButtonText: '否(N)',
type: 'warning'
}).then(() => {
//调用取消接诊API
cancelEncounter(patientInfo.value.encounterId).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('取消接诊成功');
patientInfo.value = {};
getPatientList();
}
}).catch((error) => {
proxy.$modal.confirm('该病人本次就诊已经有业务产生,不能取消接诊!','提示信息',{
confirmButtonText: '是(Y)',
type: 'warning'
});
});
}).catch(() => {
// 用户取消操作
});
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -0,0 +1,26 @@
<template>
<div class="patient-record-query">
<h1>患者记录查询</h1>
<!-- 患者记录查询组件内容 -->
</div>
</template>
<script>
export default {
name: 'PatientRecordQuery',
data() {
return {
// 组件数据
}
},
methods: {
// 组件方法
}
}
</script>
<style scoped>
.patient-record-query {
padding: 20px;
}
</style>

View File

@@ -20,7 +20,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- <el-col :span="6"> <!-- <el-col :span="6">
<el-form-item label="活动标识" prop="tempFlag"> <el-form-item label="" prop="tempFlag">
<el-radio-group v-model="form.tempFlag" :disabled="isViewMode"> <el-radio-group v-model="form.tempFlag" :disabled="isViewMode">
<el-radio v-for="dict in patient_temp_flag" :key="dict.value" :label="dict.value"> <el-radio v-for="dict in patient_temp_flag" :key="dict.value" :label="dict.value">
{{ dict.label }} {{ dict.label }}

View File

@@ -1,106 +1,457 @@
<template> <template>
<div class="container"> <div class="dashboard-container">
<div class="awaitingBtn"> <!-- 顶部导航栏 -->
<el-button @click="awaitingMedicineBtn">效期预警 <span>{{total}}</span></el-button> <header class="header">
<div class="logo">
<span class="logo-text">医院信息管理系统</span>
</div>
<div class="header-right">
<div class="notification-badge">
<i class="header-icon">🔔</i>
<span class="badge">2</span>
</div>
<i class="header-icon"></i>
<div class="user-info">
<span class="user-avatar">👤</span>
<span>用户</span>
</div>
</div>
</header>
<!-- 主体内容 -->
<div class="main-content">
<!-- 左侧导航栏 -->
<nav class="sidebar">
<div class="menu-item active">
<span class="menu-icon">📊</span>
<span>仪表盘</span>
</div>
<div class="menu-item">
<span class="menu-icon">👥</span>
<span>患者管理</span>
</div>
<div class="menu-item">
<span class="menu-icon">💉</span>
<span>预约管理</span>
</div>
<div class="menu-item">
<span class="menu-icon">🏥</span>
<span>门诊管理</span>
</div>
<div class="menu-item">
<span class="menu-icon"></span>
<span>住院管理</span>
</div>
<div class="menu-item">
<span class="menu-icon">💊</span>
<span>药房管理</span>
</div>
<div class="menu-item">
<span class="menu-icon">📋</span>
<span>报表统计</span>
</div>
<div class="menu-item">
<span class="menu-icon"></span>
<span>系统设置</span>
</div>
</nav>
<!-- 主内容区 -->
<div class="content-wrapper">
<div class="awaitingBtn">
<el-button @click="awaitingMedicineBtn">效期预警 <span>{{ total }}</span></el-button>
</div>
<h1 class="section-title">仪表盘</h1>
<!-- 关键指标卡片 -->
<div class="dashboard-grid">
<div class="card">
<div class="card-title">今日预约量</div>
<div class="card-value">126</div>
<div class="card-stats">
<span class="stats-icon"></span>
<span>12% 较昨日</span>
</div>
</div>
<div class="card">
<div class="card-title">门诊量统计</div>
<div class="card-value">342</div>
<div class="card-stats">
<span class="stats-icon"></span>
<span>8% 较上周</span>
</div>
</div>
<div class="card">
<div class="card-title">住院患者数</div>
<div class="card-value">78</div>
<div class="card-stats down">
<span class="stats-icon"></span>
<span>5% 较上周</span>
</div>
</div>
<div class="card">
<div class="card-title">药品库存预警</div>
<div class="card-value">8</div>
<div class="card-stats">
<span class="stats-icon">!</span>
<span>需要立即处理</span>
</div>
</div>
</div>
<!-- 图表展示区 -->
<div class="chart-container">
<canvas id="statsChart"></canvas>
</div>
<!-- 重要通知和待办事项 -->
<div class="bottom-content">
<div class="notification-container">
<div class="notification-title">重要通知</div>
<ul class="notification-list">
<li class="notification-item">系统将于周六凌晨2:00进行维护升级</li>
<li class="notification-item">新的门诊排班系统已上线请尽快熟悉</li>
<li class="notification-item">三季度医疗质量分析报告已发布</li>
</ul>
</div>
<div class="todo-container">
<div class="todo-title">待办事项</div>
<ul class="todo-list">
<li class="todo-item">
<span>未完成预约</span>
<span class="todo-count">12</span>
</li>
<li class="todo-item">
<span>待处理检查结果</span>
<span class="todo-count">7</span>
</li>
<li class="todo-item">
<span>待开处方</span>
<span class="todo-count">3</span>
</li>
</ul>
<button class="btn btn-primary">查看详情</button>
</div>
</div>
<!-- 患者列表 -->
<h2 class="section-title">最近患者</h2>
<div class="table-container">
<table>
<thead>
<tr>
<th>患者姓名</th>
<th>病历号</th>
<th>年龄</th>
<th>最后就诊时间</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>张明</td>
<td>HN2023001</td>
<td>45</td>
<td>2023-10-20 09:30</td>
<td><span style="color: #67c23a;">已完成</span></td>
<td>
<button class="btn btn-outline">详情</button>
</td>
</tr>
<tr>
<td>李红</td>
<td>HN2023002</td>
<td>32</td>
<td>2023-10-20 10:15</td>
<td><span style="color: #ff9800;">待检查</span></td>
<td>
<button class="btn btn-outline">详情</button>
</td>
</tr>
<tr>
<td>王建军</td>
<td>HN2023003</td>
<td>68</td>
<td>2023-10-19 14:20</td>
<td><span style="color: #d45d79;">住院中</span></td>
<td>
<button class="btn btn-outline">详情</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div> </div>
<!-- <div class="logo"> -->
<!-- <img src="/src/assets/images/jlau.jpg" /> -->
<!-- </div> -->
</div> </div>
</template> </template>
<script setup name="Index"> <script setup name="Index">
import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import Chart from 'chart.js/auto';
import { import {
getExpirationWarning, getExpirationWarning,
} from "./medicationmanagement/statisticalManagement/statisticalManagent"; } from "./medicationmanagement/statisticalManagement/statisticalManagent";
const router = useRouter(); const router = useRouter();
const version = ref('3.8.7'); const total = ref(0);
const total = ref(0)
function awaitingMedicineBtn(){ function awaitingMedicineBtn() {
router.push({ path: '/medicationmanagement/statisticalManagement/earlyWarning' }) router.push({ path: '/medicationmanagement/statisticalManagement/earlyWarning' });
} }
function goTarget(url) {
window.open(url, '__blank'); function getExpirationWarningCount() {
} getExpirationWarning({ pageNo: 1, pageSize: 10 }).then((res) => {
function getExpirationWarningCount(){ total.value = res.data.total || 0;
getExpirationWarning({pageNo: 1,pageSize: 10}).then((res) => { }).catch((err) => {
total.value = res.data.total||0 console.error('获取效期预警数量失败:', err);
total.value = 0;
}); });
} }
getExpirationWarningCount()
let chartInstance = null;
onMounted(() => {
getExpirationWarningCount();
const ctx = document.getElementById('statsChart');
if (!ctx) return;
// 销毁已有实例
if (chartInstance) {
chartInstance.destroy();
}
// 模拟数据近7天门诊量与预约量
const labels = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
const outpatientData = [310, 335, 305, 345, 370, 270, 210]; // 门诊量(柱状图)
const appointmentData = [100, 120, 110, 130, 140, 90, 70]; // 预约量(折线图)
chartInstance = new Chart(ctx.getContext('2d'), {
type: 'bar',
data: {
labels: labels,
datasets: [
{
label: '门诊量',
data: outpatientData,
backgroundColor: '#91c8e5',
borderColor: '#66a8cc',
borderWidth: 1,
borderRadius: 4,
borderSkipped: false,
},
{
label: '预约量',
data: appointmentData,
type: 'line',
borderColor: '#f56c6c',
backgroundColor: 'rgba(245, 108, 108, 0.1)',
borderWidth: 2,
pointBackgroundColor: '#f56c6c',
pointBorderColor: '#fff',
pointBorderWidth: 2,
pointRadius: 4,
fill: true,
tension: 0.3,
lineTension: 0.3,
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top',
labels: {
font: {
size: 12,
weight: 'bold'
}
}
},
tooltip: {
mode: 'index',
intersect: false,
backgroundColor: 'rgba(0, 0, 0, 0.7)',
titleFont: {
size: 13
},
bodyFont: {
size: 12
}
}
},
scales: {
x: {
grid: {
display: false
},
ticks: {
font: {
size: 11
}
}
},
y: {
beginAtZero: true,
max: 400,
stepSize: 50,
grid: {
color: 'rgba(0, 0, 0, 0.05)'
},
ticks: {
font: {
size: 11
}
}
}
}
}
});
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.home { .dashboard-container {
blockquote {
padding: 10px 20px;
margin: 0 0 20px;
font-size: 17.5px;
border-left: 5px solid #eee;
}
hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eee;
}
.col-item {
margin-bottom: 20px;
}
ul {
padding: 0;
margin: 0;
}
font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 13px;
color: #676a6c;
overflow-x: hidden;
ul {
list-style-type: none;
}
h4 {
margin-top: 0px;
}
h2 {
margin-top: 10px;
font-size: 26px;
font-weight: 100;
}
p {
margin-top: 10px;
b {
font-weight: 700;
}
}
.update-log {
ol {
display: block;
list-style-type: decimal;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0;
margin-inline-end: 0;
padding-inline-start: 40px;
}
}
}
.container {
display: flex; display: flex;
justify-content: center; /* 水平居中 */ flex-direction: column;
align-items: center; /* 垂直居中 */ height: 100vh;
height: 70vh; /* 确保容器高度至少为视口高度 */ background-color: #f5f7fa;
width: 100%; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
padding: 0;
} }
.awaitingBtn{
.header {
height: 60px;
background-color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
position: sticky;
top: 0;
z-index: 1000;
}
.logo {
display: flex;
align-items: center;
}
.logo-text {
font-size: 18px;
font-weight: 600;
color: #166773;
}
.header-right {
display: flex;
align-items: center;
gap: 16px;
}
.notification-badge {
position: relative;
cursor: pointer;
}
.header-icon {
font-size: 20px;
cursor: pointer;
padding: 8px;
border-radius: 4px;
transition: background-color 0.3s;
}
.header-icon:hover {
background-color: #f5f7fa;
}
.badge {
position: absolute;
top: 0;
right: 0;
background-color: #ff4d4f;
color: white;
font-size: 12px;
padding: 2px 6px;
border-radius: 10px;
min-width: 16px;
text-align: center;
}
.user-info {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
.user-info:hover {
background-color: #f5f7fa;
}
.user-avatar {
font-size: 20px;
}
.main-content {
display: flex;
flex: 1;
overflow: hidden;
}
.sidebar {
width: 200px;
background-color: white;
box-shadow: 2px 0 4px rgba(0, 0, 0, 0.1);
overflow-y: auto;
}
.menu-item {
display: flex;
align-items: center;
padding: 16px 24px;
cursor: pointer;
transition: all 0.3s;
border-left: 3px solid transparent;
}
.menu-item:hover {
background-color: #f0f9ff;
}
.menu-item.active {
background-color: #e6f7ff;
border-left-color: #1890ff;
color: #1890ff;
font-weight: 500;
}
.menu-icon {
margin-right: 12px;
font-size: 18px;
}
.content-wrapper {
flex: 1;
padding: 20px;
overflow-y: auto;
background-color: #f5f7fa;
}
.awaitingBtn {
margin-bottom: 20px;
.el-button{ .el-button{
border: 1px #166773 solid; border: 1px #166773 solid;
span{ span{
@@ -108,6 +459,7 @@ getExpirationWarningCount()
margin-left: 5px; margin-left: 5px;
} }
} }
.el-button:hover{ .el-button:hover{
border: 1px #166773 solid; border: 1px #166773 solid;
color: #166773; color: #166773;
@@ -116,15 +468,204 @@ getExpirationWarningCount()
margin-left: 5px; margin-left: 5px;
} }
} }
position: absolute;
top:20px;
left: 20px;
} }
.logo {
width: 1000px;
margin: 0 auto;
background: url(../assets/images/logo_bg.jpg) no-repeat right bottom;
overflow: hidden;
}
</style>
.section-title {
font-size: 24px;
margin-bottom: 20px;
color: #333;
font-weight: 600;
}
.card {
background-color: #fff;
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
}
.card:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}
.card-title {
font-size: 14px;
color: #606266;
margin-bottom: 12px;
}
.card-value {
font-size: 32px;
font-weight: 600;
color: #303133;
margin-bottom: 8px;
}
.card-stats {
display: flex;
align-items: center;
font-size: 12px;
color: #67c23a;
}
.card-stats.down {
color: #f56c6c;
}
.stats-icon {
margin-right: 4px;
}
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.chart-container {
background-color: #fff;
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
margin-bottom: 30px;
height: 300px;
canvas {
width: 100% !important;
height: 100% !important;
}
}
.bottom-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 30px;
}
.notification-container,
.todo-container {
background-color: #fff;
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.notification-title,
.todo-title {
font-size: 18px;
font-weight: 600;
color: #303133;
margin-bottom: 16px;
}
.notification-list {
list-style: none;
padding: 0;
margin: 0;
}
.notification-item {
padding: 12px 0;
border-bottom: 1px solid #ebeef5;
&:last-child {
border-bottom: none;
}
}
.todo-list {
list-style: none;
padding: 0;
margin: 0;
margin-bottom: 20px;
}
.todo-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #ebeef5;
&:last-child {
border-bottom: none;
}
}
.todo-count {
color: #606266;
font-size: 14px;
}
.table-container {
background-color: #fff;
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ebeef5;
}
th {
background-color: #f5f7fa;
font-weight: 600;
color: #303133;
}
tr:last-child td {
border-bottom: none;
}
.btn {
padding: 6px 12px;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
border: none;
outline: none;
}
.btn-primary {
background-color: #1890ff;
color: #fff;
}
.btn-primary:hover {
background-color: #40a9ff;
}
.btn-outline {
background-color: transparent;
border: 1px solid #dcdfe6;
color: #606266;
}
.btn-outline:hover {
border-color: #c6e2ff;
color: #40a9ff;
background-color: #ecf5ff;
}
@media (max-width: 768px) {
.bottom-content {
grid-template-columns: 1fr;
}
.dashboard-grid {
grid-template-columns: 1fr;
}
}
</style>

View File

@@ -93,6 +93,18 @@ export function delEncounterDiagnosis(conditionId) {
}) })
} }
/**
* 是否是食源性诊断
*/
export function isFoodDiseasesNew(params) {
return request({
url: '/external-integration/foodborne-acquisition/is-food-diseases-new',
method: 'get',
params: params
})
}
/** /**
* 获取中医诊断 * 获取中医诊断
*/ */
@@ -230,6 +242,15 @@ export function getOrgTree() {
}) })
} }
export function getEmrDetail(encounterId) {
return request({
url: '/doctor-station/emr/emr-detail?encounterId=' + encounterId,
method: 'get',
})
}
/** /**
* 组合/拆组 * 组合/拆组
*/ */

View File

@@ -2,19 +2,17 @@
<div> <div>
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="4" :xs="24"> <el-col :span="4" :xs="24">
<div style="height: 44px; display: flex; align-items: center; flex: none"> <el-input
<el-input v-model="diagnosis"
v-model="diagnosis" placeholder="诊断名称"
placeholder="诊断名称" clearable
clearable style="width: 100%; margin-bottom: 10px"
style="width: 100%" @keyup.enter="queryDiagnosisUse"
@keyup.enter="queryDiagnosisUse" >
> <template #append>
<template #append> <el-button icon="Search" @click="queryDiagnosisUse" />
<el-button icon="Search" @click="queryDiagnosisUse" /> </template>
</template> </el-input>
</el-input>
</div>
<el-tree <el-tree
ref="treeRef" ref="treeRef"
:data="tree" :data="tree"
@@ -68,16 +66,21 @@
</el-tree> </el-tree>
</el-col> </el-col>
<el-col :span="20" :xs="24"> <el-col :span="20" :xs="24">
<div style="height: 44px; display: flex; align-items: center; flex: none"> <div style="margin-bottom: 10px">
<el-button type="primary" plain @click="handleAddDiagnosis()"> 新增诊断 </el-button> <el-button type="primary" plain @click="handleAddDiagnosis()"> 新增诊断 </el-button>
<el-button type="primary" plain @click="handleSaveDiagnosis()"> 保存诊断 </el-button> <el-button type="primary" plain @click="handleSaveDiagnosis()"> 保存诊断 </el-button>
<el-button type="primary" plain @click="handleAddTcmDiagonsis()"> 中医诊断 </el-button> <el-button type="primary" plain @click="handleAddTcmDiagonsis()"> 中医诊断 </el-button>
<el-button type="primary" plain @click="handleImport()"> 导入慢性病诊断 </el-button> <el-button type="primary" plain @click="handleImport()"> 导入慢性病诊断 </el-button>
</div> </div>
<el-form :model="form" :rules="rules" ref="formRef"> <el-form :model="form" :rules="rules" ref="formRef">
<el-table ref="diagnosisTableRef" :data="form.diagnosisList" height="650"> <el-table ref="diagnosisTableRef" :data="form.diagnosisList" height="650">
<el-table-column label="序号" type="index" width="50" /> <el-table-column label="序号" width="50" >
<el-table-column label="诊断排序" align="center" prop="diagSrtNo" width="180"> <template #default="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column label="诊断排序" align="center" prop="diagSrtNo" width="120">
<template #default="scope"> <template #default="scope">
<el-form-item <el-form-item
:prop="`diagnosisList.${scope.$index}.diagSrtNo`" :prop="`diagnosisList.${scope.$index}.diagSrtNo`"
@@ -136,9 +139,13 @@
</el-form-item> </el-form-item>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="医保码" align="center" prop="ybNo" width="180" />
<el-table-column label="诊断类型" align="center" prop="maindiseFlag"> <el-table-column label="诊断医生" align="center" prop="diagnosisDoctor" width="120" />
<el-table-column label="诊断时间" align="center" prop="diagnosisTime" width="150" />
<el-table-column label="诊断代码" align="center" prop="ybNo" width="180" />
<el-table-column label="诊断类型" align="center" prop="maindiseFlag" width="120">
<template #default="scope"> <template #default="scope">
<div style="display:flex;flex-direction:column;align-items:center;gap:5px;">
<el-checkbox <el-checkbox
label="主诊断" label="主诊断"
:trueLabel="1" :trueLabel="1"
@@ -151,7 +158,7 @@
<el-select <el-select
v-model="scope.row.verificationStatusEnum" v-model="scope.row.verificationStatusEnum"
placeholder=" " placeholder=" "
style="width: 40%; padding-bottom: 5px; padding-left: 10px" style="width: 100%; padding-bottom: 5px; padding-left: 10px"
size="small" size="small"
> >
<el-option <el-option
@@ -161,6 +168,7 @@
:value="item.value" :value="item.value"
/> />
</el-select> </el-select>
</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" width="130"> <el-table-column label="操作" align="center" width="130">
@@ -185,28 +193,30 @@
/> />
<AddDiagnosisDialog <AddDiagnosisDialog
:openAddDiagnosisDialog="openAddDiagnosisDialog" :openAddDiagnosisDialog="openAddDiagnosisDialog"
:patientInfo="patientInfo" :patientInfo="props.patientInfo"
@close="closeDiagnosisDialog" @close="closeDiagnosisDialog"
/> />
</div> </div>
</template> </template>
<script setup> <script setup>
import { getCurrentInstance } from 'vue'; import { getCurrentInstance, nextTick } from 'vue'; // 添加 nextTick 导入
import useUserStore from '@/store/modules/user';
import { import {
getConditionDefinitionInfo, getConditionDefinitionInfo,
saveDiagnosis, saveDiagnosis,
diagnosisInit, diagnosisInit,
deleteDiagnosisBind, deleteDiagnosisBind,
getEncounterDiagnosis, getEncounterDiagnosis,
getEmrDetail,
getChronicDisease, getChronicDisease,
getTcmDiagnosis, getTcmDiagnosis,
delEncounterDiagnosis, delEncounterDiagnosis,
isFoodDiseasesNew,
} from '../api'; } from '../api';
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';
import { patientInfo } from '../../store/patient.js';
// const diagnosisList = ref([]); // const diagnosisList = ref([]);
const allowAdd = ref(false); const allowAdd = ref(false);
const tree = ref([]); const tree = ref([]);
@@ -228,50 +238,93 @@ const props = defineProps({
}); });
const emits = defineEmits(['diagnosisSave']); const emits = defineEmits(['diagnosisSave']);
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const userStore = useUserStore();
const { med_type } = proxy.useDict('med_type'); const { med_type } = proxy.useDict('med_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 isSaving = ref(false);
watch( watch(
() => form.value.diagnosisList, () => form.value.diagnosisList,
() => { () => {
emits('diagnosisSave', false); // 如果正在保存,则不触发更新事件
if (!isSaving.value) {
emits('diagnosisSave', false);
}
}, },
{ deep: true } { deep: true }
); );
// function getDetail(encounterId) { function getDetail(encounterId) {
// getEmrDetail(encounterId).then((res) => { if (!encounterId) {
// allowAdd.value = res.data ? true : false; console.warn('未提供有效的就诊ID无法获取病历详情');
// }); return;
// } }
getEmrDetail(encounterId).then((res) => {
allowAdd.value = res.data ? true : false;
});
}
function getList() { function getList() {
getEncounterDiagnosis(patientInfo.value.encounterId).then((res) => { if (!props.patientInfo || !props.patientInfo.encounterId) {
console.warn('患者就诊信息不完整,无法获取诊断数据');
return;
}
getEncounterDiagnosis(props.patientInfo.encounterId).then((res) => {
if (res.code == 200) { if (res.code == 200) {
form.value.diagnosisList = res.data; // 为每个诊断项添加默认的诊断医生和时间(如果不存在)
const diagnosisList = res.data.map(item => ({
...item,
diagnosisDoctor: item.diagnosisDoctor || props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: item.diagnosisTime || new Date().toLocaleString('zh-CN')
}));
// 对获取的数据进行排序
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;
});
form.value.diagnosisList = diagnosisList;
emits('diagnosisSave', false); emits('diagnosisSave', false);
console.log(form.value.diagnosisList); console.log(form.value.diagnosisList);
} }
}); });
getTcmDiagnosis({ encounterId: patientInfo.value.encounterId }).then((res) => {
getTcmDiagnosis({ encounterId: props.patientInfo.encounterId }).then((res) => {
if (res.code == 200) { if (res.code == 200) {
if (res.data.illness.length > 0) { if (res.data.illness && res.data.illness.length > 0 && res.data.symptom) {
const newList = [];
res.data.illness.forEach((item, index) => { res.data.illness.forEach((item, index) => {
form.value.diagnosisList.push({ newList.push({
name: item.name + '-' + res.data.symptom[index].name, name: item.name + '-' + (res.data.symptom[index]?.name || ''),
ybNo: item.ybNo, ybNo: item.ybNo,
medTypeCode: item.medTypeCode, medTypeCode: item.medTypeCode,
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: new Date().toLocaleString('zh-CN')
}); });
}); });
// 将新数据添加到现有列表中
form.value.diagnosisList.push(...newList);
// 重新排序整个列表
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;
});
} }
emits('diagnosisSave', false); emits('diagnosisSave', false);
console.log(form.value.diagnosisList); console.log(form.value.diagnosisList);
} }
}); });
getTree(); getTree();
} }
@@ -285,19 +338,28 @@ function init() {
} }
function handleImport() { function handleImport() {
if (patientInfo.value.contractName != '自费') { if (!props.patientInfo || !props.patientInfo.encounterId) {
console.warn('患者就诊信息不完整,无法导入慢性病信息');
return;
}
if (props.patientInfo.contractName != '自费') {
// 获取患者慢性病信息 // 获取患者慢性病信息
getChronicDisease({ encounterId: patientInfo.value.encounterId }).then((res) => { getChronicDisease({ encounterId: props.patientInfo.encounterId }).then((res) => {
if (res.data.length > 0) { if (res.data && res.data.length > 0) {
res.data.forEach((item, index) => { res.data.forEach((item, index) => {
form.value.diagnosisList.push({ form.value.diagnosisList.push({
...item, ...item,
...{ ...{
medTypeCode: '140104', medTypeCode: '140104',
verificationStatusEnum: 4, verificationStatusEnum: 4,
definitionId: item.id, definitionId: item.id,
diagSrtNo: form.value.diagnosisList.length + 1, diagSrtNo: form.value.diagnosisList.length + 1,
}, iptDiseTypeCode: 2,
diagnosisDesc: '',
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: new Date().toLocaleString('zh-CN')
},
}); });
}); });
} }
@@ -340,7 +402,8 @@ const filterNode = (value, data) => {
* 获取诊断树列表 * 获取诊断树列表
*/ */
function getTree() { function getTree() {
getConditionDefinitionInfo(patientInfo.value ? patientInfo.value.patientId : '').then((res) => { const patientId = props.patientInfo?.patientId || '';
getConditionDefinitionInfo(patientId).then((res) => {
if (res.code == 200) { if (res.code == 200) {
let list = []; let list = [];
list = res.data.patientHistoryList; list = res.data.patientHistoryList;
@@ -377,14 +440,20 @@ function getTree() {
function handleAddDiagnosis() { function handleAddDiagnosis() {
proxy.$refs.formRef.validate((valid) => { proxy.$refs.formRef.validate((valid) => {
if (valid) { if (valid) {
if (!allowAdd.value) {
proxy.$modal.msgWarning('请先填写病历');
return;
}
form.value.diagnosisList.push({ form.value.diagnosisList.push({
showPopover: false, showPopover: false,
name: undefined, name: undefined,
verificationStatusEnum: 4, verificationStatusEnum: 4,
medTypeCode: '21', medTypeCode: '11',
diagSrtNo: form.value.diagnosisList.length + 1, diagSrtNo: form.value.diagnosisList.length + 1,
iptDiseTypeCode: 31, iptDiseTypeCode: 2,
diagnosisDesc: '', diagnosisDesc: '',
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: new Date().toLocaleString('zh-CN')
}); });
if (form.value.diagnosisList.length == 1) { if (form.value.diagnosisList.length == 1) {
form.value.diagnosisList[0].maindiseFlag = 1; form.value.diagnosisList[0].maindiseFlag = 1;
@@ -398,13 +467,31 @@ function handleAddTcmDiagonsis() {
openAddDiagnosisDialog.value = true; openAddDiagnosisDialog.value = true;
} }
/**
* 删除诊断
*/
/** /**
* 删除诊断 * 删除诊断
*/ */
function handleDeleteDiagnosis(row, index) { function handleDeleteDiagnosis(row, index) {
if (row.conditionId) { if (row.conditionId) {
delEncounterDiagnosis(row.conditionId).then(() => { delEncounterDiagnosis(row.conditionId).then(() => {
getList(); // 不要立即调用getList(),而是从当前列表中移除
form.value.diagnosisList.splice(index, 1);
// 重新排序剩余的项目
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, idx) => {
item.diagSrtNo = idx + 1;
});
// 更新列表
form.value.diagnosisList = sortedList.map(item => ({ ...item }));
getTree(); getTree();
}); });
} else { } else {
@@ -428,35 +515,72 @@ function handleMaindise(value, index) {
} }
} }
/**
* 保存诊断
*/
/**
* 保存诊断
*/
/**
* 保存诊断
*/
/** /**
* 保存诊断 * 保存诊断
*/ */
function handleSaveDiagnosis() { function handleSaveDiagnosis() {
proxy.$refs.formRef.validate((valid) => { proxy.$refs.formRef.validate((valid) => {
if (valid) { if (valid) {
if (form.value.diagnosisList.length == 0) { if (form.value.diagnosisList.length === 0) {
proxy.$modal.msgWarning('诊断不能为空'); proxy.$modal.msgWarning('诊断不能为空');
return false; return;
} else if (!form.value.diagnosisList.some((diagnosis) => diagnosis.maindiseFlag === 1)) { } else if (!form.value.diagnosisList.some((diagnosis) => diagnosis.maindiseFlag === 1)) {
proxy.$modal.msgWarning('至少添加一条主诊断'); proxy.$modal.msgWarning('至少添加一条主诊断');
} else { return;
saveDiagnosis({
patientId: patientInfo.value.patientId,
encounterId: patientInfo.value.encounterId,
diagnosisChildList: form.value.diagnosisList,
}).then((res) => {
if (res.code == 200) {
getTree();
getList();
emits('diagnosisSave', false);
proxy.$modal.msgSuccess('诊断已保存');
}
});
} }
// 设置保存标志避免触发watch监听器
isSaving.value = true;
// 步骤1深拷贝并按 diagSrtNo 排序
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;
});
// 步骤2重新分配连续的序号从1开始
sortedList.forEach((item, index) => {
item.diagSrtNo = index + 1; // 这里是关键!把“诊断排序”改成新顺序
});
// 步骤3提交排序后的数据
saveDiagnosis({
patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId,
diagnosisChildList: sortedList,
}).then((res) => {
if (res.code === 200) {
// 步骤4更新本地数据使用全新对象防止响应式问题
form.value.diagnosisList = sortedList.map(item => ({ ...item }));
emits('diagnosisSave', false);
proxy.$modal.msgSuccess('诊断已保存');
// 食源性疾病逻辑
isFoodDiseasesNew({ encounterId: props.patientInfo.encounterId }).then((res2) => {
if (res2.code === 20 && res2.data) {
window.open(res2.data, '_blank');
}
});
}
}).finally(() => {
setTimeout(() => {
isSaving.value = false;
}, 100);
});
} }
}); });
} }
/** /**
* 关闭诊断弹窗 * 关闭诊断弹窗
*/ */
@@ -519,12 +643,15 @@ function handleNodeClick(data) {
medTypeCode: '11', medTypeCode: '11',
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,
diagnosisTime: new Date().toLocaleString('zh-CN')
}); });
if (form.value.diagnosisList.length == 1) { if (form.value.diagnosisList.length == 1) {
form.value.diagnosisList[0].maindiseFlag = 1; form.value.diagnosisList[0].maindiseFlag = 1;
} }
} }
defineExpose({ getList, handleSaveDiagnosis });
defineExpose({ getList, getDetail, handleSaveDiagnosis });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -542,4 +669,5 @@ defineExpose({ getList, handleSaveDiagnosis });
display: flex; display: flex;
align-items: center; align-items: center;
} }
</style> </style>

View File

@@ -55,10 +55,10 @@
</template> </template>
<script setup> <script setup>
import { getCurrentInstance, onBeforeMount, onMounted, reactive, ref, computed } from 'vue'; import { getCurrentInstance, onBeforeMount, onMounted, reactive, ref, computed } from 'vue';
import BloodTransfusion from './bloodTransfusion.vue'; import BloodTransfusion from './bloodTransfusion';
import { patientInfo } from '../../../store/patient.js'; import { patientInfo } from '../../../store/patient.js';
import Surgery from './surgery.vue'; import Surgery from './surgery.vue';
import LaboratoryTests from './LaboratoryTests.vue'; import laboratoryTests from './laboratoryTests.vue';
import MedicalExaminations from './medicalExaminations.vue'; import MedicalExaminations from './medicalExaminations.vue';
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const emits = defineEmits([]); const emits = defineEmits([]);

View File

@@ -1,12 +1,14 @@
<template> <template>
<div class="login"> <div class="login">
<!-- 顶部 --> <!-- 顶部 -->
<!-- <div class="el-login-top">
<el-image :src="logoNew"></el-image>
</div> -->
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form"> <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
<h1 class="title">医院信息管理系统</h1> <div class="el-login-top">
<el-image :src="logoNew"></el-image>
</div>
<h1 class="title">{{ currentTenantName || settings.systemName }}信息管理系统</h1>
<p class="login-subtitle">请使用您的账号密码安全登录系统</p>
<el-form-item prop="username"> <el-form-item prop="username">
<p class="label">用户名</p>
<el-input <el-input
v-model="loginForm.username" v-model="loginForm.username"
type="text" type="text"
@@ -21,25 +23,35 @@
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="password"> <el-form-item prop="password">
<p class="label">密码</p>
<el-input <el-input
v-model="loginForm.password" v-model="loginForm.password"
type="password" :type="passwordVisible ? 'text' : 'password'"
size="large" size="large"
auto-complete="off" auto-complete="off"
placeholder="密码" placeholder="密码"
@keyup.enter="handleLogin" @keyup.enter="handleLogin"
show-password
> >
<template #prefix <template #prefix
><svg-icon icon-class="password" class="el-input__icon input-icon" ><svg-icon icon-class="password" class="el-input__icon input-icon"
/></template> /></template>
<template #suffix>
<span
class="password-toggle"
@click="togglePasswordVisibility"
style="cursor: pointer; color: #606266; font-size: 14px; padding: 0 10px;"
>
{{ passwordVisible ? '隐藏' : '显示' }}
</span>
</template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="tenantId"> <el-form-item prop="tenantId">
<p class="label">医疗机构</p>
<el-select <el-select
v-model="loginForm.tenantId" v-model="loginForm.tenantId"
size="large" size="large"
placeholder="所属医院" placeholder="请选择医疗机构"
clearable clearable
filterable filterable
> >
@@ -51,6 +63,11 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item class="login-options">
<el-checkbox v-model="loginForm.rememberMe" class="remember-me">记住我</el-checkbox>
<el-link type="primary" class="forgot-password" @click="handleForgotPassword">忘记密码</el-link>
</el-form-item>
<!-- <el-form-item prop="tenantId"> --> <!-- <el-form-item prop="tenantId"> -->
<!-- <span class="descriptions-item-label" style="margin: 0 10px 0 0">连接医保</span> --> <!-- <span class="descriptions-item-label" style="margin: 0 10px 0 0">连接医保</span> -->
<!-- <el-switch v-model="loginForm.invokeYb" @change="topNavChange" size="large"/> --> <!-- <el-switch v-model="loginForm.invokeYb" @change="topNavChange" size="large"/> -->
@@ -87,8 +104,17 @@
<router-link class="link-type" :to="'/register'">立即注册</router-link> <router-link class="link-type" :to="'/register'">立即注册</router-link>
</div> </div>
</el-form-item> </el-form-item>
<div class="footer">
© 2025 {{ currentTenantName || settings.systemName }}信息管理系统 | 版本 v2.5.1
<!-- 公司版权信息新增 -->
<div class="company-copyright">
技术支持上海经创贺联信息技术有限公司
</div>
</div>
</el-form> </el-form>
<!-- 底部 --> <!-- 底部 -->
<div class="el-login-footer"> <div class="el-login-footer">
<div class="el-login-footer-link"> <div class="el-login-footer-link">
<span><el-link :underline="false">his账号用户协议</el-link></span> <span><el-link :underline="false">his账号用户协议</el-link></span>
@@ -109,12 +135,15 @@
</template> </template>
<script setup> <script setup>
import { getCurrentInstance, ref, reactive, computed, onMounted, watch } from 'vue';
import settings from '@/settings';
import { getCodeImg, sign, getUserBindTenantList } from '@/api/login'; import { getCodeImg, sign, getUserBindTenantList } from '@/api/login';
import { invokeYbPlugin } from '@/api/public'; import { invokeYbPlugin } from '@/api/public';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { encrypt, decrypt } from '@/utils/jsencrypt'; import { encrypt, decrypt } from '@/utils/jsencrypt';
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
import logoNew from '@/assets/logo/logoBlack.png'; import { ElMessage } from 'element-plus';
import logoNew from '@/assets/logo/LOGO.jpg';
const userStore = useUserStore(); const userStore = useUserStore();
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@@ -131,6 +160,7 @@ const loginForm = ref({
}); });
const tenantOptions = ref([]); const tenantOptions = ref([]);
const currentTenantName = ref('');
const loginRules = { const loginRules = {
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }], username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
@@ -146,6 +176,21 @@ const captchaEnabled = ref(true);
// 注册开关 // 注册开关
const register = ref(false); const register = ref(false);
const redirect = ref(undefined); const redirect = ref(undefined);
const passwordVisible = ref(false);
function togglePasswordVisibility() {
passwordVisible.value = !passwordVisible.value;
}
// 处理忘记密码功能
function handleForgotPassword() {
// 这里可以添加忘记密码的逻辑,例如跳转到忘记密码页面或显示忘记密码弹窗
// 目前先显示一个提示
ElMessage({
message: '忘记密码功能正在开发中',
type: 'info'
});
}
watch( watch(
route, route,
@@ -155,7 +200,7 @@ watch(
{ immediate: true } { immediate: true }
); );
// 页面加载时从 localStorage 获取 invokeYb 的值 // 页面加载时从 localStorage 获取 invokeYb 的值和从 Cookies 获取记住的登录信息
onMounted(() => { onMounted(() => {
const storedInvokeYb = localStorage.getItem('invokeYb'); const storedInvokeYb = localStorage.getItem('invokeYb');
if (storedInvokeYb !== null) { if (storedInvokeYb !== null) {
@@ -164,6 +209,38 @@ onMounted(() => {
// 如果 localStorage 中没有值,则设置默认值并保存 // 如果 localStorage 中没有值,则设置默认值并保存
localStorage.setItem('invokeYb', 'true'); localStorage.setItem('invokeYb', 'true');
} }
// 从 Cookies 中恢复记住的登录信息
const rememberMe = Cookies.get('rememberMe');
if (rememberMe && rememberMe === 'true') {
const username = Cookies.get('username');
const password = Cookies.get('password');
if (username) {
loginForm.value.username = username;
loginForm.value.rememberMe = true;
// 解密密码
if (password) {
try {
loginForm.value.password = decrypt(password);
} catch (e) {
console.error('密码解密失败:', e);
}
}
}
}
// 获取医疗机构列表
getUserBindTenantList().then((res) => {
tenantOptions.value = res.data.map(item => ({
label: item.tenantName,
value: item.id
}));
// 如果只有一个医疗机构,自动选中
if (tenantOptions.value.length === 1) {
loginForm.value.tenantId = tenantOptions.value[0].value;
currentTenantName.value = tenantOptions.value[0].label;
}
});
}); });
function handleLogin() { function handleLogin() {
@@ -279,6 +356,14 @@ function getCode() {
}); });
} }
// 监听租户选择变化
watch(() => loginForm.value.tenantId, (newTenantId) => {
const selectedTenant = tenantOptions.value.find(item => item.value === newTenantId);
if (selectedTenant) {
currentTenantName.value = selectedTenant.label;
}
});
// 切换医保连接开关时更新 localStorage // 切换医保连接开关时更新 localStorage
function topNavChange(value) { function topNavChange(value) {
localStorage.setItem('invokeYb', value.toString()); localStorage.setItem('invokeYb', value.toString());
@@ -303,6 +388,7 @@ function getTenantList(username) {
label: item.tenantName, label: item.tenantName,
})); }));
loginForm.value.tenantId = tenantOptions.value[0].value; //默认选中第一个 loginForm.value.tenantId = tenantOptions.value[0].value; //默认选中第一个
currentTenantName.value = tenantOptions.value[0].label;
} }
} }
}); });
@@ -389,31 +475,85 @@ getCookie();
getTenantList(loginForm.value.username); getTenantList(loginForm.value.username);
</script> </script>
<style>
html, body {
height: auto;
min-height: 100vh;
overflow-y: auto;
margin: 0;
padding: 0;
}
</style>
<style lang="scss" scoped> <style lang="scss" scoped>
.login { .login {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; background: #f8f9fa;
height: 100%; min-height: 100vh;
padding-bottom: 100px; /* 为底部固定footer留出空间 */
//background-image: url("../assets/images/login-background.jpg"); //background-image: url("../assets/images/login-background.jpg");
background-size: cover; background-size: cover;
} }
.title { .title {
margin: 0px auto 30px auto; margin: 10px auto 15px auto;
text-align: center; text-align: center;
color: #000; color: #000;
font-family: 'Microsoft Yahei,STHeiti,Simsun,STSong,Helvetica Neue,Helvetica,Arial,sans-serif'; font-family: 'Microsoft Yahei,STHeiti,Simsun,STSong,Helvetica Neue,Helvetica,Arial,sans-serif';
} }
.label{
display: block;
margin-bottom: 6px;
font-size: 14px;
font-weight: 500;
color: var(--text);
}
.login-options {
display: flex;
justify-content: space-between;
align-items: center;
margin: 8px 0 10px 0;
width: 100%; /* 确保容器占满宽度 */
}
.remember-me {
margin: 0;
color: var(--text);
}
.forgot-password {
font-size: 14px;
color: var(--primary);
cursor: pointer;
text-align: right; /* 确保文本右对齐 */
margin-left: auto; /* 确保元素右对齐 */
}
.footer {
margin-top: 32px;
font-size: 12px;
color: var(--text-secondary);
}
.login-subtitle {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 20px;
}
.login-form { .login-form {
border-radius: 6px; border-radius: 16px;
box-shadow: 0 4px 12px var(--shadow);
border: 1px solid var(--border);
background: #ffffff; background: #ffffff;
width: 400px; width: 100%;
max-width: 400px;
padding: 40px;
padding: 25px 25px 5px 25px; padding: 25px 25px 5px 25px;
text-align: center;
.el-input { .el-input {
height: 50px; // 修改输入框高度 height: 50px; // 修改输入框高度
input { input {
height: 50px; // 修改输入框高度 height: 50px; // 修改输入框高度
font-size: 18px; // 修改输入框内文字大小 font-size: 18px; // 修改输入框内文字大小
} }
} }
@@ -438,24 +578,20 @@ getTenantList(loginForm.value.username);
} }
} }
.el-login-top { .el-login-top {
height: 80px; text-align: center;
line-height: 40px; margin: 0 auto 10px;
position: fixed; .el-image {
top: 0; max-width: 120px;
width: 100%; max-height: 120px;
text-align: left; }
color: #000;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
background-color: #f1f1f1;
padding: 20px;
} }
.el-login-footer { .el-login-footer {
height: 100px; height: 80px;
line-height: 40px; line-height: 30px;
position: fixed; position: fixed;
bottom: 0; bottom: 0;
left: 0;
right: 0;
width: 100%; width: 100%;
text-align: center; text-align: center;
color: #000; color: #000;
@@ -463,8 +599,7 @@ getTenantList(loginForm.value.username);
font-size: 12px; font-size: 12px;
letter-spacing: 1px; letter-spacing: 1px;
background-color: #f1f1f1; background-color: #f1f1f1;
z-index: 100; /* 确保footer在最上层 */
color: #000;
span { span {
margin: 0 10px; margin: 0 10px;
} }
@@ -490,6 +625,13 @@ getTenantList(loginForm.value.username);
width: 24px !important; // 调整图标的宽度 width: 24px !important; // 调整图标的宽度
height: 24px !important; // 调整图标的高度 height: 24px !important; // 调整图标的高度
font-size: 24px !important; // 调整图标的字体大小 font-size: 24px !important; // 调整图标的字体大小
color: #606266 !important; // 确保图标颜色可见
cursor: pointer !important; // 确保鼠标悬停时显示指针
}
:deep(.password-toggle) {
line-height: 50px !important; // 确保文字垂直居中
user-select: none; // 禁止选中文字
} }
:deep(.el-select__wrapper) { :deep(.el-select__wrapper) {
background-color: #f5f7fa !important; background-color: #f5f7fa !important;
@@ -498,4 +640,8 @@ getTenantList(loginForm.value.username);
font-size: 18px !important; font-size: 18px !important;
height: 50px !important; // 修改输入框高度 height: 50px !important; // 修改输入框高度
} }
:deep(.el-form-item) {
margin-bottom: 15px !important; // 减小输入框之间的距离
}
</style> </style>

View File

@@ -0,0 +1,271 @@
<template>
<div class="app-container">
<div class="config-header">
<el-button type="primary" @click="handleDefault">默认(M)</el-button>
<el-button type="success" @click="handleSave">保存(S)</el-button>
<el-button type="danger" @click="handleClose">关闭(X)</el-button>
</div>
<el-tabs v-model="activeTab" class="config-tabs">
<el-tab-pane label="划价收费1" name="tab1">
<div class="tab-content">划价收费1相关配置</div>
</el-tab-pane>
<el-tab-pane label="划价收费2" name="tab2">
<div class="tab-content">划价收费2相关配置</div>
</el-tab-pane>
<el-tab-pane label="挂号处理" name="tab3">
<el-form ref="formRef" :model="formData" label-width="120px" class="config-form">
<!-- 第一行 -->
<div class="form-row">
<el-form-item label="病历本费用(元)" prop="medicalRecordFee" style="margin-right: 20px;">
<el-input v-model="formData.medicalRecordFee" style="width: 180px" />
</el-form-item>
<el-form-item style="margin-left: 20px;">
<el-checkbox v-model="formData.medicalRecordFlag">病历费入账标志</el-checkbox>
</el-form-item>
</div>
<!-- 第二行 -->
<div class="form-row">
<el-form-item label="就诊卡费(元)" prop="patientCardFee" style="margin-right: 20px;">
<el-input v-model="formData.patientCardFee" style="width: 180px" />
</el-form-item>
<el-form-item style="margin-left: 20px;">
<el-checkbox v-model="formData.isNightShift">是否启用晚班</el-checkbox>
</el-form-item>
</div>
<!-- 第三行 -->
<div class="form-row">
<el-form-item style="margin-right: 40px;">
<el-checkbox v-model="formData.patientCardFlag">就诊卡记账标志</el-checkbox>
</el-form-item>
<el-form-item label="上午接诊起始时间" prop="morningStartTime" style="width: 300px; white-space: nowrap;">
<el-input v-model="formData.morningStartTime" style="width: 120px" />
</el-form-item>
</div>
<!-- 第四行 -->
<div class="form-row">
<el-form-item style="margin-right: 40px;">
<el-checkbox v-model="formData.autoGenerateOutpatientNo">自动产生门诊号</el-checkbox>
</el-form-item>
<el-form-item style="margin-right: 40px;">
<el-checkbox v-model="formData.allowModifyOutpatientNo">建档时是否允许修改门诊号</el-checkbox>
</el-form-item>
</div>
<!-- 第五行 -->
<div class="form-row">
<el-form-item label="下午起始时间" prop="afternoonStartTime" style="margin-right: 40px;">
<el-input v-model="formData.afternoonStartTime" style="width: 120px" />
</el-form-item>
<el-form-item label="晚上起始时间" prop="eveningStartTime">
<el-input v-model="formData.eveningStartTime" style="width: 120px" />
</el-form-item>
</div>
<!-- 第六行 -->
<div class="form-row">
<el-form-item label="挂号有效期" prop="registrationValidity" style="margin-right: 40px;">
<el-input v-model="formData.registrationValidity" style="width: 120px" />
</el-form-item>
<el-form-item label="挂号单据模式" prop="registrationDocumentMode">
<el-select v-model="formData.registrationDocumentMode" style="width: 150px">
<el-option label="使用发票" value="使用发票" />
<el-option label="其他模式" value="其他模式" />
</el-select>
</el-form-item>
</div>
<!-- 第七行 -->
<div class="form-row">
<el-form-item style="margin-right: 40px;">
<el-checkbox v-model="formData.exemptFlag">减免标志</el-checkbox>
</el-form-item>
<el-form-item style="margin-right: 40px;">
<el-checkbox v-model="formData.consultationFlag">义诊标志</el-checkbox>
</el-form-item>
<el-form-item style="margin-right: 40px;">
<el-checkbox v-model="formData.enableHolidayFeeFloat">启用法定节假日挂号费浮动</el-checkbox>
</el-form-item>
</div>
<!-- 第八行 -->
<div class="form-row">
<el-form-item label="监护人规定年龄" prop="guardianAge" style="margin-right: 40px;">
<el-input v-model="formData.guardianAge" style="width: 120px" />
</el-form-item>
<el-form-item>
<el-checkbox v-model="formData.enableDoubleScreen">门诊挂号启用双屏</el-checkbox>
</el-form-item>
</div>
<!-- 第九行 -->
<div class="form-row">
<el-form-item>
<el-checkbox v-model="formData.optionalRegistrationType">挂号类型可选择</el-checkbox>
</el-form-item>
</div>
</el-form>
</el-tab-pane>
<el-tab-pane label="挂号预约" name="tab4">
<div class="tab-content">挂号预约相关配置</div>
</el-tab-pane>
<el-tab-pane label="打印设置" name="tab5">
<el-checkbox v-model="formData.isPrint">是否打印挂号单</el-checkbox>
</el-tab-pane>
<el-tab-pane label="其他选项" name="tab6">
<div class="tab-content">其他选项相关配置</div>
</el-tab-pane>
<el-tab-pane label="病人账户" name="tab7">
<div class="tab-content">病人账户相关配置</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
const router = useRouter();
const formRef = ref(null);
const activeTab = ref('tab1');
// 表单数据
const formData = reactive({
medicalRecordFee: '1',
patientCardFee: '1',
medicalRecordFlag: true,
patientCardFlag: false,
autoGenerateOutpatientNo: true,
allowModifyOutpatientNo: false,
enableHolidayFeeFloat: true,
enableDoubleScreen: true,
isNightShift: false,
exemptFlag: false,
consultationFlag: false,
optionalRegistrationType: false,
morningStartTime: '08:00',
afternoonStartTime: '13:00',
eveningStartTime: '22:00',
registrationValidity: '5',
registrationDocumentMode: '使用发票',
guardianAge: '16',
isPrint: false,
});
// 默认按钮点击事件
const handleDefault = () => {
// 重置为默认值
Object.assign(formData, {
medicalRecordFee: '1',
patientCardFee: '1',
medicalRecordFlag: true,
patientCardFlag: false,
autoGenerateOutpatientNo: true,
allowModifyOutpatientNo: false,
enableHolidayFeeFloat: true,
enableDoubleScreen: true,
isNightShift: false,
exemptFlag: false,
consultationFlag: false,
optionalRegistrationType: false,
morningStartTime: '08:00',
afternoonStartTime: '13:00',
eveningStartTime: '22:00',
registrationValidity: '5',
registrationDocumentMode: '使用发票',
guardianAge: '16',
isPrint: false,
});
ElMessage.success('已恢复默认值');
};
// 保存按钮点击事件
const handleSave = async () => {
try {
// 这里可以添加表单验证逻辑
if (formRef.value) {
await formRef.value.validate();
}
// 模拟保存操作
// 实际项目中应该调用API保存数据
console.log('保存配置:', formData);
ElMessage.success('保存成功');
} catch (error) {
console.error('保存失败:', error);
ElMessage.error('保存失败,请重试');
}
};
// 关闭按钮点击事件
const handleClose = () => {
router.back();
};
</script>
<style scoped>
.app-container {
padding: 20px;
}
.config-header {
margin-bottom: 20px;
}
.config-header .el-button {
margin-right: 10px;
}
.config-tabs {
margin-top: 10px;
}
.config-form {
padding: 20px;
background-color: #f5f7fa;
border-radius: 4px;
}
.form-row {
display: flex;
align-items: center;
margin-bottom: 15px;
flex-wrap: wrap;
}
.config-form .el-form-item {
margin-bottom: 0;
margin-right: 0;
}
.config-form .el-form-item__label {
font-weight: 500;
width: 120px;
text-align: right;
}
.config-form .el-form-item--medium .el-form-item__content {
line-height: 32px;
}
.tab-content {
padding: 20px;
color: #606266;
text-align: center;
line-height: 100px;
background-color: #f5f7fa;
border-radius: 4px;
}
</style>

View File

@@ -185,6 +185,7 @@ import {
getpurchaseInventoryDetail, getpurchaseInventoryDetail,
getpurchaseInventoryDetailReturn getpurchaseInventoryDetailReturn
} from "./components/api"; } from "./components/api";
import { purchaseInventoryApproved as purchaseReturnApproved } from '../returnedPurchase/components/api';
import { useStore } from '@/store/store'; import { useStore } from '@/store/store';
import { formatDate } from "@/utils/index"; import { formatDate } from "@/utils/index";
import useTagsViewStore from '@/store/modules/tagsView'; import useTagsViewStore from '@/store/modules/tagsView';
@@ -260,6 +261,21 @@ function handelApply(row) {
getList(); getList();
} }
}) })
}else if(row.typeEnum == 5){ // 采购退货审批通过
console.log("采购退货审批通过typeEnum:", row.typeEnum);
console.log("供应单据号:", row.supplyBusNo);
purchaseReturnApproved(row.supplyBusNo).then((res) => {
console.log("采购退货审批结果:", res);
if (res.code == 200) {
proxy.$modal.msgSuccess("操作成功");
getList();
} else {
proxy.$modal.msgError("操作失败: " + (res.msg || "未知错误"));
}
}).catch((error) => {
console.error("采购退货审批错误:", error);
proxy.$modal.msgError("操作异常: " + error.message);
});
}else{ }else{
purchaseInventoryApproved(row.supplyBusNo).then((res) => { purchaseInventoryApproved(row.supplyBusNo).then((res) => {
if (res.code == 200) { if (res.code == 200) {

View File

@@ -87,23 +87,38 @@ export function getCount(queryParams) {
}) })
} }
// 获取药房列表 // 获取药房列表(权限过滤)
export function getPharmacyList() { export function getPharmacyList() {
return request({ return request({
url: '/app-common/inventory-pharmacy-list', url: '/app-common/inventory-pharmacy-list',
// '/app-common/pharmacy-list',
method: 'get', method: 'get',
}) })
} }
// 获取药库列表 // 获取药库列表(权限过滤)
export function getDispensaryList() { export function getDispensaryList() {
return request({ return request({
url: '/app-common/inventory-cabinet-list', url: '/app-common/inventory-cabinet-list',
// '/app-common/cabinet-list',
method: 'get', method: 'get',
}) })
} }
// 获取药房列表(不受权限过滤,作为回退)
export function getPharmacyListAll() {
return request({
url: '/app-common/pharmacy-list',
method: 'get',
})
}
// 获取药库列表(不受权限过滤,作为回退)
export function getDispensaryListAll() {
return request({
url: '/app-common/cabinet-list',
method: 'get',
})
}
/** /**
* 审批驳回 * 审批驳回
*/ */

View File

@@ -30,7 +30,8 @@
<el-button type="primary" plain icon="Plus" @click="handleSave">批量保存</el-button> <el-button type="primary" plain icon="Plus" @click="handleSave">批量保存</el-button>
</el-col> </el-col>
</el-row> </el-row>
<el-button type="primary" plain icon="Printer" @click="handlePrint"> 打印单据 </el-button> <el-button type="primary" icon="View" @click="handlePrintPreview"> 预览单据 </el-button>
<el-button type="success" plain icon="Printer" @click="handlePrint"> 打印单据 </el-button>
<el-form <el-form
:model="receiptHeaderForm" :model="receiptHeaderForm"
ref="receiptHeaderRef" ref="receiptHeaderRef"
@@ -130,8 +131,8 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-tabs type="border-card"> <el-tabs type="border-card">
<el-tab-pane label="入库单明细"> <el-tab-pane label="入库单明细">
<el-row :gutter="10" class="mb8" v-if="!viewStatus"> <el-row :gutter="10" class="mb8" v-if="!viewStatus">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="addNewRow">添加行</el-button> <el-button type="primary" plain icon="Plus" @click="addNewRow">添加行</el-button>
@@ -635,6 +636,8 @@ import {
getInitBusNo, getInitBusNo,
purchaseInventoryApproved, purchaseInventoryApproved,
reject, reject,
getPharmacyListAll,
getDispensaryListAll,
} from './components/api'; } from './components/api';
const route = useRoute(); const route = useRoute();
import PopoverList from '@/components/OpenHis/popoverList/index.vue'; import PopoverList from '@/components/OpenHis/popoverList/index.vue';
@@ -643,7 +646,8 @@ import TraceNoDialog from '@/components/OpenHis/TraceNoDialog/index.vue';
import { formatDate, formatDateymd } from '@/utils/index'; import { formatDate, formatDateymd } from '@/utils/index';
import { useStore } from '@/store/store'; import { useStore } from '@/store/store';
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
import { nextTick, ref, watch } from 'vue'; import { ElMessage } from 'element-plus';
import { nextTick, ref, watch, onMounted, onUnmounted } from 'vue';
import useTagsViewStore from '@/store/modules/tagsView'; import useTagsViewStore from '@/store/modules/tagsView';
import _, { isEqual } from 'lodash'; import _, { isEqual } from 'lodash';
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
@@ -765,7 +769,7 @@ watch(
} else { } else {
editData.value = {}; editData.value = {};
show(); show();
} }
}, },
{ immediate: true } { immediate: true }
); );
@@ -793,14 +797,43 @@ watch(
{ immediate: true } { immediate: true }
); );
onMounted(() => { onMounted(() => {
document.addEventListener("click", handleClickOutside);
// 获取源仓库列表(假设 getPharmacyList 返回仓库数据)
getPharmacyList({ type: 'source' }).then(res => {
if (res.code === 200) {
sourceTypeListOptions.value = res.data; // 格式需为 [{id, name}, ...]
}
});
// 获取目的仓库列表
getPharmacyList({ type: 'purpose' }).then(res => {
if (res.code === 200) {
purposeTypeListOptions.value = res.data;
}
});
// 初始化默认仓库数据,使用药房类型(16)
handleChangeLocationType(16);
console.log(route, '!1212'); console.log(route, '!1212');
if (route.query.view) { if (route.query.view) {
viewStatus.value = route.query.view; viewStatus.value = route.query.view;
// 查看模式下不要清空store数据让watch监听器自然处理
} else { } else {
viewStatus.value = ''; viewStatus.value = '';
// 正常进入模式下清空store数据确保显示空表单
store.clearCurrentData();
} }
if (Object.keys(editData.value).length === 0) { // 不再直接调用show()而是让watch监听器根据store.currentData状态来决定
show(); });
// 关键修复在组件卸载时清除store数据确保离开页面后再次进入时不会显示历史记录
onUnmounted(() => {
document.removeEventListener("click", handleClickOutside);
// 只有在非查看模式下才清除store数据
if (!viewStatus.value) {
store.clearCurrentData();
} }
}); });
@@ -821,7 +854,8 @@ function addNewRow() {
remake: '', remake: '',
supplierId: '', supplierId: '',
purposeTypeEnum: '', purposeTypeEnum: '',
purposeLocationId: null, // 使用表头的仓库作为新增行默认值,减少重复选择
purposeLocationId: receiptHeaderForm.purposeLocationId || null,
purposeLocationStoreId: null, purposeLocationStoreId: null,
practitionerId: '', practitionerId: '',
traceNo: '', traceNo: '',
@@ -1120,15 +1154,63 @@ function changeValEnd(val, index) {
} }
} }
} }
// 切换仓库类型获取药房/药库列表 // 切换仓库类型获取药房/药库/耗材库列表
function handleChangeLocationType(value) { function handleChangeLocationType(value) {
if (value == 16) { if (value == 16) {
// 药房类型
getPharmacyList().then((res) => { getPharmacyList().then((res) => {
locationList.value = res.data; const list = res?.data || [];
if (!list.length) {
getPharmacyListAll().then((res2) => {
locationList.value = res2?.data || [];
});
} else {
locationList.value = list;
}
}); });
} else if (value == 11) { } else if (value == 11) {
getDispensaryList().then((res) => { // 根据用户说明标识为11的是药库
locationList.value = res.data; // 直接使用getDispensaryListAll获取药库数据而不是从药房数据中过滤
getDispensaryListAll().then((res) => {
const pharmacies = res?.data || [];
console.log('===== 药库数据获取结果 =====');
console.log('获取到的药库数量:', pharmacies.length);
console.log('药库列表:', pharmacies);
// 直接使用获取到的药库数据
locationList.value = pharmacies;
// 如果没有找到药库,显示提示信息
if (pharmacies.length === 0) {
console.warn('未获取到任何药库数据');
}
}).catch(error => {
console.error('获取药库列表失败:', error);
locationList.value = [];
});
} else if (value == 17) {
// 耗材库类型 - 确保只显示中心耗材库
console.log('选择了耗材库类型,设置中心耗材库');
// 只设置一个硬编码的中心耗材库使用数字ID避免类型转换错误
const centralSupplyWarehouse = {
id: 1, // 使用数字ID而不是字符串
locationStoreId: 1, // 使用数字ID而不是字符串
locationStoreName: '中心耗材库',
formEnum: '17',
storeName: '中心耗材库',
name: '中心耗材库'
};
// 直接设置locationList只包含中心耗材库
locationList.value = [centralSupplyWarehouse];
console.log('已设置仅包含中心耗材库的列表');
// 可选:如果需要在后台记录日志,可以保留获取数据的逻辑但不覆盖显示
getPharmacyListAll().then((res) => {
console.log('后台获取仓库数据(仅供参考,不影响显示):', res?.data?.length || 0);
}).catch(error => {
console.error('后台获取仓库数据失败(不影响显示):', error);
}); });
} }
} }
@@ -1303,27 +1385,193 @@ function handleSelectionChange(selection) {
multiple.value = !selection.length; multiple.value = !selection.length;
} }
function handlePrint() { // 预览单据函数 - 简化实现,避免复杂嵌套数据结构
const result = []; function handlePrintPreview() {
let supplierName = supplierListOptions.value.filter((item) => { console.log('开始执行预览功能');
return item.value == receiptHeaderForm.supplierId;
})[0].label; // 检查是否存在数据
if (!form.purchaseinventoryList || form.purchaseinventoryList.length === 0) {
ElMessage.warning('没有数据可预览');
return;
}
// 检查是否所有必填字段都已填写
const hasEmptyFields = form.purchaseinventoryList.some(row => {
return !row.name || !row.itemQuantity || !row.price || !row.totalPrice;
});
if (hasEmptyFields) {
ElMessage.warning('请确保所有药品信息完整后再预览');
return;
}
// 检查templateJson是否已正确导入
if (!templateJson) {
console.error('错误templateJson未定义请检查导入');
ElMessage.error('打印模板未加载,请刷新页面重试');
return;
}
// 安全获取供应商名称
let supplierName = '';
if (supplierListOptions.value && receiptHeaderForm.supplierId) {
const filteredSuppliers = supplierListOptions.value.filter((item) => {
return item.value == receiptHeaderForm.supplierId;
});
supplierName = filteredSuppliers.length > 0 ? filteredSuppliers[0].label : '';
}
// 计算总金额
const totalAmount = form.purchaseinventoryList.reduce((accumulator, currentRow) => { const totalAmount = form.purchaseinventoryList.reduce((accumulator, currentRow) => {
return accumulator + (Number(currentRow.totalPrice) || 0); return accumulator + (Number(currentRow.totalPrice) || 0);
}, 0); }, 0);
result.push({
supplierName: supplierName, // 重新构建包含药品清单的数据结构
const simpleData = {
supplierName: supplierName || '',
totalAmount: totalAmount.toFixed(2), totalAmount: totalAmount.toFixed(2),
...receiptHeaderForm, receiptNumber: receiptHeaderForm.receiptNumber || '',
purchaseinventoryList: form.purchaseinventoryList, receiptDate: receiptHeaderForm.receiptDate || '',
organizationName: receiptHeaderForm.organizationName || '',
// 重新添加药品清单数组,但确保格式安全
purchaseinventoryList: Array.isArray(form.purchaseinventoryList) ? form.purchaseinventoryList.map(item => ({
id: item.id || '',
name: item.name || '',
itemQuantity: item.itemQuantity || 0,
price: item.price || 0,
totalPrice: item.totalPrice || 0,
// 保留其他可能需要的字段
...item
})) : []
};
console.log('简化后的预览数据:', simpleData);
try {
// 直接使用templateJson创建打印
// 避免复杂的模板创建和数据处理逻辑
setTimeout(() => {
try {
// 最简单的方式使用hiprint - 直接调用print2方法
// 只传递必要的数据,不传递复杂嵌套的数组
const printOptions = {
preview: true,
title: '药品采购入库清单',
printer: ''
};
// 方法1: 尝试直接使用templateJson
console.log('尝试直接使用templateJson');
if (typeof hiprint.template !== 'undefined' && typeof hiprint.template.print === 'function') {
hiprint.template.print(templateJson, simpleData, printOptions);
}
// 方法2: 尝试创建简单模板
else if (typeof hiprint.PrintTemplate === 'function') {
console.log('尝试使用PrintTemplate');
const template = new hiprint.PrintTemplate({template: templateJson});
template.print(simpleData, printOptions);
}
// 方法3: 最直接的方式
else {
console.log('尝试最直接的打印方式');
hiprint.print(templateJson, simpleData, printOptions);
}
ElMessage.success('正在生成单据预览,请等待...');
} catch (e) {
console.error('预览执行失败:', e);
ElMessage.error('预览功能执行失败,请稍后重试');
}
}, 50);
} catch (error) {
console.error('预览准备失败:', error);
ElMessage.error('预览失败,请检查配置');
}
}
// 打印单据函数 - 简化实现,与预览函数保持一致
function handlePrint() {
// 检查是否存在数据
if (!form.purchaseinventoryList || form.purchaseinventoryList.length === 0) {
ElMessage.warning('没有数据可打印');
return;
}
// 检查是否所有必填字段都已填写
const hasEmptyFields = form.purchaseinventoryList.some(row => {
return !row.name || !row.itemQuantity || !row.price || !row.totalPrice;
}); });
console.log(result, '345678987654');
const printElements = templateJson; if (hasEmptyFields) {
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板 ElMessage.warning('请确保所有药品信息完整后再打印');
hiprintTemplate.print2(result, { return;
printer: 'EPSON LQ-80KFII', }
title: '打印标题',
}); //开始打印 // 安全获取供应商名称
let supplierName = '';
if (supplierListOptions.value && receiptHeaderForm.supplierId) {
const filteredSuppliers = supplierListOptions.value.filter((item) => {
return item.value == receiptHeaderForm.supplierId;
});
supplierName = filteredSuppliers.length > 0 ? filteredSuppliers[0].label : '';
}
// 计算总金额
const totalAmount = form.purchaseinventoryList.reduce((accumulator, currentRow) => {
return accumulator + (Number(currentRow.totalPrice) || 0);
}, 0);
// 重新构建包含药品清单的数据结构
const simpleData = {
supplierName: supplierName || '',
totalAmount: totalAmount.toFixed(2),
receiptNumber: receiptHeaderForm.receiptNumber || '',
receiptDate: receiptHeaderForm.receiptDate || '',
organizationName: receiptHeaderForm.organizationName || '',
// 重新添加药品清单数组,但确保格式安全
purchaseinventoryList: Array.isArray(form.purchaseinventoryList) ? form.purchaseinventoryList.map(item => ({
id: item.id || '',
name: item.name || '',
itemQuantity: item.itemQuantity || 0,
price: item.price || 0,
totalPrice: item.totalPrice || 0,
// 保留其他可能需要的字段
...item
})) : []
};
try {
// 使用与预览函数相同的简化打印逻辑
setTimeout(() => {
try {
const printOptions = {
preview: false, // 直接打印模式
title: '药品采购入库清单',
printer: ''
};
// 尝试多种可能的API调用方式
if (typeof hiprint.template !== 'undefined' && typeof hiprint.template.print === 'function') {
hiprint.template.print(templateJson, simpleData, printOptions);
}
else if (typeof hiprint.PrintTemplate === 'function') {
const template = new hiprint.PrintTemplate({template: templateJson});
template.print(simpleData, printOptions);
}
else {
hiprint.print(templateJson, simpleData, printOptions);
}
ElMessage.success('打印请求已发送');
} catch (e) {
console.error('打印执行失败:', e);
ElMessage.error('打印执行失败,请稍后重试');
}
}, 50);
} catch (error) {
console.error('打印失败:', error);
ElMessage.error('打印失败,请检查配置');
}
} }
function deleteSelectedRows() { function deleteSelectedRows() {
@@ -1365,38 +1613,19 @@ function handleTotalAmount() {
/** 重置操作表单 */ /** 重置操作表单 */
function reset() { function reset() {
// form.value = {
// id: undefined,
// name: undefined,
// categoryCode: undefined,
// cwTypeCode: undefined,
// fwTypeCode: undefined,
// specialtyCode: undefined,
// locationId: undefined,
// offeredOrgId: undefined,
// activeFlag: undefined,
// extraDetails: undefined,
// contact: undefined,
// appointmentRequiredFlag: undefined,
// chargeName: undefined,
// price: undefined,
// description: undefined,
// ybType: undefined,
// title: undefined,
// comment: undefined,
// };
// proxy.resetForm("purchaseinventoryRef");
// receiptHeaderForm = {
// busNo: undefined,
// practitionerId: undefined,
// occurrenceTime: undefined,
// supplierId: undefined,
// medicationType: "1",
// purposeTypeEnum: undefined,
// };
proxy.resetForm('receiptHeaderRef'); proxy.resetForm('receiptHeaderRef');
form.purchaseinventoryList = []; form.purchaseinventoryList = [];
// 完全重置所有表单字段
Object.assign(receiptHeaderForm, {
busNo: undefined,
practitionerId: undefined,
occurrenceTime: formatDate(new Date()),
supplierId: undefined,
medicationType: "1",
purposeTypeEnum: undefined
});
// 重置编辑数据
editData.value = {};
} }
// 显示弹框 // 显示弹框
function show() { function show() {
@@ -1406,7 +1635,9 @@ function show() {
visible.value = true; visible.value = true;
getPurchaseinventoryTypeList(); getPurchaseinventoryTypeList();
getInitBusNo().then((res) => { getInitBusNo().then((res) => {
receiptHeaderForm.busNo = res.data.busNo; if (res.data && res.data.busNo) {
receiptHeaderForm.busNo = res.data.busNo;
}
}); });
// supplierListOptions.value = props.supplierListOptions; // supplierListOptions.value = props.supplierListOptions;
// itemTypeOptions.value = props.itemTypeOptions; // itemTypeOptions.value = props.itemTypeOptions;
@@ -1428,9 +1659,15 @@ function edit() {
// itemTypeOptions.value = props.itemTypeOptions; // itemTypeOptions.value = props.itemTypeOptions;
// practitionerListOptions.value = props.practitionerListOptions; // practitionerListOptions.value = props.practitionerListOptions;
// receiptHeaderForm.busNo = props.busNoAdd; // receiptHeaderForm.busNo = props.busNoAdd;
// 确保数据存在
if (!editData.value.item || editData.value.item.length === 0) {
proxy.$message.warning('没有可编辑的数据');
return;
}
receiptHeaderForm.value = editData.value.item.length > 0 ? editData.value.item[0] : {}; receiptHeaderForm.value = editData.value.item.length > 0 ? editData.value.item[0] : {};
receiptHeaderForm.busNo = editData.value.editRow.supplyBusNo; receiptHeaderForm.busNo = editData.value.editRow.supplyBusNo;
receiptHeaderForm.supplierId = editData.value.editRow.supplierId; // 修改这里优先从详情数据中获取供应商ID如果不存在则从列表数据中获取
receiptHeaderForm.supplierId = (editData.value.item[0] && editData.value.item[0].supplierId) || editData.value.editRow.supplierId;
receiptHeaderForm.practitionerId = editData.value.editRow.practitionerId; receiptHeaderForm.practitionerId = editData.value.editRow.practitionerId;
console.log(editData.value.editRow, editData.value, 'editData.value.editRow'); console.log(editData.value.editRow, editData.value, 'editData.value.editRow');
receiptHeaderForm.occurrenceTime = editData.value.item[0].occurrenceTime receiptHeaderForm.occurrenceTime = editData.value.item[0].occurrenceTime
@@ -1463,6 +1700,10 @@ function edit() {
volume: item.totalVolume, volume: item.totalVolume,
manufacturer: item.supplierName, manufacturer: item.supplierName,
quantity: item.totalQuantity, quantity: item.totalQuantity,
// 确保仓库字段回显
purposeLocationId: item.purposeLocationId || editData.value.editRow?.purposeLocationId || null,
locationStoreName: item.locationStoreName || '',
purposeLocationStoreId: item.purposeLocationStoreId || null,
// locationInventoryList: locationList.value, // locationInventoryList: locationList.value,
startTime: formatDateymd(item.startTime), startTime: formatDateymd(item.startTime),
endTime: formatDateymd(item.endTime), endTime: formatDateymd(item.endTime),
@@ -1475,6 +1716,27 @@ function edit() {
console.log(form.purchaseinventoryList, 'purchaseinventoryList.value'); console.log(form.purchaseinventoryList, 'purchaseinventoryList.value');
console.log(receiptHeaderForm, 'receiptHeaderForm'); console.log(receiptHeaderForm, 'receiptHeaderForm');
loading.value = false; loading.value = false;
// 使用 Promise 确保数据加载完成
Promise.resolve().then(() => {
form.purchaseinventoryList = editData.value.item.map((item) => {
return {
...item,
name: item.itemName,
volume: item.totalVolume,
manufacturer: item.supplierName,
quantity: item.totalQuantity,
// 仓库字段回显Promise 阶段再保障一次)
purposeLocationId: item.purposeLocationId || editData.value.editRow?.purposeLocationId || null,
locationStoreName: item.locationStoreName || '',
purposeLocationStoreId: item.purposeLocationStoreId || null,
startTime: formatDateymd(item.startTime),
endTime: formatDateymd(item.endTime),
applyTime: formatDate(item.applyTime),
isSave: true,
};
});
});
} }
function getPurchaseinventoryTypeList() { function getPurchaseinventoryTypeList() {
@@ -1485,7 +1747,7 @@ function getPurchaseinventoryTypeList() {
}); });
} }
// 驳回 // reject
function handleReject() { function handleReject() {
reject(route.query.supplyBusNo).then((res) => { reject(route.query.supplyBusNo).then((res) => {
if (res.code == 200) { if (res.code == 200) {

View File

@@ -693,6 +693,7 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const title = ref(""); const title = ref("");
const visible = ref(false); const visible = ref(false);
const pageLoading = ref(false);
const row = ref({}); const row = ref({});
const rowIndex = ref(-1); const rowIndex = ref(-1);
const totalAmount = ref(0); const totalAmount = ref(0);

View File

@@ -73,9 +73,9 @@
/> />
</el-select> </el-select>
</el-form-item> --> </el-form-item> -->
<el-form-item label="药房:" prop="purposeTypeEnum" label-width="80px"> <el-form-item label="药房:" prop="purposeLocationId" label-width="80px">
<el-select <el-select
v-model="queryParams.purposeTypeEnum" v-model="queryParams.purposeLocationId"
placeholder="" placeholder=""
clearable clearable
style="width: 150px" style="width: 150px"
@@ -103,9 +103,9 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="供应商:" prop="supplierId"> <el-form-item label="供应商:" prop="manufacturerText">
<el-input <el-input
v-model="queryParams.supplierId" v-model="queryParams.manufacturerText"
placeholder="回车查询" placeholder="回车查询"
clearable clearable
style="width: 200px" style="width: 200px"
@@ -363,7 +363,7 @@ const data = reactive({
busNo:undefined, busNo:undefined,
name: undefined, name: undefined,
categoryType:undefined, categoryType:undefined,
purposeTypeEnum:undefined, purposeLocationId:undefined,
inventoryScope: undefined, inventoryScope: undefined,
supplierId: undefined, supplierId: undefined,
// occurrenceTimeSTime:undefined, // occurrenceTimeSTime:undefined,

View File

@@ -164,27 +164,42 @@
:data="purchaseinventoryList" :data="purchaseinventoryList"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
:span-method="arraySpanMethod" :span-method="arraySpanMethod"
border
fit
:default-sort="{ prop: 'chargeTime', order: 'descending' }"
@header-click="handleHeaderClick"
show-summary
:summary-method="getSummaries"
> >
<!-- :span-method="arraySpanMethod" --> <!-- :span-method="arraySpanMethod" -->
<!-- :summary-method="getSummaries" <!-- :summary-method="getSummaries"
show-summary 每页单独合计--> show-summary 每页单独合计-->
<el-table-column type="selection" width="50px" align="center" /> <el-table-column type="selection" width="50px" align="center" resizable />
<el-table-column <el-table-column
label='门诊号' label='门诊号'
align="center" align="center"
key="busNo" key="busNo"
prop="busNo" min-width="150px"
width="110px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> resizable
>
<template #default="scope">
<el-button
type="text"
@click="handleArchive(scope.row)"
>
{{ scope.row.busNo }}
</el-button>
</template>
</el-table-column>
<el-table-column <el-table-column
label="科室" label="科室"
align="center" align="center"
key="departmentName" key="departmentName"
prop="departmentName" prop="departmentName"
min-width="120px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
/> />
<el-table-column <el-table-column
label="姓名" label="姓名"
@@ -193,14 +208,16 @@
prop="name" prop="name"
width="90px" width="90px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
/> />
<el-table-column <el-table-column
label="医保号" label="医保号"
align="center" align="center"
key="ybCode" key="ybCode"
prop="ybCode" prop="ybCode"
width="190px" min-width="200px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
/> />
<el-table-column <el-table-column
@@ -208,24 +225,27 @@
align="center" align="center"
key="clinicalName" key="clinicalName"
prop="clinicalName" prop="clinicalName"
min-width="180px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
/> />
<el-table-column <el-table-column
label="医保码" label="医保码"
align="center" align="center"
key="ybNo" key="ybNo"
prop="ybNo" prop="ybNo"
width="220px" min-width="250px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
/> />
<el-table-column <el-table-column
label="医保类别" label="医保类别"
align="center" align="center"
key="type_dictText" key="type_dictText"
prop="type_dictText" prop="type_dictText"
min-width="100px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
/> />
<el-table-column <el-table-column
@@ -233,8 +253,9 @@
align="center" align="center"
key="issuerName" key="issuerName"
prop="issuerName" prop="issuerName"
min-width="100px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
/> />
<el-table-column <el-table-column
@@ -242,8 +263,9 @@
align="center" align="center"
key="payeeName" key="payeeName"
prop="payeeName" prop="payeeName"
min-width="100px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
/> />
<el-table-column <el-table-column
label="规格" label="规格"
@@ -252,49 +274,59 @@
prop="totalVolume" prop="totalVolume"
width="135px" width="135px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
/> />
<el-table-column <el-table-column
label="数量" label="数量"
align="center" align="center"
key="number" key="number"
prop="number" prop="number"
min-width="80px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
/> />
<el-table-column <el-table-column
label="单价" label="单价"
align="center" align="center"
key="price" key="price"
prop="price" prop="price"
min-width="100px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> resizable
>
<template #default="scope">
<span>{{ scope.row.price || scope.row.salePrice || '0.00' }}</span>
</template>
</el-table-column>
<el-table-column <el-table-column
label="金额" label="金额"
align="center" align="center"
key="totalPrice" key="totalPrice"
prop="totalPrice" prop="totalPrice"
min-width="100px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
/> />
<el-table-column <el-table-column
label="医保等级" label="医保等级"
align="center" align="center"
key="chrgitmLv_enumText" key="chrgitmLv_enumText"
prop="chrgitmLv_enumText" prop="chrgitmLv_enumText"
min-width="100px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
/> />
<el-table-column <el-table-column
label="收费时间" label="收费时间"
align="center" align="center"
key="chargeTime" key="chargeTime"
prop="chargeTime" prop="chargeTime"
min-width="160px"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
resizable
> >
<template #default="scope"> <template #default="scope">
<span>{{ parseTime(scope.row.chargeTime) }}</span> <span>{{ parseTime(scope.row.chargeTime || scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -333,6 +365,7 @@ import {
} from "./statisticalManagent"; } from "./statisticalManagent";
import useUserStore from "@/store/modules/user"; import useUserStore from "@/store/modules/user";
import { watch } from "vue"; import { watch } from "vue";
import { parseTime } from '@/utils/his'; // 导入日期时间处理工具函数
const userStore = useUserStore(); const userStore = useUserStore();
const router = useRouter(); const router = useRouter();
@@ -418,10 +451,10 @@ function getTotals(row,i){
// totalReturnPriceSums += Number(purchaseinventoryList.value[i-j].refundPrice) // totalReturnPriceSums += Number(purchaseinventoryList.value[i-j].refundPrice)
} }
} }
xiaojiTotal.value.push({inde:i+1,busNo:row.busNo,genderEnum_enumText:row.genderEnum_enumText,totalPrice:totalPriceSums.toFixed(4)||0.0000}) xiaojiTotal.value.push({inde:i+1,busNo:row.busNo,genderEnum_enumText:row.genderEnum_enumText,totalPrice:totalPriceSums.toFixed(2)||0.00})
// var totalPrice2 = 0 // var totalPrice2 = 0
// var refundPrice2 = 0 // var refundPrice2 = 0
purchaseinventoryList.value.splice(i+1, 0, {busNo:row.busNo,genderEnum_enumText:row.genderEnum_enumText,departmentName:'小计',totalPrice:totalPriceSums.toFixed(4)||0.0000}) purchaseinventoryList.value.splice(i+1, 0, {busNo:row.busNo,genderEnum_enumText:row.genderEnum_enumText,departmentName:'小计',totalPrice:totalPriceSums.toFixed(2)||0.00})
// purchaseinventoryList.value.map(k=>{ // purchaseinventoryList.value.map(k=>{
// if(k.departmentName!='小计'){ // if(k.departmentName!='小计'){
// totalPrice2 += Number(k.totalPrice) // totalPrice2 += Number(k.totalPrice)
@@ -432,66 +465,43 @@ function getTotals(row,i){
// refundPrice2 = refundPrice2?refundPrice2.toFixed(4):refundPrice2 // refundPrice2 = refundPrice2?refundPrice2.toFixed(4):refundPrice2
// purchaseinventoryList.value.push({departmentName:'合计',totalPrice:totalPrice2,refundPrice:refundPrice2}) // purchaseinventoryList.value.push({departmentName:'合计',totalPrice:totalPrice2,refundPrice:refundPrice2})
} }
const arraySpanMethod = ({ // 行合并方法 - 简化实现,避免在合并过程中修改数组
row, const arraySpanMethod = ({row, column, rowIndex, columnIndex}) => {
column, // 跳过小计行和合计行的合并处理
rowIndex, if (row.departmentName === '小计' || row.departmentName === '合计') {
columnIndex, return { rowspan: 1, colspan: 1 };
}) => { }
// 合并一样的列住院号 outpatientNumber属性&&(queryParams.value.statisticsType==1||queryParams.value.statisticsType==4||queryParams.value.statisticsType==6)
if (columnIndex === 1&&purchaseinventoryList.value.length>0) { // 只合并门诊号列(索引1)
if (rowIndex === 0 || (rowIndex > 0 && row.busNo !== purchaseinventoryList.value[rowIndex - 1]?.busNo)) { if (columnIndex === 1) {
let rowspan = 1; // 查找当前门诊号的起始位置
let totalPriceSum = 0 let startIndex = rowIndex;
// let totalReturnPriceSum = 0 while (startIndex > 0) {
for (let i = rowIndex + 1; i < purchaseinventoryList.value.length+1; i++) { const prevRow = purchaseinventoryList.value[startIndex - 1];
if(purchaseinventoryList.value[i-1].departmentName!='合计'){ // 如果前一行是小计行或者门诊号不同,当前行就是起始行
if (purchaseinventoryList.value[i]&&purchaseinventoryList.value[i].busNo === row.busNo) { if (prevRow.departmentName === '小计' || prevRow.busNo !== row.busNo) {
rowspan++; break;
totalPriceSum += Number(purchaseinventoryList.value[i].totalPrice) }
// totalReturnPriceSum += Number(purchaseinventoryList.value[i].refundPrice) startIndex--;
if(i==purchaseinventoryList.value.length-1){ }
let findIndexTotal = xiaojiTotal.value.findIndex(k=>k.busNo==row.busNo)
if(findIndexTotal<0){ // 如果当前行是门诊号的起始行
getTotals(row,i) if (startIndex === rowIndex) {
} let rowspan = 1;
}else{ // 计算门诊号相同的连续行数
} for (let i = rowIndex + 1; i < purchaseinventoryList.value.length; i++) {
} else { const nextRow = purchaseinventoryList.value[i];
totalPriceSum += Number(row.totalPrice) // 如果下一行是小计行或者门诊号不同,停止计数
// totalReturnPriceSum += Number(row.refundPrice) if (nextRow.departmentName === '小计' || nextRow.busNo !== row.busNo) {
let findIndexTotal = xiaojiTotal.value.findIndex(k=>k.busNo==row.busNo) break;
if(findIndexTotal<0){ }
xiaojiTotal.value.push({inde:i,genderEnum_enumText:row.genderEnum_enumText,busNo:row.busNo,totalPrice:totalPriceSum}) rowspan++;
purchaseinventoryList.value.splice(i, 0, {busNo:row.busNo,genderEnum_enumText:row.genderEnum_enumText,departmentName:'小计',totalPrice:totalPriceSum.toFixed(4)||0.0000})
// console.log(purchaseinventoryList.value,"12121")
}
break;
}
}
} }
// console.log(purchaseinventoryList.value,"12122")
return { rowspan, colspan: 1 }; return { rowspan, colspan: 1 };
} else { } else {
// console.log(purchaseinventoryList.value,"12123") // 非起始行不显示
return { rowspan: 0, colspan: 0 }; return { rowspan: 0, colspan: 0 };
} }
// }else{ // 姓名列patientName
// if (columnIndex ===2&&purchaseinventoryList.value.length>0) {
// if (rowIndex === 0 || (rowIndex > 0 && row.name !== purchaseinventoryList.value[rowIndex - 1]?.name)) {
// let rowspan = 1;
// for (let i = rowIndex + 1; i < purchaseinventoryList.value.length; i++) {
// if (purchaseinventoryList.value[i].name === row.name) {
// rowspan++;
// } else {
// break;
// }
// }
// return { rowspan:rowspan, colspan: 1 };
// } else {
// return { rowspan: 0, colspan: 0 };
// }
// }
} }
} }
const getSummaries = (param) => { const getSummaries = (param) => {
@@ -509,11 +519,11 @@ const getSummaries = (param) => {
sums[index] = `${values.reduce((prev, curr) => { sums[index] = `${values.reduce((prev, curr) => {
const value = Number(curr) const value = Number(curr)
if (!Number.isNaN(value)) { if (!Number.isNaN(value)) {
return (Number(prev) + Number(curr)).toFixed(4) || 0.0000 return (Number(prev) + Number(curr)).toFixed(2) || 0.00
} else { } else {
return Number(prev) return Number(prev)
} }
}, 0.0000)}` }, 0.00)}`
} else { } else {
sums[index] = ' ' sums[index] = ' '
} }
@@ -565,6 +575,7 @@ function getList(type) {
// var purchaseinventoryLists = [] // var purchaseinventoryLists = []
// var purchaseinventoryListsArray = [] // var purchaseinventoryListsArray = []
getReportChargePage(queryParams.value).then((res) => { getReportChargePage(queryParams.value).then((res) => {
console.log('API返回数据:', res.data.records && res.data.records.length > 0 ? res.data.records[0] : {}); // 调试日志
// purchaseinventoryLists = res.data.records||[] //转换数据结构 // purchaseinventoryLists = res.data.records||[] //转换数据结构
// if(purchaseinventoryLists.length>0){ // if(purchaseinventoryLists.length>0){
// purchaseinventoryLists.map(p=>{ // purchaseinventoryLists.map(p=>{
@@ -576,10 +587,15 @@ function getList(type) {
purchaseinventoryList.value = res.data.records||[] purchaseinventoryList.value = res.data.records||[]
total.value = res.data.total total.value = res.data.total
purchaseinventoryList.value.map(k=>{ purchaseinventoryList.value.map(k=>{
k.totalPrice = k.totalPrice?k.totalPrice.toFixed(4):'0.0000' k.totalPrice = k.totalPrice?k.totalPrice.toFixed(2):'0.00'
k.price = k.price?k.price.toFixed(4):'0.0000' // 优先使用price其次使用salePrice
k.price = k.price ? k.price.toFixed(2) : (k.salePrice ? k.salePrice.toFixed(2) : '0.00')
// 如果没有chargeTime尝试使用createTime作为替代
if (!k.chargeTime && k.createTime) {
k.chargeTime = k.createTime;
}
// k.refundPrice = k.refundPrice || '0.00' // k.refundPrice = k.refundPrice || '0.00'
k.number = k.number?(k.number+(k.quantityUnit_dictText?k.quantityUnit_dictText:"")):('0.0000'+ k.quantityUnit_dictText?k.quantityUnit_dictText:"") k.number = k.number ? (k.number + (k.quantityUnit_dictText || '')) : ('0.00' + (k.quantityUnit_dictText || ''))
// k.refundQuantity = k.refundQuantity?(k.refundQuantity+k.refundUnitCode_dictText):('0.00'+ k.quantityUnit_dictText) // k.refundQuantity = k.refundQuantity?(k.refundQuantity+k.refundUnitCode_dictText):('0.00'+ k.quantityUnit_dictText)
if(total.value&&total.value<=queryParams.value.pageSize){ if(total.value&&total.value<=queryParams.value.pageSize){
totalPrice2 += Number(k.totalPrice) totalPrice2 += Number(k.totalPrice)
@@ -589,15 +605,7 @@ function getList(type) {
if(total.value<=res.data.size){ if(total.value<=res.data.size){
loading.value = false; loading.value = false;
} }
// 1页数据 // 1页数据不再手动添加合计行使用表格的show-summary功能
if(total.value&&total.value<=queryParams.value.pageSize){
totalPrice2 = totalPrice2?totalPrice2.toFixed(4):totalPrice2
// refundPrice2 = refundPrice2?refundPrice2.toFixed(4):refundPrice2
let pageNoAll = total.value / queryParams.value.pageSize
if(Math.ceil(pageNoAll)==queryParams.value.pageNo){
purchaseinventoryList.value.push({departmentName:'合计',totalPrice:totalPrice2})
}
}
// 带分页数据 // 带分页数据
if(total.value&&total.value>queryParams.value.pageSize){ if(total.value&&total.value>queryParams.value.pageSize){
@@ -618,10 +626,15 @@ function getList(type) {
purchaseinventoryListAll.value = res.data.records||[] purchaseinventoryListAll.value = res.data.records||[]
if(purchaseinventoryListAll.value.length>0){ if(purchaseinventoryListAll.value.length>0){
purchaseinventoryListAll.value.map((k,index)=>{ purchaseinventoryListAll.value.map((k,index)=>{
k.totalPrice = k.totalPrice?k.totalPrice.toFixed(4):'0.0000' k.totalPrice = k.totalPrice?k.totalPrice.toFixed(2):'0.00'
k.price = k.price?k.price.toFixed(4):'0.0000' // 优先使用price其次使用salePrice
k.price = k.price ? k.price.toFixed(2) : (k.salePrice ? k.salePrice.toFixed(2) : '0.00')
// 如果没有chargeTime尝试使用createTime作为替代
if (!k.chargeTime && k.createTime) {
k.chargeTime = k.createTime;
}
// k.refundPrice = k.refundPrice || '0.00' // k.refundPrice = k.refundPrice || '0.00'
k.number = k.number?(k.number+k.quantityUnit_dictText?k.quantityUnit_dictText:""):('0.0000'+ k.quantityUnit_dictText?k.quantityUnit_dictText:"") k.number = k.number ? (k.number + (k.quantityUnit_dictText || '')) : ('0.00' + (k.quantityUnit_dictText || ''))
// k.refundQuantity = k.refundQuantity?(k.refundQuantity+k.refundUnitCode_dictText):('0.00'+ k.quantityUnit_dictText) // k.refundQuantity = k.refundQuantity?(k.refundQuantity+k.refundUnitCode_dictText):('0.00'+ k.quantityUnit_dictText)
totalPrice2 += Number(k.totalPrice) totalPrice2 += Number(k.totalPrice)
// refundPrice2 += Number(k.refundPrice) // refundPrice2 += Number(k.refundPrice)
@@ -637,7 +650,7 @@ function getList(type) {
if(dispenseNoIndex1>0){ if(dispenseNoIndex1>0){
purchaseinventoryList.value[dispenseNoIndex1].totalPrice = Number(purchaseinventoryList.value[dispenseNoIndex1].totalPrice)+Number(purchaseinventoryListAll.value[index-m].totalPrice) purchaseinventoryList.value[dispenseNoIndex1].totalPrice = Number(purchaseinventoryList.value[dispenseNoIndex1].totalPrice)+Number(purchaseinventoryListAll.value[index-m].totalPrice)
purchaseinventoryList.value[dispenseNoIndex1].totalPrice = purchaseinventoryList.value[dispenseNoIndex1].totalPrice?purchaseinventoryList.value[dispenseNoIndex1].totalPrice.toFixed(4):'0.0000' purchaseinventoryList.value[dispenseNoIndex1].totalPrice = purchaseinventoryList.value[dispenseNoIndex1].totalPrice?purchaseinventoryList.value[dispenseNoIndex1].totalPrice.toFixed(2):'0.00'
} }
} }
if ((index+m) == (queryParams.value.pageSize*queryParams.value.pageNo)&& k.busNo == purchaseinventoryListAll.value[index + m]?.busNo) { if ((index+m) == (queryParams.value.pageSize*queryParams.value.pageNo)&& k.busNo == purchaseinventoryListAll.value[index + m]?.busNo) {
@@ -656,10 +669,7 @@ function getList(type) {
totalPrice2 = totalPrice2?totalPrice2.toFixed(4):totalPrice2 totalPrice2 = totalPrice2?totalPrice2.toFixed(4):totalPrice2
// refundPrice2 = refundPrice2?refundPrice2.toFixed(4):refundPrice2 // refundPrice2 = refundPrice2?refundPrice2.toFixed(4):refundPrice2
loading.value = false; loading.value = false;
let pageNoAll = total.value / queryParams.value.pageSize // 不再手动添加合计行使用表格的show-summary功能
if(Math.ceil(pageNoAll)==queryParams.value.pageNo){
purchaseinventoryList.value.push({departmentName:'合计',totalPrice:totalPrice2})
}
} }
}) })
} }
@@ -697,6 +707,51 @@ function handleSelectionChange(selection) {
multiple.value = !selection.length; multiple.value = !selection.length;
} }
// 表头点击事件,用于实现更灵活的列操作
function handleHeaderClick(column) {
// 可以在这里添加额外的表头操作逻辑
// 例如:根据列内容自动调整宽度
if (column.property) {
autoFitColumnWidth(column.property);
}
}
// 根据内容自动调整列宽
function autoFitColumnWidth(property) {
// 实际项目中可以根据需要实现更复杂的自动宽度计算逻辑
// 这里提供一个简单的实现方式
const table = document.querySelector('.el-table__body-wrapper table');
if (table) {
const rows = table.querySelectorAll('tbody tr');
let maxWidth = 100; // 默认最小宽度
rows.forEach(row => {
const cell = Array.from(row.cells).find((cell, index) => {
const headerCell = table.querySelector(`thead th:nth-child(${index + 1})`);
return headerCell && headerCell.dataset.property === property;
});
if (cell) {
const textWidth = cell.textContent.length * 8; // 粗略估算文本宽度
maxWidth = Math.max(maxWidth, textWidth);
}
});
// 应用计算出的宽度(可以根据实际需要调整)
// 注意在实际Element Plus中可能需要通过ref获取表格实例并调用API来设置列宽
}
}
/** 跳转到患者档案管理页面 */
function handleArchive(row) {
// 跳转到患者档案管理页面并传递busNo参数
proxy.$router.push({
path: '/system/basicmanage/patientmanagement',
query: { searchKey: row.busNo }
});
}
getList(); getList();
getPharmacyCabinetLists() getPharmacyCabinetLists()

View File

@@ -1,5 +1,6 @@
<template> <template>
<div class="app-container" v-loading="pageLoading" loading-text="审批中..."> <div class="app-container" v-loading="pageLoading" loading-text="审批中...">
<el-row :gutter="10" class="mb8" v-if="viewStatus"> <el-row :gutter="10" class="mb8" v-if="viewStatus">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button
@@ -144,7 +145,6 @@
style="width: 150px" style="width: 150px"
:disabled="data.isEdit" :disabled="data.isEdit"
> >
<!-- purchase_type -->
<el-option <el-option
v-for="supplier in sourceTypeListOptions" v-for="supplier in sourceTypeListOptions"
:key="supplier.id" :key="supplier.id"
@@ -198,7 +198,6 @@
style="width: 150px" style="width: 150px"
:disabled="data.isEdit" :disabled="data.isEdit"
> >
<!-- purchase_type -->
<el-option <el-option
v-for="supplier in purposeTypeListOptions" v-for="supplier in purposeTypeListOptions"
:key="supplier.id" :key="supplier.id"
@@ -208,6 +207,21 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- purposeLocationStoreName --> <!-- purposeLocationStoreName -->
<el-form-item label="目的货位:" prop="purposeLocationStoreId">
<el-select
v-model="receiptHeaderForm.purposeLocationStoreId"
placeholder="请选择目的货位"
style="width: 150px"
>
<el-option
v-for="supplier in purposeLocationStoreIdListOptions"
:key="supplier.value"
:label="supplier.label"
:value="supplier.value"
/>
</el-select>
</el-form-item>
<!-- purposeLocationStoreName -->
<el-form-item label="目的货位:" prop="purposeLocationStoreId"> <el-form-item label="目的货位:" prop="purposeLocationStoreId">
<el-select <el-select
v-model="receiptHeaderForm.purposeLocationStoreId" v-model="receiptHeaderForm.purposeLocationStoreId"
@@ -769,6 +783,7 @@ import useUserStore from "@/store/modules/user";
import { useStore } from '@/store/store'; import { useStore } from '@/store/store';
import useTagsViewStore from '@/store/modules/tagsView'; import useTagsViewStore from '@/store/modules/tagsView';
import TraceNoDialog from '@/components/OpenHis/TraceNoDialog/index.vue' import TraceNoDialog from '@/components/OpenHis/TraceNoDialog/index.vue'
import { nextTick } from 'vue';
const tagsViewStore = useTagsViewStore(); const tagsViewStore = useTagsViewStore();
const store = useStore(); const store = useStore();
const router = useRouter(); const router = useRouter();
@@ -831,6 +846,11 @@ const forms = reactive({
const receiptHeaderForm = reactive({ const receiptHeaderForm = reactive({
busNo: undefined, busNo: undefined,
occurrence_time: formatDate(new Date()), occurrence_time: formatDate(new Date()),
sourceTypeEnum: 16, // 默认药房类型
purposeTypeEnum: 16, // 默认药房类型
sourceLocationId: '',
purposeLocationId: '',
medicationType: '',
}); });
const data = reactive({ const data = reactive({
@@ -886,8 +906,18 @@ const data = reactive({
}); });
const { queryParams, rules, tableRules } = toRefs(data); const { queryParams, rules, tableRules } = toRefs(data);
const purposeTypeListOptions = ref(undefined); const purposeTypeListOptions = ref([]);
const sourceTypeListOptions = ref(undefined); const sourceTypeListOptions = ref([]);
// 立即初始化模拟仓库数据
const mockWarehouses = [
{ id: '1', name: '药房1号' },
{ id: '2', name: '药房2号' },
{ id: '3', name: '药房3号' },
{ id: '4', name: '中心药房' },
{ id: '5', name: '门诊药房' }
];
// 设置模拟数据到仓库列表选项
// 通过onMounted中调用的handleChangeSourceTypeEnum和handleChangePurposeTypeEnum函数动态获取仓库数据
const sourceLocationStoreIdListOptions = ref(undefined); const sourceLocationStoreIdListOptions = ref(undefined);
const purposeLocationStoreIdListOptions = ref(undefined); const purposeLocationStoreIdListOptions = ref(undefined);
const categoryListOptions = ref(undefined); const categoryListOptions = ref(undefined);
@@ -941,6 +971,9 @@ watch(
// 挂载时绑定事件 // 挂载时绑定事件
onMounted(() => { onMounted(() => {
document.addEventListener("click", handleClickOutside); document.addEventListener("click", handleClickOutside);
// 初始化仓库数据,默认加载药房类型(16)的仓库
handleChangeSourceTypeEnum(receiptHeaderForm.sourceTypeEnum, 1);
handleChangePurposeTypeEnum(receiptHeaderForm.purposeTypeEnum, 1);
}); });
// 卸载时移除事件 // 卸载时移除事件
@@ -975,11 +1008,11 @@ function addNewRow() {
itemId: "", itemId: "",
unitCode: "", unitCode: "",
remake: "", remake: "",
// supplierId: "", supplierId: "",
purposeTypeEnum: "", purposeTypeEnum: "",
purposeLocationId: null, purposeLocationId: null,
purposeLocationStoreId: null, purposeLocationStoreId: null,
// practitionerId: "", practitionerId: "",
traceNo: "", traceNo: "",
invoiceNo: "", invoiceNo: "",
startTime: "", startTime: "",
@@ -1018,6 +1051,7 @@ function handleBlur(row, index) {
if (receiptHeaderForm.medicationType == 1) { if (receiptHeaderForm.medicationType == 1) {
row.itemTable = "med_medication_definition"; row.itemTable = "med_medication_definition";
} else { } else {
console.log('Unknown warehouse type:', value);
row.itemTable = "adm_device_definition"; row.itemTable = "adm_device_definition";
} }
} }
@@ -1206,42 +1240,66 @@ function handleLocationClick(id,purposeLocationId,itemId, index) {
} }
// 切换仓库类型获取药房/药库列表 目的仓库切换 // 切换仓库类型获取药房/药库列表 目的仓库切换
function handleChangePurposeTypeEnum(value,type) { function handleChangePurposeTypeEnum(value,type) {
if (value == 16) { console.log('handleChangePurposeTypeEnum called with value:', value, 'type:', type);
getPharmacyList().then((res) => { receiptHeaderForm.purposeLocationId = '';
purposeTypeListOptions.value = res.data; if(value === 16){ // 药房类型
if(!route.query.supplyBusNo&&!type){ getPharmacyList().then(res => {
receiptHeaderForm.purposeLocationId = '' console.log('getPharmacyList response:', res);
receiptHeaderForm.purposeLocationId1 = '' if (res && res.data) {
purposeTypeListOptions.value = res.data;
if(!type && res.data.length > 0) {
receiptHeaderForm.purposeLocationId = res.data[0].id;
}
} }
}).catch(error => {
console.error('Error fetching pharmacy list:', error);
}); });
} else if (value == 11) { } else if(value === 17){ // 药库类型
getDispensaryList().then((res) => { getDispensaryList().then(res => {
purposeTypeListOptions.value = res.data; console.log('getDispensaryList response:', res);
if(!route.query.supplyBusNo&&!type){ if (res && res.data) {
receiptHeaderForm.purposeLocationId = '' purposeTypeListOptions.value = res.data;
receiptHeaderForm.purposeLocationId1 = '' if(!type && res.data.length > 0) {
receiptHeaderForm.purposeLocationId = res.data[0].id;
}
} }
}).catch(error => {
console.error('Error fetching dispensary list:', error);
}); });
} else {
purposeTypeListOptions.value = [];
} }
} }
// 源仓库切换 // 源仓库切换
function handleChangeSourceTypeEnum(value,type) { function handleChangeSourceTypeEnum(value,type) {
if (value == 16) { console.log('handleChangeSourceTypeEnum called with value:', value, 'type:', type);
getPharmacyList().then((res) => { receiptHeaderForm.sourceLocationId = '';
sourceTypeListOptions.value = res.data; if(value === 16){ // 药房类型
if(!route.query.supplyBusNo&&!type){ getPharmacyList().then(res => {
receiptHeaderForm.sourceLocationId = '' console.log('getPharmacyList response:', res);
receiptHeaderForm.sourceLocationId1 = '' if (res && res.data) {
sourceTypeListOptions.value = res.data;
if(!type && res.data.length > 0) {
receiptHeaderForm.sourceLocationId = res.data[0].id;
}
} }
}).catch(error => {
console.error('Error fetching pharmacy list:', error);
}); });
} else if (value == 11) { } else if(value === 17){ // 药库类型
getDispensaryList().then((res) => { getDispensaryList().then(res => {
sourceTypeListOptions.value = res.data; console.log('getDispensaryList response:', res);
if(!route.query.supplyBusNo&&!type){ if (res && res.data) {
receiptHeaderForm.sourceLocationId = '' sourceTypeListOptions.value = res.data;
receiptHeaderForm.sourceLocationId1 = '' if(!type && res.data.length > 0) {
receiptHeaderForm.sourceLocationId = res.data[0].id;
}
} }
}).catch(error => {
console.error('Error fetching dispensary list:', error);
}); });
} else {
sourceTypeListOptions.value = [];
} }
} }
function editBatchTransfer(index){ function editBatchTransfer(index){
@@ -1857,8 +1915,19 @@ function getBusNoInitList() {
} }
} }
getTransferProductTypeList(); // 初始化函数
getBusNoInitList() function initComponent() {
console.log('initComponent called');
getTransferProductTypeList();
getBusNoInitList();
console.log('组件初始化完成,仓库列表数据已预先设置');
}
// 调用初始化函数
initComponent();
</script> </script>
<style scoped> <style scoped>
.custom-tree-node { .custom-tree-node {

View File

@@ -110,8 +110,7 @@ export function getCount(queryParams) {
// 获取药房列表 // 获取药房列表
export function getPharmacyList() { export function getPharmacyList() {
return request({ return request({
url: '/app-common/inventory-pharmacy-list', url: '/app-common/pharmacy-list',
// '/app-common/pharmacy-list',
method: 'get', method: 'get',
}) })
} }
@@ -119,8 +118,7 @@ export function getPharmacyList() {
// 获取药库列表 // 获取药库列表
export function getDispensaryList() { export function getDispensaryList() {
return request({ return request({
url:'/app-common/inventory-cabinet-list', url: '/app-common/cabinet-list',
// '/app-common/cabinet-list',
method: 'get', method: 'get',
}) })
} }

View File

@@ -1,5 +1,7 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<!-- 患者添加对话框组件 -->
<patient-add-dialog ref="patientAddRef" @submit="handlePatientAdded" />
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
<el-form-item label="病人名称" prop="searchKey"> <el-form-item label="病人名称" prop="searchKey">
<el-input <el-input
@@ -97,23 +99,14 @@
/> />
<!-- 添加或修改对话框 --> <!-- 添加或修改对话框 -->
<el-dialog :title="title" v-model="open" width="980px" append-to-body> <el-dialog :title="title" v-model="open" width="1000px" append-to-body>
<el-form ref="patientRef" :model="form" :rules="rules" label-width="100px"> <el-form ref="patientRef" :model="form" :rules="rules" label-width="90px" size="medium">
<el-row> <el-row :gutter="10">
<el-col :span="6"> <el-col :span="6">
<el-form-item label="姓名" prop="name"> <el-form-item label="姓名" prop="name">
<el-input v-model="form.name" clearable :disabled="isViewMode" /> <el-input v-model="form.name" clearable :disabled="isViewMode" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- <el-col :span="7">
<el-form-item label="其他姓名" prop="nameJson">
<el-input
v-model="form.nameJson"
clearable
:disabled="isViewMode"
/>
</el-form-item>
</el-col> -->
<el-col :span="6"> <el-col :span="6">
<el-form-item label="民族" prop="nationalityCode"> <el-form-item label="民族" prop="nationalityCode">
<el-select v-model="form.nationalityCode" filterable clearable :disabled="isViewMode"> <el-select v-model="form.nationalityCode" filterable clearable :disabled="isViewMode">
@@ -126,11 +119,9 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> <el-col :span="6">
<el-row>
<el-col :span="12">
<el-form-item label="性别" prop="genderEnum"> <el-form-item label="性别" prop="genderEnum">
<el-radio-group v-model="form.genderEnum" :disabled="isViewMode"> <el-radio-group v-model="form.genderEnum" :disabled="isViewMode" size="small">
<el-radio <el-radio
v-for="item in administrativegenderList" v-for="item in administrativegenderList"
:key="item.value" :key="item.value"
@@ -141,9 +132,9 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="7"> <el-col :span="6">
<el-form-item label="活动标识" prop="tempFlag"> <el-form-item label="" prop="tempFlag">
<el-radio-group v-model="form.tempFlag" :disabled="isViewMode"> <el-radio-group v-model="form.tempFlag" :disabled="isViewMode" size="small">
<el-radio v-for="item in tempFlagList" :key="item.value" :label="item.value"> <el-radio v-for="item in tempFlagList" :key="item.value" :label="item.value">
{{ item.info }} {{ item.info }}
</el-radio> </el-radio>
@@ -151,8 +142,9 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row>
<el-col :span="7"> <el-row :gutter="10">
<el-col :span="6">
<el-form-item label="证件类别" prop="typeCode"> <el-form-item label="证件类别" prop="typeCode">
<el-select <el-select
v-model="form.typeCode" v-model="form.typeCode"
@@ -177,30 +169,31 @@
<el-input v-model="form.birthDate" v-show="false" /> <el-input v-model="form.birthDate" v-show="false" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8">
<el-form-item label="年龄" prop="age">
<el-input
v-model="form.age"
:disabled="isViewMode"
@input="(value) => (form.age = value.replace(/[^0-9]/g, ''))"
>
<template #suffix></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="年龄" prop="age">
<el-input
v-model="form.age"
:disabled="isViewMode"
@input="(value) => (form.age = value.replace(/[^0-9]/g, ''))"
>
<template #suffix></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="国家编码" prop="countryCode"> <el-form-item label="国家编码" prop="countryCode">
<el-input v-model="form.countryCode" clearable :disabled="isViewMode" /> <el-input v-model="form.countryCode" clearable :disabled="isViewMode" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row>
<el-col :span="7"> <el-row :gutter="10">
<el-col :span="6">
<el-form-item label="联系方式" prop="phone"> <el-form-item label="联系方式" prop="phone">
<el-input v-model="form.phone" clearable :disabled="isViewMode" /> <el-input v-model="form.phone" clearable :disabled="isViewMode" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="6">
<el-form-item label="职业" prop="prfsEnum"> <el-form-item label="职业" prop="prfsEnum">
<el-select <el-select
v-model="form.prfsEnum" v-model="form.prfsEnum"
@@ -222,14 +215,15 @@
<el-input v-model="form.workCompany" clearable :disabled="isViewMode" /> <el-input v-model="form.workCompany" clearable :disabled="isViewMode" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> <el-col :span="6">
<el-row>
<el-col :span="7">
<el-form-item label="联系人" prop="linkName"> <el-form-item label="联系人" prop="linkName">
<el-input v-model="form.linkName" clearable :disabled="isViewMode" /> <el-input v-model="form.linkName" clearable :disabled="isViewMode" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> </el-row>
<el-row :gutter="10">
<el-col :span="6">
<el-form-item label="联系人关系" prop="linkRelationCode"> <el-form-item label="联系人关系" prop="linkRelationCode">
<el-select <el-select
v-model="form.linkRelationCode" v-model="form.linkRelationCode"
@@ -251,33 +245,7 @@
<el-input v-model="form.linkTelcom" clearable :disabled="isViewMode" /> <el-input v-model="form.linkTelcom" clearable :disabled="isViewMode" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> <el-col :span="6">
<el-row>
<el-col :span="10">
<el-form-item label="地址选择" prop="addressSelect">
<!-- <RegionFullGroup v-model="form.addressSelectvalue" @change="handleChangeAddress" :disabled="isViewMode"/> -->
<el-cascader
:options="options"
:props="{ checkStrictly: true, value: 'code', label: 'name' }"
v-model="selectedOptions"
@change="handleChange"
:disabled="isViewMode"
>
<template #default="{ node, data }">
<span>{{ data.name }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="详细地址" prop="address">
<el-input v-model="form.address" clearable :disabled="isViewMode" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="10">
<el-form-item label="血型ABO" prop="bloodAbo"> <el-form-item label="血型ABO" prop="bloodAbo">
<el-select <el-select
v-model="form.bloodAbo" v-model="form.bloodAbo"
@@ -294,7 +262,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="6">
<el-form-item label="血型RH" prop="bloodRh"> <el-form-item label="血型RH" prop="bloodRh">
<el-select <el-select
v-model="form.bloodRh" v-model="form.bloodRh"
@@ -312,8 +280,31 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row>
<el-col :span="10"> <el-row :gutter="10">
<el-col :span="8">
<el-form-item label="地址选择" prop="addressSelect">
<el-cascader
:options="options"
:props="{ checkStrictly: true, value: 'code', label: 'name' }"
v-model="selectedOptions"
@change="handleChange"
:disabled="isViewMode"
size="small"
>
<template #default="{ node, data }">
<span>{{ data.name }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="详细地址" prop="address">
<el-input v-model="form.address" clearable :disabled="isViewMode" />
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="婚姻状态" prop="maritalStatusEnum"> <el-form-item label="婚姻状态" prop="maritalStatusEnum">
<el-select <el-select
v-model="form.maritalStatusEnum" v-model="form.maritalStatusEnum"
@@ -330,15 +321,16 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="4">
<el-form-item label="死亡时间" prop="deceasedDate"> <el-form-item label="死亡时间" prop="deceasedDate">
<el-date-picker <el-date-picker
v-model="form.deceasedDate" v-model="form.deceasedDate"
type="datetime" type="datetime"
placeholder="请选择时间" placeholder="时间"
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
:disabled="isViewMode" :disabled="isViewMode"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
size="small"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
@@ -356,14 +348,17 @@
<script setup name="patientManagement"> <script setup name="patientManagement">
import pcas from 'china-division/dist/pcas-code.json'; import pcas from 'china-division/dist/pcas-code.json';
import { ref, computed } from 'vue'; import { ref, computed, onMounted, toRefs, getCurrentInstance, reactive, watch } from 'vue';
import { addPatient, listPatient, updatePatient, lists } from './component/api'; import { addPatient, listPatient, updatePatient, lists } from './component/api';
import PatientAddDialog from '@/views/charge/outpatientregistration/components/patientAddDialog';
const showSearch = ref(true); const showSearch = ref(true);
const open = ref(false); const open = ref(false);
const title = ref(''); const title = ref('');
const total = ref(); const total = ref();
const patientList = ref([]); const patientList = ref([]);
// 患者添加对话框组件的引用
const patientAddRef = ref(null);
const maritalstatusList = ref([]); //婚姻 const maritalstatusList = ref([]); //婚姻
const occupationtypeList = ref([]); //职业 const occupationtypeList = ref([]); //职业
const administrativegenderList = ref([]); //性别 const administrativegenderList = ref([]); //性别
@@ -407,7 +402,7 @@ const data = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
searchKey: undefined, searchKey: undefined,
name: undefined, name: undefined
}, },
rules: { rules: {
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }], name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
@@ -522,6 +517,12 @@ function reset() {
}; };
proxy.resetForm('patientRef'); proxy.resetForm('patientRef');
} }
/** 处理患者添加后的回调 */
function handlePatientAdded(patientData) {
// 患者添加成功后刷新列表
getList();
}
/** 搜索按钮操作 */ /** 搜索按钮操作 */
function handleQuery() { function handleQuery() {
queryParams.value.pageNo = 1; queryParams.value.pageNo = 1;
@@ -529,7 +530,6 @@ function handleQuery() {
dateRange.value && dateRange.value.length == 2 ? dateRange.value[0] : ''; dateRange.value && dateRange.value.length == 2 ? dateRange.value[0] : '';
queryParams.value.createTimeETime = queryParams.value.createTimeETime =
dateRange.value && dateRange.value.length == 2 ? dateRange.value[1] : ''; dateRange.value && dateRange.value.length == 2 ? dateRange.value[1] : '';
getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
function resetQuery() { function resetQuery() {
@@ -539,11 +539,7 @@ function resetQuery() {
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
function handleAdd() { function handleAdd() {
reset(); patientAddRef.value.show(); // 使用与门诊挂号一致的方式显示新增患者组件
selectedOptions.value = [];
isViewMode.value = false;
open.value = true;
title.value = '新增患者';
} }
//查看按钮 //查看按钮
function handleSee(row) { function handleSee(row) {
@@ -651,7 +647,10 @@ function submitForm() {
} }
}); });
} }
getList();
onMounted(() => {
getList();
});
</script> </script>
<style> <style>

View File

@@ -165,7 +165,8 @@
"widthHeightSync": false, "widthHeightSync": false,
"qrCodeLevel": 0, "qrCodeLevel": 0,
"fontSize": 9, "fontSize": 9,
"field": "genderEnum_enumText" "field": "gender",
"formatter": "function(title,value,options,templateData,target,paperNo){ return templateData.gender || templateData.sex || templateData.genderCode_dictText || '-' }"
}, },
"printElementType": { "printElementType": {
"title": "文本", "title": "文本",
@@ -219,7 +220,8 @@
"widthHeightSync": false, "widthHeightSync": false,
"qrCodeLevel": 0, "qrCodeLevel": 0,
"fontSize": 9, "fontSize": 9,
"field": "encounterNo" "field": "visitNo",
"formatter": "function(title,value,options,templateData,target,paperNo){ return templateData.visitNo || templateData.patientNo || templateData.outpatientNo || templateData.encounterNo || '-' }"
}, },
"printElementType": { "printElementType": {
"title": "文本", "title": "文本",
@@ -255,7 +257,8 @@
"widthHeightSync": false, "widthHeightSync": false,
"qrCodeLevel": 0, "qrCodeLevel": 0,
"fontSize": 9, "fontSize": 9,
"field": "conditionName" "field": "diagnosis",
"formatter": "function(title,value,options,templateData,target,paperNo){ return templateData.diagnosis || templateData.diagnosisName || templateData.diagnose || templateData.conditionName || '-' }"
}, },
"printElementType": { "printElementType": {
"title": "文本", "title": "文本",
@@ -273,7 +276,8 @@
"widthHeightSync": false, "widthHeightSync": false,
"qrCodeLevel": 0, "qrCodeLevel": 0,
"fontSize": 9, "fontSize": 9,
"field": "phone" "field": "mobile",
"formatter": "function(title,value,options,templateData,target,paperNo){ return templateData.mobile || templateData.patientPhone || templateData.phone || templateData.contactPhone || '-' }"
}, },
"printElementType": { "printElementType": {
"title": "文本", "title": "文本",
@@ -291,7 +295,8 @@
"widthHeightSync": false, "widthHeightSync": false,
"qrCodeLevel": 0, "qrCodeLevel": 0,
"fontSize": 9, "fontSize": 9,
"field": "reqTime" "field": "createTime",
"formatter": "function(title,value,options,templateData,target,paperNo){ return templateData.createTime || templateData.createDate || templateData.orderDate || templateData.confirmTime || templateData.reqTime || '-' }"
}, },
"printElementType": { "printElementType": {
"title": "文本", "title": "文本",
@@ -360,7 +365,7 @@
"tableQRCodeLevel": 0, "tableQRCodeLevel": 0,
"tableSummaryTitle": true, "tableSummaryTitle": true,
"tableSummary": "", "tableSummary": "",
"renderFormatter": "function(value,row,colIndex,options,rowIndex){ return value + '<br/>' + '用法用量'; }", "renderFormatter": "function(value,row,colIndex,options,rowIndex){ return value + '<br/>' + (row.totalVolume || '') + '<br/>' + '用法用量'; }",
"styler2": "function(value,row,index,options){ return {padding: '5px 5px' }; }", "styler2": "function(value,row,index,options){ return {padding: '5px 5px' }; }",
"width": 86.5148548770295, "width": 86.5148548770295,
"field": "itemName", "field": "itemName",
@@ -462,7 +467,7 @@
"tableQRCodeLevel": 0, "tableQRCodeLevel": 0,
"tableSummaryTitle": true, "tableSummaryTitle": true,
"tableSummary": "", "tableSummary": "",
"renderFormatter": "function(value,row,colIndex,options,rowIndex){ return value + '<br/>' + '用法用量'; }", "renderFormatter": "function(value,row,colIndex,options,rowIndex){ return value + '<br/>' + (row.totalVolume || '') + '<br/>' + (row.methodCode_dictText || '用法用量'); }",
"styler2": "function(value,row,index,options){ return {padding: '5px 5px' }; }", "styler2": "function(value,row,index,options){ return {padding: '5px 5px' }; }",
"width": 86.5148548770295, "width": 86.5148548770295,
"field": "medicineName", "field": "medicineName",

View File

@@ -196,7 +196,7 @@
批量发药 批量发药
</el-button> </el-button>
<el-button type="primary" @click="handleScan()" style="margin-left: 30px"> 扫码 </el-button> <el-button type="primary" @click="handleScan()" style="margin-left: 30px"> 扫码 </el-button>
<el-button type="primary" @click="printPrescription()" style="margin-left: 30px"> <el-button type="primary" @click="previewAndPrint()" style="margin-left: 30px">
处方打印 处方打印
</el-button> </el-button>
<div style="position: absolute; top: 30px; right: 25px"> <div style="position: absolute; top: 30px; right: 25px">
@@ -398,7 +398,7 @@ import { advicePrint } from '@/api/public';
import { formatDate, formatDateStr } from '@/utils/index'; import { formatDate, formatDateStr } from '@/utils/index';
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
import TraceNoDialog from '@/components/OpenHis/TraceNoDialog/index.vue'; import TraceNoDialog from '@/components/OpenHis/TraceNoDialog/index.vue';
import { hiprint } from 'vue-plugin-hiprint'; // 使用window.hiprint不再从vue-plugin-hiprint导入
import templateJson from './components/templateJson.json'; import templateJson from './components/templateJson.json';
// import disposalTemplate from './components/disposalTemplate.json'; // import disposalTemplate from './components/disposalTemplate.json';
import { import {
@@ -655,91 +655,518 @@ function getList() {
const printerList = ref([]); // 打印机列表 const printerList = ref([]); // 打印机列表
const selectedPrinter = ref(''); // 选中的打印机 const selectedPrinter = ref(''); // 选中的打印机
const showPrinterDialog = ref(false); // 显示打印机选择对话框 const showPrinterDialog = ref(false); // 显示打印机选择对话框
const previewData = ref(null); // 预览数据
// 修改printPrescription方法强制显示打印机选择弹窗 // 抽取获取打印数据的公共函数
async function printPrescription() { async function getPrintData() {
const selectedRows = tableRef.value.getSelectionRows(); const selectedRows = tableRef.value.getSelectionRows();
if (selectedRows.length === 0) { if (selectedRows.length === 0) {
proxy.$modal.msgWarning('未选择要打印的项目,请重新选择,打印失败'); proxy.$modal.msgWarning('未选择要打印的项目,请重新选择');
return; return null;
} }
try { try {
// 获取打印数据 // 获取打印数据
let requestIds = selectedRows.map((item) => item.requestId).join(','); let requestIds = selectedRows.map((item) => item.requestId).join(',');
console.log('打印请求ID:', requestIds);
advicePrint({ requestIds: requestIds, isPrescription: '1' }).then((res) => { const res = await advicePrint({ requestIds: requestIds, isPrescription: '1' });
const groupedRows = {}; console.log('打印数据获取结果:', res);
res.data.adviceItemList.forEach((row) => {
const prescriptionNo = row.prescriptionNo; if (!res || !res.data || !res.data.adviceItemList) {
if (!groupedRows[prescriptionNo]) { proxy.$modal.msgError('获取打印数据失败:数据格式不正确');
groupedRows[prescriptionNo] = []; return null;
} }
row.contractName = res.data.contractName;
groupedRows[prescriptionNo].push(row); const groupedRows = {};
}); res.data.adviceItemList.forEach((row) => {
const prescriptionNo = row.prescriptionNo;
if (!groupedRows[prescriptionNo]) {
groupedRows[prescriptionNo] = [];
}
row.contractName = res.data.contractName;
groupedRows[prescriptionNo].push(row);
});
const groupedArray = Object.values(groupedRows); const groupedArray = Object.values(groupedRows);
const result = []; const result = [];
groupedArray.forEach((item) => { groupedArray.forEach((item) => {
if (item && item.length > 0) {
// 从处方列表的第一个条目提取处方信息
const firstItem = item[0];
const total = item.reduce((sum, item) => { const total = item.reduce((sum, item) => {
return sum + (item.totalPrice || 0); return sum + (item.totalPrice || 0);
}, 0); }, 0);
// 转换药品列表字段,确保与打印模板匹配
const transformedPrescriptionList = item.map(medItem => ({
...medItem,
// 添加模板需要的字段映射
medicineName: medItem.itemName, // 用于模板中的medicineName字段
totalVolume: medItem.totalVolume || medItem.spec || '', // 后端返回的规格字段是totalVolume
unitPrice: medItem.unitPrice || medItem.price || 0, // 使用后端返回的unitPrice
unitCode_dictText: medItem.unitCode_dictText || medItem.unit || '', // 使用后端返回的unitCode_dictText
// 添加其他可能需要的字段
dose: medItem.dose || '',
doseUnitCode_dictText: medItem.doseUnitCode_dictText || '',
methodCode_dictText: medItem.methodCode_dictText || medItem.usage || '',
rateCode_dictText: medItem.rateCode_dictText || medItem.rateCode || ''
}));
result.push({ result.push({
...res.data, ...res.data,
// 确保处方号、患者信息等关键数据正确提取,并添加模板需要的字段映射
prescriptionNo: firstItem.prescriptionNo,
patientName: res.data.patientName || firstItem.patientName,
gender: res.data.genderEnum || firstItem.gender,
genderEnum_enumText: res.data.genderEnum_enumText || firstItem.gender || '', // 使用后端返回的性别文本
age: res.data.age || firstItem.age,
adviceDate: res.data.reqTime || firstItem.adviceDate || firstItem.createTime || new Date().toLocaleDateString(),
reqTime: res.data.reqTime || firstItem.adviceDate || firstItem.createTime || new Date().toLocaleDateString(), // 开具日期字段映射
doctorName: res.data.doctorName || firstItem.doctorName,
medTotalAmount: total, medTotalAmount: total,
prescriptionList: item, // 添加模板需要的其他字段
encounterNo: res.data.encounterNo || firstItem.encounterNo || '', // 门诊号
departmentName: res.data.departmentName || firstItem.departmentName || '', // 科室
conditionName: res.data.conditionName || firstItem.conditionName || '', // 临床诊断
phone: res.data.phone || firstItem.phone || '', // 电话
contractName: res.data.contractName || firstItem.contractName || '', // 医疗机构
prescriptionList: transformedPrescriptionList, // 使用转换后的药品列表
}); });
});
// 获取打印机列表
const printers = getPrinterList();
if (printers.length === 0) {
proxy.$modal.msgWarning('未检测到可用打印机');
return;
} }
// 强制显示打印机选择对话框,无论是否有缓存
// 设置打印机列表
printerList.value = printers;
// 显示对话框
showPrinterDialog.value = true;
// 保存当前打印数据到临时变量
window.currentPrintData = result;
}); });
console.log('打印数据准备完成:', result);
return result;
} catch (error) { } catch (error) {
console.error('打印过程出错:', error); console.error('获取打印数据失败:', error);
proxy.$modal.msgError('打印失败: ' + error.message); proxy.$modal.msgError('获取打印数据失败: ' + error.message);
return null;
}
}
// 合并预览和打印功能的主函数
async function previewAndPrint() {
try {
const result = await getPrintData();
if (!result) return;
// 保存预览数据
previewData.value = result;
console.log('直接执行处方打印预览');
// 构建预览HTML保持原有格式
let previewHtml = '';
// 为每个处方生成预览内容
result.forEach((prescription, index) => {
// 确保处方数据存在
if (!prescription) return;
// 从处方获取药品列表,确保是数组
const prescriptionList = Array.isArray(prescription.prescriptionList) ? prescription.prescriptionList : [];
previewHtml += '<div class="prescription-container">';
previewHtml += '<h3>处方 ' + (index + 1) + '</h3>';
previewHtml += '<div class="prescription-info">';
previewHtml += '<p><strong>处方号:</strong> ' + (prescription.prescriptionNo || 'N/A') + '</p>';
previewHtml += '<p><strong>患者姓名:</strong> ' + (prescription.patientName || 'N/A') + '</p>';
previewHtml += '<p><strong>性别:</strong> ' + (prescription.genderEnum_enumText || prescription.gender || 'N/A') + '</p>';
previewHtml += '<p><strong>年龄:</strong> ' + (prescription.age || 'N/A') + '</p>';
previewHtml += '<p><strong>开具日期:</strong> ' + (prescription.reqTime || prescription.adviceDate || prescription.createTime || new Date().toLocaleDateString()) + '</p>';
previewHtml += '<p><strong>医生:</strong> ' + (prescription.doctorName || 'N/A') + '</p>';
// 添加额外的处方信息
if (prescription.encounterNo) previewHtml += '<p><strong>门诊号:</strong> ' + prescription.encounterNo + '</p>';
if (prescription.departmentName) previewHtml += '<p><strong>科室:</strong> ' + prescription.departmentName + '</p>';
if (prescription.conditionName) previewHtml += '<p><strong>临床诊断:</strong> ' + prescription.conditionName + '</p>';
if (prescription.phone) previewHtml += '<p><strong>电话:</strong> ' + prescription.phone + '</p>';
previewHtml += '</div>';
previewHtml += '<table class="prescription-table">';
previewHtml += '<thead>';
previewHtml += '<tr>';
previewHtml += '<th>药品名称</th>';
previewHtml += '<th>规格</th>';
previewHtml += '<th>单价</th>';
previewHtml += '<th>数量</th>';
previewHtml += '<th>金额</th>';
previewHtml += '<th>费用等级</th>';
previewHtml += '<th>用法用量</th>';
previewHtml += '</tr>';
previewHtml += '</thead>';
previewHtml += '<tbody>';
// 添加药品列表
if (prescriptionList.length > 0) {
prescriptionList.forEach((item) => {
previewHtml += '<tr>';
// 使用映射后的字段名,确保与打印模板一致
previewHtml += '<td>' + (item.medicineName || item.itemName || 'N/A') + '</td>';
previewHtml += '<td>' + (item.totalVolume || item.spec || '') + '</td>';
previewHtml += '<td>' + (item.unitPrice || item.price || '0.00') + '</td>';
previewHtml += '<td>' + (item.quantity || 'N/A') + ' ' + (item.unitCode_dictText || item.unit || '') + '</td>';
previewHtml += '<td>' + (item.totalPrice || '0.00') + '</td>';
previewHtml += '<td>' + (item.contractName || 'N/A') + '</td>';
// 组合用法用量信息
const usageInfo = [];
if (item.dose && item.doseUnitCode_dictText) usageInfo.push(item.dose + item.doseUnitCode_dictText);
if (item.methodCode_dictText || item.usage) usageInfo.push(item.methodCode_dictText || item.usage);
if (item.frequency) usageInfo.push(item.frequency);
previewHtml += '<td>' + usageInfo.join('<br/>') + '</td>';
previewHtml += '</tr>';
});
} else {
previewHtml += '<tr><td colspan="7" style="text-align: center;">暂无药品信息</td></tr>';
}
previewHtml += '</tbody>';
previewHtml += '<tfoot>';
previewHtml += '<tr>';
previewHtml += '<td colspan="6" style="text-align: right; font-weight: bold;">总计:</td>';
previewHtml += '<td style="font-weight: bold;">' + (prescription.medTotalAmount || '0.00') + '</td>';
previewHtml += '</tr>';
previewHtml += '</tfoot>';
previewHtml += '</table>';
previewHtml += '<div class="prescription-footer">';
previewHtml += '<p><strong>医疗机构:</strong> ' + (prescription.contractName || 'N/A') + '</p>';
previewHtml += '</div>';
previewHtml += '</div>';
previewHtml += '<hr class="prescription-separator">';
});
// 创建临时打印容器(不可见)
const printContainer = document.createElement('div');
printContainer.style.position = 'fixed';
printContainer.style.left = '-9999px';
printContainer.style.top = '-9999px';
printContainer.style.width = '800px';
// 添加打印样式,包含患者信息和药品信息
const style = document.createElement('style');
style.textContent = `
/* 确保打印样式应用于患者信息和药品信息 */
.print-content {
font-family: Arial, sans-serif;
font-size: 12px;
}
/* 患者信息表格样式 */
.patient-info-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 15px;
}
.patient-info-table td {
padding: 4px 8px;
border: 1px solid #ddd;
font-size: 12px;
}
.patient-info-table .label {
font-weight: bold;
background-color: #f9f9f9;
}
/* 药品表格样式 */
.medicine-table {
width: 100%;
border-collapse: collapse;
}
.medicine-table th,
.medicine-table td {
border: 1px solid #000;
padding: 8px;
text-align: left;
}
.medicine-table th {
background: #f2f2f2;
font-weight: bold;
}
.page-break {
page-break-after: always;
}
/* 隐藏不需要打印的元素 */
@media print {
body > *:not(#print-container) {
display: none !important;
}
#print-container {
position: static !important;
width: 100% !important;
}
}
`;
document.head.appendChild(style);
// 设置容器ID以便CSS选择
printContainer.id = 'print-container';
// 构建包含患者信息和药品信息的打印内容
let printHtml = '<div class="print-content">';
result.forEach((prescription, index) => {
if (!prescription) return;
const prescriptionList = Array.isArray(prescription.prescriptionList) ? prescription.prescriptionList : [];
printHtml += '<div class="prescription-medicines">';
printHtml += '<h4>处方 ' + (index + 1) + '</h4>';
// 添加患者信息表格
printHtml += '<table class="patient-info-table">';
printHtml += '<tr>';
printHtml += '<td class="label">姓名:</td>';
printHtml += '<td>' + (prescription.patientName || '-') + '</td>';
printHtml += '<td class="label">性别:</td>';
printHtml += '<td>' + (prescription.genderEnum_enumText || prescription.gender || '-') + '</td>';
printHtml += '<td class="label">年龄:</td>';
printHtml += '<td>' + (prescription.age || '-') + '</td>';
printHtml += '<td class="label">处方号:</td>';
printHtml += '<td>' + (prescription.prescriptionNo || '-') + '</td>';
printHtml += '</tr>';
printHtml += '<tr>';
printHtml += '<td class="label">门诊号:</td>';
printHtml += '<td>' + (prescription.encounterNo || '-') + '</td>';
printHtml += '<td class="label">开具日期:</td>';
printHtml += '<td>' + (prescription.reqTime || prescription.adviceDate || prescription.createTime || '-') + '</td>';
printHtml += '<td class="label">医生:</td>';
printHtml += '<td>' + (prescription.doctorName || '-') + '</td>';
printHtml += '<td class="label">科室:</td>';
printHtml += '<td>' + (prescription.departmentName || '-') + '</td>';
printHtml += '</tr>';
printHtml += '<tr>';
printHtml += '<td class="label">临床诊断:</td>';
printHtml += '<td colspan="3">' + (prescription.conditionName || '-') + '</td>';
printHtml += '<td class="label">电话:</td>';
printHtml += '<td colspan="3">' + (prescription.phone || prescription.mobile || prescription.patientPhone || '-') + '</td>';
printHtml += '</tr>';
printHtml += '</table>';
// 添加药品信息表格
if (prescriptionList.length > 0) {
printHtml += '<table class="medicine-table">';
printHtml += '<thead>';
printHtml += '<tr>';
printHtml += '<th>药品名称</th>';
printHtml += '<th>规格</th>';
printHtml += '<th>单价</th>';
printHtml += '<th>数量</th>';
printHtml += '<th>金额</th>';
printHtml += '<th>用法用量</th>';
printHtml += '</tr>';
printHtml += '</thead>';
printHtml += '<tbody>';
prescriptionList.forEach((item) => {
printHtml += '<tr>';
printHtml += '<td>' + (item.medicineName || item.itemName || 'N/A') + '</td>';
printHtml += '<td>' + (item.totalVolume || item.spec || '') + '</td>';
printHtml += '<td>' + (item.unitPrice || item.price || '0.00') + '</td>';
printHtml += '<td>' + (item.quantity || 'N/A') + ' ' + (item.unitCode_dictText || item.unit || '') + '</td>';
printHtml += '<td>' + (item.totalPrice || '0.00') + '</td>';
// 组合用法用量信息
const usageInfo = [];
if (item.dose && item.doseUnitCode_dictText) usageInfo.push(item.dose + item.doseUnitCode_dictText);
if (item.methodCode_dictText || item.usage) usageInfo.push(item.methodCode_dictText || item.usage);
if (item.frequency) usageInfo.push(item.frequency);
printHtml += '<td>' + usageInfo.join('<br/>') + '</td>';
printHtml += '</tr>';
});
printHtml += '</tbody>';
printHtml += '<tfoot>';
printHtml += '<tr>';
printHtml += '<td colspan="4" style="text-align: right; font-weight: bold;">总计:</td>';
printHtml += '<td colspan="2" style="font-weight: bold;">' + (prescription.medTotalAmount || '0.00') + '</td>';
printHtml += '</tr>';
printHtml += '</tfoot>';
printHtml += '</table>';
} else {
printHtml += '<p>暂无药品信息</p>';
}
// 分页符(最后一个处方除外)
if (index < result.length - 1) {
printHtml += '<div class="page-break"></div>';
}
printHtml += '</div>';
});
printHtml += '</div>';
printContainer.innerHTML = printHtml;
// 添加到文档
document.body.appendChild(printContainer);
// 触发打印预览
window.print();
// 打印完成后移除临时容器和样式
const cleanup = () => {
// 移除临时容器
if (printContainer && document.body.contains(printContainer)) {
document.body.removeChild(printContainer);
}
// 移除临时样式
if (style && document.head.contains(style)) {
document.head.removeChild(style);
}
// 移除事件监听器
window.removeEventListener('afterprint', cleanup);
};
// 监听打印完成事件
window.addEventListener('afterprint', cleanup);
// 添加超时清理,防止打印预览取消时资源未释放
setTimeout(() => {
cleanup();
}, 30000); // 30秒后自动清理
proxy.$modal.msgSuccess('打印预览已启动');
// 同时处理后台打印逻辑
setTimeout(() => {
handleBackgroundPrint(result);
}, 500);
} catch (error) {
console.error('预览和打印过程出错:', error);
proxy.$modal.msgError('操作失败: ' + error.message);
}
}
// 后台打印逻辑,复用原有的打印功能
function handleBackgroundPrint(result) {
try {
// 获取打印机列表
const printers = getPrinterList();
console.log('获取到的打印机列表:', printers);
if (printers.length === 0) {
console.log('未检测到可用打印机,仅提供预览功能');
return;
}
// 设置打印机列表
printerList.value = printers;
// 检查是否有缓存的打印机
const cachedPrinter = getCachedPrinter();
console.log('缓存的打印机:', cachedPrinter);
if (cachedPrinter && printers.some(printer => printer.name === cachedPrinter)) {
// 如果有缓存的打印机,直接使用
selectedPrinter.value = cachedPrinter;
confirmPrinter(result);
} else {
// 否则不自动显示打印机选择对话框,让用户在预览页面手动选择打印
console.log('无缓存打印机,用户可在预览页面手动打印');
}
} catch (error) {
console.error('后台打印过程出错:', error);
// 不显示错误提示,以免干扰用户在预览页面的操作
} }
} }
// 使用保存的打印数据执行打印 // 使用保存的打印数据执行打印
function confirmPrinter() { function confirmPrinter(printData) {
console.log('开始打印,传入数据:', printData);
console.log('window临时数据:', window.currentPrintData);
// 如果没有传入数据尝试从window临时变量获取
let data = printData || window.currentPrintData;
if (!selectedPrinter.value) { if (!selectedPrinter.value) {
proxy.$modal.msgWarning('请选择打印机'); proxy.$modal.msgWarning('请选择打印机');
return; return;
} }
if (!data || !Array.isArray(data) || data.length === 0) {
proxy.$modal.msgError('没有找到有效的打印数据');
showPrinterDialog.value = false;
return;
}
if (!templateJson) {
proxy.$modal.msgError('打印模板加载失败');
showPrinterDialog.value = false;
return;
}
// 确保数据字段映射正确,特别是规格字段
data = data.map(prescription => {
const mappedPrescription = { ...prescription };
// 确保药品列表字段映射正确
if (Array.isArray(prescription.prescriptionList)) {
mappedPrescription.prescriptionList = prescription.prescriptionList.map(med => ({
...med,
// 确保规格字段正确映射优先使用totalVolume
totalVolume: med.totalVolume || med.spec || '',
// 确保其他关键字段存在
medicineName: med.medicineName || med.itemName || '',
unitPrice: med.unitPrice || med.price || 0,
unitCode_dictText: med.unitCode_dictText || med.unit || '',
methodCode_dictText: med.methodCode_dictText || med.usage || ''
}));
}
return mappedPrescription;
});
console.log('处理后的打印数据:', data);
console.log('准备执行打印,打印机:', selectedPrinter.value);
// 使用his.js中的savePrinterToCache保存选择 // 使用his.js中的savePrinterToCache保存选择
savePrinterToCache(selectedPrinter.value); savePrinterToCache(selectedPrinter.value);
// 使用his.js中的executePrint方法执行打印 try {
if (window.currentPrintData) { // 检查hiprint是否可用
executePrint(window.currentPrintData, templateJson, selectedPrinter.value) if (!window.hiprint) {
.then(() => { throw new Error('打印插件未正确加载');
showPrinterDialog.value = false; }
// 清除临时数据
window.currentPrintData = null; // 直接使用hiprint执行打印确保功能正常
}) const hiprintTemplate = new window.hiprint.PrintTemplate({ template: templateJson });
.catch((error) => { const printOptions = {
proxy.$modal.msgError(error.message || '打印失败'); title: '处方打印',
showPrinterDialog.value = false; height: 210,
window.currentPrintData = null; width: 148,
}); printer: selectedPrinter.value
};
// 打印成功回调
hiprintTemplate.on('printSuccess', function (e) {
console.log('打印成功:', e);
proxy.$modal.msgSuccess('打印成功');
showPrinterDialog.value = false;
// 清除临时数据
window.currentPrintData = null;
});
// 打印失败回调
hiprintTemplate.on('printError', function (e) {
console.error('打印失败:', e);
proxy.$modal.msgError('打印失败,请检查打印机连接');
showPrinterDialog.value = false;
window.currentPrintData = null;
});
// 执行打印
console.log('调用hiprint.print2方法');
hiprintTemplate.print2(data, printOptions);
} catch (error) {
console.error('打印执行出错:', error);
proxy.$modal.msgError('打印执行失败: ' + error.message);
showPrinterDialog.value = false;
window.currentPrintData = null;
} }
} }
// 取消打印机选择
function cancelPrinter() {
showPrinterDialog.value = false;
window.currentPrintData = null;
}
/** 重置按钮操作 */ /** 重置按钮操作 */
function resetQuery() { function resetQuery() {
setDefaultDateRange(); setDefaultDateRange();

View File

@@ -69,7 +69,11 @@ import bindUser from "./bindUser";
import { getTenantUserPage, unbindTenantUser } from "@/api/system/tenant"; import { getTenantUserPage, unbindTenantUser } from "@/api/system/tenant";
// 在 <script setup> 顶部添加导入 // 在 <script setup> 顶部添加导入
import { ref, reactive, onMounted } from "vue"; // 补充 onMounted 导入 import { ref, reactive, onMounted } from "vue"; // 补充 onMounted 导入
import { useRoute, useRouter } from "vue-router"; // 添加 useRouter 导入
const route = useRoute(); const route = useRoute();
const router = useRouter(); // 创建 router 实例
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const { sys_normal_disable } = proxy.useDict("sys_normal_disable"); const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
@@ -100,8 +104,14 @@ function getList() {
} }
// 返回按钮 // 返回按钮
function handleClose() { function handleClose() {
const obj = { path: "/system/basicmanage/tenant" }; /* const obj = { path: "/system/basicmanage/tenant" };
proxy.$tab.closeOpenPage(obj); proxy.$tab.closeOpenPage(obj); 目前先注释掉改成返回上一级页面*/
// 先关闭当前标签页
const obj={ path: "/system/basicmanage/tenant" };
proxy.$tab.closeOpenPage(route).then(() => {
// 然后返回上一个页面
router.go(-1);
});
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
function handleQuery() { function handleQuery() {

View File

@@ -614,7 +614,7 @@ const deptName = ref('');
const deptOptions = ref(undefined); const deptOptions = ref(undefined);
const wardListOptions = ref(undefined); const wardListOptions = ref(undefined);
const locationOptions = ref([]); const locationOptions = ref([]);
const initPassword = ref(undefined); const initPassword = ref('123456');
const postOptions = ref([]); const postOptions = ref([]);
const roleOptions = ref([]); const roleOptions = ref([]);
const doctorShow = ref(true); const doctorShow = ref(true);
@@ -904,7 +904,8 @@ function handleAdd() {
locationAdminShow.value = true; locationAdminShow.value = true;
pharmacyShow.value = true; pharmacyShow.value = true;
title.value = '添加用户'; title.value = '添加用户';
form.value.password = initPassword.value; // 强制赋值初始密码(覆盖原逻辑,确保一定生效)
form.value.password = initPassword.value; // 这里会将123456赋值给密码输入框
}); });
} }
/** 修改按钮操作 */ /** 修改按钮操作 */

View File

@@ -52,6 +52,11 @@ export default defineConfig (({mode, command}) => {
target: 'http://localhost:5000', target: 'http://localhost:5000',
changeOrigin: true, changeOrigin: true,
rewrite: p => p.replace (/^\/ybplugin/, ''), rewrite: p => p.replace (/^\/ybplugin/, ''),
},
'/prod-api': {
target: 'http://localhost:18080/openhis',
changeOrigin: true,
rewrite: p => p.replace (/^\/prod-api/, ''),
} }
}, },
}, },