Revert "Fix Bug #550: AI修复"

This reverts commit 16c42ca108.
This commit is contained in:
2026-05-27 08:59:07 +08:00
parent bd14563691
commit 9db5ced4e3
5432 changed files with 778638 additions and 171 deletions

View File

@@ -0,0 +1,46 @@
import request from '@/utils/request'
// 获取支付记录列表
export function getList(query) {
return request({
url: '/payment/payment/page',
method: 'get',
params: query
})
}
export function invoiceMzInvoice(data) {
return request({
url: '/invoice/mzInvoice',
method: 'post',
data: data
})
}
export function invoiceReissue(data) {
return request({
url: '/invoice/invoiceReissue',
method: 'post',
data: data
})
}
export function invoiceWriteoff(data) {
return request({
url: '/invoice/invoiceWriteoff',
method: 'post',
data: data
})
}
export function invoiceOpen(invoiceId) {
return request({
url: '/invoice/invoiceOpen?invoiceId=' + invoiceId,
method: 'put',
})
}
// 收费详情
export function paymentDetail(data) {
return request({
url: '/payment/payment/detail',
method: 'post',
data: data
})
}

View File

@@ -0,0 +1,346 @@
<template>
<div class="app-continer">
<div style="margin: 15px 0; padding: 0 20px">
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="90px">
<el-form-item label="患者姓名:" prop="searchKey">
<el-input
v-model="queryParams.searchKey"
placeholder="患者姓名"
clearable
style="width: 240px"
@keyup.enter="getClinicRecord"
/>
</el-form-item>
<el-form-item label="发票号:" prop="invoiceNo" label-width="120px">
<el-input
v-model="queryParams.invoiceNo"
placeholder="发票号"
clearable
style="width: 240px"
@keyup.enter="getClinicRecord"
/>
</el-form-item>
<el-form-item label="发票状态:" prop="invoiceStatus">
<el-select
v-model="queryParams.invoiceStatus"select
placeholder="发票状态"
clearable
style="width: 240px"
@keyup.enter="getClinicRecord"
>
<el-option v-for="item in invoiceStatusList" :key="item.value" :label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="结算时间:" prop="activeFlag">
<el-date-picker
v-model="occurrenceTime"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
style="width: 300px; margin-bottom: 10px; margin-left: 20px"
value-format="YYYY-MM-DD"
:clearable="false"
@change="getClinicRecord"
/>
</el-form-item>
<div style="float: right">
<el-button type="primary" plain @click="getClinicRecord">查询</el-button>
<el-button type="warning" plain @click="handleReset">重置</el-button>
</div>
</el-form>
</div>
<div style="float: right; margin: 0 20px 10px 0">
<span style="margin-right: 20px">
{{ '总数:' + count + '/' + '成功:' + successCount }}
</span>
<el-button type="primary" :loading="loading" plain @click="handleBatchProcess">批量开具</el-button>
</div>
<el-table :data="clinicRecord" border>
<!-- <el-table-column label="计算类型" align="center" prop="statusEnum_enumText" /> -->
<el-table-column label="患者姓名" align="center" prop="patientName" :show-overflow-tooltip="true"/>
<el-table-column label="支付状态" align="center" prop="statusEnum_dictText" :show-overflow-tooltip="true"/>
<el-table-column label="费用类型" align="center" prop="paymentEnum_dictText" :show-overflow-tooltip="true"/>
<el-table-column label="医保结算Id" align="center" prop="ybSettleIds" :show-overflow-tooltip="true"/>
<el-table-column label="收费流水号" align="center" prop="paymentNo" width="280" :show-overflow-tooltip="true"/>
<el-table-column label="发票号" align="center" prop="invoiceNo" :show-overflow-tooltip="true"/>
<el-table-column
label="结算金额"
align="right"
prop="tenderedAmount"
header-align="center"
width="100"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ scope.row.tenderedAmount + ' 元' }}</span>
</template>
</el-table-column>
<el-table-column
label="支付金额"
align="right"
prop="displayAmount"
header-align="center"
width="100"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ scope.row.displayAmount + ' 元' }}</span>
</template>
</el-table-column>
<el-table-column
label="结算时间"
align="center"
key="billDate"
prop="billDate"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ parseTime(scope.row.billDate) }}</span>
</template>
</el-table-column>
<el-table-column label="收款人" align="center" prop="entererName" :show-overflow-tooltip="true"/>
<!-- <el-table-column label="医生" align="center" prop="paymentEnum_enumText" /> -->
<el-table-column label="支付结果" align="center" prop="outcomeEnum_dictText" :show-overflow-tooltip="true"/>
<el-table-column label="打印次数" align="center" prop="printCount" :show-overflow-tooltip="true"/>
<el-table-column label="操作" align="center" prop="paymentEnum_enumText" width="340">
<template #default="scope">
<!-- <el-button type="primary" link @click="handlePrint(scope.row)">单据</el-button> -->
<!-- <el-button type="primary" link @click="handleEdit(scope.row)">冲红</el-button> -->
<el-button type="primary" link @click="handleOpen(scope.row,4)">收费详情</el-button>
<el-button type="primary" link @click="handleOpen(scope.row,1)" :disabled="scope.row.invoiceNo || scope.row.statusEnum == 3">开具电子发票</el-button>
<el-button :disabled="!scope.row.relationId" type="primary" link @click="handleOpenReasonDialog(scope.row)">冲销发票</el-button>
<el-button type="primary" link @click="handleOpen(scope.row,3)" :disabled="!scope.row.invoiceNo">调阅发票</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getLists"
/>
<el-dialog title="收费详情" v-model="paymentDetailShow" width="1000" append-to-body style="height:90vh">
<el-table :data="paymentDetailList" border style="height: 80vh">
<el-table-column label="支付类型" align="center" prop="payEnum_dictText" :show-overflow-tooltip="true"/>
<el-table-column label="金额" align="center" prop="amount" :show-overflow-tooltip="true"/>
<el-table-column label="找零" align="center" prop="returnAmount" :show-overflow-tooltip="true"/>
<el-table-column label="交款" align="center" prop="chargeAmount" :show-overflow-tooltip="true"/>
</el-table>
</el-dialog>
<el-dialog title="请输入原因" v-model="reasonDialogVisible" width="30%">
<el-form :model="reasonForm" label-width="80px">
<el-form-item label="冲销原因">
<el-input v-model="reasonForm.reason" placeholder="请输入原因" type="textarea" :rows="3" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="reasonDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="submitReason">确 定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup name="ClinicRecord">
const { proxy } = getCurrentInstance();
import {getList, invoiceOpen, invoiceReissue, invoiceWriteoff, paymentDetail} from './components/api.js';
import {formatDate} from '@/utils/index';
const occurrenceTime = ref([formatDate(new Date()),formatDate(new Date())]);
const total = ref(0);
const queryParams = ref({
pageNo: 1,
pageSize: 10,
billDateSTime:"",
billDateETime:"",
searchKey:"",
invoiceStatus:1,
kinsEnum: 1
});
const invoiceStatusList = ref([
{ value: 1, label: '已开具' },
{ value: 0, label: '未开具' },
]);
const paymentDetailShow = ref(false)
const clinicRecord = ref([]);
const successCount = ref(0);
const count = ref(0);
const reasonDialogVisible = ref(false);
const reasonForm = ref({
reason: ''
});
const currentRow = ref(null);
const paymentDetailList = ref([])
getLists();
function getClinicRecord() {
queryParams.value.billDateSTime =
occurrenceTime.value && occurrenceTime.value.length == 2
? occurrenceTime.value[0] + " 00:00:00"
: "";
queryParams.value.billDateETime =
occurrenceTime.value && occurrenceTime.value.length == 2
? occurrenceTime.value[1] + " 23:59:59"
: "";
queryParams.value.pageNo = 1;
getLists()
}
function getLists(){
getList(queryParams.value).then((res) => {
total.value = res.data.total;
clinicRecord.value = res.data.records;
});
}
function handleOpenReasonDialog(row) {
// 可选:保存当前行数据以便后续使用
currentRow.value = row;
reasonForm.value.reason = ''; // 清空上次输入
reasonDialogVisible.value = true;
}
// 提交原因
function submitReason() {
if (!reasonForm.value.reason.trim()) {
proxy.$message.warning('请输入原因');
return;
}
invoiceWriteoff({paymentId:currentRow.value.relationIdStr,reason:reasonForm.value.reason}).then((res) => {
if (res.data) {
if(res.data.includes(" 电子票据红冲失败")){
proxy.$message.error(res.data);
}else{
proxy.$message.success('红冲成功');
// 关闭弹窗
reasonDialogVisible.value = false;
// 可选:重置表单
reasonForm.value.reason = '';
window.open(res.data)
}
}
getLists();
}).catch((err)=>{
})
}
/** 清空条件按钮操作 */
function handleReset() {
// 清空查询条件
occurrenceTime.value = ""
queryParams.value.billDateSTime = ""
queryParams.value.billDateETime = ""
proxy.resetForm("queryRef");
getLists();
}
// function handlePrint(row){
// }
// function handleEdit(row){
// }
const loading = ref(false)
async function handleBatchProcess() {
// 遍历list并异步执行invoiceReissue方法
let list = clinicRecord.value.filter(item =>{
return item.statusEnum == 1 && (item.invoiceNo == undefined || item.invoiceNo == null)
})
loading.value = true
count.value = list.length;
for (const item of list) {
try {
const res = await invoiceReissue({
paymentId: item.id,
encounterId: item.encounterId ? item.encounterId : ""
});
if (res.data) {
// 门诊电子发票开具失败 住院电子发票开具失败 电子发票类型不明确
if (
res.data.includes(" 挂号电子发票开具失败") ||
res.data.includes(" 住院电子发票开具失败") ||
res.data.includes(" 门诊电子发票开具失败") ||
res.data.includes(" 电子发票类型不明确")
) {
// proxy.$message.error(res.data);
} else {
successCount.value++;
// window.open(res.data);
}
}
if (res.code == 200) {
// getLists();
} else {
// proxy.$message.error(res.data);
}
} catch (err) {
console.error(err);
}
}
loading.value = false
getLists()
}
function handleOpen(row,type){
if(type==1){
invoiceReissue({paymentId:row.id,encounterId:row.encounterId?row.encounterId:""}).then((res) => {
if (res.data) {
// 门诊电子发票开具失败 住院电子发票开具失败 电子发票类型不明确
if(res.data.includes(" 挂号电子发票开具失败")||res.data.includes(" 住院电子发票开具失败")||res.data.includes(" 门诊电子发票开具失败")||res.data.includes(" 电子发票类型不明确")){
proxy.$message.error(res.data);
}else{
window.open(res.data)
}
}
if(res.code == 200) {
getLists();
}
proxy.$message.error(res.data);
}).catch((err)=>{
})
}else if(type==2) {
invoiceWriteoff({paymentId:row.id,encounterId:row.encounterId?row.encounterId:""}).then((res) => {
if (res.data) {
if(res.data.includes(" 电子票据红冲失败")){
proxy.$message.error(res.data);
}else{
window.open(res.data)
}
}
}).catch((err)=>{
})
}else if(type==3){
// 调阅电子发票
invoiceOpen(row.invoiceId?row.invoiceId:"").then((res) => {
if (res.data) {
window.open(res.data)
}
}).catch((err)=>{
})
}else{
paymentDetail({id:row.id?row.id:""}).then((res) => {
if (res.data) {
paymentDetailShow.value = true
paymentDetailList.value = res.data
}
}).catch((err)=>{
})
}
}
</script>
<style scoped>
.el-dialog{
height: 90vh!important;
}
.dialog-footer {
text-align: right;
}
.el-textarea__inner {
resize: none;
}
</style>

View File

@@ -0,0 +1,156 @@
import request from '@/utils/request'
/**
* 收费患者列表
*/
export function getList(queryParams) {
return request({
url: '/charge-manage/charge/encounter-patient-page',
method: 'get',
params: queryParams
})
}
/**
* 患者处方列表
*/
export function getChargeList(encounterId, config = {}) {
return request({
url: '/charge-manage/charge/patient-prescription?encounterId=' + encounterId,
method: 'get',
...config
})
}
/**
* 医保转自费
*/
export function changeToSelfPay(encounterId) {
return request({
url: '/charge-manage/charge/self-pay?encounterId=' + encounterId,
method: 'put',
})
}
/**
* 自费转医保
*/
export function changeToMedicalInsurance(encounterId) {
return request({
url: '/charge-manage/charge/medical-insurance?encounterId=' + encounterId,
method: 'put',
})
}
/**
* 学生医保转学生自费
*/
export function changeStudentPayTosStudentSelf(encounterId) {
return request({
url: '/charge-manage/charge/student-self-pay?encounterId=' + encounterId,
method: 'put',
})
}
/**
* 学生自费转学生医保
*/
export function changeStudentSelfToStudentPay(encounterId) {
return request({
url: '/charge-manage/charge/student-yb-pay?encounterId=' + encounterId,
method: 'put',
})
}
/**
* 收费
*/
export function savePayment(data) {
return request({
url: '/payment/payment/charge',
method: 'post',
data: data
})
}
/**
* 初始化
*/
export function init() {
return request({
url: '/charge-manage/charge/init',
method: 'get',
})
}
/**
* 收费预结算
*/
export function precharge(data) {
return request({
url: '/payment/payment/precharge',
method: 'post',
data: data
})
}
/**
* 取消预结算
*/
export function unprecharge(data) {
return request({
url: '/payment/payment/unprecharge',
method: 'post',
params: data
})
}
/**
* 发耗材
*/
export function dispenseMedicalConsumables(data, config = {}) {
return request({
url: '/pharmacy-manage/device-dispense/consumables-dispense',
method: 'put',
data: data,
...config
})
}
/**
* 补打小票
*/
export function getChargeInfo(param, config = {}) {
return request({
url: '/payment/bill/getDetail',
method: 'get',
params: param,
...config
})
}
/**
* 微信支付
*/
export function wxPay (data) {
return request ({
url: '/three-part/pay/pay-for',
method: 'post',
data: data,
});
}
/**
* 微信支付
*/
export function WxPayResult (data) {
return request ({
url: '/three-part/pay/pay-query/' + data.paymentId,
method: 'get',
});
}

View File

