register.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <route lang="json5" type="home">
  2. {
  3. layout: 'default',
  4. style: {
  5. navigationStyle: 'custom',
  6. },
  7. }
  8. </route>
  9. <script lang="ts" setup>
  10. import { getCode, register } from '@/api/login'
  11. import { t } from '@/locale'
  12. import { goBack, toPage } from '@/utils/page'
  13. import { toast } from '@/utils/toast'
  14. defineOptions({
  15. name: 'Register', // 注册
  16. })
  17. // 获取屏幕边界到安全区域距离
  18. const systemInfo = uni.getSystemInfoSync()
  19. const safeAreaInsets = systemInfo.safeAreaInsets
  20. // 表单数据
  21. const formData = ref({
  22. name: '',
  23. phone: '',
  24. verifyCode: '',
  25. pwd: '',
  26. code: '',
  27. })
  28. // 验证码倒计时
  29. const countdown = ref(0)
  30. const countdownTimer = ref<any>(null)
  31. // 获取验证码
  32. async function getVerificationCode() {
  33. // 验证手机号
  34. if (!formData.value.phone.trim()) {
  35. toast.error(t('auth.register.error.emptyPhone'))
  36. return
  37. }
  38. // 验证手机号格式
  39. const phoneRegex = /^1[3-9]\d{9}$/
  40. if (!phoneRegex.test(formData.value.phone)) {
  41. toast.error(t('auth.register.error.emptyPhone'))
  42. return
  43. }
  44. // 防止重复点击
  45. if (countdown.value > 0) {
  46. return
  47. }
  48. try {
  49. // 显示加载状态
  50. uni.showLoading({
  51. title: t('common.loading'),
  52. mask: true,
  53. })
  54. // 调用获取验证码接口
  55. await getCode(formData.value.phone)
  56. uni.hideLoading()
  57. toast.success(t('auth.register.success.codeSent'))
  58. // 开始倒计时
  59. countdown.value = 60
  60. countdownTimer.value = setInterval(() => {
  61. countdown.value--
  62. if (countdown.value <= 0) {
  63. clearInterval(countdownTimer.value!)
  64. countdownTimer.value = null
  65. }
  66. }, 1000)
  67. }
  68. catch (error) {
  69. uni.hideLoading()
  70. toast.error(error.message || 'Failed to send verification code')
  71. }
  72. }
  73. // 注册处理
  74. async function handleRegister() {
  75. try {
  76. // 表单验证
  77. const isValid = await validateForm()
  78. if (!isValid)
  79. return
  80. // 显示加载状态
  81. uni.showLoading({
  82. title: t('common.saving'),
  83. mask: true,
  84. })
  85. // 调用注册接口
  86. const registerData = {
  87. name: formData.value.name,
  88. phone: formData.value.phone,
  89. verifyCode: formData.value.verifyCode,
  90. pwd: formData.value.pwd,
  91. code: formData.value.code,
  92. }
  93. await register(registerData)
  94. uni.hideLoading()
  95. // 注册成功
  96. toast.success(t('auth.register.success.registered'))
  97. setTimeout(() => {
  98. toPage('/pages/login/login', {}, true)
  99. }, 1500)
  100. }
  101. catch (error) {
  102. uni.hideLoading()
  103. toast.error(error.message || t('auth.register.error.registrationFailed'))
  104. }
  105. }
  106. // 表单验证
  107. function validateForm() {
  108. return new Promise((resolve) => {
  109. // 验证必填字段
  110. if (!formData.value.name.trim()) {
  111. toast.error(t('auth.register.error.emptyUsername'))
  112. resolve(false)
  113. return
  114. }
  115. if (!formData.value.phone.trim()) {
  116. toast.error(t('auth.register.error.emptyPhone'))
  117. resolve(false)
  118. return
  119. }
  120. // 验证手机号格式
  121. const phoneRegex = /^1[3-9]\d{9}$/
  122. if (!phoneRegex.test(formData.value.phone)) {
  123. toast.error(t('auth.register.error.emptyPhone'))
  124. resolve(false)
  125. return
  126. }
  127. if (!formData.value.verifyCode.trim()) {
  128. toast.error(t('auth.register.error.emptyVerifyCode'))
  129. resolve(false)
  130. return
  131. }
  132. if (!formData.value.pwd.trim()) {
  133. toast.error(t('auth.register.error.emptyPassword'))
  134. resolve(false)
  135. return
  136. }
  137. // 验证密码长度
  138. if (formData.value.pwd.length < 6 || formData.value.pwd.length > 20) {
  139. toast.error(t('auth.register.error.passwordLength'))
  140. resolve(false)
  141. return
  142. }
  143. resolve(true)
  144. })
  145. }
  146. // 页面卸载时清理定时器
  147. onUnmounted(() => {
  148. if (countdownTimer.value) {
  149. clearInterval(countdownTimer.value)
  150. }
  151. })
  152. </script>
  153. <template>
  154. <view class="register-page min-h-screen bg-white">
  155. <!-- 背景图片区域 -->
  156. <view class="auth-bg-section relative">
  157. <!-- 自定义导航栏 -->
  158. <view :style="{ paddingTop: `${safeAreaInsets?.top}px` }">
  159. <view class="h-88rpx flex items-center px-24rpx">
  160. <wd-icon name="thin-arrow-left" size="32rpx" @click="() => goBack()" />
  161. </view>
  162. </view>
  163. <!-- Logo和标语 -->
  164. <view class="pb-40rpx pt-60rpx text-center">
  165. <view class="mb-20rpx flex items-center justify-center">
  166. <image src="/static/logo.png" class="mr-16rpx h-60rpx w-60rpx" />
  167. </view>
  168. </view>
  169. </view>
  170. <!-- 表单内容区域 -->
  171. <view class="flex flex-col px-20rpx">
  172. <view class="mb-40rpx" />
  173. <!-- 注册表单 -->
  174. <wd-form ref="form" :model="formData">
  175. <view class="mb-40rpx space-y-32rpx">
  176. <wd-input
  177. v-model="formData.name"
  178. prop="name"
  179. :placeholder="t('auth.register.username.placeholder')"
  180. no-border
  181. custom-class="bandhu-auth-input-field"
  182. />
  183. <wd-input
  184. v-model="formData.phone"
  185. prop="phone"
  186. :placeholder="t('auth.register.phone.placeholder')"
  187. no-border
  188. custom-class="bandhu-auth-input-field"
  189. />
  190. <view class="flex items-center gap-20rpx">
  191. <wd-input
  192. v-model="formData.verifyCode"
  193. prop="verifyCode"
  194. :placeholder="t('auth.register.verifyCode.placeholder')"
  195. no-border
  196. custom-class="flex-1 bandhu-auth-input-field"
  197. />
  198. <wd-button
  199. type="error"
  200. plain
  201. :disabled="countdown > 0"
  202. custom-class="bandhu-auth-secondary-btn"
  203. @click="getVerificationCode"
  204. >
  205. {{ countdown > 0 ? `${countdown}s` : t('auth.register.getCode') }}
  206. </wd-button>
  207. </view>
  208. <wd-input
  209. v-model="formData.pwd"
  210. prop="pwd"
  211. :placeholder="t('auth.register.password.placeholder')"
  212. no-border
  213. show-password
  214. custom-class="bandhu-auth-input-field"
  215. />
  216. <wd-input
  217. v-model="formData.code"
  218. :placeholder="t('auth.register.referrerCode.placeholder')"
  219. no-border
  220. custom-class="bandhu-auth-input-field"
  221. />
  222. </view>
  223. <!-- 注册按钮 -->
  224. <wd-button
  225. type="error"
  226. size="large"
  227. custom-class="mb-200rpx w-full bandhu-auth-primary-btn"
  228. @click="handleRegister"
  229. >
  230. {{ $t('auth.register.button') }}
  231. </wd-button>
  232. </wd-form>
  233. <!-- 登录提示 -->
  234. <view class="text-center">
  235. <text class="text-28rpx text-#666">
  236. {{ $t('auth.register.hasAccount') }}
  237. </text>
  238. <text class="ml-10rpx text-28rpx text-[var(--wot-color-theme)]" @click="toPage('/pages/login/login')">
  239. {{ $t('auth.register.loginNow') }}
  240. </text>
  241. </view>
  242. </view>
  243. </view>
  244. </template>
  245. <style lang="scss" scoped>
  246. // 注册页面特有样式(如果有的话)
  247. </style>