2021-06-07 17:22:07 +08:00
|
|
|
|
<template>
|
2022-07-18 20:36:31 +08:00
|
|
|
|
<div>
|
|
|
|
|
|
<el-form ref="loginFormRef" :model="loginForm" :rules="rules" class="login-content-form" size="large">
|
|
|
|
|
|
<el-form-item prop="username">
|
2025-03-05 12:47:52 +08:00
|
|
|
|
<el-input type="text" :placeholder="$t('common.username')" prefix-icon="user" v-model="loginForm.username" clearable autocomplete="off">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
</el-input>
|
2022-07-18 20:36:31 +08:00
|
|
|
|
</el-form-item>
|
2025-03-05 12:47:52 +08:00
|
|
|
|
|
2022-07-18 20:36:31 +08:00
|
|
|
|
<el-form-item prop="password">
|
2023-07-22 20:51:46 +08:00
|
|
|
|
<el-input
|
|
|
|
|
|
type="password"
|
2025-03-05 12:47:52 +08:00
|
|
|
|
:placeholder="$t('common.password')"
|
2023-07-22 20:51:46 +08:00
|
|
|
|
prefix-icon="lock"
|
|
|
|
|
|
v-model="loginForm.password"
|
|
|
|
|
|
autocomplete="off"
|
|
|
|
|
|
@keyup.enter="login"
|
|
|
|
|
|
show-password
|
|
|
|
|
|
>
|
2022-07-18 20:36:31 +08:00
|
|
|
|
</el-input>
|
|
|
|
|
|
</el-form-item>
|
2025-03-05 12:47:52 +08:00
|
|
|
|
|
2023-06-17 15:15:03 +08:00
|
|
|
|
<el-form-item v-if="accountLoginSecurity.useCaptcha" prop="captcha">
|
2022-07-18 20:36:31 +08:00
|
|
|
|
<el-row :gutter="15">
|
|
|
|
|
|
<el-col :span="16">
|
2023-07-22 20:51:46 +08:00
|
|
|
|
<el-input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
maxlength="6"
|
2025-03-05 12:47:52 +08:00
|
|
|
|
:placeholder="$t('common.captcha')"
|
2023-07-22 20:51:46 +08:00
|
|
|
|
prefix-icon="position"
|
|
|
|
|
|
v-model="loginForm.captcha"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
autocomplete="off"
|
|
|
|
|
|
@keyup.enter="login"
|
|
|
|
|
|
></el-input>
|
2022-07-18 20:36:31 +08:00
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
|
<div class="login-content-code">
|
2025-04-18 22:07:37 +08:00
|
|
|
|
<img class="login-content-code-img cursor-pointer" @click="getCaptcha" width="130px" height="40px" :src="captchaImage" />
|
2022-07-18 20:36:31 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
</el-form-item>
|
2025-03-05 12:47:52 +08:00
|
|
|
|
|
2023-08-23 22:09:41 +08:00
|
|
|
|
<el-form-item v-if="ldapEnabled" prop="ldapLogin">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-checkbox v-model="loginForm.ldapLogin" :label="$t('login.ldapLogin')" size="small" />
|
2023-08-23 22:09:41 +08:00
|
|
|
|
</el-form-item>
|
2025-03-05 12:47:52 +08:00
|
|
|
|
|
2023-07-06 20:59:22 +08:00
|
|
|
|
<span v-if="showLoginFailTips" style="color: #f56c6c; font-size: 12px">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
{{
|
|
|
|
|
|
$t('login.loginFailTip', {
|
|
|
|
|
|
loginFailCount: accountLoginSecurity.loginFailCount,
|
|
|
|
|
|
loginFailMin: accountLoginSecurity.loginFailMin,
|
|
|
|
|
|
})
|
|
|
|
|
|
}}
|
2023-06-17 15:15:03 +08:00
|
|
|
|
</span>
|
2025-03-05 12:47:52 +08:00
|
|
|
|
|
2022-07-18 20:36:31 +08:00
|
|
|
|
<el-form-item>
|
|
|
|
|
|
<el-button type="primary" class="login-content-submit" round @click="login" :loading="loading.signIn">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<span>{{ $t('login.login') }}</span>
|
2022-07-18 20:36:31 +08:00
|
|
|
|
</el-button>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-dialog :title="$t('login.changePassword')" v-model="changePwdDialog.visible" :close-on-click-modal="false" width="450px" :destroy-on-close="true">
|
2023-07-06 20:59:22 +08:00
|
|
|
|
<el-form :model="changePwdDialog.form" :rules="changePwdDialog.rules" ref="changePwdFormRef" label-width="auto">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-form-item prop="username" :label="$t('common.username')" required>
|
2022-07-18 20:36:31 +08:00
|
|
|
|
<el-input v-model.trim="changePwdDialog.form.username" disabled></el-input>
|
|
|
|
|
|
</el-form-item>
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-form-item prop="oldPassword" :label="$t('login.oldPassword')" required>
|
2023-07-22 20:51:46 +08:00
|
|
|
|
<el-input v-model.trim="changePwdDialog.form.oldPassword" autocomplete="new-password" type="password"></el-input>
|
2022-07-18 20:36:31 +08:00
|
|
|
|
</el-form-item>
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-form-item prop="newPassword" :label="$t('login.newPassword')" required>
|
2023-07-22 20:51:46 +08:00
|
|
|
|
<el-input
|
|
|
|
|
|
v-model.trim="changePwdDialog.form.newPassword"
|
2024-11-20 22:43:53 +08:00
|
|
|
|
:placeholder="$t('login.passwordRuleTip')"
|
2023-07-22 20:51:46 +08:00
|
|
|
|
type="password"
|
|
|
|
|
|
autocomplete="new-password"
|
|
|
|
|
|
></el-input>
|
2022-07-18 20:36:31 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<div class="dialog-footer">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-button @click="cancelChangePwd">{{ $t('common.cancel') }}</el-button>
|
|
|
|
|
|
<el-button @click="changePwd" type="primary" :loading="loading.changePwd">
|
|
|
|
|
|
{{ $t('common.confirm') }}
|
|
|
|
|
|
</el-button>
|
2022-07-18 20:36:31 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
2023-06-17 15:15:03 +08:00
|
|
|
|
|
2023-07-22 20:51:46 +08:00
|
|
|
|
<el-dialog
|
2024-11-20 22:43:53 +08:00
|
|
|
|
:title="$t('login.otpValidation')"
|
2023-07-22 20:51:46 +08:00
|
|
|
|
v-model="otpDialog.visible"
|
|
|
|
|
|
@close="loading.signIn = false"
|
|
|
|
|
|
:close-on-click-modal="false"
|
|
|
|
|
|
width="350px"
|
|
|
|
|
|
:destroy-on-close="true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-form ref="otpFormRef" :model="otpDialog.form" :rules="otpDialog.rules" @submit.native.prevent label-width="auto">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-form-item v-if="otpDialog.otpUrl" :label="$t('login.qrCode')">
|
2023-06-17 15:15:03 +08:00
|
|
|
|
<qrcode-vue :value="otpDialog.otpUrl" :size="200" level="H" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item prop="code" label="OTP" required>
|
2023-07-22 20:51:46 +08:00
|
|
|
|
<el-input
|
|
|
|
|
|
style="width: 220px"
|
|
|
|
|
|
ref="otpCodeInputRef"
|
|
|
|
|
|
v-model.trim="otpDialog.form.code"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
@keyup.enter="otpVerify"
|
2024-11-20 22:43:53 +08:00
|
|
|
|
:placeholder="$t('login.enterOtpCodeTip')"
|
2023-07-22 20:51:46 +08:00
|
|
|
|
></el-input>
|
2023-06-17 15:15:03 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<div class="dialog-footer">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-button @click="otpVerify" type="primary" :loading="loading.otpConfirm">
|
|
|
|
|
|
{{ $t('common.confirm') }}
|
|
|
|
|
|
</el-button>
|
2023-06-17 15:15:03 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
2023-07-24 22:36:07 +08:00
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-dialog :title="$t('updateBasicInfo')" v-model="baseInfoDialog.visible" :close-on-click-modal="false" width="450px" :destroy-on-close="true">
|
2023-07-26 10:24:32 +08:00
|
|
|
|
<el-form :model="baseInfoDialog.form" :rules="baseInfoDialog.rules" ref="baseInfoFormRef" label-width="auto">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-form-item prop="username" :label="$t('common.username')" required>
|
2023-07-24 22:36:07 +08:00
|
|
|
|
<el-input v-model.trim="baseInfoDialog.form.username"></el-input>
|
|
|
|
|
|
</el-form-item>
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-form-item prop="name" :label="$t('login.name')" required>
|
2023-07-24 22:36:07 +08:00
|
|
|
|
<el-input v-model.trim="baseInfoDialog.form.name"></el-input>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<div class="dialog-footer">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-button @click="updateUserInfo()" type="primary" :loading="loading.updateUserConfirm">
|
|
|
|
|
|
{{ $t('common.confirm') }}
|
|
|
|
|
|
</el-button>
|
2023-07-24 22:36:07 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
2022-07-18 20:36:31 +08:00
|
|
|
|
</div>
|
2021-06-07 17:22:07 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
2022-10-29 20:08:15 +08:00
|
|
|
|
<script lang="ts" setup>
|
2025-03-05 12:47:52 +08:00
|
|
|
|
import { nextTick, onMounted, ref, toRefs, reactive } from 'vue';
|
2021-06-07 17:22:07 +08:00
|
|
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
|
|
|
|
import { ElMessage } from 'element-plus';
|
2023-04-13 20:11:22 +08:00
|
|
|
|
import { initRouter } from '@/router/index';
|
2024-05-13 19:55:43 +08:00
|
|
|
|
import { getRefreshToken, saveRefreshToken, saveToken, saveUser } from '@/common/utils/storage';
|
2021-06-07 17:22:07 +08:00
|
|
|
|
import openApi from '@/common/openApi';
|
2022-07-18 20:36:31 +08:00
|
|
|
|
import { RsaEncrypt } from '@/common/rsa';
|
2023-10-14 16:00:16 +08:00
|
|
|
|
import { getAccountLoginSecurity, getLdapEnabled } from '@/common/sysconfig';
|
2021-06-07 17:22:07 +08:00
|
|
|
|
import { letterAvatar } from '@/common/utils/string';
|
2023-03-15 11:41:03 +08:00
|
|
|
|
import { useUserInfo } from '@/store/userInfo';
|
2023-07-06 20:59:22 +08:00
|
|
|
|
import QrcodeVue from 'qrcode.vue';
|
2023-07-24 22:36:07 +08:00
|
|
|
|
import { personApi } from '@/views/personal/api';
|
2023-10-14 00:38:51 +08:00
|
|
|
|
import { getToken } from '@/common/utils/storage';
|
|
|
|
|
|
import { useThemeConfig } from '@/store/themeConfig';
|
2024-10-21 22:27:42 +08:00
|
|
|
|
import { getFileUrl } from '@/common/request';
|
2024-11-20 22:43:53 +08:00
|
|
|
|
import { useI18n } from 'vue-i18n';
|
2025-03-05 12:47:52 +08:00
|
|
|
|
import { Rules } from '@/common/rule';
|
2024-11-20 22:43:53 +08:00
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n();
|
2022-07-18 20:36:31 +08:00
|
|
|
|
|
2022-10-29 20:08:15 +08:00
|
|
|
|
const rules = {
|
2025-03-05 12:47:52 +08:00
|
|
|
|
username: [Rules.requiredInput('common.username')],
|
|
|
|
|
|
password: [Rules.requiredInput('common.password')],
|
|
|
|
|
|
captcha: [Rules.requiredInput('common.captcha')],
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2022-07-18 20:36:31 +08:00
|
|
|
|
|
2023-10-14 00:38:51 +08:00
|
|
|
|
// 定义变量内容
|
|
|
|
|
|
const storesThemeConfig = useThemeConfig();
|
|
|
|
|
|
|
2022-10-29 20:08:15 +08:00
|
|
|
|
const route = useRoute();
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
const loginFormRef: any = ref(null);
|
|
|
|
|
|
const changePwdFormRef: any = ref(null);
|
2023-06-17 15:15:03 +08:00
|
|
|
|
const otpFormRef: any = ref(null);
|
|
|
|
|
|
const otpCodeInputRef: any = ref(null);
|
2023-07-26 10:24:32 +08:00
|
|
|
|
const baseInfoFormRef: any = ref(null);
|
2022-10-29 20:08:15 +08:00
|
|
|
|
|
|
|
|
|
|
const state = reactive({
|
2023-06-17 15:15:03 +08:00
|
|
|
|
accountLoginSecurity: {
|
2023-10-12 12:14:56 +08:00
|
|
|
|
useCaptcha: false,
|
2023-06-17 15:15:03 +08:00
|
|
|
|
useOtp: false,
|
|
|
|
|
|
loginFailCount: 5,
|
|
|
|
|
|
loginFailMin: 10,
|
|
|
|
|
|
},
|
|
|
|
|
|
showLoginFailTips: false,
|
2022-10-29 20:08:15 +08:00
|
|
|
|
captchaImage: '',
|
|
|
|
|
|
loginForm: {
|
|
|
|
|
|
username: '',
|
|
|
|
|
|
password: '',
|
|
|
|
|
|
captcha: '',
|
|
|
|
|
|
cid: '',
|
2023-08-23 22:09:41 +08:00
|
|
|
|
ldapLogin: false,
|
2022-10-29 20:08:15 +08:00
|
|
|
|
},
|
2023-07-24 22:36:07 +08:00
|
|
|
|
loginRes: {} as any,
|
2022-10-29 20:08:15 +08:00
|
|
|
|
changePwdDialog: {
|
|
|
|
|
|
visible: false,
|
|
|
|
|
|
form: {
|
|
|
|
|
|
username: '',
|
|
|
|
|
|
oldPassword: '',
|
|
|
|
|
|
newPassword: '',
|
|
|
|
|
|
},
|
|
|
|
|
|
rules: {
|
2025-03-05 12:47:52 +08:00
|
|
|
|
newPassword: [Rules.requiredInput('login.newPassword'), Rules.accountPassword],
|
2022-10-29 20:08:15 +08:00
|
|
|
|
},
|
|
|
|
|
|
},
|
2023-06-17 15:15:03 +08:00
|
|
|
|
otpDialog: {
|
|
|
|
|
|
visible: false,
|
2023-07-06 20:59:22 +08:00
|
|
|
|
otpUrl: '',
|
2023-06-17 15:15:03 +08:00
|
|
|
|
form: {
|
|
|
|
|
|
code: '',
|
|
|
|
|
|
otpToken: '',
|
|
|
|
|
|
},
|
|
|
|
|
|
rules: {
|
2025-03-05 12:47:52 +08:00
|
|
|
|
code: [Rules.requiredInput('OTP')],
|
2023-06-17 15:15:03 +08:00
|
|
|
|
},
|
|
|
|
|
|
},
|
2023-07-24 22:36:07 +08:00
|
|
|
|
baseInfoDialog: {
|
|
|
|
|
|
visible: false,
|
|
|
|
|
|
form: {
|
|
|
|
|
|
username: '',
|
|
|
|
|
|
name: '',
|
|
|
|
|
|
},
|
|
|
|
|
|
rules: {
|
2025-03-05 12:47:52 +08:00
|
|
|
|
username: [Rules.requiredInput('common.username'), Rules.accountUsername],
|
|
|
|
|
|
name: [Rules.requiredInput('common.name')],
|
2023-07-24 22:36:07 +08:00
|
|
|
|
},
|
|
|
|
|
|
},
|
2022-10-29 20:08:15 +08:00
|
|
|
|
loading: {
|
|
|
|
|
|
signIn: false,
|
|
|
|
|
|
changePwd: false,
|
2023-06-17 15:15:03 +08:00
|
|
|
|
otpConfirm: false,
|
2023-07-24 22:36:07 +08:00
|
|
|
|
updateUserConfirm: false,
|
2022-10-29 20:08:15 +08:00
|
|
|
|
},
|
2023-08-23 22:09:41 +08:00
|
|
|
|
ldapEnabled: false,
|
2022-10-29 20:08:15 +08:00
|
|
|
|
});
|
2021-06-07 17:22:07 +08:00
|
|
|
|
|
2023-08-23 22:09:41 +08:00
|
|
|
|
const { accountLoginSecurity, showLoginFailTips, captchaImage, loginForm, changePwdDialog, otpDialog, baseInfoDialog, loading, ldapEnabled } = toRefs(state);
|
2021-07-28 18:03:19 +08:00
|
|
|
|
|
2022-10-29 20:08:15 +08:00
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
nextTick(async () => {
|
2023-06-17 15:15:03 +08:00
|
|
|
|
const res = await getAccountLoginSecurity();
|
|
|
|
|
|
if (res) {
|
|
|
|
|
|
state.accountLoginSecurity = res;
|
|
|
|
|
|
}
|
2022-10-29 20:08:15 +08:00
|
|
|
|
getCaptcha();
|
2023-08-23 22:09:41 +08:00
|
|
|
|
|
|
|
|
|
|
const ldap = await getLdapEnabled();
|
|
|
|
|
|
state.ldapEnabled = ldap;
|
2023-08-28 17:25:59 +08:00
|
|
|
|
state.loginForm.ldapLogin = ldap;
|
2022-10-29 20:08:15 +08:00
|
|
|
|
});
|
|
|
|
|
|
// 移除公钥, 方便后续重新获取
|
|
|
|
|
|
sessionStorage.removeItem('RsaPublicKey');
|
|
|
|
|
|
});
|
2021-07-28 18:03:19 +08:00
|
|
|
|
|
2022-10-29 20:08:15 +08:00
|
|
|
|
const getCaptcha = async () => {
|
2023-06-17 15:15:03 +08:00
|
|
|
|
if (!state.accountLoginSecurity.useCaptcha) {
|
2022-10-29 20:08:15 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2023-07-06 20:59:22 +08:00
|
|
|
|
let res: any = await openApi.captcha();
|
2022-10-29 20:08:15 +08:00
|
|
|
|
state.captchaImage = res.base64Captcha;
|
|
|
|
|
|
state.loginForm.cid = res.cid;
|
|
|
|
|
|
};
|
2021-06-07 17:22:07 +08:00
|
|
|
|
|
2022-10-29 20:08:15 +08:00
|
|
|
|
// 校验登录表单并登录
|
|
|
|
|
|
const login = () => {
|
|
|
|
|
|
loginFormRef.value.validate((valid: boolean) => {
|
|
|
|
|
|
if (valid) {
|
|
|
|
|
|
onSignIn();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
2021-06-07 17:22:07 +08:00
|
|
|
|
|
2023-06-17 15:15:03 +08:00
|
|
|
|
const otpVerify = async () => {
|
2024-05-13 19:55:43 +08:00
|
|
|
|
try {
|
|
|
|
|
|
await otpFormRef.value.validate();
|
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
state.loading.otpConfirm = true;
|
|
|
|
|
|
const res = await openApi.otpVerify(state.otpDialog.form);
|
|
|
|
|
|
await signInSuccess(res.token, res.refresh_token);
|
|
|
|
|
|
state.otpDialog.visible = false;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
state.loading.otpConfirm = false;
|
|
|
|
|
|
}
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2023-06-17 15:15:03 +08:00
|
|
|
|
|
2022-10-29 20:08:15 +08:00
|
|
|
|
// 登录
|
|
|
|
|
|
const onSignIn = async () => {
|
|
|
|
|
|
state.loading.signIn = true;
|
|
|
|
|
|
let loginRes;
|
|
|
|
|
|
const originPwd = state.loginForm.password;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const loginReq = { ...state.loginForm };
|
|
|
|
|
|
loginReq.password = await RsaEncrypt(originPwd);
|
2023-08-23 22:09:41 +08:00
|
|
|
|
if (state.loginForm.ldapLogin) {
|
2023-08-28 17:25:59 +08:00
|
|
|
|
loginRes = await openApi.ldapLogin(loginReq);
|
2023-08-23 22:09:41 +08:00
|
|
|
|
} else {
|
2023-08-28 17:25:59 +08:00
|
|
|
|
loginRes = await openApi.login(loginReq);
|
2023-08-23 22:09:41 +08:00
|
|
|
|
}
|
2022-10-29 20:08:15 +08:00
|
|
|
|
} catch (e: any) {
|
|
|
|
|
|
state.loading.signIn = false;
|
|
|
|
|
|
state.loginForm.captcha = '';
|
|
|
|
|
|
// 密码强度不足
|
|
|
|
|
|
if (e.code && e.code == 401) {
|
|
|
|
|
|
state.changePwdDialog.form.username = state.loginForm.username;
|
|
|
|
|
|
state.changePwdDialog.form.oldPassword = originPwd;
|
|
|
|
|
|
state.changePwdDialog.form.newPassword = '';
|
|
|
|
|
|
state.changePwdDialog.visible = true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
getCaptcha();
|
2023-06-17 15:15:03 +08:00
|
|
|
|
state.showLoginFailTips = true;
|
2022-10-29 20:08:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2023-06-17 15:15:03 +08:00
|
|
|
|
state.showLoginFailTips = false;
|
2023-07-21 21:18:31 +08:00
|
|
|
|
loginResDeal(loginRes);
|
2023-07-22 20:51:46 +08:00
|
|
|
|
};
|
2023-07-21 21:18:31 +08:00
|
|
|
|
|
2023-07-24 22:36:07 +08:00
|
|
|
|
const updateUserInfo = async () => {
|
2024-05-13 19:55:43 +08:00
|
|
|
|
try {
|
|
|
|
|
|
await baseInfoFormRef.value.validate();
|
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
state.loading.updateUserConfirm = true;
|
|
|
|
|
|
const form = state.baseInfoDialog.form;
|
|
|
|
|
|
await personApi.updateAccount.request(state.baseInfoDialog.form);
|
|
|
|
|
|
state.baseInfoDialog.visible = false;
|
|
|
|
|
|
useUserInfo().userInfo.username = form.username;
|
|
|
|
|
|
useUserInfo().userInfo.name = form.name;
|
|
|
|
|
|
await toIndex();
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
state.loading.updateUserConfirm = false;
|
|
|
|
|
|
}
|
2023-07-24 22:36:07 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2024-10-21 22:27:42 +08:00
|
|
|
|
const loginResDeal = async (loginRes: any) => {
|
2023-07-24 22:36:07 +08:00
|
|
|
|
state.loginRes = loginRes;
|
2022-10-29 20:08:15 +08:00
|
|
|
|
// 用户信息
|
|
|
|
|
|
const userInfos = {
|
2022-10-30 22:56:57 +08:00
|
|
|
|
name: loginRes.name,
|
2023-07-21 21:18:31 +08:00
|
|
|
|
username: loginRes.username,
|
2022-10-29 20:08:15 +08:00
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
|
lastLoginTime: loginRes.lastLoginTime,
|
|
|
|
|
|
lastLoginIp: loginRes.lastLoginIp,
|
2024-10-21 22:27:42 +08:00
|
|
|
|
photo: '',
|
2022-10-29 20:08:15 +08:00
|
|
|
|
};
|
2021-06-07 17:22:07 +08:00
|
|
|
|
|
2024-10-21 22:27:42 +08:00
|
|
|
|
const avatarFileKey = `avatar_${loginRes.username}`;
|
|
|
|
|
|
const avatarFileDetail = await openApi.getFileDetail([avatarFileKey]);
|
|
|
|
|
|
// 说明存在头像文件
|
|
|
|
|
|
if (avatarFileDetail.length > 0) {
|
|
|
|
|
|
userInfos.photo = getFileUrl(avatarFileKey);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
userInfos.photo = letterAvatar(loginRes.username);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-10-29 20:08:15 +08:00
|
|
|
|
// 存储用户信息到浏览器缓存
|
2023-09-16 17:07:48 +08:00
|
|
|
|
saveUser(userInfos);
|
2022-10-29 20:08:15 +08:00
|
|
|
|
// 1、请注意执行顺序(存储用户信息到vuex)
|
2023-04-13 20:11:22 +08:00
|
|
|
|
useUserInfo().setUserInfo(userInfos);
|
2023-06-17 15:15:03 +08:00
|
|
|
|
|
|
|
|
|
|
const token = loginRes.token;
|
2024-10-21 22:27:42 +08:00
|
|
|
|
// 如果不需要otp校验,则该token即为accessToken,否则为otp校验token
|
2023-06-17 15:15:03 +08:00
|
|
|
|
if (loginRes.otp == -1) {
|
2024-05-13 19:55:43 +08:00
|
|
|
|
signInSuccess(token, loginRes.refresh_token);
|
2023-06-17 15:15:03 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
state.otpDialog.form.otpToken = token;
|
2023-07-06 20:59:22 +08:00
|
|
|
|
state.otpDialog.otpUrl = loginRes.otpUrl;
|
2023-06-17 15:15:03 +08:00
|
|
|
|
state.otpDialog.visible = true;
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
otpCodeInputRef.value.focus();
|
|
|
|
|
|
}, 400);
|
2022-10-29 20:08:15 +08:00
|
|
|
|
};
|
2021-07-28 18:03:19 +08:00
|
|
|
|
|
2022-10-29 20:08:15 +08:00
|
|
|
|
// 登录成功后的跳转
|
2024-05-13 19:55:43 +08:00
|
|
|
|
const signInSuccess = async (accessToken: string = '', refreshToken = '') => {
|
2023-07-24 22:36:07 +08:00
|
|
|
|
if (!accessToken) {
|
2023-09-16 17:07:48 +08:00
|
|
|
|
accessToken = getToken();
|
2023-07-24 22:36:07 +08:00
|
|
|
|
}
|
2024-05-13 19:55:43 +08:00
|
|
|
|
if (!refreshToken) {
|
|
|
|
|
|
refreshToken = getRefreshToken();
|
|
|
|
|
|
}
|
2023-06-17 15:15:03 +08:00
|
|
|
|
// 存储 token 到浏览器缓存
|
2023-09-16 17:07:48 +08:00
|
|
|
|
saveToken(accessToken);
|
2024-05-13 19:55:43 +08:00
|
|
|
|
saveRefreshToken(refreshToken);
|
2023-10-18 15:24:29 +08:00
|
|
|
|
|
2023-06-17 15:15:03 +08:00
|
|
|
|
// 初始化路由
|
|
|
|
|
|
await initRouter();
|
2023-07-24 22:36:07 +08:00
|
|
|
|
|
|
|
|
|
|
// 判断是否为第一次oauth2登录,是的话需要用户填写姓名和用户名
|
|
|
|
|
|
if (state.loginRes.isFirstOauth2Login) {
|
|
|
|
|
|
state.baseInfoDialog.form.username = state.loginRes.username;
|
|
|
|
|
|
state.baseInfoDialog.visible = true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await toIndex();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const toIndex = async () => {
|
2022-10-29 20:08:15 +08:00
|
|
|
|
// 登录成功,跳到转首页
|
|
|
|
|
|
// 添加完动态路由,再进行 router 跳转,否则可能报错 No match found for location with path "/"
|
|
|
|
|
|
// 如果是复制粘贴的路径,非首页/登录页,那么登录成功后重定向到对应的路径中
|
|
|
|
|
|
route.query?.redirect ? router.push(route.query.redirect as string) : router.push('/');
|
|
|
|
|
|
// 登录成功提示
|
|
|
|
|
|
setTimeout(async () => {
|
|
|
|
|
|
// 关闭 loading
|
|
|
|
|
|
state.loading.signIn = true;
|
2024-11-20 22:43:53 +08:00
|
|
|
|
ElMessage.success(t('login.loginSuccessTip'));
|
2023-10-14 16:00:16 +08:00
|
|
|
|
// 水印设置用户信息
|
|
|
|
|
|
storesThemeConfig.setWatermarkUser();
|
2022-10-29 20:08:15 +08:00
|
|
|
|
}, 300);
|
|
|
|
|
|
};
|
2022-07-18 20:36:31 +08:00
|
|
|
|
|
2024-05-13 19:55:43 +08:00
|
|
|
|
const changePwd = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await changePwdFormRef.value.validate();
|
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
state.loading.changePwd = true;
|
|
|
|
|
|
const form = state.changePwdDialog.form;
|
|
|
|
|
|
const changePwdReq: any = { ...form };
|
|
|
|
|
|
changePwdReq.oldPassword = await RsaEncrypt(form.oldPassword);
|
|
|
|
|
|
changePwdReq.newPassword = await RsaEncrypt(form.newPassword);
|
|
|
|
|
|
await openApi.changePwd(changePwdReq);
|
2024-11-20 22:43:53 +08:00
|
|
|
|
ElMessage.success(t('login.passwordChangeSuccessTip'));
|
2024-05-13 19:55:43 +08:00
|
|
|
|
state.loginForm.password = state.changePwdDialog.form.newPassword;
|
|
|
|
|
|
state.changePwdDialog.visible = false;
|
|
|
|
|
|
getCaptcha();
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
state.loading.changePwd = false;
|
|
|
|
|
|
}
|
2022-10-29 20:08:15 +08:00
|
|
|
|
};
|
2022-07-18 20:36:31 +08:00
|
|
|
|
|
2022-10-29 20:08:15 +08:00
|
|
|
|
const cancelChangePwd = () => {
|
|
|
|
|
|
state.changePwdDialog.visible = false;
|
|
|
|
|
|
state.changePwdDialog.form.newPassword = '';
|
|
|
|
|
|
state.changePwdDialog.form.oldPassword = '';
|
|
|
|
|
|
state.changePwdDialog.form.username = '';
|
|
|
|
|
|
getCaptcha();
|
|
|
|
|
|
};
|
2023-07-24 22:36:07 +08:00
|
|
|
|
|
|
|
|
|
|
defineExpose({
|
|
|
|
|
|
loginResDeal,
|
|
|
|
|
|
});
|
2021-06-07 17:22:07 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
.login-content-form {
|
|
|
|
|
|
margin-top: 20px;
|
2022-10-29 20:08:15 +08:00
|
|
|
|
|
2021-06-07 17:22:07 +08:00
|
|
|
|
.login-content-code {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-around;
|
2022-10-29 20:08:15 +08:00
|
|
|
|
|
2021-06-07 17:22:07 +08:00
|
|
|
|
.login-content-code-img {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
line-height: 40px;
|
|
|
|
|
|
background-color: #ffffff;
|
|
|
|
|
|
border: 1px solid rgb(220, 223, 230);
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
letter-spacing: 5px;
|
|
|
|
|
|
text-indent: 5px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: all ease 0.2s;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
user-select: none;
|
2022-10-29 20:08:15 +08:00
|
|
|
|
|
2021-06-07 17:22:07 +08:00
|
|
|
|
&:hover {
|
|
|
|
|
|
border-color: #c0c4cc;
|
|
|
|
|
|
transition: all ease 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-10-29 20:08:15 +08:00
|
|
|
|
|
2021-06-07 17:22:07 +08:00
|
|
|
|
.login-content-submit {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
letter-spacing: 2px;
|
|
|
|
|
|
font-weight: 300;
|
|
|
|
|
|
margin-top: 15px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|