修复背景:
账号注销成功后, 用户被重定向到 native 登录页 (手机号+验证码表单风格, 像 H5),
而不是微信小程序原生授权登录 (ns-login 弹窗 + getPhoneNumber 授权).
此外, 微信小程序下短时间内连发 reLaunch 会触发 native 错误:
appLaunch with non-empty page stack
以及历史遗留的 navigateTo push 登录页 + 旧页面在栈中导致的死循环问题.
主要修复:
1. util.js redirectTo reLaunch 分支 (4 平台兼容):
- H5 平台: window.location.replace (真正清空 history, 避免 history.back + router.replace 假清栈)
- 小程序平台 (mp-weixin/mp-harmony/...): 加 300ms 全局跳转锁 + setTimeout(0) 包裹
解决 appLaunch with non-empty page stack 错误
- APP/其他: 保留原生 uni.reLaunch
2. util.js redirectTo 入口智能默认 (跨平台兜底):
- 目标 == LOGIN_PAGE_URL && mode 是默认 navigateTo && 没传 back 参数
→ 自动改成 reLaunch (避免注销后死循环)
- 显式传 mode 或带 back 参数的场景保持原行为 (不影响登录后回原页面)
3. member-public.js mixin 增强:
- getLoginEntryUrl(): 跨平台登录入口智能分流
微信小程序/鸿蒙/其他小程序 → INDEX_PAGE_URL (首页, 走 ns-login 原生授权弹窗)
H5/快应用/APP → LOGIN_PAGE_URL (原生登录页)
- redirectToLoginOnce(): 统一封装 clearLoginState + 跳登录入口, 带 800ms 组件级防抖锁
- logout / getCancelStatus 兜底 / submitCancelApply 三个地方都改用 redirectToLoginOnce()
4. 5 个注销/退出流程页面改造:
- cancelsuccess.vue / cancelstatus.vue / cancellation.vue / cancelrefuse.vue
/ assets.vue: 跳登录入口全部用 redirectToLoginOnce (不再直接 redirectTo(LOGIN_PAGE_URL))
- cancellation.vue / cancelrefuse.vue: 加 memberPublic mixin (原本没有)
5. 取消了在 pages/index/public/js/index.js 中 onShow 自动弹登录弹窗的尝试
(因触发 Vue 框架的 dataset of null pre-existing 错误, 改由其他途径处理)
测试覆盖 (300+ 测试用例全部通过):
- tests/member-cancel-flow.test.js (28 个测试, 9 个用户故事场景)
- tests/redirectTo-relaunch-h5.test.js (11 个测试, 9 个跨平台场景)
- tests/redirectTo-real.test.js (12 个)
- tests/util.test.js (30 个)
- 其他 8 个测试文件全部通过
238 lines
5.7 KiB
Vue
238 lines
5.7 KiB
Vue
<template>
|
|
<view class="container" :style="themeColor">
|
|
<view class="assets-wrap">
|
|
<view class="assets-block">
|
|
<view class="assets-tips"><text>{{ $lang('member.assets.riskTip') }}</text></view>
|
|
<view class="assets-box assets-account">
|
|
<view class="assets-title">
|
|
<text class="color-base-bg"></text>
|
|
<text>{{ $lang('member.assets.accountAssets') }}</text>
|
|
</view>
|
|
<view class="assets-list">
|
|
<view class="assets-li">
|
|
<view>{{ member_info.point }}</view>
|
|
<view>{{ $lang('member.assets.point') }}</view>
|
|
</view>
|
|
<view class="assets-li">
|
|
<view>¥{{ member_info.balance_money }}</view>
|
|
<view>{{ $lang('member.assets.cashBalance') }}</view>
|
|
</view>
|
|
<view class="assets-li">
|
|
<view>¥{{ member_info.balance }}</view>
|
|
<view>{{ $lang('member.assets.storedValueBalance') }}</view>
|
|
</view>
|
|
<view class="assets-li">
|
|
<view>{{ accountInfo.member_coupon_count }}</view>
|
|
<view>{{ $lang('member.assets.coupon') }}</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="assets-box assets-order">
|
|
<view class="assets-title">
|
|
<text class="color-base-bg"></text>
|
|
<text>{{ $lang('member.assets.orderAssets') }}</text>
|
|
</view>
|
|
<view class="assets-list">
|
|
<view class="assets-li">
|
|
<view>{{ accountInfo.order_pay_count }}</view>
|
|
<view>{{ $lang('member.assets.waitDelivery') }}</view>
|
|
</view>
|
|
<view class="assets-li">
|
|
<view>{{ accountInfo.order_delivery_count }}</view>
|
|
<view>{{ $lang('member.assets.waitReceive') }}</view>
|
|
</view>
|
|
<view class="assets-li">
|
|
<view>{{ accountInfo.order_refund_count }}</view>
|
|
<view>{{ $lang('member.assets.refunding') }}</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="member_info.is_fenxiao == 1" class="assets-box assets-fenxiao">
|
|
<view class="assets-title">
|
|
<text class="color-base-bg"></text>
|
|
<text>{{ $lang('member.assets.fenxiaoAssets') }}</text>
|
|
</view>
|
|
<view class="assets-list">
|
|
<view class="assets-li">
|
|
<view>¥{{ fenxiao_info.account }}</view>
|
|
<view>{{ $lang('member.assets.withdrawableCommission') }}</view>
|
|
</view>
|
|
<view class="assets-li">
|
|
<view>¥{{ fenxiao_info.account_withdraw_apply }}</view>
|
|
<view>{{ $lang('member.assets.withdrawingCommission') }}</view>
|
|
</view>
|
|
<view class="assets-li">
|
|
<view>{{ accountInfo.fenxiao_order_count }}</view>
|
|
<view>{{ $lang('member.assets.waitSettleOrder') }}</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="assets-btn">
|
|
<button type="primary" @click="prev">{{ $lang('member.assets.prevStep') }}</button>
|
|
<button class="color-base-bg" @click="submit">{{ $lang('member.assets.confirmApply') }}</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import memberPublic from './public/js/member-public.js';
|
|
|
|
export default {
|
|
mixins: [memberPublic],
|
|
data() {
|
|
return {
|
|
accountInfo: {},
|
|
member_info: {},
|
|
fenxiao_info: {}
|
|
};
|
|
},
|
|
onLoad(option) {
|
|
|
|
if (option.back) this.back = option.back;
|
|
|
|
// 判断登录
|
|
if (!this.storeToken) {
|
|
// 跳登录入口 (mixin 提供, 内部带 800ms 防抖锁 + 跨平台分流:
|
|
// 微信小程序 / 鸿蒙下跳首页 INDEX_PAGE_URL, 走 ns-login 原生授权弹窗;
|
|
// H5 / APP 下跳原生登录页 LOGIN_PAGE_URL)
|
|
this.redirectToLoginOnce();
|
|
} else {
|
|
this.getAccountInfo();
|
|
}
|
|
},
|
|
methods: {
|
|
getAccountInfo() {
|
|
this.$api.sendRequest({
|
|
url: '/membercancel/api/membercancel/accountInfo',
|
|
success: res => {
|
|
if (res.code >= 0) {
|
|
this.accountInfo = res.data;
|
|
this.member_info = res.data.member_info;
|
|
if (res.data.member_info.is_fenxiao == 1) {
|
|
this.fenxiao_info = res.data.fenxiao_info;
|
|
}
|
|
} else if (res.code == -1) {
|
|
// 跳登录入口 (mixin 提供, 内部带 800ms 防抖锁 + 跨平台分流)
|
|
this.redirectToLoginOnce();
|
|
}
|
|
}
|
|
});
|
|
},
|
|
prev() {
|
|
this.$util.redirectTo('/pages_tool/member/cancellation');
|
|
},
|
|
submit() {
|
|
this.submitCancelApply();
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.assets-wrap {
|
|
.assets-block {
|
|
padding: 0 24rpx;
|
|
padding-top: 30rpx;
|
|
}
|
|
|
|
.assets-tips {
|
|
width: 100%;
|
|
height: 56rpx;
|
|
background-color: rgba(250, 106, 0, 0.2);
|
|
border-radius: 6rpx;
|
|
line-height: 56rpx;
|
|
padding-left: 20rpx;
|
|
box-sizing: border-box;
|
|
|
|
text {
|
|
color: #fa6a00;
|
|
font-size: 28rpx;
|
|
}
|
|
}
|
|
|
|
.assets-box {
|
|
width: 100%;
|
|
margin-top: 30rpx;
|
|
background-color: #ffffff;
|
|
border-radius: 6rpx;
|
|
padding: 20rpx;
|
|
box-sizing: border-box;
|
|
|
|
.assets-title {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
text:nth-child(1) {
|
|
width: 6rpx;
|
|
height: 28rpx;
|
|
border-radius: 2rpx;
|
|
}
|
|
|
|
text:nth-child(2) {
|
|
margin-left: 20rpx;
|
|
font-size: 28rpx;
|
|
line-height: 28rpx;
|
|
padding-top: 8rpx;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
.assets-list {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
padding: 0 26rpx 35rpx;
|
|
margin-top: 53rpx;
|
|
|
|
.assets-li {
|
|
text-align: center;
|
|
|
|
view:nth-child(1) {
|
|
font-size: 36rpx;
|
|
line-height: 36rpx;
|
|
}
|
|
|
|
view:nth-child(2) {
|
|
font-size: 28rpx;
|
|
line-height: 28rpx;
|
|
color: #666666;
|
|
margin-top: 30rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.assets-btn {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
position: fixed;
|
|
bottom: 0;
|
|
width: 100%;
|
|
height: 150rpx;
|
|
|
|
button {
|
|
width: 300rpx;
|
|
height: 80rpx;
|
|
font-size: 28rpx;
|
|
line-height: 80rpx;
|
|
margin: 0 15rpx;
|
|
}
|
|
|
|
button[type='primary'] {
|
|
background-color: unset !important;
|
|
color: #333333;
|
|
border: 2rpx solid #dddddd;
|
|
}
|
|
|
|
button:nth-child(2) {
|
|
color: var(--btn-text-color);
|
|
}
|
|
}
|
|
}
|
|
</style>
|