将登陆页面和主页面合二为一
This commit is contained in:
parent
bd78b99b0a
commit
9fed446a6f
File diff suppressed because it is too large
Load Diff
439
XNSimHtml/components/auth-component.js
Normal file
439
XNSimHtml/components/auth-component.js
Normal file
@ -0,0 +1,439 @@
|
||||
class AuthComponent extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
render() {
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
.container {
|
||||
width: 90%;
|
||||
max-width: 1200px;
|
||||
min-height: 600px;
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
}
|
||||
.welcome-section {
|
||||
text-align: center;
|
||||
}
|
||||
.welcome-section h1 {
|
||||
font-size: 3.5rem;
|
||||
margin-bottom: 2rem;
|
||||
font-weight: 700;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.welcome-text {
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.8;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.auth-container {
|
||||
width: 500px;
|
||||
background: white;
|
||||
padding: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.form-toggle {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.toggle-btn {
|
||||
flex: 1;
|
||||
padding: 15px;
|
||||
border: none;
|
||||
background: #f0f0f0;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.toggle-btn.active {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
}
|
||||
.form-panel {
|
||||
display: none;
|
||||
animation: fadeIn 0.3s ease;
|
||||
margin-top: 10px;
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.form-panel.active {
|
||||
display: block;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.form-row .form-group {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #555;
|
||||
font-weight: 500;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
border: 2px solid #eee;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
input:focus {
|
||||
border-color: #667eea;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
.password-input {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.password-input input {
|
||||
padding-right: 40px;
|
||||
}
|
||||
.toggle-password {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.toggle-password:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
.toggle-password:focus {
|
||||
outline: none;
|
||||
}
|
||||
.visibility-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
object-fit: contain;
|
||||
}
|
||||
.remember-me {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.remember-me input {
|
||||
width: auto;
|
||||
}
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.submit-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
.toast {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
z-index: 10000;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.toast.show {
|
||||
opacity: 1;
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
min-height: auto;
|
||||
margin: 20px;
|
||||
}
|
||||
.content {
|
||||
padding: 40px;
|
||||
}
|
||||
.auth-container {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.auth-container {
|
||||
padding: 20px;
|
||||
}
|
||||
.welcome-section h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
.form-row {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div class="welcome-section">
|
||||
<h1>XNSim</h1>
|
||||
<div class="welcome-text">
|
||||
<p>欢迎使用XNSim仿真平台</p>
|
||||
<p>高效、专业的仿真解决方案</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="auth-container">
|
||||
<div class="form-toggle">
|
||||
<button id="loginToggle" class="toggle-btn active">登录</button>
|
||||
<button id="registerToggle" class="toggle-btn">注册</button>
|
||||
</div>
|
||||
|
||||
<div id="loginForm" class="form-panel active">
|
||||
<form id="loginFormElement">
|
||||
<div class="form-group">
|
||||
<label for="loginUsername">用户名</label>
|
||||
<input type="text" id="loginUsername" name="username" placeholder="请输入用户名" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="loginPassword">密码</label>
|
||||
<div class="password-input">
|
||||
<input type="password" id="loginPassword" name="password" placeholder="请输入密码" required>
|
||||
<button type="button" class="toggle-password">
|
||||
<img src="assets/icons/png/invisiable_b.png" alt="显示/隐藏密码" class="visibility-icon">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group remember-me">
|
||||
<input type="checkbox" id="remember" name="remember">
|
||||
<label for="remember">记住我</label>
|
||||
</div>
|
||||
<button type="submit" class="submit-btn">登录</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="registerForm" class="form-panel">
|
||||
<form id="registerFormElement">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="regUsername">用户名 *</label>
|
||||
<input type="text" id="regUsername" name="username" placeholder="请输入用户名" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="fullName">姓名(选填)</label>
|
||||
<input type="text" id="fullName" name="fullName" placeholder="请输入姓名">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="regPassword">密码 *</label>
|
||||
<div class="password-input">
|
||||
<input type="password" id="regPassword" name="password" placeholder="请输入密码" required>
|
||||
<button type="button" class="toggle-password">
|
||||
<img src="assets/icons/png/invisiable_b.png" alt="显示/隐藏密码" class="visibility-icon">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="regConfirmPassword">确认密码 *</label>
|
||||
<div class="password-input">
|
||||
<input type="password" id="regConfirmPassword" name="confirmPassword" placeholder="请再次输入密码" required>
|
||||
<button type="button" class="toggle-password">
|
||||
<img src="assets/icons/png/invisiable_b.png" alt="显示/隐藏密码" class="visibility-icon">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="phone">电话(选填)</label>
|
||||
<input type="tel" id="phone" name="phone" placeholder="请输入电话号码">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">邮箱(选填)</label>
|
||||
<input type="email" id="email" name="email" placeholder="请输入邮箱">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="department">部门(选填)</label>
|
||||
<input type="text" id="department" name="department" placeholder="请输入所属部门">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="position">职位(选填)</label>
|
||||
<input type="text" id="position" name="position" placeholder="请输入职位">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="submit-btn">注册</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
const loginToggle = this.shadowRoot.getElementById('loginToggle');
|
||||
const registerToggle = this.shadowRoot.getElementById('registerToggle');
|
||||
const loginForm = this.shadowRoot.getElementById('loginForm');
|
||||
const registerForm = this.shadowRoot.getElementById('registerForm');
|
||||
const loginFormElement = this.shadowRoot.getElementById('loginFormElement');
|
||||
const registerFormElement = this.shadowRoot.getElementById('registerFormElement');
|
||||
const togglePasswordButtons = this.shadowRoot.querySelectorAll('.toggle-password');
|
||||
|
||||
// 创建Toast提示函数
|
||||
const showToast = (message) => {
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'toast';
|
||||
toast.textContent = message;
|
||||
this.shadowRoot.appendChild(toast);
|
||||
|
||||
setTimeout(() => toast.classList.add('show'), 10);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show');
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
// 切换表单显示
|
||||
loginToggle.addEventListener('click', () => {
|
||||
loginToggle.classList.add('active');
|
||||
registerToggle.classList.remove('active');
|
||||
loginForm.classList.add('active');
|
||||
registerForm.classList.remove('active');
|
||||
});
|
||||
|
||||
registerToggle.addEventListener('click', () => {
|
||||
registerToggle.classList.add('active');
|
||||
loginToggle.classList.remove('active');
|
||||
registerForm.classList.add('active');
|
||||
loginForm.classList.remove('active');
|
||||
});
|
||||
|
||||
// 密码显示/隐藏
|
||||
togglePasswordButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const input = button.parentElement.querySelector('input');
|
||||
const icon = button.querySelector('img');
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
icon.src = 'assets/icons/png/visiable_b.png';
|
||||
} else {
|
||||
input.type = 'password';
|
||||
icon.src = 'assets/icons/png/invisiable_b.png';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 表单提交
|
||||
loginFormElement.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
const username = this.shadowRoot.getElementById('loginUsername').value;
|
||||
const password = this.shadowRoot.getElementById('loginPassword').value;
|
||||
const remember = this.shadowRoot.getElementById('remember').checked;
|
||||
|
||||
if (!username || !password) {
|
||||
showToast('请输入用户名和密码');
|
||||
return;
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CustomEvent('login', {
|
||||
detail: { username, password, remember },
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}));
|
||||
});
|
||||
|
||||
registerFormElement.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
const username = this.shadowRoot.getElementById('regUsername').value;
|
||||
const password = this.shadowRoot.getElementById('regPassword').value;
|
||||
const confirmPassword = this.shadowRoot.getElementById('regConfirmPassword').value;
|
||||
|
||||
if (!username || !password) {
|
||||
showToast('用户名和密码为必填项');
|
||||
return;
|
||||
}
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
showToast('两次输入的密码不一致');
|
||||
return;
|
||||
}
|
||||
|
||||
const userInfo = {
|
||||
full_name: this.shadowRoot.getElementById('fullName').value.trim() || '',
|
||||
phone: this.shadowRoot.getElementById('phone').value.trim() || '',
|
||||
email: this.shadowRoot.getElementById('email').value.trim() || '',
|
||||
department: this.shadowRoot.getElementById('department').value.trim() || '',
|
||||
position: this.shadowRoot.getElementById('position').value.trim() || '',
|
||||
access_level: 1
|
||||
};
|
||||
|
||||
this.dispatchEvent(new CustomEvent('register', {
|
||||
detail: { username, password, userInfo },
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('auth-component', AuthComponent);
|
@ -82,6 +82,10 @@ class UserInfo extends HTMLElement {
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
display: none;
|
||||
z-index: 1000;
|
||||
min-width: 150px;
|
||||
max-width: 250px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.user-dropdown.active .user-dropdown-menu {
|
||||
@ -95,6 +99,9 @@ class UserInfo extends HTMLElement {
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
gap: 8px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
@ -159,6 +166,11 @@ class UserInfo extends HTMLElement {
|
||||
border-style: solid;
|
||||
border-color: transparent transparent rgba(0, 0, 0, 0.8) transparent;
|
||||
}
|
||||
|
||||
.dropdown-item span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
<div class="user-dropdown">
|
||||
<div class="user-dropdown-toggle">
|
||||
@ -242,13 +254,15 @@ class UserInfo extends HTMLElement {
|
||||
try {
|
||||
const userInfoStr = localStorage.getItem('userInfo');
|
||||
if (!userInfoStr) {
|
||||
window.location.href = 'index.html';
|
||||
document.getElementById('authContainer').style.display = 'block';
|
||||
document.getElementById('mainContainer').style.display = 'none';
|
||||
return;
|
||||
}
|
||||
userInfo = JSON.parse(userInfoStr);
|
||||
} catch (error) {
|
||||
console.error('解析用户信息失败:', error);
|
||||
window.location.href = 'index.html';
|
||||
document.getElementById('authContainer').style.display = 'block';
|
||||
document.getElementById('mainContainer').style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
@ -314,7 +328,8 @@ class UserInfo extends HTMLElement {
|
||||
|
||||
logout() {
|
||||
localStorage.removeItem('userInfo');
|
||||
window.location.href = 'index.html';
|
||||
document.getElementById('authContainer').style.display = 'block';
|
||||
document.getElementById('mainContainer').style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,111 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>XNSim - 登录/注册</title>
|
||||
<link rel="icon" type="image/png" href="assets/icons/XNSim.png">
|
||||
<link rel="shortcut icon" type="image/png" href="assets/icons/XNSim.png">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div class="welcome-section">
|
||||
<h1>XNSim</h1>
|
||||
<div class="welcome-text">
|
||||
<p>欢迎使用XNSim仿真平台</p>
|
||||
<p>高效、专业的仿真解决方案</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="auth-container">
|
||||
<div class="form-toggle">
|
||||
<button id="loginToggle" class="toggle-btn active">登录</button>
|
||||
<button id="registerToggle" class="toggle-btn">注册</button>
|
||||
</div>
|
||||
|
||||
<div id="loginForm" class="form-panel active">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="loginUsername">用户名</label>
|
||||
<input type="text" id="loginUsername" name="username" placeholder="请输入用户名" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="loginPassword">密码</label>
|
||||
<div class="password-input">
|
||||
<input type="password" id="loginPassword" name="password" placeholder="请输入密码" required>
|
||||
<button type="button" class="toggle-password">
|
||||
<img src="assets/icons/png/invisiable_b.png" alt="显示/隐藏密码" class="visibility-icon">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group remember-me">
|
||||
<input type="checkbox" id="remember" name="remember">
|
||||
<label for="remember">记住我</label>
|
||||
</div>
|
||||
<button type="submit" class="submit-btn">登录</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="registerForm" class="form-panel">
|
||||
<form>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="regUsername">用户名 *</label>
|
||||
<input type="text" id="regUsername" name="username" placeholder="请输入用户名" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="fullName">姓名(选填)</label>
|
||||
<input type="text" id="fullName" name="fullName" placeholder="请输入姓名">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="regPassword">密码 *</label>
|
||||
<div class="password-input">
|
||||
<input type="password" id="regPassword" name="password" placeholder="请输入密码" required>
|
||||
<button type="button" class="toggle-password">
|
||||
<img src="assets/icons/png/invisiable_b.png" alt="显示/隐藏密码" class="visibility-icon">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="regConfirmPassword">确认密码 *</label>
|
||||
<div class="password-input">
|
||||
<input type="password" id="regConfirmPassword" name="confirmPassword" placeholder="请再次输入密码" required>
|
||||
<button type="button" class="toggle-password">
|
||||
<img src="assets/icons/png/invisiable_b.png" alt="显示/隐藏密码" class="visibility-icon">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="phone">电话(选填)</label>
|
||||
<input type="tel" id="phone" name="phone" placeholder="请输入电话号码">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">邮箱(选填)</label>
|
||||
<input type="email" id="email" name="email" placeholder="请输入邮箱">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="department">部门(选填)</label>
|
||||
<input type="text" id="department" name="department" placeholder="请输入所属部门">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="position">职位(选填)</label>
|
||||
<input type="text" id="position" name="position" placeholder="请输入职位">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="submit-btn">注册</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -7,6 +7,7 @@
|
||||
<link rel="icon" type="image/png" href="assets/icons/XNSim.png">
|
||||
<link rel="shortcut icon" type="image/png" href="assets/icons/XNSim.png">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script src="components/auth-component.js"></script>
|
||||
<script src="components/main-toolbar.js"></script>
|
||||
<script src="components/sub-toolbar.js"></script>
|
||||
<script src="components/user-info.js"></script>
|
||||
@ -183,10 +184,39 @@
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Toast 提示样式 */
|
||||
.toast {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
z-index: 10000;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.toast.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#authContainer, #mainContainer {
|
||||
display: none;
|
||||
}
|
||||
#authContainer.visible, #mainContainer.visible {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-container">
|
||||
<div id="authContainer">
|
||||
<auth-component></auth-component>
|
||||
</div>
|
||||
<div id="mainContainer" class="main-container">
|
||||
<div class="content-wrapper">
|
||||
<main-toolbar></main-toolbar>
|
||||
<sub-toolbar></sub-toolbar>
|
||||
@ -210,284 +240,391 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Toast 提示函数
|
||||
function showToast(message) {
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'toast';
|
||||
toast.textContent = message;
|
||||
document.body.appendChild(toast);
|
||||
|
||||
setTimeout(() => toast.classList.add('show'), 10);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show');
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const authComponent = document.querySelector('auth-component');
|
||||
const authContainer = document.getElementById('authContainer');
|
||||
const mainContainer = document.getElementById('mainContainer');
|
||||
const tabsContainer = document.querySelector('tabs-container');
|
||||
const contentArea = document.querySelector('content-area');
|
||||
|
||||
// 初始化时创建概览标签页并加载概览内容
|
||||
tabsContainer.createTab('overview', '概览', 'dashboard', '主页', 'home');
|
||||
contentArea.loadContent('overview');
|
||||
|
||||
// 监听主工具栏选择事件
|
||||
document.querySelector('main-toolbar').addEventListener('tool-selected', function(e) {
|
||||
const { tool, text } = e.detail;
|
||||
document.querySelector('sub-toolbar').setAttribute('current-tool', tool);
|
||||
// 当选择主页时,显示概览
|
||||
if (tool === 'home') {
|
||||
tabsContainer.createTab('overview', '概览', 'dashboard', '主页', 'home');
|
||||
// 检查是否已登录
|
||||
const checkAuth = () => {
|
||||
const userInfo = localStorage.getItem('userInfo');
|
||||
if (userInfo) {
|
||||
authContainer.classList.remove('visible');
|
||||
mainContainer.classList.add('visible');
|
||||
// 初始化主页面
|
||||
initializeMainPage();
|
||||
} else {
|
||||
authContainer.classList.add('visible');
|
||||
mainContainer.classList.remove('visible');
|
||||
}
|
||||
});
|
||||
|
||||
// 监听子工具栏选择事件
|
||||
document.querySelector('sub-toolbar').addEventListener('sub-item-selected', function(e) {
|
||||
const { parent, text, icon } = e.detail;
|
||||
const parentText = document.querySelector(`main-toolbar`).shadowRoot.querySelector(`[data-tool="${parent}"] span`).textContent;
|
||||
updateBreadcrumb(parentText, text);
|
||||
updateContent(text, icon, parentText, parent);
|
||||
});
|
||||
|
||||
// 监听用户菜单动作
|
||||
document.querySelector('user-info').addEventListener('menu-action', function(e) {
|
||||
const { action } = e.detail;
|
||||
handleMenuAction(action);
|
||||
});
|
||||
|
||||
// 监听标签页激活事件
|
||||
tabsContainer.addEventListener('tab-activated', function(e) {
|
||||
const { id, parentText, title, parentTool, isTabSwitch } = e.detail;
|
||||
|
||||
// 更新面包屑导航
|
||||
updateBreadcrumb(parentText, title);
|
||||
|
||||
// 只有在真正的标签切换时才加载内容
|
||||
if (isTabSwitch) {
|
||||
contentArea.loadContent(id);
|
||||
}
|
||||
|
||||
// 同步更新面包屑导航
|
||||
const breadcrumbIcon = document.querySelector('.breadcrumb .icon-small');
|
||||
if (breadcrumbIcon) {
|
||||
const iconName = getIconNameForTitle(title, parentTool);
|
||||
breadcrumbIcon.src = `assets/icons/png/${iconName}_b.png`;
|
||||
breadcrumbIcon.alt = `${parentText} / ${title}`;
|
||||
}
|
||||
});
|
||||
|
||||
// 监听标签页关闭事件
|
||||
tabsContainer.addEventListener('tab-closed', function(e) {
|
||||
const { id } = e.detail;
|
||||
// 清除关闭标签页的内容缓存
|
||||
contentArea.clearCache(id);
|
||||
});
|
||||
});
|
||||
|
||||
function handleMenuAction(action) {
|
||||
const contentArea = document.querySelector('content-area');
|
||||
const tabsContainer = document.querySelector('tabs-container');
|
||||
switch(action) {
|
||||
case 'profile':
|
||||
tabsContainer.createTab('profile', '个人中心', 'user', '系统', 'system');
|
||||
contentArea.loadContent('profile');
|
||||
break;
|
||||
case 'users':
|
||||
tabsContainer.createTab('users', '用户管理', 'users', '系统', 'system');
|
||||
contentArea.loadContent('users');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function updateBreadcrumb(mainMenu, subMenu) {
|
||||
const currentPath = document.getElementById('currentPath');
|
||||
currentPath.textContent = subMenu ? `${mainMenu} / ${subMenu}` : mainMenu;
|
||||
}
|
||||
|
||||
function updateContent(title, icon, parentText, parentTool) {
|
||||
const tabsContainer = document.querySelector('tabs-container');
|
||||
const contentArea = document.querySelector('content-area');
|
||||
|
||||
if (title === '概览') {
|
||||
// 直接激活第一个标签页(概览标签页)
|
||||
const firstTab = tabsContainer.shadowRoot.querySelector('.tab');
|
||||
if (firstTab) {
|
||||
tabsContainer.activateTab(firstTab.getAttribute('data-tab'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 特殊处理更新记录标签页
|
||||
if (title === '更新记录') {
|
||||
const id = 'update-history';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理运行日志标签页
|
||||
if (title === '运行日志') {
|
||||
const id = 'run-log';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理资源监控标签页
|
||||
if (title === '资源监控') {
|
||||
const id = 'system-info';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
contentArea.loadContent(id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理系统日志标签页
|
||||
if (title === '系统日志') {
|
||||
const id = 'system-log';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
contentArea.loadContent(id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理待办事项标签页
|
||||
if (title === '待办事项') {
|
||||
const id = 'todo';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
contentArea.loadContent(id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理帮助标签页
|
||||
if (title === '帮助') {
|
||||
const id = 'help';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
contentArea.loadContent(id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理Q&A标签页
|
||||
if (title === 'Q&A') {
|
||||
const id = 'qa';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
contentArea.loadContent(id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理运行环境配置标签页
|
||||
if (title === '运行环境配置') {
|
||||
const id = 'run-env-config';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理模型配置标签页
|
||||
if (title === '模型配置') {
|
||||
const id = 'model-config';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理服务配置标签页
|
||||
if (title === '服务配置') {
|
||||
const id = 'service-config';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理接口配置标签页
|
||||
if (title === '接口配置') {
|
||||
const id = 'interface-config';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理模型开发标签页
|
||||
if (title === '模型开发') {
|
||||
const id = 'model-development';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理服务开发标签页
|
||||
if (title === '服务开发') {
|
||||
const id = 'service-development';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理运行测试标签页
|
||||
if (title === '运行测试') {
|
||||
const id = 'run-test';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理运行仿真标签页
|
||||
if (title === '运行仿真') {
|
||||
const id = 'run-simulation';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理仿真监控标签页
|
||||
if (title === '仿真监控') {
|
||||
const id = 'simulation-monitor';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理模型监控标签页
|
||||
if (title === '模型监控') {
|
||||
const id = 'model-monitor';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理数据监控标签页
|
||||
if (title === '数据监控') {
|
||||
const id = 'data-monitor';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理数据采集标签页
|
||||
if (title === '数据采集') {
|
||||
const id = 'data-collection';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理个人中心标签页
|
||||
if (title === '个人中心') {
|
||||
const id = 'profile';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理用户管理标签页
|
||||
if (title === '用户管理') {
|
||||
const id = 'users';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 默认情况下使用标题转换为ID
|
||||
const id = title.toLowerCase().replace(/\s+/g, '-');
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
}
|
||||
|
||||
// 根据标题和父工具获取图标名称的辅助函数
|
||||
function getIconNameForTitle(title, parentTool) {
|
||||
const iconMap = {
|
||||
'概览': 'dashboard',
|
||||
'更新记录': 'clock',
|
||||
'运行日志': 'file',
|
||||
'资源监控': 'server',
|
||||
'帮助': 'help',
|
||||
'Q&A': 'question',
|
||||
'运行环境配置': 'chip',
|
||||
'模型配置': 'cube',
|
||||
'服务配置': 'settings',
|
||||
'接口配置': 'plug',
|
||||
'运行测试': 'flask',
|
||||
'运行仿真': 'rocket',
|
||||
'仿真监控': 'desktop',
|
||||
'模型监控': 'cubes',
|
||||
'数据监控': 'chart-bar',
|
||||
'数据采集': 'database',
|
||||
'个人中心': 'user',
|
||||
'用户管理': 'users',
|
||||
'模型开发': 'cube',
|
||||
'服务开发': 'settings'
|
||||
};
|
||||
|
||||
return iconMap[title] || parentTool || 'con';
|
||||
}
|
||||
// 初始化主页面
|
||||
function initializeMainPage() {
|
||||
// 初始化时创建概览标签页并加载概览内容
|
||||
tabsContainer.createTab('overview', '概览', 'dashboard', '主页', 'home');
|
||||
contentArea.loadContent('overview');
|
||||
|
||||
// 监听主工具栏选择事件
|
||||
document.querySelector('main-toolbar').addEventListener('tool-selected', function(e) {
|
||||
const { tool, text } = e.detail;
|
||||
document.querySelector('sub-toolbar').setAttribute('current-tool', tool);
|
||||
// 当选择主页时,显示概览
|
||||
if (tool === 'home') {
|
||||
tabsContainer.createTab('overview', '概览', 'dashboard', '主页', 'home');
|
||||
}
|
||||
});
|
||||
|
||||
// 监听子工具栏选择事件
|
||||
document.querySelector('sub-toolbar').addEventListener('sub-item-selected', function(e) {
|
||||
const { parent, text, icon } = e.detail;
|
||||
const parentText = document.querySelector(`main-toolbar`).shadowRoot.querySelector(`[data-tool="${parent}"] span`).textContent;
|
||||
updateBreadcrumb(parentText, text);
|
||||
updateContent(text, icon, parentText, parent);
|
||||
});
|
||||
|
||||
// 监听用户菜单动作
|
||||
document.querySelector('user-info').addEventListener('menu-action', function(e) {
|
||||
const { action } = e.detail;
|
||||
handleMenuAction(action);
|
||||
});
|
||||
|
||||
// 监听标签页激活事件
|
||||
tabsContainer.addEventListener('tab-activated', function(e) {
|
||||
const { id, parentText, title, parentTool, isTabSwitch } = e.detail;
|
||||
|
||||
// 更新面包屑导航
|
||||
updateBreadcrumb(parentText, title);
|
||||
|
||||
// 只有在真正的标签切换时才加载内容
|
||||
if (isTabSwitch) {
|
||||
contentArea.loadContent(id);
|
||||
}
|
||||
|
||||
// 同步更新面包屑导航
|
||||
const breadcrumbIcon = document.querySelector('.breadcrumb .icon-small');
|
||||
if (breadcrumbIcon) {
|
||||
const iconName = getIconNameForTitle(title, parentTool);
|
||||
breadcrumbIcon.src = `assets/icons/png/${iconName}_b.png`;
|
||||
breadcrumbIcon.alt = `${parentText} / ${title}`;
|
||||
}
|
||||
});
|
||||
|
||||
// 监听标签页关闭事件
|
||||
tabsContainer.addEventListener('tab-closed', function(e) {
|
||||
const { id } = e.detail;
|
||||
// 清除关闭标签页的内容缓存
|
||||
contentArea.clearCache(id);
|
||||
});
|
||||
}
|
||||
|
||||
function handleMenuAction(action) {
|
||||
const contentArea = document.querySelector('content-area');
|
||||
const tabsContainer = document.querySelector('tabs-container');
|
||||
switch(action) {
|
||||
case 'profile':
|
||||
tabsContainer.createTab('profile', '个人中心', 'user', '系统', 'system');
|
||||
contentArea.loadContent('profile');
|
||||
break;
|
||||
case 'users':
|
||||
tabsContainer.createTab('users', '用户管理', 'users', '系统', 'system');
|
||||
contentArea.loadContent('users');
|
||||
break;
|
||||
case 'logout':
|
||||
// 清除所有用户相关数据
|
||||
localStorage.removeItem('userInfo');
|
||||
localStorage.removeItem('authToken');
|
||||
// 清除所有标签页
|
||||
tabsContainer.clearAllTabs();
|
||||
// 显示退出成功提示
|
||||
showToast('已安全退出登录');
|
||||
// 重新检查认证状态,这会触发返回登录页面
|
||||
checkAuth();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function updateBreadcrumb(mainMenu, subMenu) {
|
||||
const currentPath = document.getElementById('currentPath');
|
||||
currentPath.textContent = subMenu ? `${mainMenu} / ${subMenu}` : mainMenu;
|
||||
}
|
||||
|
||||
function updateContent(title, icon, parentText, parentTool) {
|
||||
const tabsContainer = document.querySelector('tabs-container');
|
||||
const contentArea = document.querySelector('content-area');
|
||||
|
||||
if (title === '概览') {
|
||||
// 直接激活第一个标签页(概览标签页)
|
||||
const firstTab = tabsContainer.shadowRoot.querySelector('.tab');
|
||||
if (firstTab) {
|
||||
tabsContainer.activateTab(firstTab.getAttribute('data-tab'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 特殊处理更新记录标签页
|
||||
if (title === '更新记录') {
|
||||
const id = 'update-history';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理运行日志标签页
|
||||
if (title === '运行日志') {
|
||||
const id = 'run-log';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理资源监控标签页
|
||||
if (title === '资源监控') {
|
||||
const id = 'system-info';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
contentArea.loadContent(id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理系统日志标签页
|
||||
if (title === '系统日志') {
|
||||
const id = 'system-log';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
contentArea.loadContent(id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理待办事项标签页
|
||||
if (title === '待办事项') {
|
||||
const id = 'todo';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
contentArea.loadContent(id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理帮助标签页
|
||||
if (title === '帮助') {
|
||||
const id = 'help';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
contentArea.loadContent(id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理Q&A标签页
|
||||
if (title === 'Q&A') {
|
||||
const id = 'qa';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
contentArea.loadContent(id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理运行环境配置标签页
|
||||
if (title === '运行环境配置') {
|
||||
const id = 'run-env-config';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理模型配置标签页
|
||||
if (title === '模型配置') {
|
||||
const id = 'model-config';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理服务配置标签页
|
||||
if (title === '服务配置') {
|
||||
const id = 'service-config';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理接口配置标签页
|
||||
if (title === '接口配置') {
|
||||
const id = 'interface-config';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理模型开发标签页
|
||||
if (title === '模型开发') {
|
||||
const id = 'model-development';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理服务开发标签页
|
||||
if (title === '服务开发') {
|
||||
const id = 'service-development';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理运行测试标签页
|
||||
if (title === '运行测试') {
|
||||
const id = 'run-test';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理运行仿真标签页
|
||||
if (title === '运行仿真') {
|
||||
const id = 'run-simulation';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理仿真监控标签页
|
||||
if (title === '仿真监控') {
|
||||
const id = 'simulation-monitor';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理模型监控标签页
|
||||
if (title === '模型监控') {
|
||||
const id = 'model-monitor';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理数据监控标签页
|
||||
if (title === '数据监控') {
|
||||
const id = 'data-monitor';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理数据采集标签页
|
||||
if (title === '数据采集') {
|
||||
const id = 'data-collection';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理个人中心标签页
|
||||
if (title === '个人中心') {
|
||||
const id = 'profile';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理用户管理标签页
|
||||
if (title === '用户管理') {
|
||||
const id = 'users';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 默认情况下使用标题转换为ID
|
||||
const id = title.toLowerCase().replace(/\s+/g, '-');
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
}
|
||||
|
||||
// 根据标题和父工具获取图标名称的辅助函数
|
||||
function getIconNameForTitle(title, parentTool) {
|
||||
const iconMap = {
|
||||
'概览': 'dashboard',
|
||||
'更新记录': 'clock',
|
||||
'运行日志': 'file',
|
||||
'资源监控': 'server',
|
||||
'帮助': 'help',
|
||||
'Q&A': 'question',
|
||||
'运行环境配置': 'chip',
|
||||
'模型配置': 'cube',
|
||||
'服务配置': 'settings',
|
||||
'接口配置': 'plug',
|
||||
'运行测试': 'flask',
|
||||
'运行仿真': 'rocket',
|
||||
'仿真监控': 'desktop',
|
||||
'模型监控': 'cubes',
|
||||
'数据监控': 'chart-bar',
|
||||
'数据采集': 'database',
|
||||
'个人中心': 'user',
|
||||
'用户管理': 'users',
|
||||
'模型开发': 'cube',
|
||||
'服务开发': 'settings'
|
||||
};
|
||||
|
||||
return iconMap[title] || parentTool || 'con';
|
||||
}
|
||||
|
||||
// 监听登录事件
|
||||
authComponent.addEventListener('login', async (e) => {
|
||||
const data = e.detail;
|
||||
try {
|
||||
const response = await fetch('/api/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
localStorage.setItem('userInfo', JSON.stringify(result.user));
|
||||
showToast(`欢迎回来,${result.user.username}!`);
|
||||
authContainer.classList.remove('visible');
|
||||
mainContainer.classList.add('visible');
|
||||
// 初始化主页面
|
||||
initializeMainPage();
|
||||
} else {
|
||||
showToast(result.message || '登录失败,请检查用户名和密码');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('登录错误:', error);
|
||||
showToast('登录过程中发生错误');
|
||||
}
|
||||
});
|
||||
|
||||
// 监听注册事件
|
||||
authComponent.addEventListener('register', async (e) => {
|
||||
const data = e.detail;
|
||||
try {
|
||||
const response = await fetch('/api/register', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
showToast('注册成功!请登录');
|
||||
// 切换到登录表单
|
||||
const loginToggle = authComponent.shadowRoot.getElementById('loginToggle');
|
||||
loginToggle.click();
|
||||
} else {
|
||||
showToast(result.message || '注册失败,请稍后重试');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('注册错误:', error);
|
||||
showToast('注册过程中发生错误');
|
||||
}
|
||||
});
|
||||
|
||||
// 初始检查认证状态
|
||||
checkAuth();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,204 +0,0 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 获取DOM元素
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
const registerForm = document.getElementById('registerForm');
|
||||
const loginToggle = document.getElementById('loginToggle');
|
||||
const registerToggle = document.getElementById('registerToggle');
|
||||
|
||||
// 处理密码可见性切换
|
||||
document.querySelectorAll('.toggle-password').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const input = this.previousElementSibling;
|
||||
const icon = this.querySelector('.visibility-icon');
|
||||
|
||||
// 切换密码可见性
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
icon.src = 'assets/icons/png/visiable_b.png';
|
||||
} else {
|
||||
input.type = 'password';
|
||||
icon.src = 'assets/icons/png/invisiable_b.png';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 创建Toast提示函数
|
||||
function showToast(message) {
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'toast';
|
||||
toast.textContent = message;
|
||||
document.body.appendChild(toast);
|
||||
|
||||
setTimeout(() => toast.classList.add('show'), 10);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show');
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// 添加Toast样式
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.toast {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
z-index: 10000;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.toast.show {
|
||||
opacity: 1;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// 处理表单切换
|
||||
if (loginToggle && registerToggle) {
|
||||
loginToggle.addEventListener('click', () => {
|
||||
loginToggle.classList.add('active');
|
||||
registerToggle.classList.remove('active');
|
||||
loginForm.classList.add('active');
|
||||
registerForm.classList.remove('active');
|
||||
});
|
||||
|
||||
registerToggle.addEventListener('click', () => {
|
||||
registerToggle.classList.add('active');
|
||||
loginToggle.classList.remove('active');
|
||||
registerForm.classList.add('active');
|
||||
loginForm.classList.remove('active');
|
||||
});
|
||||
}
|
||||
|
||||
// 处理登录表单提交
|
||||
if (loginForm) {
|
||||
loginForm.querySelector('form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const username = document.getElementById('loginUsername').value;
|
||||
const password = document.getElementById('loginPassword').value;
|
||||
const remember = document.getElementById('remember').checked;
|
||||
|
||||
if (!username || !password) {
|
||||
showToast('请输入用户名和密码');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
password,
|
||||
remember
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
localStorage.setItem('userInfo', JSON.stringify(data.user));
|
||||
showToast(`欢迎回来,${data.user.username}!`);
|
||||
setTimeout(() => {
|
||||
window.location.href = 'main.html';
|
||||
}, 1000);
|
||||
} else {
|
||||
showToast(data.message || '登录失败,请检查用户名和密码');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('登录请求错误:', error);
|
||||
showToast('登录请求失败,请稍后再试');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理注册表单提交
|
||||
if (registerForm) {
|
||||
registerForm.querySelector('form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const username = document.getElementById('regUsername').value;
|
||||
const password = document.getElementById('regPassword').value;
|
||||
const confirmPassword = document.getElementById('regConfirmPassword').value;
|
||||
|
||||
if (!username || !password) {
|
||||
showToast('用户名和密码为必填项');
|
||||
return;
|
||||
}
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
showToast('两次输入的密码不一致');
|
||||
return;
|
||||
}
|
||||
|
||||
const userInfo = {
|
||||
full_name: document.getElementById('fullName').value.trim() || '',
|
||||
phone: document.getElementById('phone').value.trim() || '',
|
||||
email: document.getElementById('email').value.trim() || '',
|
||||
department: document.getElementById('department').value.trim() || '',
|
||||
position: document.getElementById('position').value.trim() || '',
|
||||
access_level: 1
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/register', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
password,
|
||||
userInfo
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
showToast('注册成功!请登录');
|
||||
loginToggle.click();
|
||||
e.target.reset();
|
||||
} else {
|
||||
let errorMessage;
|
||||
switch (data.userId) {
|
||||
case -1:
|
||||
errorMessage = '注册失败:系统错误';
|
||||
break;
|
||||
case -2:
|
||||
errorMessage = '注册失败:用户名已存在';
|
||||
break;
|
||||
case -3:
|
||||
errorMessage = '注册失败:无效的用户信息格式';
|
||||
break;
|
||||
case -4:
|
||||
errorMessage = '注册失败:用户名不能为空';
|
||||
break;
|
||||
case -5:
|
||||
errorMessage = '注册失败:密码不能为空';
|
||||
break;
|
||||
case -6:
|
||||
errorMessage = '注册失败:无效的权限级别';
|
||||
break;
|
||||
default:
|
||||
errorMessage = data.message || '注册失败,请稍后重试';
|
||||
}
|
||||
showToast(errorMessage);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('注册请求失败:', error);
|
||||
showToast('注册请求失败,请稍后重试');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
@ -37,6 +37,13 @@ if (!xnCorePath) {
|
||||
// 中间件
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
// 根路径直接返回main.html
|
||||
app.get('/', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'main.html'));
|
||||
});
|
||||
|
||||
// 静态文件服务 - 放在根路径处理之后
|
||||
app.use(express.static(__dirname));
|
||||
|
||||
// 监听进程退出事件
|
||||
@ -80,11 +87,6 @@ app.use('/api/qa', qaRoutes);
|
||||
app.use('/api/todos', todoRoutes);
|
||||
app.use('/api', userRoutes);
|
||||
|
||||
// 主页路由
|
||||
app.get('/', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'main.html'));
|
||||
});
|
||||
|
||||
// 接口配置页面路由
|
||||
app.get('/interface-config', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'interface-config.html'));
|
||||
|
@ -1,776 +0,0 @@
|
||||
* {
|
||||
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, #f5f7fa 0%, #c3cfe2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 90%;
|
||||
max-width: 1200px;
|
||||
min-height: 600px;
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.welcome-section {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.welcome-section h1 {
|
||||
font-size: 3.5rem;
|
||||
margin-bottom: 2rem;
|
||||
font-weight: 700;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.8;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.auth-container {
|
||||
width: 500px;
|
||||
background: white;
|
||||
padding: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-toggle {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
flex: 1;
|
||||
padding: 15px;
|
||||
border: none;
|
||||
background: #f0f0f0;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.toggle-btn.active {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.form-panel {
|
||||
display: none;
|
||||
animation: fadeIn 0.3s ease;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.form-panel.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.form-panel h2 {
|
||||
font-size: 1.8rem;
|
||||
color: #333;
|
||||
margin-bottom: 25px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-row .form-group {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #555;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
border: 2px solid #eee;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
border-color: #667eea;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.remember-me {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.remember-me input {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
min-height: auto;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.auth-container {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.auth-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.welcome-section h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.main-container {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: none;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
padding: 2rem;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.content-section h2 {
|
||||
color: #333;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.admin-features,
|
||||
.user-features {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.feature-btn {
|
||||
padding: 1rem;
|
||||
background: linear-gradient(135deg, #6e8efb, #a777e3);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.feature-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.main-nav {
|
||||
padding: 1rem;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.admin-features,
|
||||
.user-features {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 内容包装器 */
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
height: 100vh; /* 修改为全屏高度 */
|
||||
}
|
||||
|
||||
/* 主工具栏样式 */
|
||||
.main-toolbar {
|
||||
width: 80px;
|
||||
background-color: #2c3e50;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px 0;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.main-toolbar.collapsed {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.main-toolbar.collapsed .tool-item span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main-toolbar.collapsed .logo-image {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.tools-container {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toolbar-toggle {
|
||||
position: absolute;
|
||||
right: -12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-color: #34495e;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.toolbar-toggle i {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.sub-toolbar.collapsed .toolbar-toggle i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.toolbar-toggle:hover {
|
||||
background-color: #2c3e50;
|
||||
}
|
||||
|
||||
/* 调整子工具栏的过渡效果 */
|
||||
.sub-toolbar {
|
||||
width: 200px;
|
||||
background-color: #34495e;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.sub-toolbar.collapsed {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.sub-toolbar-content {
|
||||
min-width: 200px;
|
||||
padding: 20px 0;
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.sub-toolbar.collapsed .sub-toolbar-content {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.tool-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 15px 0;
|
||||
color: #ecf0f1;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tool-item i {
|
||||
font-size: 24px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.tool-item span {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tool-item:hover {
|
||||
background-color: #34495e;
|
||||
}
|
||||
|
||||
.tool-item.active {
|
||||
background-color: #3498db;
|
||||
}
|
||||
|
||||
/* 子工具栏样式 */
|
||||
.sub-menu {
|
||||
display: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sub-menu.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sub-item {
|
||||
padding: 12px 20px;
|
||||
color: #ecf0f1;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.sub-item:hover {
|
||||
background-color: #2c3e50;
|
||||
}
|
||||
|
||||
.sub-item.active {
|
||||
background-color: #3498db;
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#contentArea {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.content-wrapper {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main-toolbar {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tool-item {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.sub-toolbar {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.form-row .form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.auth-container {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo-image {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 20px;
|
||||
background-color: white;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.breadcrumb i {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 用户下拉菜单 */
|
||||
.user-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.user-dropdown-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.user-dropdown-toggle:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.user-dropdown-toggle i {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.user-dropdown.active .user-dropdown-toggle i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.user-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
margin-top: 8px;
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
min-width: 160px;
|
||||
display: none;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.user-dropdown.active .user-dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
padding: 10px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.dropdown-item i {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.dropdown-divider {
|
||||
height: 1px;
|
||||
background-color: #e0e0e0;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.admin-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 主内容区域调整 */
|
||||
#contentArea {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 标签页容器 */
|
||||
.tabs-container {
|
||||
background-color: white;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.tabs-header {
|
||||
display: flex;
|
||||
padding: 0 20px;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #ccc transparent;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.tabs-header::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.tabs-header::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.tabs-header::-webkit-scrollbar-thumb {
|
||||
background-color: #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 16px;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-bottom: none;
|
||||
border-radius: 4px 4px 0 0;
|
||||
margin-right: 4px;
|
||||
margin-top: 4px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
color: #666;
|
||||
min-width: 120px;
|
||||
max-width: 200px;
|
||||
height: 36px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.tab i {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tab span {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tab .close-tab {
|
||||
visibility: hidden;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.tab:not([data-tab="overview"]):hover .close-tab {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.tab .close-tab:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
background-color: white;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-bottom: 1px solid white;
|
||||
margin-bottom: -1px;
|
||||
color: #2c3e50;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 -2px 4px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.tab.active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
right: -1px;
|
||||
height: 2px;
|
||||
background: #3498db;
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
|
||||
.tab.active i {
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.tab:hover:not(.active) {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
/* 标签页内容区域 */
|
||||
.tabs-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
background-color: white;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tab-pane {
|
||||
display: none;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.tab-pane.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 调整主内容区域的布局 */
|
||||
.main-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.password-input {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.password-input input {
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.toggle-password {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.toggle-password:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.toggle-password:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.visibility-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
object-fit: contain;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user