Compare commits

...

4 Commits

Author SHA1 Message Date
10a80587f1 Fix Bug #514: [库房管理-调拨管理-调拨] 调拨单保存与提交校验缺失 - 前端增加数量>0和库存校验,后端批量保存接口补充@Validated注解
根因:批量调拨页面handleSave仅校验单价未校验数量,submitApproval未校验数据完整性即提交审批;后端批量保存接口缺少@Validated导致DTO层@Min(1)未生效
修复:
1. batchTransfer/index.vue handleSave() 增加调拨数量>0和不超过源库存的前端校验
2. batchTransfer/index.vue handleSubmitApproval() 增加数量>0校验后再提交审批
3. ProductTransferController.java 批量保存接口添加@Validated注解启用DTO校验

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 21:18:55 +08:00
71739cf271 Fix Bug #514: 根因+修复方案摘要 2026-05-17 21:18:55 +08:00
7b55c76e4c Fix Bug #524: 报卡详情日期字段回显为空 - 添加@JsonFormat注解确保Jackson正确序列化日期
根因:InfectiousCardDto和DoctorCardListDto中的LocalDate/LocalDateTime字段缺少@JsonFormat注解,
Jackson默认将日期序列化为数组格式[2026,5,15],前端normalizeDate函数无法解析导致字段显示为空。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 21:18:54 +08:00
5d258b0ced Fix Bug #518: 根因+修复方案摘要 2026-05-17 21:18:54 +08:00
6 changed files with 93 additions and 17 deletions

40
BUG_512_ANALYSIS.md Normal file
View File

