You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
6.8 KiB
215 lines
6.8 KiB
<script lang="ts" setup> |
|
import { computed, reactive, ref, unref } from 'vue' |
|
|
|
import { Button, Checkbox, Col, Divider, Form, Input, Row } from 'ant-design-vue' |
|
import { AlipayCircleFilled, GithubFilled, WechatFilled } from '@ant-design/icons-vue' |
|
import LoginFormTitle from './LoginFormTitle.vue' |
|
|
|
import { LoginStateEnum, useFormRules, useFormValid, useLoginState } from './useLogin' |
|
import { useI18n } from '@/hooks/web/useI18n' |
|
import { useMessage } from '@/hooks/web/useMessage' |
|
|
|
import { useUserStore } from '@/store/modules/user' |
|
import { usePermissionStore } from '@/store/modules/permission' |
|
|
|
import { useGlobSetting } from '@/hooks/setting' |
|
import { useDesign } from '@/hooks/web/useDesign' |
|
|
|
import * as authUtil from '@/utils/auth' |
|
|
|
import { Verify } from '@/components/Verifition' |
|
import { getTenantIdByName } from '@/api/base/login' |
|
|
|
const FormItem = Form.Item |
|
const InputPassword = Input.Password |
|
|
|
const { t } = useI18n() |
|
const { notification, createErrorModal } = useMessage() |
|
const { prefixCls } = useDesign('login') |
|
const userStore = useUserStore() |
|
const permissionStore = usePermissionStore() |
|
|
|
const { tenantEnable, captchaEnable } = useGlobSetting() |
|
|
|
const { setLoginState, getLoginState } = useLoginState() |
|
const { getFormRules } = useFormRules() |
|
|
|
const formRef = ref() |
|
const loading = ref(false) |
|
const rememberMe = ref(false) |
|
|
|
const verify = ref() |
|
const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文字 |
|
|
|
const formData = reactive({ |
|
tenantName: '芋道源码', |
|
username: 'admin', |
|
password: 'admin123', |
|
captchaVerification: '', |
|
}) |
|
|
|
const { validForm } = useFormValid(formRef) |
|
|
|
// onKeyStroke('Enter', handleLogin); |
|
|
|
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN) |
|
|
|
// 获取验证码 |
|
async function getCode() { |
|
// 情况一,未开启:则直接登录 |
|
if (captchaEnable === 'false') { |
|
await handleLogin({}) |
|
} |
|
else { |
|
// 情况二,已开启:则展示验证码;只有完成验证码的情况,才进行登录 |
|
// 弹出验证码 |
|
verify.value.show() |
|
} |
|
} |
|
|
|
// 获取租户ID |
|
async function getTenantId() { |
|
if (tenantEnable === 'true') { |
|
const res = await getTenantIdByName(formData.tenantName) |
|
authUtil.setTenantId(res) |
|
} |
|
} |
|
|
|
async function handleLogin(params) { |
|
await getTenantId() |
|
const data = await validForm() |
|
if (!data) |
|
return |
|
try { |
|
loading.value = true |
|
const userInfo = await userStore.login({ |
|
password: data.password, |
|
username: data.username, |
|
captchaVerification: params.captchaVerification, |
|
mode: 'none', // 不要默认的错误提示 |
|
}) |
|
if (userInfo) { |
|
await permissionStore.changePermissionCode(userInfo.permissions) |
|
notification.success({ |
|
message: t('sys.login.loginSuccessTitle'), |
|
description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.user.nickname}`, |
|
duration: 3, |
|
}) |
|
} |
|
} |
|
catch (error) { |
|
createErrorModal({ |
|
title: t('sys.api.errorTip'), |
|
content: (error as unknown as Error).message || t('sys.api.networkExceptionMsg'), |
|
getContainer: () => document.body.querySelector(`.${prefixCls}`) || document.body, |
|
}) |
|
} |
|
finally { |
|
loading.value = false |
|
} |
|
} |
|
</script> |
|
|
|
<template> |
|
<LoginFormTitle v-show="getShow" class="enter-x" /> |
|
<Form v-show="getShow" ref="formRef" class="p-4 enter-x" :model="formData" :rules="getFormRules" @keypress.enter="handleLogin"> |
|
<FormItem name="tenantName" class="enter-x"> |
|
<Input |
|
v-if="tenantEnable === 'true'" |
|
v-model:value="formData.tenantName" |
|
size="large" |
|
:placeholder="t('sys.login.tenantName')" |
|
class="fix-auto-fill" |
|
/> |
|
</FormItem> |
|
<FormItem name="username" class="enter-x"> |
|
<Input v-model:value="formData.username" size="large" :placeholder="t('sys.login.userName')" class="fix-auto-fill" /> |
|
</FormItem> |
|
<FormItem name="password" class="enter-x"> |
|
<InputPassword |
|
v-model:value="formData.password" |
|
size="large" |
|
visibility-toggle |
|
:placeholder="t('sys.login.password')" |
|
class="fix-auto-fill" |
|
/> |
|
</FormItem> |
|
|
|
<Row class="enter-x"> |
|
<Col :span="12"> |
|
<FormItem> |
|
<!-- No logic, you need to deal with it yourself --> |
|
<Checkbox v-model:checked="rememberMe" size="small"> |
|
{{ t('sys.login.rememberMe') }} |
|
</Checkbox> |
|
</FormItem> |
|
</Col> |
|
<Col :span="12"> |
|
<FormItem :style="{ 'text-align': 'right' }"> |
|
<!-- No logic, you need to deal with it yourself --> |
|
<Button type="link" size="small" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)"> |
|
{{ t('sys.login.forgetPassword') }} |
|
</Button> |
|
</FormItem> |
|
</Col> |
|
</Row> |
|
|
|
<FormItem class="enter-x"> |
|
<Button type="primary" size="large" block :loading="loading" @click="getCode"> |
|
{{ t('sys.login.loginButton') }} |
|
</Button> |
|
<!-- <Button size="large" class="mt-4 enter-x" block @click="handleRegister"> |
|
{{ t('sys.login.registerButton') }} |
|
</Button> --> |
|
</FormItem> |
|
<Row class="enter-x" :gutter="[16, 16]"> |
|
<Col :md="8" :xs="24"> |
|
<Button block @click="setLoginState(LoginStateEnum.MOBILE)"> |
|
{{ t('sys.login.mobileSignInFormTitle') }} |
|
</Button> |
|
</Col> |
|
<Col :md="8" :xs="24"> |
|
<Button block @click="setLoginState(LoginStateEnum.QR_CODE)"> |
|
{{ t('sys.login.qrSignInFormTitle') }} |
|
</Button> |
|
</Col> |
|
<Col :md="8" :xs="24"> |
|
<a-button block @click="setLoginState(LoginStateEnum.REGISTER)"> |
|
{{ t('sys.login.registerButton') }} |
|
</a-button> |
|
</Col> |
|
</Row> |
|
|
|
<Divider class="enter-x"> |
|
{{ t('sys.login.otherSignIn') }} |
|
</Divider> |
|
|
|
<div class="flex justify-evenly enter-x" :class="`${prefixCls}-sign-in-way`"> |
|
<GithubFilled /> |
|
<WechatFilled /> |
|
<AlipayCircleFilled /> |
|
<!-- <GoogleCircleFilled /> --> |
|
<!-- <TwitterCircleFilled /> --> |
|
</div> |
|
|
|
<!-- 萌新必读 --> |
|
<Divider class="enter-x"> |
|
萌新必读 |
|
</Divider> |
|
<div class="flex justify-evenly enter-x" :class="`${prefixCls}-sign-in-way`"> |
|
<Button href="https://doc.iocoder.cn/" target="_blank"> |
|
📚开发指南 |
|
</Button> |
|
<Button href="https://doc.iocoder.cn/video/" target="_blank" style="padding-left: 10px"> |
|
🔥视频教程 |
|
</Button> |
|
<Button href="https://www.iocoder.cn/Interview/good-collection/" target="_blank" style="padding-left: 10px"> |
|
⚡面试手册 |
|
</Button> |
|
<Button href="http://static.yudao.iocoder.cn/mp/xinyu370.jpeg" target="_blank" style="padding-left: 10px"> |
|
🤝外包咨询 |
|
</Button> |
|
</div> |
|
</Form> |
|
<Verify ref="verify" mode="pop" :captcha-type="captchaType" :img-size="{ width: '400px', height: '200px' }" @success="handleLogin" /> |
|
</template>
|
|
|