From c1bef2f87fda4f7a089ff7ad9b054e2eb6eb59a5 Mon Sep 17 00:00:00 2001 From: dqzboy Date: Wed, 2 Apr 2025 06:51:11 +0800 Subject: [PATCH] fix: fix the nav menu --- hubcmdui/users.json | 2 +- hubcmdui/web/index.html | 33 ++++- hubcmdui/web/js/nav-menu.js | 154 +++++++++++++++++++ hubcmdui/web/style.css | 286 +++++++++++++++++++++++++++++++----- 4 files changed, 429 insertions(+), 46 deletions(-) create mode 100644 hubcmdui/web/js/nav-menu.js diff --git a/hubcmdui/users.json b/hubcmdui/users.json index 5f217b1..f082238 100644 --- a/hubcmdui/users.json +++ b/hubcmdui/users.json @@ -4,7 +4,7 @@ "username": "root", "password": "$2b$10$lh1kqJtq3shL2BhMD1LbVOThGeAlPXsDgME/he4ZyDMRupVtj0Hl.", "loginCount": 1, - "lastLogin": "2025-04-01T22:16:21.808Z" + "lastLogin": "2025-04-01T22:45:10.184Z" } ] } \ No newline at end of file diff --git a/hubcmdui/web/index.html b/hubcmdui/web/index.html index 688b493..e468e09 100644 --- a/hubcmdui/web/index.html +++ b/hubcmdui/web/index.html @@ -7,6 +7,7 @@ +
@@ -168,12 +169,6 @@ // 设置当前年份 document.getElementById('currentYear').textContent = new Date().getFullYear(); document.addEventListener('DOMContentLoaded', (event) => { - const menuToggle = document.getElementById('menuToggle'); - const navMenu = document.getElementById('navMenu'); - menuToggle.addEventListener('click', () => { - navMenu.classList.toggle('active'); - }); - // 版权保护 protectCopyright(); }); @@ -343,6 +338,29 @@ let currentTagPage = 1; let currentImageData = null; + // 初始化时加载代理域名配置 + async function initProxyDomain() { + try { + const response = await fetch('/api/config'); + if (response.ok) { + const config = await response.json(); + if (config.proxyDomain) { + proxyDomain = config.proxyDomain; + console.log('成功加载代理域名:', proxyDomain); + } else { + console.warn('配置中没有proxyDomain字段'); + proxyDomain = 'registry-1.docker.io'; // 使用默认值 + } + } else { + console.error('加载配置失败:', response.status, response.statusText); + proxyDomain = 'registry-1.docker.io'; // 使用默认值 + } + } catch (error) { + console.error('初始化代理域名失败:', error); + proxyDomain = 'registry-1.docker.io'; // 使用默认值 + } + } + // ======================================== // === 新增:全局提示函数 === // ======================================== @@ -1648,6 +1666,9 @@ // DOMContentLoaded 事件监听器 document.addEventListener('DOMContentLoaded', function() { + // 初始化代理域名 + initProxyDomain(); + // 确保元素存在再添加事件监听器 const searchInput = document.getElementById('searchInput'); if (searchInput) { diff --git a/hubcmdui/web/js/nav-menu.js b/hubcmdui/web/js/nav-menu.js new file mode 100644 index 0000000..33372b9 --- /dev/null +++ b/hubcmdui/web/js/nav-menu.js @@ -0,0 +1,154 @@ +/** + * 导航菜单加载模块 + * 负责从config加载并渲染前端导航菜单 + */ + +// 用于跟踪菜单是否已加载 +let menuLoaded = false; + +// 立即执行初始化函数 +(function() { + console.log('[菜单模块] 初始化开始'); + try { + // 页面加载完成后执行,但不在这里调用加载函数 + document.addEventListener('DOMContentLoaded', function() { + console.log('[菜单模块] DOM内容加载完成,等待loadMenu或loadNavMenu调用'); + // 不在这里调用,避免重复加载 + }); + console.log('[菜单模块] 初始化完成,等待调用'); + } catch (error) { + console.error('[菜单模块] 初始化失败:', error); + } +})(); + +// 加载导航菜单 +async function loadNavMenu() { + if (menuLoaded) { + console.log('[菜单模块] 菜单已加载,跳过'); + return; + } + + console.log('[菜单模块] loadNavMenu() 函数被调用'); + const navMenu = document.getElementById('navMenu'); + if (!navMenu) { + console.error('[菜单模块] 无法找到id为navMenu的元素,菜单加载失败'); + return; + } + + try { + console.log('[菜单模块] 正在从/api/config获取菜单配置...'); + + // 从API获取配置 + const response = await fetch('/api/config'); + console.log('[菜单模块] API响应状态:', response.status, response.statusText); + + if (!response.ok) { + throw new Error(`获取配置失败: ${response.status} ${response.statusText}`); + } + + const config = await response.json(); + console.log('[菜单模块] 成功获取配置:', config); + + // 确保menuItems存在且是数组 + if (!config.menuItems || !Array.isArray(config.menuItems) || config.menuItems.length === 0) { + console.warn('[菜单模块] 配置中没有菜单项或格式不正确', config); + navMenu.innerHTML = '未设置菜单'; + menuLoaded = true; + return; + } + + // 渲染菜单 + renderNavMenu(navMenu, config.menuItems); + menuLoaded = true; + + } catch (error) { + console.error('[菜单模块] 加载导航菜单失败:', error); + navMenu.innerHTML = `菜单加载失败: ${error.message}`; + } +} + +// 渲染导航菜单 +function renderNavMenu(navMenuElement, menuItems) { + try { + console.log('[菜单模块] 开始渲染导航菜单,菜单项数量:', menuItems.length); + + // 清空现有内容 + navMenuElement.innerHTML = ''; + + // 移动设备菜单切换按钮 + const menuToggle = document.createElement('div'); + menuToggle.id = 'menuToggle'; + menuToggle.className = 'menu-toggle'; + menuToggle.innerHTML = ''; + menuToggle.style.color = '#333'; // 设置为深色 + menuToggle.style.fontSize = '28px'; // 增大菜单图标 + navMenuElement.appendChild(menuToggle); + + // 菜单项容器 + const menuList = document.createElement('ul'); + menuList.className = 'nav-list'; + menuList.style.display = 'flex'; + menuList.style.listStyle = 'none'; + menuList.style.margin = '0'; + menuList.style.padding = '0'; + + // 添加菜单项 + menuItems.forEach((item, index) => { + console.log(`[菜单模块] 渲染菜单项 #${index+1}:`, item); + const menuItem = document.createElement('li'); + menuItem.style.marginLeft = '25px'; // 增加间距 + + const link = document.createElement('a'); + link.href = item.link || '#'; + link.textContent = item.text || '未命名菜单'; + + // 使用内联样式确保文字颜色可见,并增大字体 + link.style.color = '#333'; // 黑色文字 + link.style.textDecoration = 'none'; + link.style.fontSize = '16px'; // 从14px增大到16px + link.style.fontWeight = 'bold'; + link.style.padding = '8px 15px'; // 增大内边距 + link.style.borderRadius = '4px'; + link.style.transition = 'background-color 0.3s, color 0.3s'; + + // 添加鼠标悬停效果 + link.addEventListener('mouseover', function() { + this.style.backgroundColor = '#3d7cf4'; // 蓝色背景 + this.style.color = '#fff'; // 白色文字 + }); + + // 鼠标移出时恢复原样 + link.addEventListener('mouseout', function() { + this.style.backgroundColor = 'transparent'; + this.style.color = '#333'; + }); + + if (item.newTab) { + link.target = '_blank'; + link.rel = 'noopener noreferrer'; + } + + menuItem.appendChild(link); + menuList.appendChild(menuItem); + }); + + navMenuElement.appendChild(menuList); + + // 绑定移动端菜单切换事件 + menuToggle.addEventListener('click', () => { + console.log('[菜单模块] 菜单切换按钮被点击'); + navMenuElement.classList.toggle('active'); + }); + + console.log(`[菜单模块] 成功渲染了 ${menuItems.length} 个导航菜单项`); + } catch (error) { + console.error('[菜单模块] 渲染导航菜单失败:', error); + navMenuElement.innerHTML = `菜单渲染失败: ${error.message}`; + } +} + +// 添加loadMenu函数,作为loadNavMenu的别名,确保与index.html中的调用匹配 +function loadMenu() { + console.log('[菜单模块] 调用loadMenu() - 转发到loadNavMenu()'); + loadNavMenu(); +} \ No newline at end of file diff --git a/hubcmdui/web/style.css b/hubcmdui/web/style.css index 87535ca..bc0a6f7 100644 --- a/hubcmdui/web/style.css +++ b/hubcmdui/web/style.css @@ -65,55 +65,90 @@ /* 头部导航 */ .header { - background-color: var(--container-bg); - box-shadow: var(--shadow-md); - position: sticky; - top: 0; - z-index: 1000; - padding: 0; - transition: all var(--transition-normal); + background-color: #ffffff; /* 白色背景 */ + padding: 20px 0; /* 增加上下padding */ + position: relative; + border-bottom: 1px solid #e0e0e0; /* 添加边框分隔 */ } .header-content { - max-width: 1320px; - margin: 0 auto; - padding: 0.75rem 1.5rem; display: flex; - align-items: center; justify-content: space-between; + align-items: center; + max-width: 1200px; + margin: 0 auto; + padding: 0 30px; /* 增加左右padding */ } .logo { - height: 42px; + height: 60px; /* 将原来的40px增大到60px */ width: auto; - transition: transform var(--transition-fast); + transition: transform 0.3s ease; } .logo:hover { - transform: scale(1.05); + transform: scale(1.05); /* 添加悬停效果 */ } + /* 导航菜单修复 */ .nav-menu { display: flex; - gap: 1.2rem; align-items: center; } - .nav-menu a { - color: var(--text-secondary); - text-decoration: none; - padding: 0.5rem 1rem; - border-radius: var(--radius-md); - transition: all var(--transition-fast); - font-weight: 500; - font-size: 0.95rem; - letter-spacing: 0.2px; + .menu-toggle { + display: none; + font-size: 24px; + color: #333333; /* 深色图标 */ + cursor: pointer; } - .nav-menu a:hover { - background-color: var(--primary-light); - color: white; - transform: translateY(-2px); + .nav-list { + display: flex; + list-style: none; + margin: 0; + padding: 0; + } + + .nav-list li { + margin-left: 20px; + } + + /* 注意:菜单项的具体样式由JS中的内联样式控制 */ + + /* 移动设备适配 */ + @media (max-width: 768px) { + .menu-toggle { + display: block; + } + + .nav-list { + position: absolute; + top: 100%; + right: 20px; + background-color: #ffffff; /* 白色背景 */ + border-radius: 5px; + padding: 20px; + flex-direction: column; + display: none; + z-index: 100; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); + border: 1px solid #e0e0e0; + } + + .nav-menu.active .nav-list { + display: flex; + } + + .nav-list li { + margin: 10px 0; + } + } + + .no-menu, .menu-error { + color: #ff6b6b; + font-size: 14px; + margin-right: 20px; } /* 主容器 */ @@ -2581,11 +2616,11 @@ /* 为 名称 列设置最大宽度和文本溢出处理 */ #dockerStatusTable th:nth-child(2), #dockerStatusTable td:nth-child(2) { - max-width: 200px; /* 根据需要调整 */ - width: 25%; /* 尝试百分比宽度 */ + width: 25%; /* 将宽度从原来的过大值减小到25% */ + max-width: 250px; /* 限制最大宽度 */ + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - white-space: nowrap; } /* 为 镜像 列设置最大宽度和文本溢出处理 */ @@ -2694,9 +2729,9 @@ /* --- 新增:详情弹窗表格样式 --- */ .details-swal-popup .details-table-container { - max-height: 60vh; /* 限制最大高度,出现滚动条 */ + max-height: 70vh; overflow-y: auto; - margin-top: 1rem; + text-align: left; /* 确保内容左对齐 */ } .details-swal-popup .details-table { @@ -2718,11 +2753,13 @@ } .details-swal-popup .details-table tbody td { - padding: 0.8rem 1rem; - border-bottom: 1px solid var(--border-light, #e5e7eb); - vertical-align: middle; - color: var(--text-primary); - text-align: left; /* 确保单元格左对齐 */ + padding: 8px 12px; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + word-break: break-word; + font-family: monospace; + white-space: pre-wrap; + text-align: left; /* 确保文本左对齐 */ + font-size: 14px; } .details-swal-popup .details-table tbody tr:last-child td { @@ -2774,4 +2811,175 @@ } /* --- 结束:资源详情类 Excel 表格样式 --- */ -/* ... 其他样式 ... */ \ No newline at end of file +/* ... 其他样式 ... */ + +/* 导航菜单样式 */ +.nav-menu { + display: flex; + align-items: center; +} + +.nav-menu .nav-list { + display: flex; + list-style: none; + margin: 0; + padding: 0; +} + +.nav-menu .nav-list li { + margin-left: 20px; +} + +.nav-menu .nav-list li a { + color: #fff; + text-decoration: none; + font-size: 14px; + transition: color 0.3s; +} + +.nav-menu .nav-list li a:hover { + color: #3d7cf4; +} + +.menu-toggle { + display: none; + cursor: pointer; + font-size: 1.5rem; + color: #fff; +} + +/* 移动设备适配 */ +@media (max-width: 768px) { + .nav-menu { + position: relative; + } + + .menu-toggle { + display: block; + } + + .nav-menu .nav-list { + position: absolute; + top: 100%; + right: 0; + flex-direction: column; + background-color: #111; + padding: 20px; + border-radius: 5px; + display: none; + box-shadow: 0 5px 15px rgba(0,0,0,0.5); + z-index: 1000; + } + + .nav-menu.active .nav-list { + display: flex; + } + + .nav-menu .nav-list li { + margin: 10px 0; + } +} + +.no-menu, .menu-error { + color: #ff6b6b; + font-size: 14px; + margin-right: 20px; +} + +/* 操作按钮布局优化 */ +.action-cell { + white-space: nowrap; + text-align: center; + display: flex; + justify-content: center; + gap: 5px; /* 使用gap来控制按钮间隔 */ +} + +.action-cell button, +.action-cell .btn { + margin: 0; + padding: 5px 8px; + font-size: 13px; + display: inline-block; + min-width: auto; /* 防止按钮过宽 */ + flex-shrink: 0; /* 防止按钮被压缩 */ +} + +/* 确保操作按钮保持在一行 */ +#dockerStatusTable th:last-child, +#dockerStatusTable td:last-child { + width: auto; + min-width: 180px; /* 确保有足够的空间放置按钮 */ + white-space: nowrap; +} + +/* 日志弹窗内容优化 */ +.log-content { + text-align: left; + font-family: 'Courier New', monospace; + white-space: pre-wrap; + overflow-x: auto; + background-color: #f5f5f5; + padding: 15px; + border-radius: 5px; + max-height: 60vh; + overflow-y: auto; +} + +/* 确保SweetAlert2弹窗内容左对齐 */ +.swal2-html-container { + text-align: left !important; +} + +/* 确保弹窗样式优先级最高 */ +.swal2-popup .swal2-html-container, +.swal2-popup .swal2-content { + text-align: left !important; +} + +.swal2-popup pre, +.swal2-popup code, +.swal2-popup .log-content { + text-align: left !important; + direction: ltr !important; + font-family: 'Courier New', monospace !important; + font-size: 14px !important; + line-height: 1.5 !important; +} + +/* 调整容器表格布局 */ +#dockerStatusTable { + table-layout: fixed; + width: 100%; +} + +/* 容器ID/名称列宽度 */ +#dockerStatusTable th:nth-child(1), +#dockerStatusTable td:nth-child(1) { + width: 20%; +} + +/* 镜像名称列宽度 */ +#dockerStatusTable th:nth-child(2), +#dockerStatusTable td:nth-child(2) { + width: 25%; + max-width: 250px; +} + +/* 容器状态列宽度 */ +#dockerStatusTable th:nth-child(3), +#dockerStatusTable td:nth-child(3) { + width: 15%; +} + +/* 创建时间列宽度 */ +#dockerStatusTable th:nth-child(4), +#dockerStatusTable td:nth-child(4) { + width: 20%; +} + +/* 操作列宽度 */ +#dockerStatusTable th:nth-child(5), +#dockerStatusTable td:nth-child(5) { + width: 20%; +} \ No newline at end of file