fix: fix the nav menu

This commit is contained in:
dqzboy
2025-04-02 06:51:11 +08:00
parent 46f067b495
commit c1bef2f87f
4 changed files with 429 additions and 46 deletions

View File

@@ -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"
}
]
}

View File

@@ -7,6 +7,7 @@
<link rel="icon" href="https://cdn.jsdelivr.net/gh/dqzboy/Blog-Image/BlogCourse/docker-proxy.png" type="image/png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="style.css">
<script src="js/nav-menu.js"></script>
</head>
<body>
<header class="header">
@@ -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) {

154
hubcmdui/web/js/nav-menu.js Normal file
View File

@@ -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 = '<span class="no-menu" style="color: #ff6b6b; font-size: 14px;">未设置菜单</span>';
menuLoaded = true;
return;
}
// 渲染菜单
renderNavMenu(navMenu, config.menuItems);
menuLoaded = true;
} catch (error) {
console.error('[菜单模块] 加载导航菜单失败:', error);
navMenu.innerHTML = `<span class="menu-error" style="color: #ff6b6b; font-size: 14px;">菜单加载失败: ${error.message}</span>`;
}
}
// 渲染导航菜单
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 = '<i class="fas fa-bars"></i>';
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 = `<span class="menu-error" style="color: #ff6b6b; font-size: 14px;">菜单渲染失败: ${error.message}</span>`;
}
}
// 添加loadMenu函数作为loadNavMenu的别名确保与index.html中的调用匹配
function loadMenu() {
console.log('[菜单模块] 调用loadMenu() - 转发到loadNavMenu()');
loadNavMenu();
}

View File

@@ -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 表格样式 --- */
/* ... 其他样式 ... */
/* ... 其他样式 ... */
/* 导航菜单样式 */
.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%;
}