|
|
@@ -7,22 +7,26 @@
|
|
|
<!-- 背景层 -->
|
|
|
<div v-if="isSpecialBg" class="background-layer"></div>
|
|
|
|
|
|
- <!-- Header -->
|
|
|
+ <!-- Header -->
|
|
|
<el-header
|
|
|
class="layout-header"
|
|
|
v-if="!isFullScreen"
|
|
|
:class="{ 'transparent-header': isSpecialBg }"
|
|
|
>
|
|
|
<div class="logo-title-row">
|
|
|
- <img src="@/assets/logo.png" alt="Logo" class="logo" />
|
|
|
- <div class="title-and-user">
|
|
|
+ <!-- 左侧:Logo和标题 -->
|
|
|
+ <div class="left-section">
|
|
|
+ <img src="@/assets/logo.png" alt="Logo" class="logo" />
|
|
|
<span class="project-name" :class="{ 'light-text': isSpecialBg }">
|
|
|
土壤酸化智能预测专家系统
|
|
|
</span>
|
|
|
+ </div>
|
|
|
|
|
|
- <div class="user-info-row" v-if="!isSelectCity">
|
|
|
+ <!-- 右侧:用户信息 -->
|
|
|
+ <div class="right-section" v-if="!isSelectCity">
|
|
|
+ <div class="user-info-row">
|
|
|
<span class="welcome-text" :class="{ 'light-text': isSpecialBg }">
|
|
|
- 欢迎 {{ userInfo.type === "admin" ? "管理员" : "用户" }} 登录成功
|
|
|
+ 欢迎 {{ userInfo.name }} 登录成功
|
|
|
</span>
|
|
|
<el-dropdown>
|
|
|
<span class="el-dropdown-link">
|
|
|
@@ -34,12 +38,12 @@
|
|
|
</span>
|
|
|
<template #dropdown>
|
|
|
<el-dropdown-menu>
|
|
|
- <el-dropdown-item disabled>
|
|
|
- 用户名:{{ userInfo.name }}
|
|
|
- </el-dropdown-item>
|
|
|
- <el-dropdown-item divided @click="handleLogout">
|
|
|
- 退出登录
|
|
|
- </el-dropdown-item>
|
|
|
+ <el-dropdown-item disabled
|
|
|
+ >用户名:{{ userInfo.name }}</el-dropdown-item
|
|
|
+ >
|
|
|
+ <el-dropdown-item divided @click="handleLogout"
|
|
|
+ >退出登录</el-dropdown-item
|
|
|
+ >
|
|
|
</el-dropdown-menu>
|
|
|
</template>
|
|
|
</el-dropdown>
|
|
|
@@ -55,7 +59,7 @@
|
|
|
v-model="activeName"
|
|
|
class="demo-tabs"
|
|
|
:style="tabStyle"
|
|
|
- @tab-click="(tab: { name: string; }) => handleClick(tab.name)"
|
|
|
+ @tab-click="handleClick"
|
|
|
>
|
|
|
<el-tab-pane v-for="tab in tabs" :key="tab.name" :name="tab.name">
|
|
|
<template #label>
|
|
|
@@ -64,7 +68,7 @@
|
|
|
</template>
|
|
|
</el-tab-pane>
|
|
|
</el-tabs>
|
|
|
- <div v-else class="single-tab" @click="handleClick(tabs[0].name)">
|
|
|
+ <div v-else class="single-tab" @click="handleClick(tabs[0], $event)">
|
|
|
<i :class="['tab-icon', tabs[0].icon]"></i>
|
|
|
<span class="tab-label-text">{{ tabs[0].label }}</span>
|
|
|
</div>
|
|
|
@@ -73,7 +77,11 @@
|
|
|
<!-- Main Content -->
|
|
|
<el-container class="layout-main-container">
|
|
|
<el-aside v-if="showAside && showTabs" class="layout-aside">
|
|
|
- <AppAside :activeTab="activeName" :showTabs="showTabs" />
|
|
|
+ <component
|
|
|
+ :is="AsideComponent"
|
|
|
+ :activeTab="activeName"
|
|
|
+ :showTabs="showTabs"
|
|
|
+ />
|
|
|
</el-aside>
|
|
|
|
|
|
<el-main class="layout-content-wrapper" :style="mainStyle">
|
|
|
@@ -87,82 +95,39 @@
|
|
|
</div>
|
|
|
</el-main>
|
|
|
</el-container>
|
|
|
+
|
|
|
+ <!-- 全局消息提示组件 -->
|
|
|
+ <div v-if="showGlobalMessage" class="global-message">
|
|
|
+ <div class="message-content" :class="messageType">
|
|
|
+ {{ globalMessage }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, computed, watch } from "vue";
|
|
|
+import { ref, reactive, computed, watch, defineAsyncComponent, onMounted } from "vue";
|
|
|
import { useRouter, useRoute } from "vue-router";
|
|
|
import { useTokenStore } from "@/stores/mytoken";
|
|
|
-import { ElMessageBox, ElMessage } from "element-plus";
|
|
|
+import { ElMessageBox, ElMessage } from "element-plus"; // 确保导入ElMessage
|
|
|
import { logout } from "@/API/users";
|
|
|
-import AppAside from "./AppAside.vue";
|
|
|
+import { useI18n } from "vue-i18n";
|
|
|
|
|
|
+const { t } = useI18n();
|
|
|
const router = useRouter();
|
|
|
const route = useRoute();
|
|
|
const tokenStore = useTokenStore();
|
|
|
+const currentBgImage = ref("");
|
|
|
|
|
|
-const userInfo = computed(() => ({
|
|
|
- name: tokenStore.userName,
|
|
|
- type: tokenStore.userType,
|
|
|
-}));
|
|
|
+// ============ 新增状态 ============
|
|
|
+const showGlobalMessage = ref(false);
|
|
|
+const globalMessage = ref("");
|
|
|
+const messageType = ref("success"); // 消息类型: success/error
|
|
|
|
|
|
-const activeName = ref("");
|
|
|
-
|
|
|
-const bgRouteMap: Record<string, string> = {
|
|
|
- // === 普通用户路由 ===
|
|
|
- "/SoilPro": "background.jpg",
|
|
|
- "/Overview": "background.jpg",
|
|
|
- "/ResearchFindings": "background.jpg",
|
|
|
- "/Unit": "background.jpg",
|
|
|
- "/acidmodelmap": "background.jpg",
|
|
|
- "/Calculation": "background.jpg",
|
|
|
- "/SoilAcidReductionIterativeEvolution": "background.jpg",
|
|
|
- "/AcidNeutralizationModel": "background.jpg",
|
|
|
- "/SoilAcidificationIterativeEvolution": "background.jpg",
|
|
|
- "/DetectionStatistics": "background.jpg",
|
|
|
- "/FarmlandPollutionStatistics": "background.jpg",
|
|
|
- "/LandClutivatesStatistics": "background.jpg",
|
|
|
- "/SoilacidificationStatistics": "background.jpg",
|
|
|
-
|
|
|
- // === 管理员路由 ===
|
|
|
- "/soilAcidReductionData": "background.jpg",
|
|
|
- "/soilAcidificationData": "background.jpg",
|
|
|
- "/crossSectionSampleData": "background.jpg",
|
|
|
- "/irrigationWaterInputFluxData": "background.jpg",
|
|
|
- "/heavyMetalEnterpriseData": "background.jpg",
|
|
|
- "/atmosphericSampleData": "background.jpg",
|
|
|
- "/IntroductionUpdate": "background.jpg",
|
|
|
- "/ModelSelection": "background.jpg",
|
|
|
- "/thres": "background.jpg",
|
|
|
- "/ModelTrain": "background.jpg",
|
|
|
- "/UserManagement": "background.jpg",
|
|
|
-};
|
|
|
+currentBgImage.value = `url(${new URL('../../assets/bg/background.jpg', import.meta.url).href})`;
|
|
|
|
|
|
-const isSpecialBg = computed(() =>
|
|
|
- Object.keys(bgRouteMap).includes(route.path)
|
|
|
-);
|
|
|
-
|
|
|
-function getBgImageUrl(): string {
|
|
|
- if (bgRouteMap[route.path]) {
|
|
|
- try {
|
|
|
- return `url(${new URL("@/assets/bg/background.jpg", import.meta.url).href})`;
|
|
|
- } catch (error) {
|
|
|
- console.error("加载背景图失败:", error);
|
|
|
- return "";
|
|
|
- }
|
|
|
- }
|
|
|
- return "";
|
|
|
-}
|
|
|
-
|
|
|
-const currentBgImage = ref("");
|
|
|
-watch(
|
|
|
- () => route.path,
|
|
|
- () => {
|
|
|
- currentBgImage.value = getBgImageUrl();
|
|
|
- },
|
|
|
- { immediate: true }
|
|
|
-);
|
|
|
+// 是否特殊背景(始终返回true → 所有页面应用特殊背景样式)
|
|
|
+const isSpecialBg = computed(() => true);
|
|
|
|
|
|
const backgroundStyle = computed(() => ({
|
|
|
backgroundImage: currentBgImage.value,
|
|
|
@@ -175,165 +140,220 @@ const backgroundStyle = computed(() => ({
|
|
|
const isFullScreen = computed(() => route.meta.fullScreen === true);
|
|
|
const isSelectCity = computed(() => route.path === "/select-city");
|
|
|
|
|
|
-const tabs = computed(() => {
|
|
|
- if (userInfo.value.type === "admin") {
|
|
|
- return [
|
|
|
- {
|
|
|
- name: "dataManagement",
|
|
|
- label: "数据管理",
|
|
|
- icon: "el-icon-folder",
|
|
|
- routes: [
|
|
|
- "/soilAcidReductionData",
|
|
|
- "/soilAcidificationData",
|
|
|
- "/crossSectionSampleData",
|
|
|
- "/irrigationWaterInputFluxData",
|
|
|
- "/heavyMetalEnterpriseData",
|
|
|
- "/atmosphericSampleData",
|
|
|
- ],
|
|
|
- },
|
|
|
- {
|
|
|
- name: "infoManagement",
|
|
|
- label: "信息管理",
|
|
|
- icon: "el-icon-document",
|
|
|
- routes: ["/IntroductionUpdate"],
|
|
|
- },
|
|
|
- {
|
|
|
- name: "modelManagement",
|
|
|
- label: "模型管理及配置",
|
|
|
- icon: "el-icon-cpu",
|
|
|
- routes: ["/ModelSelection", "/thres", "/ModelTrain"],
|
|
|
- },
|
|
|
- {
|
|
|
- name: "userManagement",
|
|
|
- label: "用户管理",
|
|
|
- icon: "el-icon-user",
|
|
|
- routes: ["/UserManagement"],
|
|
|
- },
|
|
|
- ];
|
|
|
- } else {
|
|
|
- return [
|
|
|
- {
|
|
|
- name: "introduction",
|
|
|
- label: "软件简介",
|
|
|
- icon: "el-icon-info-filled",
|
|
|
- routes: ["/SoilPro", "/Overview", "/ResearchFindings", "/Unit"],
|
|
|
- },
|
|
|
- {
|
|
|
- name: "acidmodelmap",
|
|
|
- label: "土壤酸化地块级计算",
|
|
|
- icon: "el-icon-data-analysis",
|
|
|
- routes: ["/acidmodelmap"],
|
|
|
- },
|
|
|
- {
|
|
|
- name: "Calculation",
|
|
|
- label: "土壤反酸模型",
|
|
|
- icon: "el-icon-data-analysis",
|
|
|
- routes: ["/Calculation", "/SoilAcidReductionIterativeEvolution"],
|
|
|
- },
|
|
|
- {
|
|
|
- name: "AcidNeutralizationModel",
|
|
|
- label: "土壤降酸模型",
|
|
|
- icon: "el-icon-data-analysis",
|
|
|
- routes: ["/AcidNeutralizationModel", "/SoilAcidificationIterativeEvolution"],
|
|
|
- },
|
|
|
- {
|
|
|
- name: "dataStatistics",
|
|
|
- label: "数据统计",
|
|
|
- icon: "el-icon-pie-chart",
|
|
|
- routes: [
|
|
|
- "/DetectionStatistics",
|
|
|
- "/FarmlandPollutionStatistics",
|
|
|
- "/LandClutivatesStatistics",
|
|
|
- "/SoilacidificationStatistics",
|
|
|
- ],
|
|
|
- },
|
|
|
- ];
|
|
|
- }
|
|
|
+// 当前用户信息
|
|
|
+const userInfo = reactive({
|
|
|
+ name: tokenStore.userName,
|
|
|
+ type: tokenStore.userType,
|
|
|
});
|
|
|
|
|
|
-// 路由 ↔ Tab 双向同步
|
|
|
-watch(
|
|
|
- () => route.path,
|
|
|
- (currentPath) => {
|
|
|
- const matchedTab = tabs.value.find(tab =>
|
|
|
- tab.routes.includes(currentPath)
|
|
|
- );
|
|
|
- if (matchedTab && activeName.value !== matchedTab.name) {
|
|
|
- activeName.value = matchedTab.name;
|
|
|
- }
|
|
|
- },
|
|
|
- { immediate: true }
|
|
|
-);
|
|
|
+const tabs = computed(() => {
|
|
|
|
|
|
-// 初始化 activeName
|
|
|
-watch(
|
|
|
- () => userInfo.value.type,
|
|
|
- (type) => {
|
|
|
- if (!activeName.value) {
|
|
|
- activeName.value = type === "admin" ? "dataManagement" : "introduction";
|
|
|
- }
|
|
|
- },
|
|
|
- { immediate: true }
|
|
|
+ if (userInfo.type === "admin") {
|
|
|
+
|
|
|
+ return [
|
|
|
+
|
|
|
+ {
|
|
|
+
|
|
|
+ name: "dataManagement",
|
|
|
+
|
|
|
+ label: "数据管理",
|
|
|
+
|
|
|
+ icon: "el-icon-folder",
|
|
|
+
|
|
|
+ routes: [
|
|
|
+
|
|
|
+ "/soilAcidReductionData",
|
|
|
+
|
|
|
+ "/soilAcidificationData",
|
|
|
+
|
|
|
+ "/crossSectionSampleData",
|
|
|
+
|
|
|
+ "/irrigationWaterInputFluxData",
|
|
|
+
|
|
|
+ "/heavyMetalEnterpriseData",
|
|
|
+
|
|
|
+ "/atmosphericSampleData",
|
|
|
+
|
|
|
+ ],
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ {
|
|
|
+
|
|
|
+ name: "infoManagement",
|
|
|
+
|
|
|
+ label: "信息管理",
|
|
|
+
|
|
|
+ icon: "el-icon-document",
|
|
|
+
|
|
|
+ routes: ["/IntroductionUpdate"],
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ {
|
|
|
+
|
|
|
+ name: "modelManagement",
|
|
|
+
|
|
|
+ label: "模型管理及配置",
|
|
|
+
|
|
|
+ icon: "el-icon-cpu",
|
|
|
+
|
|
|
+ routes: ["/ModelSelection", "/thres", "/ModelTrain"],
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ {
|
|
|
+
|
|
|
+ name: "userManagement",
|
|
|
+
|
|
|
+ label: "用户管理",
|
|
|
+
|
|
|
+ icon: "el-icon-user",
|
|
|
+
|
|
|
+ routes: ["/UserManagement"],
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ ];
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ return [
|
|
|
+
|
|
|
+ {
|
|
|
+
|
|
|
+ name: "introduction",
|
|
|
+
|
|
|
+ label: "软件简介",
|
|
|
+
|
|
|
+ icon: "el-icon-info-filled",
|
|
|
+
|
|
|
+ routes: ["/SoilPro", "/Overview", "/ResearchFindings", "/Unit"],
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ {
|
|
|
+ name: "acidmodelmap",
|
|
|
+
|
|
|
+ label: "土壤酸化地块级计算",
|
|
|
+
|
|
|
+ icon: "el-icon-data-analysis",
|
|
|
+
|
|
|
+ routes: ["/acidmodelmap"],
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ {
|
|
|
+
|
|
|
+ name: "Calculation",
|
|
|
+
|
|
|
+ label: "土壤反酸模型",
|
|
|
+
|
|
|
+ icon: "el-icon-data-analysis",
|
|
|
+
|
|
|
+ routes: ["/Calculation", "/SoilAcidReductionIterativeEvolution"],
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ {
|
|
|
+
|
|
|
+ name: "AcidNeutralizationModel",
|
|
|
+
|
|
|
+ label: "土壤降酸模型",
|
|
|
+
|
|
|
+ icon: "el-icon-data-analysis",
|
|
|
+
|
|
|
+ routes: ["/AcidNeutralizationModel", "/SoilAcidificationIterativeEvolution"],
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ {
|
|
|
+
|
|
|
+ name: "dataStatistics",
|
|
|
+
|
|
|
+ label: "数据统计",
|
|
|
+
|
|
|
+ icon: "el-icon-pie-chart",
|
|
|
+
|
|
|
+ routes: [
|
|
|
+
|
|
|
+ "/DetectionStatistics",
|
|
|
+
|
|
|
+ "/FarmlandPollutionStatistics",
|
|
|
+
|
|
|
+ "/LandClutivatesStatistics",
|
|
|
+
|
|
|
+ "/SoilacidificationStatistics",
|
|
|
+
|
|
|
+ ],
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+});
|
|
|
+
|
|
|
+const activeName = ref(tabs.value[0]?.name || "");
|
|
|
+const showTabs = computed(() => tabs.value.length > 1);
|
|
|
+const tabStyle = computed(() =>
|
|
|
+ tabs.value.length === 1 ? { width: "100%", justifyContent: "center" } : {}
|
|
|
);
|
|
|
+let hasNavigated = false;
|
|
|
|
|
|
-// 自动跳转到 Tab 第一个子路由(如果不在该 Tab 内)
|
|
|
watch(
|
|
|
() => activeName.value,
|
|
|
- (newTabName) => {
|
|
|
- if (!newTabName) return;
|
|
|
- const currentTab = tabs.value.find(tab => tab.name === newTabName);
|
|
|
- if (!currentTab) return;
|
|
|
- if (currentTab.routes.includes(route.path)) return;
|
|
|
-
|
|
|
- const firstRoute = currentTab.routes[0];
|
|
|
- if (firstRoute && firstRoute !== route.path) {
|
|
|
- router.push(firstRoute).catch((err) => {
|
|
|
- if (err.name !== "NavigationDuplicated") {
|
|
|
- console.warn("[Tab Auto Redirect] 跳转失败:", err);
|
|
|
- }
|
|
|
- });
|
|
|
+ (newTab) => {
|
|
|
+ const tab = tabs.value.find((t) => t.name === newTab);
|
|
|
+ const targetPath = tab?.routes?.[0];
|
|
|
+ if (!hasNavigated) {
|
|
|
+ hasNavigated = true;
|
|
|
+ return;
|
|
|
}
|
|
|
+ if (tab && targetPath && router.currentRoute.value.path !== targetPath)
|
|
|
+ router.push({ path: targetPath });
|
|
|
},
|
|
|
{ immediate: true }
|
|
|
);
|
|
|
|
|
|
-const showTabs = computed(() => tabs.value.length > 1);
|
|
|
-const tabStyle = computed(() =>
|
|
|
- tabs.value.length === 1 ? { width: "100%", justifyContent: "center" } : {}
|
|
|
-);
|
|
|
-
|
|
|
-// ✅ 修复后的 handleClick:只接收 tabName
|
|
|
-const handleClick = (tabName: string) => {
|
|
|
- const tab = tabs.value.find(t => t.name === tabName);
|
|
|
- if (!tab) return;
|
|
|
-
|
|
|
- if (tab.routes.includes(route.path)) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const targetRoute = tab.routes[0];
|
|
|
- if (targetRoute && targetRoute !== route.path) {
|
|
|
- router.push(targetRoute).catch((err) => {
|
|
|
- if (err.name !== "NavigationDuplicated") {
|
|
|
- console.warn("[Tab Click] 跳转失败:", err);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
+// 点击 tab
|
|
|
+const activeAsideTab = ref(activeName.value || "");
|
|
|
+const handleClick = (tab: any, event?: Event) => {
|
|
|
+ activeAsideTab.value = tab.name || tab.props?.name;
|
|
|
};
|
|
|
|
|
|
+// 动态加载侧边栏
|
|
|
+const AsideComponent = computed(() => {
|
|
|
+ return [
|
|
|
+ "dataManagement",
|
|
|
+ "infoManagement",
|
|
|
+ "modelManagement",
|
|
|
+ "userManagement",
|
|
|
+ ].includes(activeName.value)
|
|
|
+ ? defineAsyncComponent(() => import("./AppAsideForTab2.vue"))
|
|
|
+ : defineAsyncComponent(() => import("./AppAside.vue"));
|
|
|
+});
|
|
|
+
|
|
|
+// 是否显示侧边栏
|
|
|
const showAside = computed(
|
|
|
() =>
|
|
|
- !isFullScreen.value &&
|
|
|
- !["cropRiskAssessment"].includes(activeName.value)
|
|
|
+ !isFullScreen.value && !["cropRiskAssessment"].includes(activeName.value)
|
|
|
);
|
|
|
|
|
|
-const mainStyle = computed(() => ({
|
|
|
- padding: ["mapView", "infoManagement"].includes(activeName.value)
|
|
|
- ? "0"
|
|
|
- : "20px",
|
|
|
- overflow: "hidden",
|
|
|
-}));
|
|
|
+// ============ 显示全局消息 ============
|
|
|
+const showMessage = (message: string, type: "success" | "error" = "success") => {
|
|
|
+ globalMessage.value = message;
|
|
|
+ messageType.value = type;
|
|
|
+ showGlobalMessage.value = true;
|
|
|
+
|
|
|
+ // 3秒后自动隐藏消息
|
|
|
+ setTimeout(() => {
|
|
|
+ showGlobalMessage.value = false;
|
|
|
+ }, 3000);
|
|
|
+};
|
|
|
|
|
|
+// 登出逻辑
|
|
|
const handleLogout = async () => {
|
|
|
try {
|
|
|
await ElMessageBox.confirm("确定要退出登录吗?", "提示", {
|
|
|
@@ -343,112 +363,149 @@ const handleLogout = async () => {
|
|
|
});
|
|
|
await logout();
|
|
|
tokenStore.clearToken();
|
|
|
- ElMessage.success("退出成功");
|
|
|
- router.push("/login");
|
|
|
- } catch {
|
|
|
- ElMessage.info("已取消退出");
|
|
|
+
|
|
|
+ // 使用ElMessage而不是全局消息提示
|
|
|
+ ElMessage.success("退出登录成功");
|
|
|
+
|
|
|
+ // 延迟跳转,让用户看到提示消息
|
|
|
+ setTimeout(() => {
|
|
|
+ router.push("/login");
|
|
|
+ }, 1000);
|
|
|
+ } catch (error) {
|
|
|
+ // 区分用户取消操作和真正的错误
|
|
|
+ if (error === 'cancel' || error?.toString().includes('cancel')) {
|
|
|
+ console.log("用户取消退出登录");
|
|
|
+ } else {
|
|
|
+ ElMessage.error("退出失败,请重试");
|
|
|
+ console.error("退出登录错误:", error);
|
|
|
+ }
|
|
|
}
|
|
|
};
|
|
|
+
|
|
|
+// 内容区样式
|
|
|
+const mainStyle = computed(() => ({
|
|
|
+ padding: ["mapView", "infoManagement"].includes(activeName.value)
|
|
|
+ ? "0"
|
|
|
+ : "20px",
|
|
|
+ overflow: "hidden",
|
|
|
+}));
|
|
|
</script>
|
|
|
|
|
|
<style>
|
|
|
/* 隐藏所有滚动条 */
|
|
|
*::-webkit-scrollbar {
|
|
|
display: none;
|
|
|
+ /* Chrome, Safari, Opera */
|
|
|
}
|
|
|
|
|
|
* {
|
|
|
-ms-overflow-style: none;
|
|
|
+ /* IE and Edge */
|
|
|
scrollbar-width: none;
|
|
|
+ /* Firefox */
|
|
|
}
|
|
|
|
|
|
+/* 整体布局容器 */
|
|
|
.layout-wrapper {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
height: 100vh;
|
|
|
overflow: hidden;
|
|
|
- position: relative;
|
|
|
- background-color: #f5f7fa;
|
|
|
+ position: relative; /* 创建层叠上下文 */
|
|
|
+ background-color: #f5f7fa; /* 默认背景色 */
|
|
|
+ /* 确保 layout-wrapper 自身的 z-index 不干扰子元素,或根据需要设置 */
|
|
|
+ /* z-index: 0; */ /* 通常不需要显式设置,除非有特殊需求 */
|
|
|
}
|
|
|
|
|
|
+/* 背景层 - 关键修改 */
|
|
|
.background-layer {
|
|
|
position: absolute;
|
|
|
top: 0;
|
|
|
left: 0;
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
- z-index: 0;
|
|
|
+ /* 将背景层的 z-index 设为一个较低的负值或非常小的正值,确保它在最底层 */
|
|
|
+ z-index: -1; /* 修改点:使用负值确保其在最底层,避免任何可能的遮挡 */
|
|
|
background-size: cover;
|
|
|
background-position: center;
|
|
|
background-repeat: no-repeat;
|
|
|
filter: brightness(0.8);
|
|
|
}
|
|
|
|
|
|
+/* 全屏页面特殊处理 */
|
|
|
.layout-wrapper.full-screen {
|
|
|
background: none;
|
|
|
min-height: 100vh;
|
|
|
}
|
|
|
|
|
|
+/* 特殊背景页面的Header样式 */
|
|
|
.transparent-header {
|
|
|
background: transparent !important;
|
|
|
- backdrop-filter: blur(2px);
|
|
|
+ backdrop-filter: blur(2px); /* 添加轻微模糊效果增强可读性 */
|
|
|
}
|
|
|
|
|
|
+/* 特殊背景页面的文字颜色 */
|
|
|
.light-text {
|
|
|
- color: #ffffff !important;
|
|
|
- text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
|
|
|
+ color: #ffffff !important; /* 白色文字 */
|
|
|
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7); /* 文字阴影增强可读性 */
|
|
|
}
|
|
|
|
|
|
+/* 特殊背景页面的头像边框 */
|
|
|
.light-avatar-border {
|
|
|
- border: 2px solid #ffffff !important;
|
|
|
+ border: 2px solid #ffffff !important; /* 白色边框 */
|
|
|
}
|
|
|
|
|
|
+/* 内容区域在特殊背景页面透明 */
|
|
|
.transparent-scroll {
|
|
|
background-color: transparent !important;
|
|
|
}
|
|
|
|
|
|
+/* Header 样式 */
|
|
|
.layout-header {
|
|
|
- height: 150px;
|
|
|
+ height: 80px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
- color: #333;
|
|
|
+ color: #333; /* 深色文字 */
|
|
|
flex-shrink: 0;
|
|
|
+ /* 确保在背景层上方 */
|
|
|
position: relative;
|
|
|
- z-index: 2;
|
|
|
- background-color: white;
|
|
|
+ /* 修改点:增大 z-index 确保在背景层之上 */
|
|
|
+ z-index: 10; /* 增大 z-index */
|
|
|
+ background-color: white; /* 默认背景色 */
|
|
|
}
|
|
|
|
|
|
+/* 修改Header布局样式 */
|
|
|
.logo-title-row {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- gap: 24px;
|
|
|
+ justify-content: space-between; /* 左右两侧分布 */
|
|
|
width: 100%;
|
|
|
+ gap: 24px;
|
|
|
}
|
|
|
|
|
|
.title-and-user {
|
|
|
display: flex;
|
|
|
- flex-direction: column;
|
|
|
+ flex-direction: row;
|
|
|
flex: 1;
|
|
|
}
|
|
|
|
|
|
+/* 用户信息区域样式调整 */
|
|
|
.user-info-row {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- justify-content: flex-end;
|
|
|
- gap: 24px;
|
|
|
+ gap: 16px;
|
|
|
color: #333;
|
|
|
- padding-top: 1px;
|
|
|
- position: static;
|
|
|
- z-index: 1;
|
|
|
}
|
|
|
|
|
|
.welcome-text {
|
|
|
font-size: 28px;
|
|
|
font-weight: 500;
|
|
|
- color: #ffffff !important;
|
|
|
+ color: #ffffff;
|
|
|
+ white-space: nowrap; /* 防止文字换行 */
|
|
|
}
|
|
|
|
|
|
+/* Tab 区域 - 不透明 */
|
|
|
.tabs-row {
|
|
|
height: 48px;
|
|
|
display: flex;
|
|
|
@@ -458,10 +515,13 @@ const handleLogout = async () => {
|
|
|
background: linear-gradient(to right, #1092d8, #02c3ad);
|
|
|
border-bottom: none !important;
|
|
|
flex-shrink: 0;
|
|
|
+ /* 确保在背景层上方 */
|
|
|
position: relative;
|
|
|
- z-index: 2;
|
|
|
+ /* 修改点:增大 z-index 确保在背景层之上 */
|
|
|
+ z-index: 10; /* 增大 z-index */
|
|
|
}
|
|
|
|
|
|
+/* el-tabs 外层容器 */
|
|
|
.demo-tabs {
|
|
|
height: 48px !important;
|
|
|
display: flex;
|
|
|
@@ -472,6 +532,7 @@ const handleLogout = async () => {
|
|
|
border-bottom: none !important;
|
|
|
}
|
|
|
|
|
|
+/* 清除滑块条和底部线条 */
|
|
|
.el-tabs__nav-wrap::after,
|
|
|
.el-tabs__active-bar {
|
|
|
display: none !important;
|
|
|
@@ -484,6 +545,7 @@ const handleLogout = async () => {
|
|
|
margin: 0 !important;
|
|
|
}
|
|
|
|
|
|
+/* Tabs 单项样式 */
|
|
|
.el-tabs__item {
|
|
|
height: 48px !important;
|
|
|
line-height: 48px !important;
|
|
|
@@ -497,28 +559,36 @@ const handleLogout = async () => {
|
|
|
transition: all 0.2s ease-in-out;
|
|
|
background-color: transparent;
|
|
|
position: relative;
|
|
|
- z-index: 1;
|
|
|
+ /* 修改点:增大 z-index 确保在背景层之上 */
|
|
|
+ z-index: 10; /* 增大 z-index */
|
|
|
}
|
|
|
|
|
|
+/* 激活 Tab */
|
|
|
.el-tabs__item.is-active {
|
|
|
background-color: #2a53ba;
|
|
|
color: #ffffff;
|
|
|
font-weight: 700;
|
|
|
box-shadow: 0 4px 16px rgba(26, 188, 156, 0.4);
|
|
|
- z-index: 2;
|
|
|
+ /* 修改点:增大 z-index 确保在背景层之上 */
|
|
|
+ z-index: 11; /* 激活项可以更高一点 */
|
|
|
}
|
|
|
|
|
|
+/* 鼠标悬停 */
|
|
|
.el-tabs__item:hover {
|
|
|
background-color: #455a64;
|
|
|
color: #ffffff;
|
|
|
+ /* 修改点:增大 z-index 确保在背景层之上 */
|
|
|
+ z-index: 11; /* 悬停时也可以更高 */
|
|
|
}
|
|
|
|
|
|
+/* 图标样式 */
|
|
|
.tab-icon {
|
|
|
font-size: 24px;
|
|
|
margin-right: 4px;
|
|
|
color: inherit;
|
|
|
}
|
|
|
|
|
|
+/* 文字样式 */
|
|
|
.tab-label-text {
|
|
|
font-size: 20px;
|
|
|
color: inherit;
|
|
|
@@ -531,10 +601,45 @@ const handleLogout = async () => {
|
|
|
}
|
|
|
|
|
|
.project-name {
|
|
|
- font-size: 48px;
|
|
|
+ font-size: 32px; /* 适当调整字体大小 */
|
|
|
font-weight: bold;
|
|
|
- margin-top: 30px;
|
|
|
color: #333;
|
|
|
+ white-space: nowrap; /* 防止标题换行 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 响应式设计:在小屏幕上调整布局 */
|
|
|
+@media (max-width: 1200px) {
|
|
|
+ .project-name {
|
|
|
+ font-size: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .welcome-text {
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 768px) {
|
|
|
+ .logo-title-row {
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .left-section {
|
|
|
+ order: 1;
|
|
|
+ width: 100%;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .right-section {
|
|
|
+ order: 2;
|
|
|
+ width: 100%;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .project-name {
|
|
|
+ font-size: 18px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.layout-main-container {
|
|
|
@@ -542,12 +647,15 @@ const handleLogout = async () => {
|
|
|
display: flex;
|
|
|
overflow: hidden;
|
|
|
min-height: 0;
|
|
|
+ /* 确保在背景层上方 */
|
|
|
position: relative;
|
|
|
- z-index: 1;
|
|
|
+ /* 修改点:增大 z-index 确保在背景层之上 */
|
|
|
+ z-index: 10; /* 增大 z-index */
|
|
|
}
|
|
|
|
|
|
+/* 侧边栏 - 白色背景 */
|
|
|
.layout-aside {
|
|
|
- width: 360px;
|
|
|
+ width: 280px;
|
|
|
background: linear-gradient(to bottom, #b7f1fc, #fff8f0);
|
|
|
border-right: 1px solid;
|
|
|
overflow-y: auto;
|
|
|
@@ -555,9 +663,11 @@ const handleLogout = async () => {
|
|
|
padding-top: 8px;
|
|
|
height: 100%;
|
|
|
position: relative;
|
|
|
- z-index: 2;
|
|
|
+ /* 修改点:增大 z-index 确保在背景层之上 */
|
|
|
+ z-index: 10; /* 增大 z-index */
|
|
|
}
|
|
|
|
|
|
+/* 隐藏侧边栏滚动条 */
|
|
|
.layout-aside::-webkit-scrollbar {
|
|
|
display: none;
|
|
|
}
|
|
|
@@ -586,6 +696,8 @@ const handleLogout = async () => {
|
|
|
border-radius: 8px;
|
|
|
font-weight: 600;
|
|
|
box-shadow: 0 2px 8px rgba(16, 146, 216, 0.25);
|
|
|
+ /* 修改点:增大 z-index 确保在背景层之上 */
|
|
|
+ z-index: 11; /* 激活项可以更高一点 */
|
|
|
}
|
|
|
|
|
|
.layout-content-wrapper {
|
|
|
@@ -594,8 +706,11 @@ const handleLogout = async () => {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
position: relative;
|
|
|
+ /* 修改点:增大 z-index 确保在背景层之上 */
|
|
|
+ z-index: 10; /* 增大 z-index */
|
|
|
}
|
|
|
|
|
|
+/* 强制重置 el-tabs header 高度/边距/背景/阴影,避免背景层穿透错位 */
|
|
|
.el-tabs__header.is-top {
|
|
|
height: 48px !important;
|
|
|
margin: 0 !important;
|
|
|
@@ -603,9 +718,11 @@ const handleLogout = async () => {
|
|
|
border: none !important;
|
|
|
background: transparent !important;
|
|
|
box-shadow: none !important;
|
|
|
- z-index: 0 !important;
|
|
|
+ /* 修改点:增大 z-index 确保在背景层之上 */
|
|
|
+ z-index: 10 !important; /* 增大 z-index */
|
|
|
}
|
|
|
|
|
|
+/* 全屏页面特殊处理 */
|
|
|
.layout-wrapper.full-screen .layout-main-container {
|
|
|
height: 100vh;
|
|
|
}
|
|
|
@@ -615,10 +732,31 @@ const handleLogout = async () => {
|
|
|
overflow: auto;
|
|
|
padding: 0 20px;
|
|
|
box-sizing: border-box;
|
|
|
- background-color: white;
|
|
|
+ background-color: white; /* 默认背景色 */
|
|
|
+ position: relative; /* 确保它可以参与 z-index 计算 */
|
|
|
+ /* 修改点:增大 z-index 确保在背景层之上 */
|
|
|
+ z-index: 10; /* 增大 z-index */
|
|
|
}
|
|
|
|
|
|
.scrollable-content.transparent-scroll {
|
|
|
background-color: transparent;
|
|
|
+ /* 修改点:增大 z-index 确保在背景层之上 */
|
|
|
+ z-index: 10; /* 即使透明也应保持在上层 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 左侧区域 */
|
|
|
+.left-section {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 24px;
|
|
|
+ flex-shrink: 0; /* 防止缩小 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 右侧区域 */
|
|
|
+.right-section {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: flex-end;
|
|
|
+ flex-shrink: 0; /* 防止缩小 */
|
|
|
}
|
|
|
</style>
|