fix(clinic): 修复门诊手术安排计费弹窗vxe-table布局与项目选择问题
问题:
1. vxe-table expand列40px切换格中渲染复杂编辑表单,内容溢出导致表头表体列错位
2. adviceBaseList clickRow未解构vxe-table 4.x cell-click事件对象{row},导致selectAdviceBase数据错误
3. prescriptionList数组元素替换(arr[i]={})不被vxe-table变更检测,选中项目后数据未填入input
4. 保存按钮调用formRef{index}但表单已迁出expand列,运行时抛undefined.validate异常
This commit is contained in:
@@ -263,9 +263,8 @@ const handleCurrentChange = (currentRow) => {
|
||||
currentSelectRow.value = currentRow;
|
||||
};
|
||||
|
||||
function clickRow(row, column, cell, event) {
|
||||
// cell-click 事件会传递 row, column, cell, event 四个参数
|
||||
// 确保传递的是完整的行数据
|
||||
function clickRow({ row }) {
|
||||
// vxe-table 4.x cell-click 事件参数是 { row, column, ... } 对象,需解构取 row
|
||||
if (row) {
|
||||
emit('selectAdviceBase', row);
|
||||
}
|
||||
|
||||
@@ -38,170 +38,15 @@
|
||||
max-height="650"
|
||||
:data="prescriptionList"
|
||||
:row-config="{ keyField: 'uniqueKey', expandRowKeys: expandOrder }"
|
||||
:column-config="{ resizable: true }"
|
||||
border
|
||||
auto-resize
|
||||
@cell-dblclick="clickRowDb"
|
||||
>
|
||||
<vxe-column
|
||||
type="expand"
|
||||
width="40"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-form
|
||||
:ref="'formRef' + scope.rowIndex"
|
||||
:model="scope.row"
|
||||
:rules="rowRules"
|
||||
>
|
||||
<div style="padding: 16px; background: #f8f9fa; border-radius: 8px">
|
||||
<!-- 药品类型(adviceType == 1)和耗材类型(adviceType == 2)使用相同的界面 -->
|
||||
<template v-if="scope.row.adviceType == 1 || scope.row.adviceType == 2">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 16px; gap: 16px">
|
||||
<span style="font-size: 16px; font-weight: 600">
|
||||
{{
|
||||
scope.row.adviceName +
|
||||
' ' +
|
||||
(scope.row.volume ? scope.row.volume + ' ' : '') +
|
||||
(scope.row.unitPrice ? scope.row.unitPrice + ' 元/' : '') +
|
||||
(scope.row.unitCode_dictText || '')
|
||||
}}
|
||||
</span>
|
||||
<div class="form-group">
|
||||
<!-- 库存不为空时显示批号选择 -->
|
||||
<el-select
|
||||
v-if="scope.row.stockList && scope.row.stockList.length > 0"
|
||||
v-model="scope.row.lotNumber"
|
||||
style="width: 180px; margin-right: 20px"
|
||||
placeholder="选择批号"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in scope.row.stockList"
|
||||
:key="item.lotNumber"
|
||||
:value="item.lotNumber"
|
||||
:label="
|
||||
item.locationName +
|
||||
' ' +
|
||||
'批次号: ' +
|
||||
item.lotNumber +
|
||||
' ' +
|
||||
' 库存:' +
|
||||
(item.quantity / scope.row.partPercent).toFixed(2) +
|
||||
item.unitCode_dictText +
|
||||
' 单价:' +
|
||||
item.price.toFixed(2) +
|
||||
'/' +
|
||||
item.unitCode_dictText
|
||||
"
|
||||
@click="handleNumberClick(item, scope.rowIndex)"
|
||||
/>
|
||||
</el-select>
|
||||
<!-- 库存为空时显示提示 -->
|
||||
<span
|
||||
v-else
|
||||
style="color: #EF4444; margin-right: 20px; font-size: 14px;"
|
||||
>
|
||||
无可用库存
|
||||
</span>
|
||||
<el-form-item
|
||||
label="数量:"
|
||||
prop="quantity"
|
||||
class="required-field"
|
||||
data-prop="quantity"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="scope.row.quantity"
|
||||
placeholder="数量"
|
||||
style="width: 70px"
|
||||
controls-position="right"
|
||||
:controls="false"
|
||||
@keyup.enter.prevent="handleEnter('quantity', scope.row, scope.rowIndex)"
|
||||
@input="calculateTotalPrice(scope.row, scope.rowIndex)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-select
|
||||
v-if="scope.row.unitCodeList && scope.row.unitCodeList.length > 0"
|
||||
v-model="scope.row.unitCode"
|
||||
style="width: 70px; margin-right: 20px"
|
||||
placeholder="单位"
|
||||
@change="calculateTotalAmount(scope.row, scope.rowIndex)"
|
||||
>
|
||||
<template
|
||||
v-for="item in scope.row.unitCodeList"
|
||||
:key="item.value"
|
||||
>
|
||||
<el-option
|
||||
v-if="item.type != unitMap['dose']"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</template>
|
||||
</el-select>
|
||||
<span class="total-amount">
|
||||
总金额:{{ scope.row.totalPrice ? scope.row.totalPrice + ' 元' : '0.00 元' }}
|
||||
</span>
|
||||
</div>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleSaveSign(scope.row, scope.rowIndex)"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div style="display: flex; align-items: center; margin-bottom: 16px; gap: 16px">
|
||||
<span style="font-size: 16px; font-weight: 600">
|
||||
{{
|
||||
scope.row.adviceName + ' ' + scope.row.unitPrice
|
||||
? Number(scope.row.unitPrice).toFixed(2)
|
||||
: '-' + '元'
|
||||
}}
|
||||
</span>
|
||||
<div class="form-group">
|
||||
<el-form-item
|
||||
label="执行次数:"
|
||||
prop="quantity"
|
||||
class="required-field"
|
||||
data-prop="quantity"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="scope.row.quantity"
|
||||
placeholder="执行次数"
|
||||
style="width: 100px; margin: 0 20px"
|
||||
controls-position="right"
|
||||
:controls="false"
|
||||
@keyup.enter.prevent="handleEnter('quantity', scope.row, scope.rowIndex)"
|
||||
@input="calculateTotalPrice(scope.row, scope.rowIndex)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-tree-select
|
||||
v-model="scope.row.orgId"
|
||||
clearable
|
||||
:data="organization"
|
||||
:props="{ value: 'id', label: 'name', children: 'children' }"
|
||||
value-key="id"
|
||||
check-strictly
|
||||
placeholder="请选择执行科室"
|
||||
style="min-width: 150px; width: auto;"
|
||||
class="org-select"
|
||||
/>
|
||||
<span class="total-amount">
|
||||
总金额:{{ scope.row.totalPrice ? scope.row.totalPrice + ' 元' : '0.00 元' }}
|
||||
</span>
|
||||
<span style="font-size: 16px; font-weight: 600">
|
||||
<!-- 金额: {{ scope.row.priceList[0].price }} -->
|
||||
</span>
|
||||
</div>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleSaveSign(scope.row, scope.rowIndex)"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</el-form>
|
||||
</template>
|
||||
</vxe-column>
|
||||
/>
|
||||
<vxe-column
|
||||
title=""
|
||||
align="center"
|
||||
@@ -370,6 +215,7 @@
|
||||
<vxe-column
|
||||
title="总量"
|
||||
align="center"
|
||||
width="100"
|
||||
field=""
|
||||
>
|
||||
<template #default="scope">
|
||||
@@ -383,6 +229,7 @@
|
||||
align="right"
|
||||
field=""
|
||||
header-align="center"
|
||||
width="130"
|
||||
>
|
||||
<template #default="scope">
|
||||
<span
|
||||
@@ -430,6 +277,151 @@
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
<!-- 编辑表单卡片:独立于表格,选中项目后显示在表格下方 -->
|
||||
<div
|
||||
v-if="editingRow"
|
||||
class="edit-form-card"
|
||||
>
|
||||
<el-form
|
||||
:ref="'editFormRef'"
|
||||
:model="editingRow"
|
||||
:rules="rowRules"
|
||||
>
|
||||
<div style="padding: 16px; background: #f8f9fa; border-radius: 8px">
|
||||
<template v-if="editingRow.adviceType == 1 || editingRow.adviceType == 2">
|
||||
<div style="display: flex; align-items: center; gap: 16px; flex-wrap: wrap">
|
||||
<span style="font-size: 16px; font-weight: 600">
|
||||
{{
|
||||
editingRow.adviceName +
|
||||
' ' +
|
||||
(editingRow.volume ? editingRow.volume + ' ' : '') +
|
||||
(editingRow.unitPrice ? editingRow.unitPrice + ' 元/' : '') +
|
||||
(editingRow.unitCode_dictText || '')
|
||||
}}
|
||||
</span>
|
||||
<div class="form-group">
|
||||
<el-select
|
||||
v-if="editingRow.stockList && editingRow.stockList.length > 0"
|
||||
v-model="editingRow.lotNumber"
|
||||
style="width: 180px; margin-right: 20px"
|
||||
placeholder="选择批号"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in editingRow.stockList"
|
||||
:key="item.lotNumber"
|
||||
:value="item.lotNumber"
|
||||
:label="
|
||||
item.locationName +
|
||||
' 批次号: ' + item.lotNumber +
|
||||
' 库存:' + (item.quantity / editingRow.partPercent).toFixed(2) +
|
||||
item.unitCode_dictText +
|
||||
' 单价:' + item.price.toFixed(2) + '/' + item.unitCode_dictText
|
||||
"
|
||||
@click="handleNumberClick(item, editingRowIndex)"
|
||||
/>
|
||||
</el-select>
|
||||
<span
|
||||
v-else
|
||||
style="color: #EF4444; margin-right: 20px; font-size: 14px;"
|
||||
>无可用库存</span>
|
||||
<el-form-item
|
||||
label="数量:"
|
||||
prop="quantity"
|
||||
class="required-field"
|
||||
data-prop="quantity"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="editingRow.quantity"
|
||||
placeholder="数量"
|
||||
style="width: 70px"
|
||||
controls-position="right"
|
||||
:controls="false"
|
||||
@keyup.enter.prevent="handleEnter('quantity', editingRow, editingRowIndex)"
|
||||
@input="calculateTotalPrice(editingRow, editingRowIndex)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-select
|
||||
v-if="editingRow.unitCodeList && editingRow.unitCodeList.length > 0"
|
||||
v-model="editingRow.unitCode"
|
||||
style="width: 70px; margin-right: 20px"
|
||||
placeholder="单位"
|
||||
@change="calculateTotalAmount(editingRow, editingRowIndex)"
|
||||
>
|
||||
<template
|
||||
v-for="item in editingRow.unitCodeList"
|
||||
:key="item.value"
|
||||
>
|
||||
<el-option
|
||||
v-if="item.type != unitMap['dose']"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</template>
|
||||
</el-select>
|
||||
<span class="total-amount">
|
||||
总金额:{{ editingRow.totalPrice ? editingRow.totalPrice + ' 元' : '0.00 元' }}
|
||||
</span>
|
||||
</div>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleSaveSign(editingRow, editingRowIndex)"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div style="display: flex; align-items: center; gap: 16px; flex-wrap: wrap">
|
||||
<span style="font-size: 16px; font-weight: 600">
|
||||
{{
|
||||
editingRow.adviceName + ' ' + editingRow.unitPrice
|
||||
? Number(editingRow.unitPrice).toFixed(2)
|
||||
: '-' + '元'
|
||||
}}
|
||||
</span>
|
||||
<div class="form-group">
|
||||
<el-form-item
|
||||
label="执行次数:"
|
||||
prop="quantity"
|
||||
class="required-field"
|
||||
data-prop="quantity"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="editingRow.quantity"
|
||||
placeholder="执行次数"
|
||||
style="width: 100px; margin: 0 20px"
|
||||
controls-position="right"
|
||||
:controls="false"
|
||||
@keyup.enter.prevent="handleEnter('quantity', editingRow, editingRowIndex)"
|
||||
@input="calculateTotalPrice(editingRow, editingRowIndex)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-tree-select
|
||||
v-model="editingRow.orgId"
|
||||
clearable
|
||||
:data="organization"
|
||||
:props="{ value: 'id', label: 'name', children: 'children' }"
|
||||
value-key="id"
|
||||
check-strictly
|
||||
placeholder="请选择执行科室"
|
||||
style="min-width: 150px; width: auto;"
|
||||
class="org-select"
|
||||
/>
|
||||
<span class="total-amount">
|
||||
总金额:{{ editingRow.totalPrice ? editingRow.totalPrice + ' 元' : '0.00 元' }}
|
||||
</span>
|
||||
</div>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleSaveSign(editingRow, editingRowIndex)"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -444,7 +436,7 @@ import {
|
||||
getEncounterDiagnosis,
|
||||
} from './api';
|
||||
import adviceBaseList from './adviceBaseList';
|
||||
import {getCurrentInstance, nextTick, ref, watch} from 'vue';
|
||||
import {getCurrentInstance, nextTick, ref, watch, computed} from 'vue';
|
||||
|
||||
const emit = defineEmits(['selectDiagnosis']);
|
||||
const prescriptionList = ref([]);
|
||||
@@ -493,6 +485,14 @@ const isAdding = ref(false);
|
||||
const isSaving = ref(false); // #437 防重复提交锁
|
||||
const prescriptionRef = ref();
|
||||
const expandOrder = ref([]); //目前的展开行
|
||||
const editingRow = computed(() => {
|
||||
if (expandOrder.value.length === 0) return null;
|
||||
return prescriptionList.value.find(r => r.uniqueKey === expandOrder.value[0]) || null;
|
||||
});
|
||||
const editingRowIndex = computed(() => {
|
||||
if (expandOrder.value.length === 0) return -1;
|
||||
return prescriptionList.value.findIndex(r => r.uniqueKey === expandOrder.value[0]);
|
||||
});
|
||||
const stockList = ref([]);
|
||||
const groupList = ref([])
|
||||
const { proxy } = getCurrentInstance();
|
||||
@@ -546,8 +546,11 @@ watch(
|
||||
nextTick(() => {
|
||||
|
||||
const index = prescriptionList.value.findIndex((row) => row.uniqueKey === newValue[0]);
|
||||
const items = proxy.$refs['formRef' + index]?.$el?.querySelectorAll('[data-prop]');
|
||||
requiredProps.value = Array.from(items).map((item) => item.dataset.prop);
|
||||
const formEl = proxy.$refs['editFormRef'];
|
||||
if (formEl) {
|
||||
const items = formEl.$el?.querySelectorAll('[data-prop]') || formEl.querySelectorAll?.('[data-prop]');
|
||||
if (items) requiredProps.value = Array.from(items).map((item) => item.dataset.prop);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
requiredProps.value = {};
|
||||
@@ -831,11 +834,9 @@ async function selectAdviceBase(key, row) {
|
||||
});
|
||||
}
|
||||
|
||||
// 将选中的基础项“覆盖”到当前处方行(这是之前正常工作的核心逻辑)
|
||||
prescriptionList.value[rowIndex.value] = {
|
||||
...prescriptionList.value[rowIndex.value],
|
||||
...JSON.parse(JSON.stringify(row)),
|
||||
};
|
||||
// 将选中的基础项“覆盖”到当前处方行
|
||||
// 用 Object.assign 原地修改,确保 vxe-table 能检测到变更重新渲染
|
||||
Object.assign(prescriptionList.value[rowIndex.value], JSON.parse(JSON.stringify(row)));
|
||||
|
||||
// 后续字段处理保持原样
|
||||
// 🔧 修复执行科室逻辑:诊疗项目优先使用项目维护的所属科室(row.orgId)
|
||||
@@ -1271,7 +1272,7 @@ function handleSaveSign(row, index) {
|
||||
return;
|
||||
}
|
||||
isSaving.value = true; // #437 立即加锁,消除 TOCTOU 竞态
|
||||
proxy.$refs['formRef' + index].validate((valid) => {
|
||||
proxy.$refs['editFormRef'].validate((valid) => {
|
||||
if (!valid) {
|
||||
isSaving.value = false; // 验证失败释放锁
|
||||
return;
|
||||
@@ -1391,6 +1392,14 @@ defineExpose({ getListInfo, closeAllPopovers });
|
||||
:deep(.vxe-table--expand-btn) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// 编辑表单卡片:独立于表格,显示在表格下方
|
||||
.edit-form-card {
|
||||
margin-top: 12px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
.medicine-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
|
||||
Reference in New Issue
Block a user