492 行
17 KiB
HTML
492 行
17 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN" dir="ltr">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
<title>{$title|default='登录'} - 中央储备粮遵义直属库有限公司</title>
|
|
<link rel="shortcut icon" href="/assets/img/favicon.ico" />
|
|
<style>
|
|
:root {
|
|
--primary: #0284c7;
|
|
--primary-hover: #0369a1;
|
|
--primary-glow: rgba(56, 189, 248, 0.35);
|
|
--bg-page: #0a1120;
|
|
--bg-card: rgba(15, 23, 42, 0.78);
|
|
--border-glass: rgba(56, 189, 248, 0.22);
|
|
--text-main: #f8fafc;
|
|
--text-sub: #94a3b8;
|
|
--input-bg: rgba(30, 41, 59, 0.7);
|
|
--input-border: rgba(148, 163, 184, 0.2);
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
margin: 0;
|
|
padding: 0;
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
background-color: var(--bg-page);
|
|
color: var(--text-main);
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
position: relative;
|
|
padding: 20px;
|
|
}
|
|
|
|
.login-bg {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: {if $background}url('{$background}') center/cover no-repeat{else}radial-gradient(ellipse at 50% 30%, rgba(2, 132, 199, 0.2) 0%, transparent 65%), #070e1b{/if};
|
|
z-index: 0;
|
|
}
|
|
|
|
.login-card {
|
|
position: relative;
|
|
z-index: 1;
|
|
width: 100%;
|
|
max-width: 420px;
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border-glass);
|
|
border-radius: 16px;
|
|
padding: 40px 36px;
|
|
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6), 0 0 30px rgba(56, 189, 248, 0.1);
|
|
backdrop-filter: blur(20px);
|
|
-webkit-backdrop-filter: blur(20px);
|
|
}
|
|
|
|
.login-header {
|
|
text-align: center;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.login-logo {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 12px;
|
|
background: linear-gradient(135deg, #0284c7, #0ea5e9);
|
|
box-shadow: 0 4px 14px var(--primary-glow);
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.login-logo svg {
|
|
width: 26px;
|
|
height: 26px;
|
|
stroke: #fff;
|
|
fill: none;
|
|
stroke-width: 2;
|
|
}
|
|
|
|
.login-header h1 {
|
|
font-size: 19px;
|
|
font-weight: 600;
|
|
color: #ffffff;
|
|
letter-spacing: 0.5px;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.login-header p {
|
|
font-size: 13px;
|
|
color: var(--text-sub);
|
|
}
|
|
|
|
.form-item {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.form-label {
|
|
display: block;
|
|
font-size: 13px;
|
|
color: #cbd5e1;
|
|
margin-bottom: 8px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.input-wrap {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.input-icon {
|
|
position: absolute;
|
|
left: 14px;
|
|
width: 18px;
|
|
height: 18px;
|
|
stroke: #64748b;
|
|
fill: none;
|
|
stroke-width: 2;
|
|
pointer-events: none;
|
|
transition: stroke 0.2s;
|
|
}
|
|
|
|
.form-input {
|
|
width: 100%;
|
|
height: 44px;
|
|
padding: 0 14px 0 42px;
|
|
background: var(--input-bg);
|
|
border: 1px solid var(--input-border);
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
color: #ffffff;
|
|
outline: none;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.form-input:focus {
|
|
border-color: #38bdf8;
|
|
box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.2);
|
|
background: rgba(15, 23, 42, 0.85);
|
|
}
|
|
|
|
.form-input:focus + .input-icon,
|
|
.input-wrap:focus-within .input-icon {
|
|
stroke: #38bdf8;
|
|
}
|
|
|
|
.password-toggle {
|
|
position: absolute;
|
|
right: 12px;
|
|
cursor: pointer;
|
|
padding: 4px;
|
|
display: flex;
|
|
align-items: center;
|
|
color: #64748b;
|
|
transition: color 0.2s;
|
|
}
|
|
|
|
.password-toggle:hover {
|
|
color: #38bdf8;
|
|
}
|
|
|
|
.captcha-row {
|
|
display: flex;
|
|
gap: 12px;
|
|
}
|
|
|
|
.captcha-row .input-wrap {
|
|
flex: 1;
|
|
}
|
|
|
|
.captcha-img {
|
|
height: 44px;
|
|
min-width: 110px;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
border: 1px solid var(--input-border);
|
|
cursor: pointer;
|
|
background: #fff;
|
|
}
|
|
|
|
.captcha-img img {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: block;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.form-options {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 24px;
|
|
font-size: 13px;
|
|
color: var(--text-sub);
|
|
}
|
|
|
|
.remember-label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
cursor: pointer;
|
|
user-select: none;
|
|
}
|
|
|
|
.remember-label input[type="checkbox"] {
|
|
width: 16px;
|
|
height: 16px;
|
|
accent-color: var(--primary);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.login-btn {
|
|
width: 100%;
|
|
height: 44px;
|
|
background: linear-gradient(135deg, #0284c7, #0274ae);
|
|
color: #ffffff;
|
|
border: none;
|
|
border-radius: 8px;
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
letter-spacing: 2px;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
transition: all 0.2s ease;
|
|
box-shadow: 0 4px 16px var(--primary-glow);
|
|
}
|
|
|
|
.login-btn:hover:not(:disabled) {
|
|
background: linear-gradient(135deg, #0369a1, #0284c7);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.login-btn:disabled {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.btn-spinner {
|
|
width: 16px;
|
|
height: 16px;
|
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
border-radius: 50%;
|
|
border-top-color: #fff;
|
|
animation: spin 0.8s linear infinite;
|
|
display: none;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
.toast-box {
|
|
position: fixed;
|
|
top: 24px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
z-index: 9999;
|
|
padding: 10px 20px;
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: #fff;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transition: all 0.25s ease;
|
|
}
|
|
|
|
.toast-box.show {
|
|
opacity: 1;
|
|
transform: translateX(-50%) translateY(4px);
|
|
}
|
|
|
|
.toast-success { background: #059669; }
|
|
.toast-error { background: #dc2626; }
|
|
.toast-warning { background: #d97706; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="login-bg"></div>
|
|
<div id="toast" class="toast-box"></div>
|
|
|
|
<div class="login-card">
|
|
<div class="login-header">
|
|
<div class="login-logo">
|
|
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
|
|
</div>
|
|
<h1>中央储备粮遵义直属库有限公司</h1>
|
|
<p>智慧仓储管理系统</p>
|
|
</div>
|
|
|
|
<form id="login-form" autocomplete="off" onsubmit="return false;">
|
|
{:token()}
|
|
<div class="form-item">
|
|
<label class="form-label" for="login-username">账号</label>
|
|
<div class="input-wrap">
|
|
<svg class="input-icon" viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
|
|
<input type="text" id="login-username" name="username" class="form-input" placeholder="请输入账号" autofocus required>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-item">
|
|
<label class="form-label" for="login-password">密码</label>
|
|
<div class="input-wrap">
|
|
<svg class="input-icon" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>
|
|
<input type="password" id="login-password" name="password" class="form-input" placeholder="请输入密码" required>
|
|
<div class="password-toggle" id="btn-toggle-pwd" title="切换密码可见性">
|
|
<svg id="eye-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{if $Think.config.fastadmin.login_captcha}
|
|
<div class="form-item">
|
|
<label class="form-label" for="login-captcha">验证码</label>
|
|
<div class="captcha-row">
|
|
<div class="input-wrap">
|
|
<svg class="input-icon" viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
|
|
<input type="text" id="login-captcha" name="captcha" class="form-input" placeholder="请输入验证码" maxlength="6" required>
|
|
</div>
|
|
<div class="captcha-img" title="点击刷新验证码">
|
|
<img id="verify-img" src="{:rtrim('__PUBLIC__', '/')}/captcha" alt="验证码" onclick="refreshCaptcha()" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="form-options">
|
|
<label class="remember-label" for="login-keeplogin">
|
|
<input type="checkbox" id="login-keeplogin" name="keeplogin" value="1" checked>
|
|
<span>保持登录</span>
|
|
</label>
|
|
</div>
|
|
|
|
<button type="submit" class="login-btn" id="btn-submit">
|
|
<span class="btn-spinner" id="btn-spinner"></span>
|
|
<span id="btn-text">登 录</span>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
function refreshCaptcha() {
|
|
var img = document.getElementById('verify-img');
|
|
if (img) {
|
|
img.src = "{:rtrim('__PUBLIC__', '/')}/captcha?r=" + Math.random();
|
|
}
|
|
}
|
|
|
|
(function () {
|
|
var form = document.getElementById('login-form');
|
|
var btnSubmit = document.getElementById('btn-submit');
|
|
var btnText = document.getElementById('btn-text');
|
|
var btnSpinner = document.getElementById('btn-spinner');
|
|
var usernameInput = document.getElementById('login-username');
|
|
var passwordInput = document.getElementById('login-password');
|
|
var captchaInput = document.getElementById('login-captcha');
|
|
var keeploginInput = document.getElementById('login-keeplogin');
|
|
var btnTogglePwd = document.getElementById('btn-toggle-pwd');
|
|
var eyeIcon = document.getElementById('eye-icon');
|
|
var toastEl = document.getElementById('toast');
|
|
var toastTimer = null;
|
|
|
|
// 密码显隐切换
|
|
if (btnTogglePwd && passwordInput && eyeIcon) {
|
|
btnTogglePwd.addEventListener('click', function () {
|
|
var isPassword = passwordInput.getAttribute('type') === 'password';
|
|
passwordInput.setAttribute('type', isPassword ? 'text' : 'password');
|
|
eyeIcon.innerHTML = isPassword
|
|
? '<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line>'
|
|
: '<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle>';
|
|
});
|
|
}
|
|
|
|
// 轻量 Toast 提示
|
|
function showToast(msg, type) {
|
|
if (!toastEl) return;
|
|
clearTimeout(toastTimer);
|
|
toastEl.className = 'toast-box toast-' + (type || 'info');
|
|
toastEl.textContent = msg;
|
|
toastEl.classList.add('show');
|
|
toastTimer = setTimeout(function () {
|
|
toastEl.classList.remove('show');
|
|
}, 2600);
|
|
}
|
|
|
|
// 设置 Loading 状态
|
|
function setLoading(loading) {
|
|
if (loading) {
|
|
btnSubmit.disabled = true;
|
|
btnSpinner.style.display = 'inline-block';
|
|
btnText.textContent = '登录中...';
|
|
} else {
|
|
btnSubmit.disabled = false;
|
|
btnSpinner.style.display = 'none';
|
|
btnText.textContent = '登 录';
|
|
}
|
|
}
|
|
|
|
// 提交处理
|
|
function handleSubmit() {
|
|
var username = usernameInput ? usernameInput.value.trim() : '';
|
|
var password = passwordInput ? passwordInput.value : '';
|
|
var captcha = captchaInput ? captchaInput.value.trim() : '';
|
|
var keeplogin = (keeploginInput && keeploginInput.checked) ? 1 : 0;
|
|
var tokenInput = document.querySelector('input[name="__token__"]');
|
|
var token = tokenInput ? tokenInput.value : '';
|
|
|
|
if (!username) {
|
|
showToast('请输入账号', 'warning');
|
|
if (usernameInput) usernameInput.focus();
|
|
return;
|
|
}
|
|
if (!password) {
|
|
showToast('请输入密码', 'warning');
|
|
if (passwordInput) passwordInput.focus();
|
|
return;
|
|
}
|
|
if (captchaInput && !captcha) {
|
|
showToast('请输入验证码', 'warning');
|
|
captchaInput.focus();
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
|
|
var postData = new URLSearchParams();
|
|
postData.append('username', username);
|
|
postData.append('password', password);
|
|
postData.append('keeplogin', keeplogin);
|
|
postData.append('__token__', token);
|
|
if (captchaInput) {
|
|
postData.append('captcha', captcha);
|
|
}
|
|
|
|
fetch('{:url("login")}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
},
|
|
body: postData.toString()
|
|
})
|
|
.then(function (res) { return res.json(); })
|
|
.then(function (res) {
|
|
setLoading(false);
|
|
if (res.data && res.data.token && tokenInput) {
|
|
tokenInput.value = res.data.token;
|
|
}
|
|
|
|
if (res.code === 1) {
|
|
showToast(res.msg || '登录成功', 'success');
|
|
setTimeout(function () {
|
|
window.location.href = res.url || 'index/index';
|
|
}, 600);
|
|
} else {
|
|
refreshCaptcha();
|
|
showToast(res.msg || '用户名或密码错误', 'error');
|
|
}
|
|
})
|
|
.catch(function () {
|
|
setLoading(false);
|
|
refreshCaptcha();
|
|
showToast('网络异常,请重试', 'error');
|
|
});
|
|
}
|
|
|
|
btnSubmit.addEventListener('click', handleSubmit);
|
|
|
|
[usernameInput, passwordInput, captchaInput].forEach(function (inp) {
|
|
if (inp) {
|
|
inp.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Enter' || e.keyCode === 13) {
|
|
handleSubmit();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html> |