<!DOCTYPE html>
|
<html lang="zh-CN">
|
<head>
|
<meta charset="UTF-8">
|
<title>系统登录</title>
|
<meta name="renderer" content="webkit">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
<link rel="icon" type="image/x-icon" href="../static/images/wcs_logo.png">
|
<link rel="stylesheet" href="../static/vue/element/element.css">
|
<link rel="stylesheet" href="../static/css/animate.min.css">
|
<style>
|
[v-cloak] {
|
display: none;
|
}
|
|
html,
|
body {
|
min-height: 100%;
|
margin: 0;
|
font-family: "Avenir Next", "PingFang SC", "Microsoft YaHei", sans-serif;
|
}
|
|
body {
|
background:
|
radial-gradient(900px 420px at 12% 14%, rgba(92, 176, 255, 0.20), transparent 60%),
|
radial-gradient(760px 360px at 86% 18%, rgba(54, 208, 177, 0.18), transparent 58%),
|
linear-gradient(135deg, rgba(6, 26, 58, 0.84), rgba(7, 38, 73, 0.64)),
|
url("../static/images/login.png") center center / cover no-repeat;
|
color: #243447;
|
}
|
|
.login-shell {
|
min-height: 100vh;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
padding: 36px 16px;
|
box-sizing: border-box;
|
position: relative;
|
}
|
|
.login-shell::before {
|
content: "";
|
position: absolute;
|
inset: 0;
|
background:
|
linear-gradient(120deg, rgba(6, 18, 38, 0.40), transparent 42%),
|
linear-gradient(300deg, rgba(16, 66, 98, 0.30), transparent 38%);
|
pointer-events: none;
|
}
|
|
.login-layout {
|
width: min(1180px, 100%);
|
display: grid;
|
grid-template-columns: minmax(0, 1.1fr) minmax(360px, 430px);
|
gap: 28px;
|
align-items: stretch;
|
position: relative;
|
z-index: 1;
|
}
|
|
.lang-switch {
|
position: fixed;
|
top: 18px;
|
right: 24px;
|
z-index: 3;
|
}
|
|
.lang-switch select {
|
min-width: 144px;
|
height: 34px;
|
padding: 0 12px;
|
border: 1px solid rgba(214, 219, 230, 0.94);
|
border-radius: 18px;
|
background: rgba(255, 255, 255, 0.94);
|
color: #3b4a5a;
|
outline: none;
|
}
|
|
.hero-panel {
|
min-height: 620px;
|
border-radius: 30px;
|
padding: 42px 40px 36px;
|
box-sizing: border-box;
|
color: #eef5ff;
|
position: relative;
|
overflow: hidden;
|
background:
|
radial-gradient(520px 280px at 18% 12%, rgba(116, 193, 255, 0.30), transparent 64%),
|
radial-gradient(480px 260px at 92% 88%, rgba(58, 214, 182, 0.26), transparent 60%),
|
linear-gradient(155deg, rgba(13, 39, 73, 0.88), rgba(12, 76, 104, 0.62));
|
border: 1px solid rgba(200, 225, 255, 0.18);
|
box-shadow: 0 32px 70px rgba(6, 17, 36, 0.32);
|
}
|
|
.hero-panel::before,
|
.hero-panel::after {
|
content: "";
|
position: absolute;
|
border-radius: 999px;
|
background: rgba(255, 255, 255, 0.07);
|
}
|
|
.hero-panel::before {
|
width: 420px;
|
height: 420px;
|
top: -220px;
|
right: -140px;
|
}
|
|
.hero-panel::after {
|
width: 260px;
|
height: 260px;
|
left: -80px;
|
bottom: -120px;
|
}
|
|
.brand-chip {
|
display: inline-flex;
|
align-items: center;
|
gap: 10px;
|
padding: 8px 14px;
|
border-radius: 999px;
|
background: rgba(255, 255, 255, 0.10);
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
font-size: 12px;
|
letter-spacing: 0.12em;
|
text-transform: uppercase;
|
}
|
|
.brand-chip::before {
|
content: "";
|
width: 8px;
|
height: 8px;
|
border-radius: 50%;
|
background: #7ff6d6;
|
box-shadow: 0 0 0 6px rgba(127, 246, 214, 0.12);
|
}
|
|
.hero-title {
|
margin: 24px 0 14px;
|
font-size: 44px;
|
line-height: 1.08;
|
font-weight: 700;
|
letter-spacing: -0.02em;
|
}
|
|
.hero-subtitle {
|
max-width: 560px;
|
color: rgba(236, 245, 255, 0.82);
|
line-height: 1.8;
|
font-size: 15px;
|
}
|
|
.hero-metrics {
|
display: grid;
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
gap: 14px;
|
margin-top: 34px;
|
}
|
|
.metric-card {
|
padding: 18px 18px 16px;
|
border-radius: 20px;
|
background: rgba(255, 255, 255, 0.08);
|
border: 1px solid rgba(255, 255, 255, 0.10);
|
backdrop-filter: blur(8px);
|
}
|
|
.metric-value {
|
font-size: 28px;
|
font-weight: 700;
|
line-height: 1.1;
|
margin-bottom: 8px;
|
}
|
|
.metric-label {
|
color: rgba(236, 245, 255, 0.70);
|
font-size: 13px;
|
}
|
|
.hero-footer {
|
position: absolute;
|
left: 40px;
|
right: 40px;
|
bottom: 30px;
|
display: flex;
|
justify-content: space-between;
|
gap: 16px;
|
font-size: 12px;
|
color: rgba(236, 245, 255, 0.60);
|
}
|
|
.login-card {
|
width: 100%;
|
min-height: 620px;
|
border-radius: 30px;
|
background: rgba(255, 255, 255, 0.92);
|
border: 1px solid rgba(219, 227, 238, 0.96);
|
box-shadow: 0 28px 60px rgba(8, 28, 61, 0.18);
|
overflow: hidden;
|
backdrop-filter: blur(16px);
|
display: flex;
|
flex-direction: column;
|
justify-content: center;
|
}
|
|
.login-head {
|
padding: 36px 38px 20px;
|
}
|
|
.login-title {
|
margin: 0;
|
font-size: 32px;
|
font-weight: 700;
|
color: #17324f;
|
cursor: pointer;
|
user-select: none;
|
}
|
|
.login-subtitle {
|
margin-top: 10px;
|
color: #6d7f90;
|
font-size: 14px;
|
line-height: 1.6;
|
}
|
|
.login-body {
|
padding: 0 38px 38px;
|
}
|
|
.login-form .el-form-item {
|
margin-bottom: 20px;
|
}
|
|
.login-form .el-input__inner,
|
.login-form .el-button {
|
height: 48px;
|
line-height: 48px;
|
}
|
|
.login-form .el-input__prefix {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
color: #91a0b2;
|
}
|
|
.login-form .el-input__inner {
|
border-radius: 16px;
|
border-color: rgba(214, 223, 235, 0.94);
|
background: rgba(248, 251, 255, 0.84);
|
padding-left: 44px;
|
}
|
|
.login-form .el-input__inner:focus {
|
background: #fff;
|
border-color: #4a8df0;
|
}
|
|
.login-submit {
|
width: 100%;
|
display: inline-flex;
|
align-items: center;
|
justify-content: center;
|
font-size: 16px;
|
font-weight: 600;
|
border-radius: 16px;
|
margin-top: 6px;
|
box-shadow: 0 14px 24px rgba(46, 115, 223, 0.28);
|
}
|
|
.tools-dialog .el-dialog,
|
.text-dialog .el-dialog,
|
.upload-dialog .el-dialog {
|
border-radius: 20px;
|
overflow: hidden;
|
}
|
|
.tools-dialog .el-dialog__header,
|
.text-dialog .el-dialog__header,
|
.upload-dialog .el-dialog__header {
|
padding: 18px 20px 12px;
|
border-bottom: 1px solid rgba(222, 230, 239, 0.92);
|
background: #f8fbff;
|
}
|
|
.tools-dialog .el-dialog__title,
|
.text-dialog .el-dialog__title,
|
.upload-dialog .el-dialog__title {
|
font-weight: 700;
|
color: #243447;
|
}
|
|
.tools-dialog .el-dialog__body,
|
.text-dialog .el-dialog__body,
|
.upload-dialog .el-dialog__body {
|
padding: 18px 20px 20px;
|
}
|
|
.tool-group + .tool-group {
|
margin-top: 18px;
|
padding-top: 18px;
|
border-top: 1px solid rgba(235, 240, 246, 0.95);
|
}
|
|
.tool-title {
|
font-weight: 700;
|
color: #43576b;
|
margin-bottom: 10px;
|
}
|
|
.tool-desc {
|
margin-top: 8px;
|
font-size: 12px;
|
color: #8c9aac;
|
line-height: 1.6;
|
}
|
|
.tool-actions {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 10px;
|
}
|
|
.tool-actions .el-button {
|
display: inline-flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.dialog-text-label {
|
font-weight: 700;
|
color: #43576b;
|
margin-bottom: 8px;
|
}
|
|
.dialog-text-tip {
|
margin-bottom: 10px;
|
color: #8c9aac;
|
font-size: 12px;
|
line-height: 1.6;
|
}
|
|
.text-footer,
|
.upload-footer {
|
display: flex;
|
justify-content: flex-end;
|
gap: 10px;
|
margin-top: 14px;
|
}
|
|
.text-footer .el-button,
|
.upload-footer .el-button {
|
min-width: 96px;
|
display: inline-flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
@media (max-width: 640px) {
|
.login-shell {
|
padding-top: 72px;
|
}
|
|
.lang-switch {
|
right: 12px;
|
top: 12px;
|
}
|
|
.login-head,
|
.login-body {
|
padding-left: 20px;
|
padding-right: 20px;
|
}
|
|
.login-title {
|
font-size: 26px;
|
}
|
}
|
|
@media (max-width: 980px) {
|
.login-layout {
|
grid-template-columns: 1fr;
|
max-width: 430px;
|
}
|
|
.hero-panel {
|
min-height: auto;
|
padding: 28px 24px 78px;
|
}
|
|
.hero-title {
|
font-size: 32px;
|
}
|
|
.hero-metrics {
|
grid-template-columns: 1fr;
|
}
|
|
.hero-footer {
|
left: 24px;
|
right: 24px;
|
bottom: 22px;
|
flex-direction: column;
|
}
|
|
.login-card {
|
min-height: auto;
|
}
|
}
|
</style>
|
</head>
|
<body>
|
<div id="app" class="login-shell" v-cloak>
|
<div class="lang-switch">
|
<select v-model="currentLocale" aria-label="Language" @change="handleLocaleChange">
|
<option v-for="item in localeOptions" :key="item.tag" :value="item.tag">{{ item.label }}</option>
|
</select>
|
</div>
|
|
<div class="login-layout">
|
<section class="hero-panel animate__animated animate__fadeInLeft">
|
<div class="brand-chip">Zoneyung WCS</div>
|
<div class="hero-title">WCS系统让设备调度、任务执行与现场监控保持在同一套业务链路中。</div>
|
<div class="hero-subtitle">
|
Warehouse Control System 面向自动化立体仓库现场执行层,围绕堆垛机、RGV、输送站台、任务指令、
|
库位状态和日志追踪进行统一调度与可视化管理,帮助仓储系统实现稳定、可追溯、可联动的作业控制。
|
浙江中扬立库技术有限公司长期专注于自动化立体仓库与智能物流系统建设,覆盖方案设计、软件控制、
|
设备集成与项目实施交付。
|
</div>
|
<div class="hero-metrics">
|
<div class="metric-card">
|
<div class="metric-value">调度</div>
|
<div class="metric-label">统一编排现场执行任务</div>
|
</div>
|
<div class="metric-card">
|
<div class="metric-value">追溯</div>
|
<div class="metric-label">作业、设备、日志全链路留痕</div>
|
</div>
|
<div class="metric-card">
|
<div class="metric-value">集成</div>
|
<div class="metric-label">对接WMS、设备与业务规则</div>
|
</div>
|
</div>
|
<div class="hero-footer">
|
<span>浙江中扬立库技术有限公司</span>
|
<span>自动化立体仓库与智能物流系统解决方案</span>
|
</div>
|
</section>
|
|
<section class="login-card animate__animated animate__fadeInUp">
|
<div class="login-head">
|
<h1 class="login-title" @click="handleTitleClick">{{ text('login.title', 'WCS系统V3.0') }}</h1>
|
<div class="login-subtitle">请输入账号和密码进入系统。</div>
|
</div>
|
<div class="login-body">
|
<el-form ref="loginForm" class="login-form" :model="loginForm" :rules="loginRules" @submit.native.prevent>
|
<el-form-item prop="mobile">
|
<el-input v-model.trim="loginForm.mobile" :placeholder="text('login.username', '账号')" clearable @keyup.enter.native="handleLogin">
|
<i slot="prefix" class="el-input__icon el-icon-user"></i>
|
</el-input>
|
</el-form-item>
|
<el-form-item prop="password">
|
<el-input v-model="loginForm.password" type="password" show-password :placeholder="text('login.password', '密码')" @keyup.enter.native="handleLogin">
|
<i slot="prefix" class="el-input__icon el-icon-lock"></i>
|
</el-input>
|
</el-form-item>
|
<el-button class="login-submit" type="primary" :loading="loginLoading" @click="handleLogin">
|
{{ text('login.submit', '登录') }}
|
</el-button>
|
</el-form>
|
</div>
|
</section>
|
</div>
|
|
<el-dialog class="tools-dialog" title="系统工具" :visible.sync="toolsDialogVisible" width="560px" :close-on-click-modal="true" append-to-body>
|
<div class="tool-group">
|
<div class="tool-title">{{ text('login.tools.recommended', '推荐操作') }}</div>
|
<div class="tool-actions">
|
<el-button type="primary" size="small" @click="requestCode">{{ text('login.tools.requestCode', '获取请求码') }}</el-button>
|
<el-button type="primary" size="small" plain @click="activateLicense">{{ text('login.tools.activate', '一键激活') }}</el-button>
|
</div>
|
<div class="tool-desc">{{ text('login.tools.recommendedDesc', '优先使用“获取请求码”和“一键激活”完成许可证申请与激活。') }}</div>
|
</div>
|
<div class="tool-group">
|
<div class="tool-title">{{ text('login.tools.others', '其他工具') }}</div>
|
<div class="tool-actions">
|
<el-button size="small" @click="getProjectName">{{ text('login.tools.projectName', '获取项目名称') }}</el-button>
|
<el-button size="small" @click="getServerInfo">{{ text('login.tools.serverInfo', '获取系统配置') }}</el-button>
|
<el-button size="small" @click="uploadDialogVisible = true">{{ text('login.tools.uploadLicense', '录入许可证') }}</el-button>
|
</div>
|
</div>
|
</el-dialog>
|
|
<el-dialog class="text-dialog" :title="textDialog.title" :visible.sync="textDialogVisible" width="720px" append-to-body>
|
<div class="dialog-text-label">{{ textDialog.label }}</div>
|
<div v-if="textDialog.tip" class="dialog-text-tip">{{ textDialog.tip }}</div>
|
<el-input v-model="textDialog.text" type="textarea" :rows="10" readonly></el-input>
|
<div class="text-footer">
|
<el-button @click="textDialogVisible = false">关闭</el-button>
|
<el-button type="primary" @click="copyText">{{ text('copy', '复制') }}</el-button>
|
</div>
|
</el-dialog>
|
|
<el-dialog class="upload-dialog" title="录入许可证" :visible.sync="uploadDialogVisible" width="760px" append-to-body>
|
<div class="dialog-text-label">许可证 Base64</div>
|
<div class="dialog-text-tip">将许可证服务端返回的 license 字段完整粘贴到这里。</div>
|
<el-input v-model.trim="licenseBase64" type="textarea" :rows="10"></el-input>
|
<div class="upload-footer">
|
<el-button @click="uploadDialogVisible = false">取消</el-button>
|
<el-button type="primary" @click="submitLicense">提交</el-button>
|
</div>
|
</el-dialog>
|
</div>
|
</body>
|
<script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script>
|
<script type="text/javascript" src="../static/js/tools/md5.js"></script>
|
<script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1"></script>
|
<script type="text/javascript" src="../static/vue/js/vue.min.js"></script>
|
<script type="text/javascript" src="../static/vue/element/element.js"></script>
|
<script type="text/javascript" src="../static/js/login/login.js?v=20260310_login_vue"></script>
|
</html>
|