批量更新

This commit is contained in:
2026-04-29 18:25:59 +08:00
parent 19828ea396
commit 9c9bbb00f2
13 changed files with 968 additions and 147 deletions
+73 -5
View File
@@ -1,5 +1,5 @@
<template>
<el-aside :width="width" class="common-aside">
<el-aside :width="width" :class="['common-aside', { 'mobile-open': isMobile && !isCollapse }]">
<!-- 加载状态 -->
<div v-if="loading" class="loading-spinner">
<i class="el-icon-loading" style="font-size: 24px; color: #fff"></i>
@@ -143,13 +143,19 @@
</el-sub-menu>
</template>
</el-menu>
<div v-if="!loading && !hasError && !isCollapse" class="aside-toggle-bottom">
<el-button class="aside-toggle-btn" size="small" @click="handleCollapse">
<el-icon><Fold /></el-icon>
</el-button>
</div>
</el-aside>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted, watch } from "vue";
import { useRouter, useRoute } from "vue-router";
import { Document, Warning } from "@element-plus/icons-vue";
import { Document, Warning, Fold } from "@element-plus/icons-vue";
import { useAllDataStore, useMenuStore } from "@/stores";
const emit = defineEmits(["menu-click"]);
@@ -163,7 +169,13 @@ const errorMsg = computed(() => menuStore.error || "加载菜单失败");
const store = useAllDataStore();
const isCollapse = computed(() => store.state.isCollapse);
const width = computed(() => (store.state.isCollapse ? "64px" : "200px"));
const isMobile = ref(false);
const width = computed(() => {
if (isMobile.value) {
return store.state.isCollapse ? "0px" : "220px";
}
return store.state.isCollapse ? "64px" : "200px";
});
const asideBgColor = ref("#304156");
const asideTextColor = ref("#bfcbd9");
@@ -171,6 +183,13 @@ const activeColor = ref("#3973FF");
const activeBgColor = ref("#3973FF");
const currentModuleId = ref(null);
const updateDeviceType = () => {
isMobile.value = window.innerWidth <= 768;
// 手机端默认收起侧边栏
if (isMobile.value) {
store.state.isCollapse = true;
}
};
const findMenuItem = (menus, targetIndex) => {
for (const menu of menus) {
@@ -328,6 +347,9 @@ const handleMenuSelect = (index) => {
const menuItem = findMenuItem(list.value, index);
if (menuItem) {
emit("menu-click", menuItem);
if (isMobile.value) {
store.state.isCollapse = true;
}
}
};
@@ -337,6 +359,10 @@ const fetchMenus = async () => {
} catch (error) {}
};
const handleCollapse = () => {
store.state.isCollapse = !store.state.isCollapse;
};
const handleMenuRefresh = () => {
fetchMenus();
};
@@ -350,6 +376,9 @@ watch(
);
onMounted(() => {
updateDeviceType();
window.addEventListener("resize", updateDeviceType);
if (!menuStore.menus || menuStore.menus.length === 0) {
setTimeout(() => {
fetchMenus();
@@ -360,6 +389,7 @@ onMounted(() => {
});
onUnmounted(() => {
window.removeEventListener("resize", updateDeviceType);
window.removeEventListener("menu-cache-refreshed", handleMenuRefresh);
});
</script>
@@ -386,6 +416,10 @@ onUnmounted(() => {
}
}
.common-aside.mobile-open {
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.35), 2px 0 12px rgba(0, 0, 0, 0.18);
}
.loading-spinner {
display: flex;
align-items: center;
@@ -422,7 +456,7 @@ h3 {
// 菜单样式
:deep(.el-menu) {
border-right: none;
height: calc(100% - 80px);
height: calc(100% - 128px);
padding: 16px 8px;
background: transparent;
@@ -519,14 +553,48 @@ h3 {
}
}
.aside-toggle-bottom {
position: absolute;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
padding: 12px 8px 14px;
background: linear-gradient(to top, rgba(0, 0, 0, 0.14), rgba(0, 0, 0, 0));
}
.aside-toggle-btn {
width: 100%;
max-width: 180px;
background-color: rgba(255, 255, 255, 0.18);
border-color: rgba(255, 255, 255, 0.3);
color: #fff;
}
.aside-toggle-btn:hover {
background-color: rgba(255, 255, 255, 0.28);
border-color: rgba(255, 255, 255, 0.45);
color: #fff;
}
// 响应式设计
@media (max-width: 768px) {
.common-aside {
width: 100% !important;
position: fixed;
left: 0;
top: 0;
bottom: 0;
z-index: 1200;
max-width: 80vw;
}
:deep(.el-menu) {
padding: 12px 4px;
}
.aside-toggle-bottom {
padding: 10px 8px 12px;
}
}
</style>
+45 -3
View File
@@ -1,8 +1,8 @@
<template>
<div class="header">
<div class="l-content">
<el-button size="small" @click="handleCollapse">
<i class="fa fa-bars"></i>
<el-button v-if="showTopToggle" size="small" @click="handleCollapse">
<el-icon><Expand /></el-icon>
</el-button>
</div>
<div class="r-content">
@@ -75,7 +75,7 @@ import { useRouter, useRoute } from "vue-router";
import { useAllDataStore, useMenuStore, useTabsStore } from "@/stores";
import { useAuthStore } from "@/stores/auth";
import { logout, getCurrentUser } from "@/api/login";
import { User, SwitchButton, Sunny, Moon, Refresh, Bell, HomeFilled } from '@element-plus/icons-vue';
import { User, SwitchButton, Sunny, Moon, Refresh, Bell, HomeFilled, Expand } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
const router = useRouter();
@@ -201,6 +201,8 @@ const handleCollapse = () => {
store.state.isCollapse = !store.state.isCollapse;
};
const showTopToggle = computed(() => store.state.isCollapse);
const goHome = () => {
tabsStore.closeAll();
router.push('/home');
@@ -457,6 +459,46 @@ onUnmounted(() => {
}
}
@media (max-width: 768px) {
.header {
padding: 0 10px;
gap: 8px;
}
.l-content .el-button {
margin-right: 6px;
}
.r-content {
gap: 6px;
.refresh-cache-btn,
.home-btn,
.theme-toggle-btn,
.message-btn {
width: 30px;
height: 30px;
min-height: 30px;
min-width: 30px;
padding: 0;
}
.el-dropdown-link {
gap: 6px;
.user {
width: 30px;
height: 30px;
}
.user-name,
.user-role-tag {
display: none;
}
}
}
}
// 下拉菜单样式 - 使用全局样式覆盖
:deep(.el-dropdown) {
.el-dropdown__popper {