各角色工作台支持批量保存常用入口和业务员看板;AI 简报改成单行横幅避免锁死滑动;仓库/提现/对账等列表统一下拉刷新,并补代付码弹窗。 Co-authored-by: Cursor <cursoragent@cursor.com>
268 lines
6.0 KiB
Vue
268 lines
6.0 KiB
Vue
<template>
|
||
<view>
|
||
<!-- 页面级返回拦截:pageGuard 与 show 拆开,关弹窗后先离场再卸,避免锁死页面滑动 -->
|
||
<!-- #ifdef MP-WEIXIN -->
|
||
<page-container
|
||
v-if="pageGuard"
|
||
:show="show"
|
||
:overlay="false"
|
||
@leave="close"
|
||
@afterleave="releasePageGuard"
|
||
/>
|
||
<!-- #endif -->
|
||
<u-popup :value="show" mode="center" border-radius="24" :mask-close-able="true" @input="onPopupInput" @close="close">
|
||
<view class="ppay">
|
||
<view class="ppay__header">
|
||
<text class="ppay__title">患者代付码</text>
|
||
<text class="ppay__close" @click="close">关闭</text>
|
||
</view>
|
||
<view class="ppay__body">
|
||
<view v-if="loading" class="ppay__loading">
|
||
<u-loading mode="circle" size="40"></u-loading>
|
||
<text class="ppay__loading-text">代付码生成中...</text>
|
||
</view>
|
||
<block v-else-if="qrcode">
|
||
<!-- 后端返回完整 data:image/png;base64 数据,长按可转发给患者 -->
|
||
<image class="ppay__qrcode" :src="qrcode" mode="aspectFit" show-menu-by-longpress></image>
|
||
<view class="ppay__meta">
|
||
<view class="ppay__meta-row">
|
||
<text class="ppay__meta-label">订单号</text>
|
||
<text class="ppay__meta-value">{{ orderNo }}</text>
|
||
</view>
|
||
<view class="ppay__meta-row">
|
||
<text class="ppay__meta-label">待支付金额</text>
|
||
<text class="ppay__meta-value ppay__meta-value--price">¥{{ totalPayPrice }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="ppay__tip">请让患者用微信「扫一扫」,进入小程序完成支付</view>
|
||
</block>
|
||
<view v-else class="ppay__error">
|
||
<text class="ppay__error-text">{{ errorText || '代付码生成失败' }}</text>
|
||
<view class="ppay__retry" @click="load">重试</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</u-popup>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
/**
|
||
* 医生端「患者代付码」弹窗
|
||
*
|
||
* 用途:本次挂号处方存在待支付订单时,医生出示小程序码给患者扫码代付
|
||
* (复用 PC 接诊台同款后端 proxy-pay-qrcode,scene=ppay={orderId},患者端解析链路零改动)。
|
||
* 读值:xk-api 形态,拦截器已返回 body → code / result / message(unwrapXkApi)
|
||
*/
|
||
import { getProxyPayQrcodeApi } from '@/api/reception.js'
|
||
import { unwrapXkApi } from '@/utils/api-response.js'
|
||
|
||
export default {
|
||
name: 'ProxyPayQrcodeModal',
|
||
props: {
|
||
/** v-model 显隐 */
|
||
value: { type: Boolean, default: false },
|
||
/** 待支付订单ID(处方关联的商品订单) */
|
||
orderId: { type: [Number, String], default: 0 },
|
||
},
|
||
data() {
|
||
return {
|
||
loading: false,
|
||
qrcode: '',
|
||
orderNo: '',
|
||
totalPayPrice: '',
|
||
errorText: '',
|
||
pageGuard: false,
|
||
releaseTimer: null,
|
||
}
|
||
},
|
||
computed: {
|
||
show: {
|
||
get() { return this.value },
|
||
set(v) { this.$emit('input', v) },
|
||
},
|
||
},
|
||
watch: {
|
||
/** 每次打开都重新生成:订单可能已被支付/取消,后端会按最新状态校验 */
|
||
value(v) {
|
||
if (v) {
|
||
this.pageGuard = true
|
||
this.load()
|
||
} else {
|
||
this.scheduleReleaseGuard()
|
||
}
|
||
},
|
||
},
|
||
beforeDestroy() {
|
||
this.clearReleaseTimer()
|
||
},
|
||
methods: {
|
||
releasePageGuard() {
|
||
this.clearReleaseTimer()
|
||
this.pageGuard = false
|
||
},
|
||
scheduleReleaseGuard() {
|
||
this.clearReleaseTimer()
|
||
this.releaseTimer = setTimeout(() => {
|
||
this.pageGuard = false
|
||
this.releaseTimer = null
|
||
}, 350)
|
||
},
|
||
clearReleaseTimer() {
|
||
if (this.releaseTimer) {
|
||
clearTimeout(this.releaseTimer)
|
||
this.releaseTimer = null
|
||
}
|
||
},
|
||
close() {
|
||
this.show = false
|
||
},
|
||
onPopupInput(v) {
|
||
this.show = v
|
||
},
|
||
/** 请求代付码(xk-api,取值 code/result/message) */
|
||
async load() {
|
||
if (!this.orderId) {
|
||
this.qrcode = ''
|
||
this.errorText = '缺少订单信息'
|
||
return
|
||
}
|
||
this.loading = true
|
||
this.qrcode = ''
|
||
this.errorText = ''
|
||
try {
|
||
const res = await getProxyPayQrcodeApi({ order_id: this.orderId })
|
||
const { ok, payload, message } = unwrapXkApi(res)
|
||
if (ok && payload && payload.qrcode) {
|
||
this.qrcode = payload.qrcode
|
||
this.orderNo = payload.order_no || ''
|
||
this.totalPayPrice = payload.total_pay_price || ''
|
||
} else {
|
||
this.errorText = message || '代付码生成失败'
|
||
}
|
||
} catch (e) {
|
||
this.errorText = (e && e.message) || '代付码生成失败'
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.ppay {
|
||
width: 600rpx;
|
||
padding: 32rpx;
|
||
box-sizing: border-box;
|
||
|
||
&__header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
&__title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #2a2e35;
|
||
}
|
||
|
||
&__close {
|
||
font-size: 26rpx;
|
||
color: #8a92a3;
|
||
padding: 8rpx;
|
||
}
|
||
|
||
&__body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
&__loading {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 60rpx 0;
|
||
}
|
||
|
||
&__loading-text {
|
||
margin-top: 16rpx;
|
||
font-size: 26rpx;
|
||
color: #8a92a3;
|
||
}
|
||
|
||
&__qrcode {
|
||
width: 400rpx;
|
||
height: 400rpx;
|
||
}
|
||
|
||
/* 订单信息卡:1px 同色系边框 + 微光(规范禁止左侧竖色条强调) */
|
||
&__meta {
|
||
width: 100%;
|
||
margin-top: 24rpx;
|
||
padding: 20rpx 24rpx;
|
||
box-sizing: border-box;
|
||
border: 1px solid rgba(0, 168, 138, 0.3);
|
||
border-radius: 16rpx;
|
||
box-shadow: 0 0 6px rgba(0, 168, 138, 0.18);
|
||
}
|
||
|
||
&__meta-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
|
||
& + & {
|
||
margin-top: 12rpx;
|
||
}
|
||
}
|
||
|
||
&__meta-label {
|
||
font-size: 26rpx;
|
||
color: #8a92a3;
|
||
}
|
||
|
||
&__meta-value {
|
||
font-size: 26rpx;
|
||
color: #2a2e35;
|
||
|
||
&--price {
|
||
font-size: 30rpx;
|
||
font-weight: bold;
|
||
color: #fa3534;
|
||
}
|
||
}
|
||
|
||
&__tip {
|
||
margin-top: 20rpx;
|
||
font-size: 24rpx;
|
||
color: #8a92a3;
|
||
text-align: center;
|
||
}
|
||
|
||
&__error {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 40rpx 0;
|
||
}
|
||
|
||
&__error-text {
|
||
font-size: 26rpx;
|
||
color: #fa3534;
|
||
}
|
||
|
||
&__retry {
|
||
margin-top: 24rpx;
|
||
padding: 12rpx 48rpx;
|
||
font-size: 26rpx;
|
||
color: #00a88a;
|
||
border: 1px solid rgba(0, 168, 138, 0.5);
|
||
border-radius: 999rpx;
|
||
}
|
||
}
|
||
</style>
|