移动端添加汉堡菜单和侧边导航栏,优化交互体验
All checks were successful
continuous-integration/drone/push Build is passing

前端改动:
- 添加移动端汉堡菜单按钮(右上角)
- 创建侧边导航栏(从右侧滑入)
  - 包含主导航菜单项
  - 包含文档和控制台链接
  - 包含登录/注册功能
  - 已登录状态显示用户信息和操作菜单
- 移动端隐藏桌面导航链接(文档、控制台、登录、注册)
- 侧边栏支持点击遮罩层、关闭按钮、导航链接关闭
- 实现桌面端和移动端登录状态同步
- 使用 MutationObserver 监听登录状态变化

样式改动:
- 汉堡菜单三条杠动画效果(点击变叉号)
- 侧边栏深色科技风格,渐变背景
- 侧边栏从右侧滑入动画,带遮罩层
- 导航项 hover 效果和滑动动画
- 用户头像和信息卡片样式
- 响应式适配(767px、575px 断点)

交互优化:
- banner-list 快速入口卡片改为一行两个(grid 布局)
- 调整幻灯片进度条位置(767px: bottom 40px,575px: bottom 60px)
- 避免进度条和按钮重合

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
yiqiu
2025-12-12 20:10:33 +08:00
parent 79cdc194d3
commit 460068b768
4 changed files with 469 additions and 7 deletions

View File

