Files
xk-client-wx/subPackages/register/register.vue
2026-06-24 10:28:47 +08:00

922 lines
23 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="container safe-area-inset-bottom">
<!-- #ifdef MP-WEIXIN -->
<page-container
:show="pageBackGuardShow"
:overlay="false"
@leave="onPageBackGuardLeave"
/>
<!-- #endif -->
<MessageNotification />
<!-- 预约信息 -->
<view class="title">
预约信息
</view>
<view class="card">
<view class="item">
<view class="aapoint">
预约诊所
</view>
<view class="detail">
{{registList.store}}
</view>
</view>
<view class="item">
<view class="aapoint">
预约科室
</view>
<view class="detail">
{{registList.depart}}
</view>
</view>
<view class="item">
<view class="aapoint">
预约医生
</view>
<view class="detail">
{{registList.doctor}}
</view>
</view>
<view class="item">
<view class="aapoint">
预约序号
</view>
<view class="detail">
{{registList.order_number}}
</view>
</view>
<view class="item">
<view class="aapoint">
预计就诊时间
</view>
<view class="detail">
{{registList.created_at}}
</view>
</view>
<view class="item">
<view class="aapoint">
挂号金额
</view>
<view class="detail active">
{{registList.price}}
</view>
</view>
<view class="item">
<view class="aapoint">
挂号类型
</view>
<view class="detail">
{{registerType}}
</view>
</view>
</view>
<!-- 就诊人 -->
<view class="title">
就诊人
</view>
<view class="info">
<view class="name_edit">
<view class="name">
{{registList.patient}}
</view>
<view class="word_num">
{{registList.order_number}}
</view>
</view>
<!-- replace(/(\w{6})\w*(\w{4})/,'$1********$2') -->
<view class="name_edit">
<view class="name">
身份证号
</view>
<view class="word_num" v-if="registList.idcard">
{{registList.idcard_tm || registList.idcard}}
</view>
</view>
<view class="name_edit">
<view class="name">
手机号
</view>
<view class="word_num" v-if="registList.mobile">
{{registList.mobile_tm || registList.mobile}}
</view>
</view>
</view>
<!-- 须知 -->
<u-popup v-model="show" :safe-area-inset-bottom="true" mode="bottom" height="68%" border-radius="20"
:closeable="true" z-index="20000">
<view class="agreen">
<u-parse :html="content"></u-parse>
</view>
</u-popup>
<!-- 未勾选须知时的确认弹窗 -->
<u-popup v-model="showConfirm" mode="center" border-radius="24" width="600" z-index="10075">
<view class="confirm-modal">
<view class="confirm-title">预约挂号须知</view>
<view class="confirm-desc">
请您在使用前仔细阅读并同意
<text class="link" @click="show=true">预约挂号须知</text>
</view>
<view class="confirm-footer">
<button class="cancel-btn" @click="showConfirm = false">不同意</button>
<button class="agree-btn" @click="agreeAndRegister">同意并挂号</button>
</view>
</view>
</u-popup>
<!-- 底部 -->
<view class="footer">
<block v-if="registList.is_pay==0">
<!-- 须知 -->
<view class="allknow">
<u-icon size="40" :color="form['check']?'#2979FF':'#999999'" name="checkmark-circle-fill"
@click="form['check']=!form['check']"></u-icon>
<view class="isknow">我已阅读并同意 <text @click="show=true">预约挂号须知</text> </view>
</view>
<!-- 按钮 -->
<button class="btn" @click="handleRegisterClick">确认挂号
</button>
</block>
<view class="gh_" v-if="registList.is_pay==1" @click="toHome">
<button class="btn">您已挂号成功请等待医生接诊
</button>
</view>
</view>
<u-toast ref="uToast" />
</view>
</template>
<script>
import {
registeInfo,
registePay,
registe,
service
} from '../../request/api/api';
import {createRoomByDoctorIdApi, sendToUserApi} from "@/request/api/im";
import {checkDev} from "@/utils/utils";
import {genOrderApi, saveRegisterInfoApi} from "@/request/api/order";
import request from "@/request/api/request";
import {
CLINIC_POST_REGISTER_STORAGE_KEY,
resolveFollowUpDrugsFromRInfo,
} from '@/utils/clinicFollowUpAssistantIm';
export default {
data() {
return {
show: false,
showConfirm: false,
know: "",
form: {
check: false
},
register_id: "",
currentUserId: "",
currentDoctorId: "",
registList: [],
content: `预约挂号须知`,
flag: true,
registerType: '线下就诊',
registerMap: {
0: '挂号',
1: '咨询',
2: '购药',
3: '复诊',
},
rInfo: {},
_urlDoctorId: '',
_getInfoPromise: null,
pageBackGuardShow: false,
_suppressPageBackGuardLeave: false,
}
},
computed: {
shouldEnablePageBackGuard() {
return !(this.registList && this.registList.is_pay == 1);
},
},
watch: {
'registList.is_pay'(val) {
if (val == 1) {
this.setPageBackGuardShow(false, { suppressLeave: true });
} else {
this.syncPageBackGuard();
}
},
},
onLoad(e) {
const uid = uni.getStorageSync('user_id');
this.currentUserId = 'user-' + (uid !== '' && uid !== undefined && uid !== null ? uid : '2512');
this.register_id = e.id;
try {
this.rInfo = e.r_info ? (typeof e.r_info === 'string' ? JSON.parse(decodeURIComponent(e.r_info)) : e.r_info) : {};
} catch (err) {
console.error('register onLoad r_info parse', err);
this.rInfo = {};
}
const urlRaw = e.doctor_id && String(e.doctor_id).trim() !== '' ? String(e.doctor_id) : '';
this._urlDoctorId = urlRaw ? this.stripDoctorPrefix(urlRaw) : '';
let initialDoc = '';
if (this._urlDoctorId) initialDoc = this._urlDoctorId;
else if (this.rInfo && (this.rInfo.doctor_id != null && this.rInfo.doctor_id !== '')) {
initialDoc = this.stripDoctorPrefix(this.rInfo.doctor_id);
} else if (this.rInfo && (this.rInfo.doctorId != null && this.rInfo.doctorId !== '')) {
initialDoc = this.stripDoctorPrefix(this.rInfo.doctorId);
}
this.currentDoctorId = initialDoc ? ('doctor-' + initialDoc) : '';
uni.setStorageSync('register_id', this.register_id);
if (this.register_id) {
this._getInfoPromise = this.getInfo();
this._getInfoPromise.then(() => this.ensureDoctorAfterLoad());
} else {
this.ensureDoctorAfterLoad();
this.syncPageBackGuard();
}
},
onBackPress() {
if (this.closeTopOverlayIfAny()) {
return true;
}
if (this.registList && this.registList.is_pay == 1) {
return false;
}
this.promptExitBeforeLeave();
return true;
},
methods: {
handleRegisterClick() {
if (!this.form.check) {
this.showConfirm = true;
return;
}
this.next();
},
agreeAndRegister() {
this.form.check = true;
this.showConfirm = false;
this.next();
},
closeTopOverlayIfAny() {
if (this.show) {
this.show = false;
return true;
}
if (this.showConfirm) {
this.showConfirm = false;
return true;
}
return false;
},
setPageBackGuardShow(show, { suppressLeave = false } = {}) {
// #ifdef MP-WEIXIN
if (suppressLeave) this._suppressPageBackGuardLeave = true;
this.pageBackGuardShow = !!show;
// #endif
},
syncPageBackGuard() {
// #ifdef MP-WEIXIN
this.$nextTick(() => {
this.setPageBackGuardShow(this.shouldEnablePageBackGuard, {
suppressLeave: !this.shouldEnablePageBackGuard,
});
});
// #endif
},
safeNavigateBack() {
// #ifdef MP-WEIXIN
this.setPageBackGuardShow(false, { suppressLeave: true });
setTimeout(() => {
uni.navigateBack();
}, 16);
// #endif
// #ifndef MP-WEIXIN
uni.navigateBack();
// #endif
},
onPageBackGuardLeave() {
// #ifdef MP-WEIXIN
if (this._suppressPageBackGuardLeave) {
this._suppressPageBackGuardLeave = false;
return;
}
if (this.closeTopOverlayIfAny()) {
this.$nextTick(() => {
this.setPageBackGuardShow(this.shouldEnablePageBackGuard, { suppressLeave: true });
});
return;
}
if (this.registList && this.registList.is_pay == 1) {
this.setPageBackGuardShow(false, { suppressLeave: true });
setTimeout(() => {
uni.navigateBack();
}, 16);
return;
}
this.setPageBackGuardShow(false);
this.promptExitBeforeLeave({
onStay: () => {
this.syncPageBackGuard();
},
onExit: () => {
this.safeNavigateBack();
},
});
// #endif
},
disableBackGuardBeforeLeave() {
this.setPageBackGuardShow(false, { suppressLeave: true });
},
promptExitBeforeLeave({ onStay, onExit } = {}) {
uni.showModal({
title: '提示',
content: '当前未挂号成功,是否继续还是退出?',
cancelText: '继续',
confirmText: '退出',
success: (res) => {
if (res.confirm) {
if (onExit) {
onExit();
} else {
this.safeNavigateBack();
}
} else if (onStay) {
onStay();
} else {
this.syncPageBackGuard();
}
},
});
},
stripDoctorPrefix(raw) {
if (raw === null || raw === undefined || raw === '') return '';
return String(raw).replace(/^doctor-/, '').trim();
},
applyDoctorNumericId(id) {
const n = this.stripDoctorPrefix(id);
if (!n) return;
this.currentDoctorId = 'doctor-' + n;
},
/** 同步可用的医生数字 id详情 > rInfo > URL > 当前 currentDoctorId */
getDoctorNumericIdFromLocalSync() {
const rl = this.registList;
if (rl && typeof rl === 'object' && !Array.isArray(rl) && rl.service_user_id) {
const s = this.stripDoctorPrefix(rl.service_user_id);
if (s) return s;
}
if (this.rInfo) {
const d = this.rInfo.doctor_id != null && this.rInfo.doctor_id !== '' ? this.rInfo.doctor_id : this.rInfo.doctorId;
if (d != null && d !== '') {
const s = this.stripDoctorPrefix(d);
if (s) return s;
}
}
if (this._urlDoctorId) {
const s = this.stripDoctorPrefix(this._urlDoctorId);
if (s) return s;
}
const cur = this.stripDoctorPrefix(this.currentDoctorId);
if (cur) return cur;
return '';
},
async fetchDefaultDoctorIdApi() {
const storeId = uni.getStorageSync('store_id') || '11001';
try {
const res = await request('/xkApi/online-consultation/get-default-doctor-id', {
method: 'GET',
data: { store_id: storeId }
}, 0);
const id = res.data?.result?.doctor_id ?? res.data?.doctor_id;
return id ? this.stripDoctorPrefix(id) : '';
} catch (err) {
console.error('get-default-doctor-id failed', err);
return '';
}
},
async resolveDoctorNumericIdForIm() {
let id = this.getDoctorNumericIdFromLocalSync();
if (!id) id = await this.fetchDefaultDoctorIdApi();
if (id) this.applyDoctorNumericId(id);
return id || '';
},
async ensureDoctorAfterLoad() {
const existing = this.getDoctorNumericIdFromLocalSync();
if (existing) {
this.applyDoctorNumericId(existing);
return;
}
const id = await this.fetchDefaultDoctorIdApi();
if (id) this.applyDoctorNumericId(id);
},
async waitForRegisterDetail() {
if (!this.register_id) return;
try {
if (this._getInfoPromise) await this._getInfoPromise;
else await this.getInfo();
} catch (err) {
console.error('waitForRegisterDetail', err);
}
},
/** 诊所复诊(2)且存在复诊药:支付后先走独立用药确认页,再进聊天 */
needsClinicPostRegisterDrugPage() {
if (!this.registList || this.registList.register_type !== 2) return false;
const { registerInfoId, drugsPayload } = resolveFollowUpDrugsFromRInfo(this.rInfo);
return !!(registerInfoId && drugsPayload.length);
},
// 挂号信息
getInfo() {
const that = this;
if (!this.register_id) return Promise.resolve();
const p = registeInfo({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || '11001',
register_id: that.register_id
}
}).then((res) => {
if (res.data.errcode == 0 && res.data.data.list && res.data.data.list[0]) {
that.registList = res.data.data.list[0];
that.registerType = that.registerMap[that.registList.register_type];
if (that.registList.service_user_id) {
that.applyDoctorNumericId(that.registList.service_user_id);
}
}
that.syncPageBackGuard();
});
this._getInfoPromise = p;
return p;
},
// 处理在线复诊支付后的流程
async handleOnlineConsultationAfterPayment() {
const that = this;
try {
const doctorId = await that.resolveDoctorNumericIdForIm();
if (!doctorId) {
uni.hideLoading();
uni.showToast({
title: '无法获取医生信息,请稍后重试',
icon: 'none'
});
return;
}
const doctorPrefixed = 'doctor-' + doctorId;
that.currentDoctorId = doctorPrefixed;
const patientId = that.currentUserId.replace('user-', '');
// 3. 创建或获取聊天房间
// type=2 表示诊所在线复诊type=3 表示药店在线复诊/购药
const roomType = (that.registList && that.registList.register_type === 2) ? 2 : 3;
const roomRes = await request('/xkApi/online-consultation/get-or-create-chat-room', {
method: 'POST',
data: {
doctor_id: doctorId,
patient_id: patientId,
type: roomType
}
}, 1);
const roomId = roomRes.data?.result?.room_id || roomRes.data?.room_id;
if (!roomId) {
uni.hideLoading();
uni.showToast({
title: '创建聊天房间失败',
icon: 'none'
});
return;
}
// 4. 仅药店模式(store_type='1')发送患者就诊经历卡片
const storeType = uni.getStorageSync('store_type')
if (storeType === '1' || storeType === 1) {
const patientExperienceData = {
symptom_description: that.rInfo.illness_info || '',
has_visited: that.rInfo.has_visited || '0',
has_used_drug: that.rInfo.has_used_drug || '0',
drug_name: that.rInfo.drug_name || '',
illness_info: that.rInfo.illness_info || '',
doctor_id: doctorId,
user_id: patientId,
register_id: that.register_id,
created_at: new Date().toLocaleString('zh-CN')
};
// 5. 发送患者就诊经历消息消息类型11
await sendToUserApi({
room_id: roomId,
sender_user_id: `user-${patientId}`,
receiver_user_id: `doctor-${doctorId}`,
message_type: 11, // 患者就诊经历卡片
message_content: JSON.stringify(patientExperienceData),
duration: 0
});
} else {
// 诊所模式(store_type='0')发送挂号信息卡片message_type=10
await sendToUserApi({
room_id: roomId,
sender_user_id: `user-${patientId}`,
receiver_user_id: `doctor-${doctorId}`,
message_type: 10, // 挂号信息卡片
message_content: JSON.stringify({
id: that.register_id,
}),
duration: 0
});
}
// 6. 生成订单(如果需要);诊所复诊有药时先 redirect 用药确认页,助理卡改在页内提交后发
const chatUrl = `/subPackages/chat/chat?user_id=${encodeURIComponent(doctorPrefixed)}&room_id=${roomId}&register_id=${that.register_id}`;
const goAfterGenOrder = () => {
if (that.needsClinicPostRegisterDrugPage()) {
const { registerInfoId, drugsPayload } = resolveFollowUpDrugsFromRInfo(that.rInfo);
uni.setStorageSync(
CLINIC_POST_REGISTER_STORAGE_KEY,
JSON.stringify({
register_info_id: registerInfoId,
register_id: that.register_id,
drugs: drugsPayload,
question: '您是否曾使用过此类药品?',
room_id: roomId,
doctor_id: doctorId,
patient_id: patientId,
doctor_prefixed: doctorPrefixed,
}),
);
uni.hideLoading();
that.disableBackGuardBeforeLeave();
uni.redirectTo({
url: '/subPackages/follow-up/medication-info?mode=clinic_post_register',
});
return;
}
uni.hideLoading();
that.disableBackGuardBeforeLeave();
uni.navigateTo({ url: chatUrl });
};
genOrderApi({
register_id: that.register_id,
register_info: typeof that.rInfo === 'string' ? that.rInfo : JSON.stringify(that.rInfo),
}).then(() => {
goAfterGenOrder();
}).catch((error) => {
console.error('生成订单失败:', error);
goAfterGenOrder();
});
} catch (error) {
uni.hideLoading();
console.error('处理在线复诊流程失败:', error);
uni.showToast({
title: '处理失败,请重试',
icon: 'none'
});
}
},
// 确认挂号
next() {
let that = this
if (this.flag) {
this.flag = false
// 获取支付配置
registePay({
method: "post",
data: {
register_id: this.register_id,
store_id: uni.getStorageSync('store_id') || '11001',
}
}).then(async (res) => {
uni.showLoading({
title: "加载中"
});
if (checkDev('open-im') && that.registList && that.registList.register_type === 1) {
createRoomByDoctorIdApi({
doctor_id: that.currentDoctorId,
type: 1
}).then(result => {
sendToUserApi({
room_id: result.data.result.room_id,
sender_user_id: `user-${that.currentUserId}`,
receiver_user_id: `doctor-${that.currentDoctorId}`,
message_type: 10,
message_content: JSON.stringify({
id: that.register_id,
}),
duration: 0
})
})
}
if (res.data.data.is_paid == 1) {
await that.waitForRegisterDetail();
if (that.registList && (that.registList.register_type === 2 || that.registList.register_type === 3)) {
await that.handleOnlineConsultationAfterPayment();
return;
}
setTimeout(() => {
that.disableBackGuardBeforeLeave();
uni.navigateTo({
url: `/subPackages/register/register-success?id=${that.register_id}&register_type=${that.registList.register_type}`,
})
uni.hideLoading();
}, 800)
return
}
uni.requestPayment({
provider: res.data.data.appId,
timeStamp: res.data.data.timestamp,
nonceStr: res.data.data.nonceStr,
package: res.data.data.package,
signType: res.data.data.signType,
paySign: res.data.data.paySign,
success: async () => {
await that.waitForRegisterDetail();
that.flag = false;
if (that.registList && (that.registList.register_type === 2 || that.registList.register_type === 3)) {
uni.showLoading({ title: '加载中' });
await that.handleOnlineConsultationAfterPayment();
return;
}
setTimeout(() => {
that.disableBackGuardBeforeLeave();
uni.navigateTo({
url: '/subPackages/register/register-success?id=' + that.register_id
})
uni.hideLoading();
}, 300)
},
fail: function(err) {
that.flag = false
uni.hideLoading();
setTimeout(() => {
that.$refs.uToast.show({
title: (err && err.errMsg) || '支付失败',
type: 'default',
icon: false
})
}, 300)
}
})
}).catch((err) => {
console.error('registePay', err);
that.flag = true;
uni.hideLoading();
})
}
},
toHome() {
uni.switchTab({
url: '/pages/home/home'
})
},
// 预约挂号须知
toAbout() {
service({
method: "get",
data: {
store_id: uni.getStorageSync('store_id') || '11001',
type: 3
}
}).then((res) => {
// console.log(res, 'gh');
if (res.data.errcode == 0) {
this.content = res.data.data.content
}
})
}
},
mounted() {
this.toAbout()
}
}
</script>
<style lang="scss" scoped>
.container {
width: 750rpx;
max-height: 100%;
min-height: 100vh;
padding-top: 32rpx;
background: #F8FAFC;
.title {
width: 750rpx;
height: 80rpx;
margin: 0 auto;
font-size: 32rpx;
font-family: PingFang SC-Bold, PingFang SC;
font-weight: bold;
color: #666666;
line-height: 80rpx;
padding-left: 32rpx;
}
.card {
width: 750rpx;
background: #FFFFFF;
border-radius: 8rpx;
padding: 32rpx;
margin: 24rpx auto 32rpx;
.item:first-child {
margin: 0 0 48rpx;
}
.item {
display: flex;
justify-content: space-between;
height: 40rpx;
font-family: PingFang SC-Regular, PingFang SC;
line-height: 33rpx;
margin: 48rpx 0 0;
.aapoint {
font-size: 28rpx;
font-weight: 400;
color: #666666;
}
.detail {
font-size: 28rpx;
font-weight: bold;
color: #232323;
}
.active {
color: #F44336;
}
}
}
.info {
width: 750rpx;
background: #FFFFFF;
padding: 32rpx;
border-radius: 8rpx;
margin: 10rpx auto 40rpx;
.name_edit:first-child {
margin-top: 0;
}
.name_edit {
margin-top: 48rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
line-height: 33rpx;
display: flex;
justify-content: space-between;
.name {
color: #475569;
}
.word_num {
font-size: 30rpx;
font-weight: 600;
color: #1E293B;
}
}
}
.footer {
width: 750rpx;
display: flex;
flex-direction: column;
padding: 0 32rpx;
position: fixed;
left: 0;
bottom: env(safe-area-inset-bottom);
z-index: 10;
.allknow {
display: flex;
align-items: center;
width: 710rpx;
height: 40rpx;
padding: 0 32rpx;
margin: 10rpx auto;
.isknow {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
margin-left: 8rpx;
text {
color: #4175EE;
}
}
}
.btn {
text-align: center;
width: 686rpx;
height: 86rpx;
line-height: 86rpx;
font-size: 32rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #FFFFFF;
background: #4175EE;
border-radius: 64rpx;
margin: 20rpx auto;
}
.bg {
width: 688rpx;
height: 88rpx;
background: #8ABAFF;
line-height: 88rpx;
font-size: 32rpx;
text-align: center;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 400;
color: #FFFFFF;
border-radius: 64rpx;
}
.gh_ {
margin-bottom: 30rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
}
}
.agreen {
padding: 60rpx 50rpx 20rpx;
}
.confirm-modal {
padding: 40rpx 30rpx;
background: #fff;
.confirm-title {
font-size: 34rpx;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 30rpx;
}
.confirm-desc {
font-size: 28rpx;
color: #666;
line-height: 1.6;
margin-bottom: 40rpx;
text-align: center;
.link {
color: #2979FF;
}
}
.confirm-footer {
display: flex;
justify-content: space-between;
gap: 20rpx;
button {
flex: 1;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
font-size: 30rpx;
border: none;
&::after {
border: none;
}
}
.cancel-btn {
background: #F5F7FA;
color: #909399;
}
.agree-btn {
background: #4175EE;
color: #FFFFFF;
}
}
}
}
</style>