feat: 增加忘记密码
							parent
							
								
									ce41faacc5
								
							
						
					
					
						commit
						edfe5a56e2
					
				| 
						 | 
					@ -26,6 +26,9 @@ declare module 'vue' {
 | 
				
			||||||
    ASwitch: typeof import('ant-design-vue/es')['Switch']
 | 
					    ASwitch: typeof import('ant-design-vue/es')['Switch']
 | 
				
			||||||
    ATextarea: typeof import('ant-design-vue/es')['Textarea']
 | 
					    ATextarea: typeof import('ant-design-vue/es')['Textarea']
 | 
				
			||||||
    ATooltip: typeof import('ant-design-vue/es')['Tooltip']
 | 
					    ATooltip: typeof import('ant-design-vue/es')['Tooltip']
 | 
				
			||||||
 | 
					    ElButton: typeof import('element-plus/es')['ElButton']
 | 
				
			||||||
 | 
					    ElDialog: typeof import('element-plus/es')['ElDialog']
 | 
				
			||||||
 | 
					    ElInput: typeof import('element-plus/es')['ElInput']
 | 
				
			||||||
    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
 | 
					    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
 | 
				
			||||||
    Empty: typeof import('./src/components/empty/Empty.vue')['default']
 | 
					    Empty: typeof import('./src/components/empty/Empty.vue')['default']
 | 
				
			||||||
    ImageCropper: typeof import('./src/components/image-cropper/index.vue')['default']
 | 
					    ImageCropper: typeof import('./src/components/image-cropper/index.vue')['default']
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,12 +21,12 @@ export function logout() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export function getConfig(params) {
 | 
					export function getConfig(params) {
 | 
				
			||||||
  return request.get('/sms/v1/logo', {
 | 
					  return request.get('/sms/v1/logo', {
 | 
				
			||||||
    params: wrapperParams(params),
 | 
					    params: wrapperParams(params)
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export function getDict(data) {
 | 
					export function getDict(data) {
 | 
				
			||||||
  return request.get('/dict/children', {
 | 
					  return request.get('/dict/children', {
 | 
				
			||||||
    params: wrapperParams(data),
 | 
					    params: wrapperParams(data)
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export function getSysconf() {
 | 
					export function getSysconf() {
 | 
				
			||||||
| 
						 | 
					@ -48,7 +48,7 @@ export function getPortal(params) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export function getServiceQuota(tenantId, data) {
 | 
					export function getServiceQuota(tenantId, data) {
 | 
				
			||||||
  return request.get(`/cos/v1/tenants/${tenantId}/quotas`, {
 | 
					  return request.get(`/cos/v1/tenants/${tenantId}/quotas`, {
 | 
				
			||||||
    params: wrapperParams(data),
 | 
					    params: wrapperParams(data)
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export function replaceToken(params) {
 | 
					export function replaceToken(params) {
 | 
				
			||||||
| 
						 | 
					@ -69,7 +69,7 @@ export function getSystemTreeConfigs(params) {
 | 
				
			||||||
// 更新系统配置信息
 | 
					// 更新系统配置信息
 | 
				
			||||||
export function updateSystemConfigs(params) {
 | 
					export function updateSystemConfigs(params) {
 | 
				
			||||||
  return request.put('/sms/v1/system-configs', params, {
 | 
					  return request.put('/sms/v1/system-configs', params, {
 | 
				
			||||||
    headers: { 'Content-Type': 'multipart/form-data', BsmAjaxHeader: true, options: { noSeri: true } },
 | 
					    headers: { 'Content-Type': 'multipart/form-data', BsmAjaxHeader: true, options: { noSeri: true } }
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
// 测试连接
 | 
					// 测试连接
 | 
				
			||||||
| 
						 | 
					@ -79,3 +79,8 @@ export function testLinkApi(category) {
 | 
				
			||||||
export function getVertifyCode(params) {
 | 
					export function getVertifyCode(params) {
 | 
				
			||||||
  return request.post('/sms/v1/users/code', wrapperParams(params))
 | 
					  return request.post('/sms/v1/users/code', wrapperParams(params))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 忘记密码
 | 
				
			||||||
 | 
					export function forgetPassword(params) {
 | 
				
			||||||
 | 
					  return request.post('/sms/v1/users/forget', params)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,13 +42,42 @@
 | 
				
			||||||
              <a-switch v-model:checked="remember"></a-switch>
 | 
					              <a-switch v-model:checked="remember"></a-switch>
 | 
				
			||||||
              <span class="m-l-xs">记住密码</span>
 | 
					              <span class="m-l-xs">记住密码</span>
 | 
				
			||||||
            </span>
 | 
					            </span>
 | 
				
			||||||
            <a :href="`mailto:${configs.helpInformationLink}`" type="text" class="text-info pull-right help-info" :title="configs.helpInformationContent">{{ configs.helpInformationContent }}</a>
 | 
					            <a :href="`mailto:${configs.helpInformationLink}`" type="text" class="text-info help-info" :title="configs.helpInformationContent">{{ configs.helpInformationContent }}</a>
 | 
				
			||||||
 | 
					            <a type="text" class="m-l text-info forgot-password" @click="openForgotPasswordDialog">忘记密码</a>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </a-form-item>
 | 
					        </a-form-item>
 | 
				
			||||||
        <a-button class="login-btn" type="primary" size="medium" :loading="loading" @click="handleLogin">登录</a-button>
 | 
					        <a-button class="login-btn" type="primary" size="medium" :loading="loading" @click="handleLogin">登录</a-button>
 | 
				
			||||||
      </a-form>
 | 
					      </a-form>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div class="copyright-info">{{ configs.copyrightInformation }}</div>
 | 
					    <div class="copyright-info">{{ configs.copyrightInformation }}</div>
 | 
				
			||||||
 | 
					    <!-- 忘记密码弹窗 -->
 | 
				
			||||||
 | 
					    <a-modal title="忘记密码" v-model:visible="showForgotPasswordDialog" width="600px" :confirm-loading="forgotPasswordLoading" @ok="handleForgotPassword" :close-on-click-modal="false">
 | 
				
			||||||
 | 
					      <a-form :model="forgotPasswordForm" ref="forgotPasswordFormRef" label-position="left" :labelCol="{ style: { width: '80px' } }">
 | 
				
			||||||
 | 
					        <a-form-item label="用户名" name="account" :rules="[{ ...required, message: '请输入用户名' }]">
 | 
				
			||||||
 | 
					          <a-input class="w-lg" v-model:value="forgotPasswordForm.account" placeholder="请输入用户名"></a-input>
 | 
				
			||||||
 | 
					        </a-form-item>
 | 
				
			||||||
 | 
					        <a-form-item label="新密码" name="password" :rules="[{ ...required, message: '请输入密码' }]">
 | 
				
			||||||
 | 
					          <a-input class="w-lg" v-model:value="forgotPasswordForm.password" type="password" placeholder="请输入新密码" show-password></a-input>
 | 
				
			||||||
 | 
					        </a-form-item>
 | 
				
			||||||
 | 
					        <a-form-item label="确认密码" name="confirmPassword" :rules="[{ ...required, message: '请输入密码' }]">
 | 
				
			||||||
 | 
					          <a-input class="w-lg" v-model:value="forgotPasswordForm.confirmPassword" type="password" placeholder="请再次输入新密码" show-password></a-input>
 | 
				
			||||||
 | 
					        </a-form-item>
 | 
				
			||||||
 | 
					        <a-form-item label="验证码" name="verificationCode" :rules="[{ ...required, message: '请输入验证码' }]">
 | 
				
			||||||
 | 
					          <div class="verification-code-input">
 | 
				
			||||||
 | 
					            <a-input class="w-lg m-r" v-model:value="forgotPasswordForm.verificationCode" placeholder="请输入验证码"></a-input>
 | 
				
			||||||
 | 
					            <a-button type="primary" :disabled="codeCountdown > 0" @click="getVerificationCode" :loading="codeLoading">
 | 
				
			||||||
 | 
					              {{ codeCountdown > 0 ? `${codeCountdown}s后重新获取` : '获取验证码' }}
 | 
				
			||||||
 | 
					            </a-button>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </a-form-item>
 | 
				
			||||||
 | 
					      </a-form>
 | 
				
			||||||
 | 
					      <template #footer>
 | 
				
			||||||
 | 
					        <div class="dialog-footer">
 | 
				
			||||||
 | 
					          <a-button @click="showForgotPasswordDialog = false">取消</a-button>
 | 
				
			||||||
 | 
					          <a-button type="primary" :loading="forgotPasswordLoading" @click="handleForgotPassword">确定</a-button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </template>
 | 
				
			||||||
 | 
					    </a-modal>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,8 +90,8 @@ import { useStore } from 'vuex'
 | 
				
			||||||
import { useRouter, useRoute } from 'vue-router'
 | 
					import { useRouter, useRoute } from 'vue-router'
 | 
				
			||||||
import setLoginData from './tools'
 | 
					import setLoginData from './tools'
 | 
				
			||||||
import { required } from '@/validate'
 | 
					import { required } from '@/validate'
 | 
				
			||||||
import { getVertifyCode } from '@/services'
 | 
					import { getVertifyCode, forgetPassword } from '@/services'
 | 
				
			||||||
import { message } from 'ant-design-vue'
 | 
					import { message, Modal } from 'ant-design-vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  components: { UserOutlined, LockOutlined },
 | 
					  components: { UserOutlined, LockOutlined },
 | 
				
			||||||
| 
						 | 
					@ -78,11 +107,23 @@ export default {
 | 
				
			||||||
      verifyLoading: false,
 | 
					      verifyLoading: false,
 | 
				
			||||||
      countdown: 0,
 | 
					      countdown: 0,
 | 
				
			||||||
      isCountingDown: false,
 | 
					      isCountingDown: false,
 | 
				
			||||||
      needSmsVerify: true
 | 
					      needSmsVerify: true,
 | 
				
			||||||
 | 
					      showForgotPasswordDialog: false,
 | 
				
			||||||
 | 
					      forgotPasswordForm: {
 | 
				
			||||||
 | 
					        account: '',
 | 
				
			||||||
 | 
					        password: '',
 | 
				
			||||||
 | 
					        confirmPassword: '',
 | 
				
			||||||
 | 
					        verificationCode: ''
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      forgotPasswordLoading: false,
 | 
				
			||||||
 | 
					      codeCountdown: 0,
 | 
				
			||||||
 | 
					      codeLoading: false
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    const store = useStore()
 | 
					    const store = useStore()
 | 
				
			||||||
 | 
					    const pwdRule = computed(() => store.state.app.systemConfig.pwdStrength + ',pswNoSpace' + ',required')
 | 
				
			||||||
    const configs = computed(() => store.getters.pageConfig)
 | 
					    const configs = computed(() => store.getters.pageConfig)
 | 
				
			||||||
    const loginFormRef = ref(null)
 | 
					    const loginFormRef = ref(null)
 | 
				
			||||||
 | 
					    const forgotPasswordFormRef = ref(null)
 | 
				
			||||||
    let countdownTimer = null
 | 
					    let countdownTimer = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 开始倒计时
 | 
					    // 开始倒计时
 | 
				
			||||||
| 
						 | 
					@ -122,12 +163,15 @@ export default {
 | 
				
			||||||
    const route = useRoute()
 | 
					    const route = useRoute()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 监听account变化,如果清空则重置needSmsVerify
 | 
					    // 监听account变化,如果清空则重置needSmsVerify
 | 
				
			||||||
    watch(() => state.loginForm.account, (newAccount) => {
 | 
					    watch(
 | 
				
			||||||
 | 
					      () => state.loginForm.account,
 | 
				
			||||||
 | 
					      (newAccount) => {
 | 
				
			||||||
        if (!newAccount.trim()) {
 | 
					        if (!newAccount.trim()) {
 | 
				
			||||||
          state.needSmsVerify = true
 | 
					          state.needSmsVerify = true
 | 
				
			||||||
          resetCountdown() // 同时重置倒计时
 | 
					          resetCountdown() // 同时重置倒计时
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    })
 | 
					      }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const getVertify = () => {
 | 
					    const getVertify = () => {
 | 
				
			||||||
      if (state.isCountingDown) return // 如果正在倒计时,不允许重复点击
 | 
					      if (state.isCountingDown) return // 如果正在倒计时,不允许重复点击
 | 
				
			||||||
| 
						 | 
					@ -209,6 +253,58 @@ export default {
 | 
				
			||||||
        state.capsTooltip = false
 | 
					        state.capsTooltip = false
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function getVerificationCode() {
 | 
				
			||||||
 | 
					      if (!state.forgotPasswordForm.account) {
 | 
				
			||||||
 | 
					        message.error('请先输入用户名')
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      state.codeLoading = true
 | 
				
			||||||
 | 
					      getVertifyCode({ account: state.forgotPasswordForm.account })
 | 
				
			||||||
 | 
					        .then((res) => {
 | 
				
			||||||
 | 
					          if (res.success) {
 | 
				
			||||||
 | 
					            message.success('验证码已发送')
 | 
				
			||||||
 | 
					            state.codeCountdown = 60
 | 
				
			||||||
 | 
					            const timer = setInterval(() => {
 | 
				
			||||||
 | 
					              state.codeCountdown--
 | 
				
			||||||
 | 
					              if (state.codeCountdown <= 0) {
 | 
				
			||||||
 | 
					                clearInterval(timer)
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }, 1000)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .finally(() => {
 | 
				
			||||||
 | 
					          state.codeLoading = false
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function openForgotPasswordDialog() {
 | 
				
			||||||
 | 
					      // 将当前登录表单的用户名带入忘记密码表单
 | 
				
			||||||
 | 
					      state.forgotPasswordForm.account = state.loginForm.account
 | 
				
			||||||
 | 
					      state.showForgotPasswordDialog = true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function handleForgotPassword() {
 | 
				
			||||||
 | 
					      forgotPasswordFormRef.value.validate().then(() => {
 | 
				
			||||||
 | 
					        const { account, password, confirmPassword, verificationCode } = state.forgotPasswordForm
 | 
				
			||||||
 | 
					        if (confirmPassword !== password) return message.error('两次输入的密码不相同')
 | 
				
			||||||
 | 
					        state.forgotPasswordLoading = true
 | 
				
			||||||
 | 
					        forgetPassword({
 | 
				
			||||||
 | 
					          account,
 | 
				
			||||||
 | 
					          password: encrypt(password),
 | 
				
			||||||
 | 
					          code: verificationCode
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					          .then((res) => {
 | 
				
			||||||
 | 
					            if (res.success) {
 | 
				
			||||||
 | 
					              message.success(res.message)
 | 
				
			||||||
 | 
					              state.showForgotPasswordDialog = false
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					          .finally(() => {
 | 
				
			||||||
 | 
					            state.forgotPasswordLoading = false
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      ...toRefs(state),
 | 
					      ...toRefs(state),
 | 
				
			||||||
      required,
 | 
					      required,
 | 
				
			||||||
| 
						 | 
					@ -218,7 +314,12 @@ export default {
 | 
				
			||||||
      checkCapslock,
 | 
					      checkCapslock,
 | 
				
			||||||
      getVertify,
 | 
					      getVertify,
 | 
				
			||||||
      startCountdown,
 | 
					      startCountdown,
 | 
				
			||||||
      resetCountdown
 | 
					      resetCountdown,
 | 
				
			||||||
 | 
					      forgotPasswordFormRef,
 | 
				
			||||||
 | 
					      getVerificationCode,
 | 
				
			||||||
 | 
					      openForgotPasswordDialog,
 | 
				
			||||||
 | 
					      handleForgotPassword,
 | 
				
			||||||
 | 
					      pwdRule
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue