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.
 
 
 

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