版本更新

This commit is contained in:
Zhang.WH
2025-09-03 15:54:41 +08:00
parent 0b93d16b64
commit 8f82322d10
3290 changed files with 154339 additions and 23829 deletions

View File

@@ -0,0 +1,97 @@
import request from '@/utils/request'
/**
* 收费患者列表
*/
export function getList(queryParams) {
return request({
url: '/charge-manage/refund/encounter-patient-page',
method: 'get',
params: queryParams
})
}
/**
* 患者退费账单
*/
export function getRefundList(params) {
return request({
url: '/charge-manage/refund/patient-refund',
method: 'get',
params: params
})
}
/**
* 未退费账单列表
*/
export function getChargeItemIds(encounterId) {
return request({
url: '/charge-manage/refund/regenerate_charge?encounterId=' + encounterId,
method: 'get',
})
}
/**
* 退费
*/
export function refund(data) {
return request({
url: '/payment/payment/uncharge',
method: 'post',
data: data
})
}
/**
* 初始化
*/
export function init() {
return request({
url: '/charge-manage/refund/init',
method: 'get',
})
}
/**
* 校验退药
*/
export function validReturnDrug(params) {
return request({
url: '/charge-manage/refund/verify_refund?chargeItemIdList=' + params,
method: 'get',
})
}
/**
* 获取退款详情列表
*/
export function getReturnDetail(data) {
return request({
url: '/payment/payment/detail',
method: 'post',
data: data
})
}
/**
* 自动退耗材
*/
export function renturnDispenseMedical(data) {
return request({
url: '/pharmacy-manage/return-medicine/medicine-return',
method: 'put',
data: data
})
}
/**
* 获取退药列表
*/
export function getReturnMedicineList(data) {
return request({
url: '/pharmacy-manage/return-medicine/medicine-return-list',
method: 'get',
params: data
})
}

View File

