Files
gzh/src/views/JiaoFei_detail copy.vue
2026-01-06 15:03:14 +08:00

805 lines
26 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="home">
<nav-bar url="/Member_wdzd"></nav-bar>
<div class="bj"></div>
<div class="app-container">
<!-- 缴费信息 -->
<div class="left-label">
<div class="label-color"></div>
<div class="label-text">缴费信息</div>
</div>
<div class="card">
<div class="cell">
<div class="cell_title">缴费金额</div>
<div class="cell_value">{{ parseFloat(params.BillMoney).toFixed(2) }}</div>
</div>
<div class="cell">
<div class="cell_title">缴费单号</div>
<div class="cell_value">{{ params.SerialFlowNo }}</div>
</div>
<div class="cell">
<div class="cell_title">开单时间</div>
<div class="cell_value">{{ params.BillTime }}</div>
</div>
<div class="cell">
<div class="cell_title">执行科室</div>
<div class="cell_value">{{ params.DoDepartName }}</div>
</div>
</div>
<!-- 缴费项目 -->
<div class="left-label">
<div class="label-color"></div>
<div class="label-text">缴费项目</div>
</div>
<div class="card">
<div v-for="item in payDetail" class="cell">
<div class="cell_title">{{ item.COSTNAME }}</div>
<div class="cell_value">{{ item.AMOUNT }}×{{ item.COSTSUM }} {{ item.UNIT }}</div>
</div>
</div>
<!-- 选择支付方式 -->
<div class="left-label">
<div class="label-color"></div>
<div class="label-text">支付方式</div>
</div>
<div class="card">
<!-- 微信支付选项 -->
<div v-if="provider == 'wxpay'" class="cell">
<label class="pay-item-label">
<div class="pay_type_list">
<div>
<img class="medical-insurance-image"
src="https://hnwjyy.oss-cn-shanghai.aliyuncs.com/%E5%8C%BB%E4%BF%9D%E6%B7%B7%E5%90%88%E6%94%AF%E4%BB%98%402x.png" />
</div>
<div>医保支付</div>
</div>
<input type="radio" name="payType" value="微信医保支付" v-model="radiovalue1" />
</label>
</div>
<div v-if="provider == 'wxpay'" class="cell">
<label class="pay-item-label">
<div class="pay_type_list">
<div>
<img class="pay-image"
src="https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon64_appwx_logo.png" />
</div>
<div>微信支付</div>
</div>
<input type="radio" name="payType" value="微信支付" v-model="radiovalue1" />
</label>
</div>
<!-- 支付宝支付选项 -->
<!-- <div v-if="provider=='alipay'" class="cell">
<div class="pay_type_list">
<div><img class="pay-image" src="../../static/icon/alipay.svg" /></div>
<div>支付宝支付</div>
</div>
<input type="radio" name="payType" value="支付宝支付" v-model="radiovalue1" />
</div> -->
</div>
<button class="btn" @click="generateOrder" :disabled="isPaying">
{{ isPaying ? '支付处理中...' : '支付' }}
</button>
</div>
</div>
</template>
<script>
import {
preCheck, apiUnifiedOrder,
apiopDjcx, apiopLBQuery,
apiOpWxQuery, getPayAuthCode,
prePay, getAccessToken
} from "@/request/api.js";
import { Dialog, Toast } from "vant";
export default {
data() {
return {
PatientId: '',
SerialFlowNo: '',
payDetail: [],
provider: 'wxpay', // 支付方式 wxpay 或 alipay
radiovalue1: '', // 选择的支付方式
isPaying: false, // 添加防止重复支付的状态
returnUrl: 'https://nxwj.btlsoln.com/nxwj-gzh/#/JiaoFei_detail', // 回调地址
params: {
FlowNo: '',
BillMoney: '',
BillTime: '',
BillType: '1',
DiagnosisCode: '',
DiagnosisName: '',
DoDepartName: '',
DoDepartCode: '',
DoctorName: '',
SerialFlowNo: '',
SerialNo: '',
SetDepartName: '',
SetDepartCode: '',
DoctorCode: '',
BillName: '',
PatientId: '',
},
globalData: {// 医保授权相关
scene: 0,
ghid: '', // 单据id
jzrid: '',//病人id就诊卡Id
authCode: '',
payAuthCode: '',
userCardNo: '',
userName: '',
cityId: '',
hosp_out_trade_no: '',
userLongitudeLatitude: {},
// 医保授权支付相关
orderInfo: {},
// 医保支付金额相关
ybPayInfo: {},
},
accessToken: '',
};
},
// 修改后的 mounted
mounted() {
// 优先从路由query获取授权回调后参数在query中
this.PatientId = this.$route.query.PatientId || this.$route.params.PatientId || sessionStorage.getItem('cachedPatientId') || '';
this.SerialFlowNo = this.$route.query.SerialFlowNo || this.$route.params.SerialFlowNo || sessionStorage.getItem('cachedSerialFlowNo') || '';
// 重新缓存参数(确保后续使用)
if (this.PatientId) sessionStorage.setItem('cachedPatientId', this.PatientId);
if (this.SerialFlowNo) sessionStorage.setItem('cachedSerialFlowNo', this.SerialFlowNo);
// 处理授权回调
let authCode = this.$route.query.authCode;
let retCode = this.$route.query.retCode;
if (retCode === '0' && authCode) {
this.radiovalue1='微信医保支付';
this.getPayAuth(authCode);
// 不return允许后续执行getData()或在getPayAuth中调用
}
// 确保数据加载(如果参数存在)
if (this.PatientId && this.SerialFlowNo) {
this.getData();
} else {
Toast('订单信息丢失,请重新选择订单');
setTimeout(() => this.$router.push('/Member_wdzd'), 1500);
}
},
methods: {
getToken() {
getAccessToken().then(res => {
console.log(res)
this.accessToken = res.data.accessToken
}).catch(err => {
Toast(err.message)
})
},
getData() {
let formdata = {
PatientId: this.PatientId,
QueryType: "0",
OrderNo: this.SerialFlowNo,
};
let queryData = {
FlowNo: this.SerialFlowNo,
PatientId: this.PatientId,
InsuranceType: '0'
};
// 并行执行两个异步请求
Promise.all([
apiopDjcx(formdata),
apiopLBQuery(queryData)
]).then(([djcxRes, lbQueryRes]) => {
// 处理 apiopDjcx 的响应
console.log(djcxRes.data);
let jsonObj = this.$x2js.xml2js(djcxRes.data);
console.log(jsonObj);
let jsonObj2 = jsonObj.Envelope.Body.MOP_OutpBillstoPayQueryResponse.request.request;
if (jsonObj2.returnresult.returncode === "1") {
// 设置参数
this.params.BillMoney = jsonObj2.data.BillMoney;
this.params.BillName = jsonObj2.data.BillName;
this.params.BillTime = jsonObj2.data.BillTime;
this.params.BillType = jsonObj2.data.BillType;
this.params.DiagnosisCode = jsonObj2.data.DiagnosisCode;
this.params.DiagnosisName = jsonObj2.data.DiagnosisName;
this.params.DoDepartName = jsonObj2.data.DoDepartName;
this.params.DoDepartCode = jsonObj2.data.DoDepartCode;
this.params.DoctorName = jsonObj2.data.DoctorName;
this.params.DoctorCode = jsonObj2.data.DoctorCode;
this.params.FlowNo = jsonObj2.data.FlowNo;
this.params.SerialFlowNo = jsonObj2.data.SerialFlowNo;
this.params.SerialNo = jsonObj2.data.SerialNo;
this.params.SetDepartName = jsonObj2.data.SetDepartName;
this.params.SetDepartCode = jsonObj2.data.SetDepartCode;
this.params.PatientId = this.PatientId;
} else {
Toast("获取缴费信息失败");
}
// 处理 apiopLBQuery 的响应
console.log(lbQueryRes);
let lbJsonObj = this.$x2js.xml2js(lbQueryRes.data);
console.log(lbJsonObj);
let lbJsonObj2 = this.$x2js.xml2js(
lbJsonObj.Envelope.Body.MOP_OutpBillsDetailQueryResponse.MOP_OutpBillsDetailQueryResult
);
console.log(lbJsonObj2);
if (Array.isArray(lbJsonObj2.response.data.data_row)) {
this.payDetail = lbJsonObj2.response.data.data_row;
} else {
this.payDetail = lbJsonObj2.response.data;
}
}).catch(err => {
Toast(err.message);
});
},
generateOrder() {
// 防止重复点击
if (this.isPaying) {
return;
}
if (!this.radiovalue1) {
Toast('请选择支付方式');
return;
}
// 设置支付状态为进行中
this.isPaying = true;
// 根据选择的支付方式执行不同逻辑
if (this.radiovalue1 === '微信医保支付') {
//判断单价小数位是否只有2位不是统一错误
console.log(this.payDetail);
if (Array.isArray(this.payDetail)) {
this.payDetail.forEach(item => {
const amountStr = item.AMOUNT.toString();
if (amountStr.includes('.')) {
const parts = amountStr.split('.');
const decimalPart = parts[1];
// 检查小数位数是否超过2位且末尾非0
if (decimalPart.length > 2 && !decimalPart.endsWith('0')) {
Toast("单价有误,暂不支持医保结账,请到门诊缴费");
this.isPaying = false;
return;
}
// 如果超过2位但末尾是0需要进一步检查去除末尾0后是否超过2位
if (decimalPart.length > 2) {
const trimmedDecimal = decimalPart.replace(/0+$/, ''); // 去除末尾的0
if (trimmedDecimal.length > 2) {
Toast("单价有误,暂不支持医保结账,请到门诊缴费");
this.isPaying = false;
return;
}
}
}
});
} else {
// console.log("不是数组");
const amountStr = this.payDetail.data_row.AMOUNT.toString();
if (amountStr.includes('.')) {
const parts = amountStr.split('.');
const decimalPart = parts[1];
// 检查小数位数是否超过2位且末尾非0
if (decimalPart.length > 2 && !decimalPart.endsWith('0')) {
Toast("单价有误,暂不支持医保结账,请到门诊缴费");
this.isPaying = false;
return;
}
// 如果超过2位但末尾是0需要进一步检查去除末尾0后是否超过2位
if (decimalPart.length > 2) {
const trimmedDecimal = decimalPart.replace(/0+$/, ''); // 去除末尾的0
if (trimmedDecimal.length > 2) {
Toast("单价有误,暂不支持医保结账,请到门诊缴费");
this.isPaying = false;
return;
}
}
}
}
// 只有特定患者可以使用医保支付
if (this.PatientId == "90131735" || this.PatientId == "90120780") {
this.handleMedicalInsurancePay();
} else {
Toast("如需医保支付,请携带身份证、医保卡等到线下缴费处操作。");
this.isPaying = false;
return;
}
} else if (this.radiovalue1 === '微信支付') {
this.handleWechatPay();
}
},
handleMedicalInsurancePay() {
preCheck().then(res => {
if (res.code === 200) {
// 生成带参数的returnUrl关键
const returnUrlWithParams = `${this.returnUrl}?PatientId=${this.PatientId}&SerialFlowNo=${this.SerialFlowNo}`;
const encodedReturnUrl = encodeURIComponent(returnUrlWithParams);
let authUrl = res.data;
if (authUrl.includes('?')) {
authUrl += '&redirectUrl=' + encodedReturnUrl;
} else {
authUrl += '?redirectUrl=' + encodedReturnUrl;
}
window.location.href = authUrl; // 跳转授权页
}
});
},
getPayAuth(code) {
const loadingToast = Toast.loading({
message: '加载中...',
forbidClick: true, // 禁止点击背景
duration: 0 // 不自动关闭
});
let params = {
// openid: localStorage.getItem("openid"),
qrcode: code,
}
console.log(params);
getPayAuthCode(params).then((res) => {
console.log("授权流程完毕,授权结果!!!")
console.log(res)
if (res.code == 200) {
// 解析 内层 JSON 字符串
let responseData;
try {
responseData = typeof res.data.json === 'string' ? JSON.parse(res.data.json) : res.data.json;
} catch (e) {
console.error("JSON解析错误", e);
Toast.fail("数据解析异常,请稍后重试");
loadingToast.clear(); // 解析错误时关闭加载
this.isPaying = false;
return;
}
if (responseData.user_card_no == res.data.IdNo) {
if (responseData.code === 0) {
// Toast.success("获取payAuthNo用户信息成功正在发起支付");
// TODO :发起支付
// console.log("用户信息:", responseData);
// {
// "code": 0,
// "message": "success",
// "user_name": "张林",
// "user_card_no": "370911198701055636",
// "pay_auth_no": "AUTH640100202510131615020070042",
// "user_longitude_latitude": {
// "longitude": "116.993209",
// "latitude": "35.978569"
// },
// "city_id": "640199",
// "family_pay_auth_no": null
// }
this.globalData.payAuthCode = responseData.pay_auth_no
this.globalData.userCardNo = responseData.user_card_no
this.globalData.userName = responseData.user_name
this.globalData.cityId = responseData.city_id
this.globalData.userLongitudeLatitude = responseData.user_longitude_latitude
this.globalData.ghid = sessionStorage.getItem('cachedSerialFlowNo') || this.SerialFlowNo;
this.globalData.jzrid = sessionStorage.getItem('cachedPatientId') || this.PatientId;
let YbInfo = {
"payAuthNo": responseData.pay_auth_no,
"userLongitudeLatitude": responseData.user_longitude_latitude,
"userCardNo": responseData.user_card_no,
"userName": responseData.user_name,
"jlid": "YBJF" + sessionStorage.getItem('cachedSerialFlowNo') || this.SerialFlowNo,
"kdsj": this.params.BillTime,
"ysje": this.params.BillMoney,
"jzrid": sessionStorage.getItem('cachedPatientId') || this.PatientId
}
sessionStorage.setItem('YbInfo', JSON.stringify(YbInfo));
// 授权完毕、开始预结算下单
let req = {
"userCardNo": this.globalData.userCardNo,
"userName": this.globalData.userName,
"orderTitle": "门诊缴费",
"openid": res.data.openid,
"ghid": this.globalData.ghid,
"FlowNo":this.params.FlowNo,
"payAuthNo": this.globalData.payAuthCode,
"jzrid": this.globalData.jzrid,
"userLongitudeLatitude": this.globalData.userLongitudeLatitude,
"accessToken": res.data.accessToken
}
this.retryPrePayWithDelay(req, 1) // 1次重试
.then(res => {
console.log(res);
if (res.code == 200) {
loadingToast.clear();
let data = encodeURIComponent(JSON.stringify(res.data));
this.$router.push(`/yborder?data=${data}`);
}
})
.catch(err => {
Toast.fail('支付请求失败');
// 错误时也清除loading提示
loadingToast.clear();
})
.finally(() => {
this.isPaying = false;
// 移除此行避免重复清除loadingToast
});
} else if (responseData.code < 0) {
Toast.fail("系统繁忙,请稍后重试");
this.getData();
loadingToast.clear(); // 异常时关闭
this.isPaying = false;
} else if (responseData.code > 0) {
Toast.fail(responseData.message);
this.radiovalue1 = ''; // 重置支付方式选择
this.getData();
loadingToast.clear(); // 异常时关闭
this.isPaying = false; // 重新启用支付按钮
}
} else {
Toast.fail("不是本人医保,不予办理");
this.getData();
loadingToast.clear(); // 异常时关闭
this.isPaying = false;
return;
}
} else {
Toast.fail(res.message || "授权失败");
this.getData();
loadingToast.clear(); // 异常时关闭
this.isPaying = false;
}
}).catch((err) => {
console.error("getPayAuthCode error", err);
Toast.fail("获取用户信息异常,请稍后重试");
this.getData();
loadingToast.clear(); // 异常时关闭
this.isPaying = false;
});
},
retryPrePayWithDelay(requestData, retries) {
return new Promise((resolve, reject) => {
const doRequest = (attempt) => {
prePay(requestData)
.then(response => {
resolve(response);
})
.catch(error => {
if (attempt < retries) {
console.warn(`prePay请求失败正在进行第${attempt}次重试`);
setTimeout(() => doRequest(attempt + 1), 1000); // 1秒后重试
} else {
reject(error);
}
});
};
doRequest(0);
});
},
// 清除 URL 中的 code
clearCodeFromUrl() {
const urlParams = new URLSearchParams(window.location.search);
urlParams.delete('code');
const newSearch = urlParams.toString() ? '?' + urlParams.toString() : '';
const cleanUrl = window.location.origin + window.location.pathname + window.location.hash.split('?')[0] + newSearch;
window.history.replaceState(null, '', cleanUrl);
},
handleWechatPay() {
console.log("this.params", this.params);
apiUnifiedOrder(this.params).then((ref) => {
if (ref.code === 200) {
//调用微信支付
try {
const data = ref.data;
// 兼容不同字段名
let payParams = {
appId: data.appid || data.appId,
timeStamp: data.timeStamp || data.timestamp,
nonceStr: data.nonce_str || data.nonceStr,
package: data.package || `prepay_id=${data.prepay_id || data.prepayId}`,
signType: 'MD5',
paySign: data.paySign
};
console.log('支付参数:', payParams);
// 查询参数
let queryParams = {
outTradeNo: data.outTradeNo
};
console.log('订单号:', queryParams.outTradeNo);
// 验证必要参数
const requiredParams = ['appId', 'timeStamp', 'nonceStr', 'package', 'paySign'];
for (let param of requiredParams) {
if (!payParams[param]) {
Toast(`缺少支付参数: ${param}`);
this.loading = false;
return;
}
}
if (typeof WeixinJSBridge == "undefined") {
Toast("请在微信中打开页面进行支付");
this.loading = false;
return;
}
// 调起微信支付
WeixinJSBridge.invoke('getBrandWCPayRequest', payParams, (res) => {
console.log('微信支付返回:', res);
console.log("订单号:", queryParams.outTradeNo);
if (res.err_msg && res.err_msg.indexOf('ok') > -1) {
// ✅ 支付成功,启动轮询
this.startPaymentPolling(queryParams); // 调用组件方法
} else if (res.err_msg && res.err_msg.indexOf('cancel') > -1) {
Toast("用户取消支付");
} else {
Toast("支付失败: " + (res.err_msg || '未知错误'));
}
this.loading = false;
});
} catch (error) {
console.error('支付调用异常:', error);
Toast("支付调用失败");
this.loading = false;
}
}
}).catch((err) => {
Toast("通讯异常,请稍后重试");
});
},
// 独立方法:启动轮询查询支付结果
startPaymentPolling(queryParams) {
// 防止重复启动
if (this.pollTimer) {
clearInterval(this.pollTimer);
this.pollTimer = null;
}
const toast = Toast.loading({
forbidClick: true,
message: '正在查询支付结果...',
loadingType: 'circular',
duration: 0
});
let pollCount = 0;
const maxPollCount = 15; // 最多 30 秒15次 × 2秒
this.pollTimer = setInterval(() => {
pollCount++;
console.log(`${pollCount} 次轮询查询支付状态...`);
apiOpWxQuery(queryParams)
.then((res) => {
console.log('查询响应:', res);
if (res.code === 200) {
const data = res.data;
const state = data.trade_state;
if (state === 'SUCCESS') {
// ✅ 支付成功
clearInterval(this.pollTimer);
this.pollTimer = null;
toast.clear();
Toast.success("支付成功");
// 设置支付状态为完成
this.isPaying = false;
// 清理缓存
sessionStorage.removeItem('cachedPatientId');
sessionStorage.removeItem('cachedSerialFlowNo');
// 先跳转再刷新
this.$router.replace({ path: "/Member_wdzd" });
// 确保路由跳转完成后刷新页面
this.$nextTick(() => {
window.location.reload();
});
sessionStorage.removeItem("yuyue");
} else if (state === 'NOTPAY') {
// 继续等待
console.log('用户尚未支付');
} else {
// 其他失败状态
clearInterval(this.pollTimer);
this.pollTimer = null;
toast.clear();
Toast.fail("支付失败:" + (data.trade_state_desc || state));
}
} else {
console.warn('查询接口异常:', res);
}
})
.catch((error) => {
console.error('查询请求失败:', error);
})
.finally(() => {
if (pollCount >= maxPollCount) {
if (this.pollTimer) {
clearInterval(this.pollTimer);
this.pollTimer = null;
}
toast.clear();
Toast.fail("支付超时,请稍后查看订单状态");
}
});
}, 2000);
// 总超时兜底30秒
setTimeout(() => {
if (this.pollTimer) {
clearInterval(this.pollTimer);
this.pollTimer = null;
}
if (toast) {
toast.clear();
}
}, maxPollCount * 2000);
},
}
}
</script>
<style scoped lang="scss">
/* 全局样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, sans-serif;
background-color: #f5f5f5;
color: #333;
line-height: 1.6;
}
.app-container {
padding-bottom: 100px;
}
/* 背景层 */
.bj {
background: #f5f5f5;
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: -1;
}
/* 标题样式 */
.left-label {
display: flex;
align-items: center;
padding: 15px 15px 10px;
font-size: 0.45rem;
font-weight: bold;
}
.label-color {
width: 4px;
height: 16px;
background-color: #166bcc;
margin-right: 8px;
border-radius: 2px;
}
/* 卡片样式 */
.card {
background: #fff;
border-radius: 8px;
margin: 0 12px 15px;
padding: 12px 15px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
}
/* 单元格样式 */
.cell {
display: flex;
justify-content: space-between;
padding: 12px 0;
border-bottom: 1px solid #f5f5f5;
}
.cell:last-child {
border-bottom: none;
}
.cell_title {
font-size: 0.35rem;
color: #666;
}
.cell_value {
font-size: 0.35rem;
color: #333;
text-align: right;
font-weight: 500;
}
/* 支付方式样式 */
.pay_type_list {
display: flex;
align-items: center;
}
/* 统一图片容器宽度,确保图片和文字都左对齐 */
.pay_type_list>div:first-child {
display: flex;
justify-content: flex-start;
width: 1.8rem;
/* 调整为适应1.2rem图片的容器宽度 */
margin-right: 0.3rem;
}
.pay_type_list>div:last-child {
font-size: 0.4rem;
font-weight: 500;
}
/* 进一步放大图片扩大0.5倍即原来的1.5倍) */
.pay-image {
width: 0.9rem;
height: 0.9rem;
}
.medical-insurance-image {
width: 1.2rem;
height: 1.2rem;
object-fit: contain;
}
.pay-item-label {
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
width: 100%;
}
/* 按钮样式 */
.btn {
position: fixed;
// bottom: 30px;
margin-top: 1rem;
left: 5%;
width: 90%;
height: 1rem;
background-color: #166bcc;
color: white;
border: none;
border-radius: 10px;
font-size: 0.4rem;
text-align: center;
}
</style>