From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/views/Login/components/ForgetPasswordForm.vue | 278 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 278 insertions(+), 0 deletions(-)
diff --git a/src/views/Login/components/ForgetPasswordForm.vue b/src/views/Login/components/ForgetPasswordForm.vue
new file mode 100644
index 0000000..f47b229
--- /dev/null
+++ b/src/views/Login/components/ForgetPasswordForm.vue
@@ -0,0 +1,278 @@
+<template>
+ <el-form
+ v-show="getShow"
+ ref="formSmsResetPassword"
+ :model="resetPasswordData"
+ :rules="rules"
+ class="login-form"
+ label-position="top"
+ label-width="120px"
+ size="large"
+ >
+ <el-row class="mx-[-10px]">
+ <!-- 绉熸埛鍚� -->
+ <el-col :span="24" class="px-10px">
+ <el-form-item>
+ <LoginFormTitle class="w-full" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="24" class="px-10px">
+ <el-form-item v-if="resetPasswordData.tenantEnable === 'true'" prop="tenantName">
+ <el-input
+ v-model="resetPasswordData.tenantName"
+ :placeholder="t('login.tenantNamePlaceholder')"
+ :prefix-icon="iconHouse"
+ type="primary"
+ link
+ />
+ </el-form-item>
+ </el-col>
+ <!-- 鎵嬫満鍙� -->
+ <el-col :span="24" class="px-10px">
+ <el-form-item prop="mobile">
+ <el-input
+ v-model="resetPasswordData.mobile"
+ :placeholder="t('login.mobileNumberPlaceholder')"
+ :prefix-icon="iconCellphone"
+ />
+ </el-form-item>
+ </el-col>
+ <Verify
+ ref="verify"
+ :captchaType="captchaType"
+ :imgSize="{ width: '400px', height: '200px' }"
+ mode="pop"
+ @success="getSmsCode"
+ />
+ <!-- 楠岃瘉鐮� -->
+ <el-col :span="24" class="px-10px">
+ <el-form-item prop="code">
+ <el-row :gutter="5" justify="space-between" style="width: 100%">
+ <el-col :span="24">
+ <el-input
+ v-model="resetPasswordData.code"
+ :placeholder="t('login.codePlaceholder')"
+ :prefix-icon="iconCircleCheck"
+ >
+ <template #append>
+ <span
+ v-if="mobileCodeTimer <= 0"
+ class="getMobileCode"
+ style="cursor: pointer"
+ @click="getCode"
+ >
+ {{ t('login.getSmsCode') }}
+ </span>
+ <span v-if="mobileCodeTimer > 0" class="getMobileCode" style="cursor: pointer">
+ {{ mobileCodeTimer }}绉掑悗鍙噸鏂拌幏鍙�
+ </span>
+ </template>
+ </el-input>
+ <!-- </el-button> -->
+ </el-col>
+ </el-row>
+ </el-form-item>
+ </el-col>
+ <el-col :span="24" class="px-10px">
+ <el-form-item prop="password">
+ <InputPassword
+ v-model="resetPasswordData.password"
+ :placeholder="t('login.passwordPlaceholder')"
+ class="w-full"
+ :strength="true"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="24" class="px-10px">
+ <el-form-item prop="check_password">
+ <InputPassword
+ v-model="resetPasswordData.check_password"
+ :placeholder="t('login.checkPassword')"
+ class="w-full"
+ :strength="true"
+ />
+ </el-form-item>
+ </el-col>
+ <!-- 鐧诲綍鎸夐挳 / 杩斿洖鎸夐挳 -->
+ <el-col :span="24" class="px-10px">
+ <el-form-item>
+ <XButton
+ :loading="loginLoading"
+ :title="t('login.resetPassword')"
+ class="w-full"
+ type="primary"
+ @click="resetPassword()"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="24" class="px-10px">
+ <el-form-item>
+ <XButton
+ :loading="loginLoading"
+ :title="t('login.backLogin')"
+ class="w-full"
+ @click="handleBackLogin()"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+</template>
+<script lang="ts" setup>
+import type { RouteLocationNormalizedLoaded } from 'vue-router'
+
+import { useIcon } from '@/hooks/web/useIcon'
+
+import { sendSmsCode, smsResetPassword } from '@/api/login'
+import LoginFormTitle from './LoginFormTitle.vue'
+import { LoginStateEnum, useFormValid, useLoginState } from './useLogin'
+import { ElLoading } from 'element-plus'
+import * as authUtil from '@/utils/auth'
+import * as LoginApi from '@/api/login'
+defineOptions({ name: 'ForgetPasswordForm' })
+const verify = ref()
+
+const { t } = useI18n()
+const message = useMessage()
+const { currentRoute } = useRouter()
+const formSmsResetPassword = ref()
+const loginLoading = ref(false)
+const iconHouse = useIcon({ icon: 'ep:house' })
+const iconCellphone = useIcon({ icon: 'ep:cellphone' })
+const iconCircleCheck = useIcon({ icon: 'ep:circle-check' })
+const { validForm } = useFormValid(formSmsResetPassword)
+const { handleBackLogin, getLoginState, setLoginState } = useLoginState()
+const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD)
+const captchaType = ref('blockPuzzle') // blockPuzzle 婊戝潡 clickWord 鐐瑰嚮鏂囧瓧 pictureWord 鏂囧瓧楠岃瘉鐮�
+
+const validatePass2 = (_rule, value, callback) => {
+ if (value === '') {
+ callback(new Error('璇峰啀娆¤緭鍏ュ瘑鐮�'))
+ } else if (value !== resetPasswordData.password) {
+ callback(new Error('涓ゆ杈撳叆瀵嗙爜涓嶄竴鑷�!'))
+ } else {
+ callback()
+ }
+}
+
+const rules = {
+ tenantName: [{ required: true, min: 2, max: 20, trigger: 'blur', message: '闀垮害涓�4鍒�16浣�' }],
+ mobile: [{ required: true, min: 11, max: 11, trigger: 'blur', message: '鎵嬫満鍙烽暱搴︿负11浣�' }],
+ password: [
+ {
+ required: true,
+ min: 4,
+ max: 16,
+ validator: validatePass2,
+ trigger: 'blur',
+ message: '瀵嗙爜闀垮害涓�4鍒�16浣�'
+ }
+ ],
+ check_password: [{ required: true, validator: validatePass2, trigger: 'blur' }],
+ code: [required]
+}
+
+const resetPasswordData = reactive({
+ captchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE,
+ tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
+ tenantName: '',
+ username: '',
+ password: '',
+ check_password: '',
+ mobile: '',
+ code: ''
+})
+
+const smsVO = reactive({
+ tenantName: '',
+ mobile: '',
+ captchaVerification: '',
+ scene: 23
+})
+const mobileCodeTimer = ref(0)
+const redirect = ref<string>('')
+
+// 鑾峰彇楠岃瘉鐮�
+const getCode = async () => {
+ // 鎯呭喌涓�锛屾湭寮�鍚細鍒欑洿鎺ュ彂閫侀獙璇佺爜
+ if (resetPasswordData.captchaEnable === 'false') {
+ await getSmsCode({})
+ } else {
+ // 鎯呭喌浜岋紝宸插紑鍚細鍒欏睍绀洪獙璇佺爜锛涘彧鏈夊畬鎴愰獙璇佺爜鐨勬儏鍐碉紝鎵嶈繘琛屽彂閫侀獙璇佺爜
+ // 寮瑰嚭楠岃瘉鐮�
+ verify.value.show()
+ }
+}
+
+const getSmsCode = async (params) => {
+ if (resetPasswordData.tenantEnable === 'true') {
+ await getTenantId()
+ }
+ smsVO.captchaVerification = params.captchaVerification
+ smsVO.mobile = resetPasswordData.mobile
+ await sendSmsCode(smsVO).then(async () => {
+ message.success(t('login.SmsSendMsg'))
+ // 璁剧疆鍊掕鏃�
+ mobileCodeTimer.value = 60
+ let msgTimer = setInterval(() => {
+ mobileCodeTimer.value = mobileCodeTimer.value - 1
+ if (mobileCodeTimer.value <= 0) {
+ clearInterval(msgTimer)
+ }
+ }, 1000)
+ })
+}
+watch(
+ () => currentRoute.value,
+ (route: RouteLocationNormalizedLoaded) => {
+ redirect.value = route?.query?.redirect as string
+ },
+ {
+ immediate: true
+ }
+)
+
+const getTenantId = async () => {
+ if (resetPasswordData.tenantEnable === 'true') {
+ const res = await LoginApi.getTenantIdByName(resetPasswordData.tenantName)
+ if (res == null) {
+ message.error(t('login.invalidTenantName'))
+ throw t('login.invalidTenantName')
+ }
+ authUtil.setTenantId(res)
+ }
+}
+
+// 閲嶇疆瀵嗙爜
+const resetPassword = async () => {
+ const data = await validForm()
+ if (!data) return
+ await getTenantId()
+ loginLoading.value = true
+ await smsResetPassword(resetPasswordData)
+ .then(async () => {
+ message.success(t('login.resetPasswordSuccess'))
+ setLoginState(LoginStateEnum.LOGIN)
+ })
+ .catch(() => {})
+ .finally(() => {
+ loginLoading.value = false
+ setTimeout(() => {
+ const loadingInstance = ElLoading.service()
+ loadingInstance.close()
+ }, 400)
+ })
+}
+</script>
+
+<style lang="scss" scoped>
+:deep(.anticon) {
+ &:hover {
+ color: var(--el-color-primary) !important;
+ }
+}
+
+.smsbtn {
+ margin-top: 33px;
+}
+</style>
--
Gitblit v1.8.0