611 lines
19 KiB
Vue
611 lines
19 KiB
Vue
<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="feeType"
|
||
placeholder="请选择费用类型"
|
||
clearable
|
||
style="width: 150px; margin-right: 20px"
|
||
>
|
||
<el-option
|
||
v-for="type in feeTypeOptions"
|
||
:key="type.value"
|
||
:label="type.label"
|
||
:value="type.value"
|
||
/>
|
||
</el-select>
|
||
|
||
<!-- 执行科室选择 -->
|
||
<el-select
|
||
v-model="execDepartment"
|
||
placeholder="请选择执行科室"
|
||
clearable
|
||
style="width: 150px; margin-right: 20px"
|
||
>
|
||
<el-option
|
||
v-for="dept in departmentOptions"
|
||
:key="dept.value"
|
||
:label="dept.label"
|
||
:value="dept.value"
|
||
/>
|
||
</el-select>
|
||
|
||
<!-- 查询按钮 -->
|
||
<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%"
|
||
:header-cell-style="{ background: '#eef9fd !important' }"
|
||
@sort-change="handleSortChange"
|
||
>
|
||
<el-table-column label="项目名称" prop="itemName" min-width="200" />
|
||
<el-table-column label="费用类型" prop="feeTypeName" width="120" align="center" />
|
||
<el-table-column label="单价" prop="unitPrice" width="100" align="center" sortable />
|
||
<el-table-column label="数量" prop="quantity" width="100" align="center" sortable />
|
||
<el-table-column label="金额" prop="amount" width="100" align="center" sortable>
|
||
<template #default="scope">
|
||
<span style="color: #ff4d4f">{{ scope.row.amount.toFixed(2) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="执行科室" prop="execDept" width="120" align="center" />
|
||
<el-table-column label="执行人" prop="executor" width="100" align="center" />
|
||
<el-table-column label="执行日期" prop="executeDate" width="120" align="center" />
|
||
<el-table-column label="医保类型" prop="insuranceType" width="100" align="center">
|
||
<template #default="scope">
|
||
<el-tag v-if="scope.row.insuranceType" size="small">{{ scope.row.insuranceType }}</el-tag>
|
||
</template>
|
||
</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="currentPage"
|
||
v-model:page-size="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 { ref, computed, onMounted } from 'vue';
|
||
import { ElMessage } from 'element-plus';
|
||
import { patientInfoList } from '../../medicalOrderExecution/store/patient.js';
|
||
import { formatDateStr } from '@/utils/index';
|
||
|
||
// 响应式数据
|
||
const loading = ref(false);
|
||
const feeDetailList = ref([]);
|
||
const dateRange = ref('today'); // today, yesterday, other
|
||
const dateRangeValue = ref([]);
|
||
const feeType = ref('');
|
||
const execDepartment = ref('');
|
||
const departmentOptions = ref([]);
|
||
const feeTypeOptions = ref([]);
|
||
const tableRef = ref(null);
|
||
const patientInfo = ref('');
|
||
|
||
// 分页相关
|
||
const currentPage = ref(1);
|
||
const pageSize = ref(20);
|
||
const total = ref(0);
|
||
|
||
// 打印相关
|
||
const printDialogVisible = ref(false);
|
||
|
||
// 计算总金额
|
||
const totalAmount = computed(() => {
|
||
return feeDetailList.value.reduce((sum, item) => {
|
||
return sum + (item.amount || 0);
|
||
}, 0);
|
||
});
|
||
|
||
// 初始化
|
||
onMounted(() => {
|
||
// 设置默认日期
|
||
const today = new Date();
|
||
dateRangeValue.value = [formatDateStr(today, 'YYYY-MM-DD'), formatDateStr(today, 'YYYY-MM-DD')];
|
||
|
||
// 加载科室选项
|
||
loadDepartmentOptions();
|
||
|
||
// 加载费用类型选项
|
||
loadFeeTypeOptions();
|
||
|
||
// 监听患者选择变化
|
||
watchPatientSelection();
|
||
});
|
||
|
||
// 监听患者选择变化
|
||
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);
|
||
}
|
||
|
||
// 加载科室选项
|
||
function loadDepartmentOptions() {
|
||
// 模拟科室数据
|
||
departmentOptions.value = [
|
||
{ label: '内科', value: 'internal' },
|
||
{ label: '外科', value: 'surgery' },
|
||
{ label: '儿科', value: 'pediatrics' },
|
||
{ label: '妇产科', value: 'obstetrics' },
|
||
{ label: '检验科', value: 'laboratory' },
|
||
{ label: '影像科', value: 'imaging' },
|
||
{ label: '其他科室', value: 'others' },
|
||
];
|
||
}
|
||
|
||
// 加载费用类型选项
|
||
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 情况保持用户选择的值
|
||
}
|
||
}
|
||
|
||
// 处理日期选择器变化
|
||
function handleDatePickerChange() {
|
||
if (dateRangeValue.value.length > 0) {
|
||
dateRange.value = 'other';
|
||
}
|
||
}
|
||
|
||
// 格式化日期范围显示
|
||
function formatDateRange() {
|
||
if (dateRangeValue.value && dateRangeValue.value.length === 2) {
|
||
return `${dateRangeValue.value[0]} 至 ${dateRangeValue.value[1]}`;
|
||
}
|
||
return '';
|
||
}
|
||
|
||
// 生成模拟数据
|
||
function generateMockData() {
|
||
// 费用类型映射
|
||
const feeTypeMap = {
|
||
'examine': '检查费',
|
||
'treatment': '治疗费',
|
||
'medicine': '药品费',
|
||
'material': '材料费',
|
||
'bed': '床位费',
|
||
'others': '其他费用'
|
||
};
|
||
|
||
// 科室映射
|
||
const deptMap = {
|
||
'internal': '内科',
|
||
'surgery': '外科',
|
||
'pediatrics': '儿科',
|
||
'obstetrics': '妇产科',
|
||
'laboratory': '检验科',
|
||
'imaging': '影像科',
|
||
'others': '其他科室'
|
||
};
|
||
|
||
// 项目数据池
|
||
const itemPools = {
|
||
'examine': ['血常规检查', '尿常规检查', '肝功能检查', '肾功能检查', '胸部CT', '心电图', 'B超'],
|
||
'treatment': ['静脉输液', '肌肉注射', '氧气吸入', '导尿', '换药', '雾化吸入'],
|
||
'medicine': ['抗生素注射液', '维生素C片', '感冒药', '止痛药', '降压药', '消炎药'],
|
||
'material': ['一次性输液器', '注射器', '医用棉花', '纱布', '胶带', '一次性手套'],
|
||
'bed': ['普通病房床位', 'ICU床位', '单人病房床位', '双人病房床位'],
|
||
'others': ['护理费', '诊疗费', '挂号费', '病历费']
|
||
};
|
||
|
||
// 合并所有项目到allItems数组中(修复:添加这部分代码)
|
||
let allItems = [];
|
||
Object.keys(itemPools).forEach(type => {
|
||
itemPools[type].forEach(item => {
|
||
allItems.push({
|
||
type: type,
|
||
name: item
|
||
});
|
||
});
|
||
});
|
||
|
||
// 筛选条件
|
||
if (feeType.value) {
|
||
allItems = allItems.filter(item => item.type === feeType.value);
|
||
}
|
||
|
||
// 生成模拟数据
|
||
const mockData = [];
|
||
const baseDate = new Date(dateRangeValue.value[0]);
|
||
const endDate = new Date(dateRangeValue.value[1]);
|
||
const daysDiff = Math.ceil((endDate - baseDate) / (1000 * 60 * 60 * 24)) + 1;
|
||
|
||
// 执行人池
|
||
const executors = ['护士A', '护士B', '医生A', '医生B', '技师A'];
|
||
|
||
// 生成数据
|
||
let id = 1;
|
||
for (let day = 0; day < daysDiff; day++) {
|
||
const currentDate = new Date(baseDate);
|
||
currentDate.setDate(baseDate.getDate() + day);
|
||
const dateStr = formatDateStr(currentDate, 'YYYY-MM-DD');
|
||
|
||
// 每天生成3-8条记录
|
||
const recordsCount = Math.floor(Math.random() * 6) + 3;
|
||
for (let i = 0; i < recordsCount; i++) {
|
||
// 随机选择一个项目
|
||
const randomItemIndex = Math.floor(Math.random() * allItems.length);
|
||
const selectedItem = allItems[randomItemIndex];
|
||
|
||
// 随机选择一个科室
|
||
const deptKeys = Object.keys(deptMap);
|
||
const randomDeptKey = deptKeys[Math.floor(Math.random() * deptKeys.length)];
|
||
|
||
// 如果有科室筛选且不符合,则跳过
|
||
if (execDepartment.value && randomDeptKey !== execDepartment.value) {
|
||
continue;
|
||
}
|
||
|
||
// 生成随机单价和数量
|
||
const unitPrice = Math.floor(Math.random() * 190) + 10; // 10-200元
|
||
const quantity = Math.floor(Math.random() * 3) + 1; // 1-4
|
||
|
||
// 医保类型
|
||
const insuranceTypes = ['', '甲类', '乙类', '丙类'];
|
||
const insuranceType = insuranceTypes[Math.floor(Math.random() * insuranceTypes.length)];
|
||
|
||
mockData.push({
|
||
id: `item-${id++}`,
|
||
itemName: selectedItem.name,
|
||
feeType: selectedItem.type,
|
||
feeTypeName: feeTypeMap[selectedItem.type],
|
||
unitPrice: unitPrice,
|
||
quantity: quantity,
|
||
amount: unitPrice * quantity,
|
||
execDept: deptMap[randomDeptKey],
|
||
executor: executors[Math.floor(Math.random() * executors.length)],
|
||
executeDate: dateStr,
|
||
insuranceType: insuranceType,
|
||
remark: ''
|
||
});
|
||
}
|
||
}
|
||
|
||
return mockData;
|
||
}
|
||
|
||
// 查询按钮点击
|
||
function handleQuery() {
|
||
// 添加调试日志,查看患者列表数据结构
|
||
console.log('患者列表数据:', patientInfoList.value);
|
||
|
||
// 更灵活的患者选择检测逻辑
|
||
let selectedPatient = null;
|
||
|
||
if (patientInfoList.value && patientInfoList.value.length > 0) {
|
||
// 尝试查找选中状态的患者
|
||
selectedPatient = patientInfoList.value.find(patient =>
|
||
patient.selected === true ||
|
||
patient.checked === true ||
|
||
patient.isChecked === true ||
|
||
(typeof patient.selected === 'string' && patient.selected === '1')
|
||
);
|
||
|
||
// 如果没有明确选中的患者,就使用列表中的第一个患者
|
||
if (!selectedPatient) {
|
||
selectedPatient = patientInfoList.value[0];
|
||
}
|
||
}
|
||
|
||
// 即使没有明确选中的患者标志,也应该允许查询
|
||
if (!selectedPatient) {
|
||
ElMessage.warning('请先选择患者');
|
||
return;
|
||
}
|
||
|
||
// 更新患者信息显示 - 修复:确保正确获取患者姓名
|
||
const patientName = selectedPatient.patientName ||
|
||
selectedPatient.name ||
|
||
selectedPatient.patientInfo ||
|
||
selectedPatient.patient ||
|
||
'未命名患者';
|
||
patientInfo.value = patientName;
|
||
|
||
loading.value = true;
|
||
|
||
// 模拟API调用延迟
|
||
setTimeout(() => {
|
||
try {
|
||
// 使用模拟数据
|
||
const allData = generateMockData();
|
||
total.value = allData.length;
|
||
|
||
// 分页处理
|
||
const start = (currentPage.value - 1) * pageSize.value;
|
||
const end = start + pageSize.value;
|
||
feeDetailList.value = allData.slice(start, end);
|
||
} catch (error) {
|
||
console.error('查询错误:', error);
|
||
ElMessage.error('查询失败,请重试');
|
||
feeDetailList.value = [];
|
||
total.value = 0;
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}, 500);
|
||
}
|
||
|
||
// 处理排序变化
|
||
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) {
|
||
pageSize.value = newSize;
|
||
currentPage.value = 1;
|
||
handleQuery();
|
||
}
|
||
|
||
// 处理当前页变化
|
||
function handleCurrentChange(newCurrent) {
|
||
currentPage.value = newCurrent;
|
||
handleQuery();
|
||
}
|
||
|
||
// 导出
|
||
function handleExport() {
|
||
if (feeDetailList.value.length === 0) {
|
||
ElMessage.warning('暂无数据可导出');
|
||
return;
|
||
}
|
||
|
||
// 模拟导出操作
|
||
ElMessage.success('导出成功');
|
||
// 实际项目中这里应该调用导出API或使用Excel库生成文件
|
||
}
|
||
|
||
// 打印预览
|
||
function handlePrint() {
|
||
if (feeDetailList.value.length === 0) {
|
||
ElMessage.warning('暂无数据可打印');
|
||
return;
|
||
}
|
||
|
||
printDialogVisible.value = true;
|
||
}
|
||
|
||
// 执行打印
|
||
function doPrint() {
|
||
try {
|
||
// 获取要打印的内容
|
||
const printContent = document.getElementById('print-content').innerHTML;
|
||
|
||
// 创建临时窗口
|
||
const printWindow = window.open('', '_blank');
|
||
|
||
// 写入内容
|
||
printWindow.document.write(`
|
||
<html>
|
||
<head>
|
||
<title>费用明细清单</title>
|
||
<style>
|
||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||
table { width: 100%; border-collapse: collapse; }
|
||
th, td { border: 1px solid #ccc; padding: 8px; }
|
||
th { background-color: #f2f2f2; }
|
||
tfoot { font-weight: bold; }
|
||
.total-row { background-color: #f5f5f5; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
${printContent}
|
||
</body>
|
||
</html>
|
||
`);
|
||
|
||
// 打印
|
||
printWindow.document.close();
|
||
printWindow.focus();
|
||
printWindow.print();
|
||
} 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> |