头部和左边栏实现响应式

This commit is contained in:
jingrow 2025-10-20 01:41:02 +08:00
parent 8038a618e0
commit 062d599f18
2 changed files with 174 additions and 6 deletions

View File

@ -5,13 +5,14 @@
quaternary
circle
@click="$emit('toggle-sidebar')"
class="sidebar-toggle"
>
<template #icon>
<Icon icon="tabler:menu" />
</template>
</n-button>
<n-breadcrumb>
<n-breadcrumb class="breadcrumb">
<n-breadcrumb-item>
<router-link to="/">{{ appName }}</router-link>
</n-breadcrumb-item>
@ -28,12 +29,12 @@
<div class="header-right">
<n-space>
<!-- 搜索框 -->
<!-- 搜索框 - 使用Naive UI的响应式属性 -->
<n-input
v-model:value="searchQuery"
:placeholder="t('Search...')"
clearable
style="width: 280px"
class="search-input"
@keyup.enter="handleSearch"
@clear="handleSearchClear"
>
@ -197,11 +198,13 @@ const handleSearchClear = () => {
display: flex;
align-items: center;
gap: 16px;
min-width: 0; /* 允许flex子项收缩 */
}
.header-right {
display: flex;
align-items: center;
min-width: 0; /* 允许flex子项收缩 */
}
.username {
@ -209,4 +212,75 @@ const handleSearchClear = () => {
font-weight: 500;
}
/* 搜索框响应式样式 */
.search-input {
width: 280px;
}
/* 面包屑响应式样式 */
.breadcrumb {
min-width: 0; /* 允许收缩 */
}
/* 平板端样式 (768px - 1024px) */
@media (max-width: 1024px) {
.app-header {
padding: 0 16px;
}
.search-input {
width: 200px;
}
.header-left {
gap: 12px;
}
}
/* 移动端样式 (最大768px) */
@media (max-width: 768px) {
.app-header {
padding: 0 12px;
}
.header-left {
gap: 8px;
}
/* 移动端隐藏面包屑 */
.breadcrumb {
display: none;
}
/* 移动端隐藏搜索框 */
.search-input {
display: none;
}
/* 移动端隐藏用户名文字 */
.username {
display: none;
}
/* 移动端调整按钮间距 */
.header-right :deep(.n-space) {
gap: 4px;
}
}
/* 小屏移动端样式 (最大480px) */
@media (max-width: 480px) {
.app-header {
padding: 0 8px;
}
.header-left {
gap: 4px;
}
/* 小屏移动端进一步压缩间距 */
.header-right :deep(.n-space) {
gap: 2px;
}
}
</style>

View File

@ -7,7 +7,10 @@
:collapsed-width="64"
:width="240"
v-model:collapsed="collapsed"
show-trigger
:show-trigger="!isMobile"
:responsive="true"
:breakpoint="768"
class="sidebar-layout"
>
<AppSidebar :collapsed="collapsed" />
</n-layout-sider>
@ -16,7 +19,7 @@
<n-layout>
<!-- 顶部导航 -->
<n-layout-header bordered>
<AppHeader @toggle-sidebar="collapsed = !collapsed" />
<AppHeader @toggle-sidebar="toggleSidebar" />
</n-layout-header>
<!-- 内容区域 -->
@ -28,11 +31,18 @@
</n-scrollbar>
</n-layout-content>
</n-layout>
<!-- 移动端遮罩层 -->
<div
v-if="isMobile && !collapsed"
class="mobile-overlay"
@click="closeMobileDrawer"
></div>
</n-layout>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { ref, watch, onMounted, onUnmounted } from 'vue'
import { NLayout, NLayoutSider, NLayoutHeader, NLayoutContent, NScrollbar } from 'naive-ui'
import AppSidebar from './AppSidebar.vue'
import AppHeader from './AppHeader.vue'
@ -40,6 +50,42 @@ import AppHeader from './AppHeader.vue'
const SIDEBAR_COLLAPSE_KEY = 'app.sidebar.collapsed'
const collapsed = ref(localStorage.getItem(SIDEBAR_COLLAPSE_KEY) === 'true')
//
const isMobile = ref(false)
//
const checkIsMobile = () => {
isMobile.value = window.innerWidth < 768
//
if (isMobile.value) {
collapsed.value = true
}
}
//
const toggleSidebar = () => {
collapsed.value = !collapsed.value
}
//
const closeMobileDrawer = () => {
collapsed.value = true
}
//
const handleResize = () => {
checkIsMobile()
}
onMounted(() => {
checkIsMobile()
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
})
watch(collapsed, (val) => {
localStorage.setItem(SIDEBAR_COLLAPSE_KEY, String(val))
})
@ -54,4 +100,52 @@ watch(collapsed, (val) => {
padding: 20px;
min-height: calc(100vh - 64px);
}
/* 移动端遮罩层 */
.mobile-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
}
/* 移动端侧边栏样式 */
@media (max-width: 767px) {
.sidebar-layout {
position: fixed !important;
top: 0;
left: 0;
height: 100vh;
z-index: 1000;
width: 280px !important;
max-width: 80vw;
transform: translateX(-100%);
transition: transform 0.3s ease;
}
/* 移动端侧边栏打开时的样式 */
.sidebar-layout:not(.n-layout-sider--collapsed) {
transform: translateX(0) !important;
}
/* 移动端时隐藏侧边栏触发器 */
.sidebar-layout :deep(.n-layout-sider-trigger) {
display: none;
}
}
/* 桌面端保持原有样式 */
@media (min-width: 768px) {
.mobile-overlay {
display: none;
}
.sidebar-layout {
position: relative !important;
transform: none !important;
}
}
</style>