erp-frontend/public/login-fallback.html
2026-04-01 17:07:17 +08:00

426 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ERP系统登录</title>
<!-- Element Plus CSS -->
<link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css">
<!-- Element Plus Icons -->
<link rel="stylesheet" href="https://unpkg.com/@element-plus/icons-vue/dist/index.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.login-container {
width: 100%;
max-width: 420px;
}
.login-box {
background: white;
border-radius: 16px;
padding: 40px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
}
.login-header {
text-align: center;
margin-bottom: 40px;
}
.login-title {
font-size: 28px;
font-weight: 700;
color: #333;
margin-bottom: 8px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.login-subtitle {
font-size: 14px;
color: #666;
margin: 0;
}
.form-group {
margin-bottom: 24px;
}
.el-input {
width: 100%;
}
.login-options {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.login-btn {
width: 100%;
height: 48px;
font-size: 16px;
font-weight: 600;
border-radius: 8px;
margin-top: 10px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
}
.login-btn:hover {
opacity: 0.9;
transform: translateY(-1px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.login-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.register-link {
text-align: center;
margin-top: 24px;
font-size: 14px;
color: #666;
}
.login-footer {
border-top: 1px solid #eee;
padding-top: 20px;
text-align: center;
margin-top: 30px;
}
.test-account {
background: #f8f9fa;
border-radius: 8px;
padding: 12px;
margin-bottom: 20px;
font-size: 12px;
color: #666;
line-height: 1.5;
}
.test-account p {
margin: 4px 0;
}
.copyright {
font-size: 12px;
color: #999;
}
.el-message {
z-index: 9999 !important;
}
@media (max-width: 480px) {
.login-box {
padding: 30px 20px;
}
.login-title {
font-size: 24px;
}
.login-options {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
}
</style>
</head>
<body>
<div class="login-container">
<div class="login-box">
<div class="login-header">
<h1 class="login-title">ERP管理系统</h1>
<p class="login-subtitle">欢迎回来,请登录您的账户</p>
</div>
<form id="loginForm" class="login-form">
<div class="form-group">
<el-input
v-model="email"
placeholder="请输入邮箱"
size="large"
clearable
>
<template #prefix>
<el-icon><User /></el-icon>
</template>
</el-input>
</div>
<div class="form-group">
<el-input
v-model="password"
type="password"
placeholder="请输入密码"
size="large"
show-password
clearable
>
<template #prefix>
<el-icon><Lock /></el-icon>
</template>
</el-input>
</div>
<div class="login-options">
<el-checkbox v-model="rememberMe" label="记住我" />
<el-link type="primary" @click="goToForgotPassword" class="forgot-password">
忘记密码?
</el-link>
</div>
<el-button
type="primary"
size="large"
class="login-btn"
:loading="loading"
@click="handleLogin"
>
{{ loading ? '登录中...' : '登录' }}
</el-button>
<div class="register-link">
还没有账号?
<el-link type="primary" @click="goToRegister">
立即注册
</el-link>
</div>
</form>
<div class="login-footer">
<div class="test-account">
<p><strong>测试账号:</strong> admin@erp.com / password123</p>
<p><strong>API地址:</strong> <span id="apiUrl">/api</span></p>
</div>
<div class="copyright">
© 2024 ERP管理系统. All rights reserved.
</div>
</div>
</div>
</div>
<!-- Vue 3 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<!-- Element Plus -->
<script src="https://unpkg.com/element-plus/dist/index.full.js"></script>
<!-- Element Plus Icons -->
<script src="https://unpkg.com/@element-plus/icons-vue/dist/index.js"></script>
<!-- Axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
// 显示API地址
document.getElementById('apiUrl').textContent = window.location.origin + '/api';
// 创建Vue应用
const { createApp, ref } = Vue;
const { ElMessage, ElLoading } = ElementPlus;
const app = createApp({
setup() {
const email = ref('admin@erp.com');
const password = ref('password123');
const rememberMe = ref(false);
const loading = ref(false);
// 创建axios实例模拟前端的配置
const axiosInstance = axios.create({
baseURL: '/api',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
});
// 添加请求拦截器
axiosInstance.interceptors.request.use(
config => {
const token = localStorage.getItem('erp_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 添加响应拦截器
axiosInstance.interceptors.response.use(
response => {
if (response.status === 204) {
return { code: 200, message: 'success' };
}
if (response.data !== undefined && response.data !== null) {
return response.data;
}
if (response.status === 200) {
return { code: 200, message: 'success' };
}
return response;
},
error => {
console.error('请求错误:', error);
if (error.response?.status === 401) {
localStorage.removeItem('erp_token');
localStorage.removeItem('current_user');
// 如果当前不是登录页,跳转到登录页
if (!window.location.pathname.includes('/login')) {
ElMessage.error('会话已过期,请重新登录');
window.location.href = '/login-fallback.html';
}
}
return Promise.reject(error);
}
);
const handleLogin = async () => {
if (!email.value || !password.value) {
ElMessage.warning('请输入邮箱和密码');
return;
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)) {
ElMessage.warning('请输入有效的邮箱地址');
return;
}
if (password.value.length < 6) {
ElMessage.warning('密码长度至少6位');
return;
}
loading.value = true;
try {
const response = await axiosInstance.post('/auth/login', {
email: email.value,
password: password.value,
remember: rememberMe.value
});
if (response.code === 200) {
ElMessage.success('登录成功');
// 保存token和用户信息
localStorage.setItem('erp_token', response.data.token);
localStorage.setItem('current_user', JSON.stringify(response.data.user));
if (rememberMe.value) {
localStorage.setItem('remember_me', 'true');
}
// 跳转到首页
setTimeout(() => {
window.location.href = '/';
}, 1000);
} else {
ElMessage.error(response.message || '登录失败');
loading.value = false;
}
} catch (error) {
console.error('登录失败:', error);
if (error.response?.data?.message) {
ElMessage.error(error.response.data.message);
} else if (error.message) {
ElMessage.error(`登录失败: ${error.message}`);
} else {
ElMessage.error('登录失败,请检查网络或服务器状态');
}
loading.value = false;
}
};
const goToRegister = () => {
window.location.href = '/register';
};
const goToForgotPassword = () => {
window.location.href = '/forgot-password';
};
// 检查是否已登录
const checkLoginStatus = () => {
const token = localStorage.getItem('erp_token');
if (token) {
// 如果已登录,跳转到首页
window.location.href = '/';
}
};
// 页面加载时检查登录状态
checkLoginStatus();
// 回车键登录
const handleKeyup = (event) => {
if (event.key === 'Enter' && !loading.value) {
handleLogin();
}
};
// 添加键盘事件监听
window.addEventListener('keyup', handleKeyup);
// 组件卸载时清理事件监听
return {
email,
password,
rememberMe,
loading,
handleLogin,
goToRegister,
goToForgotPassword
};
}
});
// 注册Element Plus
app.use(ElementPlus);
// 注册Element Plus图标
for (const [key, component] of Object.entries(window.ElementPlusIconsVue || {})) {
app.component(key, component);
}
// 挂载应用
app.mount('#app');
// 页面加载完成后的初始化
window.addEventListener('load', () => {
console.log('✅ 备用登录页面已加载');
console.log('🔧 Mock状态: 已启用');
console.log('📡 API地址: /api');
});
</script>
</body>
</html>