Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

156 lignes
3.8 KiB

  1. <template>
  2. <view class="phone-login">
  3. <view class="title">手机号登录</view>
  4. <view class="field">
  5. <input v-model="phone" class="input" type="number" placeholder="请输入手机号" maxlength="11" />
  6. </view>
  7. <view class="field code">
  8. <input v-model="code" class="input" type="number" placeholder="请输入验证码" maxlength="6" />
  9. <view class="code-btn" :class="{ disabled: counting }" @click="sendCode">
  10. {{ counting ? `${count}s` : '获取验证码' }}
  11. </view>
  12. </view>
  13. <button class="btn-primary submit" :loading="postLoading" @click="onLogin">登录</button>
  14. <view class="agreement" @click="agree = !agree">
  15. <view class="checkbox" :class="{ checked: agree }">{{ agree ? '✓' : '' }}</view>
  16. <text>登录即代表同意《服务条款》与《隐私政策》</text>
  17. </view>
  18. </view>
  19. </template>
  20. <script setup>
  21. import { ref } from 'vue'
  22. import { userApi } from '@/api/index.js'
  23. import { useUserStore } from '@/store/user.js'
  24. import { useApp } from '@/utils/useApp.js'
  25. const userStore = useUserStore()
  26. const { toast } = useApp()
  27. const phone = ref('')
  28. const code = ref('')
  29. const agree = ref(false)
  30. const postLoading = ref(false)
  31. const counting = ref(false)
  32. const count = ref(60)
  33. function sendCode() {
  34. if (counting.value) return
  35. if (!/^1\d{10}$/.test(phone.value)) return toast('请输入正确的手机号')
  36. userApi
  37. .sendCode({ phone: phone.value })
  38. .then(() => {
  39. toast('验证码已发送')
  40. counting.value = true
  41. count.value = 60
  42. const timer = setInterval(() => {
  43. count.value--
  44. if (count.value <= 0) {
  45. clearInterval(timer)
  46. counting.value = false
  47. }
  48. }, 1000)
  49. })
  50. .catch((e) => toast(e.msg || '发送失败'))
  51. }
  52. async function onLogin() {
  53. if (!/^1\d{10}$/.test(phone.value)) return toast('请输入正确的手机号')
  54. if (!code.value) return toast('请输入验证码')
  55. if (!agree.value) return toast('请阅读并同意服务政策')
  56. try {
  57. postLoading.value = true
  58. const res = await userApi.loginPhone({ phone: phone.value, code: code.value })
  59. userStore.setLogin({ token: res.data.token, userInfo: res.data })
  60. toast('登录成功!')
  61. // #ifdef H5
  62. const redirect = uni.getStorageSync('login_redirect') || ''
  63. if (redirect) {
  64. uni.removeStorageSync('login_redirect')
  65. setTimeout(() => uni.reLaunch({ url: redirect }), 800)
  66. return
  67. }
  68. // #endif
  69. setTimeout(() => uni.switchTab({ url: '/pages/toolbox/toolbox' }), 800)
  70. } catch (e) {
  71. toast(e.msg || '登录失败,请重试!')
  72. } finally {
  73. postLoading.value = false
  74. }
  75. }
  76. </script>
  77. <style lang="scss" scoped>
  78. .phone-login {
  79. padding: 60rpx;
  80. }
  81. .title {
  82. font-size: 48rpx;
  83. font-weight: 800;
  84. margin-bottom: 60rpx;
  85. }
  86. .field {
  87. margin-bottom: 28rpx;
  88. &.code {
  89. display: flex;
  90. align-items: center;
  91. gap: 20rpx;
  92. }
  93. .input {
  94. flex: 1;
  95. height: 92rpx;
  96. padding: 0 28rpx;
  97. border-radius: 16rpx;
  98. background: $ai-panel;
  99. font-size: 30rpx;
  100. box-shadow: $ai-shadow;
  101. }
  102. .code-btn {
  103. flex-shrink: 0;
  104. height: 92rpx;
  105. line-height: 92rpx;
  106. padding: 0 28rpx;
  107. border-radius: 16rpx;
  108. font-size: 26rpx;
  109. color: $ai-accent;
  110. background: rgba(11, 140, 255, 0.1);
  111. &.disabled {
  112. color: $ai-muted;
  113. }
  114. }
  115. }
  116. .submit {
  117. margin-top: 20rpx;
  118. }
  119. .agreement {
  120. margin-top: 40rpx;
  121. display: flex;
  122. align-items: center;
  123. justify-content: center;
  124. font-size: 24rpx;
  125. color: $ai-muted;
  126. .checkbox {
  127. width: 34rpx;
  128. height: 34rpx;
  129. margin-right: 12rpx;
  130. border-radius: 50%;
  131. border: 2rpx solid $ai-muted;
  132. color: #fff;
  133. font-size: 22rpx;
  134. display: flex;
  135. align-items: center;
  136. justify-content: center;
  137. &.checked {
  138. background: $ai-accent;
  139. border-color: $ai-accent;
  140. }
  141. }
  142. }
  143. </style>