Files
his/openhis-ui-vue3/src/views/inpatientNurse/InpatientBilling/components/FeeDialog.vue
2026-05-18 17:44:15 +08:00

1019 lines
37 KiB
Vue
Executable File
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>
<el-dialog v-model="dialogVisible" title="补费" width="80%" :close-on-click-modal="false">
<div style="font-size: 16px; font-weight: bold; margin-bottom: 10px">
患者信息{{
props.patientInfo.patientName +
' ' +
props.patientInfo.genderEnum_enumText +
' ' +
props.patientInfo.age
}}
| 住院号{{ props.patientInfo.busNo }} | 科室{{ props.patientInfo.organizationName }} |
病区{{ props.patientInfo.wardName }} | 病房{{ props.patientInfo.houseName }} | 床号{{
props.patientInfo.bedName
}}
</div>
<div style="margin-bottom: 15px; display: flex; align-items: center; gap: 15px">
<el-select
v-model="adviceType"
placeholder="医嘱类型"
style="width: 200px"
@change="getAdviceBaseInfos"
>
<el-option
v-for="item in adviceTypeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<div>
<el-input
style="width: 400px"
v-model="searchText"
placeholder="输入项目名称"
@keydown.enter="getAdviceBaseInfos"
clearable
>
<template #append>
<el-button icon="Search" @click="getAdviceBaseInfos"></el-button>
</template>
</el-input>
</div>
<el-button type="primary" @click="openGroupSetDialog">划价组套</el-button>
</div>
<!-- 弹窗内容 - 左右布局 -->
<div style="display: flex; gap: 20px; height: 70vh">
<div
style="
width: 250px;
border: 1px solid #e4e7ed;
border-radius: 4px;
display: flex;
flex-direction: column;
"
>
<!-- 诊疗耗材 -->
<div
style="flex: 1; overflow-y: auto; padding: 10px; min-height: 200px"
v-loading="adviceLoading"
element-loading-text="正在努力加载..."
element-loading-background="rgba(255, 255, 255, 0.8)"
>
<div
v-for="item in AdviceBaseInfoList"
:key="item.id"
class="item-card"
@click="selectChange(item)"
>
<div class="item-status">
<span class="status-dot"></span>
{{ getItemType_Text(item.adviceType) }}
</div>
<div class="item-name">{{ item.adviceName }}</div>
<div class="item-name">
{{
item.priceList && item.priceList.length > 0
? (item.priceList[0].price / item.partPercent).toFixed(2) +
'元' +
'/' +
item.minUnitCode_dictText
: ''
}}
</div>
<div class="item-name" v-if="item.adviceType === 2">
库存数量
{{ handleQuantity(item) }}
</div>
</div>
<!-- 只显示暂无数据文本 -->
<div
v-if="AdviceBaseInfoList.length === 0"
style="text-align: center; color: #909399; padding: 20px"
>
暂无数据
</div>
</div>
</div>
<!-- 右侧费用项目和信息 -->
<div style="flex: 1; display: flex; flex-direction: column">
<!-- 费用项目表格 -->
<el-table :data="feeItemsList" border style="width: 100%; flex: 1">
<el-table-column label="收费项目" prop="itemName" min-width="200">
<!-- <template #default="scope">
<el-input
v-model="scope.row.adviceName"
placeholder="请输入收费项目"
style="width: 100%"
/>
</template> -->
<template #default="scope">
{{ scope.row.adviceName }}
{{
scope.row.volume === '-' || !scope.row.volume ? '' : '[' + scope.row.volume + ']'
}}
</template>
</el-table-column>
<!-- 单价 - 简化版 -->
<el-table-column label="单价" width="200" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.unitPrice"
:min="0"
:step="0.01"
precision="6"
style="width: 150px"
/>
</template>
</el-table-column>
<el-table-column label="计费数量" prop="quantity" width="150" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.quantity"
:min="1"
:step="1"
style="width: 100px"
/>
</template>
</el-table-column>
<!-- <el-table-column label="最小单位" prop="minUnitCode_dictText" width="100" align="center">
<template #default="scope">
{{ scope.row.minUnitCode_dictText }}
</template>
</el-table-column> -->
<el-table-column label="单位" width="140" align="center">
<template #default="scope">
<el-select
v-model="scope.row.selectUnitCode"
placeholder="单位"
filterable
style="width: 100px"
@change="unitCodeChange(scope.row)"
>
<el-option
v-for="unit in scope.row.uniqueUnitCodes"
:key="unit.code"
:label="unit.codeText"
:value="unit.code"
/>
</el-select>
</template>
</el-table-column>
<!-- 优化后的金额列 -->
<el-table-column label="金额" width="100" align="center">
<template #default="scope">
{{ (scope.row.unitPrice * scope.row.quantity).toFixed(6) }}
</template>
</el-table-column>
<el-table-column label="执行科室/位置" prop="dept" width="250" align="center">
<template #default="scope">
<el-select
v-if="scope.row.adviceType == 3"
clearable
filterable
:filter-method="(val) => filterOptions(val, scope.row, 'departmentOptions')"
v-model="scope.row.positionId"
placeholder="选择科室"
style="width: 220px"
>
<el-option
v-for="dept in getFilteredOptions(scope.row, 'departmentOptions')"
:key="dept.id"
:label="dept.name"
:value="String(dept.id)"
/>
</el-select>
<el-select
v-if="scope.row.adviceType == 2"
clearable
filterable
:filter-method="(val) => filterOptions(val, scope.row, 'locationOptions')"
v-model="scope.row.positionId"
placeholder="选择药房/耗材房"
style="width: 220px"
>
<el-option
v-for="dept in getFilteredOptions(scope.row, 'locationOptions')"
:key="dept.value"
:label="dept.label"
:value="String(dept.value)"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="医保等级" prop="chrgitmLv_dictText" width="150" align="center">
<template #default="scope">
{{ scope.row.chrgitmLv_dictText }}
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center">
<template #default="scope">
<el-button link size="small" @click="feeItemsList.splice(scope.$index, 1)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 底部信息区域 -->
<div style="margin-top: 20px; display: flex; flex-wrap: wrap; gap: 15px">
<div style="display: flex; align-items: center">
<span style="margin-right: 8px">执行时间:</span>
<el-date-picker
v-model="executeTime"
type="datetime"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="选择日期时间"
style="width: 200px"
/>
</div>
</div>
<!-- 总金额和操作按钮 -->
<div
style="
margin-top: 20px;
display: flex;
justify-content: space-between;
align-items: center;
"
>
<div style="font-size: 14px; font-weight: bold; text-align: right">
本次补费总金额:<span style="color: #ff4d4f">{{ totalAmount.toFixed(6) }}</span>
</div>
<div>
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button>
</div>
</div>
</div>
</div>
</el-dialog>
<!-- 划价组套选择对话框 -->
<el-dialog v-model="groupSetDialogVisible" title="划价组套选择" width="600px" :close-on-click-modal="false" append-to-body :z-index="3000">
<div style="margin-bottom: 15px; display: flex; align-items: center; gap: 10px">
<el-input
v-model="groupSetSearchText"
placeholder="请输入组套名称搜索"
clearable
style="width: 250px"
@keyup.enter="loadGroupSets"
>
<template #append>
<el-button icon="Search" @click="loadGroupSets" />
</template>
</el-input>
<el-radio-group v-model="groupSetRange" @change="loadGroupSets">
<el-radio-button :label="1">个人</el-radio-button>
<el-radio-button :label="2">科室</el-radio-button>
<el-radio-button :label="3">全院</el-radio-button>
</el-radio-group>
</div>
<el-table
:data="groupSetList"
v-loading="groupSetLoading"
highlight-current-row
border
stripe
@current-change="handleGroupSetSelect"
style="width: 100%"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="组套名称" min-width="180" show-overflow-tooltip>
<template #default="scope">
{{ scope.row.name || scope.row.Name || '' }}
</template>
</el-table-column>
<el-table-column label="使用范围" width="100" align="center">
<template #default="scope">
{{ getRangeText(scope.row) }}
</template>
</el-table-column>
<el-table-column label="明细数量" width="100" align="center">
<template #default="scope">
{{ scope.row.detailList?.length || 0 }} 项
</template>
</el-table-column>
</el-table>
<el-empty v-if="!groupSetLoading && groupSetList.length === 0" description="暂无划价组套数据" :image-size="80" />
<div style="margin-top: 15px; text-align: right">
<el-button @click="groupSetDialogVisible = false">取消</el-button>
<el-button type="primary" @click="applyGroupSet" :disabled="!selectedGroupSet">应用</el-button>
</div>
</el-dialog>
</template>
<script setup>
import {computed, getCurrentInstance, onMounted, reactive, ref, watch} from 'vue';
import {ElMessage} from 'element-plus';
import {formatDateStr} from '@/utils/index';
import {getAdviceBaseInfo, getDiseaseTreatmentInitLoc, getOrgList} from './api.js';
import {getOrderGroup} from '@/views/doctorstation/components/api.js';
import useUserStore from '@/store/modules/user';
const { proxy } = getCurrentInstance();
const { drord_doctor_type } = proxy.useDict('drord_doctor_type');
// Props定义
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
initialData: {
type: Array,
default: () => [],
},
patientInfo: {
type: Object, // 通常这类信息是对象,这里假设你已经修正了类型
default: () => ({}),
},
});
// Emits定义
const emit = defineEmits(['update:visible', 'confirm', 'cancel']);
// 响应式数据
const dialogVisible = computed({
get: () => props.visible,
set: (value) => emit('update:visible', value),
});
// 使用 drord_doctor_type 字典
const adviceTypeList = computed(() => {
if (drord_doctor_type.value && drord_doctor_type.value.length > 0) {
// 只保留耗材(4)和诊疗(3)类型,并添加全部选项
const filtered = drord_doctor_type.value.filter(item => {
const val = parseInt(item.value);
return val === 3 || val === 4;
}).map(item => ({
label: item.label,
value: parseInt(item.value)
}));
return [...filtered, { label: '全部', value: '' }];
}
// 默认值
return [
{ label: '耗材', value: 2 },
{ label: '诊疗', value: 3 },
{ label: '全部', value: '' },
];
});
const adviceType = ref('');
const feeItemsList = ref([]);
const executeTime = ref('');
const departmentOptions = ref([]);
const AdviceBaseInfoList = ref([]);
const locationOptions = ref([]);
const searchText = ref('');
const userId = ref('');
const orgId = ref('');
// 下拉框模糊搜索关键字(按行存储)
const filterKeywords = ref({});
const queryParams = ref({
pageSize: 100,
pageNum: 1,
// 默认加载全部类型药品1+耗材2+诊疗3
adviceTypes: [1, 2, 3],
});
/**
* 医嘱提交数据模型
* @type {object}
* @property {number} organizationId - 提交机构ID
* @property {string} startTime - 开始时间 (ISO格式字符串如 '2023-10-27T10:00:00')
* @property {string} authoredTime - 开立时间
* @property {Array<RegAdviceItem>} regAdviceSaveList - 医嘱项目列表
*/
const submitData = reactive({
organizationId: 0,
startTime: '',
authoredTime: '',
regAdviceSaveList: [
{
dbOpType: '1', // '1':新增, '2': 修改, '3': 删除 (签发操作时传1)
adviceType: 0, // 1:药品 , 2: 耗材 , 3:项目
requestId: 0, // 请求ID
chargeItemId: 0, // 费用项id
contentJson: '',
categoryCode: '', // 医嘱详细分类代码,来自数据字典
positionId: 0, // 物理位置id | 可能是 发药药房id,耗材房id,执行科室id
pharmacologyCategoryCode: '', // 药理分类代码
partPercent: 0.0, // 拆零比
partAttributeEnum: 0, // 拆分属性-门诊
executeNum: 0, // 执行次数
prescriptionNo: '', // 处方号
quantity: 0.0, // 数量
dispensePerDuration: 0, // 每次发药供应天数
unitCode: '', // 包装单位
unitPrice: 0.0, // 单价
totalPrice: 0.0, // 总价
definitionId: 0, // 费用定价主表ID
definitionDetailId: 0, // 费用定价子表ID
lotNumber: '', // 默认产品批号
statusEnum: 0, // 请求状态
categoryEnum: 0, // 请求类型枚举 (与categoryCode的区别这是程序内部使用的数字枚举用于逻辑判断)
adviceDefinitionId: 0, // 医嘱定义ID
adviceTableName: '', // 医嘱定义对应表名
adviceName: '', // 医嘱名称
minUnitQuantity: 0.0, // 请求小单位数量
patientId: 0, // 患者ID
practitionerId: 0, // 开方医生ID
locationId: 0, // 请求发起的位置ID
performLocation: 0, // 发放/执行位置ID
founderOrgId: 0, // 开方人科室ID
encounterId: 0, // 就诊ID
accountId: 0, // 账户ID
conditionId: 0, // 诊断ID
encounterDiagnosisId: 0, // 就诊诊断ID
conditionDefinitionId: 0, // 诊断定义ID | 分方使用
therapyEnum: 0, // 治疗类型
methodCode: '', // 用法代码
rateCode: '', // 使用频次代码
dose: 0.0, // 单次剂量
firstDose: 0.0, // 首次用量
doseUnitCode: '', // 剂量单位代码
skinTestFlag: 0, // 是否皮试标识
injectFlag: 0, // 是否为注射药物
groupId: 0, // 分组id, 一组药品共用一个id
packageId: 0, // 组套id
activityId: 0, // 活动(项目)定义id
ybClassEnum: 0, // 类别医保编码
chineseHerbsDoseQuantity: 0.0, // 中药付数
sufferingFlag: 0, // 代煎标识 | 0:否 , 1:是
dosageInstruction: '', // 用药说明
sortNumber: 0, // 排序号
basedOnId: 0, // 请求基于什么的ID
},
],
});
const adviceLoading = ref(false);
// 划价组套相关
const groupSetDialogVisible = ref(false);
const groupSetList = ref([]);
const groupSetLoading = ref(false);
const groupSetSearchText = ref('');
const groupSetRange = ref(2);
const selectedGroupSet = ref(null);
// 【优化核心】计算总金额 - 使用计算属性实现实时更新
const totalAmount = computed(() => {
return feeItemsList.value.reduce((sum, item) => {
const price = Number(item.unitPrice) || 0;
const quantity = Number(item.quantity) || 0;
return sum + price * quantity;
}, 0);
});
// 初始化
onMounted(() => {
const userStore = useUserStore();
userId.value = userStore.id;
orgId.value = userStore.orgId;
console.log(props.patientInfo, 'patientInfo in FeeDialog');
console.log('initialData in FeeDialog');
loadDepartmentOptions();
getAdviceBaseInfos();
getDiseaseInitLoc();
});
// 监听弹窗显示状态
watch(
() => props.visible,
(visible) => {
if (visible) {
executeTime.value = formatDateStr(new Date(), 'YYYY-MM-DD HH:mm:ss');
// 弹窗打开时重新加载科室和位置选项,确保数据最新
loadDepartmentOptions();
getDiseaseInitLoc(16);
} else {
resetData();
}
}
);
// 监听科室选项加载完成,为已添加的诊疗项目设置默认执行科室
watch(
() => departmentOptions.value,
(depts) => {
if (!depts || depts.length === 0) return;
feeItemsList.value.forEach(item => {
if (item.adviceType === 3 && !item.positionId) {
const patientOrgId = props.patientInfo.organizationId;
const matched = depts.find(d => String(d.id) === String(patientOrgId));
item.positionId = matched ? String(matched.id) : String(depts[0].id);
}
});
}
);
// 监听位置选项加载完成,为已添加的耗材项目设置默认位置
watch(
() => locationOptions.value,
(locs) => {
if (!locs || locs.length === 0) return;
feeItemsList.value.forEach(item => {
if (item.adviceType === 2 && !item.positionId) {
item.positionId = String(locs[0].value);
}
});
}
);
// 加载科室选项(支持树形/扁平两种数据结构)
function loadDepartmentOptions() {
getOrgList()
.then((res) => {
if (res.data) {
// 尝试从树形结构中取records[0].children
if (res.data.records && res.data.records.length > 0) {
if (res.data.records[0].children && res.data.records[0].children.length > 0) {
departmentOptions.value = res.data.records[0].children;
return;
}
// 如果 records[0] 有 id 和 name非树根节点直接用所有 records
if (res.data.records[0].id) {
departmentOptions.value = res.data.records;
return;
}
}
// 兜底:如果 records 不存在或为空,尝试直接使用 data 本身
if (Array.isArray(res.data)) {
departmentOptions.value = res.data;
return;
}
}
// 所有方式都失败,置空
departmentOptions.value = [];
})
.catch(() => {
console.warn('科室列表加载失败(可能无权限)');
departmentOptions.value = [];
});
}
// 加载收费组套数据
function getAdviceBaseInfos() {
adviceLoading.value = true;
queryParams.value.searchKey = searchText.value;
// 字典值(3=诊疗,4=耗材)映射为后端adviceType(2=耗材,3=诊疗)
if (adviceType.value === 4) {
queryParams.value.adviceTypes = [2];
} else if (adviceType.value === 3) {
queryParams.value.adviceTypes = [3];
} else {
queryParams.value.adviceTypes = [1, 2, 3];
}
queryParams.value.organizationId = orgId.value;
queryParams.value.pricingFlag = 1; // 划价标记
getAdviceBaseInfo(queryParams.value)
.then((res) => {
const list = res.data?.records || [];
// 药品(1)和耗材(2)必须有库存才能展示,诊疗(3)无库存概念不过滤
AdviceBaseInfoList.value = list.filter(item => {
if (item.adviceType === 1 || item.adviceType === 2) {
return item.inventoryList && item.inventoryList.length > 0;
}
return true;
});
})
.finally(() => {
adviceLoading.value = false;
});
}
function getDiseaseInitLoc() {
getDiseaseTreatmentInitLoc(16)
.then((response) => {
console.log('Disease Treatment Init Loc:', response);
locationOptions.value = response.data.locationOptions;
})
.catch(() => {
console.warn('位置列表加载失败(可能无权限)');
locationOptions.value = [];
});
}
// 下拉框模糊搜索过滤自定义filter-method配合element-plus filterable使用
function filterOptions(val, row, optionsKey) {
const key = row.adviceDefinitionId + '_' + optionsKey;
if (!val || val.trim() === '') {
delete filterKeywords.value[key];
} else {
filterKeywords.value[key] = val.toLowerCase();
}
}
function getFilteredOptions(row, optionsKey) {
const key = row.adviceDefinitionId + '_' + optionsKey;
const keyword = filterKeywords.value[key];
const options = optionsKey === 'departmentOptions' ? departmentOptions.value : locationOptions.value;
if (!keyword) {
return options;
}
return options.filter(item => {
const name = (item.name || item.label || '').toLowerCase();
const id = String(item.id || item.value || '').toLowerCase();
const py = (item.pyStr || '').toLowerCase();
return name.includes(keyword) || id.includes(keyword) || py.includes(keyword);
});
}
// 获取组套类型文本
function getItemType_Text(type) {
const map = { 2: '耗材', 3: '诊疗' };
return map[type] || '其他';
}
function getUnitCodeOptions(row) {
const unitCodes = [];
// 大单位:优先用 codecode 缺失时用字典文本兜底
if (row.unitCode != null && String(row.unitCode) !== '') {
unitCodes.push({ code: String(row.unitCode), codeText: row.unitCode_dictText });
} else if (row.unitCode_dictText) {
unitCodes.push({ code: row.unitCode_dictText, codeText: row.unitCode_dictText });
}
// 小单位:同上
if (row.minUnitCode != null && String(row.minUnitCode) !== '') {
unitCodes.push({ code: String(row.minUnitCode), codeText: row.minUnitCode_dictText });
} else if (row.minUnitCode_dictText) {
unitCodes.push({ code: row.minUnitCode_dictText, codeText: row.minUnitCode_dictText });
}
// 去重
const seenCodes = new Set();
const uniqueUnitCodes = unitCodes.filter((item) => {
if (!seenCodes.has(item.code)) {
seenCodes.add(item.code);
return true;
}
return false;
});
return uniqueUnitCodes;
}
// 单位变更处理
function unitCodeChange(row) {
// 获取价格
const price = row.priceList?.[0]?.price || 0;
// 根据选择的单位调整单价(统一用字符串比较)
if (String(row.selectUnitCode) === String(row.unitCode)) {
// 如果选择的是大单位 (如 "")
row.unitPrice = price.toFixed(6); // 单价就是原价
} else if (String(row.selectUnitCode) === String(row.minUnitCode)) {
// 如果选择的是小单位 (如 "")
row.unitPrice = (price / (row.partPercent || 1)).toFixed(6); // 单价 = 原价 / 拆零比
}
}
function handleQuantity(row) {
if (row.inventoryList && row.inventoryList.length > 0) {
const totalQuantity = row.inventoryList.reduce((sum, item) => sum + (item.quantity || 0), 0);
return totalQuantity.toString() + row.minUnitCode_dictText;
}
return 0;
}
// 选择收费项目 - 添加到费用列表
function selectChange(row) {
console.log('Added item:', row, props.patientInfo);
if (row == null) {
return;
}
// 校验库存
if (row.adviceType === 2) {
if (!row.inventoryList || row.inventoryList.length === 0) {
ElMessage.warning(`"${row.adviceName}" 库存信息不可用,无法添加`);
return;
}
const totalQuantity = row.inventoryList.reduce((sum, item) => sum + (item.quantity || 0), 0);
if (totalQuantity <= 0) {
ElMessage.warning(`"${row.adviceName}" 库存不足,无法添加`);
return;
}
}
// 检查是否已存在
const exists = feeItemsList.value.some(
(existing) => existing.adviceDefinitionId === row.adviceDefinitionId
);
if (exists) {
ElMessage.warning(`"${row.adviceName}" 已存在于费用列表中`);
return;
}
//获取价格
const price = row.priceList?.[0]?.price || 0;
//获取大小单位
const uniqueUnitCodes = getUnitCodeOptions(row);
// 设置默认单位:优先使用 minUnitCode小单位回退到 unitCode大单位
let defaultUnitCode = '';
if (row.minUnitCode) {
defaultUnitCode = String(row.minUnitCode);
} else if (row.unitCode) {
defaultUnitCode = String(row.unitCode);
} else if (row.minUnitCode_dictText) {
// 兜底:如果 minUnitCode 为空但字典文本存在,使用文本作为选项值
defaultUnitCode = row.minUnitCode_dictText;
} else if (row.unitCode_dictText) {
defaultUnitCode = row.unitCode_dictText;
}
// 如果默认单位不在 uniqueUnitCodes 中,添加兜底选项
if (defaultUnitCode && !uniqueUnitCodes.some(u => u.code === defaultUnitCode)) {
uniqueUnitCodes.push({ code: defaultUnitCode, codeText: defaultUnitCode });
}
// 设置默认执行科室/位置(统一转为字符串,避免 el-select 类型不匹配)
let defaultPositionId = undefined;
if (row.adviceType === 3 && departmentOptions.value.length > 0) {
// 诊疗:
// 1. 优先使用诊疗目录项目的"所属科室"row.orgId
// 2. 其次使用患者当前病房科室patientInfo.organizationId
// 3. 最后取第一个科室
const orgIdPriority = [row.orgId, props.patientInfo.organizationId];
for (const id of orgIdPriority) {
if (id) {
const matched = departmentOptions.value.find(d => String(d.id) === String(id));
if (matched) {
defaultPositionId = String(matched.id);
break;
}
}
}
if (!defaultPositionId) {
defaultPositionId = String(departmentOptions.value[0].id);
}
} else if (row.adviceType === 2 && locationOptions.value.length > 0) {
// 耗材:默认取第一个药房/耗材房
defaultPositionId = String(locationOptions.value[0].value);
}
//插入费用列表
feeItemsList.value.push({
...row,
uniqueUnitCodes: uniqueUnitCodes,
unitPrice: (price / (row.partPercent || 1)).toFixed(6), // 根据拆零比计算单价
quantity: 1,
positionId: defaultPositionId, // 默认执行科室/位置(字符串类型)
selectUnitCode: defaultUnitCode, // 默认选择小单位
});
}
// 取消操作
function handleCancel() {
emit('cancel');
dialogVisible.value = false;
}
// 确认操作
function handleConfirm() {
if (feeItemsList.value.length === 0) {
ElMessage.warning('请至少添加一项费用项目');
return;
}
if (!executeTime.value) {
ElMessage.warning('请选择执行时间');
return;
}
//验证数据的完整性
if (feeItemsList.value.some((item) => !item.positionId)) {
ElMessage.warning('请为所有费用项目选择执行科室/位置');
return;
}
convertDataManually();
emit('confirm', submitData);
dialogVisible.value = false;
}
// 手动转换数据
function convertDataManually() {
// 从 feeItemsList.value 中提取机构ID
const organizationId = props.patientInfo.organizationId;
const encounterId = props.patientInfo.encounterId;
const patientId = props.patientInfo.patientId;
const accountId = props.patientInfo.accountId;
console.log('Submitting data for patientId:', props.patientInfo);
if (!organizationId || !encounterId || !patientId || !accountId) {
ElMessage.error('患者信息不完整,无法提交费用项目');
return;
}
// 设置提交数据的机构ID和时间
submitData.organizationId = organizationId;
submitData.startTime = executeTime.value;
submitData.authoredTime = executeTime.value;
// 清空并重新填充 regAdviceSaveList
submitData.regAdviceSaveList = feeItemsList.value.map((item) => {
// ... (这里是和上面computed中完全相同的映射逻辑)
const mappedItem = {
dbOpType: '1', // 默认为新增操作
adviceType: item.adviceType, // 1:药品 , 2: 耗材 , 3:项目
requestId: null, // 新增项ID为0
chargeItemId: null, // feeItemsList中没有直接对应的chargeItemId可能需要从chargeItemDefinitionId映射或设为0
contentJson: feeItemsList.value.length > 0 ? JSON.stringify(feeItemsList.value) : '',
categoryCode: item.categoryCode, //医嘱详细分类代码,来自数据字典
positionId: item.positionId, // 物理位置id | 可能是 发药药房id,耗材房id,执行科室id
pharmacologyCategoryCode: item.pharmacologyCategoryCode || '', //药品性质 | 分方使用
partPercent: item.partPercent || 1.0, // 默认拆零比为1
partAttributeEnum: item.partAttributeEnum, //拆分属性-门诊
executeNum: 1, // 默认执行次数为1
prescriptionNo: '', // 处方号
quantity: item.quantity || 0.0, // 数量
// dispensePerDuration: 0, //每次发药供应天数
unitCode: item.unitCode || '',
unitPrice: item.unitPrice.toFixed(6) || 0.0, // 单价
totalPrice: ((item.quantity || 0) * (item.unitPrice || 0)).toFixed(6), // 计算总价
definitionId: item.chargeItemDefinitionId || 0,
definitionDetailId: 0,
lotNumber: item.defaultLotNumber || '',
// statusEnum: 1, // 假设默认为“待执行”状态
categoryEnum: item.categoryCode, // 简单映射项目类型枚举值可能与adviceType相同
adviceDefinitionId: item.adviceDefinitionId || '',
adviceTableName: item.adviceTableName || '',
adviceName: item.adviceName || '',
minUnitQuantity: item.quantity || 0.0, // 假设最小单位数量等于请求数量
patientId: patientId, // 需要从外部获取
practitionerId: userId.value, // 需要从外部获取
locationId: item.adviceType === 2 ? item.positionId : null, // 请求发起的位置ID
performLocation: item.adviceType === 2 ? item.positionId : null, // 发放位置ID
founderOrgId: organizationId, // 开方科室ID可能与机构ID相同
encounterId: encounterId, // 需要从外部获取
accountId: accountId, // 需要从外部获取
// conditionId: null, // 需要从外部获取
// encounterDiagnosisId: null, // 需要从外部获取
// conditionDefinitionId: null, // 需要从外部获取
therapyEnum: 2, // 默认治疗类型 1 长期 2 临时
methodCode: item.methodCode || '',
rateCode: item.rateCode || '',
dose: item.dose || null,
// firstDose: null, // 默认无首次用量
doseUnitCode: item.doseUnitCode || '',
skinTestFlag: item.skinTestFlag,
injectFlag: item.injectFlag, //注射药物 1:是 , 0:否
// groupId: null, // 默认不分组
// packageId: null, // 默认不是组套
activityId: item.adviceType === 3 ? item.adviceDefinitionId : null, // 如果是项目类型activityId可能等于adviceDefinitionId
// ybClassEnum: 0, // 默认医保类别
// chineseHerbsDoseQuantity: null, // 默认为0非中药
// sufferingFlag: null, // 默认不代煎
dosageInstruction: item.dosageInstruction || '', //用药说明
// sortNumber: 0, // 默认排序号
basedOnId: null, // 默认无基于ID
};
return mappedItem;
});
}
// 重置数据
function resetData() {
feeItemsList.value = [];
adviceType.value = '';
searchText.value = '';
executeTime.value = '';
}
// 划价组套相关功能
function openGroupSetDialog() {
console.log('openGroupSetDialog called');
groupSetDialogVisible.value = true;
groupSetSearchText.value = '';
selectedGroupSet.value = null;
loadGroupSets();
}
function loadGroupSets() {
groupSetLoading.value = true;
const params = { organizationId: orgId.value };
// 传递搜索关键字,后端 /group-package-for-order 虽不直接支持 searchKey
// 但保持参数传递以便后续扩展
if (groupSetSearchText.value && groupSetSearchText.value.trim()) {
params.searchKey = groupSetSearchText.value.trim();
}
getOrderGroup(params)
.then((res) => {
const data = res?.data || {};
let rawList = [];
if (groupSetRange.value === 1) {
rawList = data.personalList || [];
} else if (groupSetRange.value === 2) {
rawList = data.organizationList || [];
} else {
rawList = data.hospitalList || [];
}
// 客户端过滤:根据搜索关键字过滤组套名称
const keyword = groupSetSearchText.value?.trim()?.toLowerCase();
if (keyword) {
groupSetList.value = rawList.filter(item => {
const name = (item.name || item.Name || '').toLowerCase();
return name.includes(keyword);
});
} else {
groupSetList.value = rawList;
}
})
.catch((err) => {
console.warn('组套列表加载失败(可能无权限):', err);
ElMessage.warning('组套列表加载失败,当前暂无可用组套');
groupSetList.value = [];
})
.finally(() => {
groupSetLoading.value = false;
});
}
function getRangeText(row) {
// 后端未返回rangeCode根据当前选中的范围标签显示
const rangeMap = { 1: '个人', 2: '科室', 3: '全院' };
if (row.rangeCode_dictText) return row.rangeCode_dictText;
return rangeMap[groupSetRange.value] || '';
}
function handleGroupSetSelect(row) {
selectedGroupSet.value = row;
}
function applyGroupSet() {
if (!selectedGroupSet.value) {
ElMessage.warning('请先选择一个组套');
return;
}
const detailList = selectedGroupSet.value.detailList;
if (!detailList || detailList.length === 0) {
ElMessage.warning('该组套没有明细项');
return;
}
let successCount = 0;
detailList.forEach((item) => {
const orderDetail = item.orderDetailInfos || {};
const feeItem = {
adviceDefinitionId: item.orderDefinitionId || orderDetail.adviceDefinitionId,
adviceName: orderDetail.adviceName || item.orderDefinitionName || '未知项目',
adviceType: orderDetail.adviceType,
unitPrice: orderDetail.unitPrice,
minUnitPrice: orderDetail.minUnitPrice,
inventoryList: orderDetail.inventoryList || [],
priceList: orderDetail.priceList || [],
partPercent: orderDetail.partPercent || 1,
positionId: item.positionId || orderDetail.positionId,
defaultLotNumber: orderDetail.defaultLotNumber,
unitCode: item.unitCode || orderDetail.unitCode,
unitCode_dictText: item.unitCodeName || orderDetail.unitCode_dictText,
minUnitCode: orderDetail.minUnitCode,
doseUnitCode: orderDetail.doseUnitCode,
categoryCode: orderDetail.categoryCode,
pharmacologyCategoryCode: orderDetail.pharmacologyCategoryCode,
partAttributeEnum: orderDetail.partAttributeEnum,
chargeItemDefinitionId: orderDetail.chargeItemDefinitionId,
adviceTableName: orderDetail.adviceTableName,
methodCode: item.methodCode || orderDetail.methodCode,
rateCode: item.rateCode || orderDetail.rateCode,
dose: item.dose || orderDetail.dose,
doseQuantity: item.doseQuantity,
dispensePerDuration: item.dispensePerDuration || orderDetail.dispensePerDuration,
dosageInstruction: orderDetail.dosageInstruction,
skinTestFlag: orderDetail.skinTestFlag,
injectFlag: orderDetail.injectFlag,
quantity: item.quantity || 1,
chrgitmLv_dictText: orderDetail.chrgitmLv_dictText,
volume: orderDetail.volume,
};
selectChange(feeItem);
successCount++;
});
if (successCount > 0) {
ElMessage.success(`已添加 ${successCount} 项组套费用`);
}
groupSetDialogVisible.value = false;
}
</script>
<style scoped>
:deep(.el-dialog__body) {
padding: 20px;
}
.item-card {
background: #ffffff;
border: 1px solid #e4e7ed;
border-radius: 6px;
padding: 12px;
margin-bottom: 12px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.item-card:hover {
border-color: #409eff;
background-color: #ecf5ff;
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
}
.item-status {
display: flex;
align-items: center;
margin-bottom: 8px;
font-size: 14px;
font-weight: 500;
color: #606266;
}
.status-dot {
width: 8px;
height: 8px;
background-color: #67c23a;
border-radius: 50%;
margin-right: 6px;
}
.item-name {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 8px;
word-break: break-word;
}
</style>