Files
his/openhis-ui-vue3/src/views/medicineStorage/profitLossOrder/components/orderTable.vue
2025-09-03 15:54:55 +08:00

1686 lines
53 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<el-row :gutter="10">
<el-form ref="formRef" :model="localForm" label-width="140px" inline :rules="rules">
<el-row :gutter="10">
<el-form-item label="单据号" prop="busNo">
<el-input v-model="localForm.busNo" placeholder="" style="width: 220px" disabled />
</el-form-item>
<el-form-item label="开单日期" prop="applyTime">
<el-date-picker
v-model="localForm.applyTime"
placeholder="请选择开单日期"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:disabled="!localIsAddOrEditOrder.isAddOrder && !localIsAddOrEditOrder.isEditOrder"
clearable
/>
</el-form-item>
<el-form-item label="开单人" prop="applicantId">
<el-select
v-model="localForm.applicantId"
placeholder="请选择开单人"
style="width: 140px"
:disabled="!localIsAddOrEditOrder.isAddOrder && !localIsAddOrEditOrder.isEditOrder"
clearable
>
<el-option
v-for="item in props.practitionerOption || []"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="单据分类" prop="categoryEnum">
<el-select
v-model="localForm.categoryEnum"
placeholder="请选择单据分类"
style="width: 180px"
:disabled="!localIsAddOrEditOrder.isAddOrder && !localIsAddOrEditOrder.isEditOrder"
clearable
>
<el-option
v-for="item in props.supplyCategoryOptions || []"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="摘要(A)" prop="reason">
<el-input
type="textarea"
v-model="localForm.reason"
placeholder="请输入摘要"
style="width: 300px"
:rows="1"
:disabled="!localIsAddOrEditOrder.isAddOrder && !localIsAddOrEditOrder.isEditOrder"
clearable
/>
</el-form-item>
</el-row>
</el-form>
</el-row>
<!-- 审核信息 -->
<el-row :gutter="10">
<el-col :span="8">
<div class="form-item">
<label class="form-label">审核人</label>
<el-tag type="warning" plain>
{{
(props.practitionerOption || []).find(
(item) => item.value === localAuditForm.approverId
)?.label || '暂无'
}}
</el-tag>
</div>
</el-col>
<el-col :span="8">
<div class="form-item">
<label class="form-label">审核日期</label>
<el-tag type="primary" plain>
{{ parseTime(localAuditForm.approvalTime, '{y}-{m}-{d} {h}:{i}:{s}') || '暂无' }}
</el-tag>
</div>
</el-col>
<el-col :span="8">
<div class="form-item">
<label class="form-label">审核状态:</label>
<el-tag
:type="localAuditForm.statusEnum_enumText === '同意' ? 'success' : 'danger'"
plain
>
{{ localAuditForm.statusEnum_enumText || '暂无' }}
</el-tag>
</div>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAddRow"
v-if="buttonShow.isAddShow"
>
添加行
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
@click="handleDeleteRow"
v-if="buttonShow.isDeleteShow"
>
删除行
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Check"
@click="handleSave"
v-if="buttonShow.isSaveShow"
>
保存
</el-button>
</el-col>
</el-row>
<el-row :gutter="10">
<el-form ref="tableFormRef" :model="{ localTableData }" :rules="rules" style="width: 100%">
<el-table
:data="localTableData"
ref="tableRef"
class="table-container"
max-height="calc(100vh - 400px)"
min-height="100px"
@selection-change="handleSelectionChange"
@row-click="handleRowClick"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column prop="itemBusNo" label="编号" fixed width="180">
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.itemBusNo`"
:rules="rules.itemBusNo"
>
<span>
{{ localTableData[scope.$index].itemBusNo || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column prop="itemName" label="品名" width="180">
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.itemName`"
:rules="rules.itemName"
>
<PopoverList
v-if="localTableData[scope.$index].isEditing"
@search="handleSearch"
:width="1300"
:modelValue="localTableData[scope.$index].itemName"
>
<template #popover-content="{}">
<medicineList
ref="medicineListRef"
@selectRow="(row) => selectRow(row, scope.$index)"
:searchKey="medicineSearchKey"
:locationId="localForm.locationId"
/>
</template>
</PopoverList>
<span v-else-if="localTableData[scope.$index].isViewing">
{{ localTableData[scope.$index].itemName || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column prop="totalVolume" label="规格" width="140">
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.totalVolume`"
:rules="rules.totalVolume"
>
<span>
{{ localTableData[scope.$index].totalVolume || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<!-- 产品批号 -->
<el-table-column prop="lotNumber" label="产品批号" width="180">
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.lotNumber`"
:rules="rules.lotNumber"
>
<el-input
v-if="localTableData[scope.$index].isEditing"
v-model="localTableData[scope.$index].lotNumber"
clearable
placeholder="请输入产品批号"
style="width: 180px"
/>
<span v-else-if="localTableData[scope.$index].isViewing">
{{ localTableData[scope.$index].lotNumber || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column prop="unitCode" label="单位" width="100">
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.unitCode`"
:rules="rules.unitCode"
>
<el-select
v-if="localTableData[scope.$index].isEditing"
v-model="localTableData[scope.$index].unitCode"
:disabled="!localTableData[scope.$index].unitCode"
@change="handleUnitCodeChange(localTableData[scope.$index])"
>
<el-option
v-for="item in localTableData[scope.$index].unitList || []"
:key="item.value"
:label="item.value_dictText"
:value="item.value"
/>
</el-select>
<span v-else-if="localTableData[scope.$index].isViewing">
{{ localTableData[scope.$index].unitCode_dictText || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column
prop="itemQuantity"
label="现存数量"
width="120"
:rules="rules.itemQuantity"
>
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.itemQuantity`"
:rules="rules.itemQuantity"
>
<el-input-number
:min="0"
v-if="localTableData[scope.$index].isEditing"
v-model="localTableData[scope.$index].itemQuantity"
@change="handleItemQuantityChange(localTableData[scope.$index])"
controls-position="right"
/>
<span v-else-if="localTableData[scope.$index].isViewing">
{{ localTableData[scope.$index].itemQuantity || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column prop="price" label="进货价" width="120">
<template #default="scope">
<el-form-item :prop="`localTableData.${scope.$index}.price`">
<span>
{{ getPriceDisplay(scope.$index, 'price') }}
</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column prop="totalPrice" label="进价金额" width="100">
<template #default="scope">
<el-form-item :prop="`localTableData.${scope.$index}.totalPrice`">
<span>
{{ getPriceDisplay(scope.$index, 'totalPrice') }}
</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column prop="retailPrice" label="当前销售价" width="120">
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.retailPrice`"
:rules="rules.retailPrice"
>
<span>
{{ getPriceDisplay(scope.$index, 'retailPrice') }}
</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column prop="totalRetailPrice" label="当前销售金额" width="120">
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.totalRetailPrice`"
:rules="rules.totalRetailPrice"
>
<span>
{{ getPriceDisplay(scope.$index, 'totalRetailPrice') }}
</span>
</el-form-item>
</template>
</el-table-column>
<!-- 生产日期 -->
<el-table-column prop="startTime" label="生产日期" width="180">
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.startTime`"
:rules="rules.startTime"
>
<span>
{{ parseTime(localTableData[scope.$index].startTime, '{y}-{m}-{d}') || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<!-- 失效日期 -->
<el-table-column prop="endTime" label="失效日期" width="180">
<template #default="scope">
<el-form-item :prop="`localTableData.${scope.$index}.endTime`" :rules="rules.endTime">
<span>
{{ parseTime(localTableData[scope.$index].endTime, '{y}-{m}-{d}') || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column prop="supplierId_dictText" label="供应商" width="220">
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.supplierId_dictText`"
:rules="rules.supplierId_dictText"
>
<span>
{{ localTableData[scope.$index].supplierId_dictText || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column prop="manufacturerText" label="厂家/产地" width="220">
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.manufacturerText`"
:rules="rules.manufacturerText"
>
<span>
{{ localTableData[scope.$index].manufacturerText || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column prop="specificationInventory" label="规格库存" width="100">
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.specificationInventory`"
:rules="rules.specificationInventory"
>
<span
v-if="
getInventoryDisplay(scope.$index, 'max') ||
getInventoryDisplay(scope.$index, 'maxUnit') ||
getInventoryDisplay(scope.$index, 'min') ||
getInventoryDisplay(scope.$index, 'minUnit')
"
>
<span>
{{ getInventoryDisplay(scope.$index, 'max') }}
</span>
<span>
{{ getInventoryDisplay(scope.$index, 'maxUnit') }}
</span>
<span>
{{ getInventoryDisplay(scope.$index, 'min') }}
</span>
<span>
{{ getInventoryDisplay(scope.$index, 'minUnit') }}
</span>
</span>
<span v-else>0</span>
</el-form-item>
</template>
</el-table-column>
<!-- 批次库存 -->
<el-table-column prop="batchInventory" label="批次库存" width="100">
<template #default="scope">
<el-form-item :prop="`localTableData.${scope.$index}.batchInventory`">
<span
v-if="
getInventoryDisplay(scope.$index, 'batchMax') ||
getInventoryDisplay(scope.$index, 'batchMaxUnit') ||
getInventoryDisplay(scope.$index, 'batchMin') ||
getInventoryDisplay(scope.$index, 'batchMinUnit')
"
>
<span>
{{ getInventoryDisplay(scope.$index, 'batchMax') }}
</span>
<span>
{{ getInventoryDisplay(scope.$index, 'batchMaxUnit') }}
</span>
<span>
{{ getInventoryDisplay(scope.$index, 'batchMin') }}
</span>
<span>
{{ getInventoryDisplay(scope.$index, 'batchMinUnit') }}
</span>
</span>
<span v-else> 0 </span>
</el-form-item>
</template>
</el-table-column>
<el-table-column prop="profitLoss" label="损益" width="100">
<template #default="scope">
<el-form-item>
<span
v-if="
getInventoryDisplay(scope.$index, 'batchMax') ||
getInventoryDisplay(scope.$index, 'batchMaxUnit') ||
getInventoryDisplay(scope.$index, 'batchMin') ||
getInventoryDisplay(scope.$index, 'batchMinUnit')
"
>
<span>
{{ localTableData[scope.$index].profitLoss }}
</span>
</span>
</el-form-item>
</template>
</el-table-column>
<!-- 药品追溯码 -->
<el-table-column prop="traceNo" label="药品追溯码" width="180">
<template #default="scope">
<el-form-item :prop="`localTableData.${scope.$index}.traceNo`" :rules="rules.traceNo">
<el-input
v-if="localTableData[scope.$index].isEditing"
v-model="localTableData[scope.$index].traceNo"
clearable
placeholder="请输入药品追溯码"
style="width: 180px"
/>
<span v-else-if="localTableData[scope.$index].isViewing">
{{ localTableData[scope.$index].traceNo || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<!-- 药品追溯码单位 -->
<el-table-column prop="traceNoUnitCode" label="药品追溯码单位" width="180">
<template #default="scope">
<el-form-item
:prop="`localTableData.${scope.$index}.traceNoUnitCode`"
:rules="rules.traceNoUnitCode"
>
<el-select
v-if="localTableData[scope.$index].isEditing"
v-model="localTableData[scope.$index].traceNoUnitCode"
placeholder="请选择药品追溯码单位"
style="width: 180px"
>
<el-option
v-for="item in localTableData[scope.$index].unitList || []"
:key="item.value"
:label="item.value_dictText"
:value="item.value"
/>
</el-select>
<span v-else-if="localTableData[scope.$index].isViewing">
{{ localTableData[scope.$index].traceNoUnitCode_dictText || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<!-- 备注 -->
<el-table-column prop="remake" label="备注" width="180">
<template #default="scope">
<el-form-item :prop="`localTableData.${scope.$index}.remake`">
<el-input
v-if="localTableData[scope.$index].isEditing"
v-model="localTableData[scope.$index].remake"
/>
<span v-else-if="localTableData[scope.$index].isViewing">
{{ localTableData[scope.$index].remake || '' }}
</span>
</el-form-item>
</template>
</el-table-column>
<!-- 操作 -->
<el-table-column label="操作" width="100" fixed="right">
<template #default="scope">
<el-button
type="primary"
@click="handleScan(scope.row, scope.$index)"
link
icon="Edit"
>
扫码
</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
</el-row>
<el-row justify="end">
<el-pagination
:current-page="localForm.pageNo"
:page-size="localForm.pageSize"
:page-sizes="[10, 50, 100, 200]"
:small="small"
layout="total, sizes, prev, pager, next, jumper"
:total="localTableDataTotal"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
v-show="localTableDataTotal > 0"
/>
</el-row>
<!-- 扫码追溯码弹窗 -->
<TraceNoDialog
:ypName="ypName"
:rowData="rowData"
:openDialog="openTraceNoDialog"
@submit="submit"
@cancel="openTraceNoDialog = false"
/>
<!-- 订单对话框 -->
<orderDialog
ref="orderDialogRef"
:dialogVisible="dialogVisible"
@dialogSubmit="dialogSubmit"
@dialogCancel="dialogCancel"
@updateTableData="updateTableData"
></orderDialog>
</div>
</template>
<script setup>
import { addOrEditOrder, getBusNo, getMedicineList } from './api';
import { nextTick, ref, watch } from 'vue';
import medicineList from './medicineList.vue';
import orderDialog from './orderDialog.vue';
import PopoverList from '@/components/OpenHis/popoverList/index.vue';
import { parseTime } from '@/utils/his';
import TraceNoDialog from '@/components/OpenHis/TraceNoDialog/index.vue';
// 获取当前实例
const { proxy } = getCurrentInstance();
// 调用父组件方法
const emit = defineEmits(['update:form', 'getList', 'updateButtonState', 'clearForm']);
// 属性
const props = defineProps({
// 父组件 表格数据
tableData: {
type: Array,
default: () => [],
},
// 父组件 表格总条数
total: {
type: Number,
default: 0,
},
// 父组件 表格每页条数
small: {
type: Boolean,
default: false,
},
// 父组件 表单数据
form: {
type: Object,
default: () => ({
busNo: undefined,
applyTime: undefined,
supplierId: undefined,
applicantId: undefined,
reason: undefined,
status: undefined,
approverId: undefined,
}),
},
// 父组件 订单编辑还是新增
isAddOrEditOrder: {
type: Object,
default: () => ({
isAddOrder: false,
isEditOrder: false,
}),
},
// 父组件 审核表单数据
auditForm: {
type: Object,
default: () => ({
approvalTime: undefined,
statusEnum_enumText: undefined,
approverId_dictText: undefined,
}),
},
// 查询供应商列表 supplierOption
supplierOption: {
type: Array,
default: () => [],
},
// 查询经手人列表 practitionerOption
practitionerOption: {
type: Array,
default: () => [],
},
// 单据分类 supplyCategoryOptions
supplyCategoryOptions: {
type: Array,
default: () => [],
},
// 包装情况 packagingConditionOptions
packagingConditionOptions: {
type: Array,
default: () => [],
},
// 验收结果 acceptanceResultoryOptions
acceptanceResultoryOptions: {
type: Array,
default: () => [],
},
// 请领部门
pharmacyListOptions: {
type: Array,
default: () => [],
},
// 按钮显示
buttonShow: {
type: Object,
default: () => ({
isAddShow: false,
isDeleteShow: false,
isSaveShow: false,
isEditShow: false,
isOrderImport: false,
}),
},
// 单据类型 supplyTypeOptions
supplyTypeOptions: {
type: Array,
default: () => [],
},
// 审批状态 supplyStatusOptions
supplyStatusOptions: {
type: Array,
default: () => [],
},
// 表格数据总条数
tableDataTotal: {
type: Number,
default: 0,
},
});
// 本地表单数据,用于接收 props.form
const localForm = ref({
pageNo: 1,
pageSize: 10,
});
// 本地表格数据 用于接收 props.tableData
const localTableData = ref([]);
// 本地表格数据总条数
const localTableDataTotal = ref(0);
// 加载
const loading = ref(false);
// 品名搜索
const medicineSearchKey = ref('');
// 选择相关变量
const ids = ref([]);
const selectedRows = ref([]);
const single = ref(true);
const multiple = ref(true);
// 审核表单数据
const localAuditForm = ref({
approvalTime: undefined,
statusEnum_enumText: undefined,
approverId_dictText: undefined,
});
// 订单编辑还是新增
const localIsAddOrEditOrder = ref({
isAddOrder: false,
isEditOrder: false,
});
// 验证规则
const rules = {
// busNo: [{ required: true, message: '请输入单据号', trigger: 'blur' }],
supplierId: [{ required: true, message: '请输入供应商', trigger: 'blur' }],
applicantId: [{ required: true, message: '请输入采购员', trigger: 'blur' }],
categoryEnum: [{ required: true, message: '请输入单据类型', trigger: 'blur' }],
applyTime: [{ required: true, message: '请输入开单日期', trigger: 'blur' }],
itemBusNo: [{ required: true, message: '请输入编号', trigger: ['blur', 'change'] }],
itemName: [{ required: true, message: '请输入品名', trigger: 'blur' }],
totalVolume: [{ required: true, message: '请输入规格', trigger: 'blur' }],
unitCode: [{ required: true, message: '请输入单位', trigger: 'blur' }],
itemQuantity: [{ required: true, message: '请输入现存数量', trigger: 'blur' }],
lotNumber: [{ required: true, message: '请输入产品批号', trigger: 'blur' }],
purposeLocationId: [{ required: true, message: '请选择', trigger: 'blur' }],
startTime: [
{ required: true, message: '请输入生产日期', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value) {
const currentRow = rule.field.split('.')[1]; // 获取当前行索引
const rowIndex = parseInt(currentRow);
const endTime = localTableData.value[rowIndex]?.endTime;
if (endTime && value > endTime) {
callback(new Error('生产日期不能大于失效日期'));
} else {
callback();
}
} else {
callback();
}
},
trigger: ['blur', 'change'],
},
],
endTime: [
{ required: true, message: '请输入失效日期', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value) {
const currentRow = rule.field.split('.')[1]; // 获取当前行索引
const rowIndex = parseInt(currentRow);
const startTime = localTableData.value[rowIndex]?.startTime;
if (startTime && value < startTime) {
callback(new Error('失效日期不能小于生产日期'));
} else {
callback();
}
} else {
callback();
}
},
trigger: ['blur', 'change'],
},
],
packagingConditionEnum: [{ required: true, message: '请输入包装情况', trigger: 'blur' }],
acceptanceResultEnum: [{ required: true, message: '请选择验收结果', trigger: 'blur' }],
batchInventory: [{ required: true, message: '请输入批次库存', trigger: 'blur' }],
traceNo: [{ required: true, message: '请输入药品追溯码', trigger: 'blur' }],
traceNoUnitCode: [{ required: true, message: '请输入药品追溯码单位', trigger: 'blur' }],
phone: [{ required: true, message: '请输入供应商联系人', trigger: 'blur' }],
practitionerId: [{ required: true, message: '请输入开单人', trigger: 'blur' }],
invoiceNo: [{ required: true, message: '请输入发票号', trigger: 'blur' }],
// price: [{ required: true, message: '请输入进货价', trigger: 'blur' }],
// totalPrice: [{ required: true, message: '请输入进价金额', trigger: 'blur' }],
// retailPrice: [{ required: true, message: '请输入销售价', trigger: 'blur' }],
// totalRetailPrice: [{ required: true, message: '请输入销售金额', trigger: 'blur' }],
manufacturerText: [{ required: true, message: '请输入厂家/产地', trigger: 'blur' }],
specificationInventory: [{ required: true, message: '请输入规格库存', trigger: 'blur' }],
approvalNumber: [{ required: true, message: '请输入批注文号', trigger: 'blur' }],
};
// 药品名称
const ypName = ref('');
// 药品追溯码
const rowData = ref({});
// 药品追溯码弹窗
const openTraceNoDialog = ref(false);
// 当前索引
const currentIndex = ref(-1);
// 订单组件对话框
const dialogVisible = ref(false);
// 计算属性:获取指定行的当前单位数据
const getCurrentUnitData = (rowIndex) => {
const row = localTableData.value[rowIndex];
if (!row || !row.unitList) return null;
return row.unitList.find((item) => item.value === row.unitCode) || null;
};
// 计算属性:获取指定行的价格显示值
const getPriceDisplay = (rowIndex, priceType) => {
const row = localTableData.value[rowIndex];
if (!row) return '';
const currentUnit = getCurrentUnitData(rowIndex);
// 根据价格类型返回对应的值
switch (priceType) {
case 'price':
return currentUnit?.priceMask ? parseFloat(currentUnit.priceMask).toFixed(2) : '';
case 'totalPrice':
return row.totalPriceMask || currentUnit?.totalPriceMask
? parseFloat(row.totalPriceMask || currentUnit?.totalPriceMask).toFixed(2)
: '';
case 'retailPrice':
return currentUnit?.retailPriceMask ? parseFloat(currentUnit.retailPriceMask).toFixed(2) : '';
case 'totalRetailPrice':
return row.totalRetailPriceMask || currentUnit?.totalRetailPriceMask
? parseFloat(row.totalRetailPriceMask || currentUnit?.totalRetailPriceMask).toFixed(2)
: '';
default:
return '';
}
};
// 计算属性:获取指定行的库存显示值
const getInventoryDisplay = (rowIndex, type) => {
const currentUnit = getCurrentUnitData(rowIndex);
if (!currentUnit) return '';
switch (type) {
case 'max':
return currentUnit.specificationInventoryMax || '';
case 'maxUnit':
return currentUnit.specificationInventoryMax > 0
? currentUnit.specificationInventoryMaxUnit
: '';
case 'min':
return currentUnit.specificationInventoryMin > 0 ? currentUnit.specificationInventoryMin : '';
case 'minUnit':
return currentUnit.specificationInventoryMin > 0
? currentUnit.specificationInventoryMinUnit
: '';
case 'batchMax':
return currentUnit.batchInventoryMax || '';
case 'batchMaxUnit':
return currentUnit.batchInventoryMax > 0 ? currentUnit.batchInventoryMaxUnit : '';
case 'batchMin':
return currentUnit.batchInventoryMin > 0 ? currentUnit.batchInventoryMin : '';
case 'batchMinUnit':
return currentUnit.batchInventoryMin > 0 ? currentUnit.batchInventoryMinUnit : '';
default:
return '';
}
};
// 监听 props.isAddOrEditOrder 的变化
watch(
() => props.isAddOrEditOrder,
(newVal) => {
localIsAddOrEditOrder.value = { ...newVal };
}
);
// 监听 props.form 的变化
watch(
() => props.form,
(newVal) => {
localForm.value = { ...newVal };
},
{ deep: true }
);
// 监听 props.tableData 的变化
watch(
() => props.tableData,
(newVal) => {
localTableData.value = [...newVal];
},
{ deep: true }
);
// 监听 props.tableDataTotal 的变化
watch(
() => props.tableDataTotal,
(newVal) => {
localTableDataTotal.value = newVal;
}
);
// 监听 props.auditForm 的变化
watch(
() => props.auditForm,
(newVal) => {
localAuditForm.value = { ...newVal };
},
{ deep: true }
);
// 品名搜索
const handleSearch = (value) => {
medicineSearchKey.value = value;
};
// 获取单据号
const getInitBusNo = async () => {
const res = await getBusNo();
return res.data.busNo;
};
// 扫码
const handleScan = (row, index) => {
// 判断是否存在产品批号
if (row.lotNumber) {
// 设置药品数据
rowData.value = row;
// 设置药品名称
ypName.value = row.name;
// 设置仓库
rowData.value.locationId = localForm.purposeLocationId;
// 设置药品类型
rowData.value.itemType = 1;
// 设置药品追溯码弹窗
openTraceNoDialog.value = true;
// 设置当前索引
currentIndex.value = index;
} else {
proxy.$message.warning('请先输入产品批号!');
}
};
// 药品追溯码提交
const submit = (value) => {
localTableData.value[currentIndex.value].traceNo = value;
openTraceNoDialog.value = false;
};
// 分页当前页
const handleCurrentChange = (page) => {
localForm.value.pageNo = page;
// 通知父组件更新查询参数并重新获取数据
emit('update:query-params', {
pageNo: page,
pageSize: localForm.value.pageSize,
});
};
// 分页每页条数
const handleSizeChange = (size) => {
localForm.value.pageSize = size;
localForm.value.pageNo = 1; // 切换每页条数时重置到第一页
// 通知父组件更新查询参数并重新获取数据
emit('update:query-params', {
pageNo: 1,
pageSize: size,
});
};
// 行点击
const handleRowClick = (row) => {
// 判断订单是否可以编辑
if (!isAllowEdit(row)) {
return;
}
// 设置表格编辑状态
row.isEditing = true;
// 设置表格查看状态
row.isViewing = false;
};
// 判断订单是否可以编辑
const isAllowEdit = (row) => {
// 已审批的订单不能编辑
if (
localAuditForm.value.statusEnum === 3 ||
localAuditForm.value.statusEnum_enumText === '同意'
) {
return false; // 已审批,不能编辑
}
return true; // 未审批,可以编辑
};
// 监听单位变化
const handleUnitCodeChange = (row) => {
const isMaxUnit = row.unitCode === row.unitList[0].value;
if (isMaxUnit) {
row.price = row.priceMaxUnit;
row.retailPrice = row.retailPriceMaxUnit;
} else {
row.price = row.priceMinUnit;
row.retailPrice = row.retailPriceMinUnit;
}
// 进价金额
row.totalPrice = row.price * row.itemQuantity;
row.totalPriceMask = row.price * row.itemQuantity;
// 销售金额
row.totalRetailPrice = row.retailPrice * row.itemQuantity;
row.totalRetailPriceMask = row.retailPrice * row.itemQuantity;
// 构建损益变化
buildProfitLoss(row);
};
// 监听数量变化
const handleItemQuantityChange = (row) => {
// 转换为数值并保留2位小数
const quantity = parseFloat(row.itemQuantity) || 0;
const price = parseFloat(row.price) || 0;
const retailPrice = parseFloat(row.retailPrice) || 0;
// 进价金额
row.totalPrice = parseFloat((quantity * price).toFixed(2));
row.totalPriceMask = parseFloat((quantity * price).toFixed(2));
// 销售金额
row.totalRetailPrice = parseFloat((quantity * retailPrice).toFixed(2));
row.totalRetailPriceMask = parseFloat((quantity * retailPrice).toFixed(2));
//构建损益变化
buildProfitLoss(row);
// console.log('handleItemQuantityChange', row);
};
// 构建损益变化
const buildProfitLoss = (item) => {
// 当前选中最大单位
if (item.unitCode != item.minUnitCode) {
if (item.itemQuantity * item.partPercent > item.batchInventory) {
item.profitLoss = '↑';
} else if (item.itemQuantity * item.partPercent == item.batchInventory) {
item.profitLoss = '-';
} else {
item.profitLoss = '↓';
}
} else {
if (item.itemQuantity > item.batchInventory) {
item.profitLoss = '↑';
} else if (item.itemQuantity == item.batchInventory) {
item.profitLoss = '-';
} else {
item.profitLoss = '↓';
}
}
};
// 监听进货价数量变化
const handleItemPriceChange = (row) => {
// 进价金额
row.totalPrice = parseFloat((row.itemQuantity * row.price).toFixed(2));
// 进价金额(假)
row.totalPriceMask = parseFloat((row.itemQuantity * row.price).toFixed(2));
};
// 监听销售价数量变化
const handleItemRetailPriceChange = (row) => {
// 销售金额
row.totalRetailPrice = parseFloat((row.itemQuantity * row.retailPrice).toFixed(2));
// 销售金额(假)
row.totalRetailPriceMask = parseFloat((row.itemQuantity * row.retailPrice).toFixed(2));
};
// 选择药品
const selectRow = (rowValue, index) => {
localTableData.value[index] = {
// 选中行药品数据
...rowValue,
// 药品 id
itemId: rowValue.id,
// 药品编号
itemBusNo: rowValue.busNo,
// 药品名称
itemName: rowValue.name,
// 单位
unitCode: rowValue.unitCode,
// 单位名称
unitCodeDictText: rowValue.unitCode_dictText,
// 备注
remake: rowValue.remake,
// 规格库存最大值
specificationInventoryMax: rowValue.specificationInventory,
// 批次库存最大值
batchInventoryMax: rowValue.batchInventory,
// 只用于显示
// 编辑状态
isEditing: true,
// 查看状态
isViewing: false,
// 包装情况
packagingConditionEnum: 1,
// 验收结果
acceptanceResultEnum: 1,
// 生产日期
startTime: parseTime(rowValue.productionDate, '{y}-{m}-{d}'),
// 失效日期
endTime: parseTime(rowValue.expirationDate, '{y}-{m}-{d}'),
};
// 重新构建价格单位 规格库存 进价 售价表格数据
rebuildTableData(localTableData.value[index]);
// 手动触发验证
nextTick(() => {
proxy.$refs.tableFormRef.validateField(`localTableData.${index}.itemBusNo`);
proxy.$refs.tableFormRef.validateField(`localTableData.${index}.itemName`);
proxy.$refs.tableFormRef.validateField(`localTableData.${index}.totalVolume`);
proxy.$refs.tableFormRef.validateField(`localTableData.${index}.unitCode`);
// proxy.$refs.tableFormRef.validateField(`localTableData.${index}.price`);
// proxy.$refs.tableFormRef.validateField(`localTableData.${index}.retailPrice`);
proxy.$refs.tableFormRef.validateField(`localTableData.${index}.manufacturerText`);
proxy.$refs.tableFormRef.validateField(`localTableData.${index}.specificationInventory`);
proxy.$refs.tableFormRef.validateField(`localTableData.${index}.approvalNumber`);
});
};
// 重新构建价格单位 规格库存 进价 售价表格数据
const rebuildTableData = (row) => {
// 构建单位列表
buildUnitList(row);
// 构建进货价和销售价
buildPrice(row);
};
// 构建进货价和销售价
const buildPrice = (item) => {
// 判断当前单位是最大单位还是最小单位
const isMaxUnit = item.unitCode === item.unitList[0].value;
if (isMaxUnit) {
// 进货价
item.price = item.price;
item.priceMaxUnit = item.price;
item.priceMinUnit = item.price / parseInt(item.partPercent);
// 销售价
item.retailPrice = item.retailPrice;
item.retailPriceMaxUnit = item.retailPrice;
item.retailPriceMinUnit = (parseFloat(item.retailPrice) / parseInt(item.partPercent)).toFixed(
2
);
} else {
// 进货价
item.price = item.price;
item.priceMaxUnit = parseFloat((item.price * parseInt(item.partPercent)).toFixed(2));
item.priceMinUnit = item.price;
// 销售价
item.retailPrice = item.retailPrice;
item.retailPriceMaxUnit = parseFloat(
(item.retailPrice * parseInt(item.partPercent)).toFixed(2)
);
item.retailPriceMinUnit = item.retailPrice;
}
};
// 构建单位列表
const buildUnitList = (row) => {
// 单位列表
(row.unitList || []).forEach((item) => {
// 最大单位
if (item.value === row.unitCode) {
// 假数据 只做显示用 不参与计算 用于显示
// 进货价(假)
item.priceMask = row.price;
// 销售价(假)
item.retailPriceMask = row.retailPrice;
// 进价金额(假)
item.totalPriceMask = row.totalPrice;
// 销售金额(假)
item.totalRetailPriceMask = row.totalRetailPrice;
// 规格库存
item.specificationInventoryOriginal = row.specificationInventory;
// 规格库存最大值
item.specificationInventoryMax = Math.floor(row.specificationInventory / row.partPercent);
// 规格库存最小智
item.specificationInventoryMin = row.specificationInventory % row.partPercent;
// 规格库在最大单位
item.specificationInventoryMaxUnit = row.unitCode_dictText;
// 规格库存最小单位
item.specificationInventoryMinUnit = row.minUnitCode_dictText;
// 批次库存
item.batchInventoryOriginal = row.batchInventory;
// 批次库存最大值
item.batchInventoryMax = Math.floor(row.batchInventory / row.partPercent);
// 批次库存最小值
item.batchInventoryMin = row.batchInventory % row.partPercent;
// 批次库存最大单位
item.batchInventoryMaxUnit = row.unitCode_dictText;
// 批次库存最小单位
item.batchInventoryMinUnit = row.minUnitCode_dictText;
} else {
// 最小单位
// 假数据 只做显示用 不参与计算 用于显示
// 进货价(假)
item.priceMask = row.price / row.partPercent;
// 销售价(假)
item.retailPriceMask = (parseFloat(row.retailPrice) / parseFloat(row.partPercent)).toFixed(2);
// 进价金额(假)
item.totalPriceMask = row.totalPrice;
// 销售金额(假)
item.totalRetailPriceMask = row.totalRetailPrice;
// 规格库存
item.specificationInventoryOriginal = row.specificationInventory;
// 规格库存最大
item.specificationInventoryMin = row.specificationInventory;
// 规格库存最大单位
item.specificationInventoryMinUnit = row.minUnitCode_dictText;
// 批次库存
item.batchInventoryOriginal = row.batchInventory;
// 批次库存最大
item.batchInventoryMin = row.batchInventory;
// 批次库存最大单位
item.batchInventoryMinUnit = row.minUnitCode_dictText;
}
});
};
// 校验是否有空行
const isEmptyRow = () => {
// 如果 localTableData的长度大于 1 时在判断否有空行
if (!localTableData.value) {
return true;
}
return localTableData.value.some((row) => !row.itemId || !row.itemQuantity || !row.itemName);
};
// 新增一个空行
const addEmptyRow = () => {
localTableData.value.push({
// 单据号
busNo: localForm.value.busNo,
// 开单日期
applyTime: localForm.value.applyTime,
// 采购员
applicantId: localForm.value.applicantId,
// 摘要
reason: localForm.value.reason,
// 单据分类
categoryEnum: localForm.value.categoryEnum,
// 供应商
supplierId: localForm.value.supplierId,
// 仓库
purposeLocationId: localForm.value.locationId,
// 表格需要的字段
itemBusNo: '',
// 药品 id
itemId: '',
// 品名
itemName: '',
// 规格
totalVolume: '',
// 单位
unitCode: '',
// 数量
itemQuantity: 0,
// 进货价
price: '',
// 进价金额
totalPrice: '',
// 销售价
retailPrice: '',
// 销售金额
totalRetailPrice: '',
// 生产厂家
manufacturerText: '',
// 规格库存
specificationInventory: '',
// 批注文号
approvalNumber: '',
// 验收结果
acceptanceResultEnum: 1,
// 包装情况
packagingConditionEnum: 1,
// 备注
remake: '',
// 编辑状态
isEditing: true,
// 查看状态
isViewing: false,
});
nextTick(() => {
// 处理本地按钮状态
emit('updateButtonState', {
// 审核 按钮禁用
isAuditDisabled: true,
// 取消 按钮禁用
isCancelDisabled: false,
// 删除 按钮禁用
isDeleteDisabled: true,
// 添加 按钮禁用
isAddDisabled: true,
// 编辑 按钮禁用
isEditDisabled: true,
// 添加行 按钮显示
isAddShow: true,
// 删除行 按钮显示
isDeleteShow: true,
// 保存行 按钮显示
isSaveShow: true,
// 编辑行 按钮显示
isEditShow: true,
// 警戒订货 按钮显示
isOrderImport: true,
});
});
};
// 添加行
const handleAddRow = async () => {
// 验证表单
const formValid = await validateFormRef();
if (!formValid) {
return;
}
// 如果当前没有数据,直接添加空行
if (!localTableData.value || localTableData.value.length === 0) {
// 确保 localTableData 存在
if (!localTableData.value) {
localTableData.value = [];
}
// 添加一个包含所有字段的空对象
addEmptyRow();
return;
}
// 如果有数据,检查是否有空行
if (isEmptyRow()) {
proxy.$message.error('请填写完整信息');
return;
}
// 添加一个包含所有字段的空对象
addEmptyRow();
};
// 验证表单
const validateFormRef = () => {
return new Promise((resolve) => {
proxy.$refs.formRef.validate((valid) => {
resolve(valid);
});
});
};
// 验证表格
const validateTableFormRef = () => {
if (!localTableData.value || localTableData.value.length === 0) {
return false;
}
return new Promise((resolve) => {
proxy.$refs.tableFormRef.validate((valid) => {
resolve(valid);
});
});
};
// 保存行
const handleSave = async () => {
// 验证表单头部
const formValid = await validateFormRef();
if (!formValid) {
return;
}
if (!formValid) {
return;
}
// 验证表格数据
const tableValid = await validateTableFormRef();
if (!tableValid) {
return;
}
// 保存当前数据状态,防止在确认过程中被修改
const currentTableData = JSON.parse(JSON.stringify(localTableData.value));
const currentFormData = JSON.parse(JSON.stringify(localForm.value));
// 是否确认保存数据
const isConfirm = await proxy.$modal.confirm('确认保存数据吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
});
if (!isConfirm) {
return;
}
// 加载中
loading.value = true;
// 使用保存的数据状态重构数据
const editData = await buildEditForm(currentTableData, currentFormData);
// 判断是新增还是编辑
if (localIsAddOrEditOrder.value.isAddOrder) {
addOrEditOrder(editData).then((res) => {
if (res.code === 200) {
proxy.$message.success('新增成功');
// 调用父组件的 getList 方法
emit('getList');
// 清空表单
resetAllData();
// 关闭加载
loading.value = false;
}
});
} else if (localIsAddOrEditOrder.value.isEditOrder) {
addOrEditOrder(editData).then((res) => {
if (res.code === 200) {
proxy.$message.success('保存成功');
// 调用父组件的 getList 方法
emit('getList');
// 清空表单
resetAllData();
} else {
proxy.$message.error('保存失败');
}
// 关闭加载
loading.value = false;
});
}
};
// 重构数据
const buildEditForm = async (data, formData = null) => {
const formDataCopy = [...data];
// 获取单据号
let busNo = '';
if (localIsAddOrEditOrder.value.isAddOrder) {
busNo = await getInitBusNo();
} else {
busNo = data[0].busNo;
}
// 订单数据
const orderData = data[0];
// 使用传入的表单数据如果没有则使用当前的localForm
const currentForm = formData || localForm.value;
// 表格数据
formDataCopy.forEach((item) => {
// 单据号
item.busNo = busNo;
// 开单日期
item.applyTime = currentForm.applyTime;
// 采购员
item.applicantId = currentForm.applicantId;
// 单据分类
item.categoryEnum = currentForm.categoryEnum;
// 摘要
item.reason = currentForm.reason;
// 仓库
item.purposeLocationId = orderData.purposeLocationId
? orderData.purposeLocationId
: currentForm.locationId;
// 开单人
item.practitionerId = currentForm.practitionerId;
// 请领部门
item.purposeLocationId = currentForm.purposeLocationId;
});
// 重构数据
return formDataCopy;
};
// 清空表单
const resetAllData = () => {
// 清空表单数据
localForm.value = {};
// 清空审核表单数据
localAuditForm.value = {};
// 清空表格数据
localTableData.value = [];
// 重置订单状态
localIsAddOrEditOrder.value = {
isAddOrder: false,
isEditOrder: false,
};
// 重置按钮状态为初始状态
emit('updateButtonState', {
// 审核 按钮禁用
isAuditDisabled: true,
// 取消 按钮禁用
isCancelDisabled: true,
// 删除 按钮禁用
isDeleteDisabled: true,
// 添加 按钮禁用
isAddDisabled: false,
// 编辑 按钮禁用
isEditDisabled: true,
// 添加行 按钮显示
isAddShow: false,
// 删除行 按钮显示
isDeleteShow: false,
// 保存行 按钮显示
isSaveShow: false,
// 编辑行 按钮显示
isEditShow: false,
});
};
// 删除行
const handleDeleteRow = () => {
if (selectedRows.value.length === 0) {
proxy.$message.warning('请选择要删除的行');
return;
}
proxy
.$confirm('确认删除选中的行吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
// 从 tableData 中删除选中的行
const selectedIndexes = [];
// 获取选中行在 localTableData 中的索引
selectedRows.value.forEach((selectedRow) => {
const index = localTableData.value.findIndex(
(row) => row.itemBusNo === selectedRow.itemBusNo && row.itemName === selectedRow.itemName
);
if (index !== -1) {
selectedIndexes.push(index);
}
});
// 按索引从大到小排序,避免删除时索引变化的问题
selectedIndexes.sort((a, b) => b - a);
// 删除选中的行
selectedIndexes.forEach((index) => {
localTableData.value.splice(index, 1);
});
// 清空选择
selectedRows.value = [];
ids.value = [];
// 如果删除后没有数据了,更新按钮状态
if (localTableData.value.length === 0) {
emit('updateButtonState', {
// 审核 按钮禁用
isAuditDisabled: true,
// 取消 按钮禁用
isCancelDisabled: true,
// 删除 按钮禁用
isDeleteDisabled: true,
// 添加 按钮禁用
isAddDisabled: false,
// 编辑 按钮禁用
isEditDisabled: true,
// 添加行 按钮显示
isAddShow: true,
// 删除行 按钮显示
isDeleteShow: true,
// 保存行 按钮显示
isSaveShow: true,
// 编辑行 按钮显示
isEditShow: false,
// 警戒订货 按钮显示
isOrderImport: true,
});
}
proxy.$message.success('删除成功');
})
.catch(() => {
// 用户取消删除
});
};
// 日期范围验证函数
const validateDateRange = (rowIndex) => {
const row = localTableData.value[rowIndex];
if (row && row.startTime && row.endTime) {
if (row.endTime < row.startTime) {
proxy.$message.error('失效日期不能小于生产日期');
// 清空失效日期
row.endTime = '';
}
}
};
// 选择变化处理
const handleSelectionChange = (selection) => {
ids.value = selection.map((item) => item.id);
selectedRows.value = selection;
single.value = selection.length != 1;
multiple.value = !selection.length;
};
// 更新表格数据
const updateTableData = (updateValue) => {
// 更新表格数据
localTableData.value = localTableData.value.concat(updateValue.currentOrderList);
// 更新表单数据 - 合并数据而不是替换
localForm.value = { ...localForm.value, ...updateValue.currentOrderForm };
// 更新按钮状态
emit('updateButtonState', {
// 审核 按钮禁用
isAuditDisabled: true,
// 取消 按钮禁用
isCancelDisabled: true,
// 删除 按钮禁用
isDeleteDisabled: true,
// 添加 按钮禁用
isAddDisabled: false,
// 编辑 按钮禁用
isEditDisabled: true,
// 添加行 按钮显示
isAddShow: true,
// 删除行 按钮显示
isDeleteShow: true,
// 保存行 按钮显示
isSaveShow: true,
// 编辑行 按钮显示
isEditShow: false,
// 警戒订货 按钮显示
isOrderImport: true,
});
};
// 对话框提交
const dialogSubmit = (value) => {
//对话框关闭
dialogVisible.value = value.dialogVisible;
};
// 对话框取消
const dialogCancel = (value) => {
//对话框关闭
dialogVisible.value = value.dialogVisible;
};
// 订货单导入
const handleOrderImport = async () => {
// 对话框显示
dialogVisible.value = true;
// 获取订单列表
await proxy.$refs.orderDialogRef.getOrderList(localForm.value.locationId);
};
// 组件名称
defineOptions({ name: 'orderTable' });
// 暴露方法给父组件
defineExpose({
resetAllData,
});
</script>
<style scoped>
/* 表格中的表单项样式 */
.table-form-item {
margin-bottom: 0 !important;
display: block !important;
}
/* 确保表格行有足够的高度显示错误信息 */
.el-table .el-table__row {
min-height: 100px;
}
/* 表格单元格样式 */
.el-table .el-table__cell {
padding-bottom: 25px;
}
/* 输入框样式 */
.el-table .el-input {
width: 100%;
}
/* 确保表格占满容器宽度 */
.el-table {
width: 100% !important;
}
/* 确保表格容器占满宽度 */
.el-form {
width: 100%;
}
/* 确保行容器占满宽度 */
.el-row {
width: 100%;
}
/* 自定义表单项样式 */
.form-item {
display: flex;
align-items: center;
margin-bottom: 18px;
}
.form-label {
width: 100px;
text-align: right;
padding-right: 12px;
font-size: 14px;
color: #606266;
line-height: 40px;
box-sizing: border-box;
}
/* 强制显示错误信息 */
.el-form-item__error {
position: static !important;
visibility: visible !important;
opacity: 1 !important;
display: block !important;
margin-top: 2px !important;
font-size: 12px !important;
color: #f56c6c !important;
}
/* 确保表单项内容正确显示 */
.el-form-item__content {
display: block !important;
width: 100% !important;
}
/* 表格单元格内容样式 */
.el-table .cell {
padding: 8px 0;
}
.table-container {
width: 100%;
}
</style>