diff --git a/backend/src/components/CommonAside.vue b/backend/src/components/CommonAside.vue index 4335495..d240aea 100644 --- a/backend/src/components/CommonAside.vue +++ b/backend/src/components/CommonAside.vue @@ -574,11 +574,9 @@ h3 { } // 响应式设计 +// 注意:手机端(≤768px)侧栏的抽屉式布局由 Main.vue 统一控制(fixed 定位 + transform 动画), +// 此处不要再对 .common-aside 设置 width,避免覆盖父级布局逻辑。 @media (max-width: 768px) { - .common-aside { - width: 100% !important; - } - :deep(.el-menu) { padding: 12px 4px; } diff --git a/backend/src/components/CommonHeader.vue b/backend/src/components/CommonHeader.vue index 8122f10..de7434c 100644 --- a/backend/src/components/CommonHeader.vue +++ b/backend/src/components/CommonHeader.vue @@ -484,6 +484,30 @@ onUnmounted(() => { } } +// 手机端(≤768px):隐藏左侧折叠按钮区(侧栏抽屉由 Main.vue 的 .mobile-menu-btn 控制) +@media (max-width: 768px) { + .l-content { + display: none; + } + + // 顶栏左侧为汉堡按钮预留空间,右侧收紧间距,避免空洞与拥挤 + .header { + padding: 0 12px 0 60px; + } + + .r-content { + gap: 10px; + + .el-dropdown-link { + gap: 8px; + + .user-name { + max-width: 72px; + } + } + } +} + .r-content { position: relative; // z-index: 1000; diff --git a/backend/src/stores/index.js b/backend/src/stores/index.js index 8076ca4..37396e0 100644 --- a/backend/src/stores/index.js +++ b/backend/src/stores/index.js @@ -5,6 +5,8 @@ import { ref, computed, reactive } from 'vue'; function initState() { return { isCollapse: false, + // 手机端抽屉式侧栏是否展开(仅 ≤768px 生效) + mobileAsideOpen: false, }; } @@ -15,11 +17,30 @@ export const useAllDataStore = defineStore('allData', () => { function increment() { count.value++; } + + // 是否为手机端(≤768px),通过 matchMedia 响应式监听 + const mobileQuery = window.matchMedia('(max-width: 768px)'); + const isMobile = ref(mobileQuery.matches); + const handleMobileQueryChange = (e) => { + isMobile.value = e.matches; + // 从手机端切回桌面端时,自动收起抽屉侧栏 + if (!e.matches) { + state.mobileAsideOpen = false; + } + }; + if (typeof mobileQuery.addEventListener === 'function') { + mobileQuery.addEventListener('change', handleMobileQueryChange); + } else { + // 兼容旧版 Safari + mobileQuery.addListener(handleMobileQueryChange); + } + return { state, count, doubleCount, increment, + isMobile, }; }); diff --git a/backend/src/views/Main.vue b/backend/src/views/Main.vue index 5d713ea..0d910c9 100644 --- a/backend/src/views/Main.vue +++ b/backend/src/views/Main.vue @@ -3,8 +3,8 @@ import CommonAside from '@/components/CommonAside.vue'; import CommonHeader from '@/components/CommonHeader.vue'; import { useTabsStore } from '@/stores'; import { useRouter, useRoute } from 'vue-router'; -import { ref, watch, reactive, nextTick, onMounted, computed } from 'vue'; -import { More, Close, CircleClose, ArrowUp } from '@element-plus/icons-vue'; +import { ref, watch, reactive, nextTick, onMounted, onBeforeUnmount, computed } from 'vue'; +import { More, Close, CircleClose, ArrowUp, Fold, Expand } from '@element-plus/icons-vue'; import { ElMessage } from 'element-plus'; const tabsStore = useTabsStore(); @@ -12,6 +12,44 @@ const router = useRouter(); const route = useRoute(); const defaultDashboardPath = '/home'; +// ========== 手机端侧栏抽屉控制 ========== +const MOBILE_QUERY = '(max-width: 768px)'; +const mobileMedia = window.matchMedia(MOBILE_QUERY); +const isMobile = ref(mobileMedia.matches); +// 手机端侧栏默认隐藏,桌面端无影响(CSS 仅在媒体查询内生效) +const sidebarVisible = ref(false); + +const handleMobileMediaChange = (e) => { + isMobile.value = e.matches; + // 切回桌面端时重置侧栏状态,避免残留 + if (!e.matches) { + sidebarVisible.value = false; + } +}; + +// 兼容旧版 Safari 的 addListener 写法 +if (typeof mobileMedia.addEventListener === 'function') { + mobileMedia.addEventListener('change', handleMobileMediaChange); +} else { + mobileMedia.addListener(handleMobileMediaChange); +} + +onBeforeUnmount(() => { + if (typeof mobileMedia.removeEventListener === 'function') { + mobileMedia.removeEventListener('change', handleMobileMediaChange); + } else { + mobileMedia.removeListener(handleMobileMediaChange); + } +}); + +const toggleMobileSidebar = () => { + sidebarVisible.value = !sidebarVisible.value; +}; + +const closeMobileSidebar = () => { + sidebarVisible.value = false; +}; + // 根据当前路由恢复 tab(刷新时使用) function restoreTabFromRoute() { const currentPath = route.fullPath; @@ -88,6 +126,10 @@ watch( (newPath) => { if (newPath) { restoreTabFromRoute(); + // 路由变化时自动关闭手机端侧栏 + if (isMobile.value) { + sidebarVisible.value = false; + } } }, { immediate: false } @@ -410,8 +452,33 @@ const canCloseRight = computed(() => { diff --git a/backend/src/views/dashboard/index.vue b/backend/src/views/dashboard/index.vue index 9e83654..fefc9e3 100644 --- a/backend/src/views/dashboard/index.vue +++ b/backend/src/views/dashboard/index.vue @@ -741,32 +741,134 @@ onMounted(async () => { } } +// 手机端适配:统计卡片一行 2 个、标题区截断、卡片高度自适应、按钮点击区域 ≥40px @media (max-width: 768px) { - .welcome-section { - padding: 24px 16px; + .dashboard { + overflow-x: hidden; + } - .welcome-title { - font-size: 24px; + // 欢迎区:缩小 padding 与字号,长文本截断 + .welcome-section { + padding: 16px; + margin-bottom: 12px; + border-radius: 8px; + + .welcome-content { + min-width: 0; + + .welcome-title { + margin: 0 0 6px; + font-size: 20px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .welcome-subtitle { + font-size: 13px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } } } + // 统计卡片一行 2 个,间距缩小 .stats-grid { grid-template-columns: repeat(2, 1fr); - gap: 12px; + gap: 10px; + margin-bottom: 12px; } .stat-card { - padding: 16px; + padding: 12px; + gap: 10px; + border-radius: 8px; - .stat-value { - font-size: 24px; + .stat-icon-wrapper { + width: 40px; + height: 40px; + border-radius: 8px; + + .el-icon { + font-size: 20px; + } + } + + .stat-content { + .stat-value { + font-size: 18px; + margin-bottom: 4px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .stat-label { + font-size: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + + // 趋势徐章移到卡片右上角,避免挤压数值区 + .stat-trend { + position: absolute; + top: 8px; + right: 8px; + font-size: 11px; + padding: 2px 6px; } } -} -@media (max-width: 480px) { - .stats-grid { - grid-template-columns: 1fr; + // 列表卡片:缩小内边距与标题字号,高度改自适应防止溢出 + .lists-section { + gap: 12px; + + .list-card { + padding: 16px; + border-radius: 8px; + + .card-header { + margin-bottom: 12px; + + .card-title { + font-size: 16px; + } + + // 按钮点击区域 ≥40px + :deep(.el-button) { + min-height: 40px; + padding: 0 12px; + } + } + + .list-content { + // 手机端适当减小最小高度(桌面端 400px),高度自适应防止溢出 + min-height: 200px; + + // 触屏上禁用 hover 负 margin 外扩效果,避免布局异常 + .task-item, + .activity-item { + &:hover { + margin: 0; + padding-left: 0; + padding-right: 0; + } + } + + // 分页按钮点击区域 ≥40px + .pagination-wrapper { + :deep(.el-pager li), + :deep(.el-pagination button) { + min-width: 40px; + min-height: 40px; + line-height: 40px; + } + } + } + } } } diff --git a/backend/src/views/home/index.vue b/backend/src/views/home/index.vue index 008f994..32fe264 100644 --- a/backend/src/views/home/index.vue +++ b/backend/src/views/home/index.vue @@ -616,7 +616,162 @@ onMounted(() => { .modules-section { .module-grid { - grid-template-columns: 1fr; + grid-template-columns: repeat(2, 1fr); + } + } + } + + // ===== 移动端优先适配(手机端为主要使用场景)===== + @media (max-width: 768px) { + .home-top { + .toolbar { + padding: 8px 12px; + + .toolbar-left { + min-width: 0; + flex-shrink: 1; + + .system-name { + font-size: 16px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + + .toolbar-right { + gap: 6px; + flex-shrink: 0; + + // 扩大移动端点击区域(≥44px) + .toolbar-btn { + width: 40px; + height: 40px; + } + + .user-dropdown-link { + padding: 6px 8px; + min-height: 40px; + + .user-name { + max-width: 72px; + } + } + } + } + } + + .home-wrapper { + padding: 20px 12px; + + .modules-section { + margin-top: 8px; + + .category-section { + margin-bottom: 24px; + + .category-title { + margin-bottom: 14px; + } + } + + .module-grid { + gap: 12px; + } + + .module-card { + height: auto; + min-height: 132px; + padding: 16px; + border-radius: 10px; + + .card-header { + gap: 8px; + + .card-title { + font-size: 16px; + word-break: break-all; + } + + .card-icon-wrapper { + width: 24px; + height: 24px; + font-size: 24px; + flex-shrink: 0; + } + } + + .card-divider { + margin: 12px 0; + } + + .card-content .card-desc { + font-size: 13px; + -webkit-line-clamp: 2; + } + } + } + + .empty-state { + padding: 48px 16px; + } + } + } + + // 超窄屏手机:保持每行 2 个卡片,收紧间距避免溢出,用户名隐藏只保留头像 + @media (max-width: 480px) { + .home-top { + .toolbar { + padding: 8px 10px; + + .toolbar-right { + .user-dropdown-link { + padding: 6px 4px; + + .user-name { + display: none; + } + } + } + } + } + + .home-wrapper { + padding: 16px 10px; + + .modules-section { + .module-grid { + grid-template-columns: repeat(2, 1fr); + gap: 8px; + } + + .module-card { + min-height: 0; + padding: 12px; + border-radius: 8px; + + .card-header { + gap: 6px; + + .card-title { + font-size: 14px; + } + + .card-icon-wrapper { + width: 20px; + height: 20px; + font-size: 20px; + } + } + + .card-divider { + margin: 10px 0; + } + + .card-content .card-desc { + font-size: 12px; + } + } } } }