Files
his/openhis-ui-vue3/src/views/charge/outpatientregistration/components/chargeDialog.vue
2025-12-27 15:30:25 +08:00

669 lines
23 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>
<el-dialog
title="确认收费"
v-model="props.open"
@close="close"
width="700px"
append-to-body
destroy-on-close
>
<div v-loading="dialogLoading">
<el-text size="large" style="display: block; margin-bottom: 15px">
收费日期{{ currentDate }}
</el-text>
<el-text size="large">费用性质{{ getFeeTypeText }}</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)"
/>
</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="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 class="payment-item">
<span>{{ payTypeText }}支付</span>
<el-input
ref="txtCodeRef"
v-model="txtCode"
style="width: 300px"
:placeholder="payTypePlaceholder"
/>
<el-button link type="primary" @click="handleWxPay()">扫码支付</el-button>
<el-button link type="primary" @click="getWxPayResult()">查看结果</el-button>
</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="throttledGetList" :disabled="dialogLoading"
> </el-button
>
<el-button @click="close" :disabled="dialogLoading"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { savePayment, wxPay, WxPayResult } from './outpatientregistration';
import { computed, watch, reactive, ref, getCurrentInstance, nextTick } from 'vue';
import { Delete } from '@element-plus/icons-vue';
import { debounce } from 'lodash-es';
import printUtils, { PRINT_TEMPLATE } from '@/utils/printUtils';
// 获取费用性质文本
const getFeeTypeText = computed(() => {
if (!props.medfee_paymtd_code || !Array.isArray(props.medfee_paymtd_code)) {
return '';
}
// 如果有feeType根据feeType查找对应的文本
if (props.feeType) {
const dict = props.medfee_paymtd_code.find(item => item.value === props.feeType);
return dict ? dict.label : '';
}
// 如果只有一个选项,直接返回第一个选项的文本
if (props.medfee_paymtd_code.length === 1) {
return props.medfee_paymtd_code[0].label || '';
}
return '';
});
const props = defineProps({
open: {
type: Boolean,
default: false,
},
totalAmount: {
type: Number,
default: 0.0,
},
patientInfo: {
type: Object,
default: undefined,
},
chargeItemIds: {
type: [],
default: [],
},
chrgBchnoList: {
type: [],
default: [],
},
chrgBchno: {
type: String,
default: '',
},
registerBusNo: {
type: String,
default: '',
},
transformedData: {
type: Object,
default: undefined,
},
<<<<<<< HEAD
medfee_paymtd_code: {
type: Array,
default: () => [],
},
feeType: {
type: String,
default: '',
}
=======
orgId: {
type: String,
default: '',
},
orgName: {
type: String,
default: '',
},
>>>>>>> v1.3
});
const { proxy } = getCurrentInstance();
const dialogLoading = ref(false);
const txtCode = ref('');
const throttledGetList = debounce(submit, 300);
const formData = reactive({
totalAmount: 0,
selfPay: [{ payEnum: 220100, amount: 0.0, payLevelEnum: 2 }],
medicalInsurance: {
account: '',
poolPay: 0,
personalPay: 0,
},
});
watch(
() => props.totalAmount,
(newValue) => {
nextTick(() => {
formData.totalAmount = newValue;
formData.selfPay[0].amount = newValue;
});
}
);
const emit = defineEmits(['close']);
// 根据printUtils实现的打印方法
async function printReceipt(param) {
console.log('打印收费小票数据:', param);
console.log('患者信息:', props.patientInfo);
try {
// 构造打印数据
const printData = {
data: [
{
...param,
// 基础支付类型
YB_FUND_PAY: param.detail.find((t) => t.payEnum === 100000)?.amount ?? 0, // 基金支付总额
SELF_PAY: param.detail.find((t) => t.payEnum === 200000)?.amount ?? 0, // 个人负担总金额
OTHER_PAY: param.detail.find((t) => t.payEnum === 300000)?.amount ?? 0, // 其他(如医院负担金额)
// 基本医保统筹基金支出
YB_TC_FUND_AMOUNT: param.detail.find((t) => t.payEnum === 110000)?.amount ?? 0, // 基本医保统筹基金支出
YB_BC_FUND_AMOUNT: param.detail.find((t) => t.payEnum === 120000)?.amount ?? 0, // 补充医疗保险基金支出
YB_JZ_FUND_AMOUNT: param.detail.find((t) => t.payEnum === 130000)?.amount ?? 0, // 医疗救助基金支出
YB_OTHER_AMOUNT: param.detail.find((t) => t.payEnum === 140000)?.amount ?? 0, // 其他支出
// 职工基本医疗保险
YB_TC_ZG_FUND_VALUE: param.detail.find((t) => t.payEnum === 110100)?.amount ?? 0, // 职工基本医疗保险
YB_TC_JM_FUND_VALUE: param.detail.find((t) => t.payEnum === 110200)?.amount ?? 0, // 居民基本医疗保险
// 补充医疗保险基金支出细分
YB_BC_JM_DB_VALUE: param.detail.find((t) => t.payEnum === 120100)?.amount ?? 0, // 全体参保人的居民大病保险
YB_BC_DE_BZ_VALUE: param.detail.find((t) => t.payEnum === 120200)?.amount ?? 0, // 大额医疗费用补助
YB_BC_ZG_DE_BZ_VALUE: param.detail.find((t) => t.payEnum === 120300)?.amount ?? 0, // 企业职工大额医疗费用补助
YB_BC_GWY_BZ_VALUE: param.detail.find((t) => t.payEnum === 120400)?.amount ?? 0, // 公务员医疗补助
// 其他支出细分
OTHER_PAY_DD_FUND_VALUE: param.detail.find((t) => t.payEnum === 300001)?.amount ?? 0, // 兜底基金支出
OTHER_PAY_YW_SH_FUND_VALUE: param.detail.find((t) => t.payEnum === 300002)?.amount ?? 0, // 意外伤害基金支出
OTHER_PAY_LX_YL_FUND_VALUE: param.detail.find((t) => t.payEnum === 300003)?.amount ?? 0, // 离休人员医疗保障金支出
OTHER_PAY_LX_YH_FUND_VALUE: param.detail.find((t) => t.payEnum === 300004)?.amount ?? 0, // 离休人员优惠金支出
OTHER_PAY_CZ_FUND_VALUE: param.detail.find((t) => t.payEnum === 300005)?.amount ?? 0, // 财政基金支出
OTHER_PAY_CZ_YZ_FUND_VALUE: param.detail.find((t) => t.payEnum === 300006)?.amount ?? 0, // 财政预支支出
OTHER_PAY_ZG_DB_FUND_VALUE: param.detail.find((t) => t.payEnum === 300007)?.amount ?? 0, // 职工大病基金支出
OTHER_PAY_EY_FUND_VALUE: param.detail.find((t) => t.payEnum === 300008)?.amount ?? 0, // 二乙基金支出
OTHER_PAY_QX_JZ_FUND_VALUE: param.detail.find((t) => t.payEnum === 300009)?.amount ?? 0, // 倾斜救助支出
OTHER_PAY_YL_JZ_FUND_VALUE: param.detail.find((t) => t.payEnum === 300010)?.amount ?? 0, // 医疗救助再救助基金
HOSP_PART_AMT: param.detail.find((t) => t.payEnum === 300011)?.amount ?? 0, // 医院负担金额
// 医保结算返回值 - 修复运算符优先级问题,添加括号确保正确拼接'元'
FULAMT_OWNPAY_AMT: (param.detail?.find((t) => t.payEnum === 1)?.amount ?? 0) + '元', // 全自费金额
OVERLMT_SELFPAY: (param.detail?.find((t) => t.payEnum === 3)?.amount ?? 0) + '元', // 超限价自费费用
PRESELFPAY_AMT: (param.detail?.find((t) => t.payEnum === 4)?.amount ?? 0) + '元', // 先行自付金额
INSCP_SCP_AMT: (param.detail?.find((t) => t.payEnum === 5)?.amount ?? 0) + '元', // 符合政策范围金额
ACT_PAY_DEDC: (param.detail?.find((t) => t.payEnum === 6)?.amount ?? 0) + '元', // 实际支付起付线
POOL_PROP_SELFPAY: (param.detail?.find((t) => t.payEnum === 7)?.amount ?? 0) + '元', // 基本医疗保险统筹基金支付比例
BALC: (param.detail?.find((t) => t.payEnum === 8)?.amount ?? 0) + '元', // 余额
// 特殊支付方式
SELF_YB_ZH_PAY:
(param.detail?.find((t) => t.payEnum === 210000)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 210000)?.amount ?? 0) + '元'
: '', // 个人医保账户支付
SELF_YB_ZH_GJ_VALUE:
(param.detail?.find((t) => t.payEnum === 210100)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 210100)?.amount ?? 0) + '元'
: '', // 账户共济支付金额
SELF_CASH_PAY:
(param.detail?.find((t) => t.payEnum === 220000)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 220000)?.amount ?? 0) + '元'
: '', // 个人现金支付金额
SELF_VX_PAY:
(param.detail?.find((t) => t.payEnum === 230000)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 230000)?.amount ?? 0) + '元'
: '', // 微信支付金额
SELF_ALI_PAY:
(param.detail?.find((t) => t.payEnum === 240000)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 240000)?.amount ?? 0) + '元'
: '', // 阿里支付金额
// 现金支付细分
SELF_CASH_VALUE:
(param.detail?.find((t) => t.payEnum === 220400)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 220400)?.amount ?? 0) + '元'
: '', // 个人现金支付金额(现金)
SELF_CASH_VX_VALUE:
(param.detail?.find((t) => t.payEnum === 220100)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 220100)?.amount ?? 0) + '元'
: '', // 个人现金支付金额(微信)
SELF_CASH_ALI_VALUE:
(param.detail?.find((t) => t.payEnum === 220200)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 220200)?.amount ?? 0) + '元'
: '', // 个人现金支付金额(支付宝)
SELF_CASH_UNION_VALUE:
(param.detail?.find((t) => t.payEnum === 220300)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 220300)?.amount ?? 0) + '元'
: '', // 个人现金支付金额(银联)
// 基金类型(扩展)
BIRTH_FUND:
(param.detail?.find((t) => t.payEnum === 510100)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 510100)?.amount ?? 0) + '元'
: '', // 生育基金
RETIREE_MEDICAL:
(param.detail?.find((t) => t.payEnum === 340100)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 340100)?.amount ?? 0) + '元'
: '', // 离休人员医疗保障基金
URBAN_BASIC_MEDICAL:
(param.detail?.find((t) => t.payEnum === 390100)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 390100)?.amount ?? 0) + '元'
: '', // 城乡居民基本医疗保险基金
URBAN_SERIOUS_ILLNESS:
(param.detail?.find((t) => t.payEnum === 390200)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 390200)?.amount ?? 0) + '元'
: '', // 城乡居民大病医疗保险基金
MEDICAL_ASSISTANCE:
(param.detail?.find((t) => t.payEnum === 610100)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 610100)?.amount ?? 0) + '元'
: '', // 医疗救助基金
GOVERNMENT_SUBSIDY:
(param.detail?.find((t) => t.payEnum === 640100)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 640100)?.amount ?? 0) + '元'
: '', // 政府兜底基金
ACCIDENT_INSURANCE:
(param.detail?.find((t) => t.payEnum === 390400)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 390400)?.amount ?? 0) + '元'
: '', // 意外伤害基金
CARE_INSURANCE:
(param.detail?.find((t) => t.payEnum === 620100)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 620100)?.amount ?? 0) + '元'
: '', // 照护保险基金
FINANCIAL_FUND:
(param.detail?.find((t) => t.payEnum === 360100)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 360100)?.amount ?? 0) + '元'
: '', // 财政基金
HOSPITAL_ADVANCE:
(param.detail?.find((t) => t.payEnum === 999900)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 999900)?.amount ?? 0) + '元'
: '', // 医院垫付
SUPPLEMENTARY_INSURANCE:
(param.detail?.find((t) => t.payEnum === 390300)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 390300)?.amount ?? 0) + '元'
: '', // 城乡居民大病补充保险基金
HEALTHCARE_PREPAYMENT:
(param.detail?.find((t) => t.payEnum === 360300)?.amount ?? 0) > 0
? (param.detail?.find((t) => t.payEnum === 360300)?.amount ?? 0) + '元'
: '', // 保健预支基金
//微信刷卡支付
SELF_CASH_VX_VALUE: (() => {
// const cashValue = param.detail.find((t) => t.payEnum === 220400)?.amount ?? 0;
const vxValue = param.detail.find((t) => t.payEnum === 220100)?.amount ?? 0;
const unionValue = param.detail.find((t) => t.payEnum === 220300)?.amount ?? 0;
const aliValue = param.detail.find((t) => t.payEnum === 220200)?.amount ?? 0;
return vxValue + unionValue + aliValue + '元';
})(),
// 患者信息
patientName: param.patientName || '',
sex: param.sex === 1 ? '女' : param.sex === 0 ? '男' : param.sex || '',
age: param.age ? param.age + '岁' : '',
personType: param.contractName, //病人类型
// 挂号和就诊信息
encounterId: props.patientInfo?.encounterId || '',
busNo: props.patientInfo?.busNo || '',
Mr_QR_Code: param.regNo || props.registerBusNo,
// 科室和医生信息
organizationName: props.orgName || props.patientInfo?.organizationName || '',
practitionerName: props.patientInfo?.practitionerName || '',
healthcareName: props.patientInfo?.healthcareName || '',
// 费用信息
fixmedinsName: param.fixmedinsName
? param.fixmedinsName + '门诊收费明细'
: '门诊收费明细',
// 收费员
cashier: param.paymentEmployee,
// 收费时间
chargeTime: new Date().toLocaleString(),
//电子收据二维码
pictureUrl: param.pictureUrl || 'https://chinaebill.com/img/xiaochengxu.png',
},
],
};
// 选择门诊挂号打印模板
console.log('printDataprintDataprintDataprintDataprintData', printData.data[0]);
await printUtils.print(PRINT_TEMPLATE.OUTPATIENT_REGISTRATION, printData.data[0]);
console.log('打印成功');
} catch (error) {
console.error('打印失败:', error);
proxy.$modal.msgError('打印失败: ' + error.message);
}
}
function handleWxPay() {
console.log('开始微信支付,当前支付详情:', formData.selfPay);
console.log(
'支付金额详情:',
formData.selfPay.map((item) => ({ payEnum: item.payEnum, amount: item.amount }))
);
wxPay({
txtCode: txtCode.value,
//chargeItemIds: props.chargeItemIds,
//encounterId: props.patientInfo.encounterId,
id: props.paymentId,
paymentDetails: formData.selfPay,
// ybMdtrtCertType: props.userCardInfo.psnCertType,
// busiCardInfo: props.userCardInfo.busiCardInfo,
})
.then((response) => {
console.log('微信支付请求成功:', response);
})
.catch((error) => {
console.error('微信支付请求失败:', error);
});
}
function getWxPayResult() {
WxPayResult({
txtCode: txtCode.value,
chargeItemIds: props.chargeItemIds,
encounterId: props.patientInfo.encounterId,
id: props.paymentId,
paymentDetails: formData.selfPay,
ybMdtrtCertType: props.userCardInfo.psnCertType,
busiCardInfo: props.userCardInfo.busiCardInfo,
});
}
function submit() {
if (parseFloat(displayAmount.value) < formData.totalAmount) {
proxy.$modal.msgError('请输入正确的结算金额');
return;
}
dialogLoading.value = true;
// 根据费用性质动态设置合同编号
if (props.transformedData && props.transformedData.accountFormData) {
// 直接使用传入的feeType作为contractNo
// 如果feeType存在就使用否则使用patientInfo中的medfeePaymtdCode最后默认使用'0000'
props.transformedData.accountFormData.contractNo = props.feeType || props.patientInfo?.medfeePaymtdCode || '0000';
}
savePayment({
outpatientRegistrationAddParam: props.transformedData,
chrgBchno: props.chrgBchno,
busNo: props.registerBusNo,
paymentDetails: formData.selfPay,
})
.then((res) => {
if (res.code == 200) {
printReceipt(res.data);
formData.selfPay = [{ payEnum: 220100, amount: 0.0, payLevelEnum: 2 }];
emit('close', 'success');
}
})
.finally(() => {
dialogLoading.value = false;
});
}
const currentDate = ref(new Date().toLocaleString());
const selfPayMethods = [
{ label: '现金', value: 220400, isWebPay: false },
{ label: '微信', value: 220100, isWebPay: true },
{ label: '支付宝', value: 220200, isWebPay: true },
{ label: '银联', value: 220300, isWebPay: true },
];
// 计算剩余可输入金额
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 = (option) => {
if (formData.selfPay.length > 1) {
// 禁用已被选择的相同方式,避免重复选择
const selectedEnums = formData.selfPay.map((item) => item.payEnum);
debugger;
if (selectedEnums.includes(option.value)) {
return true;
}
// 若已经选择了任一线上支付方式,则其他线上方式不可再选,仅允许现金等线下方式
const hasSelectedWebPay = selectedEnums.some((val) => {
const found = selfPayMethods.find((m) => m.value === val);
return found ? found.isWebPay === true : false;
});
if (hasSelectedWebPay && option.isWebPay) {
return true;
}
return false;
} else {
return false;
}
};
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 payTypeText = ref('微信');
const payTypePlaceholder = computed(() => {
if (payTypeText.value === '现金') {
return '请输入现金金额';
}
return payTypeText.value + '支付二维码';
});
const clearAmount = (index) => {
// formData.selfPay[index].amount = 0;
const selectedOption = selfPayMethods.find(
(item) => item.value === formData.selfPay[index].payEnum
);
if (selectedOption) {
payTypeText.value = selectedOption.label;
}
};
// 计算属性
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() {
//清空表单数据
txtCode.value = '';
emit('close', 'cancel');
}
</script>
<style 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;
}
.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; /* 避免点击干扰 */
}
</style>