ForgetPasswordForm.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <script lang="ts" setup>
  2. import type { RouteLocationNormalizedLoaded } from 'vue-router'
  3. import { ElLoading } from 'element-plus'
  4. import * as LoginApi from '@/api/login'
  5. import * as authUtil from '@/utils/auth'
  6. import { useIcon } from '@/hooks/web/useIcon'
  7. import { sendSmsCode, smsResetPassword } from '@/api/login'
  8. import LoginFormTitle from './LoginFormTitle.vue'
  9. import { LoginStateEnum, useFormValid, useLoginState } from './useLogin'
  10. defineOptions({ name: 'ForgetPasswordForm' })
  11. const { t } = useI18n()
  12. const message = useMessage()
  13. const { currentRoute } = useRouter()
  14. const formSmsResetPassword = ref()
  15. const loginLoading = ref(false)
  16. const iconHouse = useIcon({ icon: 'ep:house' })
  17. const iconCellphone = useIcon({ icon: 'ep:cellphone' })
  18. const iconCircleCheck = useIcon({ icon: 'ep:circle-check' })
  19. const { validForm } = useFormValid(formSmsResetPassword)
  20. const { handleBackLogin, getLoginState, setLoginState } = useLoginState()
  21. const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD)
  22. const resetPasswordData = reactive({
  23. tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
  24. tenantName: '',
  25. username: '',
  26. password: '',
  27. check_password: '',
  28. mobile: '',
  29. code: '',
  30. })
  31. const validatePass2 = (_rule, value) => {
  32. if (value === '') {
  33. return Promise.reject(new Error('请再次输入密码'))
  34. }
  35. if (value !== resetPasswordData.password) {
  36. return Promise.reject(new Error('两次输入密码不一致!'))
  37. }
  38. return Promise.resolve()
  39. }
  40. const rules = {
  41. tenantName: [{ required: true, min: 2, max: 20, trigger: 'blur', message: '长度为4到16位' }],
  42. mobile: [{ required: true, min: 11, max: 11, trigger: 'blur', message: '手机号长度为11位' }],
  43. password: [
  44. {
  45. required: true,
  46. min: 4,
  47. max: 16,
  48. validator: validatePass2,
  49. trigger: 'blur',
  50. message: '密码长度为4到16位',
  51. },
  52. ],
  53. check_password: [{ required: true, validator: validatePass2, trigger: 'blur' }],
  54. code: [required],
  55. }
  56. const smsVO = reactive({ tenantName: '', mobile: '', scene: 23 })
  57. const mobileCodeTimer = ref(0)
  58. const redirect = ref<string>('')
  59. const getTenantId = async () => {
  60. if (resetPasswordData.tenantEnable === 'true') {
  61. const res = await LoginApi.getTenantIdByName(resetPasswordData.tenantName)
  62. if (res == null) {
  63. message.error(t('login.invalidTenantName'))
  64. throw t('login.invalidTenantName')
  65. }
  66. authUtil.setTenantId(res)
  67. }
  68. }
  69. const getSmsCode = async () => {
  70. if (resetPasswordData.tenantEnable === 'true') {
  71. await getTenantId()
  72. }
  73. smsVO.mobile = resetPasswordData.mobile
  74. await sendSmsCode(smsVO).then(async () => {
  75. message.success(t('login.SmsSendMsg'))
  76. // 设置倒计时
  77. mobileCodeTimer.value = 60
  78. const msgTimer = setInterval(() => {
  79. mobileCodeTimer.value = mobileCodeTimer.value - 1
  80. if (mobileCodeTimer.value <= 0) {
  81. clearInterval(msgTimer)
  82. }
  83. }, 1000)
  84. })
  85. }
  86. // 获取验证码
  87. const getCode = async () => {
  88. await getSmsCode()
  89. }
  90. watch(
  91. () => currentRoute.value,
  92. (route: RouteLocationNormalizedLoaded) => {
  93. redirect.value = route?.query?.redirect as string
  94. },
  95. { immediate: true }
  96. )
  97. // 重置密码
  98. const resetPassword = async () => {
  99. const data = await validForm()
  100. if (!data) return
  101. await getTenantId()
  102. loginLoading.value = true
  103. await smsResetPassword(resetPasswordData)
  104. .then(async () => {
  105. message.success(t('login.resetPasswordSuccess'))
  106. setLoginState(LoginStateEnum.LOGIN)
  107. })
  108. .catch(() => {})
  109. .finally(() => {
  110. loginLoading.value = false
  111. setTimeout(() => {
  112. const loadingInstance = ElLoading.service()
  113. loadingInstance.close()
  114. }, 400)
  115. })
  116. }
  117. </script>
  118. <template>
  119. <el-form
  120. v-show="getShow"
  121. ref="formSmsResetPassword"
  122. :model="resetPasswordData"
  123. :rules="rules"
  124. class="login-form"
  125. label-position="top"
  126. label-width="120px"
  127. size="large">
  128. <el-row class="mx-[-10px]">
  129. <!-- 租户名 -->
  130. <el-col :span="24" class="px-10px">
  131. <el-form-item>
  132. <LoginFormTitle class="w-full" />
  133. </el-form-item>
  134. </el-col>
  135. <el-col :span="24" class="px-10px">
  136. <el-form-item v-if="resetPasswordData.tenantEnable === 'true'" prop="tenantName">
  137. <el-input
  138. v-model="resetPasswordData.tenantName"
  139. :placeholder="t('login.tenantNamePlaceholder')"
  140. :prefix-icon="iconHouse"
  141. type="primary"
  142. link />
  143. </el-form-item>
  144. </el-col>
  145. <!-- 手机号 -->
  146. <el-col :span="24" class="px-10px">
  147. <el-form-item prop="mobile">
  148. <el-input
  149. v-model="resetPasswordData.mobile"
  150. :placeholder="t('login.mobileNumberPlaceholder')"
  151. :prefix-icon="iconCellphone" />
  152. </el-form-item>
  153. </el-col>
  154. <!-- 短信验证码 -->
  155. <el-col :span="24" class="px-10px">
  156. <el-form-item prop="code">
  157. <el-row :gutter="5" justify="space-between" style="width: 100%">
  158. <el-col :span="24">
  159. <el-input
  160. v-model="resetPasswordData.code"
  161. :placeholder="t('login.codePlaceholder')"
  162. :prefix-icon="iconCircleCheck">
  163. <template #append>
  164. <span
  165. v-if="mobileCodeTimer <= 0"
  166. class="getMobileCode"
  167. style="cursor: pointer"
  168. @click="getCode">
  169. {{ t('login.getSmsCode') }}
  170. </span>
  171. <span v-if="mobileCodeTimer > 0" class="getMobileCode" style="cursor: pointer">
  172. {{ mobileCodeTimer }}秒后可重新获取
  173. </span>
  174. </template>
  175. </el-input>
  176. <!-- </el-button> -->
  177. </el-col>
  178. </el-row>
  179. </el-form-item>
  180. </el-col>
  181. <el-col :span="24" class="px-10px">
  182. <el-form-item prop="password">
  183. <InputPassword
  184. v-model="resetPasswordData.password"
  185. :placeholder="t('login.passwordPlaceholder')"
  186. class="w-full"
  187. :strength="true" />
  188. </el-form-item>
  189. </el-col>
  190. <el-col :span="24" class="px-10px">
  191. <el-form-item prop="check_password">
  192. <InputPassword
  193. v-model="resetPasswordData.check_password"
  194. :placeholder="t('login.checkPassword')"
  195. class="w-full"
  196. :strength="true" />
  197. </el-form-item>
  198. </el-col>
  199. <!-- 登录按钮 / 返回按钮 -->
  200. <el-col :span="24" class="px-10px">
  201. <el-form-item>
  202. <el-button :loading="loginLoading" class="w-full" type="primary" @click="resetPassword()">
  203. {{ t('login.resetPassword') }}
  204. </el-button>
  205. </el-form-item>
  206. </el-col>
  207. <el-col :span="24" class="px-10px">
  208. <el-form-item>
  209. <el-button :loading="loginLoading" class="w-full" @click="handleBackLogin()">
  210. {{ t('login.backLogin') }}
  211. </el-button>
  212. </el-form-item>
  213. </el-col>
  214. </el-row>
  215. </el-form>
  216. </template>
  217. <style lang="scss" scoped>
  218. :deep(.anticon) {
  219. &:hover {
  220. color: var(--el-color-primary) !important;
  221. }
  222. }
  223. .smsbtn {
  224. margin-top: 33px;
  225. }
  226. </style>