| | |
| | | </view> |
| | | |
| | | <scroll-view scroll-y="true" class="result-content"> |
| | | <view class="status-icon-wrap"><image class="status-icon" :src="statusIcon" mode="aspectFit"></image></view> |
| | | <text class="result-title">{{statusTitle}}</text> |
| | | <text v-if="statusReason.length > 0" class="result-reason">{{statusReason}}</text> |
| | | <view v-if="refreshable" class="progress-panel"> |
| | | <text class="result-reason">最后成功同步:{{lastSyncedAt.length > 0 ? lastSyncedAt : '尚未同步'}}</text> |
| | | <text v-if="syncError.length > 0" class="result-reason">{{syncError}}</text> |
| | | <view v-for="stage in stages" :key="stage.key" class="progress-stage"> |
| | | <text>{{stage.label}} · {{stageLabel(stage.status)}}</text> |
| | | <text class="result-reason">{{stage.reason}}</text> |
| | | <image v-if="stage.qrCode.length > 0" :src="stage.qrCode" mode="aspectFit" style="width: 360rpx; height: 360rpx;"></image> |
| | | </view> |
| | | <text class="result-reason">刷新会查询进度并继续办理已满足条件的缺失能力。本人实名或授权办理后,请再次刷新。</text> |
| | | <button :disabled="syncing" :loading="syncing" @tap="refreshProgress(false)">刷新进度</button> |
| | | <button v-if="retryable" :disabled="syncing" @tap="refreshProgress(true)">重试失败步骤</button> |
| | | <button v-if="realnameActionable" :disabled="syncing" @tap="startRealname">{{realnameButtonLabel}}</button> |
| | | <view class="status-hero"> |
| | | <view class="status-icon-wrap"><image class="status-icon" :src="statusIcon" mode="aspectFit"></image></view> |
| | | <text class="status-title">{{statusTitle}}</text> |
| | | <text v-if="statusReason.length > 0" class="status-reason">{{statusReason}}</text> |
| | | </view> |
| | | <view v-if="onboardingStatus == 'rejected'" class="return-button" @tap="resubmit"><text>重新提交</text></view> |
| | | <view v-else class="return-button" @tap="goBack"><text>返回商户中心</text></view> |
| | | |
| | | <view v-if="refreshable" class="progress-panel"> |
| | | <view class="panel-head"> |
| | | <text class="panel-head-label">开通进度</text> |
| | | <text class="panel-head-time">{{lastSyncedAt.length > 0 ? lastSyncedAt : '尚未同步'}}</text> |
| | | </view> |
| | | <text v-if="syncError.length > 0" class="panel-sync-error">{{syncError}}</text> |
| | | <view v-for="(stage, index) in stages" :key="stage.key" class="progress-stage" :class="index == stages.length - 1 ? 'progress-stage-last' : ''"> |
| | | <view class="stage-head"> |
| | | <text class="stage-label">{{stage.label}}</text> |
| | | <text class="stage-pill" :class="pillClass(stage.status)">{{stageLabel(stage.status)}}</text> |
| | | </view> |
| | | <text v-if="stage.reason.length > 0" class="stage-reason">{{stage.reason}}</text> |
| | | <view v-if="qrVisible(stage)" class="qr-block"> |
| | | <view class="qr-frame"><image class="qr-image" :src="stage.qrCode" mode="aspectFit"></image></view> |
| | | <text class="qr-hint">请本人用微信扫码完成实名授权</text> |
| | | </view> |
| | | </view> |
| | | <text v-if="canRefresh" class="panel-foot">刷新会查询进度并继续办理已满足条件的缺失能力。本人实名或授权办理后,请再次刷新。</text> |
| | | </view> |
| | | |
| | | <view class="action-area"> |
| | | <view v-if="canRefresh" class="action-primary" :class="syncing ? 'button-disabled' : ''" @tap="refreshProgress(false)"><text>{{syncing ? '处理中...' : '刷新进度'}}</text></view> |
| | | <view v-if="refreshable && retryable" class="action-secondary" :class="syncing ? 'button-disabled' : ''" @tap="refreshProgress(true)"><text>重试失败步骤</text></view> |
| | | <view v-if="refreshable && realnameActionable" class="action-secondary" :class="syncing ? 'button-disabled' : ''" @tap="startRealname"><text>{{realnameButtonLabel}}</text></view> |
| | | <view v-if="onboardingStatus == 'rejected'" class="action-primary" @tap="resubmit"><text>重新提交</text></view> |
| | | <view v-else class="action-secondary" @tap="goBack"><text>返回商户中心</text></view> |
| | | </view> |
| | | <view class="safe-space"></view> |
| | | </scroll-view> |
| | | </view> |
| | | </template> |
| | |
| | | if (status == 'failed') return '失败' |
| | | return '未确认' |
| | | } |
| | | /** 阶段状态对应的标记底色。只用小程序既有的语义色:橙=完成、灰=等待、暖橙=待办、红=失败。 */ |
| | | const pillClass = (status: string): string => { |
| | | if (status == 'confirmed') return 'stage-pill-done' |
| | | if (status == 'failed') return 'stage-pill-failed' |
| | | if (status == 'action_required' || status == 'missing') return 'stage-pill-action' |
| | | return 'stage-pill-waiting' |
| | | } |
| | | // 全部确认之后就没有可办理的步骤了:不再显示「刷新进度」,也不再说「请再次刷新」—— |
| | | // 那时候让商户去点刷新只会让人以为还有事没做完。通道能力由服务端定时刷新兜底, |
| | | // 页面留「返回商户中心」即可。 |
| | | const canRefresh = computed((): boolean => refreshable.value && onboardingStatus.value != 'approved') |
| | | // 二维码是给本人扫码授权用的:授权已完成(confirmed)之后它就没有用途了, |
| | | // 再挂着「请扫码」只会让商户以为还要去扫一次。通道查询在授权完成后仍会回传这个码, |
| | | // 所以由前端按状态收起。 |
| | | const qrVisible = (stage: OnboardingStage): boolean => stage.qrCode.length > 0 && stage.status != 'confirmed' |
| | | const applyProgress = (item: SettlementOnboarding) => { |
| | | onboardingStatus.value = item.status |
| | | statusReason.value = item.reason |
| | |
| | | </script> |
| | | |
| | | <style> |
| | | /* 与商户端其它页面同一套视觉:底 #F4F6F9、白卡大圆角 + 极淡弥散阴影、橙色渐变做主操作。 |
| | | 这里刻意不用 <button>:微信默认按钮样式(灰底直角)跟小程序整体美术风格对不上, |
| | | 改成与进件表单同一套 view + 渐变胶囊。 */ |
| | | page { background-color: #F4F6F9; } |
| | | .page { display: flex; flex: 1; width: 100%; height: 100vh; flex-direction: column; overflow: hidden; background-color: #F4F6F9; } |
| | | .navigation-bar { display: flex; height: 174rpx; padding: 68rpx 38rpx 0; flex: none; flex-direction: row; align-items: center; position: relative; background-color: #F4F6F9; box-sizing: border-box; } |
| | | .back-button { display: flex; width: 64rpx; height: 64rpx; justify-content: center; align-items: center; position: absolute; left: 38rpx; top: 89rpx; z-index: 2; } |
| | | .back-icon { width: 38rpx; height: 38rpx; } |
| | | .back-button { display: flex; width: 80rpx; height: 80rpx; justify-content: center; align-items: center; position: absolute; left: 18rpx; top: 79rpx; z-index: 2; } |
| | | .back-icon { width: 44rpx; height: 44rpx; } |
| | | .page-title { width: 100%; color: #202B3E; font-size: 34rpx; line-height: 48rpx; text-align: center; font-weight: 700; } |
| | | .result-content { display: flex; padding: 70rpx 44rpx 0; flex: 1; flex-direction: column; align-items: center; box-sizing: border-box; } |
| | | .progress-panel { width: 100%; padding-top: 24rpx; padding-bottom: 32rpx; } |
| | | .progress-stage { padding: 20rpx; margin-bottom: 16rpx; background-color: #FFFFFF; border-radius: 16rpx; } |
| | | |
| | | .result-content { display: flex; flex: 1; height: 0; padding: 24rpx 38rpx 0; flex-direction: column; box-sizing: border-box; } |
| | | |
| | | .status-hero { display: flex; flex-direction: column; align-items: center; padding: 30rpx 0 8rpx; } |
| | | .status-icon-wrap { display: flex; width: 176rpx; height: 176rpx; justify-content: center; align-items: center; border-radius: 88rpx; background-image: linear-gradient(145deg, #FFF0E6 0%, #FFF8F3 100%); box-shadow: 0 18rpx 38rpx rgba(255, 107, 44, 0.12); } |
| | | .status-icon { width: 82rpx; height: 82rpx; } |
| | | .result-title { margin-top: 40rpx; color: #293547; font-size: 34rpx; line-height: 48rpx; font-weight: 700; } |
| | | .result-reason { width: 100%; margin-top: 24rpx; color: #7D8796; font-size: 25rpx; line-height: 38rpx; text-align: center; } |
| | | .return-button { display: flex; width: 100%; height: 88rpx; margin-top: 70rpx; justify-content: center; align-items: center; border-radius: 44rpx; color: #FFFFFF; font-size: 29rpx; line-height: 41rpx; font-weight: 700; background-image: linear-gradient(90deg, #FF7625 0%, #FF9630 100%); box-shadow: 0 12rpx 24rpx rgba(255, 107, 36, 0.20); } |
| | | .status-title { margin-top: 32rpx; color: #202B3E; font-size: 36rpx; line-height: 50rpx; font-weight: 700; text-align: center; } |
| | | /* display: block 才能正常换行:<text> 默认是行内元素,之前用 width:100% + 居中会排成 |
| | | 一整行溢出并被裁掉两侧。 */ |
| | | .status-reason { display: block; margin-top: 16rpx; padding: 0 20rpx; color: #7D8796; font-size: 25rpx; line-height: 38rpx; text-align: center; } |
| | | |
| | | .progress-panel { margin-top: 34rpx; padding: 8rpx 30rpx 26rpx; border-radius: 34rpx; background-color: #FFFFFF; box-shadow: 0 14rpx 34rpx rgba(49, 55, 66, 0.06); box-sizing: border-box; } |
| | | .panel-head { display: flex; height: 88rpx; flex-direction: row; align-items: center; justify-content: space-between; } |
| | | .panel-head-label { color: #2E394A; font-size: 29rpx; line-height: 40rpx; font-weight: 700; } |
| | | .panel-head-time { color: #9AA4B3; font-size: 22rpx; line-height: 32rpx; } |
| | | .panel-sync-error { display: block; margin-bottom: 16rpx; padding: 16rpx 20rpx; color: #F06A66; font-size: 23rpx; line-height: 34rpx; background-color: #FEF3F2; border-radius: 20rpx; } |
| | | |
| | | .progress-stage { padding: 24rpx 0; border-bottom: 1rpx solid #F0F3F7; } |
| | | .progress-stage-last { border-bottom-width: 0; } |
| | | .stage-head { display: flex; flex-direction: row; align-items: center; justify-content: space-between; } |
| | | .stage-label { flex: 1; color: #313744; font-size: 29rpx; line-height: 40rpx; font-weight: 700; } |
| | | .stage-pill { flex: none; margin-left: 16rpx; padding: 6rpx 18rpx; border-radius: 20rpx; font-size: 22rpx; line-height: 30rpx; font-weight: 600; } |
| | | .stage-pill-done { color: #FF7625; background-color: #FFF0E4; } |
| | | .stage-pill-waiting { color: #9AA4B3; background-color: #EDF0F4; } |
| | | .stage-pill-action { color: #B4520F; background-color: #FFF5EA; } |
| | | .stage-pill-failed { color: #F06A66; background-color: #FEF3F2; } |
| | | .stage-reason { display: block; margin-top: 10rpx; color: #9AA4B3; font-size: 24rpx; line-height: 36rpx; } |
| | | |
| | | .qr-block { display: flex; flex-direction: column; align-items: center; margin-top: 24rpx; } |
| | | .qr-frame { display: flex; padding: 22rpx; justify-content: center; align-items: center; border-radius: 24rpx; background-color: #F7F9FC; } |
| | | .qr-image { width: 340rpx; height: 340rpx; } |
| | | .qr-hint { margin-top: 14rpx; color: #9AA4B3; font-size: 23rpx; line-height: 34rpx; } |
| | | |
| | | .panel-foot { display: block; margin-top: 22rpx; color: #9AA4B3; font-size: 22rpx; line-height: 34rpx; } |
| | | |
| | | .action-area { display: flex; margin-top: 34rpx; flex-direction: column; } |
| | | .action-primary { display: flex; height: 100rpx; margin-bottom: 22rpx; justify-content: center; align-items: center; border-radius: 50rpx; color: #FFFFFF; font-size: 30rpx; line-height: 42rpx; font-weight: 700; background-image: linear-gradient(90deg, #FF8A24 0%, #FF4B36 100%); box-shadow: 0 14rpx 26rpx rgba(255, 101, 48, 0.22); } |
| | | .action-secondary { display: flex; height: 100rpx; margin-bottom: 22rpx; justify-content: center; align-items: center; border-radius: 50rpx; color: #313744; font-size: 28rpx; line-height: 38rpx; font-weight: 700; background-color: #EDF0F4; } |
| | | .button-disabled { opacity: 0.58; } |
| | | .safe-space { height: 60rpx; } |
| | | </style> |