2026-05-27 16:38:40 +08:00
|
|
|
<template>
|
|
|
|
|
<div class="login-page">
|
2026-06-29 10:52:08 +08:00
|
|
|
<div class="login-overlay"></div>
|
|
|
|
|
|
|
|
|
|
<!-- 左侧品牌区 -->
|
|
|
|
|
<div class="login-brand">
|
|
|
|
|
<div class="brand-logo">
|
|
|
|
|
<div class="brand-mark">酸</div>
|
|
|
|
|
<div class="brand-word">
|
|
|
|
|
<div class="brand-title">推拉酸洗线</div>
|
|
|
|
|
<div class="brand-en">PUSH-PULL PICKLING LINE</div>
|
2026-05-27 17:04:48 +08:00
|
|
|
</div>
|
2026-05-27 16:38:40 +08:00
|
|
|
</div>
|
2026-06-29 10:52:08 +08:00
|
|
|
<div class="brand-slogan">二级过程控制系统</div>
|
|
|
|
|
<div class="brand-desc">物料跟踪 · 计划实绩 · 工艺模型 · 质量管理</div>
|
|
|
|
|
</div>
|
2026-05-27 16:38:40 +08:00
|
|
|
|
2026-06-29 10:52:08 +08:00
|
|
|
<!-- 右侧登录卡片 -->
|
|
|
|
|
<div class="login-card">
|
|
|
|
|
<div class="card-title">系统登录</div>
|
|
|
|
|
<div class="card-tip">请输入账号密码以继续</div>
|
|
|
|
|
|
|
|
|
|
<div class="field-group">
|
|
|
|
|
<div class="field-label">用户名</div>
|
|
|
|
|
<input v-model="form.username" class="login-input" autocomplete="username"
|
|
|
|
|
placeholder="请输入用户名" @keyup.enter="handleLogin" />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="field-group">
|
|
|
|
|
<div class="field-label">密码</div>
|
|
|
|
|
<input v-model="form.password" type="password" class="login-input" autocomplete="current-password"
|
|
|
|
|
placeholder="请输入密码" @keyup.enter="handleLogin" />
|
2026-05-27 16:38:40 +08:00
|
|
|
</div>
|
|
|
|
|
|
2026-06-29 10:52:08 +08:00
|
|
|
<div v-if="error" class="login-error">{{ error }}</div>
|
|
|
|
|
|
|
|
|
|
<button class="login-btn" :disabled="loading" @click="handleLogin">
|
|
|
|
|
{{ loading ? '验证中…' : '登 录' }}
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<div class="login-foot">
|
|
|
|
|
<span class="dot"></span>系统就绪
|
|
|
|
|
<span class="ver">v1.0.0</span>
|
2026-05-27 16:38:40 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-06-29 10:52:08 +08:00
|
|
|
|
|
|
|
|
<div class="copyright">推拉酸洗线 L2 MES · 过程控制系统</div>
|
2026-05-27 16:38:40 +08:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
export default {
|
|
|
|
|
name: 'Login',
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
loading: false,
|
|
|
|
|
error: '',
|
|
|
|
|
form: { username: '', password: '' }
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
async handleLogin() {
|
|
|
|
|
if (!this.form.username || !this.form.password) {
|
2026-05-27 17:04:48 +08:00
|
|
|
this.error = '用户名和密码不能为空'
|
2026-05-27 16:38:40 +08:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
this.loading = true
|
|
|
|
|
this.error = ''
|
|
|
|
|
try {
|
|
|
|
|
await this.$store.dispatch('auth/login', this.form)
|
|
|
|
|
this.$router.push('/')
|
|
|
|
|
} catch {
|
|
|
|
|
this.error = '用户名或密码错误'
|
|
|
|
|
} finally {
|
|
|
|
|
this.loading = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
@import '@/assets/styles/variables';
|
|
|
|
|
|
|
|
|
|
.login-page {
|
2026-06-29 10:52:08 +08:00
|
|
|
position: relative;
|
2026-05-27 16:38:40 +08:00
|
|
|
height: 100vh;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
2026-06-29 10:52:08 +08:00
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 0 9vw;
|
|
|
|
|
background: url('~@/assets/images/login-bg.jpg') center/cover no-repeat;
|
|
|
|
|
overflow: hidden;
|
2026-05-27 16:38:40 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-29 10:52:08 +08:00
|
|
|
// 主题色渐变蒙版(左深红 → 右半透明,保证文字与卡片可读)
|
|
|
|
|
.login-overlay {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background:
|
|
|
|
|
linear-gradient(105deg, rgba($sms-teal, .82) 0%, rgba(28, 22, 24, .72) 46%, rgba(20, 16, 18, .55) 100%);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── 左侧品牌 ───
|
|
|
|
|
.login-brand {
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
color: #fff;
|
|
|
|
|
max-width: 480px;
|
2026-05-27 16:38:40 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-29 10:52:08 +08:00
|
|
|
.brand-logo {
|
2026-05-27 17:04:48 +08:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
2026-06-29 10:52:08 +08:00
|
|
|
gap: 16px;
|
2026-05-27 16:38:40 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-29 10:52:08 +08:00
|
|
|
.brand-mark {
|
|
|
|
|
width: 60px;
|
|
|
|
|
height: 60px;
|
|
|
|
|
background: #fff;
|
|
|
|
|
color: $sms-teal;
|
|
|
|
|
font-size: 32px;
|
2026-05-27 17:04:48 +08:00
|
|
|
font-weight: 900;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
2026-06-29 10:52:08 +08:00
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 6px 20px rgba(0, 0, 0, .3);
|
2026-05-27 17:04:48 +08:00
|
|
|
flex-shrink: 0;
|
2026-05-27 16:38:40 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-29 10:52:08 +08:00
|
|
|
.brand-title {
|
|
|
|
|
font-size: 38px;
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
letter-spacing: 6px;
|
|
|
|
|
line-height: 1.1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-en {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
letter-spacing: 3px;
|
|
|
|
|
opacity: .8;
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.brand-slogan {
|
|
|
|
|
margin-top: 26px;
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
font-weight: 600;
|
2026-05-27 17:04:48 +08:00
|
|
|
letter-spacing: 2px;
|
2026-05-27 16:38:40 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-29 10:52:08 +08:00
|
|
|
.brand-desc {
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
letter-spacing: 1px;
|
|
|
|
|
opacity: .85;
|
2026-05-27 16:38:40 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-29 10:52:08 +08:00
|
|
|
// ─── 右侧登录卡片 ───
|
|
|
|
|
.login-card {
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
width: 380px;
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
border-top: 4px solid $sms-teal;
|
|
|
|
|
padding: 40px 36px 28px;
|
|
|
|
|
box-shadow: 0 18px 50px rgba(0, 0, 0, .35);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-title {
|
|
|
|
|
font-size: 22px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: $text-primary;
|
|
|
|
|
letter-spacing: 1px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-tip {
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
color: $text-muted;
|
|
|
|
|
margin-bottom: 26px;
|
2026-05-27 16:38:40 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-27 17:04:48 +08:00
|
|
|
.field-group {
|
2026-06-29 10:52:08 +08:00
|
|
|
margin-bottom: 18px;
|
2026-05-27 17:04:48 +08:00
|
|
|
|
|
|
|
|
.field-label {
|
2026-06-29 10:52:08 +08:00
|
|
|
font-size: 13px;
|
2026-05-27 17:04:48 +08:00
|
|
|
color: $text-secondary;
|
2026-06-29 10:52:08 +08:00
|
|
|
margin-bottom: 8px;
|
2026-05-27 17:04:48 +08:00
|
|
|
}
|
2026-06-29 10:52:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-input {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 42px;
|
|
|
|
|
padding: 0 14px;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
color: $text-primary;
|
|
|
|
|
background: $bg-panel;
|
|
|
|
|
border: 1px solid $border;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
outline: none;
|
|
|
|
|
transition: all .15s;
|
2026-05-27 17:04:48 +08:00
|
|
|
|
2026-06-29 10:52:08 +08:00
|
|
|
&::placeholder { color: $text-muted; }
|
|
|
|
|
&:focus {
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-color: $sms-teal;
|
|
|
|
|
box-shadow: 0 0 0 3px rgba($sms-teal, .12);
|
|
|
|
|
}
|
2026-05-27 16:38:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-error {
|
2026-06-29 10:52:08 +08:00
|
|
|
font-size: 13px;
|
2026-05-27 16:38:40 +08:00
|
|
|
color: $accent-red;
|
2026-06-29 10:52:08 +08:00
|
|
|
padding: 8px 12px;
|
2026-05-27 17:04:48 +08:00
|
|
|
background: rgba($accent-red, .08);
|
2026-06-29 10:52:08 +08:00
|
|
|
border-radius: 6px;
|
|
|
|
|
margin-bottom: 14px;
|
2026-05-27 16:38:40 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-29 10:52:08 +08:00
|
|
|
.login-btn {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 44px;
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
letter-spacing: 4px;
|
|
|
|
|
color: #fff;
|
|
|
|
|
background: $sms-teal;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all .15s;
|
|
|
|
|
|
|
|
|
|
&:hover { background: $sms-teal-dim; box-shadow: 0 6px 16px rgba($sms-teal, .35); }
|
|
|
|
|
&:active { transform: translateY(1px); }
|
|
|
|
|
&:disabled { opacity: .6; cursor: not-allowed; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-foot {
|
2026-05-27 16:38:40 +08:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
2026-06-29 10:52:08 +08:00
|
|
|
gap: 7px;
|
|
|
|
|
margin-top: 22px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: $text-muted;
|
2026-05-27 16:38:40 +08:00
|
|
|
|
2026-06-29 10:52:08 +08:00
|
|
|
.dot { width: 7px; height: 7px; border-radius: 50%; background: $status-run; box-shadow: 0 0 6px rgba($status-run, .6); }
|
|
|
|
|
.ver { margin-left: auto; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.copyright {
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: 22px;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
text-align: center;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: rgba(255, 255, 255, .65);
|
|
|
|
|
letter-spacing: 1px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 窄屏:隐藏品牌区,卡片居中
|
|
|
|
|
@media (max-width: 900px) {
|
|
|
|
|
.login-page { justify-content: center; padding: 0 20px; }
|
|
|
|
|
.login-brand { display: none; }
|
2026-05-27 16:38:40 +08:00
|
|
|
}
|
|
|
|
|
</style>
|