Files
xk-client-wx/subPackages/register/components/RegisterConfirmStep.vue
2026-07-30 07:37:29 +08:00

961 lines
24 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" style="padding-bottom: 100rpx">
<!-- #ifdef MP-WEIXIN -->
<page-container
:if="pageBackGuardShow"
:overlay="false"
@leave="onPageBackGuardLeave"
/>
<!-- #endif -->
<!-- 预约信息 -->
<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>
<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="bottom-spacer" aria-hidden="true"></view>
<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 class="notice-link" @click="show=true">预约挂号须知</text> </view>
</view>
<view class="footer-btns">
<button class="btn-prev" @click="onPrevStep">上一步</button>
<button class="btn" @click="handleRegisterClick">确认挂号</button>
</view>
</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,
service
} from '@/request/api/api';
import { createRoomByDoctorIdApi, sendToUserApi } from '@/request/api/im';
import { checkDev } from '@/utils/utils';
import { genOrderApi } from '@/request/api/order';
import request from '@/request/api/request';
import {
CLINIC_POST_REGISTER_STORAGE_KEY,
resolveFollowUpDrugsFromRInfo,
} from '@/utils/clinicFollowUpAssistantIm';
import { tryCreateSpecialPrescriptionPatientRecord } from '@/request/api/specialPrescription';
export default {
name: 'RegisterConfirmStep',
props: {
registerId: {
type: [String, Number],
default: '',
},
urlDoctorId: {
type: String,
default: '',
},
rInfoProp: {
type: Object,
default: () => ({}),
},
},
data() {
return {
show: false,
showConfirm: false,
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 });
this.bindSpecialPrescriptionRecord();
} else {
this.syncPageBackGuard();
}
},
registerId: {
immediate: true,
handler(val) {
if (val) this.bootstrapFromProps();
},
},
},
mounted() {
this.toAbout();
const uid = uni.getStorageSync('user_id');
this.currentUserId = 'user-' + (uid !== '' && uid !== undefined && uid !== null ? uid : '2512');
},
methods: {
bootstrapFromProps() {
this.register_id = this.registerId;
this.rInfo = { ...(this.rInfoProp || {}) };
this._urlDoctorId = this.urlDoctorId ? this.stripDoctorPrefix(this.urlDoctorId) : '';
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();
}
},
handleBackPress() {
if (this.closeTopOverlayIfAny()) {
return true;
}
if (this.registList && this.registList.is_pay == 1) {
return false;
}
this.promptExitBeforeLeave();
return true;
},
onPrevStep() {
this.$emit('prev-step');
},
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 });
},
async bindSpecialPrescriptionRecord() {
if (!this.register_id) return;
await tryCreateSpecialPrescriptionPatientRecord(this.register_id);
},
promptExitBeforeLeave({ onStay, onExit } = {}) {
uni.showActionSheet({
itemList: ['返回上一步', '继续支付', '退出'],
success: (res) => {
if (res.tapIndex === 0) {
this.$emit('prev-step');
} else if (res.tapIndex === 1) {
if (onStay) {
onStay();
} else {
this.syncPageBackGuard();
}
} else if (res.tapIndex === 2) {
if (onExit) {
onExit();
} else {
this.safeNavigateBack();
}
}
},
fail: () => {
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;
},
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);
}
},
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-', '');
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;
}
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')
};
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
});
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
});
} else {
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
});
}
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() {
const 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) => {
if (res.data.errcode == 0) {
this.content = res.data.data.content;
}
});
},
},
};
</script>
<style lang="scss" scoped>
.container {
width: 750rpx;
box-sizing: border-box;
padding-top: 32rpx;
padding-bottom: calc(320rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(320rpx + env(safe-area-inset-bottom));
background: #F8FAFC;
.bottom-spacer {
height: 120rpx;
flex-shrink: 0;
}
.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 48rpx;
.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;
right: 0;
bottom: 0;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
background-color: #FFFFFF;
border-top: 2rpx solid #F1F5F9;
z-index: 100;
.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;
.notice-link {
color: #4175EE;
}
}
}
.footer-btns {
display: flex;
align-items: center;
gap: 20rpx;
margin: 20rpx auto;
width: 686rpx;
}
.btn-prev {
flex-shrink: 0;
width: 180rpx;
height: 86rpx;
line-height: 86rpx;
text-align: center;
font-size: 28rpx;
color: #4175EE;
background: #FFFFFF;
border: 2rpx solid #4175EE;
border-radius: 64rpx;
padding: 0;
margin: 0;
&::after {
border: none;
}
}
.btn {
text-align: center;
flex: 1;
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: 0;
}
.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;
.cancel-btn,
.agree-btn {
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>