mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-11-04 20:28:45 +08:00 
			
		
		
		
	Merge branch 'dev' of https://gitee.com/scholarli/yudao-ui-admin-vue3 into dev
# Conflicts: # src/views/Login/components/RegisterForm.vue
This commit is contained in:
		@@ -1,6 +1,6 @@
 | 
			
		||||
import request from '@/config/axios'
 | 
			
		||||
import { getRefreshToken } from '@/utils/auth'
 | 
			
		||||
import type { UserLoginVO } from './types'
 | 
			
		||||
import type { RegisterVO, UserLoginVO } from './types'
 | 
			
		||||
 | 
			
		||||
export interface SmsCodeVO {
 | 
			
		||||
  mobile: string
 | 
			
		||||
@@ -17,6 +17,11 @@ export const login = (data: UserLoginVO) => {
 | 
			
		||||
  return request.post({ url: '/system/auth/login', data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 注册
 | 
			
		||||
export const register = (data: RegisterVO) => {
 | 
			
		||||
  return request.post({ url: '/system/auth/register', data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 刷新访问令牌
 | 
			
		||||
export const refreshToken = () => {
 | 
			
		||||
  return request.post({ url: '/system/auth/refresh-token?refreshToken=' + getRefreshToken() })
 | 
			
		||||
 
 | 
			
		||||
@@ -29,3 +29,10 @@ export type UserVO = {
 | 
			
		||||
  loginIp: string
 | 
			
		||||
  loginDate: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type RegisterVO = {
 | 
			
		||||
  tenantName: string
 | 
			
		||||
  username: string
 | 
			
		||||
  password: string
 | 
			
		||||
  captchaVerification: string
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,142 +1,282 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <Form
 | 
			
		||||
  <el-form
 | 
			
		||||
    v-show="getShow"
 | 
			
		||||
    :rules="rules"
 | 
			
		||||
    :schema="schema"
 | 
			
		||||
    class="w-[100%] dark:(border-1 border-[var(--el-border-color)] border-solid)"
 | 
			
		||||
    hide-required-asterisk
 | 
			
		||||
    ref="formLogin"
 | 
			
		||||
    :model="registerData.registerForm"
 | 
			
		||||
    :rules="registerRules"
 | 
			
		||||
    class="login-form"
 | 
			
		||||
    label-position="top"
 | 
			
		||||
    label-width="120px"
 | 
			
		||||
    size="large"
 | 
			
		||||
    @register="register"
 | 
			
		||||
  >
 | 
			
		||||
    <template #title>
 | 
			
		||||
      <LoginFormTitle style="width: 100%" />
 | 
			
		||||
    </template>
 | 
			
		||||
 | 
			
		||||
    <template #code="form">
 | 
			
		||||
      <div class="w-[100%] flex">
 | 
			
		||||
        <el-input v-model="form['code']" :placeholder="t('login.codePlaceholder')" />
 | 
			
		||||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
 | 
			
		||||
    <template #register>
 | 
			
		||||
      <div class="w-[100%]">
 | 
			
		||||
        <XButton
 | 
			
		||||
          :loading="loading"
 | 
			
		||||
          :title="t('login.register')"
 | 
			
		||||
          class="w-[100%]"
 | 
			
		||||
          type="primary"
 | 
			
		||||
          @click="loginRegister()"
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="mt-15px w-[100%]">
 | 
			
		||||
        <XButton :title="t('login.hasUser')" class="w-[100%]" @click="handleBackLogin()" />
 | 
			
		||||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
  </Form>
 | 
			
		||||
    <el-row style="margin-right: -10px; margin-left: -10px">
 | 
			
		||||
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
 | 
			
		||||
        <el-form-item>
 | 
			
		||||
          <LoginFormTitle style="width: 100%" />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </el-col>
 | 
			
		||||
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
 | 
			
		||||
        <el-form-item v-if="registerData.tenantEnable === 'true'" prop="tenantName">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="registerData.registerForm.tenantName"
 | 
			
		||||
            :placeholder="t('login.tenantname')"
 | 
			
		||||
            :prefix-icon="iconHouse"
 | 
			
		||||
            link
 | 
			
		||||
            type="primary"
 | 
			
		||||
            size="large"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </el-col>
 | 
			
		||||
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
 | 
			
		||||
        <el-form-item prop="username">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="registerData.registerForm.username"
 | 
			
		||||
            :placeholder="t('login.username')"
 | 
			
		||||
            size="large"
 | 
			
		||||
            :prefix-icon="iconAvatar"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </el-col>
 | 
			
		||||
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
 | 
			
		||||
        <el-form-item prop="username">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="registerData.registerForm.nickname"
 | 
			
		||||
            placeholder="昵称"
 | 
			
		||||
            size="large"
 | 
			
		||||
            :prefix-icon="iconAvatar"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </el-col>
 | 
			
		||||
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
 | 
			
		||||
        <el-form-item prop="password">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="registerData.registerForm.password"
 | 
			
		||||
            type="password"
 | 
			
		||||
            auto-complete="off"
 | 
			
		||||
            :placeholder="t('login.password')"
 | 
			
		||||
            size="large"
 | 
			
		||||
            :prefix-icon="iconLock"
 | 
			
		||||
            show-password
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </el-col>
 | 
			
		||||
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
 | 
			
		||||
        <el-form-item prop="confirmPassword">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="registerData.registerForm.confirmPassword"
 | 
			
		||||
            type="password"
 | 
			
		||||
            size="large"
 | 
			
		||||
            auto-complete="off"
 | 
			
		||||
            :placeholder="t('login.checkPassword')"
 | 
			
		||||
            :prefix-icon="iconLock"
 | 
			
		||||
            show-password
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </el-col>
 | 
			
		||||
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
 | 
			
		||||
        <el-form-item>
 | 
			
		||||
          <XButton
 | 
			
		||||
            :loading="loginLoading"
 | 
			
		||||
            :title="t('login.register')"
 | 
			
		||||
            class="w-[100%]"
 | 
			
		||||
            type="primary"
 | 
			
		||||
            @click="getCode()"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </el-col>
 | 
			
		||||
      <Verify
 | 
			
		||||
        ref="verify"
 | 
			
		||||
        :captchaType="captchaType"
 | 
			
		||||
        :imgSize="{ width: '400px', height: '200px' }"
 | 
			
		||||
        mode="pop"
 | 
			
		||||
        @success="handleRegister"
 | 
			
		||||
      />
 | 
			
		||||
    </el-row>
 | 
			
		||||
    <XButton :title="t('login.hasUser')" class="w-[100%]" @click="handleBackLogin()" />
 | 
			
		||||
  </el-form>
 | 
			
		||||
</template>
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import type { FormRules } from 'element-plus'
 | 
			
		||||
 | 
			
		||||
import { useForm } from '@/hooks/web/useForm'
 | 
			
		||||
import { useValidator } from '@/hooks/web/useValidator'
 | 
			
		||||
import { ElLoading } from 'element-plus'
 | 
			
		||||
import LoginFormTitle from './LoginFormTitle.vue'
 | 
			
		||||
import type { RouteLocationNormalizedLoaded } from 'vue-router'
 | 
			
		||||
import { useIcon } from '@/hooks/web/useIcon'
 | 
			
		||||
import * as authUtil from '@/utils/auth'
 | 
			
		||||
import { usePermissionStore } from '@/store/modules/permission'
 | 
			
		||||
import * as LoginApi from '@/api/login'
 | 
			
		||||
import { LoginStateEnum, useLoginState } from './useLogin'
 | 
			
		||||
import { FormSchema } from '@/types/form'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'RegisterForm' })
 | 
			
		||||
defineOptions({ name: 'LoginForm' })
 | 
			
		||||
 | 
			
		||||
const { t } = useI18n()
 | 
			
		||||
const { required } = useValidator()
 | 
			
		||||
const { register, elFormRef } = useForm()
 | 
			
		||||
const iconHouse = useIcon({ icon: 'ep:house' })
 | 
			
		||||
const iconAvatar = useIcon({ icon: 'ep:avatar' })
 | 
			
		||||
const iconLock = useIcon({ icon: 'ep:lock' })
 | 
			
		||||
const formLogin = ref()
 | 
			
		||||
const { handleBackLogin, getLoginState } = useLoginState()
 | 
			
		||||
const { currentRoute, push } = useRouter()
 | 
			
		||||
const permissionStore = usePermissionStore()
 | 
			
		||||
const redirect = ref<string>('')
 | 
			
		||||
const loginLoading = ref(false)
 | 
			
		||||
const verify = ref()
 | 
			
		||||
const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文字
 | 
			
		||||
 | 
			
		||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER)
 | 
			
		||||
 | 
			
		||||
const schema = reactive<FormSchema[]>([
 | 
			
		||||
  {
 | 
			
		||||
    field: 'title',
 | 
			
		||||
    colProps: {
 | 
			
		||||
      span: 24
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    field: 'username',
 | 
			
		||||
    label: t('login.username'),
 | 
			
		||||
    value: '',
 | 
			
		||||
    component: 'Input',
 | 
			
		||||
    colProps: {
 | 
			
		||||
      span: 24
 | 
			
		||||
    },
 | 
			
		||||
    componentProps: {
 | 
			
		||||
      placeholder: t('login.usernamePlaceholder')
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    field: 'password',
 | 
			
		||||
    label: t('login.password'),
 | 
			
		||||
    value: '',
 | 
			
		||||
    component: 'InputPassword',
 | 
			
		||||
    colProps: {
 | 
			
		||||
      span: 24
 | 
			
		||||
    },
 | 
			
		||||
    componentProps: {
 | 
			
		||||
      style: {
 | 
			
		||||
        width: '100%'
 | 
			
		||||
      },
 | 
			
		||||
      strength: true,
 | 
			
		||||
      placeholder: t('login.passwordPlaceholder')
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    field: 'check_password',
 | 
			
		||||
    label: t('login.checkPassword'),
 | 
			
		||||
    value: '',
 | 
			
		||||
    component: 'InputPassword',
 | 
			
		||||
    colProps: {
 | 
			
		||||
      span: 24
 | 
			
		||||
    },
 | 
			
		||||
    componentProps: {
 | 
			
		||||
      style: {
 | 
			
		||||
        width: '100%'
 | 
			
		||||
      },
 | 
			
		||||
      strength: true,
 | 
			
		||||
      placeholder: t('login.passwordPlaceholder')
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    field: 'code',
 | 
			
		||||
    label: t('login.code'),
 | 
			
		||||
    colProps: {
 | 
			
		||||
      span: 24
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    field: 'register',
 | 
			
		||||
    colProps: {
 | 
			
		||||
      span: 24
 | 
			
		||||
    }
 | 
			
		||||
const equalToPassword = (rule, value, callback) => {
 | 
			
		||||
  if (registerData.registerForm.password !== value) {
 | 
			
		||||
    callback(new Error('两次输入的密码不一致'))
 | 
			
		||||
  } else {
 | 
			
		||||
    callback()
 | 
			
		||||
  }
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
const rules: FormRules = {
 | 
			
		||||
  username: [required()],
 | 
			
		||||
  password: [required()],
 | 
			
		||||
  check_password: [required()],
 | 
			
		||||
  code: [required()]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const loading = ref(false)
 | 
			
		||||
const registerRules = {
 | 
			
		||||
  tenantName: [
 | 
			
		||||
    { required: true, trigger: 'blur', message: '请输入您所属的租户' },
 | 
			
		||||
    { min: 2, max: 20, message: '租户账号长度必须介于 2 和 20 之间', trigger: 'blur' }
 | 
			
		||||
  ],
 | 
			
		||||
  username: [
 | 
			
		||||
    { required: true, trigger: 'blur', message: '请输入您的账号' },
 | 
			
		||||
    { min: 4, max: 30, message: '用户账号长度必须介于 4 和 30 之间', trigger: 'blur' }
 | 
			
		||||
  ],
 | 
			
		||||
  nickname: [
 | 
			
		||||
    { required: true, trigger: 'blur', message: '请输入您的昵称' },
 | 
			
		||||
    { min: 0, max: 30, message: '昵称长度必须介于 0 和 30 之间', trigger: 'blur' }
 | 
			
		||||
  ],
 | 
			
		||||
  password: [
 | 
			
		||||
    { required: true, trigger: 'blur', message: '请输入您的密码' },
 | 
			
		||||
    { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' },
 | 
			
		||||
    { pattern: /^[^<>"'|\\]+$/, message: '不能包含非法字符:< > " \' \\\ |', trigger: 'blur' }
 | 
			
		||||
  ],
 | 
			
		||||
  confirmPassword: [
 | 
			
		||||
    { required: true, trigger: 'blur', message: '请再次输入您的密码' },
 | 
			
		||||
    { required: true, validator: equalToPassword, trigger: 'blur' }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const loginRegister = async () => {
 | 
			
		||||
  const formRef = unref(elFormRef)
 | 
			
		||||
  formRef?.validate(async (valid) => {
 | 
			
		||||
    if (valid) {
 | 
			
		||||
      try {
 | 
			
		||||
        loading.value = true
 | 
			
		||||
      } finally {
 | 
			
		||||
        loading.value = false
 | 
			
		||||
      }
 | 
			
		||||
const registerData = reactive({
 | 
			
		||||
  isShowPassword: false,
 | 
			
		||||
  captchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE,
 | 
			
		||||
  tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
 | 
			
		||||
  registerForm: {
 | 
			
		||||
    tenantName: '',
 | 
			
		||||
    nickname: '',
 | 
			
		||||
    tenantId: 0,
 | 
			
		||||
    username: '',
 | 
			
		||||
    password: '',
 | 
			
		||||
    confirmPassword: '',
 | 
			
		||||
    captchaVerification: ''
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
async function handleRegister(params) {
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  try {
 | 
			
		||||
    if (registerData.tenantEnable) {
 | 
			
		||||
      await getTenantId()
 | 
			
		||||
      registerData.registerForm.tenantId = authUtil.getTenantId()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
    if (registerData.captchaEnable) {
 | 
			
		||||
      registerData.registerForm.captchaVerification = params.captchaVerification
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    console.log(
 | 
			
		||||
      'registerData.registerForm.captchaVerification====',
 | 
			
		||||
      registerData.registerForm.captchaVerification
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    const res = await LoginApi.register(registerData.registerForm)
 | 
			
		||||
    if (!res) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    loading.value = ElLoading.service({
 | 
			
		||||
      lock: true,
 | 
			
		||||
      text: '正在加载系统中...',
 | 
			
		||||
      background: 'rgba(0, 0, 0, 0.7)'
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    authUtil.removeLoginForm()
 | 
			
		||||
 | 
			
		||||
    authUtil.setToken(res)
 | 
			
		||||
    if (!redirect.value) {
 | 
			
		||||
      redirect.value = '/'
 | 
			
		||||
    }
 | 
			
		||||
    // 判断是否为SSO登录
 | 
			
		||||
    if (redirect.value.indexOf('sso') !== -1) {
 | 
			
		||||
      window.location.href = window.location.href.replace('/login?redirect=', '')
 | 
			
		||||
    } else {
 | 
			
		||||
      push({ path: redirect.value || permissionStore.addRouters[0].path })
 | 
			
		||||
    }
 | 
			
		||||
  } finally {
 | 
			
		||||
    loginLoading.value = false
 | 
			
		||||
    loading.value.close()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取验证码
 | 
			
		||||
const getCode = async () => {
 | 
			
		||||
  // 情况一,未开启:则直接注册
 | 
			
		||||
  if (registerData.captchaEnable === 'false') {
 | 
			
		||||
    await handleRegister({})
 | 
			
		||||
  } else {
 | 
			
		||||
    // 情况二,已开启:则展示验证码;只有完成验证码的情况,才进行注册
 | 
			
		||||
    // 弹出验证码
 | 
			
		||||
    verify.value.show()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// 获取租户 ID
 | 
			
		||||
const getTenantId = async () => {
 | 
			
		||||
  if (registerData.tenantEnable === 'true') {
 | 
			
		||||
    const res = await LoginApi.getTenantIdByName(registerData.registerForm.tenantName)
 | 
			
		||||
    authUtil.setTenantId(res)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 根据域名,获得租户信息
 | 
			
		||||
const getTenantByWebsite = async () => {
 | 
			
		||||
  const website = location.host
 | 
			
		||||
  const res = await LoginApi.getTenantByWebsite(website)
 | 
			
		||||
  if (res) {
 | 
			
		||||
    registerData.registerForm.tenantName = res.name
 | 
			
		||||
    authUtil.setTenantId(res.id)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const loading = ref() // ElLoading.service 返回的实例
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => currentRoute.value,
 | 
			
		||||
  (route: RouteLocationNormalizedLoaded) => {
 | 
			
		||||
    redirect.value = route?.query?.redirect as string
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    immediate: true
 | 
			
		||||
  }
 | 
			
		||||
)
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  // getCookie()
 | 
			
		||||
  getTenantByWebsite()
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
:deep(.anticon) {
 | 
			
		||||
  &:hover {
 | 
			
		||||
    color: var(--el-color-primary) !important;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-code {
 | 
			
		||||
  float: right;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 38px;
 | 
			
		||||
 | 
			
		||||
  img {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: auto;
 | 
			
		||||
    max-width: 100px;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user