mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 22:57:17 +08:00 
			
		
		
		
	feat: 支持手机号登录(演示环境不开放)
1.在个人中心-安全设置中绑手机号后,才支持手机号登录 2.SMS4J(短信聚合框架,轻松集成多家短信服务,解决接入多个短信 SDK 的繁琐流程)
This commit is contained in:
		| @@ -4,8 +4,7 @@ import { UserState } from '@/store/modules/user/types'; | ||||
|  | ||||
| const BASE_URL = '/auth'; | ||||
|  | ||||
| export interface LoginReq { | ||||
|   phone?: string; | ||||
| export interface AccountLoginReq { | ||||
|   username?: string; | ||||
|   password?: string; | ||||
|   captcha: string; | ||||
| @@ -16,7 +15,7 @@ export interface LoginRes { | ||||
|   token: string; | ||||
| } | ||||
|  | ||||
| export function accountLogin(req: LoginReq) { | ||||
| export function accountLogin(req: AccountLoginReq) { | ||||
|   return axios.post<LoginRes>(`${BASE_URL}/account`, req); | ||||
| } | ||||
|  | ||||
| @@ -29,6 +28,15 @@ export function emailLogin(req: EmailLoginReq) { | ||||
|   return axios.post<LoginRes>(`${BASE_URL}/email`, req); | ||||
| } | ||||
|  | ||||
| export interface PhoneLoginReq { | ||||
|   phone: string; | ||||
|   captcha: string; | ||||
| } | ||||
|  | ||||
| export function phoneLogin(req: PhoneLoginReq) { | ||||
|   return axios.post<LoginRes>(`${BASE_URL}/phone`, req); | ||||
| } | ||||
|  | ||||
| export function logout() { | ||||
|   return axios.post(`${BASE_URL}/logout`); | ||||
| } | ||||
|   | ||||
| @@ -1,22 +1,19 @@ | ||||
| import axios from 'axios'; | ||||
| import qs from 'query-string'; | ||||
|  | ||||
| const BASE_URL = '/common/captcha'; | ||||
|  | ||||
| export interface ImageCaptchaRes { | ||||
|   uuid: string; | ||||
|   img: string; | ||||
| } | ||||
| export function getImageCaptcha() { | ||||
|   return axios.get<ImageCaptchaRes>('/common/captcha/img'); | ||||
|   return axios.get<ImageCaptchaRes>(`${BASE_URL}/img`); | ||||
| } | ||||
|  | ||||
| export interface MailCaptchaReq { | ||||
|   email: string; | ||||
| export function getMailCaptcha(email: string) { | ||||
|   return axios.get(`${BASE_URL}/mail?email=${email}`); | ||||
| } | ||||
| export function getMailCaptcha(params: MailCaptchaReq) { | ||||
|   return axios.get('/common/captcha/mail', { | ||||
|     params, | ||||
|     paramsSerializer: (obj) => { | ||||
|       return qs.stringify(obj); | ||||
|     }, | ||||
|   }); | ||||
|  | ||||
| export function getSmsCaptcha(phone: string) { | ||||
|   return axios.get(`${BASE_URL}/sms?phone=${phone}`); | ||||
| } | ||||
|   | ||||
| @@ -16,31 +16,41 @@ export function uploadAvatar(data: FormData) { | ||||
|   return axios.post<AvatarRes>(`${BASE_URL}/avatar`, data); | ||||
| } | ||||
|  | ||||
| export interface UpdateBasicInfoReq { | ||||
| export interface UserBasicInfoUpdateReq { | ||||
|   nickname: string; | ||||
|   gender: number; | ||||
| } | ||||
|  | ||||
| export function updateBasicInfo(req: UpdateBasicInfoReq) { | ||||
| export function updateBasicInfo(req: UserBasicInfoUpdateReq) { | ||||
|   return axios.patch(`${BASE_URL}/basic/info`, req); | ||||
| } | ||||
|  | ||||
| export interface UpdatePasswordReq { | ||||
| export interface UserPasswordUpdateReq { | ||||
|   oldPassword: string; | ||||
|   newPassword: string; | ||||
| } | ||||
|  | ||||
| export function updatePassword(req: UpdatePasswordReq) { | ||||
| export function updatePassword(req: UserPasswordUpdateReq) { | ||||
|   return axios.patch(`${BASE_URL}/password`, req); | ||||
| } | ||||
|  | ||||
| export interface UpdateEmailReq { | ||||
| export interface UserPhoneUpdateReq { | ||||
|   newPhone: string; | ||||
|   captcha: string; | ||||
|   currentPassword: string; | ||||
| } | ||||
|  | ||||
| export function updatePhone(req: UserPhoneUpdateReq) { | ||||
|   return axios.patch(`${BASE_URL}/phone`, req); | ||||
| } | ||||
|  | ||||
| export interface UserEmailUpdateReq { | ||||
|   newEmail: string; | ||||
|   captcha: string; | ||||
|   currentPassword: string; | ||||
| } | ||||
|  | ||||
| export function updateEmail(req: UpdateEmailReq) { | ||||
| export function updateEmail(req: UserEmailUpdateReq) { | ||||
|   return axios.patch(`${BASE_URL}/email`, req); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,11 @@ | ||||
| import { defineStore } from 'pinia'; | ||||
| import { | ||||
|   LoginReq, | ||||
|   AccountLoginReq, | ||||
|   EmailLoginReq, | ||||
|   PhoneLoginReq, | ||||
|   accountLogin as userAccountLogin, | ||||
|   emailLogin as userEmailLogin, | ||||
|   phoneLogin as userPhoneLogin, | ||||
|   socialLogin as userSocialLogin, | ||||
|   logout as userLogout, | ||||
|   getUserInfo, | ||||
| @@ -45,7 +47,7 @@ const useUserStore = defineStore('user', { | ||||
|     }, | ||||
|  | ||||
|     // 账号登录 | ||||
|     async accountLogin(req: LoginReq) { | ||||
|     async accountLogin(req: AccountLoginReq) { | ||||
|       try { | ||||
|         const res = await userAccountLogin(req); | ||||
|         setToken(res.data.token); | ||||
| @@ -66,6 +68,17 @@ const useUserStore = defineStore('user', { | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     // 手机号登录 | ||||
|     async phoneLogin(req: PhoneLoginReq) { | ||||
|       try { | ||||
|         const res = await userPhoneLogin(req); | ||||
|         setToken(res.data.token); | ||||
|       } catch (err) { | ||||
|         clearToken(); | ||||
|         throw err; | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     // 三方账号身份登录 | ||||
|     async socialLogin(source: string, req: any) { | ||||
|       try { | ||||
|   | ||||
| @@ -57,7 +57,7 @@ | ||||
|   import { useI18n } from 'vue-i18n'; | ||||
|   import { useStorage } from '@vueuse/core'; | ||||
|   import { useUserStore } from '@/store'; | ||||
|   import { LoginReq } from '@/api/auth'; | ||||
|   import { AccountLoginReq } from '@/api/auth'; | ||||
|   import { ValidatedError } from '@arco-design/web-vue'; | ||||
|   import { encryptByRsa } from '@/utils/encrypt'; | ||||
|   import { useRouter } from 'vue-router'; | ||||
| @@ -81,7 +81,7 @@ | ||||
|       password: loginConfig.value.password, | ||||
|       captcha: '', | ||||
|       uuid: '', | ||||
|     } as LoginReq, | ||||
|     } as AccountLoginReq, | ||||
|     rules: { | ||||
|       username: [ | ||||
|         { required: true, message: t('login.account.error.required.username') }, | ||||
|   | ||||
| @@ -93,9 +93,7 @@ | ||||
|       if (!valid) { | ||||
|         captchaLoading.value = true; | ||||
|         captchaBtnNameKey.value = 'login.captcha.ing'; | ||||
|         getMailCaptcha({ | ||||
|           email: form.value.email, | ||||
|         }) | ||||
|         getMailCaptcha(form.value.email) | ||||
|           .then((res) => { | ||||
|             captchaLoading.value = false; | ||||
|             captchaDisable.value = true; | ||||
| @@ -108,10 +106,7 @@ | ||||
|                 captchaTime.value | ||||
|               }s)`; | ||||
|               if (captchaTime.value <= 0) { | ||||
|                 window.clearInterval(captchaTimer.value); | ||||
|                 captchaTime.value = 60; | ||||
|                 captchaBtnNameKey.value = t('login.captcha.get'); | ||||
|                 captchaDisable.value = false; | ||||
|                 resetCaptcha(); | ||||
|               } | ||||
|             }, 1000); | ||||
|             proxy.$message.success(res.msg); | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|     layout="vertical" | ||||
|     size="large" | ||||
|     class="login-form" | ||||
|     @submit="handleLogin" | ||||
|   > | ||||
|     <a-form-item field="phone" hide-label> | ||||
|       <a-select :options="['+86']" style="flex: 1 1" default-value="+86" /> | ||||
| @@ -20,7 +21,7 @@ | ||||
|       <a-input | ||||
|         v-model="form.captcha" | ||||
|         :placeholder="$t('login.phone.placeholder.captcha')" | ||||
|         :max-length="6" | ||||
|         :max-length="4" | ||||
|         allow-clear | ||||
|         style="flex: 1 1" | ||||
|       /> | ||||
| @@ -33,8 +34,13 @@ | ||||
|         {{ captchaBtnName }} | ||||
|       </a-button> | ||||
|     </a-form-item> | ||||
|     <a-button class="btn" :loading="loading" type="primary" html-type="submit" | ||||
|       >{{ $t('login.button') }}(即将开放) | ||||
|     <a-button | ||||
|       class="btn" | ||||
|       :loading="loading" | ||||
|       type="primary" | ||||
|       html-type="submit" | ||||
|       :disabled="captchaDisable" | ||||
|       >{{ $t('login.button') }}(演示不开放) | ||||
|     </a-button> | ||||
|   </a-form> | ||||
| </template> | ||||
| @@ -42,21 +48,28 @@ | ||||
| <script lang="ts" setup> | ||||
|   import { getCurrentInstance, ref, toRefs, reactive, computed } from 'vue'; | ||||
|   import { useI18n } from 'vue-i18n'; | ||||
|   import { useRouter } from 'vue-router'; | ||||
|   import { ValidatedError } from '@arco-design/web-vue'; | ||||
|   import { useUserStore } from '@/store'; | ||||
|   import { LoginReq } from '@/api/auth'; | ||||
|   import { PhoneLoginReq } from '@/api/auth'; | ||||
|   import { getSmsCaptcha } from '@/api/common/captcha'; | ||||
|  | ||||
|   const { proxy } = getCurrentInstance() as any; | ||||
|   const { t } = useI18n(); | ||||
|   const router = useRouter(); | ||||
|   const userStore = useUserStore(); | ||||
|   const loading = ref(false); | ||||
|   const captchaLoading = ref(false); | ||||
|   const captchaDisable = ref(false); | ||||
|   const captchaDisable = ref(true); | ||||
|   const captchaTime = ref(60); | ||||
|   const captchaTimer = ref(); | ||||
|   const captchaBtnNameKey = ref('login.captcha.get'); | ||||
|   const captchaBtnName = computed(() => t(captchaBtnNameKey.value)); | ||||
|   const data = reactive({ | ||||
|     form: {} as LoginReq, | ||||
|     form: { | ||||
|       phone: '', | ||||
|       captcha: '', | ||||
|     } as PhoneLoginReq, | ||||
|     rules: { | ||||
|       phone: [ | ||||
|         { required: true, message: t('login.phone.error.required.phone') }, | ||||
| @@ -91,26 +104,71 @@ | ||||
|       if (!valid) { | ||||
|         captchaLoading.value = true; | ||||
|         captchaBtnNameKey.value = 'login.captcha.ing'; | ||||
|         captchaLoading.value = false; | ||||
|         captchaDisable.value = true; | ||||
|         captchaBtnNameKey.value = `${t( | ||||
|           'login.captcha.get' | ||||
|         )}(${(captchaTime.value -= 1)}s)`; | ||||
|         captchaTimer.value = window.setInterval(() => { | ||||
|           captchaTime.value -= 1; | ||||
|           captchaBtnNameKey.value = `${t('login.captcha.get')}(${ | ||||
|             captchaTime.value | ||||
|           }s)`; | ||||
|           if (captchaTime.value <= 0) { | ||||
|             window.clearInterval(captchaTimer.value); | ||||
|             captchaTime.value = 60; | ||||
|             captchaBtnNameKey.value = t('login.captcha.get'); | ||||
|             captchaDisable.value = false; | ||||
|           } | ||||
|         }, 1000); | ||||
|         getSmsCaptcha(form.value.phone) | ||||
|           .then((res) => { | ||||
|             captchaLoading.value = false; | ||||
|             captchaDisable.value = true; | ||||
|             captchaBtnNameKey.value = `${t( | ||||
|               'login.captcha.get' | ||||
|             )}(${(captchaTime.value -= 1)}s)`; | ||||
|             captchaTimer.value = window.setInterval(() => { | ||||
|               captchaTime.value -= 1; | ||||
|               captchaBtnNameKey.value = `${t('login.captcha.get')}(${ | ||||
|                 captchaTime.value | ||||
|               }s)`; | ||||
|               if (captchaTime.value <= 0) { | ||||
|                 resetCaptcha(); | ||||
|               } | ||||
|             }, 1000); | ||||
|             proxy.$message.success(res.msg); | ||||
|           }) | ||||
|           .catch(() => { | ||||
|             resetCaptcha(); | ||||
|             captchaLoading.value = false; | ||||
|           }); | ||||
|       } | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 登录 | ||||
|    * | ||||
|    * @param errors 表单验证错误 | ||||
|    * @param values 表单数据 | ||||
|    */ | ||||
|   const handleLogin = ({ | ||||
|     errors, | ||||
|     values, | ||||
|   }: { | ||||
|     errors: Record<string, ValidatedError> | undefined; | ||||
|     values: Record<string, any>; | ||||
|   }) => { | ||||
|     if (loading.value) return; | ||||
|     if (!errors) { | ||||
|       loading.value = true; | ||||
|       userStore | ||||
|         .phoneLogin({ | ||||
|           phone: values.phone, | ||||
|           captcha: values.captcha, | ||||
|         }) | ||||
|         .then(() => { | ||||
|           const { redirect, ...othersQuery } = router.currentRoute.value.query; | ||||
|           router.push({ | ||||
|             name: (redirect as string) || 'Workplace', | ||||
|             query: { | ||||
|               ...othersQuery, | ||||
|             }, | ||||
|           }); | ||||
|           proxy.$notification.success(t('login.success')); | ||||
|         }) | ||||
|         .catch(() => { | ||||
|           form.value.captcha = ''; | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           loading.value = false; | ||||
|         }); | ||||
|     } | ||||
|   }; | ||||
| </script> | ||||
|  | ||||
| <style lang="less" scoped> | ||||
|   | ||||
| @@ -62,9 +62,7 @@ | ||||
|         <a-input | ||||
|           v-model="form.captcha" | ||||
|           :placeholder=" | ||||
|             $t( | ||||
|               'userCenter.securitySettings.updateEmail.form.placeholder.captcha' | ||||
|             ) | ||||
|             $t('userCenter.securitySettings.form.placeholder.captcha') | ||||
|           " | ||||
|           :max-length="6" | ||||
|           allow-clear | ||||
| @@ -107,13 +105,12 @@ | ||||
|   import { getCurrentInstance, ref, reactive, computed } from 'vue'; | ||||
|   import { FieldRule } from '@arco-design/web-vue'; | ||||
|   import { getMailCaptcha } from '@/api/common/captcha'; | ||||
|   import { updateEmail } from '@/api/system/user-center'; | ||||
|   import { UserEmailUpdateReq, updateEmail } from '@/api/system/user-center'; | ||||
|   import { useI18n } from 'vue-i18n'; | ||||
|   import { useUserStore } from '@/store'; | ||||
|   import { encryptByRsa } from '@/utils/encrypt'; | ||||
|  | ||||
|   const { proxy } = getCurrentInstance() as any; | ||||
|  | ||||
|   const { t } = useI18n(); | ||||
|   const userStore = useUserStore(); | ||||
|   const captchaTime = ref(60); | ||||
| @@ -121,13 +118,11 @@ | ||||
|   const captchaLoading = ref(false); | ||||
|   const captchaDisable = ref(false); | ||||
|   const visible = ref(false); | ||||
|   const captchaBtnNameKey = ref( | ||||
|     'userCenter.securitySettings.updateEmail.form.sendCaptcha' | ||||
|   ); | ||||
|   const captchaBtnNameKey = ref('userCenter.securitySettings.captcha.get'); | ||||
|   const captchaBtnName = computed(() => t(captchaBtnNameKey.value)); | ||||
|  | ||||
|   // 表单数据 | ||||
|   const form = reactive({ | ||||
|   const form = reactive<UserEmailUpdateReq>({ | ||||
|     newEmail: '', | ||||
|     captcha: '', | ||||
|     currentPassword: '', | ||||
| @@ -152,9 +147,7 @@ | ||||
|       captcha: [ | ||||
|         { | ||||
|           required: true, | ||||
|           message: t( | ||||
|             'userCenter.securitySettings.updateEmail.form.error.required.captcha' | ||||
|           ), | ||||
|           message: t('userCenter.securitySettings.form.error.required.captcha'), | ||||
|         }, | ||||
|       ], | ||||
|       currentPassword: [ | ||||
| @@ -174,8 +167,7 @@ | ||||
|   const resetCaptcha = () => { | ||||
|     window.clearInterval(captchaTimer.value); | ||||
|     captchaTime.value = 60; | ||||
|     captchaBtnNameKey.value = | ||||
|       'userCenter.securitySettings.updateEmail.form.sendCaptcha'; | ||||
|     captchaBtnNameKey.value = 'userCenter.securitySettings.captcha.get'; | ||||
|     captchaDisable.value = false; | ||||
|   }; | ||||
|  | ||||
| @@ -187,29 +179,21 @@ | ||||
|     proxy.$refs.formRef.validateField('newEmail', (valid: any) => { | ||||
|       if (!valid) { | ||||
|         captchaLoading.value = true; | ||||
|         captchaBtnNameKey.value = | ||||
|           'userCenter.securitySettings.updateEmail.form.loading.sendCaptcha'; | ||||
|         getMailCaptcha({ | ||||
|           email: form.newEmail, | ||||
|         }) | ||||
|         captchaBtnNameKey.value = 'userCenter.securitySettings.captcha.ing'; | ||||
|         getMailCaptcha(form.newEmail) | ||||
|           .then((res) => { | ||||
|             captchaLoading.value = false; | ||||
|             captchaDisable.value = true; | ||||
|             captchaBtnNameKey.value = `${t( | ||||
|               'userCenter.securitySettings.updateEmail.form.reSendCaptcha' | ||||
|               'userCenter.securitySettings.captcha.get' | ||||
|             )}(${(captchaTime.value -= 1)}s)`; | ||||
|             captchaTimer.value = window.setInterval(() => { | ||||
|               captchaTime.value -= 1; | ||||
|               captchaBtnNameKey.value = `${t( | ||||
|                 'userCenter.securitySettings.updateEmail.form.reSendCaptcha' | ||||
|                 'userCenter.securitySettings.captcha.get' | ||||
|               )}(${captchaTime.value}s)`; | ||||
|               if (captchaTime.value <= 0) { | ||||
|                 window.clearInterval(captchaTimer.value); | ||||
|                 captchaTime.value = 60; | ||||
|                 captchaBtnNameKey.value = t( | ||||
|                   'userCenter.securitySettings.updateEmail.form.reSendCaptcha' | ||||
|                 ); | ||||
|                 captchaDisable.value = false; | ||||
|                 resetCaptcha(); | ||||
|               } | ||||
|             }, 1000); | ||||
|             proxy.$message.success(res.msg); | ||||
|   | ||||
| @@ -18,18 +18,232 @@ | ||||
|         </a-typography-paragraph> | ||||
|       </div> | ||||
|       <div class="operation"> | ||||
|         <a-link disabled :title="$t('userCenter.securitySettings.button.update')"> | ||||
|         <a-link | ||||
|           :title="$t('userCenter.securitySettings.button.update')" | ||||
|           @click="toUpdate" | ||||
|         > | ||||
|           {{ $t('userCenter.securitySettings.button.update') }} | ||||
|         </a-link> | ||||
|       </div> | ||||
|     </template> | ||||
|   </a-list-item-meta> | ||||
|  | ||||
|   <a-modal | ||||
|     :title="$t('userCenter.securitySettings.updatePhone.modal.title')" | ||||
|     :visible="visible" | ||||
|     :mask-closable="false" | ||||
|     :esc-to-close="false" | ||||
|     @ok="handleUpdate" | ||||
|     @cancel="handleCancel" | ||||
|   > | ||||
|     <a-form ref="formRef" :model="form" :rules="rules" size="large"> | ||||
|       <a-form-item | ||||
|         :label=" | ||||
|           $t('userCenter.securitySettings.updatePhone.form.label.newPhone') | ||||
|         " | ||||
|         field="newPhone" | ||||
|       > | ||||
|         <a-input | ||||
|           v-model="form.newPhone" | ||||
|           :placeholder=" | ||||
|             $t( | ||||
|               'userCenter.securitySettings.updatePhone.form.placeholder.newPhone' | ||||
|             ) | ||||
|           " | ||||
|           allow-clear | ||||
|         /> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         :label=" | ||||
|           $t('userCenter.securitySettings.updatePhone.form.label.captcha') | ||||
|         " | ||||
|         field="captcha" | ||||
|       > | ||||
|         <a-input | ||||
|           v-model="form.captcha" | ||||
|           :placeholder=" | ||||
|             $t('userCenter.securitySettings.form.placeholder.captcha') | ||||
|           " | ||||
|           :max-length="4" | ||||
|           allow-clear | ||||
|           style="width: 80%" | ||||
|         /> | ||||
|         <a-button | ||||
|           :loading="captchaLoading" | ||||
|           type="primary" | ||||
|           :disabled="captchaDisable" | ||||
|           class="captcha-btn" | ||||
|           @click="handleSendCaptcha" | ||||
|         > | ||||
|           {{ captchaBtnName }} | ||||
|         </a-button> | ||||
|       </a-form-item> | ||||
|       <a-form-item | ||||
|         :label=" | ||||
|           $t( | ||||
|             'userCenter.securitySettings.updatePhone.form.label.currentPassword' | ||||
|           ) | ||||
|         " | ||||
|         field="currentPassword" | ||||
|       > | ||||
|         <a-input-password | ||||
|           v-model="form.currentPassword" | ||||
|           :placeholder=" | ||||
|             $t( | ||||
|               'userCenter.securitySettings.updatePhone.form.placeholder.currentPassword' | ||||
|             ) | ||||
|           " | ||||
|           :max-length="32" | ||||
|           allow-clear | ||||
|         /> | ||||
|       </a-form-item> | ||||
|     </a-form> | ||||
|   </a-modal> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts" setup> | ||||
|   import { getCurrentInstance, ref, reactive, computed } from 'vue'; | ||||
|   import { FieldRule } from '@arco-design/web-vue'; | ||||
|   import { getSmsCaptcha } from '@/api/common/captcha'; | ||||
|   import { UserPhoneUpdateReq, updatePhone } from '@/api/system/user-center'; | ||||
|   import { useI18n } from 'vue-i18n'; | ||||
|   import { useUserStore } from '@/store'; | ||||
|   import { encryptByRsa } from '@/utils/encrypt'; | ||||
|  | ||||
|   const { proxy } = getCurrentInstance() as any; | ||||
|   const { t } = useI18n(); | ||||
|   const userStore = useUserStore(); | ||||
|   const captchaTime = ref(60); | ||||
|   const captchaTimer = ref(); | ||||
|   const captchaLoading = ref(false); | ||||
|   const captchaDisable = ref(true); | ||||
|   const visible = ref(false); | ||||
|   const captchaBtnNameKey = ref('userCenter.securitySettings.captcha.get'); | ||||
|   const captchaBtnName = computed(() => t(captchaBtnNameKey.value)); | ||||
|  | ||||
|   // 表单数据 | ||||
|   const form = reactive<UserPhoneUpdateReq>({ | ||||
|     newPhone: '', | ||||
|     captcha: '', | ||||
|     currentPassword: '', | ||||
|   }); | ||||
|   // 表单验证规则 | ||||
|   const rules = computed((): Record<string, FieldRule[]> => { | ||||
|     return { | ||||
|       newPhone: [ | ||||
|         { | ||||
|           required: true, | ||||
|           message: t( | ||||
|             'userCenter.securitySettings.updatePhone.form.error.required.newPhone' | ||||
|           ), | ||||
|         }, | ||||
|         { | ||||
|           match: /^1[3-9]\d{9}$/, | ||||
|           message: t( | ||||
|             'userCenter.securitySettings.updatePhone.form.error.match.newPhone' | ||||
|           ), | ||||
|         }, | ||||
|       ], | ||||
|       captcha: [ | ||||
|         { | ||||
|           required: true, | ||||
|           message: t('userCenter.securitySettings.form.error.required.captcha'), | ||||
|         }, | ||||
|       ], | ||||
|       currentPassword: [ | ||||
|         { | ||||
|           required: true, | ||||
|           message: t( | ||||
|             'userCenter.securitySettings.updatePhone.form.error.required.currentPassword' | ||||
|           ), | ||||
|         }, | ||||
|       ], | ||||
|     }; | ||||
|   }); | ||||
|  | ||||
|   /** | ||||
|    * 重置验证码 | ||||
|    */ | ||||
|   const resetCaptcha = () => { | ||||
|     window.clearInterval(captchaTimer.value); | ||||
|     captchaTime.value = 60; | ||||
|     captchaBtnNameKey.value = 'userCenter.securitySettings.captcha.get'; | ||||
|     captchaDisable.value = false; | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 发送验证码 | ||||
|    */ | ||||
|   const handleSendCaptcha = () => { | ||||
|     if (captchaLoading.value) return; | ||||
|     proxy.$refs.formRef.validateField('newPhone', (valid: any) => { | ||||
|       if (!valid) { | ||||
|         captchaLoading.value = true; | ||||
|         captchaBtnNameKey.value = 'userCenter.securitySettings.captcha.ing'; | ||||
|         getSmsCaptcha(form.newPhone) | ||||
|           .then((res) => { | ||||
|             captchaLoading.value = false; | ||||
|             captchaDisable.value = true; | ||||
|             captchaBtnNameKey.value = `${t( | ||||
|               'userCenter.securitySettings.captcha.get' | ||||
|             )}(${(captchaTime.value -= 1)}s)`; | ||||
|             captchaTimer.value = window.setInterval(() => { | ||||
|               captchaTime.value -= 1; | ||||
|               captchaBtnNameKey.value = `${t( | ||||
|                 'userCenter.securitySettings.captcha.get' | ||||
|               )}(${captchaTime.value}s)`; | ||||
|               if (captchaTime.value <= 0) { | ||||
|                 resetCaptcha(); | ||||
|               } | ||||
|             }, 1000); | ||||
|             proxy.$message.success(res.msg); | ||||
|           }) | ||||
|           .catch(() => { | ||||
|             resetCaptcha(); | ||||
|             captchaLoading.value = false; | ||||
|           }); | ||||
|       } | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 取消 | ||||
|    */ | ||||
|   const handleCancel = () => { | ||||
|     visible.value = false; | ||||
|     proxy.$refs.formRef.resetFields(); | ||||
|     resetCaptcha(); | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 修改 | ||||
|    */ | ||||
|   const handleUpdate = () => { | ||||
|     proxy.$refs.formRef.validate((valid: any) => { | ||||
|       if (!valid) { | ||||
|         updatePhone({ | ||||
|           newPhone: form.newPhone, | ||||
|           captcha: form.captcha, | ||||
|           currentPassword: encryptByRsa(form.currentPassword) || '', | ||||
|         }).then((res) => { | ||||
|           handleCancel(); | ||||
|           userStore.getInfo(); | ||||
|           proxy.$message.success(res.msg); | ||||
|         }); | ||||
|       } | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   /** | ||||
|    * 打开修改对话框 | ||||
|    */ | ||||
|   const toUpdate = () => { | ||||
|     visible.value = true; | ||||
|   }; | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="less"></style> | ||||
| <style scoped lang="less"> | ||||
|   .captcha-btn { | ||||
|     margin-left: 5px; | ||||
|   } | ||||
| </style> | ||||
|   | ||||
| @@ -74,6 +74,24 @@ export default { | ||||
|     'It is used to receive messages, verify identity, and support mobile phone verification code login after binding', | ||||
|   'userCenter.securitySettings.phone.content': 'Unbound', | ||||
|  | ||||
|   'userCenter.securitySettings.updatePhone.modal.title': 'Update phone', | ||||
|   'userCenter.securitySettings.updatePhone.form.label.newPhone': 'New phone', | ||||
|   'userCenter.securitySettings.updatePhone.form.label.captcha': 'Captcha', | ||||
|   'userCenter.securitySettings.updatePhone.form.label.currentPassword': | ||||
|     'Current password', | ||||
|  | ||||
|   'userCenter.securitySettings.updatePhone.form.placeholder.newPhone': | ||||
|     'Please enter new phone', | ||||
|   'userCenter.securitySettings.updatePhone.form.placeholder.currentPassword': | ||||
|     'Please enter current password', | ||||
|  | ||||
|   'userCenter.securitySettings.updatePhone.form.error.required.newPhone': | ||||
|     'Please enter new phone', | ||||
|   'userCenter.securitySettings.updatePhone.form.error.match.newPhone': | ||||
|     'Please enter the correct phone', | ||||
|   'userCenter.securitySettings.updatePhone.form.error.required.currentPassword': | ||||
|     'Please enter current password', | ||||
|  | ||||
|   // update-email | ||||
|   'userCenter.securitySettings.email.label': 'Email', | ||||
|   'userCenter.securitySettings.email.tip': | ||||
| @@ -85,16 +103,9 @@ export default { | ||||
|   'userCenter.securitySettings.updateEmail.form.label.captcha': 'Captcha', | ||||
|   'userCenter.securitySettings.updateEmail.form.label.currentPassword': | ||||
|     'Current password', | ||||
|   'userCenter.securitySettings.updateEmail.form.sendCaptcha': 'Send captcha', | ||||
|   'userCenter.securitySettings.updateEmail.form.reSendCaptcha': | ||||
|     'Resend captcha', | ||||
|   'userCenter.securitySettings.updateEmail.form.loading.sendCaptcha': | ||||
|     'Sending...', | ||||
|  | ||||
|   'userCenter.securitySettings.updateEmail.form.placeholder.newEmail': | ||||
|     'Please enter new email', | ||||
|   'userCenter.securitySettings.updateEmail.form.placeholder.captcha': | ||||
|     'Please enter email captcha', | ||||
|   'userCenter.securitySettings.updateEmail.form.placeholder.currentPassword': | ||||
|     'Please enter current password', | ||||
|  | ||||
| @@ -102,8 +113,6 @@ export default { | ||||
|     'Please enter new email', | ||||
|   'userCenter.securitySettings.updateEmail.form.error.match.newEmail': | ||||
|     'Please enter the correct email', | ||||
|   'userCenter.securitySettings.updateEmail.form.error.required.captcha': | ||||
|     'Please enter email captcha', | ||||
|   'userCenter.securitySettings.updateEmail.form.error.required.currentPassword': | ||||
|     'Please enter current password', | ||||
|  | ||||
| @@ -115,4 +124,10 @@ export default { | ||||
|  | ||||
|   'userCenter.securitySettings.content.hasBeenSet': 'Has been set', | ||||
|   'userCenter.securitySettings.button.update': 'Update', | ||||
|   'userCenter.securitySettings.captcha.get': 'Get captcha', | ||||
|   'userCenter.securitySettings.captcha.ing': 'Sending...', | ||||
|   'userCenter.securitySettings.form.placeholder.captcha': | ||||
|     'Please enter captcha', | ||||
|   'userCenter.securitySettings.form.error.required.captcha': | ||||
|     'Please enter captcha', | ||||
| }; | ||||
|   | ||||
| @@ -70,6 +70,25 @@ export default { | ||||
|     '用于接收消息、验证身份,绑定后可支持手机验证码登录', | ||||
|   'userCenter.securitySettings.phone.content': '未绑定', | ||||
|  | ||||
|   'userCenter.securitySettings.updatePhone.modal.title': '修改手机号', | ||||
|   'userCenter.securitySettings.updatePhone.form.label.newPhone': '新手机号', | ||||
|   'userCenter.securitySettings.updatePhone.form.label.captcha': '验证码', | ||||
|   'userCenter.securitySettings.updatePhone.form.label.currentPassword': | ||||
|     '当前密码', | ||||
|  | ||||
|   'userCenter.securitySettings.updatePhone.form.placeholder.newPhone': | ||||
|     '请输入新手机号', | ||||
|   'userCenter.securitySettings.updatePhone.form.placeholder.currentPassword': | ||||
|     '请输入当前密码', | ||||
|  | ||||
|   'userCenter.securitySettings.updatePhone.form.error.required.newPhone': | ||||
|     '请输入新手机号', | ||||
|   'userCenter.securitySettings.updatePhone.form.error.match.newPhone': | ||||
|     '请输入正确的手机号', | ||||
|  | ||||
|   'userCenter.securitySettings.updatePhone.form.error.required.currentPassword': | ||||
|     '请输入当前密码', | ||||
|  | ||||
|   // update-email | ||||
|   'userCenter.securitySettings.email.label': '安全邮箱', | ||||
|   'userCenter.securitySettings.email.tip': | ||||
| @@ -81,15 +100,9 @@ export default { | ||||
|   'userCenter.securitySettings.updateEmail.form.label.captcha': '验证码', | ||||
|   'userCenter.securitySettings.updateEmail.form.label.currentPassword': | ||||
|     '当前密码', | ||||
|   'userCenter.securitySettings.updateEmail.form.sendCaptcha': '发送验证码', | ||||
|   'userCenter.securitySettings.updateEmail.form.reSendCaptcha': '重新发送', | ||||
|   'userCenter.securitySettings.updateEmail.form.loading.sendCaptcha': | ||||
|     '发送中...', | ||||
|  | ||||
|   'userCenter.securitySettings.updateEmail.form.placeholder.newEmail': | ||||
|     '请输入新邮箱', | ||||
|   'userCenter.securitySettings.updateEmail.form.placeholder.captcha': | ||||
|     '请输入邮箱验证码', | ||||
|   'userCenter.securitySettings.updateEmail.form.placeholder.currentPassword': | ||||
|     '请输入当前密码', | ||||
|  | ||||
| @@ -97,8 +110,6 @@ export default { | ||||
|     '请输入新邮箱', | ||||
|   'userCenter.securitySettings.updateEmail.form.error.match.newEmail': | ||||
|     '请输入正确的邮箱', | ||||
|   'userCenter.securitySettings.updateEmail.form.error.required.captcha': | ||||
|     '请输入邮箱验证码', | ||||
|   'userCenter.securitySettings.updateEmail.form.error.required.currentPassword': | ||||
|     '请输入当前密码', | ||||
|  | ||||
| @@ -109,4 +120,8 @@ export default { | ||||
|  | ||||
|   'userCenter.securitySettings.content.hasBeenSet': '已设置', | ||||
|   'userCenter.securitySettings.button.update': '修改', | ||||
|   'userCenter.securitySettings.captcha.get': '获取验证码', | ||||
|   'userCenter.securitySettings.captcha.ing': '发送中...', | ||||
|   'userCenter.securitySettings.form.placeholder.captcha': '请输入验证码', | ||||
|   'userCenter.securitySettings.form.error.required.captcha': '请输入验证码', | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user