190 lines
4.4 KiB
Vue
190 lines
4.4 KiB
Vue
<script setup>
|
|
import { computed, reactive, ref } from "vue";
|
|
import { useRoute, useRouter } from "vue-router";
|
|
import { useUserStore } from "@/store/modules/user.js";
|
|
import { encryptPasswordPayload } from "@/utils/passwordEncrypt.js";
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const userStore = useUserStore();
|
|
const form = reactive({
|
|
username: "",
|
|
password: ""
|
|
});
|
|
const loading = ref(false);
|
|
const errorMessage = ref("");
|
|
const redirectPath = computed(() => {
|
|
const redirect = route.query.redirect;
|
|
if (typeof redirect === "string" && redirect.startsWith("/") && !redirect.startsWith("//")) {
|
|
return redirect;
|
|
}
|
|
return "/dashboard/integrated-center";
|
|
});
|
|
function validateLoginForm() {
|
|
if (!form.username.trim()) {
|
|
errorMessage.value = "请输入账号";
|
|
return false;
|
|
}
|
|
if (!form.password) {
|
|
errorMessage.value = "请输入密码";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
async function handleLogin() {
|
|
if (!validateLoginForm() || loading.value) {
|
|
return;
|
|
}
|
|
loading.value = true;
|
|
errorMessage.value = "";
|
|
try {
|
|
const encryptedForm = await encryptPasswordPayload(form, ["password"]);
|
|
await userStore.login(encryptedForm);
|
|
router.replace(redirectPath.value);
|
|
} catch (error) {
|
|
errorMessage.value = error instanceof Error ? error.message : "登录失败\,请检查账号或密码";
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="login-page">
|
|
<form class="login-card" @submit.prevent="handleLogin">
|
|
<div class="login-card__eyebrow">Robot Manager</div>
|
|
<h1>系统登录</h1>
|
|
<p>请输入账号和密码进入巡检管理大屏</p>
|
|
|
|
<label class="login-field">
|
|
<span>账号</span>
|
|
<input v-model="form.username" autocomplete="username" placeholder="请输入账号" type="text" />
|
|
</label>
|
|
|
|
<label class="login-field">
|
|
<span>密码</span>
|
|
<input v-model="form.password" autocomplete="current-password" placeholder="请输入密码" type="password" />
|
|
</label>
|
|
|
|
<div v-if="errorMessage" class="login-error">{{ errorMessage }}</div>
|
|
|
|
<button class="login-button" :disabled="loading" type="submit">
|
|
{{ loading ? "登录中..." : "登录" }}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.login-page {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 100%;
|
|
height: 100%;
|
|
color: #dff7ff;
|
|
background:
|
|
radial-gradient(circle at 50% 42%,
|
|
rgba(31, 126, 202, 0.32),
|
|
transparent 34%),
|
|
radial-gradient(circle at center, #15385d 0%, #061426 60%, #02070f 100%);
|
|
}
|
|
|
|
.login-card {
|
|
box-sizing: border-box;
|
|
width: 440px;
|
|
padding: 44px 48px 48px;
|
|
text-align: left;
|
|
border: 1px solid rgba(116, 236, 255, 0.28);
|
|
border-radius: 18px;
|
|
background: linear-gradient(180deg,
|
|
rgba(7, 31, 61, 0.9),
|
|
rgba(4, 15, 31, 0.86));
|
|
box-shadow:
|
|
0 0 40px rgba(69, 188, 255, 0.18),
|
|
inset 0 0 26px rgba(116, 236, 255, 0.08);
|
|
}
|
|
|
|
.login-card__eyebrow {
|
|
margin-bottom: 12px;
|
|
color: #74ecff;
|
|
font-size: 13px;
|
|
letter-spacing: 0.22em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.login-card h1 {
|
|
margin: 0 0 12px;
|
|
font-size: 34px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.08em;
|
|
}
|
|
|
|
.login-card p {
|
|
margin: 0 0 34px;
|
|
color: rgba(223, 247, 255, 0.72);
|
|
font-size: 15px;
|
|
}
|
|
|
|
.login-field {
|
|
display: block;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.login-field span {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
color: rgba(223, 247, 255, 0.78);
|
|
font-size: 14px;
|
|
}
|
|
|
|
.login-field input {
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
height: 46px;
|
|
padding: 0 16px;
|
|
color: #e8fbff;
|
|
border: 1px solid rgba(116, 236, 255, 0.26);
|
|
border-radius: 8px;
|
|
outline: none;
|
|
background: rgba(3, 15, 32, 0.74);
|
|
transition:
|
|
border-color 0.2s,
|
|
box-shadow 0.2s;
|
|
}
|
|
|
|
.login-field input:focus {
|
|
border-color: rgba(116, 236, 255, 0.78);
|
|
box-shadow: 0 0 18px rgba(116, 236, 255, 0.16);
|
|
}
|
|
|
|
.login-field input::placeholder {
|
|
color: rgba(223, 247, 255, 0.34);
|
|
}
|
|
|
|
.login-error {
|
|
min-height: 20px;
|
|
margin: -4px 0 16px;
|
|
color: #ff9c9c;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.login-button {
|
|
width: 100%;
|
|
height: 48px;
|
|
color: #062239;
|
|
font-size: 17px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.12em;
|
|
cursor: pointer;
|
|
border: 0;
|
|
border-radius: 8px;
|
|
background: linear-gradient(90deg, #74ecff, #46a7ff);
|
|
box-shadow: 0 0 24px rgba(70, 167, 255, 0.32);
|
|
}
|
|
|
|
.login-button:disabled {
|
|
cursor: not-allowed;
|
|
opacity: 0.68;
|
|
}
|
|
</style>
|