@@ -0,0 +1,435 @@
<template>
<el-dialog
title="确认退费"
v-model="props.open"
width="700px"
append-to-body
destroy-on-close
@close="close"
>
<div>
<el-text size="large" style="display: block; margin-bottom: 15px">
退费日期{{ currentDate }}
</el-text>
<el-text size="large">费用性质{{ '自费' }}</el-text>
<div class="amount-row">
<el-text size="large">应退金额</el-text>
<el-text size="large" type="primary" class="amount">
{{ props.totalAmount.toFixed(2) + ' 元' }}
</el-text>
</div>
<!-- 自费支付 -->
<div class="payment-container">
<div v-for="(item, index) in formData.selfPay" :key="index" class="payment-item">
<span>退费方式</span>
<el-select
v-model="item.payEnum"
placeholder="选择退费方式"
style="width: 160px"
@change="clearAmount(index)"
>
<el-option
v-for="payEnum in selfPayMethods"
:key="payEnum.value"
:label="payEnum.label"
:value="payEnum.value"
:disabled="isMethodDisabled(payEnum.value)"
/>
</el-select>
<span>退费金额</span>
<div class="suffix-wrapper">
<el-input-number
v-model="item.amount"
:precision="2"
:min="0"
:max="getMax(index)"
:controls="false"
placeholder="金额"
class="amount-input"
@change="handleAmountChange"
/>
<span class="suffix-text"></span>
</div>
<el-button
type="danger"
circle
:icon="Delete"
@click="removePayment(index)"
v-if="index > 0"
/>
</div>
<div class="payment-container" style="position: relative">
<span style="position: absolute; top: 5px">退费原因</span>
<el-input
type="textarea"
:rows="2"
v-model="reason"
placeholder="退费原因"
class="reason-textarea"
@change="handleAmountChange"
/>
</div>
<div class="add-payment">
<el-button
type="primary"
plain
@click="addPayment"
:disabled="formData.selfPay.length >= 4 || remainingAmount <= 0"
>
添加退费方式
</el-button>
<el-text v-if="remainingAmount <= 0" type="danger" class="tip">
金额已满足应退不可继续添加
</el-text>
</div>
</div>
<div>
<el-table :data="props.details" max-height="200" border>
<el-table-column prop="payEnum_dictText" label="支付类型" align="center" />
<el-table-column
prop="amount"
label="金额"
header-align="center"
align="right"
width="200"
>
<template #default="scope">
{{ scope.row.amount ? scope.row.amount + ' 元' : '-' }}
</template>
</el-table-column>
</el-table>
</div>
<!-- 金额汇总 -->
<div class="summary">
<el-space :size="30">
<div class="summary-item">
<el-text type="info">实退合计</el-text>
<el-text type="success">{{ displayAmount + ' 元' }}</el-text>
</div>
<!-- <div class="summary-item">
<el-text type="info">应找零</el-text>
<el-text type="warning">{{ returnedAmount + ' 元' }}</el-text>
</div> -->
</el-space>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submit"> </el-button>
<el-button @click="close"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { refund, renturnDispenseMedical, getReturnMedicineList } from './api';
import { computed, watch, reactive, ref, getCurrentInstance } from 'vue';
import { Delete } from '@element-plus/icons-vue';
import useUserStore from '@/store/modules/user';
const props = defineProps({
open: {
type: Boolean,
default: false,
},
totalAmount: {
type: Number,
default: 0.0,
},
category: {
type: String,
},
paymentId: {
type: String,
},
patientInfo: {
type: Object,
default: undefined,
},
chargeItemIds: {
type: [],
default: [],
},
medicineReturnList: {
type: [],
default: [],
},
details: {
type: Object,
default: undefined,
},
});
const { proxy } = getCurrentInstance();
const reason = ref('');
const userStore = useUserStore();
const formData = reactive({
totalAmount: 0,
selfPay: [{ payEnum: 220100, amount: 0.0, payLevelEnum: 2 }],
medicalInsurance: {
account: '',
poolPay: 0,
personalPay: 0,
},
});
watch(
() => props.totalAmount,
(newValue) => {
formData.totalAmount = newValue;
formData.selfPay[0].amount = newValue;
}
);
const emit = defineEmits(['close']);
function submit() {
console.log(props.chargeItemIds);
if (parseFloat(displayAmount.value) < formData.totalAmount) {
proxy.$modal.msgError('请输入正确的金额');
return;
}
if (userStore.fixmedinsCode == '1123123') {
getReturnMedicineList({ encounterId: props.patientInfo.encounterId, refundStatus: 11 }).then(
(res) => {
let returnMedicineList = [];
returnMedicineList = res.data
.filter((item) => {
return item.serviceTable == 'wor_device_request';
})
.map((item) => {
return {
requestId: item.requestId,
dispenseId: item.dispenseId,
tableName: 'wor_device_request',
};
});
renturnDispenseMedical(returnMedicineList).then((res) => {
if (res.code == 200) {
refund({
paymentEnum: 0,
kindEnum: 1,
patientId: props.patientInfo.patientId,
id: props.paymentId,
encounterId: props.patientInfo.encounterId,
chargeItemIds: props.chargeItemIds,
paymentDetails: formData.selfPay,
reason: reason.value,
ybFlag: '0',
eleFlag: '0',
// returnedAmount: parseFloat(returnedAmount.value),
}).then((res) => {
if (res.code == 200) {
emit('close', 'success');
}
});
}
});
}
);
} else {
refund({
paymentEnum: 0,
kindEnum: 1,
patientId: props.patientInfo.patientId,
id: props.paymentId,
encounterId: props.patientInfo.encounterId,
chargeItemIds: props.chargeItemIds,
paymentDetails: formData.selfPay,
reason: reason.value,
ybFlag: '0',
eleFlag: '0',
// returnedAmount: parseFloat(returnedAmount.value),
}).then((res) => {
if (res.code == 200) {
// 长春大学自动退耗材
emit('close', 'success');
}
});
}
}
const currentDate = ref(new Date().toLocaleString());
const selfPayMethods = [
{ label: '现金', value: 220400 },
{ label: '微信', value: 220100 },
{ label: '支付宝', value: 220200 },
{ label: '银联', value: 220300 },
];
// 计算剩余可输入金额
const remainingAmount = computed(() => {
return (
formData.totalAmount - formData.selfPay.reduce((sum, item) => sum + Number(item.amount), 0)
);
});
// 获取单个支付方式的最大可输入金额
const getMax = (index) => {
const otherSum = formData.selfPay.reduce(
(sum, item, i) => (i !== index ? sum + Number(item.amount) : sum),
0
);
if (formData.selfPay[index].payEnum == 220400) {
return formData.totalAmount + 100 - otherSum;
}
return formData.totalAmount - otherSum;
};
// 检查支付方式是否已使用
const isMethodDisabled = (payEnum) => {
return formData.selfPay.some((item) => item.payEnum === payEnum);
};
const handleAmountChange = () => {
// 不需要在这里直接设置 returnedAmount依赖 computed 属性
};
const addPayment = () => {
if (remainingAmount.value <= 0) {
return;
}
formData.selfPay.push({ payEnum: '', amount: remainingAmount.value });
};
const removePayment = (index) => {
formData.selfPay.splice(index, 1);
};
const clearAmount = (index) => {
// formData.selfPay[index].amount = 0;
};
// 计算属性
const displayAmount = computed(() => {
return formData.selfPay.reduce((sum, item) => sum + (Number(item.amount) || 0), 0).toFixed(2);
});
const returnedAmount = computed(() => {
const display = parseFloat(displayAmount.value);
if (isNaN(display) || display <= 0) {
return '0.00';
}
const returned = display - formData.totalAmount;
return returned >= 0 ? returned.toFixed(2) : '0.00';
});
function close() {
emit('close');
}
</script>
<style lang="scss" scoped>
:deep(.pagination-container .el-pagination) {
right: 20px !important;
}
.charge-container {
max-width: 600px;
margin: 20px auto;
padding: 20px 30px;
}
.header {
margin-bottom: 10px;
}
.amount-row {
display: flex;
align-items: center;
gap: 15px;
margin: 15px 0;
}
.amount {
font-size: 20px;
font-weight: bold;
}
.payment-type {
margin: 15px 0;
}
.payment-container {
margin: 15px 0;
}
.payment-item {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 12px;
}
.amount-input {
width: 140px;
}
.reason-textarea {
margin-left: 80px;
width: 59%;
}
.add-payment {
margin-top: 10px;
display: flex;
align-items: center;
gap: 10px;
}
.tip {
font-size: 12px;
}
.summary {
margin: 25px 0;
padding: 15px;
background-color: #f8f9fa;
border-radius: 4px;
}
.summary-item {
display: flex;
align-items: center;
gap: 10px;
}
.action-buttons {
text-align: center;
margin-top: 25px;
}
.el-text.el-text--success {
font-size: 18px !important;
font-weight: 500;
}
.el-text.el-text--warning {
font-size: 18px !important;
font-weight: 500;
}
.suffix-wrapper {
position: relative;
display: inline-block;
}
.suffix-text {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
color: #999;
pointer-events: none; /* 避免点击干扰 */
}
/* 调整输入框内边距 */
.amount-input .el-input__inner {
padding-right: 30px !important;
}
</style>

