diff --git a/common/common.js b/common/common.js index a7cd4e0..0ee410f 100644 --- a/common/common.js +++ b/common/common.js @@ -127,50 +127,76 @@ $(function () { }; function initHeader() { let showIndex = 0; + let hoverTimer = null; + let leaveTimer = null; + $(".nav-menu .nav-item").hover( function () { const index = $(".nav-menu .nav-item").index($(this)); - $(".nav-cont .nav-cont-menu") - .eq(index) - .attr("style", "display: block;"); - - // $('.nav-cont').attr('style','display: block;') - if ( - !$(".nav-cont .nav-cont-menu").eq(index).hasClass("nav-cont-empty") - ) { - const height = $(".nav-cont .nav-cont-menu").eq(index).height(); - $(".nav-cont").attr("style", `height: ${height}px;`); + + // 清除离开定时器 + if (leaveTimer) { + clearTimeout(leaveTimer); + leaveTimer = null; } - showIndex = index; + + // 添加150ms延迟,防止误触 + hoverTimer = setTimeout(() => { + $(".nav-cont .nav-cont-menu") + .eq(index) + .css("display", "block"); + + if ( + !$(".nav-cont .nav-cont-menu").eq(index).hasClass("nav-cont-empty") + ) { + const height = $(".nav-cont .nav-cont-menu").eq(index).outerHeight(true); + $(".nav-cont").css("height", height + "px"); + } + showIndex = index; + }, 150); }, function () { - const index = $(".nav-menu .nav-item").index($(this)); - $(".nav-cont ").eq(index).attr("style", "display: none;"); - $(".nav-cont .nav-cont-menu").eq(index).attr("style", "display: none;"); - $(".nav-cont").attr("style", "height:0"); + // 清除悬停定时器 + if (hoverTimer) { + clearTimeout(hoverTimer); + hoverTimer = null; + } + + leaveTimer = setTimeout(() => { + const index = $(".nav-menu .nav-item").index($(this)); + $(".nav-cont .nav-cont-menu").eq(index).css("display", "none"); + $(".nav-cont").css("height", "0"); + }, 100); } ); $(".nav-cont").hover( function () { - //$('.nav-cont ').attr('style','display: block;') + // 清除离开定时器 + if (leaveTimer) { + clearTimeout(leaveTimer); + leaveTimer = null; + } + $(".nav-cont .nav-cont-menu") .eq(showIndex) - .attr("style", "display: block;"); - //if (showIndex != 0) { - if (!$(this).hasClass("nav-cont-empty")) { - const height = $(".nav-cont .nav-cont-menu").eq(showIndex).height(); - $(".nav-cont").attr("style", `height: ${height}px;`); + .css("display", "block"); + + if (!$(".nav-cont .nav-cont-menu").eq(showIndex).hasClass("nav-cont-empty")) { + const height = $(".nav-cont .nav-cont-menu").eq(showIndex).outerHeight(true); + $(".nav-cont").css("height", height + "px"); } }, function () { - //$('.nav-cont ').attr('style','display: none;') - $(".nav-cont .nav-cont-menu") - .eq(showIndex) - .attr("style", "display: none;"); - $(".nav-cont").attr("style", "height:0"); + leaveTimer = setTimeout(() => { + $(".nav-cont .nav-cont-menu") + .eq(showIndex) + .css("display", "none"); + $(".nav-cont").css("height", "0"); + }, 100); } ); + if (localStorage.jwt) { if (sessionStorage.accountInfo) { const obj = JSON.parse(sessionStorage.accountInfo); diff --git a/css/nav-mega-menu.css b/css/nav-mega-menu.css new file mode 100644 index 0000000..b2c333d --- /dev/null +++ b/css/nav-mega-menu.css @@ -0,0 +1,222 @@ +/* 三级导航菜单样式 - Mega Menu风格 */ + +/* 导航容器 */ +.nav-cont { + position: absolute; + top: 100%; + left: 0; + width: 100%; + background: #fff; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); + z-index: 1000; + overflow: hidden; + transition: height 0.3s ease-out; +} + +/* Mega Menu 内容区 */ +.nav-cont-menu.mega-menu { + display: none; + padding: 24px 0; +} + +.nav-cont-menu.mega-menu .nav-content { + display: flex; + flex-wrap: wrap; + gap: 32px; + max-width: 1400px; + margin: 0 auto; + padding: 0 20px; +} + +/* 二级菜单项(带三级菜单的分类) */ +.nav-category { + flex: 0 0 auto; + min-width: 200px; + max-width: 280px; +} + +.nav-category-header { + display: flex; + align-items: center; + gap: 12px; + padding: 8px 12px; + margin-bottom: 12px; + border-radius: 6px; + background: linear-gradient(135deg, #f0f7ff 0%, #e6f3ff 100%); + transition: all 0.2s ease; +} + +.nav-category-header:hover { + background: linear-gradient(135deg, #e6f3ff 0%, #d9edff 100%); + transform: translateX(2px); +} + +.nav-category-icon { + flex-shrink: 0; + width: 32px; + height: 32px; + border-radius: 6px; + overflow: hidden; +} + +.nav-category-icon img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.nav-category-info { + flex: 1; + min-width: 0; +} + +.nav-category-title { + font-size: 14px; + font-weight: 600; + color: #1890ff; + margin-bottom: 2px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.nav-category-desc { + font-size: 12px; + color: #8c8c8c; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* 三级菜单列表 */ +.nav-third-level { + padding-left: 12px; +} + +.nav-third-item { + margin-bottom: 8px; +} + +.nav-third-link { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + border-radius: 4px; + color: #333; + font-size: 13px; + text-decoration: none; + transition: all 0.2s ease; + position: relative; +} + +.nav-third-link::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 3px; + height: 0; + background: #1890ff; + border-radius: 2px; + transition: height 0.2s ease; +} + +.nav-third-link:hover { + background: #f5f5f5; + color: #1890ff; + padding-left: 16px; +} + +.nav-third-link:hover::before { + height: 16px; +} + +.nav-third-name { + flex: 1; + font-weight: 500; +} + +.nav-third-desc { + font-size: 11px; + color: #999; + margin-left: auto; +} + +/* 没有三级菜单的二级菜单项(旧版兼容) */ +.nav-item-box { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 12px 16px; + border-radius: 6px; + transition: all 0.2s ease; + text-decoration: none; + color: #333; +} + +.nav-item-box:hover { + background: #f5f5f5; + transform: translateY(-2px); +} + +.nav-item-box img { + width: 40px; + height: 40px; + flex-shrink: 0; + border-radius: 4px; +} + +.item-box-title .title { + font-size: 14px; + font-weight: 600; + color: #333; + margin-bottom: 4px; +} + +.item-box-title .desc { + font-size: 12px; + color: #8c8c8c; +} + +/* 空菜单占位 */ +.nav-cont-menu.nav-cont-empty { + display: none; +} + +/* 响应式调整 */ +@media (max-width: 768px) { + .nav-cont { + display: none !important; + } + + .nav-cont-menu.mega-menu { + padding: 16px 0; + } + + .nav-cont-menu.mega-menu .nav-content { + flex-direction: column; + gap: 16px; + } + + .nav-category { + max-width: 100%; + } +} + +/* 加载动画 */ +@keyframes fadeInDown { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.nav-cont-menu.animated { + animation: fadeInDown 0.3s ease-out; +} diff --git a/header.html b/header.html index 5c74ad0..24d8dfe 100644 --- a/header.html +++ b/header.html @@ -2,112 +2,113 @@ - {php} - // 在渲染阶段从主题配置插件读取配置,并覆盖 $data 与 SEO 变量,实现 SSR - if (class_exists('\\addon\\theme_configurator\\model\\ThemeConfigModel')) { - $cfgModel = new \addon\theme_configurator\model\ThemeConfigModel(); - $themeCfg = $cfgModel->getConfig(); + {php} + // 在渲染阶段从主题配置插件读取配置,并覆盖 $data 与 SEO 变量,实现 SSR + if (class_exists('\\addon\\theme_configurator\\model\\ThemeConfigModel')) { + $cfgModel = new \addon\theme_configurator\model\ThemeConfigModel(); + $themeCfg = $cfgModel->getConfig(); - if (is_array($themeCfg)) { - $site = isset($themeCfg['site_config']) && is_array($themeCfg['site_config']) - ? $themeCfg['site_config'] - : []; + if (is_array($themeCfg)) { + $site = isset($themeCfg['site_config']) && is_array($themeCfg['site_config']) + ? $themeCfg['site_config'] + : []; - if (!isset($data) || !is_array($data)) { - $data = []; - } + if (!isset($data) || !is_array($data)) { + $data = []; + } - // 覆盖 / 补充首页相关结构数据 - if (isset($themeCfg['banner'])) { - $data['banner'] = $themeCfg['banner']; - } - if (isset($themeCfg['honor'])) { - $data['honor'] = $themeCfg['honor']; - } - if (isset($themeCfg['friendly_link'])) { - $data['friendly_link'] = $themeCfg['friendly_link']; - } - if (isset($themeCfg['header_nav'])) { - $data['header_nav'] = $themeCfg['header_nav']; - } - if (isset($themeCfg['footer_nav'])) { - $data['footer_nav'] = $themeCfg['footer_nav']; - } - if (isset($themeCfg['side'])) { - $data['side_floating_window'] = $themeCfg['side']; - } + // 覆盖 / 补充首页相关结构数据 + if (isset($themeCfg['banner'])) { + $data['banner'] = $themeCfg['banner']; + } + if (isset($themeCfg['honor'])) { + $data['honor'] = $themeCfg['honor']; + } + if (isset($themeCfg['friendly_link'])) { + $data['friendly_link'] = $themeCfg['friendly_link']; + } + if (isset($themeCfg['header_nav'])) { + $data['header_nav'] = $themeCfg['header_nav']; + } + if (isset($themeCfg['footer_nav'])) { + $data['footer_nav'] = $themeCfg['footer_nav']; + } + if (isset($themeCfg['side'])) { + $data['side_floating_window'] = $themeCfg['side']; + } - // 站点基础信息,供 header/footer 与其他模板直接使用 - $data['enterprise_name'] = $site['enterprise_name'] ?? ($data['enterprise_name'] ?? ''); - $data['enterprise_telephone'] = $site['enterprise_telephone'] ?? ($data['enterprise_telephone'] ?? ''); - $data['enterprise_mailbox'] = $site['enterprise_mailbox'] ?? ($data['enterprise_mailbox'] ?? ''); - $data['enterprise_qrcode'] = $site['enterprise_qrcode'] ?? ($data['enterprise_qrcode'] ?? ''); - $data['official_website_logo']= $site['official_website_logo']?? ($data['official_website_logo']?? ''); - $data['online_customer_service_link'] = - $site['online_customer_service_link'] ?? ($data['online_customer_service_link'] ?? ''); - $data['icp_info'] = $site['icp_info'] ?? ($data['icp_info'] ?? ''); - $data['icp_info_link'] = $site['icp_info_link'] ?? ($data['icp_info_link'] ?? ''); - $data['public_security_network_preparation'] = - $site['public_security_network_preparation'] ?? - ($data['public_security_network_preparation'] ?? ''); - $data['public_security_network_preparation_link'] = - $site['public_security_network_preparation_link'] ?? - ($data['public_security_network_preparation_link'] ?? ''); - $data['telecom_appreciation'] = $site['telecom_appreciation'] ?? ($data['telecom_appreciation'] ?? ''); - $data['copyright_info'] = $site['copyright_info'] ?? ($data['copyright_info'] ?? ''); - $data['terms_service_url'] = $site['terms_service_url'] ?? ($data['terms_service_url'] ?? ''); - $data['terms_privacy_url'] = $site['terms_privacy_url'] ?? ($data['terms_privacy_url'] ?? ''); - $data['cloud_product_link'] = $site['cloud_product_link'] ?? ($data['cloud_product_link'] ?? ''); - $data['dcim_product_link'] = $site['dcim_product_link'] ?? ($data['dcim_product_link'] ?? ''); + // 站点基础信息,供 header/footer 与其他模板直接使用 + $data['enterprise_name'] = $site['enterprise_name'] ?? ($data['enterprise_name'] ?? ''); + $data['enterprise_telephone'] = $site['enterprise_telephone'] ?? ($data['enterprise_telephone'] ?? ''); + $data['enterprise_mailbox'] = $site['enterprise_mailbox'] ?? ($data['enterprise_mailbox'] ?? ''); + $data['enterprise_qrcode'] = $site['enterprise_qrcode'] ?? ($data['enterprise_qrcode'] ?? ''); + $data['official_website_logo']= $site['official_website_logo']?? ($data['official_website_logo']?? ''); + $data['online_customer_service_link'] = + $site['online_customer_service_link'] ?? ($data['online_customer_service_link'] ?? ''); + $data['icp_info'] = $site['icp_info'] ?? ($data['icp_info'] ?? ''); + $data['icp_info_link'] = $site['icp_info_link'] ?? ($data['icp_info_link'] ?? ''); + $data['public_security_network_preparation'] = + $site['public_security_network_preparation'] ?? + ($data['public_security_network_preparation'] ?? ''); + $data['public_security_network_preparation_link'] = + $site['public_security_network_preparation_link'] ?? + ($data['public_security_network_preparation_link'] ?? ''); + $data['telecom_appreciation'] = $site['telecom_appreciation'] ?? ($data['telecom_appreciation'] ?? ''); + $data['copyright_info'] = $site['copyright_info'] ?? ($data['copyright_info'] ?? ''); + $data['terms_service_url'] = $site['terms_service_url'] ?? ($data['terms_service_url'] ?? ''); + $data['terms_privacy_url'] = $site['terms_privacy_url'] ?? ($data['terms_privacy_url'] ?? ''); + $data['cloud_product_link'] = $site['cloud_product_link'] ?? ($data['cloud_product_link'] ?? ''); + $data['dcim_product_link'] = $site['dcim_product_link'] ?? ($data['dcim_product_link'] ?? ''); - // SEO:如插件配置了 SEO,则覆盖控制器传入的标题/关键词/描述 - if (!empty($themeCfg['seo']['title'])) { - $title = $themeCfg['seo']['title']; - } - if (!empty($themeCfg['seo']['keywords'])) { - $keywords = $themeCfg['seo']['keywords']; - } - if (!empty($themeCfg['seo']['description'])) { - $description = $themeCfg['seo']['description']; - } - } - } - {/php} + // SEO:如插件配置了 SEO,则覆盖控制器传入的标题/关键词/描述 + if (!empty($themeCfg['seo']['title'])) { + $title = $themeCfg['seo']['title']; + } + if (!empty($themeCfg['seo']['keywords'])) { + $keywords = $themeCfg['seo']['keywords']; + } + if (!empty($themeCfg['seo']['description'])) { + $description = $themeCfg['seo']['description']; + } + } + } + {/php} - - {$title} - - + + {$title} + + - - - + + + - - - - - - - - - - - + + + + + + + + + + + + - - - - - - + + + + + + - - + + - - - - \ No newline at end of file + \ No newline at end of file diff --git a/plugins/addon/theme_configurator/model/ThemeConfigModel.php b/plugins/addon/theme_configurator/model/ThemeConfigModel.php index 675db31..e7ba3fc 100644 --- a/plugins/addon/theme_configurator/model/ThemeConfigModel.php +++ b/plugins/addon/theme_configurator/model/ThemeConfigModel.php @@ -130,8 +130,70 @@ class ThemeConfigModel 'keywords' => '云服务器,主题云,BlackFruit', 'description' => 'BlackFruit-UI 默认站点说明', ], - // 顶部/底部导航 - 'header_nav' => [], + // 顶部/底部导航(第一个默认为产品中心,支持三级菜单) + 'header_nav' => [ + [ + 'name' => '产品中心', + 'file_address' => '', + 'blank' => false, + 'children' => [ + [ + 'name' => 'SAS轻量云服务器', + 'file_address' => '/cloud.html', + 'icon' => '/web/BlackFruit-web/assets/img/index/cloud-icon.png', + 'description' => '高性能SSD云服务器', + 'blank' => false, + 'children' => [ + [ + 'name' => '香港SAS', + 'file_address' => '/cloud.html?region=hk', + 'description' => '香港数据中心', + 'blank' => false + ], + [ + 'name' => '美国SAS', + 'file_address' => '/cloud.html?region=us', + 'description' => '美国数据中心', + 'blank' => false + ], + [ + 'name' => '日本SAS', + 'file_address' => '/cloud.html?region=jp', + 'description' => '日本数据中心', + 'blank' => false + ] + ] + ], + [ + 'name' => 'ECS精品云服务器', + 'file_address' => '/dedicated.html', + 'icon' => '/web/BlackFruit-web/assets/img/index/server-icon.png', + 'description' => '企业级云服务器', + 'blank' => false, + 'children' => [ + [ + 'name' => '宁波ECS', + 'file_address' => '/dedicated.html?region=nb', + 'description' => '宁波数据中心', + 'blank' => false + ], + [ + 'name' => '镇江ECS', + 'file_address' => '/dedicated.html?region=zj', + 'description' => '镇江数据中心', + 'blank' => false + ], + [ + 'name' => '北京ECS', + 'file_address' => '/dedicated.html?region=bj', + 'description' => '北京数据中心', + 'blank' => false + ] + ] + ] + ] + ] + ], 'footer_nav' => [], // 站点基础信息 'site_config' => [ diff --git a/plugins/addon/theme_configurator/template/admin/index.html b/plugins/addon/theme_configurator/template/admin/index.html index 3cc7f2b..ad442cc 100644 --- a/plugins/addon/theme_configurator/template/admin/index.html +++ b/plugins/addon/theme_configurator/template/admin/index.html @@ -718,25 +718,109 @@ container.innerHTML = ''; children.forEach((child, childIndex) => { + const hasGrandChildren = Array.isArray(child.children) && child.children.length > 0; const item = document.createElement('div'); - item.style.cssText = 'padding:8px; margin-bottom:8px; background:#f9f9f9; border-radius:4px;'; + item.style.cssText = 'padding:12px; margin-bottom:12px; background:#f9f9f9; border-radius:4px; border-left:3px solid #1890ff;'; item.innerHTML = ` -
-
- - - - - +
+ 二级菜单 ${childIndex + 1} +
+ +
- +
+
+ + + + + +
+
+
三级菜单
+
+ +
+ `; + container.appendChild(item); + + // 渲染三级菜单 + if (hasGrandChildren) { + renderThirdLevel(navIndex, childIndex, child.children); + } + }); + } + + // 渲染三级菜单 + function renderThirdLevel(navIndex, childIndex, grandChildren) { + const container = document.getElementById(`third-level-list-${navIndex}-${childIndex}`); + if (!container) return; + container.innerHTML = ''; + + grandChildren.forEach((grandChild, grandIndex) => { + const item = document.createElement('div'); + item.style.cssText = 'padding:8px; margin-bottom:6px; background:#fafafa; border-radius:3px; border-left:2px solid #52c41a;'; + item.innerHTML = ` +
+
+ + + + +
+
`; container.appendChild(item); }); } + // 切换三级菜单显示 + window.toggleThirdLevel = function (navIndex, childIndex) { + const container = document.getElementById(`third-level-${navIndex}-${childIndex}`); + const btn = event.target; + if (container) { + const isHidden = container.style.display === 'none'; + container.style.display = isHidden ? 'block' : 'none'; + btn.textContent = isHidden ? '收起三级' : '展开三级'; + } + }; + + // 添加三级菜单项 + window.addThirdLevelItem = function (navIndex, childIndex) { + const navs = collectHeaderNav(); + if (!navs[navIndex]) return; + if (!navs[navIndex].children[childIndex]) return; + + if (!navs[navIndex].children[childIndex].children) { + navs[navIndex].children[childIndex].children = []; + } + + navs[navIndex].children[childIndex].children.push({ + name: '', + file_address: '', + description: '', + blank: false + }); + + renderHeaderNav(navs); + // 确保展开 + document.getElementById(`third-level-${navIndex}-${childIndex}`).style.display = 'block'; + }; + + // 删除三级菜单项 + window.removeThirdLevelItem = function (navIndex, childIndex, grandIndex) { + const navs = collectHeaderNav(); + if (navs[navIndex] && navs[navIndex].children[childIndex] && navs[navIndex].children[childIndex].children) { + navs[navIndex].children[childIndex].children.splice(grandIndex, 1); + renderHeaderNav(navs); + } + }; + function collectHeaderNav() { + const navs = []; // 收集主导航 @@ -754,7 +838,7 @@ if (!navs[navIndex]) navs[navIndex] = { children: [] }; if (!navs[navIndex].children[childIndex]) { - navs[navIndex].children[childIndex] = {}; + navs[navIndex].children[childIndex] = { children: [] }; } if (input.type === 'checkbox') { @@ -764,6 +848,45 @@ } }); + // 收集三级菜单 + document.querySelectorAll('[data-hnav-grandchild]').forEach(input => { + const [navIndex, childIndex, grandIndex] = input.dataset.hnavGrandchild.split('.').map(Number); + const field = input.dataset.field; + + if (!navs[navIndex]) navs[navIndex] = { children: [] }; + if (!navs[navIndex].children[childIndex]) { + navs[navIndex].children[childIndex] = { children: [] }; + } + if (!navs[navIndex].children[childIndex].children) { + navs[navIndex].children[childIndex].children = []; + } + if (!navs[navIndex].children[childIndex].children[grandIndex]) { + navs[navIndex].children[childIndex].children[grandIndex] = {}; + } + + if (input.type === 'checkbox') { + navs[navIndex].children[childIndex].children[grandIndex][field] = input.checked; + } else { + navs[navIndex].children[childIndex].children[grandIndex][field] = input.value; + } + }); + + // 清理空的children数组 + navs.forEach(nav => { + if (nav && nav.children) { + nav.children = nav.children.filter(child => child); + nav.children.forEach(child => { + if (child && child.children) { + child.children = child.children.filter(gc => gc); + // 如果三级菜单为空,删除children属性 + if (child.children.length === 0) { + delete child.children; + } + } + }); + } + }); + return navs.filter(n => n); } diff --git a/public/header.html b/public/header.html index 89285bf..ca8e579 100644 --- a/public/header.html +++ b/public/header.html @@ -64,21 +64,59 @@ {if isset($data.header_nav) && !empty($data.header_nav)} {foreach $data.header_nav as $k=>$item} {if isset($item.children) && !empty($item.children)} -