|
@@ -14,6 +14,12 @@
|
|
|
<text class="reject-label">拒绝原因</text>
|
|
<text class="reject-label">拒绝原因</text>
|
|
|
<text class="reject-reason">{{ applyRecord && applyRecord.rejectReason }}</text>
|
|
<text class="reject-reason">{{ applyRecord && applyRecord.rejectReason }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
+ <!-- P1 补绑入口:记录已存在(status 0/1/2)、表单隐藏后,绑定失败时唯一可达的就地补绑路径。
|
|
|
|
|
+ @click 直连 bindUserReferral,绝不接 handleSubmit/doSubmit(表单隐藏 + 后端查重 ServiceImpl:74-76 会拒)。 -->
|
|
|
|
|
+ <view v-if="!userRefBound && inviteRefCode" class="rebind-section">
|
|
|
|
|
+ <text class="rebind-desc">推荐人尚未绑定成功,请在网络稳定后补绑</text>
|
|
|
|
|
+ <button class="btn-rebind" :disabled="bindingRef" @click="bindUserReferral">补绑推荐人</button>
|
|
|
|
|
+ </view>
|
|
|
<view class="action-buttons">
|
|
<view class="action-buttons">
|
|
|
<button v-if="applyStatus === 'rejected'" class="btn-reapply" @click="handleReapply">
|
|
<button v-if="applyStatus === 'rejected'" class="btn-reapply" @click="handleReapply">
|
|
|
重新申请
|
|
重新申请
|
|
@@ -485,10 +491,28 @@ export default {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
// 非 200 且非 skipped 理论上由 request() reject 走 catch,此处防御性兜底按失败处理
|
|
// 非 200 且非 skipped 理论上由 request() reject 走 catch,此处防御性兜底按失败处理
|
|
|
- this.showBindRetryHint(bindRes && bindRes.message)
|
|
|
|
|
|
|
+ // P2 幂等抛错误报循环收敛:后端已绑定成功但响应丢失、重试命中「已绑定推荐人」时按实质成功收敛。
|
|
|
|
|
+ var retryMsg = bindRes && bindRes.message
|
|
|
|
|
+ if (retryMsg && String(retryMsg).indexOf('已绑定推荐人') > -1) {
|
|
|
|
|
+ this.userRefBound = true
|
|
|
|
|
+ uni.removeStorageSync('refCode')
|
|
|
|
|
+ uni.showToast({ title: '推荐人已绑定', icon: 'none' })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ this.showBindRetryHint(retryMsg)
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
// 401 由 request() 统一 reLaunch 登录页,不在这里叠加 modal
|
|
// 401 由 request() 统一 reLaunch 登录页,不在这里叠加 modal
|
|
|
if (e && e.code === 401) return
|
|
if (e && e.code === 401) return
|
|
|
|
|
+ // P2 幂等抛错误报循环收敛(在 401 检查之后、showBindRetryHint 之前):
|
|
|
|
|
+ // 后端已实际绑定成功(CF 资产已发)但响应在传输中丢失 → 重试命中「已绑定推荐人,不可修改」,
|
|
|
|
|
+ // 此时按实质成功收敛:置幂等标记 + 清僵尸码 + toast,不弹失败 modal,
|
|
|
|
|
+ // 避免 userRefBound 永远 false、流程无法收敛为成功态。
|
|
|
|
|
+ if (e && e.message && String(e.message).indexOf('已绑定推荐人') > -1) {
|
|
|
|
|
+ this.userRefBound = true
|
|
|
|
|
+ uni.removeStorageSync('refCode')
|
|
|
|
|
+ uni.showToast({ title: '推荐人已绑定', icon: 'none' })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
// 失败:userRefBound 保持 false、inviteRefCode 未清,故可就地重试(不依赖 storage.refCode)。
|
|
// 失败:userRefBound 保持 false、inviteRefCode 未清,故可就地重试(不依赖 storage.refCode)。
|
|
|
// request() 拦截器已对非 200 业务响应 toast 后端具体 message(如「邀请码无效」「已有推荐人」),
|
|
// request() 拦截器已对非 200 业务响应 toast 后端具体 message(如「邀请码无效」「已有推荐人」),
|
|
|
// 这里用 modal 同时承载后端原文 + 明确重试引导:既不覆盖具体错误,又给出可见的重试入口。
|
|
// 这里用 modal 同时承载后端原文 + 明确重试引导:既不覆盖具体错误,又给出可见的重试入口。
|
|
@@ -675,6 +699,35 @@ export default {
|
|
|
line-height: 1.6;
|
|
line-height: 1.6;
|
|
|
display: block;
|
|
display: block;
|
|
|
}
|
|
}
|
|
|
|
|
+/* P1 推荐人补绑区:与 status-tip 同款卡片,按钮沿用 .btn-reapply 尺寸与品牌渐变,视觉同体系 */
|
|
|
|
|
+.rebind-section {
|
|
|
|
|
+ background: $app-surface;
|
|
|
|
|
+ border-radius: 16rpx;
|
|
|
|
|
+ padding: 24rpx 28rpx;
|
|
|
|
|
+ margin-bottom: 40rpx;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+}
|
|
|
|
|
+.rebind-desc {
|
|
|
|
|
+ font-size: calc(24rpx * var(--font-scale, 1));
|
|
|
|
|
+ color: $status-warning-strong;
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin-bottom: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.btn-rebind {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 88rpx;
|
|
|
|
|
+ line-height: 88rpx;
|
|
|
|
|
+ border-radius: 24rpx;
|
|
|
|
|
+ font-size: calc(30rpx * var(--font-scale, 1));
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+}
|
|
|
|
|
+.btn-rebind[disabled] {
|
|
|
|
|
+ opacity: 0.5;
|
|
|
|
|
+}
|
|
|
.action-buttons {
|
|
.action-buttons {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
display: flex;
|
|
display: flex;
|