Files
his/openhis-ui-vue3/src/views/inpatientNurse/InpatientBilling/components/feeDetailQuery.vue
zhangfei 9c3e603b94 Fix Bug #443: 手术计费:点击签发耗材时异常报错
当手术计费弹窗中点击"签发"耗材时,因耗材的locationId(发放库房)为空导致后端异常。
在DoctorStationAdviceAppServiceImpl.handDevice方法中,当locationId为null时,使用登录用户的科室ID作为默认值,
与NurseBillingAppService中的处理方式保持一致。
2026-05-08 09:14:18 +08:00

538 lines
16 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>
<div style="height: calc(100vh - 126px)">
<!-- 操作工具栏 -->
<div
style="
height: 51px;
border-bottom: 2px solid #e4e7ed;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 15px;
"
>
<div style="display: flex; align-items: center">
<!-- 日期选择tabs -->
<el-tabs
v-model="dateRange"
type="card"
class="date-tabs"
@tab-click="handleDateTabClick"
style="margin-right: 20px"
>
<el-tab-pane label="今日" name="today"></el-tab-pane>
<el-tab-pane label="昨日" name="yesterday"></el-tab-pane>
<!-- <el-tab-pane label="其他" name="other"></el-tab-pane> -->
</el-tabs>
<!-- 日期选择器 -->
<el-date-picker
v-model="dateRangeValue"
type="daterange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="handleDatePickerChange"
style="width: 240px; margin-right: 20px"
/>
<!-- 费用类型选择 -->
<el-select
v-model="formParams.chargeItemEnum"
placeholder="请选择费用类型"
clearable
style="width: 150px; margin-right: 20px"
>
<el-option
v-for="type in med_chrgitm_type"
:key="type.value"
:label="type.label"
:value="type.value"
/>
</el-select>
<!-- 执行科室选择 -->
<el-select
v-model="formParams.orgId"
placeholder="请选择执行科室"
clearable
style="width: 150px; margin-right: 20px"
>
<el-option
v-for="dept in orgOptions"
:key="dept.id"
:label="dept.name"
:value="dept.id"
/>
</el-select>
<!-- 查询按钮 -->
<el-button type="primary" @click="onReset">重置</el-button>
<el-button type="primary" @click="handleQuery">查询</el-button>
</div>
<div style="display: flex; align-items: center">
<!-- 导出按钮 -->
<el-button @click="handleExport">导出</el-button>
<!-- 打印按钮 -->
<!-- <el-button @click="handlePrint" style="margin-left: 15px">打印</el-button> -->
</div>
</div>
<!-- 费用明细列表区域 -->
<div
style="padding: 10px; background-color: #eef9fd; height: 100%; overflow-y: auto"
v-loading="loading"
>
<!-- 费用汇总信息 -->
<div style="background-color: white; padding: 15px; margin-bottom: 10px; border-radius: 4px">
<div style="display: flex; justify-content: space-between; align-items: center">
<div>
<h3 style="margin: 0; font-weight: normal; color: #303133">费用汇总</h3>
<p style="margin: 5px 0; color: #606266; font-size: 14px">
费用周期{{ formatDateRange() }}
</p>
</div>
<div style="text-align: right">
<p style="margin: 0; font-size: 18px; font-weight: bold; color: #ff4d4f">
合计金额¥{{ totalAmount.toFixed(2) }}
</p>
<p style="margin: 5px 0; color: #606266; font-size: 14px">
明细项数{{ feeDetailList.length }}
</p>
</div>
</div>
</div>
<el-table
ref="tableRef"
:data="feeDetailList"
border
style="width: 100%"
max-height="400px"
:header-cell-style="{ background: '#eef9fd !important' }"
@sort-change="handleSortChange"
>
<el-table-column label="项目名称" prop="chargeName" min-width="200" />
<el-table-column
label="费用类型"
prop="chargeItemEnum_enumText"
width="120"
align="center"
/>
<el-table-column label="单价" prop="unitPrice" width="100" align="center" sortable />
<el-table-column label="数量" prop="quantityValue" width="100" align="center" sortable />
<el-table-column label="金额" prop="totalPrice" width="100" align="center" sortable>
<template #default="scope">
<span style="color: #ff4d4f">{{ scope.row.totalPrice.toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column :prop="orgId" label="执行科室" width="120" align="center">
<template #default="scope">
{{ selectOrg(scope.row.orgId) }}
</template>
</el-table-column>
<el-table-column label="执行人" prop="practitioner" width="100" align="center" />
<el-table-column label="执行日期" prop="recordedTime" width="120" align="center">
<template #default="scope">
{{ moment(scope.row?.recordedTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="医保类型" prop="chrgitmLv_enumText" width="100" align="center">
</el-table-column>
<el-table-column label="备注" prop="remark" min-width="150" />
</el-table>
<!-- 分页 -->
<div style="margin-top: 15px; display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="formParams.pageNo"
v-model:page-size="formParams.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<!-- 无数据提示 -->
<el-empty v-if="!loading && feeDetailList.length === 0" description="暂无费用明细数据" />
</div>
<!-- 打印预览弹窗 -->
<el-dialog
v-model="printDialogVisible"
title="打印预览"
width="80%"
:close-on-click-modal="false"
>
<div id="print-content">
<div style="text-align: center; margin-bottom: 20px">
<h2 style="margin: 0">费用明细清单</h2>
<p style="margin: 5px 0">患者姓名{{ patientInfo || '未选择患者' }}</p>
<p style="margin: 5px 0">费用周期{{ formatDateRange() }}</p>
</div>
<table style="width: 100%; border-collapse: collapse">
<thead>
<tr style="background-color: #eef9fd">
<th style="border: 1px solid #e4e7ed; padding: 8px">项目名称</th>
<th style="border: 1px solid #e4e7ed; padding: 8px">费用类型</th>
<th style="border: 1px solid #e4e7ed; padding: 8px">单价</th>
<th style="border: 1px solid #e4e7ed; padding: 8px">数量</th>
<th style="border: 1px solid #e4e7ed; padding: 8px">金额</th>
<th style="border: 1px solid #e4e7ed; padding: 8px">执行科室</th>
<th style="border: 1px solid #e4e7ed; padding: 8px">执行日期</th>
</tr>
</thead>
<tbody>
<tr v-for="item in feeDetailList" :key="item.id">
<td style="border: 1px solid #e4e7ed; padding: 8px">{{ item.itemName }}</td>
<td style="border: 1px solid #e4e7ed; padding: 8px">{{ item.feeTypeName }}</td>
<td style="border: 1px solid #e4e7ed; padding: 8px">
{{ item.unitPrice.toFixed(2) }}
</td>
<td style="border: 1px solid #e4e7ed; padding: 8px">{{ item.quantity }}</td>
<td style="border: 1px solid #e4e7ed; padding: 8px">{{ item.amount.toFixed(2) }}</td>
<td style="border: 1px solid #e4e7ed; padding: 8px">{{ item.execDept }}</td>
<td style="border: 1px solid #e4e7ed; padding: 8px">{{ item.executeDate }}</td>
</tr>
</tbody>
<tfoot>
<tr style="background-color: #f5f7fa">
<td
colspan="4"
style="
border: 1px solid #e4e7ed;
padding: 8px;
text-align: right;
font-weight: bold;
"
>
合计
</td>
<td
colspan="3"
style="border: 1px solid #e4e7ed; padding: 8px; color: #ff4d4f; font-weight: bold"
>
¥{{ totalAmount.toFixed(2) }}
</td>
</tr>
</tfoot>
</table>
</div>
<template #footer>
<el-button @click="printDialogVisible = false">关闭</el-button>
<el-button type="primary" @click="doPrint">打印</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {computed, onMounted, reactive, ref, watch} from 'vue';
import moment from 'moment';
import {ElMessage} from 'element-plus';
import {patientInfoList} from '../../components/store/patient.js';
import {formatDateStr} from '@/utils/index';
import {getCostDetail} from './api.js';
import {getOrgList} from '../../../basicmanage/ward/components/api.js';
const { proxy } = getCurrentInstance();
const { med_chrgitm_type } = proxy.useDict('med_chrgitm_type');
// 响应式数据
const loading = ref(false);
const feeDetailList = ref([]);
const dateRange = ref('today'); // today, yesterday, other
const dateRangeValue = ref([]);
const execDepartment = ref('');
const departmentOptions = ref([]);
const feeTypeOptions = ref([]);
const tableRef = ref(null);
const patientInfo = ref('');
const orgOptions = ref([]);
const formParams = reactive({
chargeItemEnum: undefined,
orgId: undefined,
recordedTimeSTime: undefined,
recordedTimeETime: undefined,
pageSize: 10,
pageNo: 1,
});
const props = defineProps({
activeTab: {
type: String,
},
});
// 分页相关
const total = ref(0);
// 打印相关
const printDialogVisible = ref(false);
// 计算总金额
const totalAmount = computed(() => {
return feeDetailList.value.reduce((sum, item) => {
return sum + (item.totalPrice || 0);
}, 0);
});
// 初始化
onMounted(() => {
// 设置默认日期
const today = new Date();
dateRangeValue.value = [formatDateStr(today, 'YYYY-MM-DD'), formatDateStr(today, 'YYYY-MM-DD')];
});
watch(
() => patientInfoList,
(newValue) => {
if (newValue.value.length > 1) {
if (props.activeTab == 'expenseDetail') {
ElMessage({
type: 'error',
message: '一次只能查询一位患者的费用明细',
});
return;
}
}
if (newValue.value.length == 1) {
if (!(dateRangeValue.value == null || dateRangeValue.value == undefined)) {
formParams.recordedTimeSTime = dateRangeValue.value[0] + ' ' + '00:00:00';
formParams.recordedTimeETime = dateRangeValue.value[1] + ' ' + '23:59:59';
}
patientInfo.value = newValue.value[0];
formParams.encounterId = patientInfo.value.encounterId;
getTableList();
}
if (newValue.value.length <= 0) {
feeDetailList.value = [];
}
},
{ deep: true }
);
// 获取列表信息
const getTableList = async () => {
const params = formParams;
try {
const res = await getCostDetail(params);
feeDetailList.value = res.data.records;
total.value = res.data?.total;
console.log('data======>', JSON.stringify(feeDetailList.value));
} catch (error) {}
};
// 监听患者选择变化
function watchPatientSelection() {
// 定期检查患者选择状态变化
setInterval(() => {
if (patientInfoList.value && patientInfoList.value.length > 0) {
const selectedPatient = patientInfoList.value.find((patient) => patient.selected === true);
if (selectedPatient) {
patientInfo.value = selectedPatient.patientName || '';
} else {
patientInfo.value = '未选择患者';
}
} else {
patientInfo.value = '未选择患者';
}
}, 300);
}
/** 查询科室 */
const getLocationInfo = () => {
getOrgList().then((res) => {
orgOptions.value = res.data?.records[0]?.children;
console.log('orgOptions======>', JSON.stringify(orgOptions.value));
});
};
getLocationInfo();
// 映射
const selectOrg = (itemid) => {
if (!itemid) return '';
const item = orgOptions.value.find((item) => {
return item.id == itemid;
});
return item?.name || '';
};
// 重置
const onReset = () => {
const today = new Date();
dateRangeValue.value = [formatDateStr(today, 'YYYY-MM-DD'), formatDateStr(today, 'YYYY-MM-DD')];
formParams.orgId = undefined;
formParams.chargeItemEnum = undefined;
formParams.recordedTimeSTime = dateRangeValue.value[0] + ' ' + '00:00:00';
formParams.recordedTimeETime = dateRangeValue.value[1] + ' ' + '23:59:59';
getTableList();
};
// 加载费用类型选项
function loadFeeTypeOptions() {
// 模拟费用类型数据
feeTypeOptions.value = [
{ label: '检查费', value: 'examine' },
{ label: '治疗费', value: 'treatment' },
{ label: '药品费', value: 'medicine' },
{ label: '材料费', value: 'material' },
{ label: '床位费', value: 'bed' },
{ label: '其他费用', value: 'others' },
];
}
// 处理日期tabs点击
function handleDateTabClick(tab) {
const rangeType = tab.paneName;
const today = new Date();
const yesterday = new Date();
yesterday.setDate(today.getDate() - 1);
switch (rangeType) {
case 'today':
dateRangeValue.value = [
formatDateStr(today, 'YYYY-MM-DD'),
formatDateStr(today, 'YYYY-MM-DD'),
];
break;
case 'yesterday':
dateRangeValue.value = [
formatDateStr(yesterday, 'YYYY-MM-DD'),
formatDateStr(yesterday, 'YYYY-MM-DD'),
];
break;
// other 情况保持用户选择的值
}
if (!(dateRangeValue.value == null || dateRangeValue.value == undefined)) {
formParams.recordedTimeSTime = dateRangeValue.value[0] + ' ' + '00:00:00';
formParams.recordedTimeETime = dateRangeValue.value[1] + ' ' + '23:59:59';
}
}
// 处理日期选择器变化
function handleDatePickerChange() {
if (dateRangeValue.value.length > 0) {
// dateRange.value = 'other';
} else {
formParams.recordedTimeSTime = undefined;
formParams.recordedTimeETime = undefined;
}
}
// 格式化日期范围显示
function formatDateRange() {
if (dateRangeValue.value && dateRangeValue.value.length === 2) {
return `${dateRangeValue.value[0]}${dateRangeValue.value[1]}`;
}
return '';
}
// 查询按钮点击
function handleQuery() {
console.log('params=======>', formParams);
getTableList();
}
// 处理排序变化
function handleSortChange({ prop, order }) {
const sortedData = [...feeDetailList.value];
if (order === 'ascending') {
sortedData.sort((a, b) => (a[prop] > b[prop] ? 1 : -1));
} else if (order === 'descending') {
sortedData.sort((a, b) => (a[prop] < b[prop] ? 1 : -1));
}
feeDetailList.value = sortedData;
}
// 处理分页大小变化
function handleSizeChange(newSize) {
formParams.pageSize = newSize;
formParams.pageNo = 1;
getTableList();
}
// 处理当前页变化
function handleCurrentChange(newCurrent) {
formParams.pageNo = newCurrent;
getTableList();
}
// 导出
async function handleExport() {
if (feeDetailList.value.length === 0) {
ElMessage.warning('暂无数据可导出');
return;
}
try {
// 实际项目中这里应该调用导出API或使用Excel库生成文件
await proxy.$download.downloadGet(
'/inhospitalnursestation/nursebilling/excel-out',
{
...formParams,
},
`dict_${new Date().getTime()}.xlsx`
);
} catch (error) {}
}
// 打印预览
function handlePrint() {
if (feeDetailList.value.length === 0) {
ElMessage.warning('暂无数据可打印');
return;
}
printDialogVisible.value = true;
}
// 执行打印 - 使用 hiprint
function doPrint() {
try {
// 获取要打印的内容
const printContent = document.getElementById('print-content');
if (printContent) {
previewPrint(printContent);
} else {
ElMessage.warning('未找到打印内容');
}
} catch (e) {
ElMessage.error('打印失败');
console.error('Print error:', e);
}
}
// 暴露方法供父组件调用
defineExpose({
handleQuery,
});
</script>
<style scoped>
/* 日期tabs样式 */
.date-tabs .el-tabs__header {
margin-bottom: 0;
}
.date-tabs .el-tabs__content {
display: none;
}
:deep(.el-table__header th) {
background-color: #eef9fd !important;
}
:deep(.el-table__row:hover > td) {
background-color: #eef9fd !important;
}
</style>