Przeglądaj źródła

feat(miniprogram): add phone quick login button on login page.

Co-authored-by: Cursor <cursoragent@cursor.com>
wuzilong 1 miesiąc temu
rodzic
commit
b5a58583bd
2 zmienionych plików z 142 dodań i 4 usunięć
  1. 141 1
      miniprogram/pages/login/login.vue
  2. 1 3
      miniprogram/stores/user.js

+ 141 - 1
miniprogram/pages/login/login.vue

@@ -15,6 +15,7 @@ const isLoading = ref(false)
 const agreeProtocol = ref(false)
 const agreeProtocol = ref(false)
 const loginDisabled = computed(() => isLoading.value)
 const loginDisabled = computed(() => isLoading.value)
 const protocolDialogVisible = ref(false)
 const protocolDialogVisible = ref(false)
+const protocolDialogForWechat = ref(false)
 let protocolDialogResolver = null
 let protocolDialogResolver = null
 
 
 onShow(() => {
 onShow(() => {
@@ -28,6 +29,7 @@ const loginPageStyle = computed(() => ({ '--theme-color': '#0a9f6d' }))
 
 
 const handleSubmit = async () => {
 const handleSubmit = async () => {
   if (!agreeProtocol.value) {
   if (!agreeProtocol.value) {
+    protocolDialogForWechat.value = false
     const shouldContinue = await new Promise((resolve) => {
     const shouldContinue = await new Promise((resolve) => {
       protocolDialogResolver = resolve
       protocolDialogResolver = resolve
       protocolDialogVisible.value = true
       protocolDialogVisible.value = true
@@ -72,11 +74,61 @@ const toggleProtocolAgreement = () => {
 
 
 const resolveProtocolDialog = (confirmed) => {
 const resolveProtocolDialog = (confirmed) => {
   protocolDialogVisible.value = false
   protocolDialogVisible.value = false
+  protocolDialogForWechat.value = false
   if (protocolDialogResolver) {
   if (protocolDialogResolver) {
     protocolDialogResolver(confirmed)
     protocolDialogResolver(confirmed)
     protocolDialogResolver = null
     protocolDialogResolver = null
   }
   }
 }
 }
+
+const onWechatGate = () => {
+  if (isLoading.value) return
+  protocolDialogForWechat.value = true
+  protocolDialogVisible.value = true
+}
+
+const onAgreeGetPhoneNumber = (e) => {
+  agreeProtocol.value = true
+  protocolDialogVisible.value = false
+  protocolDialogForWechat.value = false
+  onGetPhoneNumber(e)
+}
+
+const onGetPhoneNumber = async (e) => {
+  if (isLoading.value) return
+  if (!agreeProtocol.value) {
+    onWechatGate()
+    return
+  }
+  const detail = (e && e.detail) || {}
+  const errMsg = detail.errMsg || ''
+  if (errMsg && errMsg.indexOf('ok') === -1) {
+    uni.showToast({ title: '需要授权手机号,或请使用账号密码登录', icon: 'none' })
+    return
+  }
+  const phoneCode = detail.code
+  if (!phoneCode) {
+    uni.showToast({ title: '需要授权手机号,或请使用账号密码登录', icon: 'none' })
+    return
+  }
+  isLoading.value = true
+  try {
+    const loginRes = await new Promise((resolve, reject) => {
+      uni.login({ provider: 'weixin', success: resolve, fail: reject })
+    })
+    const ok = await userStore.wechatLogin({
+      loginCode: loginRes.code,
+      phoneCode
+    })
+    if (ok) {
+      userStore.navigateToHome()
+    }
+  } catch (err) {
+    uni.showToast({ title: (err && err.message) || '登录失败', icon: 'none' })
+  } finally {
+    isLoading.value = false
+  }
+}
 </script>
 </script>
 
 
 <template>
 <template>
@@ -144,6 +196,26 @@ const resolveProtocolDialog = (confirmed) => {
           <text class="link" @tap="goPrivacyPolicy">《隐私政策》</text>
           <text class="link" @tap="goPrivacyPolicy">《隐私政策》</text>
         </view>
         </view>
       </view>
       </view>
+
+      <view class="login-divider">或</view>
+
+      <button
+        v-if="agreeProtocol"
+        class="login-wechat"
+        :disabled="isLoading"
+        open-type="getPhoneNumber"
+        @getphonenumber="onGetPhoneNumber"
+      >
+        <text class="login-wechat__label">手机号快捷登录</text>
+      </button>
+      <view
+        v-else
+        class="login-wechat"
+        :class="{ 'is-disabled': isLoading }"
+        @tap="onWechatGate"
+      >
+        <text class="login-wechat__label">手机号快捷登录</text>
+      </view>
     </view>
     </view>
 
 
     <view v-if="protocolDialogVisible" class="protocol-dialog-mask" @tap="resolveProtocolDialog(false)">
     <view v-if="protocolDialogVisible" class="protocol-dialog-mask" @tap="resolveProtocolDialog(false)">
@@ -152,7 +224,21 @@ const resolveProtocolDialog = (confirmed) => {
         <text class="protocol-dialog__desc">登录前请先同意《用户服务协议》和《隐私政策》</text>
         <text class="protocol-dialog__desc">登录前请先同意《用户服务协议》和《隐私政策》</text>
         <view class="protocol-dialog__actions">
         <view class="protocol-dialog__actions">
           <view class="protocol-dialog__btn protocol-dialog__btn--cancel" @tap="resolveProtocolDialog(false)">否</view>
           <view class="protocol-dialog__btn protocol-dialog__btn--cancel" @tap="resolveProtocolDialog(false)">否</view>
-          <view class="protocol-dialog__btn protocol-dialog__btn--confirm" @tap="resolveProtocolDialog(true)">是,继续登录</view>
+          <button
+            v-if="protocolDialogForWechat"
+            class="protocol-dialog__btn protocol-dialog__btn--confirm"
+            open-type="getPhoneNumber"
+            @getphonenumber="onAgreeGetPhoneNumber"
+          >
+            同意并登录
+          </button>
+          <view
+            v-else
+            class="protocol-dialog__btn protocol-dialog__btn--confirm"
+            @tap="resolveProtocolDialog(true)"
+          >
+            是,继续登录
+          </view>
         </view>
         </view>
       </view>
       </view>
     </view>
     </view>
@@ -468,6 +554,60 @@ const resolveProtocolDialog = (confirmed) => {
 .protocol-dialog__btn--confirm {
 .protocol-dialog__btn--confirm {
   background: linear-gradient(135deg, var(--theme-color) 0%, #07c160 100%);
   background: linear-gradient(135deg, var(--theme-color) 0%, #07c160 100%);
   color: #fff;
   color: #fff;
+  border: none;
+  margin: 0;
+  padding: 0;
+  line-height: normal;
+}
+
+button.protocol-dialog__btn--confirm::after {
+  border: none;
+}
+
+.login-divider {
+  display: flex;
+  align-items: center;
+  gap: 24rpx;
+  margin: 44rpx 0 28rpx;
+  width: 100%;
+  font-size: 24rpx;
+  color: #94a3b8;
+}
+
+.login-divider::before,
+.login-divider::after {
+  content: '';
+  flex: 1;
+  height: 1px;
+  background: #e2e8f0;
+}
+
+.login-wechat {
+  height: 88rpx;
+  border-radius: 24rpx;
+  border: 1px solid #e2e8f0;
+  background: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  margin: 0;
+  padding: 0;
+  line-height: normal;
+  box-sizing: border-box;
+  font-size: 28rpx;
+  font-weight: 500;
+  color: #1e293b;
+}
+
+button.login-wechat::after {
+  border: none;
+}
+
+.login-wechat__label {
+  font-size: 28rpx;
+  font-weight: 500;
+  color: #1e293b;
 }
 }
 </style>
 </style>
 
 

+ 1 - 3
miniprogram/stores/user.js

@@ -130,8 +130,8 @@ export const useUserStore = defineStore('user', () => {
     }
     }
   }
   }
 
 
+  /** 手机号快捷登录;loading 由登录页控制,此处不改 isLoading */
   async function wechatLogin(params) {
   async function wechatLogin(params) {
-    isLoading.value = true
     try {
     try {
       const res = await request({
       const res = await request({
         url: AUTH_API.WECHAT_LOGIN,
         url: AUTH_API.WECHAT_LOGIN,
@@ -156,8 +156,6 @@ export const useUserStore = defineStore('user', () => {
     } catch (e) {
     } catch (e) {
       uni.showToast({ title: (e && e.message) || '登录失败', icon: 'none' })
       uni.showToast({ title: (e && e.message) || '登录失败', icon: 'none' })
       return false
       return false
-    } finally {
-      isLoading.value = false
     }
     }
   }
   }