20260809223428
This commit is contained in:
1 parent
d72ca4193d
commit
c4891538a7
466 files changed
+21000
-3339
No files matched your search
@@ -1,324 +0,0 @@
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { Building2, LockKeyhole, LogIn, MapPin, Ship, Truck, UserRound } from "@lucide/vue";
|
||||
import { App } from "antdv-next";
|
||||
import { loginApi } from "@/services/api";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
|
||||
const LOGIN_IDENTITY_KEY = "fms-login-identity";
|
||||
|
||||
function readRememberedIdentity() {
|
||||
try {
|
||||
const identity = JSON.parse(localStorage.getItem(LOGIN_IDENTITY_KEY) || "{}");
|
||||
return {
|
||||
orgId: typeof identity.orgId === "string" ? identity.orgId : "",
|
||||
userId: typeof identity.userId === "string" ? identity.userId : "",
|
||||
};
|
||||
} catch {
|
||||
return { orgId: "", userId: "" };
|
||||
}
|
||||
}
|
||||
|
||||
function rememberIdentity(orgId, userId) {
|
||||
try {
|
||||
localStorage.setItem(LOGIN_IDENTITY_KEY, JSON.stringify({ orgId, userId }));
|
||||
} catch {
|
||||
// Login remains available when browser storage is restricted.
|
||||
}
|
||||
}
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const authStore = useAuthStore();
|
||||
const { message } = App.useApp();
|
||||
const loading = ref(false);
|
||||
const rememberedIdentity = readRememberedIdentity();
|
||||
const form = reactive({
|
||||
orgId: rememberedIdentity.orgId,
|
||||
userId: rememberedIdentity.userId,
|
||||
password: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
const reason = route.query.reason;
|
||||
if (reason !== "session-replaced" && reason !== "expired") return;
|
||||
|
||||
if (reason === "session-replaced") {
|
||||
message.warning("账号已在其他设备登录,请重新登录");
|
||||
} else {
|
||||
message.warning("登录已过期或已失效,请重新登录");
|
||||
}
|
||||
|
||||
const query = { ...route.query };
|
||||
delete query.reason;
|
||||
router.replace({ name: "login", query });
|
||||
});
|
||||
|
||||
function getRedirectPath() {
|
||||
const redirect = route.query.redirect;
|
||||
return typeof redirect === "string" && redirect.startsWith("/") && !redirect.startsWith("//")
|
||||
? redirect
|
||||
: "/dashboard";
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
if (loading.value) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const response = await loginApi(form.orgId, form.userId, form.password);
|
||||
const session = response.data;
|
||||
authStore.setSession(session, form.orgId);
|
||||
rememberIdentity(form.orgId, form.userId);
|
||||
message.success("登录成功,正在进入工作台");
|
||||
await router.replace(getRedirectPath());
|
||||
} catch (error) {
|
||||
message.error(error.message || "登录失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="login-page">
|
||||
<section class="login-shell" aria-label="FMS 货代物流管理系统登录">
|
||||
<aside class="login-overview" aria-label="货代物流业务概览">
|
||||
<div class="overview-brand">
|
||||
<div class="overview-brand__mark"><Ship /></div>
|
||||
<div class="overview-brand__copy">
|
||||
<strong>FMS</strong>
|
||||
<span>Freight & Logistics</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overview-copy">
|
||||
<span class="overview-copy__eyebrow">货代 · 物流 · 费用一体化</span>
|
||||
<h1>货代物流管理系统</h1>
|
||||
<p>贯通订舱委托、运输跟踪与费用结算,让每一票业务清晰可控。</p>
|
||||
</div>
|
||||
|
||||
<div class="overview-scope">
|
||||
<span><Ship /> 海运订舱</span>
|
||||
<span><Truck /> 运输跟踪</span>
|
||||
<span><MapPin /> 业务协同</span>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="login-panel">
|
||||
<header class="login-heading">
|
||||
<span>业务工作台</span>
|
||||
<h2>账号登录</h2>
|
||||
<p>欢迎回来,请使用您的业务账号登录</p>
|
||||
</header>
|
||||
|
||||
<a-form layout="vertical" :model="form" @finish="submit">
|
||||
<a-form-item
|
||||
label="机构码"
|
||||
name="orgId"
|
||||
:rules="[{ required: true, message: '请输入机构码' }]"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="form.orgId"
|
||||
size="large"
|
||||
autocomplete="organization"
|
||||
placeholder="请输入机构码"
|
||||
autofocus
|
||||
>
|
||||
<template #prefix><Building2 /></template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="账号"
|
||||
name="userId"
|
||||
:rules="[{ required: true, message: '请输入账号' }]"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="form.userId"
|
||||
size="large"
|
||||
autocomplete="username"
|
||||
placeholder="请输入账号"
|
||||
>
|
||||
<template #prefix><UserRound /></template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="密码" name="password">
|
||||
<a-input-password
|
||||
v-model:value="form.password"
|
||||
size="large"
|
||||
autocomplete="current-password"
|
||||
placeholder="请输入密码"
|
||||
>
|
||||
<template #prefix><LockKeyhole /></template>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
<a-button type="primary" html-type="submit" size="large" block :loading="loading">
|
||||
<template #icon><LogIn /></template>
|
||||
登录
|
||||
</a-button>
|
||||
</a-form>
|
||||
|
||||
<footer class="login-footer">FMS · 货代 / 物流管理系统</footer>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-page {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
place-items: center;
|
||||
padding: 32px;
|
||||
background: #edf1f3;
|
||||
}
|
||||
|
||||
.login-shell {
|
||||
display: grid;
|
||||
width: 880px;
|
||||
min-height: 520px;
|
||||
grid-template-columns: 380px 500px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #d5dee2;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
box-shadow: 0 18px 48px rgb(25 47 55 / 12%);
|
||||
}
|
||||
|
||||
.login-overview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 42px;
|
||||
color: #f5faf9;
|
||||
background: #173a42;
|
||||
}
|
||||
|
||||
.overview-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
|
||||
&__mark {
|
||||
display: grid;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
place-items: center;
|
||||
border: 1px solid rgb(255 255 255 / 20%);
|
||||
border-radius: 6px;
|
||||
background: #15998e;
|
||||
color: #fff;
|
||||
|
||||
:deep(.lucide) {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&__copy {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
|
||||
strong {
|
||||
font-size: 15px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-top: 3px;
|
||||
color: #a9c0c2;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.overview-copy {
|
||||
margin-top: 72px;
|
||||
|
||||
&__eyebrow {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
color: #5dd2c5;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
font-size: 28px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 14px 0 0;
|
||||
color: #c4d4d5;
|
||||
font-size: 13px;
|
||||
line-height: 1.75;
|
||||
}
|
||||
}
|
||||
|
||||
.overview-scope {
|
||||
display: flex;
|
||||
margin-top: auto;
|
||||
flex-direction: column;
|
||||
gap: 13px;
|
||||
color: #b8cbcc;
|
||||
font-size: 12px;
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
}
|
||||
|
||||
:deep(.lucide) {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: #62c8be;
|
||||
}
|
||||
}
|
||||
|
||||
.login-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 58px 60px 30px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.login-heading {
|
||||
margin-bottom: 30px;
|
||||
|
||||
span {
|
||||
color: #168f86;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 8px 0 0;
|
||||
color: #19323a;
|
||||
font-size: 25px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 8px 0 0;
|
||||
color: #738287;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
margin-top: auto;
|
||||
padding-top: 26px;
|
||||
color: #8c999d;
|
||||
font-size: 11px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user