Procházet zdrojové kódy

feat(login): 手机号快捷登录 - 使用微信 getPhoneNumber 授权获取手机号

iwt před 1 měsícem
rodič
revize
00e7dee424

+ 54 - 71
cfc-frontend/pages/login/login.vue

@@ -28,13 +28,13 @@
     <!-- 手机号快捷登录 -->
     <view class="login-form">
       <view class="phone-section">
-        <button
-          class="phone-btn"
-          :loading="loading"
-          @click="handleWechatLogin"
-        >
-          <text>手机号快捷登录</text>
-        </button>
+          <button
+            class="phone-btn"
+            open-type="getPhoneNumber"
+            @getphonenumber="getPhoneNumber"
+          >
+            <text>手机号快捷登录</text>
+          </button>
       </view>
 
       <!-- 暂不登录 -->
@@ -58,7 +58,7 @@
 </template>
 
 <script>
-import { wechatLogin, checkTeamInviteStatus, joinTeamByInvite, bindReferral } from '../../utils/api.js'
+import { wechatPhoneLogin, checkTeamInviteStatus, joinTeamByInvite, bindReferral } from '../../utils/api.js'
 
 export default {
   data() {
@@ -67,7 +67,8 @@ export default {
       agreed: false,
       nickname: '',
       avatar: '',
-      redirectUrl: ''
+      redirectUrl: '',
+      loginCode: ''
     }
   },
   onLoad(options) {
@@ -80,6 +81,13 @@ export default {
     if (options.redirect) {
       this.redirectUrl = decodeURIComponent(options.redirect)
     }
+    // 预获取微信登录code(有效期5分钟,用户操作通常在时限内)
+    var self = this
+    uni.login({
+      provider: 'weixin',
+      success: function(res) { self.loginCode = res.code },
+      fail: function() {}
+    })
   },
   methods: {
     handleImageError(e) {
@@ -102,76 +110,51 @@ export default {
       // 不登录直接进入首页
       uni.switchTab({ url: '/pages/index-home/index' })
     },
-    handleWechatLogin() {
+    getPhoneNumber: function(e) {
       if (!this.agreed) {
         uni.showToast({ title: '请先同意用户协议', icon: 'none' })
         return
       }
-
-      this.loading = true
+      if (!e.detail || !e.detail.code) {
+        uni.showToast({ title: '授权失败,请重试', icon: 'none' })
+        return
+      }
       var self = this
-
-      // 超时保护:10秒强制结束登录流程,避免 uni.login 挂死时按钮 loading 卡死
-      var loginTimeout = new Promise(function(_, reject) {
-        setTimeout(function() {
-          reject(new Error('微信登录超时'))
-        }, 10000)
-      })
-
-      Promise.race([loginTimeout, new Promise(function(resolve, reject) {
-        uni.login({
-          provider: 'weixin',
-          success: function(loginRes) { resolve(loginRes) },
-          fail: function() { reject(new Error('微信登录失败')) }
-        })
-      })]).then(function(loginRes) {
-        // 静默登录:仅获取 openid,不强制索取手机号
-        wechatLogin(loginRes.code, {
-          nickname: self.nickname || '用户',
-          avatar: self.avatar || ''
-        }, '').then(function(res) {
-          // 保存 token 和用户信息(只写一次)
-          uni.setStorageSync('token', res.data.token)
-          uni.setStorageSync('userId', res.data.userId)
-          uni.setStorageSync('role', res.data.role)
-          uni.setStorageSync('currentRole', res.data.role)
-          uni.setStorageSync('isSwitchedChild', false)
-          uni.setStorageSync('isSwitchedTeacher', false)
-          if (res.data.openid) {
-            uni.setStorageSync('openid', res.data.openid)
-          }
-          uni.setStorageSync('userInfo', {
-            nickname: res.data.nickname,
-            userId: res.data.userId
-          })
-          if (res.data.teacherStatus) {
-            uni.setStorageSync('teacherStatus', res.data.teacherStatus)
-          }
-          if (res.data.teacherRejectReason) {
-            uni.setStorageSync('teacherRejectReason', res.data.teacherRejectReason)
-          }
-          // 同步 familyId 到 store(hasFamily getter 依赖,8ddc5b55 移除存储后此数据流断裂)
-          self.$store.commit('setFamilyId', res.data.familyId)
-
-          uni.showToast({ title: '登录成功', icon: 'success' })
-
-          if (res.data.needsRoleSelect) {
-            // 首次登录:跳过角色选择,默认 parent,直接进入信息补充页
-            uni.redirectTo({
-              url: '/pages/user-edit/user-edit?token=' + res.data.token + '&userId=' + res.data.userId + '&autoParent=true'
-            })
-          } else {
-            self.handlePostLoginRouting()
-          }
-        }).catch(function(e) {
-          uni.showToast({ title: e.message || '登录失败', icon: 'none' })
-          self.loading = false
-        })
-      }).catch(function(e) {
-        uni.showToast({ title: e.message || '微信登录失败', icon: 'none' })
+      self.loading = true
+      if (!self.loginCode) {
+        uni.showToast({ title: '网络异常,请重试', icon: 'none' })
+        self.loading = false
+        return
+      }
+      wechatPhoneLogin({ code: self.loginCode, phoneCode: e.detail.code }).then(function(res) {
+        self.completeLogin(res.data)
+      }).catch(function(err) {
+        uni.showToast({ title: err.message || '登录失败', icon: 'none' })
         self.loading = false
       })
     },
+    completeLogin: function(data) {
+      var self = this
+      uni.setStorageSync('token', data.token)
+      uni.setStorageSync('userId', data.userId)
+      uni.setStorageSync('role', data.role)
+      uni.setStorageSync('currentRole', data.role)
+      uni.setStorageSync('isSwitchedChild', false)
+      uni.setStorageSync('isSwitchedTeacher', false)
+      if (data.openid) uni.setStorageSync('openid', data.openid)
+      uni.setStorageSync('userInfo', { nickname: data.nickname, userId: data.userId })
+      if (data.phone) uni.setStorageSync('phone', data.phone)
+      if (data.teacherStatus) uni.setStorageSync('teacherStatus', data.teacherStatus)
+      if (data.teacherRejectReason) uni.setStorageSync('teacherRejectReason', data.teacherRejectReason)
+      self.$store.commit('setFamilyId', data.familyId)
+      uni.showToast({ title: '登录成功', icon: 'success' })
+      self.loading = false
+      if (data.needsRoleSelect) {
+        uni.redirectTo({ url: '/pages/user-edit/user-edit?token=' + data.token + '&userId=' + data.userId + '&autoParent=true' })
+      } else {
+        self.handlePostLoginRouting()
+      }
+    },
     handlePostLoginRouting() {
       const inviteCode = uni.getStorageSync('inviteCode')
       const inviteType = uni.getStorageSync('inviteType')

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-b63002004626f006889b059953b0a3caa3c86a07
+469c2df77e23677a91fb62838ac2e520816afc57

+ 2 - 2
cfc-web/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "cfc-web",
-  "version": "1.0.893",
+  "version": "1.0.894",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "cfc-web",
-      "version": "1.0.893",
+      "version": "1.0.894",
       "dependencies": {
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor-for-vue": "^1.0.2",