Files
robot_manager/src/views/login/index.vue
2026-07-02 18:42:32 +08:00

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>