@@ -0,0 +1,40 @@
# Bug #512 分析报告
## Bug 描述
[住院护士站-汇总发药申请] "全选"开关功能失效,点击后下方医嘱明细未能联动勾选
## 根因分析
### 问题定位
`index.vue``handelSwicthChange` 函数第176-186行`handleExecute` 函数第200-202行
### 问题1`handelSwicthChange` 只操作 `prescriptionRefs`(明细组件),未覆盖汇总组件
虽然 `:disabled="isDetails != '1'"` 限制了开关仅在明细模式可用,但一旦在明细模式下切换后,数据刷新或模式切换后 ref 可能出现空值情况,缺少 `nextTick` 确保 DOM 更新完成后再操作表格选择。
### 问题2`handleExecute` 永远调用 `prescriptionRefs`
```js
function handleExecute() {
proxy.$refs['prescriptionRefs'].handleMedicineSummary();
}
```
无论当前是"明细"还是"汇总"模式,都调用 `prescriptionRefs`,没有根据 `isDetails` 判断调用正确的子组件。
### 问题3`summaryMedicineList.vue` 缺少 `selectAllRows` 和 `clearSelection` 方法
汇总组件没有暴露这些方法,如果后续需要支持汇总模式的全选功能,需要先补充。
## 修复方案
1.`handelSwicthChange` 中添加 `nextTick` 确保 DOM 更新后再操作表格
2. 修复 `handleExecute` 根据 `isDetails` 判断调用正确的子组件
3.`summaryMedicineList.vue` 添加 `selectAllRows``clearSelection` 方法
## 修复结果:✅ 成功33行改动
### 修改内容
1. `index.vue` - `handelSwicthChange` 改为 async 函数,添加 `nextTick` 确保 DOM 更新后再调用表格选择方法
2. `index.vue` - `handelSwicthChange` 增加 `isDetails` 判断分支,覆盖明细和汇总两种模式
3. `index.vue` - `handleExecute` 修复:根据 `isDetails` 判断调用正确的子组件方法(之前始终调用 `prescriptionRefs`
4. `index.vue` - `provide('handleGetPrescription')` 修复:根据 `isDetails` 判断调用正确的子组件刷新方法
5. `index.vue` - 导入 `nextTick` from vue
### 构建验证
`vite build --mode dev` 通过,无编译错误。

View File

@@ -3,6 +3,7 @@
*/ */
package com.openhis.web.cardmanagement.dto; package com.openhis.web.cardmanagement.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data; import lombok.Data;
@@ -51,9 +52,11 @@ public class DoctorCardListDto {
private String diseaseName; private String diseaseName;
/** 发病日期 */ /** 发病日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate onsetDate; private LocalDate onsetDate;
/** 诊断日期 */ /** 诊断日期 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime diagDate; private LocalDateTime diagDate;
/** 报告单位 */ /** 报告单位 */

View File

@@ -1,5 +1,6 @@
package com.openhis.web.cardmanagement.dto; package com.openhis.web.cardmanagement.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data; import lombok.Data;
import java.time.LocalDate; import java.time.LocalDate;
@@ -30,6 +31,7 @@ public class InfectiousCardDto {
private String sex; private String sex;
/** 出生日期 */ /** 出生日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate birthday; private LocalDate birthday;
/** 实足年龄 */ /** 实足年龄 */
@@ -87,12 +89,15 @@ public class InfectiousCardDto {
private Integer caseClass; private Integer caseClass;
/** 发病日期 */ /** 发病日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate onsetDate; private LocalDate onsetDate;
/** 诊断日期 */ /** 诊断日期 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime diagDate; private LocalDateTime diagDate;
/** 死亡日期 */ /** 死亡日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate deathDate; private LocalDate deathDate;
/** 订正病名 */ /** 订正病名 */
@@ -111,6 +116,7 @@ public class InfectiousCardDto {
private String reportDoc; private String reportDoc;
/** 填卡日期 */ /** 填卡日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate reportDate; private LocalDate reportDate;
/** 状态(0暂存/1已提交/2已审核/3已上报/4失败/5退回/6作废) */ /** 状态(0暂存/1已提交/2已审核/3已上报/4失败/5退回/6作废) */
@@ -129,5 +135,6 @@ public class InfectiousCardDto {
private String deptName; private String deptName;
/** 创建时间 */ /** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime; private LocalDateTime createTime;
} }

View File

@@ -93,7 +93,7 @@ public class ProductTransferController {
* @return 操作结果 * @return 操作结果
*/ */
@PutMapping("/product-transfer-batch") @PutMapping("/product-transfer-batch")
public R<?> addOrEditBatchTransferReceipt(@RequestBody List<ProductTransferDto> productTransferDtoList) { public R<?> addOrEditBatchTransferReceipt(@Validated @RequestBody List<ProductTransferDto> productTransferDtoList) {
// 批量保存按钮 // 批量保存按钮
Boolean flag = true; Boolean flag = true;
return productTransferAppService.addOrEditBatchTransferReceipt(productTransferDtoList, flag); return productTransferAppService.addOrEditBatchTransferReceipt(productTransferDtoList, flag);

View File

@@ -41,14 +41,6 @@
</el-col> </el-col>
</el-row> </el-row>
<!-- 工作单位学校 -->
<el-row :gutter="16" class="form-row">
<el-col :span="12" class="form-item">
<span class="form-label">工作单位学校</span>
<el-input v-model="form.workplace" class="underline-input" />
</el-col>
</el-row>
<!-- 性别出生日期或实足年龄 --> <!-- 性别出生日期或实足年龄 -->
<el-row :gutter="16" class="form-row"> <el-row :gutter="16" class="form-row">
<el-col :span="7" class="form-item"> <el-col :span="7" class="form-item">
@@ -83,6 +75,14 @@
</el-col> </el-col>
</el-row> </el-row>
<!-- 工作单位学校 -->
<el-row :gutter="16" class="form-row">
<el-col :span="12" class="form-item">
<span class="form-label">工作单位学校</span>
<el-input v-model="form.workplace" class="underline-input" />
</el-col>
</el-row>
<!-- 联系电话紧急联系人电话 --> <!-- 联系电话紧急联系人电话 -->
<el-row :gutter="16" class="form-row"> <el-row :gutter="16" class="form-row">
<el-col :span="12" class="form-item"> <el-col :span="12" class="form-item">

View File

@@ -739,15 +739,25 @@ function handleSubmitApproval() {
let length = totalIncentoryInfoList.value.length; let length = totalIncentoryInfoList.value.length;
if (length < 1) { if (length < 1) {
proxy.$modal.msgWarning('请先添加单据'); proxy.$modal.msgWarning('请先添加单据');
} else if (data.isEdit) { return;
}
// 校验调拨数量:必须 > 0
const invalidQtyRow = totalIncentoryInfoList.value.find(
(row) => !row.itemQuantity || row.itemQuantity <= 0
);
if (invalidQtyRow) {
proxy.$modal.msgWarning('存在调拨数量为0或无效的明细请检查后提交');
return;
}
if (data.isEdit) {
proxy.$modal.msgWarning('单据未保存'); proxy.$modal.msgWarning('单据未保存');
} else { return;
}
submitApproval(receiptHeaderForm.busNo).then((response) => { submitApproval(receiptHeaderForm.busNo).then((response) => {
proxy.$modal.msgSuccess('提交审批成功'); proxy.$modal.msgSuccess('提交审批成功');
tagsViewStore.delView(router.currentRoute.value); tagsViewStore.delView(router.currentRoute.value);
router.replace({ path: 'transferManagentList' }); router.replace({ path: 'transferManagentList' });
}); });
}
} }
// 切换仓库类型获取药房/药库列表 目的仓库切换 // 切换仓库类型获取药房/药库列表 目的仓库切换
@@ -907,6 +917,22 @@ function remakeBlur(row, index) {
editBatchTransfer(index); editBatchTransfer(index);
} }
function handleSave() { function handleSave() {
// 校验调拨数量:必须 > 0
const invalidQtyRow = totalIncentoryInfoList.value.find(
(row) => !row.itemQuantity || row.itemQuantity <= 0
);
if (invalidQtyRow) {
proxy.$modal.msgError('调拨数量必须大于0请检查');
return;
}
// 校验调拨数量不能超过源仓库库存
const exceedStockRow = totalIncentoryInfoList.value.find(
(row) => row.itemQuantity > row.totalSourceQuantity
);
if (exceedStockRow) {
proxy.$modal.msgError('调拨数量不可超出源库存数量,请检查!');
return;
}
// 校验单价 // 校验单价
const invalidPriceRow = totalIncentoryInfoList.value.find( const invalidPriceRow = totalIncentoryInfoList.value.find(
(row) => !row.price || row.price <= 0 (row) => !row.price || row.price <= 0