Files
offerpai_backend/admin/src/main/resources/templates/login.html
T
2026-07-10 15:27:16 +08:00

127 lines
5.0 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>OfferPie 管理后台</title>
<link rel="stylesheet" href="https://unpkg.com/element-plus@2.7.3/dist/index.css"/>
<style>[v-cloak] { display: none; }</style>
<style>
body { margin: 0; padding: 0; }
#app {
min-height: 100vh;
display: flex;
background: #f5f7fa;
}
.left-panel {
flex: 1;
background: linear-gradient(160deg, #1d1e3c 0%, #2b2d5e 100%);
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 80px;
position: relative;
overflow: hidden;
}
.left-panel::before {
content: '';
position: absolute;
width: 400px;
height: 400px;
border-radius: 50%;
background: rgba(64, 158, 255, 0.06);
top: 10%;
right: -100px;
}
.left-panel .logo { display: flex; align-items: center; gap: 14px; margin-bottom: 24px; }
.left-panel .logo img { height: 44px; }
.left-panel .logo span { font-size: 26px; font-weight: 600; color: #fff; }
.left-panel .desc { font-size: 15px; color: rgba(255,255,255,0.5); line-height: 1.8; }
.left-panel .footer { position: absolute; bottom: 30px; left: 80px; font-size: 12px; color: rgba(255,255,255,0.2); }
.right-panel {
width: 480px;
display: flex;
align-items: center;
justify-content: center;
padding: 40px;
}
.login-box { width: 100%; max-width: 340px; }
.login-box h2 { font-size: 22px; color: #303133; margin-bottom: 6px; }
.login-box .sub { font-size: 13px; color: #909399; margin-bottom: 32px; }
.login-box .el-button { width: 100%; margin-top: 8px; }
@media (max-width: 900px) {
.left-panel { display: none; }
.right-panel { width: 100%; }
}
</style>
</head>
<body>
<div id="app" v-cloak>
<div class="left-panel">
<div class="logo">
<img src="/admin/img/logo.png" alt="logo"/>
<span>OfferPie</span>
</div>
<div class="desc">运营管理后台</div>
<div class="footer">© 2025 OfferPie</div>
</div>
<div class="right-panel">
<div class="login-box">
<h2>欢迎回来</h2>
<p class="sub">请登录管理员账号</p>
<el-form :model="form" :rules="rules" ref="formRef" @keyup.enter="handleLogin">
<el-form-item prop="username">
<el-input v-model="form.username" placeholder="账号" prefix-icon="User" size="large" maxlength="32"/>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="form.password" type="password" placeholder="密码" prefix-icon="Lock" size="large" maxlength="32" show-password/>
</el-form-item>
<el-form-item>
<el-button type="primary" size="large" :loading="loading" @click="handleLogin">登录</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
<script src="https://unpkg.com/vue@3.4.21/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/element-plus@2.7.3/dist/index.full.min.js"></script>
<script src="https://unpkg.com/@element-plus/icons-vue@2.3.1/dist/index.iife.min.js"></script>
<script src="/admin/js/request.js"></script>
<script src="/admin/js/api/auth.js"></script>
<script>
const { createApp, ref, reactive } = Vue;
const app = createApp({
setup() {
const formRef = ref(null);
const loading = ref(false);
const form = reactive({ username: '', password: '' });
const rules = {
username: [{ required: true, message: '请输入账号', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }, { min: 6, max: 32, message: '密码长度6-32位', trigger: 'blur' }]
};
const handleLogin = async () => {
const valid = await formRef.value.validate().catch(() => false);
if (!valid) return;
loading.value = true;
try {
await authApi.login(form.username, form.password);
window.location.href = '/admin/page/index';
} catch (e) { }
finally { loading.value = false; }
};
return { formRef, form, rules, loading, handleLogin };
}
});
// 注册 Element Plus 图标(容错)
if (window.ElementPlusIconsVue) {
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
}
app.use(ElementPlus);
app.mount('#app');
</script>
</body>
</html>