View File

@@ -0,0 +1,361 @@
<template>
<div style="display: flex; justify-content: space-between" class="app-container">
<el-card style="width: 30%">
<template #header>
<span style="vertical-align: middle">患者列表</span>
</template>
<div style="width: 100%">
<el-input
v-model="queryParams.patientName"
placeholder="请输入患者名"
clearable
style="width: 49%; margin-bottom: 10px; margin-right: 10px"
@keyup.enter="getPatientList"
>
<template #append>
<el-button icon="Search" @click="getPatientList" />
</template>
</el-input>
<el-select
v-model="queryParams.statusEnum"
style="width: 49%; margin-bottom: 10px"
placeholder="收费状态"
@change="getPatientList"
>
<el-option
v-for="item in chargeOption"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-date-picker
v-model="maxBillDate"
type="daterange"
range-separator="~"
start-placeholder="开始时间"
end-placeholder="结束时间"
placement="bottom"
:clearable="false"
value-format="YYYY-MM-DD"
style="width: 84%; margin-bottom: 10px; margin-right: 10px"
/>
<el-button type="primary" style="margin-bottom: 10px" @click="getPatientList">
搜索
</el-button>
<el-table
ref="patientListRef"
height="630"
:data="patientList"
row-key="encounterId"
@cell-click="clickRow"
highlight-current-row
width=""
>
<el-table-column label="病历号" align="center" prop="encounterBusNo" />
<el-table-column label="姓名" align="center" prop="patientName" />
<!-- <el-table-column label="时间" align="center" prop="startTime">
<template #default="scope">
{{ formatDate(scope.row.startTime) }}
</template>
</el-table-column> -->
<el-table-column label="收费状态" align="center" prop="statusEnum_enumText" />
</el-table>
</div>
</el-card>
<div style="width: 69%">
<el-card style="margin-bottom: 20px; height: 15%">
<template #header>
<span style="vertical-align: middle">基本信息</span>
</template>
<el-descriptions :column="4">
<el-descriptions-item label="就诊号:">
{{ patientInfo.encounterId }}
</el-descriptions-item>
<el-descriptions-item label="姓名:">
{{ patientInfo.patientName }}
</el-descriptions-item>
<el-descriptions-item label="性别:">
{{ patientInfo.genderEnum_enumText }}
</el-descriptions-item>
<el-descriptions-item label="年龄:">
{{ patientInfo.age }}
</el-descriptions-item>
<!-- <el-descriptions-item label="合同类型:">
{{ patientInfo.categoryEnum_enumText }}
</el-descriptions-item>
<el-descriptions-item label="结算时间:">
{{ patientInfo.billDate ? formatDate(patientInfo.billDate) : '' }}
</el-descriptions-item>
<el-descriptions-item label="账单总额:">
{{ patientInfo.totalAmount ? patientInfo.totalAmount.toFixed(2) + ' 元' : '' }}
</el-descriptions-item>
<el-descriptions-item label="医保支付:">
{{ patientInfo.insurancePrice }}
</el-descriptions-item>
<el-descriptions-item label="自费金额:">
{{ patientInfo.selfAmount ? patientInfo.selfAmount.toFixed(2) + ' 元' : '-' }}
</el-descriptions-item>
<el-descriptions-item label="支付方式:">
{{ patientInfo.typeCode_dictText }}
</el-descriptions-item>
<el-descriptions-item label="发票号:">
{{ patientInfo.idCard }}
</el-descriptions-item> -->
<!-- <el-descriptions-item label="手机号">{{ patientInfo.name }}</el-descriptions-item>
<el-descriptions-item label="出生日期">{{ patientInfo.name }}</el-descriptions-item> -->
</el-descriptions>
</el-card>
<el-card style="height: 83%">
<template #header>
<span style="vertical-align: middle">退费单据</span>
</template>
<!-- <el-button type="primary" @click="handleRefund()" :disabled="buttonDisabled">
确认退费
</el-button> -->
<el-table
ref="chargeListRef"
height="510"
:data="chargeList"
row-key="encounterId"
v-loading="chargeLoading"
:span-method="spanMethod"
class="no-hover-table"
border
width=""
>
<!-- <el-table-column type="selection" :selectable="checkSelectable" width="55" /> -->
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button link type="primary" @click="handleRefund(scope.row)">退费</el-button>
</template>
</el-table-column>
<el-table-column prop="paymentId" label="支付单据号" align="center" />
<el-table-column label="项目单据号" align="center" prop="busNo" width="150" />
<el-table-column label="项目名称" align="center" prop="itemName" />
<el-table-column
label="收费状态"
align="center"
prop="chargeStatus_enumText"
width="100"
/>
<!-- <el-table-column
label="发药/执行状态"
align="center"
prop="dispenseStatus_enumText"
width="130"
>
<template #default="scope">
{{ scope.row.dispenseStatus_enumText || scope.row.serviceStatus_enumText }}
</template>
</el-table-column> -->
<el-table-column label="数量" align="center" width="100">
<template #default="scope">
{{ scope.row.quantityValue + ' ' + scope.row.quantityUnit_dictText }}
</template>
</el-table-column>
<el-table-column
label="付款总额"
align="right"
prop="totalPrice"
header-align="center"
width="100"
>
<template #default="scope">
{{ scope.row.totalPrice.toFixed(2) + ' 元' }}
</template>
</el-table-column>
<!-- <el-table-column label="处方号" align="center" prop="prescriptionNo" /> -->
<el-table-column label="收款人" align="center" prop="entererName" width="120" />
</el-table>
</el-card>
</div>
<RefundDialog
:open="openDialog"
@close="handleClose"
:totalAmount="totalAmount"
:patientInfo="patientInfo"
:paymentId="paymentId"
:chargeItemIds="chargeItemIdList"
:details="details"
/>
</div>
</template>
<script setup name="ClinicCharge">
import {
getList,
getRefundList,
refund,
getReturnDetail,
init,
getChargeItemIds,
validReturnDrug,
} from './components/api';
import { formatDate, formatDateStr } from '@/utils/index';
import RefundDialog from './components/refundDialog.vue';
import Decimal from 'decimal.js';
const { proxy } = getCurrentInstance();
const queryParams = ref({
pageNum: 1,
pageSize: 50,
statusEnum: 7,
});
const spanMap = ref({});
const patientList = ref([]);
const patientInfo = ref({});
const chargeList = ref([]);
const totalAmount = ref(0);
const chargeOption = ref([]);
const chargeLoading = ref(false);
const openDialog = ref(false);
const chargeItemIdList = ref([]);
const details = ref({});
const encounterId = ref('');
const paymentId = ref('');
const maxBillDate = ref([
formatDateStr(new Date(), 'YYYY-MM-DD'),
formatDateStr(new Date(), 'YYYY-MM-DD'),
]);
getPatientList();
initOptions();
/**
* 患者列表
*/
function getPatientList() {
if (maxBillDate.value.length > 0) {
queryParams.value.maxBillDateSTime = maxBillDate.value[0] + ' 00:00:00';
queryParams.value.maxBillDateETime = maxBillDate.value[1] + ' 23:59:59';
} else {
queryParams.value.maxBillDateSTime = undefined;
queryParams.value.maxBillDateETime = undefined;
}
getList(queryParams.value).then((res) => {
patientList.value = res.data.records;
});
}
function initOptions() {
init().then((res) => {
chargeOption.value = res.data.chargeItemStatusOptions;
});
}
// 生成合并行
const generateSpanMap = () => {
spanMap.value = {};
let currentId = null;
let startIndex = 0;
chargeList.value.forEach((row, index) => {
if (row.paymentId !== currentId) {
if (currentId !== null) {
spanMap.value[currentId] = {
start: startIndex,
count: index - startIndex,
};
}
currentId = row.paymentId;
startIndex = index;
}
});
// 处理最后一个分组
if (currentId !== null) {
spanMap.value[currentId] = {
start: startIndex,
count: chargeList.value.length - startIndex,
};
}
};
// 合并方法同时处理多选列和paymentId列
const spanMethod = ({ row, column, rowIndex, columnIndex }) => {
if (columnIndex <= 1) {
// 合并前两列
const group = spanMap.value[row.paymentId];
if (!group) return;
if (rowIndex === group.start) {
return { rowspan: group.count, colspan: 1 };
} else {
return { rowspan: 0, colspan: 0 };
}
}
};
/**
* 点击患者列表行 获取处方列表
*/
function clickRow(row) {
patientInfo.value = row;
chargeLoading.value = true;
encounterId.value = row.encounterId;
getRefundList({
encounterId: row.encounterId,
billDateSTime: maxBillDate.value[0] + ' 00:00:00',
billDateETime: maxBillDate.value[1] + ' 23:59:59',
}).then((res) => {
chargeList.value = res.data;
spanMap.value = {};
chargeList.value.sort((a, b) => a.paymentId.localeCompare(b.paymentId));
console.log(chargeList.value);
generateSpanMap();
setTimeout(() => {
chargeLoading.value = false;
}, 100);
});
}
function handleRefund(row) {
// totalAmount.value = chargeList.value
// .filter((item) => {
// return item.paymentId === row.paymentId;
// })
// .reduce((accumulator, currentRow) => {
// return new Decimal(accumulator).add(new Decimal(currentRow.totalPrice || 0));
// }, 0);
getReturnDetail({ id: row.paymentId }).then((res) => {
if (res.data.length > 0) {
totalAmount.value = res.data.find((item) => item.payEnum === 220000).amount;
}
details.value = res.data;
});
paymentId.value = row.paymentId;
patientInfo.value.patientId = row.patientId;
getChargeItemIds(row.encounterId).then((res) => {
chargeItemIdList.value = res.data;
validReturnDrug(row.chargeItemIds.split(',')).then((res) => {
if (res.code == 200) {
openDialog.value = true;
} else {
proxy.$modal.msgWarning(res.msg);
}
});
});
// refund(
// chargeList.value.map((item) => {
// item.id;
// })
// ).then((res) => {
// if (res.code == 200) {
// proxy.$modal.msgSuccess('操作成功');
// }
// getPatientList();
// });
}
function handleClose(value) {
openDialog.value = false;
if (value == 'success') {
proxy.$modal.msgSuccess('操作成功');
clickRow(patientInfo.value);
}
}
</script>
<style lang="scss" scoped>
:deep(.no-hover-table) .el-table__body tr:hover > td {
background: inherit !important;
}
</style>