index.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <template>
  2. <view class="auth-content">
  3. <wd-navbar fixed placeholder :title="title" left-arrow safeAreaInsetTop customClass="navbar" />
  4. <view class="auth-bg" />
  5. <view class="auth-body">
  6. <view class="auth-title">欢迎登录</view>
  7. <view class="auth-form">
  8. <!-- 账号 -->
  9. <AuthInputItem
  10. label="账号"
  11. v-model="form.username"
  12. type="number"
  13. :maxlength="11"
  14. placeholder="请输入账号"
  15. :error="errors.username"
  16. @clear="clearFieldError('username')"
  17. @validate="validateField('username')"
  18. />
  19. <!-- 密码 -->
  20. <AuthInputItem
  21. label="密码"
  22. v-model="form.password"
  23. type="number"
  24. :maxlength="11"
  25. placeholder="请输入密码"
  26. passwordToggle
  27. :error="errors.password"
  28. @validate="validateField('password')"
  29. @clear="clearFieldError('password')"
  30. >
  31. <template #suffix>
  32. <view class="forget-password" @click="forgetPassword"> 忘记密码? </view>
  33. </template>
  34. </AuthInputItem>
  35. <!-- 验证码 -->
  36. <AuthInputItem
  37. label="验证码"
  38. v-model="form.code"
  39. type="number"
  40. :maxlength="6"
  41. placeholder="请输入验证码"
  42. :error="errors.code"
  43. @clear="clearFieldError('code')"
  44. @validate="validateField('code')"
  45. >
  46. <template #suffix>
  47. <button class="get-code" :disabled="codeDisabled" @click.stop.prevent="sendCode">
  48. {{ countdown > 0 ? `${countdown}s 后重试` : '发送验证码' }}
  49. </button>
  50. </template>
  51. </AuthInputItem>
  52. <!-- 协议 -->
  53. <view class="agreement">
  54. <label class="agreement-label">
  55. <checkbox
  56. :checked="agreementChecked"
  57. class="agreement-checkbox"
  58. @click.stop="toggleAgreement"
  59. />
  60. <view class="txt">
  61. 我已阅读并同意
  62. <text @click.stop="agreementClick('agreement')"> 《要易云平台用户协议》 </text>、
  63. <text @click.stop="agreementClick('policy')"> 《隐私权政策》 </text>、
  64. <text @click.stop="agreementClick('personalInfo')"> 《个人信息使用授权书》 </text>、
  65. <text @click.stop="handleOpenPrivacyContractClick"> 《用户隐私保护指引》 </text>
  66. </view>
  67. </label>
  68. <view class="error-text" :class="{ visible: agreementError }"> 请阅读并勾选协议 </view>
  69. </view>
  70. <!-- 提交 -->
  71. <view class="submit">
  72. <button class="submit-btn" type="default" @click="submit">登录</button>
  73. </view>
  74. </view>
  75. </view>
  76. </view>
  77. </template>
  78. <script setup lang="ts">
  79. import { onMounted, reactive, ref } from 'vue'
  80. import projectConfig from '@/config/presets'
  81. import { debounce } from '@/plugins/debounce'
  82. import { getAuthTokenApi, getMobileCodeApi } from '@/services/modules/auth'
  83. import { useCodeCountdown } from '@/composables/useCodeCountdown'
  84. import { useFormValidator } from '@/composables/useFormValidator'
  85. import { useUserStore } from '@/stores/modules/user'
  86. import { resetPasswordRules } from '@/validators/authFormRules'
  87. import AuthInputItem from '@/components/auth/AuthInputItem.vue'
  88. const userStore = useUserStore()
  89. const { title } = projectConfig
  90. const form = reactive({
  91. username: '15093284672',
  92. password: '284672',
  93. code: '5657',
  94. })
  95. const { errors, validate, validateField, clearFieldError } = useFormValidator(
  96. resetPasswordRules,
  97. form
  98. )
  99. const agreementChecked = ref(false)
  100. const agreementError = ref(false)
  101. const toggleAgreement = () => {
  102. agreementChecked.value = !agreementChecked.value
  103. if (agreementChecked.value) agreementError.value = false
  104. }
  105. const agreementClick = (type: string) => {
  106. // 打开你自己的协议页面
  107. }
  108. const privacyAuthed = ref(false)
  109. onMounted(() => {
  110. checkPrivacyAuthorization()
  111. })
  112. const checkPrivacyAuthorization = () => {
  113. wx.getPrivacySetting({
  114. success(res) {
  115. if (res.needAuthorization) {
  116. requirePrivacyAuthorization()
  117. } else {
  118. privacyAuthed.value = true
  119. }
  120. },
  121. fail() {
  122. requirePrivacyAuthorization()
  123. },
  124. })
  125. }
  126. const requirePrivacyAuthorization = () => {
  127. wx.requirePrivacyAuthorize({
  128. success() {
  129. privacyAuthed.value = true
  130. },
  131. fail() {
  132. uni.showModal({
  133. title: '提示',
  134. content: '请同意隐私协议后再使用',
  135. showCancel: false,
  136. })
  137. },
  138. })
  139. }
  140. const handleOpenPrivacyContractClick = () => {
  141. wx.openPrivacyContract({})
  142. }
  143. /* ---------------- 验证码 ---------------- */
  144. const { countdown, disabled: codeDisabled, start: startCountdown } = useCodeCountdown()
  145. const toast = (title: string) => uni.showToast({ title, icon: 'none', duration: 800 })
  146. const sendCodeImpl = async () => {
  147. if (codeDisabled.value) return
  148. const [usernameOk, passwordOk] = await Promise.all([
  149. validateField('username'),
  150. validateField('password'),
  151. ])
  152. if (!usernameOk || !passwordOk) return
  153. uni.showLoading()
  154. const res = await getAuthTokenApi({
  155. username: form.username,
  156. password: form.password,
  157. })
  158. uni.hideLoading()
  159. if (res.access_token) {
  160. const codeRes = await getMobileCodeApi(form.username)
  161. if (codeRes.code === 0) {
  162. toast('验证码发送成功')
  163. startCountdown(60)
  164. } else {
  165. toast(codeRes.msg || '验证码发送失败')
  166. }
  167. }
  168. }
  169. const sendCode = debounce(sendCodeImpl)
  170. /* ---------------- 提交 ---------------- */
  171. const submit = async () => {
  172. if (!privacyAuthed.value) {
  173. requirePrivacyAuthorization()
  174. return
  175. }
  176. const agreementOk = agreementChecked.value
  177. agreementError.value = !agreementOk
  178. const formOk = await validate()
  179. if (!agreementOk || !formOk) return
  180. uni.showLoading()
  181. const res = await userStore.login({
  182. username: form.username,
  183. code: form.code,
  184. })
  185. if (!res.success) {
  186. uni.showModal({
  187. title: '登录失败',
  188. content: res.message ?? '登录失败,请重试',
  189. showCancel: false,
  190. confirmText: '我知道了',
  191. })
  192. return
  193. }
  194. uni.hideLoading()
  195. uni.reLaunch({
  196. url: '/pages/index/index',
  197. })
  198. }
  199. /* ---------------- 其他 ---------------- */
  200. const forgetPassword = () => {
  201. uni.navigateTo({
  202. url: '/pages/reset-password/index',
  203. })
  204. }
  205. </script>
  206. <style lang="scss" scoped>
  207. @import '../../styles/modules/auth-page.scss';
  208. </style>