XNSim/XNSimHtml/main.html

733 lines
28 KiB
HTML
Raw Normal View History

2025-04-28 12:25:20 +08:00
<!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">
<script src="components/auth-component.js"></script>
2025-04-28 12:25:20 +08:00
<script src="components/main-toolbar.js"></script>
<script src="components/sub-toolbar.js"></script>
<script src="components/user-info.js"></script>
<script src="components/tabs-container.js"></script>
<script src="components/content-area.js"></script>
<script src="components/overview-page.js"></script>
<script src="components/update-history.js"></script>
<script src="components/run-log.js"></script>
<script src="components/system-info.js"></script>
<script src="components/help-component.js"></script>
2025-05-07 17:01:58 +08:00
<script src="components/system-log.js"></script>
2025-04-28 12:25:20 +08:00
<script src="components/run-env-config.js" type="module"></script>
<script src="components/model-config.js" type="module"></script>
<script src="components/service-config.js" type="module"></script>
<script src="components/interface-config.js" type="module"></script>
2025-04-28 12:25:20 +08:00
<script src="components/run-test.js"></script>
<script src="components/run-simulation.js"></script>
<script src="components/simulation-monitor.js"></script>
<script src="components/model-monitor.js"></script>
<script src="components/data-monitor.js"></script>
<script src="components/data-collection.js"></script>
<script src="components/profile-center.js"></script>
<script src="components/user-management.js"></script>
<script src="components/model-development.js"></script>
<script src="components/service-development.js"></script>
2025-05-08 17:01:57 +08:00
<script src="components/header-tools.js"></script>
<script src="components/qa-component.js"></script>
<script src="components/todo-component.js" type="module"></script>
2025-04-28 12:25:20 +08:00
<style>
.icon {
width: 24px;
height: 24px;
display: inline-block;
vertical-align: middle;
margin-bottom: 5px;
}
.icon-small {
width: 16px;
height: 16px;
}
/* 用户等级图标样式 */
.user-level-icon {
width: 20px;
height: 20px;
margin-right: 8px;
vertical-align: middle;
}
/* 主要内容区域样式 */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
background-color: #fff;
height: 100vh;
padding: 0;
margin: 0;
overflow: hidden;
}
/* 内容包装器样式 */
.content-wrapper {
display: flex;
height: 100vh;
background: #fff;
}
/* 主工具栏样式 */
.main-toolbar {
width: 80px;
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
display: flex;
flex-direction: column;
transition: width 0.3s ease;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
}
/* 子工具栏样式 */
.sub-toolbar {
width: 200px;
background: linear-gradient(180deg, #7986E7 0%, #8B6DB3 100%);
position: relative;
transition: width 0.3s ease;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
}
.sub-toolbar.collapsed {
width: 0;
}
.sub-toolbar-content {
width: 200px;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
padding: 10px 0;
}
.sub-toolbar.collapsed .sub-toolbar-content {
display: none;
}
/* 主容器样式 */
.main-container {
display: none;
2025-04-28 12:25:20 +08:00
width: 100%;
height: 100vh;
overflow: hidden;
opacity: 0;
transition: opacity 0.3s ease;
}
.main-container.visible {
display: block !important;
opacity: 1;
}
/* 认证容器样式 */
#authContainer {
display: none;
opacity: 0;
transition: opacity 0.3s ease;
}
#authContainer.visible {
display: block !important;
opacity: 1;
2025-04-28 12:25:20 +08:00
}
/* 内容头部样式 */
.content-header {
display: flex;
justify-content: space-between;
align-items: center;
height: 54px;
background: white;
border-bottom: 1px solid #e0e0e0;
padding: 0 16px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
flex-shrink: 0;
}
.breadcrumb {
display: flex;
align-items: center;
color: #000000;
font-size: 20px;
font-weight: 1000;
height: 100%;
gap: 8px;
}
.breadcrumb .icon-small {
margin-right: 0;
margin-left: 0;
width: 20px;
height: 20px;
}
/* 添加新的样式 */
.header-right {
display: flex;
align-items: center;
gap: 16px;
}
2025-04-28 12:25:20 +08:00
/* 重置body样式 */
body {
margin: 0;
padding: 0;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 15px;
}
/* 确保内容区域完全填充 */
.main-content > * {
width: 100%;
}
/* 内容区域容器 */
.content-area-container {
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
}
tabs-container {
flex-shrink: 0;
}
content-area {
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;
}
2025-04-28 12:25:20 +08:00
</style>
</head>
<body>
<div id="authContainer">
<auth-component></auth-component>
</div>
<div id="mainContainer" class="main-container">
2025-04-28 12:25:20 +08:00
<div class="content-wrapper">
<main-toolbar></main-toolbar>
<sub-toolbar></sub-toolbar>
<main class="main-content">
<div class="content-header">
<div class="breadcrumb">
<img src="assets/icons/png/con_b.png" alt="主页 / 概览" class="icon-small">
<span id="currentPath">主页 / 概览</span>
</div>
<div class="header-right">
<header-tools></header-tools>
<user-info></user-info>
</div>
2025-04-28 12:25:20 +08:00
</div>
<div class="content-area-container">
<tabs-container></tabs-container>
<content-area></content-area>
</div>
</main>
</div>
</div>
<script>
// 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');
2025-04-28 12:25:20 +08:00
const tabsContainer = document.querySelector('tabs-container');
const contentArea = document.querySelector('content-area');
// 检查是否已登录
const checkAuth = async () => {
try {
const response = await fetch('/api/check-auth', {
credentials: 'include'
});
const result = await response.json();
if (result.success) {
// 只存储非敏感的用户信息
localStorage.setItem('userInfo', JSON.stringify({
username: result.user.username,
role: result.user.role,
// 其他非敏感信息...
}));
// 先初始化主页面
initializeMainPage();
authContainer.classList.remove('visible');
mainContainer.classList.add('visible');
// 触发一个自定义事件,通知其他组件登录成功
const loginSuccessEvent = new CustomEvent('login-success', {
detail: { user: result.user }
});
document.dispatchEvent(loginSuccessEvent);
} else {
authContainer.classList.add('visible');
mainContainer.classList.remove('visible');
}
} catch (error) {
console.error('认证检查错误:', error);
authContainer.classList.add('visible');
mainContainer.classList.remove('visible');
2025-04-28 12:25:20 +08:00
}
};
2025-04-28 12:25:20 +08:00
// 初始化主页面
async function initializeMainPage() {
// 确保所有组件都已经加载
const components = [
'main-toolbar',
'sub-toolbar',
'tabs-container',
'content-area'
];
// 等待所有组件加载完成
await Promise.all(components.map(component => {
return new Promise(resolve => {
if (customElements.get(component)) {
resolve();
} else {
customElements.whenDefined(component).then(resolve);
}
});
}));
// 初始化时创建概览标签页并加载概览内容
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');
}
});
// 监听子工具栏选择事件
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);
});
}
2025-04-28 12:25:20 +08:00
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':
// 调用登出接口
fetch('/api/logout', {
method: 'POST',
credentials: 'include'
}).then(() => {
// 清除本地存储的用户信息
localStorage.removeItem('userInfo');
// 清除所有标签页
tabsContainer.clearAllTabs();
// 显示退出成功提示
showToast('已安全退出登录');
// 隐藏主容器
mainContainer.classList.remove('visible');
setTimeout(() => {
mainContainer.style.display = 'none';
authContainer.style.display = 'block';
requestAnimationFrame(() => {
authContainer.classList.add('visible');
const authComponent = document.querySelector('auth-component');
if (authComponent) {
authComponent.reset();
}
});
}, 300);
}).catch(error => {
console.error('登出错误:', error);
showToast('登出过程中发生错误');
});
break;
}
}
2025-04-28 12:25:20 +08:00
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;
2025-04-28 12:25:20 +08:00
}
// 特殊处理更新记录标签页
if (title === '更新记录') {
const id = 'update-history';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
2025-04-28 12:25:20 +08:00
}
// 处理运行日志标签页
if (title === '运行日志') {
const id = 'run-log';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 处理资源监控标签页
if (title === '资源监控') {
const id = 'system-info';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
contentArea.loadContent(id);
return;
}
2025-04-28 12:25:20 +08:00
// 处理系统日志标签页
if (title === '系统日志') {
const id = 'system-log';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
contentArea.loadContent(id);
return;
2025-04-28 12:25:20 +08:00
}
// 处理待办事项标签页
if (title === '待办事项') {
const id = 'todo';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
contentArea.loadContent(id);
return;
}
2025-04-28 12:25:20 +08:00
// 处理帮助标签页
if (title === '帮助') {
const id = 'help';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
contentArea.loadContent(id);
return;
}
2025-04-28 12:25:20 +08:00
// 处理Q&A标签页
if (title === 'Q&A') {
const id = 'qa';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
contentArea.loadContent(id);
return;
}
2025-05-07 17:01:58 +08:00
// 处理运行环境配置标签页
if (title === '运行环境配置') {
const id = 'run-env-config';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-05-08 17:01:57 +08:00
// 处理模型配置标签页
if (title === '模型配置') {
const id = 'model-config';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 处理服务配置标签页
if (title === '服务配置') {
const id = 'service-config';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-05-07 16:02:39 +08:00
// 处理接口配置标签页
if (title === '接口配置') {
const id = 'interface-config';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 处理模型开发标签页
if (title === '模型开发') {
const id = 'model-development';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 处理服务开发标签页
if (title === '服务开发') {
const id = 'service-development';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 处理运行测试标签页
if (title === '运行测试') {
const id = 'run-test';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 处理运行仿真标签页
if (title === '运行仿真') {
const id = 'run-simulation';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 处理仿真监控标签页
if (title === '仿真监控') {
const id = 'simulation-monitor';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 处理模型监控标签页
if (title === '模型监控') {
const id = 'model-monitor';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 处理数据监控标签页
if (title === '数据监控') {
const id = 'data-monitor';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 处理数据采集标签页
if (title === '数据采集') {
const id = 'data-collection';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 处理个人中心标签页
if (title === '个人中心') {
const id = 'profile';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 处理用户管理标签页
if (title === '用户管理') {
const id = 'users';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
return;
}
2025-04-28 12:25:20 +08:00
// 默认情况下使用标题转换为ID
const id = title.toLowerCase().replace(/\s+/g, '-');
2025-04-28 12:25:20 +08:00
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';
2025-04-28 12:25:20 +08:00
}
// 监听登录事件
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),
credentials: 'include' // 允许跨域请求携带cookie
});
const result = await response.json();
if (result.success) {
showToast(`欢迎回来,${result.user.username}`);
// 先显示主容器
mainContainer.style.display = 'block';
// 等待一帧以确保display:block生效
await new Promise(resolve => requestAnimationFrame(resolve));
// 添加visible类触发过渡效果
mainContainer.classList.add('visible');
// 等待过渡效果完成
await new Promise(resolve => setTimeout(resolve, 300));
// 隐藏认证容器
authContainer.classList.remove('visible');
// 等待过渡效果完成
await new Promise(resolve => setTimeout(resolve, 300));
authContainer.style.display = 'none';
// 初始化主页面
await initializeMainPage();
// 更新用户信息组件
const userInfo = document.querySelector('user-info');
if (userInfo) {
userInfo.updateUserInfo(result.user);
}
// 触发登录成功事件
const loginSuccessEvent = new CustomEvent('login-success', {
detail: { user: result.user }
});
document.dispatchEvent(loginSuccessEvent);
} else {
showToast(result.message || '登录失败,请检查用户名和密码');
}
} catch (error) {
console.error('登录错误:', error);
showToast('登录过程中发生错误');
}
});
2025-04-28 12:25:20 +08:00
// 监听注册事件
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('注册过程中发生错误');
}
});
2025-04-28 12:25:20 +08:00
// 初始检查认证状态
checkAuth();
});
2025-04-28 12:25:20 +08:00
</script>
</body>
</html>