|
|
@@ -36,12 +36,22 @@
|
|
|
<!-- 登录表单 -->
|
|
|
<view class="login-form">
|
|
|
<view class="phone-section">
|
|
|
+ <!--
|
|
|
+ disabled 必须同时受 agreed 和 loading 约束。
|
|
|
+ open-type="getPhoneNumber" 在「点击那一刻」就由渲染层向微信发起授权请求,
|
|
|
+ 早于 @getphonenumber 回调执行 —— 也就是说每一次点击都会消耗一次平台调用配额,
|
|
|
+ JS 里的 if (!this.agreed) return 只能弹 toast,拦不住已经发生的调用。
|
|
|
+ 未同意协议时按钮若仍可点,用户会因「点了没反应」而反复点击,最终触发微信限频:
|
|
|
+ [渲染层错误] invoke getPhoneNumber too frequently。
|
|
|
+ 故未勾选协议时直接禁用按钮,让无效点击根本不会发生。
|
|
|
+ -->
|
|
|
<button
|
|
|
class="phone-btn"
|
|
|
open-type="getPhoneNumber"
|
|
|
@getphonenumber="getPhoneNumber"
|
|
|
+ @tap="onPhoneBtnTap"
|
|
|
:loading="loading"
|
|
|
- :disabled="loading"
|
|
|
+ :disabled="!agreed || loading || tapLock"
|
|
|
>
|
|
|
<text class="phone-btn-icon">📱</text>
|
|
|
<text>手机号快捷登录</text>
|
|
|
@@ -75,6 +85,8 @@ export default {
|
|
|
data() {
|
|
|
return {
|
|
|
loading: false,
|
|
|
+ // tapLock:在「点击」瞬间上锁,用于关闭授权弹窗弹出前的连点窗口(见 onPhoneBtnTap)
|
|
|
+ tapLock: false,
|
|
|
agreed: false,
|
|
|
nickname: '',
|
|
|
avatar: '',
|
|
|
@@ -99,6 +111,13 @@ export default {
|
|
|
// 预取后如果用户隔几分钟再点击授权,code 已失效会报 invalid code (40029)。
|
|
|
// 正确做法:getPhoneNumber 中用户点击授权时实时获取。
|
|
|
},
|
|
|
+ onUnload() {
|
|
|
+ // 清理 tap 锁兜底定时器,避免登录成功 reLaunch 后定时器仍在已销毁页面上置值
|
|
|
+ if (this._tapLockTimer) {
|
|
|
+ clearTimeout(this._tapLockTimer)
|
|
|
+ this._tapLockTimer = null
|
|
|
+ }
|
|
|
+ },
|
|
|
methods: {
|
|
|
handleImageError(e) {
|
|
|
console.log('Logo 加载失败,使用默认显示')
|
|
|
@@ -120,12 +139,39 @@ export default {
|
|
|
// 不登录直接进入首页
|
|
|
uni.switchTab({ url: '/pages/index-home/index' })
|
|
|
},
|
|
|
+ // 点击瞬间上锁,关闭「授权弹窗弹出前」的连点窗口。
|
|
|
+ //
|
|
|
+ // 为什么不能只在 getPhoneNumber 里判 loading:
|
|
|
+ // open-type="getPhoneNumber" 在点击那一刻就由渲染层向微信发起了授权调用,
|
|
|
+ // @getphonenumber 回调要等用户在系统弹窗里点完才返回。这中间几百毫秒内
|
|
|
+ // loading 还是 false、按钮仍可点,用户「以为没反应」就连点,
|
|
|
+ // 每次点击都真实消耗一次平台调用配额 → invoke getPhoneNumber too frequently。
|
|
|
+ // 在 @tap 上锁能让按钮在一个渲染周期内变为不可点,早于弹窗出现。
|
|
|
+ onPhoneBtnTap() {
|
|
|
+ if (this.loading || this.tapLock) return
|
|
|
+ this.tapLock = true
|
|
|
+ // 兜底:若因系统异常导致 getphonenumber 始终不回调,3 秒后强制解锁,
|
|
|
+ // 避免按钮永久锁死导致无法再登录。
|
|
|
+ if (this._tapLockTimer) clearTimeout(this._tapLockTimer)
|
|
|
+ this._tapLockTimer = setTimeout(() => {
|
|
|
+ this.tapLock = false
|
|
|
+ this._tapLockTimer = null
|
|
|
+ }, 3000)
|
|
|
+ },
|
|
|
getPhoneNumber: function(e) {
|
|
|
+ // 授权流程已结束,释放 tap 锁与兜底定时器,按钮恢复可点。
|
|
|
+ // 注意:这里原有的 `if (this.loading) return` 已移除 —— loading 在本函数内才置 true,
|
|
|
+ // 保留它会把首个合法点击也拦掉;防重复消费改由下面的 tapLock 承担。
|
|
|
+ if (this._tapLockTimer) {
|
|
|
+ clearTimeout(this._tapLockTimer)
|
|
|
+ this._tapLockTimer = null
|
|
|
+ }
|
|
|
+ this.tapLock = false
|
|
|
+
|
|
|
if (!this.agreed) {
|
|
|
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
- if (this.loading) return
|
|
|
if (!e.detail || !e.detail.code) {
|
|
|
uni.showToast({ title: '授权失败,请重试', icon: 'none' })
|
|
|
return
|