Files
gzh/src/views/Ghxq.vue

855 lines
26 KiB
Vue
Raw Normal View History

2026-01-06 15:03:14 +08:00
<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:48-00:01之间这个时间段内禁止充值
const now = new Date();
const hours = now.getHours();
const minutes = now.getMinutes();
if ((hours === 23 && minutes >= 48) || (hours === 0 && minutes <= 1)) {
Toast({
message: '当前时间段(23:48-00:01)正在对账暂不支持支付操作请0点过后再试',
duration: 5000
});
return;
2026-01-06 15:03:14 +08:00
}
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>