
Some checks failed
Build And Deploy v3-admin-vite / build-and-deploy (push) Has been cancelled
592 lines
16 KiB
Vue
592 lines
16 KiB
Vue
<script lang="ts" setup>
|
||
import type { FormInstance, FormRules } from "element-plus";
|
||
import type {
|
||
LoginRequestData,
|
||
forgetRequestData,
|
||
phoneLoginRequestData,
|
||
} from "./apis/type";
|
||
import { useSettingsStore } from "@/pinia/stores/settings";
|
||
import { useUserStore } from "@/pinia/stores/user";
|
||
import ThemeSwitch from "@@/components/ThemeSwitch/index.vue";
|
||
import { Lock, User, Iphone, Promotion, Check } from "@element-plus/icons-vue";
|
||
import { loginApi } from "./apis";
|
||
import Owl from "./components/Owl.vue";
|
||
import { useFocus } from "./composables/useFocus";
|
||
import { reactive, ref, inject } from "vue";
|
||
const $md5 = inject("md5");
|
||
|
||
const router = useRouter();
|
||
|
||
const userStore = useUserStore();
|
||
|
||
const settingsStore = useSettingsStore();
|
||
|
||
const { isFocus, handleBlur, handleFocus } = useFocus();
|
||
|
||
/** 登录表单元素的引用 */
|
||
const loginFormRef = ref<FormInstance | null>(null);
|
||
/** 短信验证码登录表单元素的引用 */
|
||
const phoneFormRef = ref<FormInstance | null>(null);
|
||
/** 短信验证码登录表单元素的引用 */
|
||
const forgetFormRef = ref<FormInstance | null>(null);
|
||
/** 登录按钮 Loading */
|
||
const loading = ref(false);
|
||
//是否验证码登录
|
||
let loginMethod = ref(true);
|
||
//倒计时
|
||
let countdown = ref(60);
|
||
//是否开启倒计时
|
||
let isDisabled = ref(false);
|
||
//是否忘记密码
|
||
let isForgetPwd = ref(false);
|
||
/** 登录表单数据 */
|
||
const loginFormData: LoginRequestData = reactive({
|
||
username: "",
|
||
password: "",
|
||
});
|
||
|
||
/** 短信登录表单数据 */
|
||
const phoneFormData: phoneLoginRequestData = reactive({
|
||
phone: "",
|
||
mobileCode: "",
|
||
});
|
||
|
||
/** 忘记密码表单数据 */
|
||
const forgetFormData: forgetRequestData = reactive({
|
||
// username: "",
|
||
/** 密码 */
|
||
password: "",
|
||
//确认密码
|
||
confirmPassword: "",
|
||
//手机号
|
||
phone: "",
|
||
//手机验证码
|
||
mobileCode: "",
|
||
});
|
||
|
||
/** 登录表单校验规则 */
|
||
const loginFormRules = reactive<FormRules<LoginRequestData>>({
|
||
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
|
||
password: [
|
||
{ required: true, message: "请输入密码", trigger: "blur" },
|
||
{ min: 8, max: 16, message: "长度在 8 到 16 个字符", trigger: "blur" },
|
||
],
|
||
});
|
||
|
||
/** 短信验证码登录登录表单校验规则 */
|
||
const phoneFormRules = reactive<FormRules<phoneLoginRequestData>>({
|
||
phone: [
|
||
{ required: true, message: "请输入手机号", trigger: "blur" },
|
||
{ min: 11, max: 11, message: "请检查手机号是否正确!", trigger: "blur" },
|
||
],
|
||
mobileCode: [
|
||
{ required: true, message: "请输入手机验证码", trigger: "blur" },
|
||
{
|
||
min: 4,
|
||
max: 4,
|
||
message: "请检查手机验证码是否正确!",
|
||
trigger: "blur",
|
||
},
|
||
],
|
||
});
|
||
//忘记密码表单校验
|
||
const forgetFormRules = reactive<FormRules<forgetRequestData>>({
|
||
phone: [
|
||
{ required: true, message: "请输入手机号 ", trigger: "blur" },
|
||
{
|
||
validator: (rule, value, callback) => {
|
||
console.log(value.toString());
|
||
if (!value) {
|
||
return callback();
|
||
}
|
||
if (
|
||
value.toString().length != 11 ||
|
||
!/^1[3-9]\d{9}$/.test(value.toString())
|
||
) {
|
||
callback(new Error("请检查手机号是否正确!"));
|
||
} else {
|
||
callback();
|
||
}
|
||
},
|
||
trigger: "blur",
|
||
},
|
||
],
|
||
password: [
|
||
{ required: true, message: "请输入密码", trigger: "blur" },
|
||
{ min: 8, max: 16, message: "长度在 8 到 16 个字符", trigger: "blur" },
|
||
{
|
||
validator: (rule, value, callback) => {
|
||
if (!value) {
|
||
return callback();
|
||
}
|
||
if (
|
||
!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/.test(
|
||
value
|
||
)
|
||
) {
|
||
callback(new Error("密码应该包含数字、大小写字母和特殊符号!"));
|
||
} else {
|
||
callback();
|
||
}
|
||
},
|
||
trigger: "blur",
|
||
},
|
||
],
|
||
mobileCode: [{ required: true, message: "请输入验证码", trigger: "blur" }],
|
||
confirmPassword: [
|
||
{ required: true, message: "请输入确认密码", trigger: "blur" },
|
||
{
|
||
validator: (rule, value, callback) => {
|
||
if (!value) {
|
||
return callback();
|
||
}
|
||
if (value !== forgetFormData.password) {
|
||
callback(new Error("密码和确认密码请保持一致!"));
|
||
} else {
|
||
callback();
|
||
}
|
||
},
|
||
trigger: "blur",
|
||
},
|
||
],
|
||
});
|
||
/** 账号密码登录 */
|
||
async function handleUserLogin(formEl) {
|
||
if (!formEl) return;
|
||
await formEl.validate((valid, fields) => {
|
||
if (valid) {
|
||
loading.value = true;
|
||
// loginFormData.password = $md5(loginFormData.password);
|
||
loginApi(loginFormData)
|
||
.then(({ data }) => {
|
||
userStore.setToken(data.token);
|
||
router.push("/");
|
||
})
|
||
.catch(() => {
|
||
loginFormData.password = "";
|
||
})
|
||
.finally(() => {
|
||
loading.value = false;
|
||
});
|
||
} else {
|
||
ElMessage.error("表单校验不通过");
|
||
return;
|
||
}
|
||
});
|
||
}
|
||
//短信验证码登录
|
||
async function handlePhoneLogin(formEl) {
|
||
if (!formEl) return;
|
||
await formEl.validate((valid, fields) => {
|
||
if (valid) {
|
||
loading.value = true;
|
||
loginApi(forgetFormData)
|
||
.then(({ data }) => {
|
||
console.log(data);
|
||
userStore.setToken(data.token);
|
||
router.push("/");
|
||
})
|
||
.catch(() => {
|
||
forgetFormData.mobileCode = "";
|
||
})
|
||
.finally(() => {
|
||
loading.value = false;
|
||
});
|
||
} else {
|
||
ElMessage.error("表单校验不通过");
|
||
return;
|
||
}
|
||
});
|
||
}
|
||
//忘记密码提交按钮
|
||
async function submit(formEl) {
|
||
if (!formEl) return;
|
||
await formEl.validate((valid, fields) => {
|
||
if (valid) {
|
||
loading.value = true;
|
||
loginApi()
|
||
.then(({ data }) => {
|
||
userStore.setToken(data.token);
|
||
router.push("/");
|
||
})
|
||
.catch(() => {
|
||
phoneFormData.mobileCode = "";
|
||
})
|
||
.finally(() => {
|
||
loading.value = false;
|
||
});
|
||
} else {
|
||
ElMessage.error("表单校验不通过");
|
||
return;
|
||
}
|
||
});
|
||
}
|
||
//手机验证码登录 true是账号密码登录 false是手机验证码登录
|
||
function mobileCode(type: boolean) {
|
||
loginMethod.value = type;
|
||
}
|
||
// 获取手机验证码
|
||
function getMobileCode(num: string) {
|
||
isDisabled.value = true;
|
||
}
|
||
// 更新倒计时显示
|
||
function updateCountdown() {
|
||
if (countdown.value === 0) {
|
||
isDisabled.value = false;
|
||
} else {
|
||
countdown.value = countdown.value - 1;
|
||
isDisabled.value = true;
|
||
}
|
||
}
|
||
//忘记密码
|
||
function forgetPwd() {
|
||
forgetFormData.password = ''
|
||
isForgetPwd.value = true;
|
||
}
|
||
watch(isDisabled, (newVal, oldVal) => {
|
||
if (newVal) {
|
||
setInterval(updateCountdown, 1000);
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<div class="login-container">
|
||
<ThemeSwitch v-if="settingsStore.showThemeSwitch" class="theme-switch" />
|
||
<Owl :close-eyes="isFocus" />
|
||
<div class="login-card">
|
||
<div class="title">
|
||
<div class="title-left">
|
||
<img src="@@/assets/images/layouts/img.png" />
|
||
</div>
|
||
<div class="title-text">智慧矿山数据管理平台</div>
|
||
</div>
|
||
<div class="content">
|
||
<el-form
|
||
ref="loginFormRef"
|
||
:model="loginFormData"
|
||
:rules="loginFormRules"
|
||
@keyup.enter="handleUserLogin"
|
||
v-show="loginMethod && !isForgetPwd"
|
||
>
|
||
<el-form-item prop="username">
|
||
<el-input
|
||
v-model.trim="loginFormData.username"
|
||
placeholder="用户名"
|
||
type="text"
|
||
tabindex="1"
|
||
:prefix-icon="User"
|
||
size="large"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item prop="password">
|
||
<el-input
|
||
v-model.trim="loginFormData.password"
|
||
placeholder="密码"
|
||
type="password"
|
||
tabindex="2"
|
||
:prefix-icon="Lock"
|
||
size="large"
|
||
show-password
|
||
@blur="handleBlur"
|
||
@focus="handleFocus"
|
||
/>
|
||
</el-form-item>
|
||
<el-button
|
||
v-if="loginMethod"
|
||
:loading="loading"
|
||
type="primary"
|
||
size="large"
|
||
@click.prevent="handleUserLogin(loginFormRef)"
|
||
>
|
||
登 录
|
||
</el-button>
|
||
</el-form>
|
||
<el-form
|
||
v-show="!loginMethod && !isForgetPwd"
|
||
ref="phoneFormRef"
|
||
:model="phoneFormData"
|
||
:rules="phoneFormRules"
|
||
@keyup.enter="handlePhoneLogin"
|
||
><el-form-item prop="phone">
|
||
<el-input
|
||
v-model.trim="phoneFormData.phone"
|
||
placeholder="手机号"
|
||
tabindex="3"
|
||
:prefix-icon="Iphone"
|
||
size="large"
|
||
maxlength="11"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item prop="mobileCode" v-show="!loginMethod">
|
||
<el-input
|
||
v-model.trim="phoneFormData.mobileCode"
|
||
placeholder="手机验证码"
|
||
tabindex="4"
|
||
:prefix-icon="Promotion"
|
||
size="large"
|
||
@blur="handleBlur"
|
||
@focus="handleFocus"
|
||
maxlength="4"
|
||
/>
|
||
<button
|
||
id="sendCode"
|
||
@click="getMobileCode(phoneFormData.mobileCode)"
|
||
:disabled="isDisabled"
|
||
>
|
||
<span v-if="!isDisabled">获取验证码</span>
|
||
<span v-if="isDisabled">{{ countdown + " 秒后重试" }}</span>
|
||
</button>
|
||
</el-form-item>
|
||
<el-button
|
||
:loading="loading"
|
||
type="primary"
|
||
size="large"
|
||
@click.prevent="handlePhoneLogin(phoneFormRef)"
|
||
>
|
||
登 录
|
||
</el-button>
|
||
</el-form>
|
||
<div class="login-btn" v-show="!isForgetPwd">
|
||
<el-link type="primary" @click="mobileCode(false)" v-if="loginMethod"
|
||
>手机验证码登录</el-link
|
||
>
|
||
<el-link type="primary" @click="mobileCode(true)" v-if="!loginMethod"
|
||
>用户名密码登录</el-link
|
||
>
|
||
<el-link type="primary" @click="forgetPwd()">忘记密码? </el-link>
|
||
</div>
|
||
|
||
<div class="login-thirdprty" v-if="!isForgetPwd">
|
||
<div class="css-1vli2z"></div>
|
||
<div class="css-wna3id">其他方式登录</div>
|
||
<div class="css-1vli2z"></div>
|
||
</div>
|
||
<div class="iconAll" v-if="!isForgetPwd">
|
||
<button>
|
||
<i class="iconfont icon-weixin"></i>
|
||
</button>
|
||
<button>
|
||
<i class="iconfont icon-qq"></i>
|
||
</button>
|
||
<button>
|
||
<i class="iconfont icon-weibo1"></i>
|
||
</button>
|
||
</div>
|
||
<el-form
|
||
v-if="isForgetPwd"
|
||
ref="forgetFormRef"
|
||
:model="forgetFormData"
|
||
:rules="forgetFormRules"
|
||
>
|
||
<el-form-item prop="phone">
|
||
<el-input
|
||
v-model.trim="forgetFormData.phone"
|
||
placeholder="手机号"
|
||
tabindex="1"
|
||
:prefix-icon="Iphone"
|
||
size="large"
|
||
maxlength="11"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item prop="mobileCode">
|
||
<el-input
|
||
v-model.trim="forgetFormData.mobileCode"
|
||
placeholder="手机验证码"
|
||
tabindex="2"
|
||
:prefix-icon="Promotion"
|
||
size="large"
|
||
@blur="handleBlur"
|
||
@focus="handleFocus"
|
||
maxlength="4"
|
||
/>
|
||
<button
|
||
id="sendCode"
|
||
@click="getMobileCode(forgetFormData.phone)"
|
||
:disabled="isDisabled"
|
||
>
|
||
<span v-if="!isDisabled">获取验证码</span>
|
||
<span v-if="isDisabled">{{ countdown + " 秒后重试" }}</span>
|
||
</button>
|
||
</el-form-item>
|
||
<el-form-item prop="password">
|
||
<el-input
|
||
v-model.trim="forgetFormData.password"
|
||
placeholder="密码"
|
||
type="password"
|
||
tabindex="3"
|
||
:prefix-icon="Lock"
|
||
size="large"
|
||
show-password
|
||
@blur="handleBlur"
|
||
@focus="handleFocus"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item prop="confirmPassword">
|
||
<el-input
|
||
v-model.trim="forgetFormData.confirmPassword"
|
||
placeholder="确认密码"
|
||
type="password"
|
||
tabindex="4"
|
||
:prefix-icon="Check"
|
||
size="large"
|
||
show-password
|
||
@blur="handleBlur"
|
||
@focus="handleFocus"
|
||
/>
|
||
</el-form-item>
|
||
<el-button
|
||
class="forgetLogin"
|
||
:loading="loading"
|
||
type="primary"
|
||
size="large"
|
||
@click.prevent="submit(forgetFormRef)"
|
||
>
|
||
提 交
|
||
</el-button>
|
||
</el-form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style lang="scss" scoped>
|
||
.login-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
width: 100%;
|
||
min-height: 100%;
|
||
// 添加背景图片样式
|
||
background-image: url("@@/assets/images/layouts/login_background.jpg"); // 修改为你的背景图路径
|
||
background-size: cover;
|
||
background-position: center;
|
||
background-repeat: no-repeat;
|
||
.theme-switch {
|
||
position: fixed;
|
||
top: 5%;
|
||
right: 5%;
|
||
cursor: pointer;
|
||
}
|
||
.login-card {
|
||
width: 480px;
|
||
max-width: 90%;
|
||
border-radius: 20px;
|
||
box-shadow: 0 0 10px #dcdfe6;
|
||
background-color: var(--el-bg-color);
|
||
overflow: hidden;
|
||
.title {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 20px 0;
|
||
|
||
img {
|
||
max-height: 40px;
|
||
width: auto;
|
||
object-fit: contain;
|
||
}
|
||
|
||
.title-text {
|
||
margin-top: 10px;
|
||
font-size: 30px;
|
||
font-weight: 800;
|
||
color: black;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
.forget-content {
|
||
padding: 20px 50px 50px 50px;
|
||
.forgetLogin {
|
||
width: 100%;
|
||
}
|
||
:deep(.el-form-item__content) {
|
||
display: flex;
|
||
flex-wrap: nowrap;
|
||
#sendCode {
|
||
width: 50%;
|
||
height: 100%;
|
||
cursor: pointer;
|
||
background-color: #fff;
|
||
width: 50%;
|
||
margin-left: 15px;
|
||
border: 1px solid #dbdde2;
|
||
border-radius: 5px;
|
||
}
|
||
}
|
||
}
|
||
.content {
|
||
padding: 20px 50px 50px 50px;
|
||
:deep(.el-input-group__append) {
|
||
padding: 0;
|
||
overflow: hidden;
|
||
.el-image {
|
||
width: 100px;
|
||
height: 40px;
|
||
border-left: 0px;
|
||
user-select: none;
|
||
cursor: pointer;
|
||
text-align: center;
|
||
}
|
||
}
|
||
.login-btn {
|
||
margin-top: 10px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
}
|
||
.el-button {
|
||
width: 100%;
|
||
margin-top: 10px;
|
||
}
|
||
:deep(.el-form-item__content) {
|
||
display: flex;
|
||
flex-wrap: nowrap;
|
||
#sendCode {
|
||
width: 50%;
|
||
height: 100%;
|
||
cursor: pointer;
|
||
background-color: #fff;
|
||
width: 50%;
|
||
margin-left: 15px;
|
||
border: 1px solid #dbdde2;
|
||
border-radius: 5px;
|
||
}
|
||
}
|
||
.login-thirdprty {
|
||
font-size: 14px;
|
||
margin-top: 20px;
|
||
display: flex;
|
||
color: #9196a1;
|
||
flex-direction: row;
|
||
flex-wrap: nowrap;
|
||
align-items: center;
|
||
.css-1vli2z {
|
||
width: 128px;
|
||
height: 0;
|
||
border-top: 1px solid #ebeced;
|
||
}
|
||
.css-wna3id {
|
||
margin: 0 20px;
|
||
}
|
||
}
|
||
.iconAll {
|
||
display: flex;
|
||
justify-content: space-evenly;
|
||
margin-top: 20px;
|
||
button {
|
||
border: none;
|
||
background-color: #fff;
|
||
cursor: pointer;
|
||
i {
|
||
font-size: 30px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|