@@ -1032,6 +1032,209 @@
} }
} }
/* 汉堡菜单按钮 - 默认隐藏 */
.mobile-menu-toggle {
display: none;
flex-direction: column;
justify-content: space-between;
width: 28px;
height: 22px;
cursor: pointer;
margin-left: 16px;
z-index: 1001;
position: relative;
}
.mobile-menu-toggle span {
display: block;
width: 100%;
height: 3px;
background: linear-gradient(135deg, #38BDF8 0%, #6366F1 100%);
border-radius: 2px;
transition: all 0.3s ease;
}
.mobile-menu-toggle.active span:nth-child(1) {
transform: translateY(9.5px) rotate(45deg);
}
.mobile-menu-toggle.active span:nth-child(2) {
opacity: 0;
}
.mobile-menu-toggle.active span:nth-child(3) {
transform: translateY(-9.5px) rotate(-45deg);
}
/* 移动端侧边导航栏 */
.mobile-sidebar {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
pointer-events: none;
}
.mobile-sidebar.active {
pointer-events: auto;
}
.mobile-sidebar-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
opacity: 0;
transition: opacity 0.3s ease;
}
.mobile-sidebar.active .mobile-sidebar-overlay {
opacity: 1;
}
.mobile-sidebar-content {
position: absolute;
top: 0;
right: 0;
width: 280px;
height: 100%;
background: linear-gradient(180deg, #0F172A 0%, #020617 100%);
box-shadow: -4px 0 24px rgba(0, 0, 0, 0.3);
transform: translateX(100%);
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
overflow-y: auto;
display: flex;
flex-direction: column;
}
.mobile-sidebar.active .mobile-sidebar-content {
transform: translateX(0);
}
.mobile-sidebar-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
border-bottom: 1px solid rgba(148, 163, 184, 0.15);
}
.mobile-sidebar-logo img {
height: 28px;
width: auto;
}
.mobile-sidebar-close {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-radius: 6px;
transition: all 0.3s ease;
}
.mobile-sidebar-close span {
font-size: 24px;
color: #94A3B8;
transition: color 0.3s ease;
}
.mobile-sidebar-close:hover {
background: rgba(56, 189, 248, 0.1);
}
.mobile-sidebar-close:hover span {
color: #38BDF8;
}
.mobile-sidebar-body {
flex: 1;
padding: 20px;
overflow-y: auto;
}
.mobile-nav-item {
margin-bottom: 8px;
}
.mobile-nav-link {
display: block;
padding: 14px 16px;
color: #E5E7EB;
font-size: 15px;
font-weight: 500;
border-radius: 8px;
transition: all 0.3s ease;
cursor: pointer;
text-decoration: none;
}
.mobile-nav-link:hover {
background: rgba(56, 189, 248, 0.1);
color: #38BDF8;
transform: translateX(4px);
}
.mobile-nav-divider {
height: 1px;
background: rgba(148, 163, 184, 0.15);
margin: 16px 0;
}
.mobile-nav-btn {
display: inline-block;
padding: 12px 24px;
background: linear-gradient(135deg, #38BDF8 0%, #6366F1 100%);
color: #ffffff;
font-size: 14px;
font-weight: 600;
border-radius: 8px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
margin: 8px 16px;
}
.mobile-nav-btn:hover {
box-shadow: 0 4px 16px rgba(56, 189, 248, 0.4);
transform: translateY(-2px);
}
.mobile-user-info {
display: flex;
align-items: center;
padding: 16px;
background: rgba(15, 23, 42, 0.8);
border-radius: 8px;
margin-bottom: 16px;
}
.mobile-user-avatar {
width: 48px;
height: 48px;
border-radius: 50%;
background: linear-gradient(135deg, #38BDF8 0%, #6366F1 100%);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 18px;
margin-right: 12px;
}
.mobile-user-name {
color: #F9FAFB;
font-size: 16px;
font-weight: 600;
}
/* 移动端导航栏和页脚优化 */ /* 移动端导航栏和页脚优化 */
@media screen and (max-width: 991px) { @media screen and (max-width: 991px) {
/* 导航栏 */ /* 导航栏 */
@@ -1096,6 +1299,21 @@
margin-right: 10px; margin-right: 10px;
} }
/* 移动端显示汉堡菜单 */
.mobile-menu-toggle {
display: flex;
}
/* 移动端显示侧边栏 */
.mobile-sidebar {
display: block;
}
/* 移动端隐藏桌面导航链接 */
.nav-desktop-link {
display: none !important;
}
.nav-menu .nav-item { .nav-menu .nav-item {
padding: 0 8px; padding: 0 8px;
font-size: 13px; font-size: 13px;

View File

@@ -2543,6 +2543,15 @@ html {
font-size: 12px !important; font-size: 12px !important;
} }
/* 调整进度条位置,避免和按钮重合 */
.banner-cont .swiper-pagination {
bottom: 40px !important;
}
.banner-cont .swiper-pagination-bullet {
width: 45px !important;
}
/* ===== Banner 快速入口 - 紧凑布局 ===== */ /* ===== Banner 快速入口 - 紧凑布局 ===== */
.banner .banner-s { .banner .banner-s {
margin-top: -40px; margin-top: -40px;
@@ -2551,11 +2560,14 @@ html {
.banner-s .banner-list { .banner-s .banner-list {
gap: 8px; gap: 8px;
display: grid;
grid-template-columns: repeat(2, 1fr); /* 一行两个 */
} }
.banner-s .banner-list .banner-item { .banner-s .banner-list .banner-item {
padding: 12px 16px !important; padding: 12px 16px !important;
gap: 10px; gap: 10px;
flex: unset; /* 移除 flex 属性,改用 grid */
} }
.banner-s .banner-list .banner-item .banner-item-icon { .banner-s .banner-list .banner-item .banner-item-icon {

View File

@@ -1,4 +1,161 @@
$(function () { $(function () {
// 移动端侧边导航栏控制
const mobileMenuToggle = document.getElementById('mobileMenuToggle');
const mobileSidebar = document.getElementById('mobileSidebar');
const mobileSidebarOverlay = document.getElementById('mobileSidebarOverlay');
const mobileSidebarClose = document.getElementById('mobileSidebarClose');
// 打开侧边栏
if (mobileMenuToggle) {
mobileMenuToggle.addEventListener('click', function() {
mobileSidebar.classList.add('active');
mobileMenuToggle.classList.add('active');
document.body.style.overflow = 'hidden'; // 禁止页面滚动
});
}
// 关闭侧边栏
function closeMobileSidebar() {
if (mobileSidebar) {
mobileSidebar.classList.remove('active');
}
if (mobileMenuToggle) {
mobileMenuToggle.classList.remove('active');
}
document.body.style.overflow = ''; // 恢复页面滚动
}
// 点击遮罩层关闭
if (mobileSidebarOverlay) {
mobileSidebarOverlay.addEventListener('click', closeMobileSidebar);
}
// 点击关闭按钮关闭
if (mobileSidebarClose) {
mobileSidebarClose.addEventListener('click', closeMobileSidebar);
}
// 点击侧边栏内的链接后关闭(可选)
document.querySelectorAll('.mobile-nav-link').forEach(function(link) {
link.addEventListener('click', function(e) {
// 如果是 a 标签且不是 javascript:; 则关闭侧边栏
if (link.tagName === 'A' && link.getAttribute('href') !== 'javascript:;') {
closeMobileSidebar();
}
});
});
// 同步桌面端和移动端的登录状态
function syncLoginStatus() {
const desktopNoLogin = document.querySelector('.nav-desktop-link.no-login');
const desktopLoginIn = document.querySelector('.nav-desktop-link.login-in');
const mobileNoLogin = document.querySelector('.mobile-no-login');
const mobileLoginIn = document.querySelector('.mobile-login-in');
// 检查桌面端登录状态
if (desktopNoLogin && desktopNoLogin.style.display !== 'none') {
// 未登录
if (mobileNoLogin) mobileNoLogin.style.display = 'block';
if (mobileLoginIn) mobileLoginIn.style.display = 'none';
} else if (desktopLoginIn && desktopLoginIn.style.display !== 'none') {
// 已登录
if (mobileNoLogin) mobileNoLogin.style.display = 'none';
if (mobileLoginIn) mobileLoginIn.style.display = 'block';
// 同步用户信息
const desktopUsername = document.getElementById('username');
const desktopHeadImg = document.getElementById('headImg');
const mobileUsername = document.getElementById('mobileUsername');
const mobileHeadImg = document.getElementById('mobileHeadImg');
if (desktopUsername && mobileUsername) {
mobileUsername.textContent = desktopUsername.textContent;
}
if (desktopHeadImg && mobileHeadImg) {
mobileHeadImg.textContent = desktopHeadImg.textContent;
}
}
}
// 移动端登录按钮事件绑定
const mobileLoginBtn = document.getElementById('mobileLoginBtn');
const desktopLoginBtn = document.getElementById('loginBtn');
if (mobileLoginBtn && desktopLoginBtn) {
mobileLoginBtn.addEventListener('click', function() {
desktopLoginBtn.click();
closeMobileSidebar();
});
}
// 移动端注册按钮事件绑定
const mobileRegistBtn = document.getElementById('mobileRegistBtn');
const desktopRegistBtn = document.getElementById('registBtn');
if (mobileRegistBtn && desktopRegistBtn) {
mobileRegistBtn.addEventListener('click', function() {
desktopRegistBtn.click();
closeMobileSidebar();
});
}
// 移动端账户信息按钮事件绑定
const mobileAccountBtn = document.getElementById('mobileAccountBtn');
const desktopAccountBtn = document.getElementById('accountBtn');
if (mobileAccountBtn && desktopAccountBtn) {
mobileAccountBtn.addEventListener('click', function() {
desktopAccountBtn.click();
closeMobileSidebar();
});
}
// 移动端未付款订单按钮事件绑定
const mobileFinanceBtn = document.getElementById('mobileFinanceBtn');
const desktopFinanceBtn = document.getElementById('financeBtn');
if (mobileFinanceBtn && desktopFinanceBtn) {
mobileFinanceBtn.addEventListener('click', function() {
desktopFinanceBtn.click();
closeMobileSidebar();
});
}
// 移动端我的工单按钮事件绑定
const mobileTicketBtn = document.getElementById('mobileTicketBtn');
const desktopTicketBtn = document.getElementById('ticketBtn');
if (mobileTicketBtn && desktopTicketBtn) {
mobileTicketBtn.addEventListener('click', function() {
desktopTicketBtn.click();
closeMobileSidebar();
});
}
// 移动端退出按钮事件绑定
const mobileLogout = document.getElementById('mobileLogout');
const desktopLogout = document.getElementById('logout');
if (mobileLogout && desktopLogout) {
mobileLogout.addEventListener('click', function() {
desktopLogout.click();
closeMobileSidebar();
});
}
// 初始化时同步登录状态
setTimeout(syncLoginStatus, 100);
// 监听登录状态变化(使用 MutationObserver
const observeLoginStatus = function() {
const desktopNoLogin = document.querySelector('.nav-desktop-link.no-login');
const desktopLoginIn = document.querySelector('.nav-desktop-link.login-in');
if (desktopNoLogin) {
const observer = new MutationObserver(syncLoginStatus);
observer.observe(desktopNoLogin, { attributes: true, attributeFilter: ['style'] });
}
if (desktopLoginIn) {
const observer = new MutationObserver(syncLoginStatus);
observer.observe(desktopLoginIn, { attributes: true, attributeFilter: ['style'] });
}
};
observeLoginStatus();
// 产品详情页的图片预览轮播(仅在存在时初始化) // 产品详情页的图片预览轮播(仅在存在时初始化)
if ($('.gallery-thumbs').length > 0 && $('.gallery-top').length > 0) { if ($('.gallery-thumbs').length > 0 && $('.gallery-top').length > 0) {
var galleryThumbs = new Swiper('.gallery-thumbs', { var galleryThumbs = new Swiper('.gallery-thumbs', {

View File

@@ -29,16 +29,16 @@
</div> </div>
</div> </div>
<div class="nav-right"> <div class="nav-right">
<a href="/document.html" class="nav-text-link">文档</a> <a href="/document.html" class="nav-text-link nav-desktop-link">文档</a>
<span class="nav-divider">|</span> <span class="nav-divider nav-desktop-link">|</span>
<a href="/home.htm" class="nav-text-link">控制台</a> <a href="/home.htm" class="nav-text-link nav-desktop-link">控制台</a>
<span class="nav-divider">|</span> <span class="nav-divider nav-desktop-link">|</span>
<div class="no-login" style="display: none;"> <div class="no-login nav-desktop-link" style="display: none;">
<span class="nav-text-link" id="loginBtn">登录</span> <span class="nav-text-link" id="loginBtn">登录</span>
<span class="nav-divider">|</span> <span class="nav-divider">|</span>
<div class="btn btn-normal" id="registBtn"><span class="regist-text">立即注册</span></div> <div class="btn btn-normal" id="registBtn"><span class="regist-text">立即注册</span></div>
</div> </div>
<div class="login-in " style="display: none;"> <div class="login-in nav-desktop-link" style="display: none;">
<div id="headImg" class="head-img"></div> <div id="headImg" class="head-img"></div>
<span class="ml-10 font-el1 name" id="username"></span> <span class="ml-10 font-el1 name" id="username"></span>
<div class="login-menu animated fadeIn"> <div class="login-menu animated fadeIn">
@@ -51,6 +51,12 @@
<div class="login-menu-item" id="logout">退出账户</div> <div class="login-menu-item" id="logout">退出账户</div>
</div> </div>
</div> </div>
<!-- 移动端汉堡菜单按钮 -->
<div class="mobile-menu-toggle" id="mobileMenuToggle">
<span></span>
<span></span>
<span></span>
</div>
</div> </div>
</div> </div>
@@ -90,7 +96,76 @@
</div> </div>
<!-- 移动端侧边导航栏 -->
<div class="mobile-sidebar" id="mobileSidebar">
<div class="mobile-sidebar-overlay" id="mobileSidebarOverlay"></div>
<div class="mobile-sidebar-content">
<div class="mobile-sidebar-header">
<div class="mobile-sidebar-logo">
<img src="{$data.official_website_logo|default='/web/BlackFruit-web/assets/img/index/logo.png'}" alt="">
</div>
<div class="mobile-sidebar-close" id="mobileSidebarClose">
<span></span>
</div>
</div>
<div class="mobile-sidebar-body">
<!-- 主导航菜单 -->
{if isset($data.header_nav) && !empty($data.header_nav)}
{foreach $data.header_nav as $k=>$item}
{if $k > 0}
<div class="mobile-nav-item">
{if !empty($item.file_address)}
<a href="{$item.file_address}" {if !empty($item.blank)}target="_blank"{/if} class="mobile-nav-link">
{$item.name}
</a>
{else /}
<div class="mobile-nav-link">{$item.name}</div>
{/if}
</div>
{/if}
{/foreach}
{/if}
<!-- 分隔线 -->
<div class="mobile-nav-divider"></div>
<!-- 文档和控制台 -->
<div class="mobile-nav-item">
<a href="/document.html" class="mobile-nav-link">文档</a>
</div>
<div class="mobile-nav-item">
<a href="/home.htm" class="mobile-nav-link">控制台</a>
</div>
<!-- 登录注册(未登录状态) -->
<div class="mobile-nav-actions mobile-no-login" style="display: none;">
<div class="mobile-nav-item">
<span class="mobile-nav-link" id="mobileLoginBtn">登录</span>
</div>
<div class="mobile-nav-item">
<div class="mobile-nav-btn" id="mobileRegistBtn">立即注册</div>
</div>
</div>
<!-- 用户信息(已登录状态) -->
<div class="mobile-nav-user mobile-login-in" style="display: none;">
<div class="mobile-nav-divider"></div>
<div class="mobile-user-info">
<div class="mobile-user-avatar" id="mobileHeadImg"></div>
<div class="mobile-user-name" id="mobileUsername"></div>
</div>
<div class="mobile-nav-item">
<span class="mobile-nav-link" id="mobileAccountBtn">账户信息</span>
</div>
<div class="mobile-nav-item">
<span class="mobile-nav-link" id="mobileFinanceBtn">未付款订单</span>
</div>
<div class="mobile-nav-item">
<span class="mobile-nav-link" id="mobileTicketBtn">我的工单</span>
</div>
<div class="mobile-nav-item">
<span class="mobile-nav-link" id="mobileLogout">退出账户</span>
</div>
</div>
</div>
</div>
</div>
<!-- 侧边 --> <!-- 侧边 -->
<!-- 回到顶部按钮 - 独立在侧边工具栏上方 --> <!-- 回到顶部按钮 - 独立在侧边工具栏上方 -->