@@ -0,0 +1,967 @@
<template>
<el-dialog
title="确认收费"
v-model="props.open"
width="700px"
append-to-body
destroy-on-close
@open="handleOpen"
@close="close"
>
<div v-loading="dialogLoading">
<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="amount-row">
<el-text size="large">折扣金额</el-text>
<el-text size="large" type="warning" class="amount">
{{ discountAmount.toFixed(2) + ' 元' }}
</el-text>
</div>
<!-- 自费支付 -->
<div class="payment-container">
<template v-for="(item, index) in formData.selfPay" :key="index">
<div v-show="item.payEnum != 220500" class="payment-item">
<span>支付方式</span>
<img
v-if="item.payEnum == 220100 || item.payEnum == 220200"
:src="imgs[item.payEnum == 220100 ? 0 : 1]"
style="width: 20px; height: 20px"
/>
<el-select
v-model="item.payEnum"
placeholder="选择支付方式"
style="width: 160px"
@change="(value) => clearAmount(index, value)"
>
<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>
</template>
<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 style="margin-top: 10px" v-if="userStore.hospitalName == '同一医院'">
<span>折扣</span>
<el-radio-group v-model="discountRadio" @change="handleDiscountChange">
<el-radio-button
v-for="item in charge_discount"
:key="item.value"
link
:label="item.label"
:value="item.value"
/>
</el-radio-group>
</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>
<el-table :data="props.details" max-height="200" border>
<el-table-column prop="payEnumText" 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="throttledGetList" :disabled="dialogLoading">
</el-button>
<!-- <el-button type="primary" @click="print()" :disabled="dialogLoading"> </el-button> -->
<el-button @click="close" :disabled="dialogLoading"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import {dispenseMedicalConsumables, getChargeInfo, savePayment, wxPay, WxPayResult,} from './api';
import {computed, getCurrentInstance, reactive, ref, watch} from 'vue';
import {Delete} from '@element-plus/icons-vue';
import {debounce} from 'lodash-es';
import useUserStore from '@/store/modules/user';
import {hiprint} from 'vue-plugin-hiprint';
import templateJson from './template.json';
import printUtils, {PRINT_TEMPLATE} from '@/utils/printUtils';
import image1 from '../../../../assets/images/weixinzhifu.png';
import image2 from '../../../../assets/images/zhifubaozhifu.png';
const imgs = ref([image1, image2]);
const props = defineProps({
open: {
type: Boolean,
default: false,
},
totalAmount: {
type: Number,
default: 0.0,
},
category: {
type: String,
},
paymentId: {
type: String,
},
userCardInfo: {
type: Object,
},
patientInfo: {
type: Object,
default: undefined,
},
chargeItemIds: {
type: Array,
default: () => [],
},
consumablesIdList: {
type: Array,
default: () => [],
},
chrgBchnoList: {
type: Array,
default: () => [],
},
details: {
type: Object,
default: undefined,
},
chargedItems: {
type: Array,
default: () => [],
},
});
const { proxy } = getCurrentInstance();
// 单位字典
const { unit_code } = proxy.useDict('unit_code');
const userStore = useUserStore();
const discountRadio = ref();
const discountAmount = ref(0);
const txtCode = ref('');
const formData = reactive({
totalAmount: 0,
selfPay: [{ payEnum: 220100, amount: 0.0, payLevelEnum: 2 }],
medicalInsurance: {
account: '',
poolPay: 0,
personalPay: 0,
},
});
const dialogLoading = ref(false);
watch(
() => props.totalAmount,
(newValue) => {
formData.totalAmount = newValue;
formData.selfPay[0].amount = newValue;
}
);
const emit = defineEmits(['close']);
let displayAmountTemp = 0;
// 获取单位字典数据的方法
const getUnitLabel = (unitCode) => {
if (!unitCode) return '';
// 从已加载的 unit_code 字典中查找对应项
const unit = unit_code.value?.find((item) => item.value === unitCode);
return unit ? unit.label : ''; // 如果找不到则返回空字符串
};
async function printReceipt(param) {
// 打印收费小票数据
console.log('========== 打印收费小票 - 开始 ==========');
console.log('[A] 传入参数检查:');
console.log(' - param对象:', param ? '存在' : '不存在');
console.log(' - param.chargedItems:', param?.chargedItems ? `存在(${param.chargedItems.length}条)` : '不存在/空');
console.log(' - param.detail:', param?.detail ? `存在(${param.detail.length}条)` : '不存在/空');
console.log(' - param.regNo:', param?.regNo);
console.log(' - param.fixmedinsName:', param?.fixmedinsName);
console.log(' - props.patientInfo:', props.patientInfo ? '存在' : '不存在');
if (param?.chargedItems) {
console.log('[B] 收费项目数据:');
param.chargedItems.forEach((item, idx) => {
console.log(` - 项目[${idx}]:`, item.chargeItemName || '未命名', '-', item.quantityWithUnit || item.quantityValue);
});
}
// 确保第一个支付方式的金额与总金额一致;
if (formData.selfPay.length > 0) {
formData.selfPay[0].amount = props.totalAmount;
}
formData.selfPay = [
{
payEnum: 220100, // 现金支付
amount: props.totalAmount,
payLevelEnum: 2,
},
];
try {
// 处理param.chargedItems添加quantityWithUnit字段
console.log('[C] 开始处理收费项目列表...');
const processedChargeItems = (param.chargedItems || []).map((item) => {
// 获取单位标签
const unitLabel = getUnitLabel(item.quantityUnit);
// 拼接数量和单位
const quantityWithUnit = unitLabel
? `${item.quantityValue} ${unitLabel}`
: item.quantityValue;
return {
...item,
quantityWithUnit, // 添加带单位的数量字段供打印使用
};
});
console.log('[C] 处理完成,项目数:', processedChargeItems.length);
// 构造打印数据,整合选中行信息
console.log('[D] 开始构造打印数据...');
const printData = {
data: [
{
...param,
// 收费项目列表
chargeItemsList: processedChargeItems,
// chargeItemsList: param.chargeItem,
// 基础支付类型
// 金额大于0时显示金额和单位等于0时不显示单位
YB_FUND_PAY: (() => {
const amount = param.detail?.find((t) => t.payEnum === 100000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 基金支付总额
SELF_PAY: (() => {
const amount = param.detail?.find((t) => t.payEnum === 200000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 个人负担总金额
OTHER_PAY: (() => {
const amount = param.detail?.find((t) => t.payEnum === 300000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 其他(如医院负担金额)
// 基本医保统筹基金支出
YB_TC_FUND_AMOUNT: (() => {
const amount = param.detail?.find((t) => t.payEnum === 110000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 基本医保统筹基金支出
YB_BC_FUND_AMOUNT: (() => {
const amount = param.detail?.find((t) => t.payEnum === 120000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 补充医疗保险基金支出
YB_JZ_FUND_AMOUNT: (() => {
const amount = param.detail?.find((t) => t.payEnum === 130000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 医疗救助基金支出
YB_OTHER_AMOUNT: (() => {
const amount = param.detail?.find((t) => t.payEnum === 140000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 其他支出
// 职工基本医疗保险
YB_TC_ZG_FUND_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 110100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 职工基本医疗保险
YB_TC_JM_FUND_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 110200)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 居民基本医疗保险
// 补充医疗保险基金支出细分
YB_BC_JM_DB_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 120100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 全体参保人的居民大病保险
YB_BC_DE_BZ_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 120200)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 大额医疗费用补助
YB_BC_ZG_DE_BZ_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 120300)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 企业职工大额医疗费用补助
YB_BC_GWY_BZ_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 120400)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 公务员医疗补助
// 其他支出细分
OTHER_PAY_DD_FUND_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 300001)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 兜底基金支出
OTHER_PAY_YW_SH_FUND_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 300002)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 意外伤害基金支出
OTHER_PAY_LX_YL_FUND_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 300003)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 离休人员医疗保障金支出
OTHER_PAY_LX_YH_FUND_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 300004)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 离休人员优惠金支出
OTHER_PAY_CZ_FUND_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 300005)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 财政基金支出
OTHER_PAY_CZ_YZ_FUND_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 300006)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 财政预支支出
OTHER_PAY_ZG_DB_FUND_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 300007)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 职工大病基金支出
OTHER_PAY_EY_FUND_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 300008)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 二乙基金支出
OTHER_PAY_QX_JZ_FUND_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 300009)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 倾斜救助支出
OTHER_PAY_YL_JZ_FUND_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 300010)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 医疗救助再救助基金
HOSP_PART_AMT: (() => {
const amount = param.detail?.find((t) => t.payEnum === 300011)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 医院负担金额
// 医保结算返回值
FULAMT_OWNPAY_AMT: (() => {
const amount = param.detail?.find((t) => t.payEnum === 1)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 全自费金额
OVERLMT_SELFPAY: (() => {
const amount = param.detail?.find((t) => t.payEnum === 3)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 超限价自费费用
PRESELFPAY_AMT: (() => {
const amount = param.detail?.find((t) => t.payEnum === 4)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 先行自付金额
INSCP_SCP_AMT: (() => {
const amount = param.detail?.find((t) => t.payEnum === 5)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 符合政策范围金额
ACT_PAY_DEDC: (() => {
const amount = param.detail?.find((t) => t.payEnum === 6)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 实际支付起付线
POOL_PROP_SELFPAY: (() => {
const amount = param.detail?.find((t) => t.payEnum === 7)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 基本医疗保险统筹基金支付比例
BALC: (() => {
const amount = param.detail?.find((t) => t.payEnum === 8)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 余额
// 特殊支付方式
SELF_YB_ZH_PAY: (() => {
const amount = param.detail?.find((t) => t.payEnum === 210000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 个人医保账户支付
SELF_YB_ZH_GJ_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 210100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 账户共济支付金额
SELF_CASH_PAY: (() => {
const amount = param.detail?.find((t) => t.payEnum === 220000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 个人现金支付金额
SELF_VX_PAY: (() => {
const amount = param.detail?.find((t) => t.payEnum === 230000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 微信支付金额
SELF_ALI_PAY: (() => {
const amount = param.detail?.find((t) => t.payEnum === 240000)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 阿里支付金额
// 现金支付细分
SELF_CASH_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 220400)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 个人现金支付金额(现金)
SELF_CASH_VX_VALUE: (() => {
// 微信+银联+支付宝的合计金额
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 (Number(vxValue) + Number(unionValue) + Number(aliValue)).toFixed(2) + ' 元';
})(),
SELF_CASH_ALI_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 220200)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 个人现金支付金额(支付宝)
SELF_CASH_UNION_VALUE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 220300)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 个人现金支付金额(银联)
// 基金类型(扩展)
BIRTH_FUND: (() => {
const amount = param.detail?.find((t) => t.payEnum === 510100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 生育基金
RETIREE_MEDICAL: (() => {
const amount = param.detail?.find((t) => t.payEnum === 340100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 离休人员医疗保障基金
URBAN_BASIC_MEDICAL: (() => {
const amount = param.detail?.find((t) => t.payEnum === 390100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 城乡居民基本医疗保险基金
URBAN_SERIOUS_ILLNESS: (() => {
const amount = param.detail?.find((t) => t.payEnum === 390200)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 城乡居民大病医疗保险基金
MEDICAL_ASSISTANCE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 610100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 医疗救助基金
GOVERNMENT_SUBSIDY: (() => {
const amount = param.detail?.find((t) => t.payEnum === 640100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 政府兜底基金
ACCIDENT_INSURANCE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 390400)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 意外伤害基金
CARE_INSURANCE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 620100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 照护保险基金
FINANCIAL_FUND: (() => {
const amount = param.detail?.find((t) => t.payEnum === 360100)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 财政基金
HOSPITAL_ADVANCE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 999900)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 医院垫付
SUPPLEMENTARY_INSURANCE: (() => {
const amount = param.detail?.find((t) => t.payEnum === 390300)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 城乡居民大病补充保险基金
HEALTHCARE_PREPAYMENT: (() => {
const amount = param.detail?.find((t) => t.payEnum === 360300)?.amount ?? 0;
return amount > 0 ? amount + ' 元' : amount;
})(), // 保健预支基金
Mr_QR_Code: param.regNo,
regNo: param.regNo || '',
sex: props.patientInfo?.genderEnum_enumText || '',
age: props.patientInfo?.age || '',
personType: param.contractName,
fixmedinsName: (param.fixmedinsName || '') + '门诊收费明细',
//电子收据二维码
pictureUrl: param.pictureUrl || 'https://chinaebill.com/img/xiaochengxu.png',
// 添加本地患者信息,注意安全访问
patientName: props.patientInfo?.patientName || '',
gender: props.patientInfo?.genderEnum_enumText || '',
encounterBusNo: props.patientInfo?.encounterBusNo || '',
currentDate: currentDate.value,
// chargedItems: props.chargedItems,
// 优先使用选中行的金额字段值(从图片中获取)
totalAmount:
(param.selectedRow?.receivableAmount
? param.selectedRow.receivableAmount.toFixed(2)
: props.totalAmount.toFixed(2)) + ' 元',
displayAmount:
(param.selectedRow?.receivedAmount
? param.selectedRow.receivedAmount.toFixed(2)
: displayAmount.value) + ' 元',
returnedAmount: returnedAmount.value + ' 元',
userName: userStore.nickName,
},
],
};
// 使用printUtils进行打印
// 选择门诊收费打印模板并传递正确的数据格式
console.log('[E] 最终打印数据检查:');
console.log(' - printData.data[0]存在:', !!printData.data[0]);
console.log(' - 关键字段:');
console.log(' * patientName:', printData.data[0]?.patientName);
console.log(' * regNo:', printData.data[0]?.regNo);
console.log(' * chargeItemsList:', printData.data[0]?.chargeItemsList ? `${printData.data[0].chargeItemsList.length}` : '无');
console.log(' * totalAmount:', printData.data[0]?.totalAmount);
console.log(' * currentDate:', printData.data[0]?.currentDate);
console.log('[F] 开始调用printUtils.print...');
console.log(' - 模板:', 'OUTPATIENT_CHARGE');
await printUtils.print(PRINT_TEMPLATE.OUTPATIENT_CHARGE, printData.data[0]);
console.log('[G] 打印调用成功完成');
console.log('========== 打印收费小票 - 结束 ==========');
} catch (error) {
console.error('[ERROR] 打印失败:', error);
console.error('[ERROR] 错误详情:', error.message);
console.error('[ERROR] 错误堆栈:', error.stack);
console.log('========== 打印收费小票 - 异常结束 ==========');
proxy.$modal.msgError('打印失败: ' + error.message);
}
}
const throttledGetList = debounce(submit, 300);
function handleWxPay() {
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,
});
}
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 handleOpen() {
formData.totalAmount = props.totalAmount;
formData.selfPay[0].amount = props.totalAmount;
}
async function submit() {
displayAmountTemp = displayAmount.value;
let amount = formData.selfPay
.reduce((sum, item) => {
return sum + (Number(item.amount) || 0);
}, 0)
.toFixed(2);
if (parseFloat(amount) < parseFloat(formData.totalAmount.toFixed(2))) {
proxy.$modal.msgError('请输入正确的结算金额');
return;
}
dialogLoading.value = true;
savePayment({
chargeItemIds: props.chargeItemIds,
encounterId: props.patientInfo.encounterId,
id: props.paymentId,
paymentDetails: formData.selfPay,
ybMdtrtCertType: props.userCardInfo.psnCertType,
busiCardInfo: props.userCardInfo.busiCardInfo,
})
.then((res) => {
if (res.code == 200) {
getChargeInfo({ paymentId: props.paymentId }, { skipErrorMsg: true })
.then((res) => {
// 传递完整的选中数据信息到打印方法
printReceipt({ ...res.data, chargedItems: props.chargedItems });
})
.catch(() => {
// 打印失败不影响收费成功流程
});
formData.selfPay = [{ payEnum: 220100, amount: 0.0, payLevelEnum: 2 }];
emit('close', 'success', res.msg);
emit('refresh'); // 发送刷新事件给父组件
// 长春市朝阳区中医院自动发耗材(静默执行,不阻塞主流程)
if (userStore.fixmedinsCode == 'H22010200672' && props.consumablesIdList.length > 0) {
dispenseMedicalConsumables(props.consumablesIdList, { skipErrorMsg: true })
.then(() => {})
.catch(() => {
// 发耗材失败不影响收费成功流程
});
}
} else {
proxy.$modal.msgError(res.msg || '收费失败');
}
})
.catch((error) => {
console.error('收费失败:', error);
proxy.$modal.msgError(error.message || '收费失败,请重试');
})
.finally(() => {
dialogLoading.value = false;
});
}
/** 打印收费结算单 */
async function print() {
console.log('patientInfo', props.patientInfo);
console.log('category', props.category);
console.log('totalAmount', props.totalAmount);
console.log('chargeItemIds', props.chargeItemIds);
console.log('consumablesIdList', props.consumablesIdList);
console.log('userCardInfo', props.userCardInfo);
console.log('paymentId', props.paymentId);
console.log('details', props.details);
console.log('chargedItems', props.chargedItems);
const result = {
data: [
{
name: props.patientInfo.patientName, // 姓名
gender: props.patientInfo.genderEnum_enumText, // 性别
age: props.patientInfo.age, // 年龄
encounterBusNo: props.patientInfo.encounterBusNo, // 病例号
currentDate: currentDate.value, // 收费日期
chargedItems: props.chargedItems, // 收费项目
totalAmount: props.totalAmount.toFixed(2) + ' 元', // 应收金额
displayAmount: displayAmount.value + ' 元', // 实收金额
returnedAmount: returnedAmount.value + ' 元', // 应找零
},
],
};
console.log(result, '==result.data==');
const printElements = JSON.parse(
JSON.stringify(templateJson).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
const printerList = hiprintTemplate.getPrinterList();
console.log(hiprintTemplate, '打印机列表');
hiprintTemplate.print2(result.data[0], {
title: '门诊收费结算单',
});
}
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 },
{ label: '优惠', value: 220500, isWebPay: false },
];
// 计算剩余可输入金额
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;
};
// 折扣计算
function handleDiscountChange(value) {
let amount = props.totalAmount * Number(value);
discountAmount.value = props.totalAmount - amount;
formData.selfPay = [
{ payEnum: 220100, amount: amount, payLevelEnum: 2 },
{ payEnum: 220500, amount: discountAmount.value, payLevelEnum: 2 },
];
}
// 检查支付方式是否已使用
const isMethodDisabled = (option) => {
if (formData.selfPay.length > 1) {
// 禁用已被选择的相同方式,避免重复选择
const selectedEnums = formData.selfPay.map((item) => item.payEnum);
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, value) => {
const selectedOption = selfPayMethods.find((item) => item.value === value);
if (selectedOption) {
payTypeText.value = selectedOption.label;
}
};
// 计算属性
const displayAmount = computed(() => {
return formData.selfPay
.reduce((sum, item) => {
if (item.payEnum !== 220500) {
return sum + (Number(item.amount) || 0);
}
return sum;
}, 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() {
// unprecharge({ encounterId: props.patientInfo.encounterId }).then((res) => {
// if (res.code == 200) {
// emit('close');
// } else {
// proxy.$modal.msgError(res.message);
// }
// });
formData.totalAmount = 0;
formData.selfPay = [{ payEnum: 220100, amount: 0.0, payLevelEnum: 2 }];
formData.medicalInsurance = {
account: '',
poolPay: 0,
personalPay: 0,
};
// 重置折扣相关状态
discountRadio.value = undefined;
discountAmount.value = 0;
emit('close');
}
defineExpose({
printReceipt,
});
</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; /* 避免点击干扰 */
}
/* 调整输入框内边距 */
.amount-input .el-input__inner {
padding-right: 30px !important;
}
</style>

View File

@@ -0,0 +1,433 @@
{
"panels": [
{
"index": 0,
"name": 1,
"paperType": "自定义",
"height": 160,
"width": 80,
"paperHeader": 0,
"paperFooter": 450.7086614173229,
"paperNumberDisabled": true,
"paperNumberContinue": true,
"expandCss": "",
"overPrintOptions": {
"content": "",
"opacity": 0.7,
"type": 1
},
"watermarkOptions": {
"content": "",
"fillStyle": "rgba(184, 184, 184, 0.3)",
"fontSize": "36px",
"rotate": 25,
"width": 413,
"height": 310,
"timestamp": false,
"format": "YYYY-MM-DD HH:mm"
},
"panelLayoutOptions": {
"layoutType": "column",
"layoutRowGap": 0,
"layoutColumnGap": 0
},
"printElements": [
{
"options": {
"left": 45,
"top": 15,
"height": 16.5,
"width": 142.5,
"title": "长大医院门诊收费结算单",
"coordinateSync": false,
"widthHeightSync": false,
"fontWeight": "bold",
"letterSpacing": 0.75,
"textAlign": "center",
"qrCodeLevel": 0,
"fontSize": 12
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 15,
"top": 39,
"height": 14,
"width": 80,
"title": "姓名",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "name"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 99,
"top": 39,
"height": 14,
"width": 60,
"title": "性别",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "gender"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 163.5,
"top": 39,
"height": 14,
"width": 60,
"title": "年龄",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "age"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 15,
"top": 60,
"height": 14,
"width": 120,
"title": "病人类型",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "medType"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 13.5,
"top": 82.5,
"height": 14,
"width": 120,
"title": "病历号",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "encounterBusNo"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 13.5,
"top": 102,
"height": 37.5,
"width": 208.5,
"title": "undefined+beforeDragIn",
"field": "chargedItems",
"coordinateSync": false,
"widthHeightSync": false,
"textAlign": "center",
"tableBodyRowBorder": "border",
"tableBodyCellBorder": "border",
"columns": [
[
{
"title": "项目名称",
"titleSync": false,
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 61.93559159547676,
"field": "itemName",
"checked": true,
"columnId": "itemName",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "单价",
"titleSync": false,
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 37.1097311853845,
"field": "unitPrice",
"checked": true,
"columnId": "unitPrice",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "数量",
"titleSync": false,
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 37.12571954521698,
"field": "quantityValue",
"checked": true,
"columnId": "quantityValue",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "总价",
"titleSync": false,
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 35.15170328143829,
"field": "totalPrice",
"checked": true,
"columnId": "totalPrice",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "费用性质",
"titleSync": false,
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 37.17725439248345,
"field": "contractName",
"checked": true,
"columnId": "contractName",
"fixed": false,
"rowspan": 1,
"colspan": 1
},
{
"title": "收费名称",
"titleSync": false,
"tableQRCodeLevel": 0,
"tableSummaryTitle": true,
"tableSummary": "",
"width": 56.86712269302148,
"field": "chargeItem",
"checked": false,
"columnId": "chargeItem",
"fixed": false,
"rowspan": 1,
"colspan": 1
}
]
]
},
"printElementType": {
"title": "表格",
"type": "table",
"editable": true,
"columnDisplayEditable": true,
"columnDisplayIndexEditable": true,
"columnTitleEditable": true,
"columnResizable": true,
"columnAlignEditable": true,
"isEnableEditField": true,
"isEnableContextMenu": true,
"isEnableInsertRow": true,
"isEnableDeleteRow": true,
"isEnableInsertColumn": true,
"isEnableDeleteColumn": true,
"isEnableMergeCell": true
}
},
{
"options": {
"left": 99,
"top": 147,
"height": 15,
"width": 123,
"title": "合计金额",
"field": "itemTotalAmount",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 6,
"top": 171,
"height": 14,
"width": 108,
"title": "应收金额",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "totalAmount"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 124.5,
"top": 171,
"height": 14,
"width": 97.5,
"title": "实收金额",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "displayAmount"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 6,
"top": 193.5,
"height": 14,
"width": 108,
"title": "全自费金额",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "FULAMT_OWNPAY_AMT"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 124.5,
"top": 193.5,
"height": 13.5,
"width": 97.5,
"title": "医保政策金额",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "INSCP_SCP_AMT"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 6,
"top": 216,
"height": 14,
"width": 108,
"title": "基金支付",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "YB_FUND_PAY"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 124.5,
"top": 216,
"height": 13.5,
"width": 97.5,
"title": "统筹支付",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "YB_TC_FUND_AMOUNT"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 6,
"top": 240,
"height": 14,
"width": 216,
"title": "个人医保账户支付",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "SELF_YB_ZH_PAY"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 6,
"top": 268.5,
"height": 14,
"width": 106.5,
"title": "收费员",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "userName"
},
"printElementType": {
"title": "文本",
"type": "text"
}
},
{
"options": {
"left": 6,
"top": 294,
"height": 14,
"width": 214.5,
"title": "收费时间",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0,
"field": "currentDate"
},
"printElementType": {
"title": "文本",
"type": "text"
}
}
]
}
]
}

View File

@@ -0,0 +1,693 @@
<template>
<div style="display: flex; justify-content: space-between" class="app-container" v-loading="readCardLoading"
:element-loading-text="loadingText">
<el-card style="width: 30%">
<template #header>
<span style="vertical-align: middle">患者列表</span>
</template>
<div style="width: 100%">
<el-input v-model="queryParams.searchKey" placeholder="请输入患者名/病历号" clearable
style="width: 48%; 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: 48%; margin-bottom: 10px; margin-right: 10px"
placeholder="收费状态" @change="getPatientList">
<el-option v-for="item in chargeStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<div style="width: 100%">
<el-date-picker v-model="receptionTime" type="daterange" range-separator="~" start-placeholder="开始时间"
end-placeholder="结束时间" placement="bottom" value-format="YYYY-MM-DD"
style="width: 84%; margin-bottom: 10px; margin-right: 10px" @change="getPatientList" />
<el-button type="primary" style="margin-bottom: 10px" @click="getPatientList">
搜索
</el-button>
</div>
<el-table ref="patientListRef" height="620" :data="patientList" row-key="encounterId" @cell-click="clickRow"
highlight-current-row>
<el-table-column label="病历号" align="center" prop="encounterBusNo" />
<el-table-column label="姓名" align="center" prop="patientName" />
<!-- <el-table-column label="时间" align="center" prop="receptionTime" width="160">
<template #default="scope">
{{ formatDate(scope.row.receptionTime) }}
</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">
<template #header>
<span style="vertical-align: middle">基本信息</span>
</template>
<el-descriptions :column="5">
<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.organizationName }}
</el-descriptions-item>
<el-descriptions-item label="就诊时间:">
{{ formatDateStr(patientInfo.receptionTime, 'YYYY-MM-DD HH:mm:ss') }}
</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="min-width: 1100px">
<template #header>
<span style="vertical-align: middle">收费项目</span>
</template>
<div style="margin-bottom: 10px">
<el-button type="primary" @click="confirmCharge()" :disabled="buttonDisabled">
确认收费
</el-button>
<el-button type="primary" plain @click="handleReadCard('01')" style="width: 65px">
电子凭证
</el-button>
<el-button type="primary" plain @click="handleReadCard('02')" style="width: 65px" :disabled="true">
身份证
</el-button>
<el-button type="primary" plain @click="handleReadCard('03')" style="width: 65px">
医保卡
</el-button>
<el-button type="primary" @click="payToSelt()" style="margin-left: 20px" :disabled="buttonDisabled">
医保转自费
</el-button>
<el-button type="primary" @click="patToMedicalInsurance()" style="margin-left: 20px"
:disabled="buttonDisabled">
自费转医保
</el-button>
<el-button
type="primary"
@click="studentPayTosStudentSelf()"
style="margin-left: 20px"
:disabled="buttonDisabled"
>
学生医保转学生自费
</el-button>
<el-button
type="primary"
@click="studentSelfToStudentPay()"
style="margin-left: 20px"
:disabled="buttonDisabled"
>
学生自费转学生医保
</el-button>
</div>
<div style="text-align: right; padding-right: 20px; margin-bottom: 10px;">
<span style="font-weight: bold; font-size: 14px;"
>合计金额{{ totalAmounts ? totalAmounts.toFixed(2) : 0 }}</span
>
</div>
<el-table
ref="chargeListRef"
height="530"
:data="chargeList"
row-key="id"
@selection-change="handleSelectionChange"
v-loading="chargeLoading"
:span-method="objectSpanMethod"
border
>
<el-table-column type="selection" :selectable="checkSelectable" width="55" />
<el-table-column label="单据号" align="center" prop="busNo" width="180" />
<el-table-column label="收费项目" align="center" prop="itemName" width="200" />
<el-table-column label="数量" align="center" prop="quantityValue" width="80" />
<el-table-column label="医疗类型" align="center" prop="medTypeCode_dictText" />
<el-table-column label="医保编码" align="center" prop="ybNo" />
<el-table-column label="费用性质" align="center" prop="contractName" />
<el-table-column label="收费状态" align="center" prop="statusEnum_enumText" width="150">
<template #default="scope">
<el-tag v-if="scope.row.statusEnum === 1" disable-transitions>
{{ scope.row.statusEnum_enumText }}
</el-tag>
<el-tag v-else-if="scope.row.statusEnum === 5" type="success" disable-transitions>
{{ scope.row.statusEnum_enumText }}
</el-tag>
<el-tag v-else-if="scope.row.statusEnum === 8" type="danger" disable-transitions>
{{ scope.row.statusEnum_enumText }}
</el-tag>
<el-tag v-else type="warning" disable-transitions>
{{ scope.row.statusEnum_enumText }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="金额" align="right" prop="totalPrice" header-align="center">
<template #default="scope">
{{ scope.row.totalPrice.toFixed(2) + ' 元' || '0.00' + ' 元' }}
</template>
</el-table-column>
<el-table-column label="收款人" align="center" prop="entererId_dictText" />
<el-table-column
label="操作"
align="center"
fixed="right"
header-align="center"
class-name="no-hover-column"
>
<template #default="scope">
<el-button
:disabled="!scope.row.paymentId"
link
type="primary"
@click="printCharge(scope.row)"
>
打印
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
<ChargeDialog
ref="chargeDialogRef"
:open="openDialog"
@close="handleClose"
:category="patientInfo.categoryEnum"
:totalAmount="totalAmount"
:patientInfo="patientInfo"
:chargeItemIds="chargeItemIdList"
:consumablesIdList="consumablesIdList"
:chrgBchnoList="chrgBchnoList"
:userCardInfo="userCardInfo"
:paymentId="paymentId"
:details="details"
:chargedItems="chargedItems"
:feeType="patientInfo.medfeePaymtdCode"
:medfee_paymtd_code="medfee_paymtd_code"
@refresh="getPatientList"
/>
</div>
</template>
<script setup name="ClinicCharge">
import {
changeStudentPayTosStudentSelf,
changeStudentSelfToStudentPay,
changeToMedicalInsurance,
changeToSelfPay,
getChargeInfo,
getChargeList,
getList,
init,
precharge,
} from './components/api';
import {invokeYbPlugin5000, invokeYbPlugin5001} from '@/api/public';
import ChargeDialog from './components/chargeDialog.vue';
import {formatDateStr} from '@/utils';
import useUserStore from '@/store/modules/user';
import Decimal from 'decimal.js';
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
const { medfee_paymtd_code } = proxy.useDict('medfee_paymtd_code');
const queryParams = ref({
pageNum: 1,
pageSize: 50,
statusEnum: 1,
});
const totalAmounts = ref(0);
const selectedRows = ref([]);
const patientList = ref([]);
const chargeList = ref([]);
const chargeItemIdList = ref([]);
const chrgBchnoList = ref([]);
const chargeLoading = ref(false);
const encounterId = ref('');
const paymentId = ref('');
const patientInfo = ref({});
const openDialog = ref(false);
const totalAmount = ref(0);
const chargeListRef = ref();
const details = ref({});
const chargeStatusOptions = ref([]);
const receptionTime = ref([
formatDateStr(new Date(), 'YYYY-MM-DD'),
formatDateStr(new Date(), 'YYYY-MM-DD'),
]);
const buttonDisabled = computed(() => {
return Object.keys(patientInfo.value).length === 0;
});
const chargedItems = ref([]);
watch(
() => selectedRows.value,
(newVlaue) => {
if (newVlaue && newVlaue.length > 0) {
handleTotalAmount();
} else {
totalAmounts.value = 0;
}
},
{ immediate: true }
);
watch(
() => chargeList.value,
(newVlaue) => {
if (newVlaue && newVlaue.length > 0) {
handleTotalAmount();
} else {
totalAmounts.value = 0;
}
},
{ immediate: true }
);
function handleSelectionChange(selection) {
selectedRows.value = selection;
}
function handleTotalAmount() {
if (selectedRows.value.length == 0) {
totalAmounts.value = chargeList.value.reduce((accumulator, currentRow) => {
return new Decimal(accumulator).add(currentRow.totalPrice.toFixed(2) || 0);
}, new Decimal(0));
} else {
totalAmounts.value = selectedRows.value.reduce((accumulator, currentRow) => {
return new Decimal(accumulator).add(currentRow.totalPrice.toFixed(2) || 0);
}, 0);
}
}
getPatientList();
initOption();
/**
* 患者列表
*/
function getPatientList() {
if (receptionTime.value.length > 0) {
queryParams.value.receptionTimeSTime = receptionTime.value[0] + ' 00:00:00';
queryParams.value.receptionTimeETime = receptionTime.value[1] + ' 23:59:59';
} else {
queryParams.value.receptionTimeSTime = undefined;
queryParams.value.receptionTimeETime = undefined;
}
getList(queryParams.value).then((res) => {
patientList.value = res.data?.data?.records || [];
});
}
function initOption() {
init().then((res) => {
chargeStatusOptions.value = res.data.chargeItemStatusOptions;
});
}
function checkSelectable(row, index) {
// 已结算时禁用选择框
return row.statusEnum === 1;
}
/**
* 点击患者列表行 获取处方列表
*/
function clickRow(row) {
patientInfo.value = row;
chargeLoading.value = true;
encounterId.value = row.encounterId;
getChargeList(row.encounterId).then((res) => {
chargeList.value = res.data;
setTimeout(() => {
chargeLoading.value = false;
chargeListRef.value.toggleAllSelection();
}, 100);
});
}
function handleClose(value, msg) {
openDialog.value = false;
if (value == 'success') {
proxy.$modal.msgSuccess(msg);
chargeLoading.value = true;
getChargeList(patientInfo.value.encounterId, { skipErrorMsg: true }).then((res) => {
chargeList.value = res.data;
setTimeout(() => {
chargeLoading.value = false;
}, 100);
});
}
}
const consumablesIdList = ref([]);
// 确认收费
function confirmCharge() {
let selectRows = chargeListRef.value.getSelectionRows();
if (selectRows.length == 0) {
proxy.$modal.msgWarning('请选择一条收费项目');
return;
}
chargeItemIdList.value = selectRows.map((item) => {
return item.id;
});
consumablesIdList.value = selectRows
.filter((item) => {
return item.serviceTable == 'wor_device_request';
})
.map((item) => {
return item.id;
});
chargedItems.value = selectRows;
// 将选中的行数据赋值给chargedItems
chargedItems.value = selectRows;
// totalAmount.value = selectRows.reduce((accumulator, currentRow) => {
// return accumulator + (currentRow.totalPrice || 0);
// }, 0);
precharge({
patientId: patientInfo.value.patientId,
encounterId: patientInfo.value.encounterId,
chargeItemIds: chargeItemIdList.value,
}).then((res) => {
if (res.code == 200 && res.data) {
// totalAmount.value = res.data.psnCashPay;
paymentId.value = res.data.paymentId;
chrgBchnoList.value = res.data.chrgBchnoList;
totalAmount.value = res.data.details?.find((item) => item.payEnum == 220000)?.amount ?? 0;
details.value = res.data.details?.filter((item) => {
return item.amount > 0;
}) || [];
openDialog.value = true;
} else {
proxy.$modal.msgError(res?.msg || '预结算失败');
}
});
// console.log(patientInfo)
}
let userCardInfo = ref({});
const readCardLoading = ref(false);
const loadingText = ref('');
const BusiCardInfo = ref(''); // miyao
async function handleReadCard(value) {
// if (window.CefSharp === undefined) {
// alert('请在医保版本中调用读卡功能!');
// } else {
try {
// await CefSharp.BindObjectAsync('boundAsync');
// string url,
// string fixmedins_code,
// string businessType,
// string operatorCode,
// string operatorName,
// string officeId,
// string officeName
// readCardLoading.value = true;
let jsonResult;
let cardInfo;
let userMessage = undefined;
switch (value) {
case '01': // 电子凭证
// readCardLoading.value = true;
await invokeYbPlugin5000({
FunctionId: 3,
url: 'http://10.47.0.67:8089/localcfc/api/hsecfc/localQrCodeQuery',
orgId: 'H22010200672',
businessType: '01101',
operatorId: userStore.id.toString(),
operatorName: userStore.name,
officeId: 'D83',
officeName: '财务科',
})
.then((res) => {
readCardLoading.value = true;
loadingText.value = '正在读取...';
jsonResult = res.data;
})
.catch(() => {
readCardLoading.value = false;
})
.finally(() => {
readCardLoading.value = false;
});
cardInfo = JSON.parse(JSON.stringify(jsonResult));
let message = JSON.parse(cardInfo.data);
userMessage = {
certType: '02', // 证件类型
certNo: message.data.idNo, // 身份证号
psnCertType: '02', // 居民身份证
};
userCardInfo = {
certType: '01', // 证件类型
certNo: message.data.idNo, // 身份证号
psnCertType: '01', // 居民身份证
busiCardInfo: message.data.ecToken, // 令牌
};
BusiCardInfo.value = message.data.ecToken;
console.log(BusiCardInfo.value);
break;
case '02':
break;
case '03': // 社保卡
readCardLoading.value = true;
loadingText.value = '正在读取...';
await invokeYbPlugin5001(
JSON.stringify({
FunctionId: 1,
IP: 'ddjk.jlhs.gov.cn',
PORT: 20215,
TIMEOUT: 60,
SFZ_DRIVER_TYPE: 1,
})
)
.then((res) => {
jsonResult = JSON.stringify(res.data);
})
.finally(() => {
readCardLoading.value = false;
});
// console.log(
// 'jsonResult',
// JSON.parse({
// IssuingAreaCode: '310000',
// SocialSecurityNumber: '371324198810224515',
// CardNumber: 'M501A1A78',
// CardIdentificationCode: '310000D15600000535925154E880AB97',
// Name: '\u5218\u5CF0',
// CardResetInfo: '00814A444686603100333E4FA9',
// SpecificationVersion: '3.00',
// IssuingDate: '20190313',
// ExpirationDate: '20290313',
// TerminalNumber: '000000000000',
// TerminalDeviceNumber: '00041161201901000005',
// Code: 0,
// ErrorMessage: null,
// })
// );
let message1 = JSON.parse(jsonResult);
userMessage = {
certType: '02', // 证件类型
certNo: message1.SocialSecurityNumber, // 身份证号
psnCertType: '02', // 居民身份证
};
userCardInfo = {
certType: '02', // 证件类型
certNo: message1.SocialSecurityNumber, // 身份证号
psnCertType: '02', // 居民身份证
busiCardInfo: message1.BusiCardInfo, //卡号
};
BusiCardInfo.value = message1.BusiCardInfo;
console.log(message1.BusiCardInfo);
break;
case '99':
break;
}
readCardLoading.value = false;
if (userMessage.certNo) {
let selectRows = chargeListRef.value.getSelectionRows();
if (selectRows.length == 0) {
proxy.$modal.msgWarning('请选择一条收费项目');
return;
}
chargeItemIdList.value = selectRows.map((item) => {
return item.id;
});
totalAmount.value = selectRows.reduce((accumulator, currentRow) => {
return accumulator + (currentRow.totalPrice || 0);
}, 0);
precharge({
patientId: patientInfo.value.patientId,
encounterId: patientInfo.value.encounterId,
chargeItemIds: chargeItemIdList.value,
ybMdtrtCertType: userCardInfo.psnCertType,
busiCardInfo: userCardInfo.busiCardInfo,
}).then((res) => {
if (res.code == 200 && res.data) {
// totalAmount.value = res.data.psnCashPay;
paymentId.value = res.data.paymentId;
totalAmount.value = res.data.details?.find((item) => item.payEnum == 220000)?.amount ?? 0;
details.value = res.data.details || [];
// chrgBchnoList.value = res.data.chrgBchnoList;
chargeItemIdList.value = selectRows.map((item) => {
return item.id;
});
// 打印项目赋值
chargedItems.value = selectRows;
consumablesIdList.value = selectRows
.filter((item) => {
return item.serviceTable == 'wor_device_request';
})
.map((item) => {
return item.id;
});
openDialog.value = true;
} else {
proxy.$modal.msgError(res?.msg || '预结算失败');
}
});
}
} catch (error) {
console.error('调用失败:', error);
readCardLoading.value = false;
}
// }
}
/**
* 医保转自费
*/
function payToSelt() {
changeToSelfPay(encounterId.value).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
}
});
}
/**
* 自费转医保
*/
function patToMedicalInsurance() {
changeToMedicalInsurance(encounterId.value).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
}
});
}
/**
* 学生医保转学生自费
*/
function studentPayTosStudentSelf() {
changeStudentPayTosStudentSelf(encounterId.value).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
}
});
}
/**
* 学生自费转学生医保
*/
function studentSelfToStudentPay() {
changeStudentSelfToStudentPay(encounterId.value).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
}
});
}
// 添加行合并方法
function objectSpanMethod({ row, column, rowIndex, columnIndex }) {
// 只对操作列进行合并(根据列索引判断,操作列为最后一列)
if (columnIndex === 10) {
// 操作列索引为10从0开始计数
// 如果当前行的paymentId为空则不合并
if (!row.paymentId) {
return [1, 1];
}
// 查找相同paymentId的连续行
let spanCount = 1;
if (rowIndex === 0 || chargeList.value[rowIndex - 1].paymentId !== row.paymentId) {
// 这是具有相同paymentId的第一行计算需要合并的行数
for (let i = rowIndex + 1; i < chargeList.value.length; i++) {
if (chargeList.value[i].paymentId === row.paymentId) {
spanCount++;
} else {
break;
}
}
return [spanCount, 1];
} else {
// 这不是具有相同paymentId的第一行返回0表示不显示
return [0, 0];
}
}
// 其他列不合并
return [1, 1];
}
// function printCharge(row) {
// // 打印功能实现
// let rows = [];
// chargeList.value.forEach((item, index) => {
// if (item.paymentId === row.paymentId) {
// rows.push(item);
// }
// });
// chargedItems.value = rows;
// getChargeInfo({ paymentId: row.paymentId }).then((res) => {
// proxy.$refs['chargeDialogRef'].printReceipt(res.data);
// });
// }
function printCharge(row) {
// 打印功能实现
let rows = [];
chargeList.value.forEach((item, index) => {
if (item.paymentId === row.paymentId) {
rows.push(item);
}
});
chargedItems.value = rows;
getChargeInfo({ paymentId: row.paymentId }).then((res) => {
// 设置实收金额
if (res.data && res.data.detail) {
const amountDetail = res.data.detail?.find((item) => item.payEnum == 220000);
if (amountDetail) {
totalAmount.value = amountDetail.amount || 0;
// 为合并的行设置金额相关字段值
rows.forEach((item) => {
if (item.actualPrice === undefined || item.actualPrice === null) {
item.actualPrice = 0;
}
if (item.discountAmount === undefined || item.discountAmount === null) {
item.discountAmount = 0;
}
if (item.discountRate === undefined || item.discountRate === null) {
item.discountRate = 100;
}
});
}
// 增强打印数据,包含选中行的完整信息
const enhancedPrintData = {
...res.data,
selectedRow: row, // 添加选中行的完整数据
chargedItems: rows, // 添加所有相关的收费项目
};
// 通知子组件更新内部状态
// 通过 nextTick 确保 DOM 更新后再调用打印
nextTick(() => {
proxy.$refs['chargeDialogRef'].printReceipt(enhancedPrintData);
});
}
});
}
</script>
<style scoped>
:deep(.no-hover-column) .cell:hover {
background-color: transparent !important;
}
:deep(.el-table__body) tr:hover td.no-hover-column {
background-color: inherit !important;
}
</style>

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,467 @@
<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">费用性质{{ 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.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 {getReturnMedicineList, refund, renturnDispenseMedical} from './api';
import {computed, getCurrentInstance, reactive, ref, watch} 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,
},
medfee_paymtd_code: {
type: Array,
default: () => [],
},
feeType: {
type: String,
default: '',
},
});
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);
console.log(
displayAmount.value,
parseFloat(displayAmount.value),
formData.totalAmount,
formData,
'feeRefund'
);
//比较时,将金额都转换为两位小数的字符串再转换为浮点数,避免浮点数精度问题
if (parseFloat(displayAmount.value) < parseFloat(formData.totalAmount.toFixed(2))) {
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');
}
// 获取费用性质文本
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 '自费';
});
</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,350 @@
<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" 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"
:medfee_paymtd_code="medfee_paymtd_code"
:feeType="patientInfo.medfeePaymtdCode"
/>
</div>
</template>
<script setup name="ClinicCharge">
import {getChargeItemIds, getList, getRefundList, getReturnDetail, init, validReturnDrug,} from './components/api';
import {formatDateStr} from '@/utils/index';
import RefundDialog from './components/refundDialog.vue';
const { proxy } = getCurrentInstance();
// 获取费用性质字典
const { medfee_paymtd_code } = proxy.useDict('medfee_paymtd_code');
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 ?? 0) -
(res.data.find((item) => item.payEnum === 220500)?.amount ?? 0);
}
details.value = res.data?.filter((item) => {
return item.amount > 0;
}) || [];
});
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>

View File

@@ -0,0 +1,502 @@
<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, getCurrentInstance, nextTick, reactive, ref, watch} from 'vue';
import {Delete} from '@element-plus/icons-vue';
import {debounce} from 'lodash-es';
import printUtils, {PRINT_TEMPLATE, printRegistrationReceipt} 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,
},
orgId: {
type: String,
default: '',
},
orgName: {
type: String,
default: '',
},
});
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 = {
patientName: props.patientInfo?.patientName || props.patientInfo?.name || '',
cardNo: props.patientInfo?.busNo || param.busNo || '',
idCard: param.idCard || '',
phone: props.patientInfo?.phone || '',
organizationName: props.orgName || props.patientInfo?.organizationName || '',
practitionerName: props.patientInfo?.practitionerName || '',
healthcareName: props.patientInfo?.healthcareName || '',
visitTime: new Date().toLocaleString(),
price: param.totalPrice || props.totalAmount || 0,
activityPrice: 0,
medicalRecordFee: 0,
totalPrice: param.totalPrice || props.totalAmount || 0,
serialNo: props.patientInfo?.encounterId || param.encounterId || '',
encounterId: props.patientInfo?.encounterId || param.encounterId || '',
printTime: new Date().toLocaleString(),
};
console.log('printReceipt printData', printData);
// 使用与补打挂号相同的打印模板 (不带"补打"字样)
await printRegistrationReceipt(printData);
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>

View File

@@ -0,0 +1,265 @@
import request from '@/utils/request';
// 查询初期所需数据
export function getInit() {
return request({
url: '/charge-manage/register/init',
method: 'get',
});
}
// 查询患者信息
export function getOutpatientRegistrationList(query) {
return request({
url: '/charge-manage/register/patient-metadata',
method: 'get',
params: query,
});
}
// 查询费用性质
export function getContractList() {
return request({
url: '/app-common/contract-list',
method: 'get',
});
}
// 查询诊断信息
export function getConditionDefinitionMetadata(query) {
return request({
url: '/charge-manage/register/condition-definition-metadata',
method: 'get',
params: query,
});
}
// // 查询就诊位置
// export function getLocationTree(query) {
// return request({
// url: '/charge-manage/register/location-tree',
// method: 'get',
// params: query
// })
// }
// 查询就诊科室
export function getLocationTree(query) {
return request({
url: '/charge-manage/register/org-list',
method: 'get',
params: query,
});
}
// 根据位置id筛选医生
export function getPractitionerMetadata(query) {
return request({
url: '/charge-manage/register/practitioner-metadata',
method: 'get',
params: query,
});
}
// 根据机构id筛选服务项目
export function getHealthcareMetadata(query) {
return request({
url: '/charge-manage/register/healthcare-metadata',
method: 'get',
params: query,
});
}
// 更新患者手机号
export function updatePatientPhone(data) {
return request({
url: '/patient-manage/information/update-patient-phone',
method: 'post',
data: data,
});
}
// 门诊挂号查询
export function getOutpatientRegistrationCurrent(query) {
return request({
url: '/charge-manage/register/current-day-encounter',
method: 'get',
params: query,
});
}
// 新增门诊挂号信息
export function addOutpatientRegistration(data) {
return request({
url: '/payment/payment/reg-pre-pay',
method: 'post',
data: data,
});
}
// 新增病人信息
export function addPatient(data) {
return request({
url: '/patient-manage/information/patient-information',
method: 'post',
data: data,
});
}
export function listmaritalstatus() {
return request({
url: '/patientmanage/information/list-maritalstatus',
method: 'get',
});
}
export function listoccupationtype() {
return request({
url: '/patientmanage/information/list-occupationtype',
method: 'get',
});
}
export function lisadministrativegender() {
return request({
url: '/patientmanage/information/list-administrativegender',
method: 'get',
});
}
export function listbloodtypeabo() {
return request({
url: '/patientmanage/information/list-bloodtypeabo',
method: 'get',
});
}
export function listbloodtypearh() {
return request({
url: '/patientmanage/information/list-bloodtypearh',
method: 'get',
});
}
export function listfamilyrelationshiptype() {
return request({
url: '/patientmanage/information/list-familyrelationshiptype',
method: 'get',
});
}
// 查询患者相关
export function patientlLists() {
return request({
url: '/patient-manage/information/init',
method: 'get',
});
}
// 患者退号
export function returnRegister(encounterId) {
return request({
url: '/charge-manage/register/return?encounterId=' + encounterId,
method: 'put',
});
}
/**
* 收费
*/
export function savePayment(data) {
return request({
url: '/payment/payment/reg-pay',
method: 'post',
data: data,
});
}
/**
* 收费预结算
*/
export function precharge(data) {
return request({
url: '/payment/payment/precharge',
method: 'post',
data: data,
});
}
/**
* 退费
*/
export function refund(data) {
return request({
url: '/payment/payment/uncharge',
method: 'post',
data: data,
});
}
/**
* 退费预检(挂号预取消)
* GET /payment/payment/pre-cancel-reg?encounterId=xxx
*/
export function preCancelReg(encounterId) {
return request({
url: '/payment/payment/pre-cancel-reg',
method: 'get',
params: { encounterId },
});
}
/**
* 取消挂号
*/
export function cancelRegister(data) {
return request({
url: '/charge-manage/register/return',
method: 'put',
data: data,
});
}
/**
* 获取用户信息
*/
export function gerPreInfo(userMaessage) {
return request({
url: '/yb-request/per-info',
method: 'post',
params: userMaessage,
});
}
/**
* 微信支付
*/
export function wxPay(data) {
return request({
url: '/three-part/pay/pay-for',
method: 'post',
data: data,
});
}
/**
* 微信支付
*/
export function WxPayResult(data) {
return request({
url: '/three-part/pay/pay-query',
method: 'get',
data: data,
});
}
/**
* 补打挂号
*/
export function reprintRegistration(data) {
return request({
url: '/charge-manage/register/reprint',
method: 'post',
data: data,
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,184 @@
<template>
<!-- <div class="app-container"> -->
<el-dialog :title="title" v-model="visible" width="800px" append-to-body>
<el-table
:data="patientInfoList"
@selection-change="handleSelectionChange"
width="90%"
@cell-dblclick="handleCellDblClick"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column
label="患者姓名"
align="center"
key="name"
prop="name"
:show-overflow-tooltip="true"
/>
<el-table-column
label="性别"
align="center"
key="genderEnum_enumText"
prop="genderEnum_enumText"
:show-overflow-tooltip="true"
/>
<el-table-column
label="身份证号"
align="center"
key="idCard"
prop="idCard"
:show-overflow-tooltip="true"
/>
<el-table-column
label="电话"
align="center"
key="phone"
prop="phone"
:show-overflow-tooltip="true"
width="100"
/>
<el-table-column
label="生日"
align="center"
key="birthDate"
prop="birthDate"
:show-overflow-tooltip="true"
width="50"
/>
<el-table-column
label="年龄"
align="center"
key="age"
:show-overflow-tooltip="true"
>
<template #default="scope">
{{ scope.row.age ? `${scope.row.age}` : '-' }}
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<!-- </div> -->
</template>
<script setup name="PatientInfoDialog">
import {getOutpatientRegistrationList,} from "./outpatientregistration";
const { proxy } = getCurrentInstance();
const patientInfoList = ref([]);
const selectedData = ref([]); // 存储选择的行数据
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const title = ref("病人信息");
const visible = ref(false);
const emits = defineEmits(['submit']); // 声明自定义事件
const data = reactive({
form: {},
queryParams: {
pageNo: 1,
pageSize: 10,
searchKey: undefined, // 品名/商品名/英文品名/编码/拼音
},
rules: {},
});
const { queryParams, form, rules } = toRefs(data);
const props = defineProps({
patientInfoData: {
type: Object,
required: false,
default: () => ({}), // 提供默认值
},
searchInfo: {
type: String,
required: true,
default: "",
},
});
// 显示弹框
function show() {
patientInfoList.value = props.patientInfoData.records;
total.value = props.patientInfoData.total;
console.log(props.patientInfoData, "props.patientInfoData");
visible.value = true;
}
/** 查询病种目录列表 */
function getList() {
const query = {
searchKey: props.searchInfo,
pageNo: queryParams.value.pageNo,
pageSize: queryParams.value.pageSize,
};
getOutpatientRegistrationList(query).then((res) => {
if (res.data.records.length > 0) {
patientInfoList.value = res.data.records;
total.value = res.data.total;
console.log(patientInfoList.value, "patientInfoList.value");
}
});
}
/** 选择条数 */
function handleSelectionChange(selection) {
console.log(selection, "selection");
selectedData.value = selection.map((item) => ({ ...item })); // 存储选择的行数据
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 取消按钮 */
function cancel() {
visible.value = false;
patientInfoList.value = [];
}
/** 提交按钮 */
function submitForm() {
if (selectedData.value.length > 1) {
proxy.$modal.msgSuccess("只能选中一条数据操作!");
} else if (selectedData.value.length === 1) {
console.log(selectedData.value[0], "selectedData.value");
const data = selectedData.value[0];
// 将表单数据发送给父组件
emits("submit", data);
}
visible.value = false;
}
// 双击数据时触发的方法
function handleCellDblClick(row, column, cell, event) {
emits("submit", row);
visible.value = false;
// 获取整条数据
console.log('双击的行数据:', row);
// 根据需求进行进一步操作
}
defineExpose({
show,
});
</script>
<style scoped>
.el-form--inline .el-form-item {
display: inline-flex;
vertical-align: middle;
margin-right: 10px !important;
}
</style>

View File

@@ -0,0 +1,64 @@
<template>
<div>
<el-table
height="400"
:data="patientList"
row-key="id"
@cell-click="clickRow"
>
<el-table-column label="姓名" align="center" prop="name" />
<el-table-column label="就诊卡号" align="center" prop="identifierNo" />
<el-table-column label="性别" align="center" prop="genderEnum_enumText" />
<el-table-column label="证件号" align="center" prop="idCard" />
<el-table-column label="联系电话" align="center" prop="phone" />
<el-table-column label="年龄" align="center">
<template #default="scope">
{{ scope.row.age ? `${scope.row.age}` : '-' }}
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import {getOutpatientRegistrationList} from "./outpatientregistration";
const props = defineProps({
searchkey: {
type: String,
default: "",
},
});
const emit = defineEmits(["selsectPatient"]);
const total = ref(0);
const queryParams = ref({
pageNum: 1,
pageSize: 50,
});
const patientList = ref([]);
watch(
() => props.searchkey,
(newValue) => {
queryParams.value.searchKey = newValue;
getList();
},
{ immdiate: true }
);
getList();
function getList() {
getOutpatientRegistrationList(queryParams.value).then((res) => {
patientList.value = res.data.records;
total.value = res.data.total;
});
}
function clickRow(row) {
emit("selsectPatient", row);
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,525 @@
<template>
<el-dialog
:title="eventType == '1' ? '确认退费' : '挂号详情'"
v-model="props.open"
width="700px"
append-to-body
destroy-on-close
@close="close"
>
<div v-loading="loading">
<el-text size="large" style="display: block; margin-bottom: 15px">
退费日期{{ currentDate }}
</el-text>
<el-text size="large">费用性质{{ props.category || '自费' }}</el-text>
<div class="amount-row">
<el-text size="large">应退金额</el-text>
<el-text size="large" type="primary" class="amount">
{{ (calculatedTotalAmount || 0) + ' 元' }}
</el-text>
</div>
<div class="amount-row" v-if="calculatedReturnAmount > 0">
<el-text size="large">退费金额</el-text>
<el-text size="large" type="primary" class="amount">
{{ (calculatedReturnAmount || 0) + ' 元' }}
</el-text>
</div>
<!-- 退费方式列表从接口数据中筛选 -->
<div class="payment-container">
<div v-for="(item, index) in refundMethodsFromApi" :key="index" class="payment-item">
<span>退费方式</span>
<el-input :value="getPayMethodLabel(item.payEnum)" readonly style="width: 160px" />
<span>退费金额</span>
<div class="suffix-wrapper">
<el-input-number
:model-value="(Number(item.amount) || 0) - (Number(item.returnAmount) || 0)"
:precision="2"
:controls="false"
disabled
class="amount-input"
/>
<span class="suffix-text"></span>
</div>
</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"
:disabled="eventType == '1' ? false : true"
/>
</div>
<!-- 添加退费方式功能暂时注释 -->
<!-- <div class="add-payment">
<el-button type="primary" plain @click="addPayment" :disabled="canAddPayment">
添加退费方式
</el-button>
<el-text v-if="canAddPayment" type="danger" class="tip">
退费金额已等于应退金额不可继续添加
</el-text>
</div> -->
</div>
<!-- 金额汇总 -->
<div class="summary">
<el-space :size="30">
<div class="summary-item">
<el-text type="info">实退合计</el-text>
<el-text type="success">{{
displayAmount > 0 ? displayAmount + ' 元' : props.totalAmount + ' 元'
}}</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 v-if="preCancelData && preCancelData.length > 0" class="pre-cancel-table-wrapper">
<el-table :data="preCancelData" border stripe max-height="300" style="width: 100%">
<el-table-column prop="payEnum_dictText" label="支付方式" min-width="200" />
<el-table-column prop="amount" label="金额" width="120" align="right">
<template #default="{ row }">
{{ row.amount ? Number(row.amount) : '0.00' }}
</template>
</el-table-column>
<el-table-column prop="returnAmount" label="退费金额" width="120" align="right">
<template #default="{ row }">
{{ row.returnAmount ? Number(row.returnAmount) : '0.00' }}
</template>
</el-table-column>
<el-table-column prop="chargeAmount" label="收费金额" min-width="120" align="right">
<template #default="{ row }">
{{ row.chargeAmount ? Number(row.chargeAmount) : '0.00' }}
</template>
</el-table-column>
</el-table>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button v-if="eventType == 1" type="primary" @click="submit"> </el-button>
<el-button @click="close"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import {cancelRegister, preCancelReg} from './outpatientregistration';
import {computed, getCurrentInstance, reactive, ref, watch} from 'vue';
import useUserStore from '@/store/modules/user.js'
//获取当前登陆用户的信息
const userStore = useUserStore();
// 获取费用性质文本
const getFeeTypeText = computed(() => {
// 优先使用从挂号记录传递过来的费用性质名称
if (props.contractName) {
return props.contractName;
}
// 如果没有传递名称,则根据费用性质代码查找
if (props.feeType && props.medfee_paymtd_code && Array.isArray(props.medfee_paymtd_code)) {
const dictItem = props.medfee_paymtd_code.find(item => item.value === props.feeType);
return dictItem ? dictItem.label : '自费';
}
return '自费'; // 默认值
});
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: [],
},
eventType: {
type: String,
default: 1,
},
});
const { proxy } = getCurrentInstance();
const reason = ref('');
const preCancelData = ref([]);
const loading = ref(false);
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']);
watch(
() => props.open,
async (val) => {
if (val && props.patientInfo && props.patientInfo.encounterId) {
loading.value = true;
try {
const res = await preCancelReg(props.patientInfo.encounterId);
if (res && res.code === 200 && res.data) {
preCancelData.value = res.data;
// 计算应退金额并更新表单数据
const targetPayEnums = [220500, 220400, 220100, 220200, 220300];
const sum = res.data
?.filter((item) => targetPayEnums.includes(item.payEnum))
.reduce((total, item) => total + (Number(item.amount) || 0), 0);
if (sum > 0) {
formData.totalAmount = sum;
// 如果第一个退费方式的金额为0或等于原来的totalAmount则自动填充
if (
formData.selfPay[0].amount === 0 ||
formData.selfPay[0].amount === props.totalAmount
) {
formData.selfPay[0].amount = sum;
}
}
}
} catch (err) {
console.error('pre-cancel-reg error', err);
preCancelData.value = [];
} finally {
loading.value = false;
}
} else {
preCancelData.value = [];
loading.value = false;
}
}
);
function submit() {
console.log(props.chargeItemIds);
// if (parseFloat(displayAmount.value) < formData.totalAmount) {
// proxy.$modal.msgError('请输入正确的金额');
// return;
// }
cancelRegister({
paymentEnum: 0,
kindEnum: 1,
patientId: props.patientInfo.patientId,
id: props.paymentId,
encounterId: props.patientInfo.encounterId,
chargeItemIds: [],
paymentDetails: formData.selfPay,
reason: reason.value,
ybFlag: '1',
eleFlag: '0',
// 退号操作记录信息
returnDate: new Date().toISOString(), // 退号操作日期
operatorId: userStore.id, // 退号操作工号用户ID
operatorName: userStore.name, // 退号操作人姓名
refundAmount: calculatedTotalAmount.value, // 退款金额
// 原挂号信息
registerTime: props.registerInfo?.registerTime || '', // 原挂号时间
registerAmount: props.registerInfo?.totalPrice || props.totalAmount, // 原挂号金额
// 患者信息
patientName: props.registerInfo?.patientName || '', // 患者姓名
patientAge: props.registerInfo?.age || '', // 患者年龄
patientGender: props.registerInfo?.genderEnum_enumText || '', // 患者性别
// 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 getMax = (index) => {
const otherSum = formData.selfPay.reduce(
(sum, item, i) => (i !== index ? sum + (Number(item.amount) || 0) : sum),
0
);
const maxAmount = calculatedTotalAmount.value - otherSum;
if (formData.selfPay[index].payEnum == 220400) {
return maxAmount + 100;
}
return maxAmount;
};
// 检查支付方式是否已使用
const isMethodDisabled = (payEnum) => {
return formData.selfPay.some((item) => item.payEnum === payEnum);
};
const handleAmountChange = () => {
// 不需要在这里直接设置 returnedAmount依赖 computed 属性
};
const addPayment = () => {
if (canAddPayment.value) {
return;
}
// 添加新的退费方式初始金额为0由用户输入
formData.selfPay.push({ payEnum: '', amount: 0 });
};
const removePayment = (index) => {
formData.selfPay.splice(index, 1);
};
const clearAmount = (index) => {
// formData.selfPay[index].amount = 0;
};
// 计算属性 - 实退合计(使用接口返回的退费方式数据)
const displayAmount = computed(() => {
if (!preCancelData.value || preCancelData.value.length === 0) {
return '0.00';
}
const targetPayEnums = [220500, 220400, 220100, 220200, 220300];
const sum = preCancelData.value
?.filter((item) => targetPayEnums.includes(item.payEnum))
.reduce((sum, item) => sum + (Number(item.amount) || 0), 0);
return sum?.toFixed(2) ?? '0.00';
});
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';
});
const calculatedTotalAmount = computed(() => {
if (!preCancelData.value || preCancelData.value.length === 0) {
return props.totalAmount || 0;
}
const targetPayEnums = [220500, 220400, 220100, 220200, 220300];
// 应退金额 = (amount - returnAmount) 的总和,即剩余应退金额
const sum = preCancelData.value
?.filter((item) => targetPayEnums.includes(item.payEnum))
.reduce(
(total, item) => total + ((Number(item.amount) || 0) - (Number(item.returnAmount) || 0)),
0
);
return sum > 0 ? sum : props.totalAmount || 0;
});
const calculatedReturnAmount = computed(() => {
if (!preCancelData.value || preCancelData.value.length === 0) {
return 0;
}
const targetPayEnums = [220500, 220400, 220100, 220200, 220300];
const sum = preCancelData.value
?.filter((item) => targetPayEnums.includes(item.payEnum))
.reduce((total, item) => total + (Number(item.returnAmount) || 0), 0);
return sum || 0;
});
// 从接口数据中筛选符合条件的退费方式
const refundMethodsFromApi = computed(() => {
if (!preCancelData.value || preCancelData.value.length === 0) {
return [];
}
const targetPayEnums = [220500, 220400, 220100, 220200, 220300];
return preCancelData.value?.filter(
(item) =>
targetPayEnums.includes(item.payEnum) &&
item.payEnum_dictText &&
item.payEnum_dictText.trim() !== ''
) || [];
});
// 根据 payEnum 获取支付方式标签
const getPayMethodLabel = (payEnum) => {
const method = selfPayMethods.find((m) => m.value === payEnum);
if (method) {
return method.label;
}
// 如果找不到,尝试从接口返回的 dictText 中获取
const apiItem = preCancelData.value?.find((item) => item.payEnum === payEnum);
return apiItem?.payEnum_dictText || `支付方式${payEnum}`;
};
// 计算所有退费方式的总和
const totalRefundAmount = computed(() => {
return refundMethodsFromApi.value?.reduce((sum, item) => sum + (Number(item.amount) || 0), 0) ?? 0;
});
// 计算剩余可输入金额(应退金额 - 已输入的退费金额总和)
const remainingAmount = computed(() => {
return calculatedTotalAmount.value - totalRefundAmount.value;
});
// 判断是否可以添加退费方式:退费金额总和等于或大于应退金额时不能添加
const canAddPayment = computed(() => {
return totalRefundAmount.value >= calculatedTotalAmount.value || formData.selfPay.length >= 10;
});
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;
}
.add-payment {
margin-top: 10px;
display: flex;
align-items: center;
gap: 10px;
}
.reason-textarea {
margin-left: 80px;
width: 59%;
}
.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;
}
.pre-cancel-table-wrapper {
margin-top: 20px;
width: 100%;
border: 1px solid #dcdfe6;
border-radius: 4px;
overflow: hidden;
:deep(.el-table) {
width: 100%;
.el-table__body-wrapper {
max-height: 300px;
overflow-y: auto;
}
}
}
</style>

View File

@@ -0,0 +1,671 @@
<template>
<el-dialog
title="补打挂号单凭证"
v-model="dialogVisible"
width="900px"
append-to-body
destroy-on-close
:close-on-click-modal="false"
>
<div v-loading="loading" style="background-color: #ffffff; padding: 20px; min-height: 400px;">
<!-- 标题区域 -->
<div style="text-align: center; margin-bottom: 20px;">
<h2 style="color: #ff0000; font-size: 24px; font-weight: bold; margin: 0 0 10px 0;">挂号单重打</h2>
<p style="color: #ff0000; font-size: 14px; margin: 0;">补打挂号单将作废原有的挂号单据并生成新的挂号单据</p>
</div>
<!-- 表单区域 -->
<el-form :model="form" ref="reprintFormRef" label-width="120px" style="background-color: #ffffff; padding: 20px; border-radius: 4px;">
<el-row :gutter="30">
<!-- 左列 -->
<el-col :span="12">
<el-form-item label="病人姓名:" prop="name">
<el-input v-model="form.name" placeholder="病人姓名" :disabled="true" />
</el-form-item>
<el-form-item label="医生姓名:" prop="practitionerName">
<el-input v-model="form.practitionerName" placeholder="医生姓名" :disabled="true" />
</el-form-item>
<el-form-item label="诊疗费:" prop="activityPrice">
<el-input v-model="form.activityPrice" placeholder="诊疗费" :disabled="true" />
</el-form-item>
<el-form-item label="流水号:" prop="serialNo">
<el-input v-model="form.serialNo" placeholder="流水号" :disabled="true" />
</el-form-item>
<el-form-item label="合计:" prop="totalPrice">
<el-input v-model="form.totalPrice" placeholder="合计" :disabled="true" />
</el-form-item>
</el-col>
<!-- 右列 -->
<el-col :span="12">
<el-form-item label="挂号科室:" prop="organizationName">
<el-input v-model="form.organizationName" placeholder="挂号科室" :disabled="true" />
</el-form-item>
<el-form-item label="挂号费:" prop="price">
<el-input v-model="form.price" placeholder="挂号费" :disabled="true" />
</el-form-item>
<el-form-item label="病历费:" prop="medicalRecordFee">
<el-input v-model="form.medicalRecordFee" placeholder="病历费" :disabled="true" />
</el-form-item>
<el-form-item label="打印时间:" prop="printTime">
<el-input v-model="form.printTime" placeholder="打印时间" :disabled="true" />
</el-form-item>
</el-col>
</el-row>
<!-- 跨两列的字段 -->
<el-row>
<el-col :span="24">
<el-form-item label="预约/挂号时间:" prop="visitTime">
<el-input v-model="form.visitTime" placeholder="预约/挂号时间" :disabled="true" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 底部搜索和操作区域 -->
<div style="margin-top: 20px; padding: 15px; background-color: #ffffff; border-radius: 4px;">
<el-form :inline="true" style="margin: 0;">
<el-form-item label="就诊卡号:" style="margin-bottom: 0;">
<el-input
v-model="searchForm.cardNo"
placeholder="请输入就诊卡号检索条"
style="width: 300px; margin-right: 10px;"
clearable
@keyup.enter="handleSearch"
/>
<el-button type="primary" @click="handleSearch" :loading="loading">确认(O)</el-button>
<el-button @click="handleCancel">取消(C)</el-button>
</el-form-item>
</el-form>
</div>
</div>
<!-- 挂号记录选择对话框 -->
<el-dialog
title="挂号记录选择"
v-model="selectDialogVisible"
width="800px"
append-to-body
:close-on-click-modal="false"
>
<el-table
:data="recordList"
highlight-current-row
@current-change="handleRecordSelect"
style="width: 100%"
max-height="400"
>
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="挂号时间" prop="registerTime" width="180" align="center">
<template #default="scope">
{{ scope.row.registerTime ? parseTime(scope.row.registerTime, '{y}.{m}.{d} {h}时{i}分') : '-' }}
</template>
</el-table-column>
<el-table-column label="挂号科室" prop="organizationName" align="center">
<template #default="scope">
{{ scope.row.organizationName ? scope.row.organizationName.trim() || '-' : '-' }}
</template>
</el-table-column>
<el-table-column label="医生姓名" prop="practitionerName" align="center">
<template #default="scope">
{{ scope.row.practitionerName ? scope.row.practitionerName.trim() || '-' : '-' }}
</template>
</el-table-column>
<el-table-column label="流水号" prop="encounterNo" align="center">
<template #default="scope">
{{ calculateSerialNo(scope.row) }}
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button @click="selectDialogVisible = false">取消 (C)</el-button>
<el-button type="primary" @click="handleConfirmSelect" :disabled="!selectedRecord">确认 (O)</el-button>
</div>
</template>
</el-dialog>
</el-dialog>
</template>
<script setup>
import {getCurrentInstance, reactive, ref, watch, nextTick, onMounted} from 'vue';
import {getOutpatientRegistrationCurrent, reprintRegistration} from './outpatientregistration';
import {parseTime} from '@/utils/his';
import {formatDateStr} from '@/utils/index';
import {hiprint} from 'vue-plugin-hiprint';
import outpatientRegistrationTemplate from '@/components/Print/OutpatientRegistration.json';
import useUserStore from '@/store/modules/user';
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
const props = defineProps({
open: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['close']);
const dialogVisible = ref(false);
const loading = ref(false);
const reprintFormRef = ref(null);
const selectDialogVisible = ref(false);
const recordList = ref([]);
const selectedRecord = ref(null);
const currentRecord = ref(null); // 保存当前选择的记录,用于打印
const searchForm = reactive({
cardNo: '',
});
const form = reactive({
cardNo: '',
name: '',
idCard: '',
phone: '',
organizationName: '',
practitionerName: '',
price: '',
activityPrice: '',
medicalRecordFee: '',
serialNo: '',
totalPrice: '',
visitTime: '',
visitLocation: '',
statusText: '',
printTime: '',
encounterId: '',
});
const rules = {
cardNo: [{ required: true, message: '就诊卡号不能为空', trigger: 'blur' }],
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
organizationName: [{ required: true, message: '就诊科室不能为空', trigger: 'blur' }],
practitionerName: [{ required: true, message: '医生不能为空', trigger: 'blur' }],
};
// 监听open属性变化
watch(
() => props.open,
(newVal) => {
dialogVisible.value = newVal;
if (newVal) {
// 打开对话框时重置表单
resetForm();
resetSearch();
}
}
);
// 监听dialogVisible变化同步到父组件
watch(dialogVisible, (newVal) => {
if (!newVal) {
emit('close');
}
});
// 搜索挂号记录并确认补打
async function handleSearch() {
if (!searchForm.cardNo) {
proxy.$modal.msgWarning('请输入就诊卡号');
return;
}
loading.value = true;
try {
// 使用就诊卡号作为searchKey查询
const endDate = new Date();
const startDate = new Date();
startDate.setFullYear(startDate.getFullYear() - 1);
const queryParams = {
pageNo: 1,
pageSize: 100,
registerTimeSTime: formatDateStr(startDate, 'YYYY-MM-DD') + ' 00:00:00',
registerTimeETime: formatDateStr(endDate, 'YYYY-MM-DD') + ' 23:59:59',
searchKey: searchForm.cardNo
};
// 调用API查询挂号记录
const response = await getOutpatientRegistrationCurrent(queryParams);
if (response.code === 200) {
let records = response.data.records || response.data || [];
// 过滤掉挂号科室或医生姓名为空的记录
records = records.filter(record => {
const orgName = record.organizationName ? record.organizationName.trim() : '';
const pracName = record.practitionerName ? record.practitionerName.trim() : '';
// 只保留挂号科室和医生姓名都不为空的记录
return orgName && pracName;
});
if (records.length > 0) {
if (records.length === 1) {
// 只有一条记录,直接使用
const row = records[0];
currentRecord.value = row; // 保存记录用于打印
fillForm(row);
await handleSubmit();
} else {
// 多条记录,显示选择对话框
recordList.value = records;
selectedRecord.value = records[0]; // 默认选择第一条
selectDialogVisible.value = true;
loading.value = false;
}
} else {
proxy.$modal.msgWarning('未查询到相关挂号记录,请检查就诊卡号是否正确');
resetForm();
loading.value = false;
}
} else {
proxy.$modal.msgWarning('查询失败: ' + (response.msg || '接口返回异常'));
resetForm();
loading.value = false;
}
} catch (error) {
console.error('查询挂号记录异常:', error);
proxy.$modal.msgError('查询出错: ' + (error.message || '网络异常'));
resetForm();
loading.value = false;
}
}
// 填充表单
function fillForm(row) {
console.log('fillForm row:', row); // 调试日志
// 就诊卡号 - 从 identifierNo 字段获取
form.cardNo = row.identifierNo || row.cardNo || '';
// 病人姓名 - 从 patientName 字段获取
form.name = row.patientName || row.name || '';
// 身份证号 - 从 idCard 字段获取,需要脱敏处理
let idCard = row.idCard || '';
if (idCard && idCard.length >= 10) {
// 只显示前6位和后4位中间用*代替
const prefix = idCard.substring(0, 6);
const suffix = idCard.substring(idCard.length - 4);
const stars = '*'.repeat(Math.max(0, idCard.length - 10));
form.idCard = prefix + stars + suffix;
} else if (idCard) {
// 如果身份证号长度不足10位只显示部分并用*代替
if (idCard.length >= 6) {
const prefix = idCard.substring(0, 3);
const suffix = idCard.substring(idCard.length - 1);
form.idCard = prefix + '*'.repeat(idCard.length - 4) + suffix;
} else {
form.idCard = idCard;
}
} else {
form.idCard = '';
}
// 联系电话 - 从 phone 字段获取,需要脱敏处理
let phone = row.phone || '';
if (phone && phone.length >= 7) {
// 只显示前3位和后4位中间用*代替
const prefix = phone.substring(0, 3);
const suffix = phone.substring(phone.length - 4);
form.phone = prefix + '****' + suffix;
} else if (phone) {
// 如果手机号长度不足7位部分脱敏
if (phone.length >= 4) {
const prefix = phone.substring(0, 2);
const suffix = phone.substring(phone.length - 2);
form.phone = prefix + '**' + suffix;
} else {
form.phone = phone;
}
} else {
form.phone = '';
}
// 挂号科室 - 从 organizationName 字段获取
form.organizationName = row.organizationName || '';
// 医生姓名 - 从 practitionerName 字段获取
form.practitionerName = row.practitionerName || '';
// 挂号费 - 如果没有单独的 price将 totalPrice 作为挂号费
// 实际系统中,可能需要查询更详细的费用明细
form.price = row.price ? parseFloat(row.price).toFixed(2) :
(row.totalPrice ? parseFloat(row.totalPrice).toFixed(2) : '0.00');
// 诊疗费 - API 未返回,暂时设为 0.00
form.activityPrice = row.activityPrice ? parseFloat(row.activityPrice).toFixed(2) : '0.00';
// 病历费 - API 未返回,暂时设为 0.00
form.medicalRecordFee = row.medicalRecordFee ? parseFloat(row.medicalRecordFee).toFixed(2) : '0.00';
// 流水号 - 使用 calculateSerialNo 函数计算
form.serialNo = calculateSerialNo(row);
// 合计 - 优先使用 totalPrice否则计算各项费用之和
if (row.totalPrice) {
form.totalPrice = parseFloat(row.totalPrice).toFixed(2);
} else {
const total = parseFloat(form.price || 0) + parseFloat(form.activityPrice || 0) + parseFloat(form.medicalRecordFee || 0);
form.totalPrice = total.toFixed(2);
}
// 预约/挂号时间 - 从 registerTime 字段获取
if (row.registerTime) {
form.visitTime = parseTime(row.registerTime, '{y}-{m}-{d} {h}:{i}:{s}');
} else {
form.visitTime = formatDateTime(new Date());
}
// 就诊地点 - 使用科室名称作为就诊地点
form.visitLocation = row.visitLocation || row.locationName || row.organizationName || '';
// 预约状态 - 从 statusEnum_enumText 字段获取,如果没有则根据 statusEnum 判断
if (row.statusEnum_enumText) {
form.statusText = row.statusEnum_enumText;
} else if (row.statusEnum !== undefined) {
// 根据 statusEnum 判断状态
// 状态枚举可能需要根据实际情况调整
const statusMap = {
1: '已预约',
2: '已完成',
3: '已取消',
4: '进行中'
};
form.statusText = statusMap[row.statusEnum] || '已预约';
} else {
form.statusText = '已预约';
}
// 打印时间(当前时间)
form.printTime = formatDateTime(new Date());
// 保存encounterId用于提交 - 注意字段名是 encounterId
form.encounterId = row.encounterId || row.id || '';
console.log('fillForm form:', form); // 调试日志
}
// 格式化日期时间
function formatDateTime(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// 计算流水号直接使用挂号记录表的主键IDencounterId
function calculateSerialNo(row) {
if (!row) {
return '-';
}
// 直接使用主键ID作为流水号
if (row.encounterId != null && row.encounterId !== undefined) {
return String(row.encounterId);
} else if (row.id != null && row.id !== undefined) {
// 兼容其他可能的ID字段名
return String(row.id);
} else {
return '-';
}
}
// 提交补打挂号
async function handleSubmit() {
if (!form.encounterId) {
proxy.$modal.msgWarning('请先搜索并选择挂号记录');
loading.value = false;
return;
}
try {
// 如果 reprintFormRef 不存在,跳过验证
let valid = true;
if (reprintFormRef.value) {
valid = await reprintFormRef.value.validate().catch(() => false);
}
if (!valid) {
loading.value = false;
return;
}
// 在提交前,验证表单数据是否已正确填充
console.log('handleSubmit form before submit:', form); // 调试日志
const submitData = {
encounterId: form.encounterId,
cardNo: form.cardNo,
name: form.name,
organizationName: form.organizationName,
practitionerName: form.practitionerName,
price: parseFloat(form.price) || 0,
activityPrice: parseFloat(form.activityPrice) || 0,
medicalRecordFee: parseFloat(form.medicalRecordFee || 0),
totalPrice: parseFloat(form.totalPrice) || 0,
visitTime: form.visitTime,
};
console.log('handleSubmit submitData:', submitData); // 调试日志
const res = await reprintRegistration(submitData);
if (res.code === 200) {
proxy.$modal.msgSuccess('补打挂号成功');
// 不关闭补打对话框,直接显示打印预览
// 这样可以确保 form 数据不会丢失
loading.value = false;
showPrintPreview();
} else {
proxy.$modal.msgError(res.msg || '补打挂号失败');
loading.value = false;
}
} catch (error) {
console.error('补打挂号失败:', error);
proxy.$modal.msgError('补打挂号失败: ' + (error.message || '未知错误'));
loading.value = false;
}
}
// 取消(实际执行提交操作,因为确认按钮触发搜索)
function handleCancel() {
dialogVisible.value = false;
}
// 重置表单
function resetForm() {
Object.assign(form, {
cardNo: '',
name: '',
idCard: '',
phone: '',
organizationName: '',
practitionerName: '',
price: '',
activityPrice: '',
medicalRecordFee: '',
serialNo: '',
totalPrice: '',
visitTime: '',
visitLocation: '',
statusText: '',
printTime: '',
encounterId: '',
});
if (reprintFormRef.value) {
reprintFormRef.value.clearValidate();
}
}
// 重置搜索
function resetSearch() {
searchForm.cardNo = '';
}
// 显示打印预览(使用 hiprint 打印)
function showPrintPreview() {
loading.value = false;
console.log('showPrintPreview form:', form);
console.log('showPrintPreview currentRecord:', currentRecord.value);
if (!form.name && currentRecord.value) {
console.log('重新填充表单数据...');
fillForm(currentRecord.value);
}
// 使用 hiprint 打印
printByHiprint();
}
/**
* 使用 hiprint 打印门诊挂号单(补打)
*/
function printByHiprint() {
try {
// 构建打印数据
const printData = {
patientName: form.name || '-',
sex: form.gender || '-',
age: form.age || '-',
personType: '自费',
busNo: form.cardNo || '-',
organizationName: form.organizationName || '-',
practitionerName: form.practitionerName || '-',
healthcareName: '挂号',
chargeTime: form.printTime || formatDateStr(new Date(), 'YYYY-MM-DD HH:mm:ss'),
cashier: userStore.name || '',
chargeItem: [
{
chargeItemName: '挂号费',
quantityValue: '1',
totalPrice: parseFloat(form.price || 0).toFixed(2),
dirClass: 1
},
...(parseFloat(form.activityPrice || 0) > 0 ? [{
chargeItemName: '诊疗费',
quantityValue: '1',
totalPrice: parseFloat(form.activityPrice || 0).toFixed(2),
dirClass: 1
}] : []),
...(parseFloat(form.medicalRecordFee || 0) > 0 ? [{
chargeItemName: '病历费',
quantityValue: '1',
totalPrice: parseFloat(form.medicalRecordFee || 0).toFixed(2),
dirClass: 1
}] : [])
],
displayAmount123: '¥' + parseFloat(form.totalPrice || 0).toFixed(2),
FULAMT_OWNPAY_AMT: '¥' + parseFloat(form.totalPrice || 0).toFixed(2),
SELF_CASH_VALUE: '¥' + parseFloat(form.totalPrice || 0).toFixed(2),
pictureUrl: ''
};
const result = {
data: [printData]
};
console.log('补打挂号打印数据:', printData);
// 处理模板(替换医院名称)
const printElements = JSON.parse(
JSON.stringify(outpatientRegistrationTemplate).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
// 创建 hiprint 模板并打印
const hiprintTemplate = new hiprint.PrintTemplate({ template: printElements });
// 打印成功回调
hiprintTemplate.on('printSuccess', function (e) {
console.log('补打挂号单打印成功', e);
proxy.$modal.msgSuccess('打印成功');
dialogVisible.value = false;
});
// 打印失败回调
hiprintTemplate.on('printError', function (e) {
console.error('补打挂号单打印失败', e);
proxy.$modal.msgError('打印失败:' + (e.message || '未知错误'));
});
// 执行打印
hiprintTemplate.print2(result.data[0], {
title: '门诊挂号单(补打)',
});
} catch (error) {
console.error('hiprint 打印挂号单失败:', error);
proxy.$modal.msgError('打印失败:' + (error.message || '未知错误'));
}
}
// 选择挂号记录
function handleRecordSelect(row) {
selectedRecord.value = row;
}
// 确认选择记录
async function handleConfirmSelect() {
if (!selectedRecord.value) {
proxy.$modal.msgWarning('请选择一条挂号记录');
return;
}
loading.value = true;
selectDialogVisible.value = false;
try {
// 保存当前记录,用于打印
currentRecord.value = selectedRecord.value;
// 填充表单 - 确保数据正确填充
fillForm(selectedRecord.value);
// 等待一小段时间,确保 form 数据已更新
await new Promise(resolve => setTimeout(resolve, 50));
// 验证表单数据是否已正确填充
if (!form.encounterId) {
proxy.$modal.msgError('获取挂号记录ID失败请重试');
loading.value = false;
return;
}
// 提交补打操作handleSubmit 内部会管理 loading 状态)
await handleSubmit();
} catch (error) {
console.error('确认选择记录失败:', error);
proxy.$modal.msgError('处理失败:' + (error.message || '未知错误'));
loading.value = false;
}
}
</script>
<style scoped>
/* 确保对话框内容区域的背景色 */
:deep(.el-dialog__body) {
padding: 0;
background-color: #ffffff;
}
/* 输入框样式 */
:deep(.el-input__inner) {
background-color: #ffffff;
}
/* 标签样式 */
:deep(.el-form-item__label) {
font-weight: normal;
color: #333;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
import request from '@/utils/request'
/**
* 执行患者换卡操作
* @param {Object} params - 换卡参数
* @param {string} params.oldCardNo - 旧门诊号码
* @param {string} params.newCardNo - 新门诊号码
* @param {string} params.patientId - 患者ID
* @param {string} params.reason - 换卡原因
* @param {string} params.remark - 备注信息
* @returns {Promise} 请求结果
*/
export const doCardRenewal = (params) => {
return request({
url: '/charge/patientCardRenewal/renewCard',
method: 'post',
data: params
})
}
/**
* 查询患者信息 - 复用门诊挂号的API
* @param {Object} params - 查询参数
* @param {string} params.searchKey - 搜索关键字(姓名、身份证号、手机号等)
* @param {number} params.pageNo - 当前页码
* @param {number} params.pageSize - 每页大小
* @returns {Promise} 请求结果
*/
export const getPatientList = (params) => {
// 构建searchKey参数将姓名、身份证号、手机号组合成一个搜索关键字
const searchKey = params.patientName || params.idCard || params.phoneNumber || '';
return request({
url: '/charge-manage/register/patient-metadata',
method: 'get',
params: {
searchKey: searchKey,
pageNo: params.pageNo || 1,
pageSize: params.pageSize || 10
}
})
}

View File

@@ -0,0 +1,757 @@
<template>
<div class="card-renewal-container">
<!-- 标题栏 -->
<div class="title-bar">
<h2>换卡处理</h2>
</div>
<!-- 按钮栏 - 修改为水平排放 -->
<div class="button-bar">
<el-button
type="primary"
size="large"
@click="handlePatientSearch"
:loading="loading"
icon="el-icon-search"
style="min-width: 140px;"
>
病人查询(Q)
</el-button>
<el-button
type="success"
size="large"
@click="handleConfirm"
:loading="loading"
:disabled="!patientInfo"
style="min-width: 140px;"
>
确定 (O)
</el-button>
<el-button
type="danger"
size="large"
@click="handleClose"
:disabled="loading"
style="min-width: 140px;"
>
关闭 (C)
</el-button>
</div>
<!-- 查询条件 -->
<div class="search-section">
<el-row :gutter="20">
<el-col :span="8">
<div class="input-item">
<label>病人姓名:</label>
<el-input
v-model="searchForm.patientName"
placeholder="请输入"
@keyup.enter="handlePatientSearch"
clearable
/>
</div>
</el-col>
<el-col :span="8">
<div class="input-item">
<label>身份证号码:</label>
<el-input
v-model="searchForm.idCard"
placeholder="请输入"
@keyup.enter="handlePatientSearch"
clearable
/>
</div>
</el-col>
<el-col :span="8">
<div class="input-item">
<label>手机号码:</label>
<el-input
v-model="searchForm.phoneNumber"
placeholder="请输入"
@keyup.enter="handlePatientSearch"
clearable
/>
</div>
</el-col>
</el-row>
</div>
<!-- 患者信息显示 -->
<div class="patient-info-section">
<el-row :gutter="20">
<el-col :span="8">
<div class="info-item">
<label>门诊号码</label>
<div class="info-value">{{ patientInfo?.outpatientNo || '' }}</div>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<label>病人姓名</label>
<div class="info-value">{{ patientInfo?.patientName || '' }}</div>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<label>身份证号码</label>
<div class="info-value">{{ patientInfo?.idCard || '' }}</div>
</div>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 15px;">
<el-col :span="8">
<div class="info-item">
<label>手机号码</label>
<div class="info-value">{{ patientInfo?.phoneNumber || '' }}</div>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<label>性别</label>
<div class="info-value">{{ patientInfo?.gender || '' }}</div>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<label>年龄</label>
<div class="info-value">{{ patientInfo?.age || '' }}</div>
</div>
</el-col>
</el-row>
</div>
<!-- 新门诊号码输入 -->
<div class="new-number-section">
<div class="input-item">
<label>新门诊号码:</label>
<el-input
v-model="renewalForm.newOutpatientNo"
placeholder="请输入新门诊号码"
@keyup.enter="handleConfirm"
style="width: 250px;"
/>
</div>
</div>
<!-- 按钮区域已移除 -->
<!-- 患者列表对话框 -->
<el-dialog
v-model="showPatientList"
:title="null"
width="800px"
:close-on-click-modal="false"
class="custom-patient-dialog"
>
<!-- 自定义标题栏和按钮区域 -->
<template #header>
<div style="width: 100%; background-color: #e6f4ff;">
<!-- 标题行 -->
<div style="display: flex; justify-content: flex-start; align-items: center; padding: 10px 20px;">
<h3 style="margin: 0; font-size: 16px; font-weight: 500; color: #303133;">病人档案查询</h3>
</div>
<!-- 按钮行 -->
<div style="display: flex; justify-content: flex-start; gap: 10px; padding: 10px 20px; background-color: #e6f4ff;">
<el-button type="primary" @click="confirmSelectPatient" style="background-color: #409eff; border-color: #409eff; padding: 8px 16px; font-size: 14px;">确认(Q)</el-button>
<el-button @click="showPatientList = false; selectedPatient = null" style="background-color: #f56c6c; border-color: #f56c6c; color: white; padding: 8px 16px; font-size: 14px;">关闭(C)</el-button>
</div>
</div>
</template>
<el-table
:data="patientList"
style="width: 100%"
@row-click="selectPatient"
:row-key="row => row.identifierNo || row.patientId || row.cardNo"
:current-row-key="selectedPatient?.identifierNo || selectedPatient?.patientId || selectedPatient?.cardNo"
highlight-current-row
>
<el-table-column label="序号" width="60" type="index">
</el-table-column>
<el-table-column label="病人姓名" width="120">
<template #default="scope">
{{ scope.row.patientName || scope.row.name || '-' }}
</template>
</el-table-column>
<el-table-column label="门诊号码" width="120">
<template #default="scope">
{{ scope.row.identifierNo || scope.row.cardNo || scope.row.card_number || scope.row.就诊卡号 || scope.row.outpatientNumber || scope.row.outpatientNo || scope.row.门诊号码 || scope.row.卡号 || scope.row.card || scope.row.patientNo || scope.row.patient_id || '-' }}
</template>
</el-table-column>
<el-table-column label="身份证号码" width="200">
<template #default="scope">
{{ scope.row.idCard || scope.row.id_card || scope.row.idNo || '-' }}
</template>
</el-table-column>
<el-table-column label="手机号码" width="120">
<template #default="scope">
{{ scope.row.phoneNumber || scope.row.phone || scope.row.mobile || scope.row.mobilePhone || '-' }}
</template>
</el-table-column>
<el-table-column label="性别" width="80">
<template #default="scope">
{{ scope.row.genderEnum_enumText || scope.row.gender || scope.row.sex || scope.row.性别 || scope.row.xb || scope.row.sexCode || scope.row.GENDER || scope.row.SEX || '-' }}
</template>
</el-table-column>
<el-table-column label="年龄" width="80">
<template #default="scope">
{{ scope.row.age || '-' }}
</template>
</el-table-column>
<el-table-column label="出生年月" width="120">
<template #default="scope">
{{ formatDate(scope.row.birthDate || scope.row.birthday || scope.row.出生日期) || '-' }}
</template>
</el-table-column>
</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="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<!-- 移除底部按钮 -->
<template #footer>
<span></span>
</template>
</el-dialog>
<!-- 换卡成功提示 -->
<el-dialog
v-model="renewalSuccessVisible"
title="换卡成功"
width="400px"
:close-on-click-modal="false"
>
<div style="text-align: center; padding: 20px;">
<div class="success-icon">✓</div>
<p style="margin-top: 15px; font-size: 16px;">患者换卡操作已成功完成!</p>
<p style="margin-top: 10px;">新门诊号码:{{ renewalForm.newOutpatientNo }}</p>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="renewalSuccessVisible = false; resetForm">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {getCurrentInstance, onMounted, onUnmounted, reactive, ref} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import {useRouter} from 'vue-router'
import {doCardRenewal, getPatientList} from './components/api.js';
// 获取路由实例
const router = useRouter();
// 获取当前组件实例以访问全局属性
const { proxy } = getCurrentInstance();
// 搜索表单
const searchForm = reactive({
patientName: '',
idCard: '',
phoneNumber: ''
})
// 患者信息
const patientInfo = ref(null)
// 患者列表
const patientList = ref([])
const showPatientList = ref(false)
const selectedPatient = ref(null)
// 分页相关状态
const currentPage = ref(1)
const pageSize = ref(5)
const total = ref(0)
// 换卡表单
const renewalForm = reactive({
newOutpatientNo: ''
})
// 加载状态
const loading = ref(false)
// 成功弹窗显示状态
const renewalSuccessVisible = ref(false)
// 分页大小变化处理
const handleSizeChange = (newSize) => {
pageSize.value = newSize
currentPage.value = 1 // 重置为第一页
selectedPatient.value = null // 切换分页时重置选中状态
if (showPatientList.value) {
handlePatientSearch() // 重新查询
}
}
// 当前页码变化处理
const handleCurrentChange = (newPage) => {
currentPage.value = newPage
selectedPatient.value = null // 切换分页时重置选中状态
if (showPatientList.value) {
handlePatientSearch() // 重新查询
}
}
// 查询患者信息
const handlePatientSearch = async () => {
// 检查是否至少填写了一个查询条件
if (!searchForm.patientName && !searchForm.idCard && !searchForm.phoneNumber) {
ElMessage.warning('请至少输入一个查询条件')
return
}
loading.value = true
try {
// 构建查询参数,包含分页信息
const queryParams = {
patientName: searchForm.patientName || '',
idCard: searchForm.idCard || '',
phoneNumber: searchForm.phoneNumber || '',
pageNo: currentPage.value,
pageSize: pageSize.value
}
// 调用复用的门诊挂号API获取患者列表
const response = await getPatientList(queryParams)
if (response && response.code === 200) {
// 检查是否有查询结果
if (response.data && response.data.records && response.data.records.length > 0) {
// 更新总条数
total.value = response.data.total || 0
selectedPatient.value = null // 查询时重置选中状态
// 如果只有一条记录且是第一页,自动选中并展示到页面
if (response.data.records.length === 1 && currentPage.value === 1) {
const patient = response.data.records[0]
// 确保patient对象中同时包含id和patientId字段
if (patient.id && !patient.patientId) {
patient.patientId = patient.id;
}
// 设置为选中状态并自动确认,将患者信息展示到页面
selectedPatient.value = patient
confirmSelectPatient() // 自动调用确认选择方法,将患者信息展示到页面
ElMessage.success('已自动选中唯一患者')
} else {
// 如果有多条记录或不是第一页,显示患者列表供选择
// 确保每条患者记录都包含patientId字段优先使用id字段
patientList.value = response.data.records.map(patient => ({
...patient,
patientId: patient.patientId || patient.id
}))
showPatientList.value = true
}
} else {
// 无数据时重置总条数
total.value = 0
ElMessage.warning('未查询到患者信息')
}
} else {
ElMessage.error('查询失败:' + (response?.msg || '未知错误'))
}
} catch (error) {
// 使用公共错误处理函数处理错误信息
const errorMessage = processErrorMessage(error, '查询失败,请稍后重试');
ElMessage.error(errorMessage);
// 可以在这里添加错误监控或日志记录
console.error('患者查询错误:', error);
} finally {
loading.value = false
}
}
// 确定换卡
const handleConfirm = async () => {
if (!patientInfo.value) {
ElMessage.warning('请先查询患者信息')
return
}
if (!renewalForm.newOutpatientNo) {
ElMessage.warning('请输入新门诊号码')
return
}
if (renewalForm.newOutpatientNo === patientInfo.value.outpatientNo) {
ElMessage.warning('新门诊号码不能与原号码相同')
return
}
loading.value = true
try {
// 构建换卡请求参数注意参数名称与API接口保持一致
const params = {
oldCardNo: patientInfo.value.outpatientNo,
newCardNo: renewalForm.newOutpatientNo,
patientId: patientInfo.value.patientId,
reason: '患者换卡',
remark: `${patientInfo.value.outpatientNo}更换到${renewalForm.newOutpatientNo}`
}
// 调用真实的换卡接口
const res = await doCardRenewal(params)
if (res && res.code === 200) {
renewalSuccessVisible.value = true
// 更新患者信息中的门诊号码为新号码
if (patientInfo.value) {
patientInfo.value.outpatientNo = renewalForm.newOutpatientNo
}
ElMessage.success('换卡成功!')
} else {
ElMessage.error('换卡失败:' + (res?.msg || '未知错误'))
}
} catch (error) {
ElMessage.error('换卡失败,卡号已存在')
// 可以在这里添加错误监控或日志记录
} finally {
loading.value = false
}
}
// 关闭窗口,同时关闭标签页并返回上一级页面
const handleClose = () => {
// 显示确认对话框
ElMessageBox.confirm('确定要关闭此页面吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 用户确认后,使用全局$tab对象关闭当前标签页
if (proxy && proxy.$tab) {
proxy.$tab.closePage()
} else {
// 降级处理:如果$tab不可用仍然使用router.back()
router.back()
}
}).catch(() => {
// 用户取消操作,不执行任何操作
// 可以选择显示一个提示消息
ElMessage.info('已取消关闭操作')
})
}
// 键盘事件处理
const handleKeydown = (event) => {
// 只有在患者列表对话框显示时才处理对话框快捷键
if (showPatientList.value) {
// 忽略在输入框中按下的按键
if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA') {
return
}
const key = event.key.toLowerCase()
if (key === 'q') {
event.preventDefault()
confirmSelectPatient()
} else if (key === 'c') {
event.preventDefault()
showPatientList.value = false
selectedPatient.value = null
}
return
}
// Alt + Q 查询
if (event.altKey && event.key.toLowerCase() === 'q') {
event.preventDefault()
handlePatientSearch()
}
// Alt + O 确定
if (event.altKey && event.key.toLowerCase() === 'o') {
event.preventDefault()
handleConfirm()
}
// Alt + C 关闭
if (event.altKey && event.key.toLowerCase() === 'c') {
event.preventDefault()
handleClose()
}
}
// 组件挂载时添加键盘事件监听
onMounted(() => {
document.addEventListener('keydown', handleKeydown)
// 初始化患者信息为空
})
// 组件卸载时移除键盘事件监听
onUnmounted(() => {
document.removeEventListener('keydown', handleKeydown)
})
// 移除了调试功能
// 选择患者(仅设置选中状态)
const selectPatient = (row) => {
selectedPatient.value = row
ElMessage.warning('已选择患者,请点击确定')
}
// 确认选择患者
const confirmSelectPatient = () => {
if (!selectedPatient.value) {
ElMessage.warning('请先选择患者')
return
}
const row = selectedPatient.value
// 获取门诊号码优先使用identifierNo
const outpatientNo = row.identifierNo || row.cardNo || row.card_number || row.就诊卡号 || row.outpatientNumber || row.outpatientNo || row.门诊号码 || row.卡号 || row.card || row.patientNo || row.patient_id;
// 获取性别优先使用genderEnum_enumText
const gender = row.genderEnum_enumText || row.gender || row.sex || row.性别 || row.xb || row.sexCode || row.GENDER || row.SEX;
patientInfo.value = {
outpatientNo: outpatientNo,
patientName: row.patientName || row.name,
idCard: row.idCard || row.id_card || row.idNo,
phoneNumber: row.phoneNumber || row.phone || row.mobile || row.mobilePhone,
gender: gender,
age: row.age,
patientId: row.patientId || row.id || outpatientNo
}
showPatientList.value = false
ElMessage.success('已选择患者:' + (row.patientName || row.name))
// 重置选中状态
selectedPatient.value = null
}
// 重置表单
const resetForm = () => {
searchForm.patientName = ''
searchForm.idCard = ''
searchForm.phoneNumber = ''
patientInfo.value = null
patientList.value = []
showPatientList.value = false
selectedPatient.value = null
renewalForm.newOutpatientNo = ''
// 重置分页状态
currentPage.value = 1
pageSize.value = 5
total.value = 0
}
// 格式化日期为YYYY-MM-DD格式
const formatDate = (date) => {
if (!date) return ''
// 如果已经是YYYY-MM-DD格式直接返回
if (typeof date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(date)) {
return date
}
// 处理日期对象或其他格式
const d = new Date(date)
if (isNaN(d.getTime())) return ''
const year = d.getFullYear()
const month = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
</script>
<style scoped>
/* 自定义对话框样式 */
.custom-patient-dialog .el-dialog__header {
padding: 0;
border-bottom: none;
height: auto;
}
/* 隐藏默认的header样式 */
.custom-header {
display: none;
}
.custom-patient-dialog .el-dialog__body {
padding: 0;
max-height: 60vh;
overflow-y: auto;
}
/* 隐藏默认底部区域 */
.custom-patient-dialog .el-dialog__footer {
padding: 0;
border-top: none;
}
.dialog-footer .el-button {
min-width: 80px;
background-color: #f56c6c;
border-color: #f56c6c;
color: white;
}
/* 表格样式 */
.el-dialog__body {
padding: 0;
overflow: hidden;
}
.el-table {
margin: 0;
}
.el-table th {
background-color: #f5f7fa;
font-weight: 500;
color: #303133;
}
.el-table td {
text-align: left;
}
/* 表格样式优化 */
.el-table {
margin: 0;
}
.el-table th {
background-color: #f5f7fa;
font-weight: 500;
color: #303133;
text-align: center;
border-bottom: 1px solid #ebeef5;
}
.el-table td {
text-align: center;
border-bottom: 1px solid #ebeef5;
}
.el-table tr:hover > td {
background-color: #f5f7fa;
}
.el-table--striped .el-table__body tr:nth-child(2n) {
background-color: #fafafa;
}
/* 分页样式优化 */
.el-pagination {
margin-top: 10px;
padding: 10px 20px;
background-color: #fafafa;
border-top: 1px solid #ebeef5;
}
.card-renewal-container {
width: 100%;
max-width: 800px;
margin: 0 auto;
padding: 10px;
border: 1px solid #dcdfe6;
border-radius: 4px;
background: #fff;
}
.title-bar {
background: #409eff;
color: white;
padding: 10px 15px;
margin: -10px -10px 15px -10px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.title-bar h2 {
margin: 0;
font-size: 16px;
font-weight: normal;
}
.button-bar {
display: flex;
gap: 15px;
padding: 15px;
background-color: #f5f7fa;
border: 1px solid #ebeef5;
margin-bottom: 20px;
}
.button-bar .el-button {
font-weight: 500;
transition: all 0.3s ease;
}
.button-bar .el-button:hover {
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.search-section {
margin-bottom: 20px;
}
.input-item {
margin-bottom: 15px;
}
.input-item label {
display: inline-block;
width: 100px;
text-align: right;
margin-right: 10px;
font-weight: 500;
}
.patient-info-section {
background: #f5f7fa;
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
}
.info-item label {
display: block;
color: #606266;
font-size: 14px;
margin-bottom: 5px;
}
.info-value {
color: #303133;
font-weight: 500;
}
.new-number-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #ebeef5;
}
.success-icon {
width: 50px;
height: 50px;
background: #67c23a;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 30px;
margin: 0 auto;
}
</style>

View File

@@ -0,0 +1,342 @@
<template>
<div class="patient-search-container">
<!-- 标题栏 -->
<div class="title-bar">
<h2>病人档案查询</h2>
<div class="button-bar">
<el-button
type="primary"
@click="handleQuery"
:loading="loading"
size="large"
icon="el-icon-check"
>
确认(Q)
</el-button>
<el-button
type="danger"
@click="handleClose"
size="large"
icon="el-icon-close"
>
关闭(C)
</el-button>
</div>
</div>
<!-- 查询表单 -->
<div class="search-form">
<el-row :gutter="20">
<el-col :span="8">
<div class="form-item">
<label>病人姓名:</label>
<el-input
v-model="searchForm.patientName"
placeholder="请输入病人姓名"
clearable
@keyup.enter="handleQuery"
/>
</div>
</el-col>
<el-col :span="8">
<div class="form-item">
<label>身份证号码:</label>
<el-input
v-model="searchForm.idCard"
placeholder="请输入身份证号码"
clearable
@keyup.enter="handleQuery"
/>
</div>
</el-col>
<el-col :span="8">
<div class="form-item">
<label>手机号码:</label>
<el-input
v-model="searchForm.phoneNumber"
placeholder="请输入手机号码"
clearable
@keyup.enter="handleQuery"
/>
</div>
</el-col>
</el-row>
</div>
<!-- 查询结果表格 -->
<div class="result-table">
<el-table
:data="patientList"
style="width: 100%"
stripe
@row-click="handleRowClick"
highlight-current-row
row-key="id"
:current-row-key="selectedPatient?.id"
>
<el-table-column label="序号" type="index" width="80" align="center">
</el-table-column>
<el-table-column label="病人姓名" width="120" align="center">
<template #default="scope">
{{ scope.row.patientName }}
</template>
</el-table-column>
<el-table-column label="门诊号码" width="150" align="center">
<template #default="scope">
{{ scope.row.outpatientNo || '-' }}
</template>
</el-table-column>
<el-table-column label="身份证号码" width="200" align="center">
<template #default="scope">
{{ scope.row.idCard || '-' }}
</template>
</el-table-column>
<el-table-column label="手机号码" width="120" align="center">
<template #default="scope">
{{ scope.row.phoneNumber || '-' }}
</template>
</el-table-column>
<el-table-column label="性别" width="80" align="center">
<template #default="scope">
{{ scope.row.gender }}
</template>
</el-table-column>
<el-table-column label="年龄" width="80" align="center">
<template #default="scope">
{{ scope.row.age }}
</template>
</el-table-column>
<el-table-column label="出生年月" width="120" align="center">
<template #default="scope">
{{ scope.row.birthDate }}
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script setup>
import {computed, onMounted, onUnmounted, reactive, ref, watch} from 'vue'
import {ElMessage} from 'element-plus'
import {getOutpatientRegistrationList} from '../outpatientregistration/components/outpatientregistration'
// 搜索表单
const searchForm = reactive({
patientName: '',
idCard: '',
phoneNumber: ''
})
// 患者列表
const patientList = ref([])
// 加载状态
const loading = ref(false)
// API调用相关变量
const queryParams = ref({
pageNum: 1,
pageSize: 50
})
// 当前选中的患者
const selectedPatient = ref(null)
// 定义事件
const emit = defineEmits(['selectPatient', 'close'])
// 计算搜索关键字合并三个字段为一个searchKey
const searchKey = computed(() => {
return searchForm.patientName || searchForm.idCard || searchForm.phoneNumber
})
// 监听搜索关键字变化,自动查询
watch(searchKey, (newValue) => {
// 当搜索关键字变化时,延迟查询,避免频繁请求
const timer = setTimeout(() => {
if (newValue) {
queryParams.value.searchKey = newValue
handleQuery()
}
}, 300)
return () => clearTimeout(timer)
})
// 处理行点击
const handleRowClick = (row) => {
selectedPatient.value = row
ElMessage.success(`已选择患者:${row.patientName}`)
}
// 查询患者信息和确认选择
const handleQuery = async () => {
// 如果已经选中了患者,则确认选择
if (selectedPatient.value) {
// 发射选择患者事件
emit('selectPatient', selectedPatient.value)
ElMessage.success(`已确认选择患者:${selectedPatient.value.patientName}`)
return
}
// 检查是否至少填写了一个查询条件
if (!searchKey.value) {
ElMessage.warning('请至少输入一个查询条件')
return
}
loading.value = true
try {
// 调用门诊挂号的API获取患者列表
const response = await getOutpatientRegistrationList(queryParams.value)
if (response.data && response.data.records) {
// 映射字段,使其与表格展示需求匹配
patientList.value = response.data.records.map(record => ({
id: record.id,
patientName: record.name,
outpatientNo: record.identifierNo,
idCard: record.idCard,
phoneNumber: record.phone,
gender: record.genderEnum_enumText || '-',
age: record.age || '-',
birthDate: record.birthDate || '-' // 如果后端没有提供,需要计算或显示为'-'
}))
if (patientList.value.length === 0) {
ElMessage.warning('未找到符合条件的患者信息')
}
} else {
ElMessage.error('查询失败')
patientList.value = []
}
} catch (error) {
ElMessage.error('查询失败,请稍后重试')
console.error('查询失败:', error)
patientList.value = []
} finally {
loading.value = false
}
}
// 关闭窗口
const handleClose = () => {
// 发射关闭事件
emit('close')
}
// 键盘事件处理
const handleKeydown = (event) => {
// Alt + Q 查询
if (event.altKey && event.key.toLowerCase() === 'q') {
event.preventDefault()
handleQuery()
}
// Alt + C 关闭
if (event.altKey && event.key.toLowerCase() === 'c') {
event.preventDefault()
handleClose()
}
}
// 组件挂载时添加键盘事件监听
onMounted(() => {
document.addEventListener('keydown', handleKeydown)
})
// 组件卸载时移除键盘事件监听
onUnmounted(() => {
document.removeEventListener('keydown', handleKeydown)
})
</script>
<style scoped>
.patient-search-container {
width: 100%;
background-color: #fff;
border: 1px solid #b3d4ff;
border-radius: 4px;
overflow: hidden;
}
.title-bar {
background-color: #d6e9ff;
padding: 10px 20px;
border-bottom: 1px solid #b3d4ff;
display: flex;
justify-content: space-between;
align-items: center;
}
.title-bar h2 {
margin: 0;
font-size: 18px;
color: #303133;
font-weight: 500;
}
.button-bar {
padding: 10px 20px;
display: flex;
gap: 10px;
}
.button-bar .el-button {
padding: 12px 24px;
font-size: 16px;
}
.search-form {
padding: 20px;
border-bottom: 1px solid #dcdfe6;
}
.form-item {
margin-bottom: 15px;
display: flex;
align-items: center;
}
.form-item label {
display: inline-block;
width: 100px;
text-align: right;
margin-right: 10px;
font-weight: 500;
color: #303133;
}
.form-item .el-input {
width: 250px;
}
.result-table {
padding: 10px;
}
.el-table {
border: 1px solid #dcdfe6;
border-radius: 4px;
}
.el-table th {
background-color: #f2f6fc;
font-weight: 600;
color: #303133;
border-bottom: 1px solid #e6e8eb;
}
.el-table td {
padding: 10px 0;
font-size: 14px;
border-bottom: 1px solid #ebeef5;
}
.el-table--striped .el-table__body tr:nth-child(2n) {
background-color: #f8f9fa;
}
.el-table__row:hover {
background-color: #f5f7fa;
}
</style>

View File

@@ -0,0 +1,48 @@
import request from '@/utils/request'
/**
* 获取门诊患者信息列表
* 复用门诊挂号的API来查询患者信息
*/
export const getOutpatientRegistrationList = (params) => {
// 调用门诊挂号的API获取患者信息
return request({
url: '/api/outpatient/registration/list',
method: 'get',
params
})
}
/**
* 根据ID获取患者详情
*/
export const getPatientById = (id) => {
return request({
url: `/api/outpatient/registration/${id}`,
method: 'get'
})
}
/**
* 根据条件查询患者信息
* 复用门诊挂号API进行查询
*/
export const getPatientList = (params) => {
// 复用门诊挂号的API
return request({
url: '/api/outpatient/registration/list',
method: 'get',
params
})
}
/**
* 根据门诊号码获取患者详细信息
*/
export const getPatientByOutpatientNo = (outpatientNo) => {
return request({
url: '/api/outpatient/registration/list',
method: 'get',
params: { identifierNo: outpatientNo }
})
}

View File

@@ -0,0 +1,2 @@
<template>
</template>

View File

@@ -0,0 +1,2 @@
<template>
</template>

View File

@@ -0,0 +1,502 @@
<template>
<div style="display: flex; justify-content: space-between" class="app-container" v-loading="loading"
:element-loading-text="loadingText">
<!-- 左侧患者基本信息区 -->
<el-card style="width: 30%">
<template #header>
<span style="vertical-align: middle">患者基本信息</span>
</template>
<el-descriptions :column="1" border>
<el-descriptions-item label="病历号">{{ patientInfo.encounterBusNo }}</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.organizationName }}</el-descriptions-item>
<el-descriptions-item label="就诊时间">
{{ formatDateStr(patientInfo.receptionTime, 'YYYY-MM-DD HH:mm:ss') }}
</el-descriptions-item>
<el-descriptions-item label="手术单号">{{ surgeryInfo.surgeryNo }}</el-descriptions-item>
<el-descriptions-item label="手术名称">{{ surgeryInfo.surgeryName }}</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 右侧收费项目区 -->
<div style="width: 69%">
<el-card style="min-width: 1100px">
<template #header>
<span style="vertical-align: middle">收费项目</span>
</template>
<div style="margin-bottom: 10px">
<el-button type="primary" @click="confirmCharge()" :disabled="buttonDisabled">
确认收费
</el-button>
<el-button type="primary" plain @click="handleReadCard('01')" style="width: 65px">
电子凭证
</el-button>
<el-button type="primary" plain @click="handleReadCard('03')" style="width: 65px">
医保卡
</el-button>
<span style="float: right">
合计金额{{ totalAmounts ? totalAmounts.toFixed(2) : 0 }}
</span>
</div>
<el-table
ref="chargeListRef"
height="530"
:data="chargeList"
row-key="id"
@selection-change="handleSelectionChange"
v-loading="chargeLoading"
:span-method="objectSpanMethod"
border
>
<el-table-column type="selection" :selectable="checkSelectable" width="55" />
<el-table-column label="单据号" align="center" prop="busNo" width="180" />
<el-table-column label="收费项目" align="center" prop="itemName" width="200" />
<el-table-column label="数量" align="center" prop="quantityValue" width="80" />
<el-table-column label="医疗类型" align="center" prop="medTypeCode_dictText" />
<el-table-column label="医保编码" align="center" prop="ybNo" />
<el-table-column label="费用性质" align="center" prop="contractName" />
<el-table-column label="收费状态" align="center" prop="statusEnum_enumText" width="150">
<template #default="scope">
<el-tag v-if="scope.row.statusEnum === 1" disable-transitions>
{{ scope.row.statusEnum_enumText }}
</el-tag>
<el-tag v-else-if="scope.row.statusEnum === 5" type="success" disable-transitions>
{{ scope.row.statusEnum_enumText }}
</el-tag>
<el-tag v-else-if="scope.row.statusEnum === 8" type="danger" disable-transitions>
{{ scope.row.statusEnum_enumText }}
</el-tag>
<el-tag v-else type="warning" disable-transitions>
{{ scope.row.statusEnum_enumText }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="金额" align="right" prop="totalPrice" header-align="center">
<template #default="scope">
{{ scope.row.totalPrice.toFixed(2) + ' 元' || '0.00' + ' 元' }}
</template>
</el-table-column>
<el-table-column label="收款人" align="center" prop="entererId_dictText" />
<el-table-column
label="操作"
align="center"
fixed="right"
header-align="center"
class-name="no-hover-column"
>
<template #default="scope">
<el-button
:disabled="!scope.row.paymentId"
link
type="primary"
@click="printCharge(scope.row)"
>
打印
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
<!-- 收费对话框 -->
<ChargeDialog
ref="chargeDialogRef"
:open="openDialog"
@close="handleClose"
:category="patientInfo.categoryEnum"
:totalAmount="totalAmount"
:patientInfo="patientInfo"
:chargeItemIds="chargeItemIdList"
:consumablesIdList="consumablesIdList"
:chrgBchnoList="chrgBchnoList"
:userCardInfo="userCardInfo"
:paymentId="paymentId"
:details="details"
:chargedItems="chargedItems"
:feeType="patientInfo.medfeePaymtdCode"
:medfee_paymtd_code="medfee_paymtd_code"
@refresh="getChargeList"
/>
</div>
</template>
<script setup name="SurgeryCharge">
import {
getChargeList,
precharge,
getChargeInfo,
} from '../cliniccharge/components/api';
import {invokeYbPlugin5000, invokeYbPlugin5001} from '@/api/public';
import ChargeDialog from '../cliniccharge/components/chargeDialog.vue';
import {formatDateStr} from '@/utils';
import useUserStore from '@/store/modules/user';
import Decimal from 'decimal.js';
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
const { medfee_paymtd_code } = proxy.useDict('medfee_paymtd_code');
// Props: 从手术安排界面传入的患者信息和手术信息
const props = defineProps({
patientInfo: {
type: Object,
required: true,
default: () => ({})
},
surgeryInfo: {
type: Object,
required: true,
default: () => ({})
}
});
// 数据定义
const totalAmounts = ref(0);
const selectedRows = ref([]);
const chargeList = ref([]);
const chargeItemIdList = ref([]);
const chrgBchnoList = ref([]);
const chargeLoading = ref(false);
const encounterId = ref('');
const paymentId = ref('');
const openDialog = ref(false);
const totalAmount = ref(0);
const chargeListRef = ref();
const details = ref({});
const buttonDisabled = computed(() => {
return Object.keys(props.patientInfo).length === 0;
});
const chargedItems = ref([]);
const consumablesIdList = ref([]);
const userCardInfo = ref({});
const readCardLoading = ref(false);
const loadingText = ref('');
const loading = ref(false);
// Watch
watch(
() => selectedRows.value,
(newVlaue) => {
if (newVlaue && newVlaue.length > 0) {
handleTotalAmount();
} else {
totalAmounts.value = 0;
}
},
{ immediate: true }
);
watch(
() => chargeList.value,
(newVlaue) => {
if (newVlaue && newVlaue.length > 0) {
handleTotalAmount();
} else {
totalAmounts.value = 0;
}
},
{ immediate: true }
);
// 初始化
onMounted(() => {
if (props.patientInfo && props.patientInfo.encounterId) {
encounterId.value = props.patientInfo.encounterId;
fetchChargeList();
}
});
// 方法
function handleSelectionChange(selection) {
selectedRows.value = selection;
}
function handleTotalAmount() {
if (selectedRows.value.length == 0) {
totalAmounts.value = chargeList.value.reduce((accumulator, currentRow) => {
return new Decimal(accumulator).add(currentRow.totalPrice.toFixed(2) || 0);
}, new Decimal(0));
} else {
totalAmounts.value = selectedRows.value.reduce((accumulator, currentRow) => {
return new Decimal(accumulator).add(currentRow.totalPrice.toFixed(2) || 0);
}, 0);
}
}
// 获取收费项目列表(只显示该手术已计费的项目)
function fetchChargeList() {
if (!props.patientInfo.encounterId) {
return;
}
chargeLoading.value = true;
// 调用门诊划价的getChargeList接口传入encounterId
// 只显示generateSourceEnum=2手术计费且sourceBillNo=手术单号的费用项
getChargeList(props.patientInfo.encounterId).then((res) => {
// 过滤出手术计费的收费项目
chargeList.value = (res.data || []).filter(item =>
item.generateSourceEnum === 2 && item.sourceBillNo === props.surgeryInfo.surgeryNo
);
setTimeout(() => {
chargeLoading.value = false;
// 默认选中所有未收费的项目
if (chargeListRef.value) {
chargeListRef.value.toggleAllSelection();
}
}, 100);
}).catch(() => {
chargeLoading.value = false;
});
}
function checkSelectable(row, index) {
// 已结算时禁用选择框
return row.statusEnum === 1;
}
function handleClose(value, msg) {
openDialog.value = false;
if (value == 'success') {
proxy.$modal.msgSuccess(msg);
fetchChargeList();
}
}
// 确认收费
function confirmCharge() {
let selectRows = chargeListRef.value.getSelectionRows();
if (selectRows.length == 0) {
proxy.$modal.msgWarning('请选择一条收费项目');
return;
}
chargeItemIdList.value = selectRows.map((item) => {
return item.id;
});
consumablesIdList.value = selectRows
.filter((item) => {
return item.serviceTable == 'wor_device_request';
})
.map((item) => {
return item.id;
});
chargedItems.value = selectRows;
precharge({
patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId,
chargeItemIds: chargeItemIdList.value,
// 传递手术计费标识
generateSourceEnum: 2,
sourceBillNo: props.surgeryInfo.surgeryNo,
}).then((res) => {
if (res.code == 200 && res.data) {
paymentId.value = res.data.paymentId;
chrgBchnoList.value = res.data.chrgBchnoList;
totalAmount.value = res.data.details?.find((item) => item.payEnum == 220000)?.amount ?? 0;
details.value = res.data.details?.filter((item) => {
return item.amount > 0;
}) || [];
openDialog.value = true;
} else {
proxy.$modal.msgError(res?.msg || '预结算失败');
}
});
}
// 读卡功能
async function handleReadCard(value) {
try {
let jsonResult;
let cardInfo;
let userMessage = undefined;
switch (value) {
case '01': // 电子凭证
await invokeYbPlugin5000({
FunctionId: 3,
url: 'http://10.47.0.67:8089/localcfc/api/hsecfc/localQrCodeQuery',
orgId: 'H22010200672',
businessType: '01101',
operatorId: userStore.id.toString(),
operatorName: userStore.name,
officeId: 'D83',
officeName: '财务科',
})
.then((res) => {
readCardLoading.value = true;
loadingText.value = '正在读取...';
jsonResult = res.data;
})
.catch(() => {
readCardLoading.value = false;
})
.finally(() => {
readCardLoading.value = false;
});
cardInfo = JSON.parse(JSON.stringify(jsonResult));
let message = JSON.parse(cardInfo.data);
userMessage = {
certType: '02',
certNo: message.data.idNo,
psnCertType: '02',
};
userCardInfo.value = {
certType: '01',
certNo: message.data.idNo,
psnCertType: '01',
busiCardInfo: message.data.ecToken,
};
break;
case '03': // 医保卡
readCardLoading.value = true;
loadingText.value = '正在读取...';
await invokeYbPlugin5001(
JSON.stringify({
FunctionId: 1,
IP: 'ddjk.jlhs.gov.cn',
PORT: 20215,
TIMEOUT: 60,
SFZ_DRIVER_TYPE: 1,
})
)
.then((res) => {
jsonResult = JSON.stringify(res.data);
})
.finally(() => {
readCardLoading.value = false;
});
let message1 = JSON.parse(jsonResult);
userMessage = {
certType: '02',
certNo: message1.SocialSecurityNumber,
psnCertType: '02',
};
userCardInfo.value = {
certType: '02',
certNo: message1.SocialSecurityNumber,
psnCertType: '02',
busiCardInfo: message1.BusiCardInfo,
};
break;
}
readCardLoading.value = false;
if (userMessage.certNo) {
let selectRows = chargeListRef.value.getSelectionRows();
if (selectRows.length == 0) {
proxy.$modal.msgWarning('请选择一条收费项目');
return;
}
chargeItemIdList.value = selectRows.map((item) => {
return item.id;
});
totalAmount.value = selectRows.reduce((accumulator, currentRow) => {
return accumulator + (currentRow.totalPrice || 0);
}, 0);
precharge({
patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId,
chargeItemIds: chargeItemIdList.value,
ybMdtrtCertType: userCardInfo.value.psnCertType,
busiCardInfo: userCardInfo.value.busiCardInfo,
generateSourceEnum: 2,
sourceBillNo: props.surgeryInfo.surgeryNo,
}).then((res) => {
if (res.code == 200 && res.data) {
paymentId.value = res.data.paymentId;
totalAmount.value = res.data.details?.find((item) => item.payEnum == 220000)?.amount ?? 0;
details.value = res.data.details || [];
chargeItemIdList.value = selectRows.map((item) => {
return item.id;
});
chargedItems.value = selectRows;
consumablesIdList.value = selectRows
.filter((item) => {
return item.serviceTable == 'wor_device_request';
})
.map((item) => {
return item.id;
});
openDialog.value = true;
} else {
proxy.$modal.msgError(res?.msg || '预结算失败');
}
});
}
} catch (error) {
console.error('调用失败:', error);
readCardLoading.value = false;
}
}
// 行合并方法
function objectSpanMethod({ row, column, rowIndex, columnIndex }) {
if (columnIndex === 10) {
if (!row.paymentId) {
return [1,1];
}
let spanCount = 1;
if (rowIndex === 0 || chargeList.value[rowIndex - 1].paymentId !== row.paymentId) {
for (let i = rowIndex + 1; i < chargeList.value.length; i++) {
if (chargeList.value[i].paymentId === row.paymentId) {
spanCount++;
} else {
break;
}
}
return [spanCount, 1];
} else {
return [0, 0];
}
}
return [1, 1];
}
// 打印功能
function printCharge(row) {
let rows = [];
chargeList.value.forEach((item, index) => {
if (item.paymentId === row.paymentId) {
rows.push(item);
}
});
chargedItems.value = rows;
getChargeInfo({ paymentId: row.paymentId }).then((res) => {
if (res.data && res.data.detail) {
const amountDetail = res.data.detail?.find((item) => item.payEnum == 220000);
if (amountDetail) {
totalAmount.value = amountDetail.amount || 0;
rows.forEach((item) => {
if (item.actualPrice === undefined || item.actualPrice === null) {
item.actualPrice = 0;
}
if (item.discountAmount === undefined || item.discountAmount === null) {
item.discountAmount = 0;
}
if (item.discountRate === undefined || item.discountRate === null) {
item.discountRate = 100;
}
});
}
const enhancedPrintData = {
...res.data,
selectedRow: row,
chargedItems: rows,
};
nextTick(() => {
proxy.$refs['chargeDialogRef'].printReceipt(enhancedPrintData);
});
}
});
}
</script>
<style scoped>
:deep(.no-hover-column) .cell:hover {
background-color: transparent !important;
}
:deep(.el-table__body) tr:hover td.no-hover-column {
background-color: inherit !important;
}
</style>

View File

@@ -0,0 +1,38 @@
import request from '@/utils/request';
// 获取网银支付参数列表
export function getList (data) {
return request ({
url: '/three-part/pay/page',
method: 'get',
params:data,
});
}
export function getPayinfo (data) {
return request ({
url: '/three-part/pay/pay-query?paymentId='+data.paymentId,
method: 'get',
});
}
export function returnfee (data) {
return request ({
url: '/three-part/pay/return-bill?paymentId='+data.paymentId,
method: 'get',
});
}
export function returnFeednext (data) {
return request ({
url: '/three-part/pay/return-goods?paymentId='+data.paymentId,
method: 'get',
});
}
export function returnResult (data) {
return request ({
url: '/three-part/pay/return-query?id='+data.id,
method: 'get',
});
}

View File

@@ -0,0 +1,120 @@
<template>
<div class="app-continer">
<el-form :model="queryParams" ref="queryRef" :inline="true">
<el-form-item label="患者姓名" prop="patientName">
<el-input v-model="queryParams.searchKey" placeholder="请输入患者姓名" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table :data="recordList" v-loading="loading" border ref="tableRef">
<el-table-column label="患者姓名" align="center" prop="patientName" :show-overflow-tooltip="true" />
<el-table-column label="支付单号" align="center" prop="paymentBusNo" :show-overflow-tooltip="true" />
<el-table-column label="交易金额(元)" align="right" prop="txnAmt" header-align="center" width="100"
:show-overflow-tooltip="true">
</el-table-column>
<el-table-column label="交易类型" align="center" prop="tranType" :show-overflow-tooltip="true" />
<el-table-column label="支付方式" align="center" prop="payType" :show-overflow-tooltip="true" />
<el-table-column label="交易时间" align="center" prop="txnTime" :show-overflow-tooltip="true">
<template #default="scope">
<span>{{ parseTime(scope.row.txnTime) }}</span>
</template>
</el-table-column>
<el-table-column label="原交易类型" align="center" prop="orgTranType" :show-overflow-tooltip="true" />
<el-table-column label="原交易类型" align="center" prop="orgTranType" :show-overflow-tooltip="true" />
<el-table-column label="第三方优惠说明" align="center" prop="otherMsg" :show-overflow-tooltip="true" />
<el-table-column label="错误信息" align="center" prop="errMsg" :show-overflow-tooltip="true" />
<el-table-column label="查询结果" align="center" prop="queryResult" :show-overflow-tooltip="true" />
<el-table-column label="查询结果说明" align="center" prop="queryResultMsg" :show-overflow-tooltip="true" />
<el-table-column label="操作" align="center" prop="paymentEnum_enumText" width="340">
<template #default="scope">
<el-button type="primary" link :disabled="!scope.row.paymentId"
@click="getPayInfo(scope.row.paymentId)">支付结果查询</el-button>
<el-button type="warning" link :disabled="!scope.row.paymentId"
@click="returnFee(scope.row.paymentId)">退费</el-button>
<el-button type="danger" link auto-insert-space :disabled="!scope.row.paymentId"
@click="returnFeeNext(scope.row.paymentId)">隔天退费</el-button>
<el-button type="success" link @click="returnFeeResultQuery(scope.row.id)">退费结果查询</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getLists" />
</div>
</template>
<script setup name="ClinicRecord">
const { proxy } = getCurrentInstance();
import {getList, getPayinfo, returnfee, returnFeednext, returnResult} from './components/api.js';
const total = ref(0);
const queryParams = ref({
pageNo: 1,
pageSize: 100,
searchKey: '',
});
const recordList = ref([]);
const loading = ref(false);
const getPayInfo = (paymentId) => {
console
getPayinfo({ paymentId }).then((res) => {
proxy.$message.success('支付结果查询成功');
console.log(res);
});
};
const returnFee = (paymentId) => {
returnfee({ paymentId }).then((res) => {
proxy.$message.success('退费成功');
console.log(res);
});
};
const returnFeeNext = (paymentId) => {
returnFeednext({ paymentId }).then((res) => {
proxy.$message.success('隔天退费成功');
console.log(res);
});
};
const returnFeeResultQuery = (id) => {
returnResult({ id }).then((res) => {
proxy.$message.success(res.data);
console.log(res);
});
};
const handleQuery = () => {
//queryParams.value.pageNum = 1;
getList(queryParams.value);
};
const resetQuery = () => {
proxy.resetForm('queryRef');
handleQuery();
};
const getLists = async () => {
loading.value = true;
getList(queryParams.value).then((res) => {
loading.value = false;
recordList.value = res.data.records;
total.value = res.data.total;
});
};
getLists();
</script>
<style scoped>
.app-continer {
padding: 20px;
}
.el-dialog {
height: 90vh !important;
}
.dialog-footer {
text-align: right;
}
.el-textarea__inner {
resize: none;
}
</style>