Files
gzh/src/views/Ghxq.vue
2026-02-06 14:37:09 +08:00

855 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 />
<div class="bj"></div>
<!-- <div class="lxr" @click="handleCardSelect">-->
<div class="lxr">
<div class="lxrr" v-if="card && card.id">
<h2>
{{ card.name }}
<p>卡号{{ card.cardNo }}</p>
</h2>
<!-- <h3>-->
<!-- <i class="van-icon van-icon-arrow van-cell__right-icon"></i>-->
<!-- </h3>-->
</div>
<van-contact-card type="add" add-text="请选择就诊人后操作" v-else />
</div>
<div class="qrxx">
<ul>
<li>
<h2>医院名称</h2>
<h3>武警宁夏总队医院</h3>
</li>
<li>
<h2>就诊科室</h2>
<h3>{{ yuyue.DeptName }}</h3>
</li>
<li>
<h2>医生名称</h2>
<h3>{{ yuyue.DoctorName }}</h3>
</li>
<li>
<h2>排队号码</h2>
<h3>{{ yuyue.Number }}</h3>
</li>
<li>
<h6 style="color: red;">号码无关排队人数请提前半小时到场等待</h6>
</li>
<li>
<h2>就诊时间</h2>
<h3>
{{ yuyue.t }} {{ yuyue.slotInfo?.formattedStartTime }}-{{ yuyue.slotInfo?.formattedEndTime }} {{ yuyue.w }}
</h3>
</li>
<li>
<h2>挂号金额</h2>
<h3>¥{{ yuyue.m }}</h3>
</li>
</ul>
</div>
<div class="card" v-if="yuyue.m != '0.00' || yuyue.m != 0">
<!-- 微信支付选项 -->
<!-- <div 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 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>
<van-button class="btn" color="#166bcc" :loading="loading" type="primary" loading-text="处理中..."
@click="onConfirm">确认挂号
</van-button>
<!--div :class="btnclass">
<div @click="onConfirm">确认挂号</div>
</div-->
</div>
</template>
<script>
// @ is an alias to /src
import {
apiUserCardList,
apiOpAppAppoint,
apiOpAppRegist,
apiOpWxQuery,
preCheck,
getPayAuthCode,
prePay,
getAccessToken
}
from "@/request/api.js";
import { Toast, Dialog } from "vant";
export default {
data() {
return {
title: "",
list: [],
httpurl: this.httpurl,
yuyue: {},
card: {},
btnclass: 'btn',
loading: false,
pollTimer: null, // 轮询定时器
radiovalue1:'微信支付', // 默认选择微信支付
provider: 'wxpay', // 支付方式 wxpay 或 alipay
isPaying: false, // 添加防止重复支付的状态
returnUrl: 'https://nxwj.btlsoln.com/nxwj-gzh/#/Ghxq', // 回调地址
globalData: { // 医保授权相关
scene: 0,
ghid: '', // 单据id
jzrid: '', // 病人id就诊卡Id
authCode: '',
payAuthCode: '',
userCardNo: '',
userName: '',
cityId: '',
hosp_out_trade_no: '',
userLongitudeLatitude: {},
// 医保授权支付相关
orderInfo: {},
// 医保支付金额相关
ybPayInfo: {},
},
accessToken: '',
};
},
mounted() {
// console.log(this.$route.meta.title);
this.title = this.$route.meta.title;
// 先从sessionStorage读取数据确保数据可用
if (!sessionStorage.getItem("yuyue")) {
this.$router.replace({
path: "/xzmz",
query: {},
});
return;
}
this.yuyue = JSON.parse(sessionStorage.getItem("yuyue"));
console.log("yuyue:",this.yuyue);
this.card = JSON.parse(sessionStorage.getItem("card"));
console.log("card:",this.card);
if (this.yuyue.DoctorName == "" || this.yuyue.DoctorName == null) {
this.yuyue.DoctorName = "由科室安排"
}
// 处理微信医保回调参数
let authCode = this.$route.query.authCode;
let retCode = this.$route.query.retCode;
// 如果路由query中没有获取到尝试从window.location.hash中解析
if (!authCode || !retCode) {
const hash = window.location.hash;
if (hash.includes('?')) {
const searchParams = new URLSearchParams(hash.split('?')[1]);
authCode = authCode || searchParams.get('authCode');
retCode = retCode || searchParams.get('retCode');
}
}
// 处理微信医保回调(现在数据已加载)
if (retCode === '0' && authCode) {
console.log("检测到微信医保回调,授权码:", authCode);
this.getPayAuth(authCode);
}
},
methods: {
// 主方法:调起微信支付
callWeChatPay(ref) {
console.log("进入callWeChatPay");
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;
}
},
// 独立方法:启动轮询查询支付结果
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.$router.replace({ path: "/Yylb" });
// 确保路由跳转完成后刷新页面
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);
},
getTime(time) {
switch (time) {
case "AM":
return "上午";
case "PM":
return "下午";
case "AL":
return "白天";
case "NT":
return "昼夜";
}
},
onConfirm() {
let _this = this;
_this.loading = true;
// console.log(_this.card)
if (!_this.card) {
Toast("请确认是否已选择就诊人");
return;
}
if( _this.yuyue.m > 0 && _this.radiovalue1 == ''){
Toast("请选择支付方式");
return;
}
// 检查当前时间是否在23:00-00:01之间这个时间段内禁止充值
const now = new Date();
const hours = now.getHours();
const minutes = now.getMinutes();
if ((hours === 23 && minutes >= 0) || (hours === 0 && minutes <= 1)) {
Toast({
message: '当前时间段(23:00-00:01)正在对账暂不支持支付操作请0点过后再试',
duration: 5000
});
return;
}
console.log("card",_this.card.cardNo);
if(_this.radiovalue1 == "微信医保支付" && _this.card.cardNo !="90120746" ){
Toast("医保支付暂未开通,请改用微信支付");
_this.loading = false;
return;
}
console.log("yuyue:",this.yuyue)
// console.log(this.card)
// let { h, k, e, d, n, t, i, c, g, r, z, m, ClinicFee, RegistrationFee,StartTime } =
// _this.yuyue;
// 把n替换为原来的空值
_this.yuyue.DoctorName = _this.yuyue.DoctorName === "由科室安排" ? "" : _this.yuyue.DoctorName;
function pad2(n) {
return n < 10 ? "0" + n : n;
}
let date = new Date();
let locationtime =
date.getFullYear().toString() +
"-" +
pad2(date.getMonth() + 1) +
"-" +
pad2(date.getDate()) +
" " +
pad2(date.getHours()) +
":" +
pad2(date.getMinutes()) +
":" +
pad2(date.getSeconds());
let formData = {
subhospitalid: "",
locktype: "1",
hisorderno: "",
scheduleid: _this.yuyue.slotTime ? (_this.yuyue.slotTime + (_this.yuyue.slotTime.split(':').length === 2 ? ':00' : '')) : '',
patientid: _this.card.cardNo,
departcode: _this.yuyue.DeptCode,
ksmc: _this.yuyue.DeptName,
name: _this.card.name,
doctorcode: _this.yuyue.DoctorCode,
ysmc: _this.yuyue.DoctorName,
partimeid: _this.yuyue.slotTime ? (_this.yuyue.slotTime + (_this.yuyue.slotTime.split(':').length === 2 ? ':00' : '')) : '',
locktime: locationtime,
mobile: "",
serialno: _this.yuyue.Number+"",
timeinterval: _this.yuyue.slotInfo.formattedStartTime + "-" + _this.yuyue.slotInfo.formattedEndTime,
registerdate: _this.yuyue.t,
clinicfee: _this.yuyue.ClinicFee,
registrationfee: _this.yuyue.RegistrationFee,
paynature: 1,
paytype: "微信",
zfamount: _this.yuyue.m,
ybzhamount: "0.00",
ybtcamount: "0.00",
yboutmsg: "",
hisopernum: "GZH",
Money: _this.yuyue.m,
};
console.log(formData);
// alert(formData.serialno+"当前的挂号为:",formData.serialno)
Dialog.confirm({
title: "提示",
message: "请确认就诊人:" + _this.card.name + "!是否提交预约",
})
.then(() => {
// on confirm
let idcard = _this.card.idNo;
if (idcard != '' || idcard != null) {
let sex = idcard.substring(16, 17)
var myDate = new Date();
var month = myDate.getMonth() + 1;
var day = myDate.getDate();
var age = myDate.getFullYear() - idcard.substring(6, 10) - 1;
if (idcard.substring(10, 12) < month || idcard.substring(10, 12) == month && idcard.substring(12, 14) <= day) {
age++;
}
if (age > 18 && _this.yuyue.DeptCode == "10010161") {
Toast("年龄大于18岁无法挂儿科");
return;
}
if ((sex % 2 != 0) && (_this.yuyue.DeptCode == "10010151" || _this.yuyue.DeptCode == "10010152")) {
Toast("男性无法挂妇产科");
return;
}
}
apiOpAppRegist(formData).then((ref) => {
console.log(ref)
if (ref.code === 200) {//响应成功后判断是否为免费的如果是免费的号源会接收到res.data.result否则接收不到。
console.log(ref.data.result);
if (ref.data.result !== undefined) {
let isOk = ref.data.result;
console.log(isOk);
if (isOk === 'success') {
Toast("挂号成功")
setTimeout(() => {
console.log("1111");
_this.$router.push({ path: "/Yylb", query: {} });
// _this.$router.push({ path: "/Yycg", query: {id:res.data,type:z} });
sessionStorage.removeItem("yuyue");
//sessionStorage.removeItem("card"); //joy
}, 300);
_this.loading = false;
} else {
Toast(isOk)
setTimeout(() => {
console.log("2222");
_this.$router.push({ path: "/Yylb", query: {} });
// _this.$router.push({ path: "/Yycg", query: {id:res.data,type:z} });
sessionStorage.removeItem("yuyue");
//sessionStorage.removeItem("card"); //joy
}, 300);
_this.loading = false;
}
} else {
//调用支付
if(_this.radiovalue1 == "微信医保支付"){
console.log("调用医保支付");
_this.handleMedicalInsurancePay();
}else if(_this.radiovalue1 == "微信支付"){
console.log("调用微信支付");
this.callWeChatPay(ref);
}
}
_this.loading = false;
}
}).catch(err => {
_this.loading = false;
Toast("通讯异常,请稍后重试");
});
return;
})
.catch(() => {
_this.btnclass == "btn"
// 防止点击取消,不显示医生名字
_this.yuyue.DoctorName = _this.yuyue.DoctorName === "" ? "由科室安排" : _this.yuyue.DoctorName;
// on cancel
});
},
// 获取访问令牌
getToken() {
getAccessToken().then(res => {
console.log(res)
this.accessToken = res.data.accessToken
}).catch(err => {
Toast(err.message)
})
},
// 处理医保支付
handleMedicalInsurancePay() {
try {
// 预检
preCheck().then(res => {
console.log("预检结果", res);
if (res.code === 200) {
// Toast.success("预检通过,正在跳转医保支付授权...");
// 确保 returnUrl 正确编码
const encodedReturnUrl = encodeURIComponent(this.returnUrl);
// 检查 authUrl 是否完整
let authUrl = res.data;
if (!authUrl) {
Toast.fail("获取授权链接失败");
return;
}
// 确保 redirectUrl 参数正确附加
if (authUrl.includes('?')) {
authUrl += '&redirectUrl=' + encodedReturnUrl;
} else {
authUrl += '?redirectUrl=' + encodedReturnUrl;
}
console.log("跳转授权链接:", authUrl);
// 使用 location.href 跳转
window.location.href = authUrl;
} else {
Toast.fail(res.message || "预检失败,请稍后重试");
this.loading = false; // 重置支付状态
}
}).catch(err => {
console.error("预检请求失败:", err);
Toast.fail("预检请求失败,请稍后重试");
this.loading = false; // 重置支付状态
});
} catch (error) {
console.error("医保支付启动失败:", error);
Toast.fail("医保支付启动失败,请稍后重试");
this.loading = false;
}
},
// 获取医保支付授权
getPayAuth(code) {
// 利用authCode调用后端接口
let params = {
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("数据解析异常,请稍后重试");
this.loading = false;
return;
}
if(responseData.user_card_no == res.data.IdNo) {
if (responseData.code === 0) {
Toast.success("获取payAuthNo用户信息成功正在发起支付");
// 保存用户信息
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 = this.yuyue.Number || '';
this.globalData.jzrid = this.card.cardNo || '';
// 授权完毕、开始预结算下单
let req = {
"userCardNo": this.globalData.userCardNo,
"userName": this.globalData.userName,
"orderTitle": "门诊挂号",
"openid": res.data.openid,
"ghid": this.globalData.ghid,
"payAuthNo": this.globalData.payAuthCode,
"jzrid": this.globalData.jzrid,
"HisCostCode": this.yuyue.HisCostCode,
"YBCostCode": this.yuyue.YBCostCode,
"YBDept": this.yuyue.YBDept,
"YBDoc": this.yuyue.YBDoc,
"DoctorName": this.yuyue.DoctorName,
"DeptName": this.yuyue.DeptName,
"ClinicFee": this.yuyue.ClinicFee,
"RegistrationFee": this.yuyue.RegistrationFee,
"TotalFee": this.yuyue.m,
"billTime":this.yuyue.t+" "+this.yuyue.slotTime+":00",
"userLongitudeLatitude": this.globalData.userLongitudeLatitude,
"accessToken": res.data.accessToken
};
prePay(req).then(res => {
console.log(res);
// 根据预结算结果处理后续流程
if (res.code === 200) {
Toast.success("医保支付成功");
setTimeout(() => {
this.$router.push({ path: "/Yylb", query: {} });
sessionStorage.removeItem("yuyue");
}, 300);
} else {
Toast.fail(res.message || "医保支付失败");
}
}).catch(err => {
Toast.fail("预结算请求失败,请稍后重试");
this.loading = false;
});
} else if (responseData.code < 0) {
Toast.fail("系统繁忙,请稍后重试");
this.loading = false;
} else if (responseData.code > 0) {
Toast.fail(responseData.message);
this.radiovalue1 = ''; // 重置支付方式选择
this.loading = false; // 重新启用支付按钮
}
} else {
Toast.fail("不是本人医保,不予办理");
this.loading = false;
return;
}
} else {
Toast.fail(res.message || "授权失败");
this.loading = false;
}
}).catch((err) => {
console.error("getPayAuthCode error", err);
Toast.fail("获取用户信息异常,请稍后重试");
this.loading = false;
});
},
// 清除 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);
},
handleCardSelect() {
this.$router.push({ path: "/Member_jzr", query: { f: 1 } });
}
,
},
};
</script>
<style scoped lang="scss">
.bj {
background: #f5f5f5;
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: -1;
}
.lxr {
.van-cell--center {
border-radius: 0.25rem;
padding: 0.25rem;
background: url(../assets/lxrbj.png) no-repeat;
background-size: 100% 100%;
background-position: center center;
color: #fff;
.van-cell__value--alone {
color: #fff;
}
.van-cell__right-icon {
color: #fff;
}
}
.van-contact-card::before {
background: none;
}
::v-deep .van-contact-card--add .van-cell__left-icon {
color: #fff;
opacity: 0.6;
}
.lxrr {
border-radius: 0.25rem;
background: url(../assets/lxrbj.png) no-repeat;
background-size: 100% 100%;
background-position: center center;
color: #fff;
padding: 0.4rem;
display: flex;
align-items: center;
justify-content: space-between;
h2 {
font-size: 0.5rem;
p {
font-size: 0.4rem;
padding-top: 0.15rem;
}
}
h3 .van-cell__right-icon {
color: #fff;
}
}
}
.qrxx ul li {
border-bottom: 1px solid #f5f5f5;
padding: 0.4rem;
background: #fff;
display: flex;
color: #333;
h2 {
font-size: 0.375rem;
width: 2.5rem;
}
h3 {
width: calc(100% - 2.5rem);
font-size: 0.375rem;
}
}
.btn {
width: 90%;
position: relative;
margin: 5%;
// margin-top: 3rem;
border-radius: 0.375rem;
}
// 支付方式
/* 卡片样式 */
.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%;
}
</style>