From d5ef0fe419c0b3a83c3c73a63fb54353572a8103 Mon Sep 17 00:00:00 2001
From: wujingjing <gersonwu@qq.com>
Date: 星期二, 01 四月 2025 17:48:37 +0800
Subject: [PATCH] 修改布局

---
 customer_list/ch/static/config/route.js            |   19 +
 src/views/project/ch/workspace/situation/index.vue |   16 +
 src/components/chat/Chat.vue                       |    3 
 src/views/error/404.vue                            |    4 
 src/views/project/ch/gis/situation/index.vue       |   16 +
 src/layout/component/sidebar/GisMenu.vue           |  234 +++++++++++++++++++
 src/layout/component/header/Header.vue             |   74 ++++-
 src/layout/component/main.vue                      |   23 +
 src/api/menu/menuData.ts                           |   66 +++++
 src/layout/component/sidebar/types.ts              |    7 
 src/layout/component/sidebar/WorkSpaceMenu.vue     |  234 +++++++++++++++++++
 11 files changed, 671 insertions(+), 25 deletions(-)

diff --git a/customer_list/ch/static/config/route.js b/customer_list/ch/static/config/route.js
index 319df7c..122790c 100644
--- a/customer_list/ch/static/config/route.js
+++ b/customer_list/ch/static/config/route.js
@@ -8,6 +8,25 @@
 		redirect: null,
 		showTitle: false,
 	},
+	{
+		name: 'WorkspaceSituation',
+		isKeepAlive: true,
+		isAffix: false,
+		path: '/workspace/situation',
+		component: '/project/ch/workspace/situation/index.vue',
+		redirect: null,
+		showTitle: true,
+	},
+	
+	{
+		name: 'GisSituation',
+		isKeepAlive: true,
+		isAffix: false,
+		path: '/gis/situation',
+		component: '/project/ch/gis/situation/index.vue',
+		redirect: null,
+		showTitle: true,
+	},
 	//搴旂敤鍦烘櫙(涓诲満鏅牴鎹綋鍓岻D鏄剧ず娆″満鏅�)
 	{
 		name: 'Scenario',
diff --git a/src/api/menu/menuData.ts b/src/api/menu/menuData.ts
index dcf099f..2e379eb 100644
--- a/src/api/menu/menuData.ts
+++ b/src/api/menu/menuData.ts
@@ -16,6 +16,70 @@
 		Description: '',
 	},
 	{
+		Children: [
+			{
+				ID: '1805430930840621056',
+				ParentID: '0',
+				Type: 2,
+				Name: '姒傚喌',
+				Path: '/workspace/situation',
+				Permission: '',
+				Icon: 'ywicon icon-a-appround15',
+				IsIframe: false,
+				OutLink: '',
+				IsHide: false,
+				Weight: 0,
+				SortCode: 1,
+				Description: '',
+			},
+		],
+		ID: '1805430930840621056',
+		ParentID: '0',
+		Type: 1,
+		Name: '涓汉宸ヤ綔鍙�',
+		Path: '/workspace',
+		Permission: '',
+		Icon: 'ywicon icon-a-appround15',
+		IsIframe: false,
+		OutLink: '',
+		IsHide: false,
+		Weight: 0,
+		SortCode: 1,
+		Description: '',
+	},
+	{
+		Children: [
+			{
+				ID: '1805430930840621056',
+				ParentID: '0',
+				Type: 2,
+				Name: '姒傚喌',
+				Path: '/gis/situation',
+				Permission: '',
+				Icon: 'ywicon icon-a-appround15',
+				IsIframe: false,
+				OutLink: '',
+				IsHide: false,
+				Weight: 0,
+				SortCode: 1,
+				Description: '',
+			},
+		],
+		ID: '1805430930840621056',
+		ParentID: '0',
+		Type: 1,
+		Name: 'GIS绯荤粺',
+		Path: '/gis',
+		Permission: '',
+		Icon: 'ywicon icon-a-appround15',
+		IsIframe: false,
+		OutLink: '',
+		IsHide: false,
+		Weight: 0,
+		SortCode: 1,
+		Description: '',
+	},
+	{
 		Children: [],
 		ID: '1805430930840621056',
 		ParentID: '0',
@@ -111,4 +175,4 @@
 		SortCode: 5,
 		Description: '',
 	},
-];
\ No newline at end of file
+];
diff --git a/src/components/chat/Chat.vue b/src/components/chat/Chat.vue
index accfaf2..9569345 100644
--- a/src/components/chat/Chat.vue
+++ b/src/components/chat/Chat.vue
@@ -48,7 +48,6 @@
 <script setup lang="ts">
 import type { CancelTokenSource } from 'axios';
 import axios from 'axios';
-import { orderBy } from 'lodash-es';
 import moment from 'moment';
 import { computed, nextTick, onActivated, onMounted, ref } from 'vue';
 import { loadAmisSource } from '../amis/load';
@@ -60,7 +59,7 @@
 import CustomDrawer from '/@/components/drawer/CustomDrawer.vue';
 import { Logger } from '/@/model/logger/Logger';
 import { triggerRef } from 'vue';
-import { ElLoadingService, ElMessage } from 'element-plus';
+import {  ElMessage } from 'element-plus';
 import ChatContainer from './components/ChatContainer.vue';
 import ShareLinkDlg from './components/shareLink/index.vue';
 import router from '/@/router';
diff --git a/src/layout/component/header/Header.vue b/src/layout/component/header/Header.vue
index 980d7e6..f0f7c8f 100644
--- a/src/layout/component/header/Header.vue
+++ b/src/layout/component/header/Header.vue
@@ -1,17 +1,19 @@
 <template>
 	<div class="top_text flex justify-between px-6 items-center" :class="sidebarIsShow ? 'px-6' : 'pl-[unset] pr-6'">
 		<div class="flex-items-center">
-			<div
-				class="flex-items-center space-x-3 mr-4 pr-4 border border-solid border-r-1 border-l-0 border-y-0 border-gray-300"
-				v-if="!sidebarIsShow"
-			></div>
-			<div v-if="routerMeta.showTitle" class="font-bold flex items-center">
-				<span class="flex-center cursor-pointer" v-if="routerMeta.showBack" @click="goBack">
-					<SvgIcon name="ele-ArrowLeft" />
-				</span>
-				<span class="">
-					{{ routerMeta.title }}
-				</span>
+			<div class="nav-menu">
+				<router-link :to="{ path: '/ask_answer', query: { id: activeRoomId } }" class="nav-item" active-class="active">
+					<i class="icon-park-outline-robot"></i>
+					鏅鸿兘鍔╂墜
+				</router-link>
+				<router-link to="/workspace/situation" class="nav-item" active-class="active">
+					<i class="icon-park-outline-workbench"></i>
+					涓汉宸ヤ綔鍙�
+				</router-link>
+				<router-link to="/gis/situation" class="nav-item" active-class="active">
+					<i class="icon-park-outline-system"></i>
+					GIS绯荤粺
+				</router-link>
 			</div>
 		</div>
 		<el-dialog
@@ -58,16 +60,17 @@
 
 <script setup lang="ts">
 import { onClickOutside } from '@vueuse/core';
+import { storeToRefs } from 'pinia';
 import { computed, onMounted, onUnmounted, reactive, ref } from 'vue';
 import { systemNotifyList } from '/@/api/ai/chat';
 import router from '/@/router';
-import { isSharePage, newChatRoomClick } from '/@/stores/chatRoom';
-import emitter from '/@/utils/mitt';
-import { storeToRefs } from 'pinia';
 import pinia from '/@/stores';
+import { activeRoomId, isSharePage, newChatRoomClick } from '/@/stores/chatRoom';
 import { useThemeConfig } from '/@/stores/themeConfig';
-import { Local } from '/@/utils/storage';
+import emitter from '/@/utils/mitt';
 import { userInfoKey } from '/@/utils/request';
+import { Local } from '/@/utils/storage';
+
 const props = defineProps(['sidebarIsShow']);
 let state = reactive({
 	isShowAnnouncement: false,
@@ -76,6 +79,9 @@
 	announcementContent: '',
 	announcementTime: '',
 });
+
+
+
 //#region ====================== 鍏憡鏄惁鐪嬭繃 ======================
 const userInfo = ref(Local.get(userInfoKey));
 const readKey = `announcementIsRead_${userInfo.value?.id}`;
@@ -107,9 +113,6 @@
 const globalTitle = computed(() => themeConfig.value.globalTitle);
 
 const setHeaderTitle = (title: string) => {
-	// routerMeta.value.title = title;
-	// triggerRef(routerMeta);
-
 	document.title = `${title} - ${globalTitle.value}`;
 };
 
@@ -261,4 +264,39 @@
 	padding: 10px 20px 20px;
 	box-sizing: border-box;
 }
+
+.nav-menu {
+	display: flex;
+	align-items: center;
+	height: 100%;
+	gap: 8px;
+
+	.nav-item {
+		display: flex;
+		align-items: center;
+		padding: 0 16px;
+		height: 100%;
+		color: var(--el-text-color-regular);
+		text-decoration: none;
+		font-size: 14px;
+		transition: all 0.3s ease;
+		border-bottom: 2px solid transparent;
+		gap: 4px;
+
+		i {
+			font-size: 16px;
+		}
+
+		&:hover {
+			color: var(--el-color-primary);
+			background-color: rgba(var(--el-color-primary-rgb), 0.1);
+		}
+
+		&.active {
+			color: var(--el-color-primary);
+			border-bottom-color: var(--el-color-primary);
+			background-color: rgba(var(--el-color-primary-rgb), 0.1);
+		}
+	}
+}
 </style>
diff --git a/src/layout/component/main.vue b/src/layout/component/main.vue
index c0bcfa2..8a8eded 100644
--- a/src/layout/component/main.vue
+++ b/src/layout/component/main.vue
@@ -9,8 +9,13 @@
 			wrap-class="layout-main-scroll  flex"
 			view-class="layout-main-scroll bg-[var(--color-bg-side)]  flex h100 w-full"
 		>
-			<SideBar v-if="!isSharePage && sidebarIsShow" :isShow="sidebarIsShow" @toggleSidebar="toggleSidebar" />
-			<SidebarOther v-if="!isSharePage && !sidebarIsShow" :isShow="!sidebarIsShow" @toggleSidebar="toggleSidebar" />
+			<WorkSpaceMenu v-show="isWorkSpace" />
+			<div v-show="isAskAnswer">
+				<SideBar v-if="!isSharePage && sidebarIsShow" :isShow="sidebarIsShow" @toggleSidebar="toggleSidebar" />
+				<SidebarOther v-if="!isSharePage && !sidebarIsShow" :isShow="!sidebarIsShow" @toggleSidebar="toggleSidebar" />
+			</div>
+			<GisMenu v-show="isGis" />
+
 			<div
 				class="flex-auto flex-col flex right-container"
 				:class="{
@@ -45,9 +50,23 @@
 import { useThemeConfig } from '/@/stores/themeConfig';
 import { NextLoading } from '/@/utils/loading';
 import { Local } from '/@/utils/storage';
+import WorkSpaceMenu from './sidebar/WorkSpaceMenu.vue';
+import GisMenu from './sidebar/GisMenu.vue';
+import router from '/@/router/index';
 // 寮曞叆缁勪欢
 const LayoutParentView = defineAsyncComponent(() => import('/@/layout/routerView/parent.vue'));
 const LayoutFooter = defineAsyncComponent(() => import('/@/layout/footer/index.vue'));
+
+const isWorkSpace = computed(() => {
+	return router.currentRoute.value.path.startsWith('/workspace');
+});
+const isAskAnswer = computed(() => {
+	return router.currentRoute.value.path.startsWith('/ask_answer');
+});
+const isGis = computed(() => {
+	return router.currentRoute.value.path.startsWith('/gis');
+});
+
 // 瀹氫箟鍙橀噺鍐呭
 const layoutMainScrollbarRef = ref();
 const route = useRoute();
diff --git a/src/layout/component/sidebar/GisMenu.vue b/src/layout/component/sidebar/GisMenu.vue
new file mode 100644
index 0000000..88205bf
--- /dev/null
+++ b/src/layout/component/sidebar/GisMenu.vue
@@ -0,0 +1,234 @@
+<template>
+	<div class="pc-chat_aside flex-0 relative" :style="`width:252px;transition: 0.7s ease-in;`">
+		<!-- Logo 閮ㄥ垎 -->
+		<div class="h-[40px] w-full bg-[#0084ff] flex justify-between items-center text-white px-2">
+			<span>GIS绯荤粺</span>
+			<!-- <span>瀹樼綉GIS</span> -->
+		</div>
+		<!-- 鑿滃崟椤� -->
+		<div class="menu-container">
+			<el-menu :default-active="activeId" class="wi-menu !w-full" :unique-opened="true" @select="handleSelect">
+				<template v-for="item in menuData">
+					<el-sub-menu :key="`sub-${item.id}`" v-if="item.children?.length" :index="item.id">
+						<template #title>
+							<i v-if="item.icon" :class="item.icon"></i>
+							<span>{{ item.title }}</span>
+						</template>
+
+						<template v-for="subItem in item.children">
+							<el-sub-menu :key="`sub-${subItem.id}`" v-if="subItem.children?.length" :index="subItem.id">
+								<template #title>
+									<i v-if="subItem.icon" :class="subItem.icon"></i>
+									<span>{{ subItem.title }}</span>
+								</template>
+								<!-- 涓夌骇鑿滃崟椤� -->
+								<el-menu-item v-for="child in subItem.children" :key="`item-${child.id}`" :index="child.id">
+									<i v-if="child.icon" :class="child.icon"></i>
+									<span>{{ child.title }}</span>
+								</el-menu-item>
+							</el-sub-menu>
+							<!-- 浜岀骇鑿滃崟椤� -->
+							<el-menu-item v-else :key="`item-${subItem.id}`" :index="subItem.id">
+								<i v-if="subItem.icon" :class="subItem.icon"></i>
+								<span>{{ subItem.title }}</span>
+							</el-menu-item>
+						</template>
+					</el-sub-menu>
+					<el-menu-item v-else :key="`item-${item.id}`" :index="item.id">
+						<i v-if="item.icon" :class="item.icon"></i>
+						<span>{{ item.title }}</span>
+					</el-menu-item>
+				</template>
+			</el-menu>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, onMounted, watch } from 'vue';
+import { useRouter } from 'vue-router';
+import type { MenuItemData } from './types';
+
+const router = useRouter();
+const activeId = ref('');
+
+// 绀轰緥鑿滃崟鏁版嵁
+const menuData = ref<MenuItemData[]>([
+	{
+		id: 'menu1',
+		title: '棣栭〉',
+		icon: 'icon-park-outline-chart-line',
+		path: '/gis/situation',
+	},
+	{
+		id: 'menu2',
+		title: '缁熻',
+		icon: 'icon-park-outline-folder',
+		children: [
+			{
+				id: '鑷畾涔夌粺璁�',
+				title: '鑷畾涔夌粺璁�',
+				path: '/gis/situation',
+			},
+			{
+				id: 'menu2-2',
+				title: '绠$嚎缁熻',
+				path: '/gis/situation',
+			},
+		],
+	},
+]);
+
+// 璁$畻灞炴�э細鏈夊瓙鑿滃崟鐨勯」
+const parentMenuItems = computed(() => menuData.value.filter((item) => item.children?.length));
+
+// 璁$畻灞炴�э細娌℃湁瀛愯彍鍗曠殑椤�
+const leafMenuItems = computed(() => menuData.value.filter((item) => !item.children?.length));
+
+const handleSelect = (index: string) => {
+	activeId.value = index;
+	const findPath = (items: MenuItemData[]): string | undefined => {
+		for (const item of items) {
+			if (item.id === index) return item.path;
+			if (item.children) {
+				const path = findPath(item.children);
+				if (path) return path;
+			}
+		}
+	};
+	console.log('馃殌 ~ index:', index);
+	const path = findPath(menuData.value);
+	if (path) {
+		router.push(path);
+	}
+};
+
+// 閫氳繃 path 鎵� index
+const findIndex = (items: MenuItemData[]): string | undefined => {
+	for (const item of items) {
+		if (item.path === router.currentRoute.value.path) return item.id;
+		if (item.children) {
+			const index = findIndex(item.children);
+			if (index) return index;
+		}
+	}
+};
+watch(router.currentRoute, (newRoute) => {
+	activeId.value = findIndex(menuData.value) || '';
+});
+</script>
+
+<style scoped lang="scss">
+.pc-chat_aside {
+	height: 100%;
+	box-sizing: border-box;
+	background-color: var(--color-bg-side);
+	overflow: visible;
+	transition: width 0.1s ease-in;
+	position: relative;
+	display: flex;
+	flex-direction: column;
+}
+
+.aside_top {
+	box-sizing: border-box;
+	position: relative;
+	width: 100%;
+	padding: 18px;
+	border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.layout-logo-medium-img {
+	width: 28px;
+	margin-right: 7px;
+}
+
+.menu-container {
+	flex: 1;
+	overflow-y: auto;
+
+	:deep(.wi-menu) {
+		border-right: none;
+		background-color: transparent;
+		--hover-menu-bg: rgba(255, 255, 255, 0.1);
+		--menu-width: 100% !important;
+
+		--active-menu-bg: #333534 !important;
+		--active-menu-color: var(--color-btn-base) !important;
+
+		.el-menu {
+			background-color: transparent;
+			width: var(--menu-width) !important;
+		}
+
+		// 榧犳爣 hover 鏃堕鑹�
+
+		.el-menu-item {
+			height: 40px;
+			line-height: 40px;
+			color: #ccc;
+
+			&:hover {
+				background: var(--hover-menu-bg) !important;
+			}
+
+			&.is-active {
+				background: var(--active-menu-bg) !important;
+				color: var(--active-menu-color);
+			}
+
+			i {
+				margin-right: 8px;
+			}
+		}
+
+		.el-sub-menu {
+			.el-menu {
+				width: var(--menu-width) !important;
+			}
+			--next-bg-menuBarActiveColor: unset !important;
+
+			.el-sub-menu__title {
+				height: 40px;
+				line-height: 40px;
+				color: #ccc;
+
+				&:hover {
+					background: var(--hover-menu-bg) !important;
+				}
+
+				i {
+					margin-right: 8px;
+				}
+			}
+
+			&.is-active {
+				.el-sub-menu__title {
+					color: var(--active-menu-color);
+				}
+			}
+		}
+
+		// 瀛愯彍鍗曞脊鍑哄眰鏍峰紡
+		.el-menu--popup {
+			background-color: var(--color-bg-side);
+			border: 1px solid rgba(255, 255, 255, 0.1);
+			padding: 0;
+
+			.el-menu-item {
+				background-color: transparent;
+				color: #ccc;
+
+				&:hover {
+					background-color: rgba(255, 255, 255, 0.1);
+				}
+
+				&.is-active {
+					background: var(--active-menu-bg);
+					color: var(--active-menu-color);
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/src/layout/component/sidebar/WorkSpaceMenu.vue b/src/layout/component/sidebar/WorkSpaceMenu.vue
new file mode 100644
index 0000000..fbc25f6
--- /dev/null
+++ b/src/layout/component/sidebar/WorkSpaceMenu.vue
@@ -0,0 +1,234 @@
+<template>
+	<div class="pc-chat_aside flex-0 relative" :style="`width:252px;transition: 0.7s ease-in;`">
+		<!-- Logo 閮ㄥ垎 -->
+		<div class="h-[40px] w-full bg-[#0084ff] flex justify-between items-center text-white px-2">
+			<span>瀹樼綉GIS</span>
+			<!-- <span>瀹樼綉GIS</span> -->
+		</div>
+		<!-- 鑿滃崟椤� -->
+		<div class="menu-container">
+			<el-menu :default-active="activeId" class="wi-menu !w-full" :unique-opened="true" @select="handleSelect">
+				<template v-for="item in menuData">
+					<el-sub-menu :key="`sub-${item.id}`" v-if="item.children?.length" :index="item.id">
+						<template #title>
+							<i v-if="item.icon" :class="item.icon"></i>
+							<span>{{ item.title }}</span>
+						</template>
+
+						<template v-for="subItem in item.children">
+							<el-sub-menu :key="`sub-${subItem.id}`" v-if="subItem.children?.length" :index="subItem.id">
+								<template #title>
+									<i v-if="subItem.icon" :class="subItem.icon"></i>
+									<span>{{ subItem.title }}</span>
+								</template>
+								<!-- 涓夌骇鑿滃崟椤� -->
+								<el-menu-item v-for="child in subItem.children" :key="`item-${child.id}`" :index="child.id">
+									<i v-if="child.icon" :class="child.icon"></i>
+									<span>{{ child.title }}</span>
+								</el-menu-item>
+							</el-sub-menu>
+							<!-- 浜岀骇鑿滃崟椤� -->
+							<el-menu-item v-else :key="`item-${subItem.id}`" :index="subItem.id">
+								<i v-if="subItem.icon" :class="subItem.icon"></i>
+								<span>{{ subItem.title }}</span>
+							</el-menu-item>
+						</template>
+					</el-sub-menu>
+					<el-menu-item v-else :key="`item-${item.id}`" :index="item.id">
+						<i v-if="item.icon" :class="item.icon"></i>
+						<span>{{ item.title }}</span>
+					</el-menu-item>
+				</template>
+			</el-menu>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, onMounted, watch } from 'vue';
+import { useRouter } from 'vue-router';
+import type { MenuItemData } from './types';
+
+const router = useRouter();
+const activeId = ref('');
+
+// 绀轰緥鑿滃崟鏁版嵁
+const menuData = ref<MenuItemData[]>([
+	{
+		id: 'menu1',
+		title: '棣栭〉',
+		icon: 'icon-park-outline-chart-line',
+		path: '/workspace/situation',
+	},
+	{
+		id: 'menu2',
+		title: '缁熻',
+		icon: 'icon-park-outline-folder',
+		children: [
+			{
+				id: '鑷畾涔夌粺璁�',
+				title: '鑷畾涔夌粺璁�',
+				path: '/workspace/situation',
+			},
+			{
+				id: 'menu2-2',
+				title: '绠$嚎缁熻',
+				path: '/workspace/situation',
+			},
+		],
+	},
+]);
+
+// 璁$畻灞炴�э細鏈夊瓙鑿滃崟鐨勯」
+const parentMenuItems = computed(() => menuData.value.filter((item) => item.children?.length));
+
+// 璁$畻灞炴�э細娌℃湁瀛愯彍鍗曠殑椤�
+const leafMenuItems = computed(() => menuData.value.filter((item) => !item.children?.length));
+
+const handleSelect = (index: string) => {
+	activeId.value = index;
+	const findPath = (items: MenuItemData[]): string | undefined => {
+		for (const item of items) {
+			if (item.id === index) return item.path;
+			if (item.children) {
+				const path = findPath(item.children);
+				if (path) return path;
+			}
+		}
+	};
+	console.log('馃殌 ~ index:', index);
+	const path = findPath(menuData.value);
+	if (path) {
+		router.push(path);
+	}
+};
+
+// 閫氳繃 path 鎵� index
+const findIndex = (items: MenuItemData[]): string | undefined => {
+	for (const item of items) {
+		if (item.path === router.currentRoute.value.path) return item.id;
+		if (item.children) {
+			const index = findIndex(item.children);
+			if (index) return index;
+		}
+	}
+};
+watch(router.currentRoute, (newRoute) => {
+	activeId.value = findIndex(menuData.value) || '';
+});
+</script>
+
+<style scoped lang="scss">
+.pc-chat_aside {
+	height: 100%;
+	box-sizing: border-box;
+	background-color: var(--color-bg-side);
+	overflow: visible;
+	transition: width 0.1s ease-in;
+	position: relative;
+	display: flex;
+	flex-direction: column;
+}
+
+.aside_top {
+	box-sizing: border-box;
+	position: relative;
+	width: 100%;
+	padding: 18px;
+	border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.layout-logo-medium-img {
+	width: 28px;
+	margin-right: 7px;
+}
+
+.menu-container {
+	flex: 1;
+	overflow-y: auto;
+
+	:deep(.wi-menu) {
+		border-right: none;
+		background-color: transparent;
+		--hover-menu-bg: rgba(255, 255, 255, 0.1);
+		--menu-width: 100% !important;
+
+		--active-menu-bg: #333534 !important;
+		--active-menu-color: var(--color-btn-base) !important;
+
+		.el-menu {
+			background-color: transparent;
+			width: var(--menu-width) !important;
+		}
+
+		// 榧犳爣 hover 鏃堕鑹�
+
+		.el-menu-item {
+			height: 40px;
+			line-height: 40px;
+			color: #ccc;
+
+			&:hover {
+				background: var(--hover-menu-bg) !important;
+			}
+
+			&.is-active {
+				background: var(--active-menu-bg) !important;
+				color: var(--active-menu-color);
+			}
+
+			i {
+				margin-right: 8px;
+			}
+		}
+
+		.el-sub-menu {
+			.el-menu {
+				width: var(--menu-width) !important;
+			}
+			--next-bg-menuBarActiveColor: unset !important;
+
+			.el-sub-menu__title {
+				height: 40px;
+				line-height: 40px;
+				color: #ccc;
+
+				&:hover {
+					background: var(--hover-menu-bg) !important;
+				}
+
+				i {
+					margin-right: 8px;
+				}
+			}
+
+			&.is-active {
+				.el-sub-menu__title {
+					color: var(--active-menu-color);
+				}
+			}
+		}
+
+		// 瀛愯彍鍗曞脊鍑哄眰鏍峰紡
+		.el-menu--popup {
+			background-color: var(--color-bg-side);
+			border: 1px solid rgba(255, 255, 255, 0.1);
+			padding: 0;
+
+			.el-menu-item {
+				background-color: transparent;
+				color: #ccc;
+
+				&:hover {
+					background-color: rgba(255, 255, 255, 0.1);
+				}
+
+				&.is-active {
+					background: var(--active-menu-bg);
+					color: var(--active-menu-color);
+				}
+			}
+		}
+	}
+}
+</style>
diff --git a/src/layout/component/sidebar/types.ts b/src/layout/component/sidebar/types.ts
new file mode 100644
index 0000000..5f953e4
--- /dev/null
+++ b/src/layout/component/sidebar/types.ts
@@ -0,0 +1,7 @@
+export interface MenuItemData {
+	id: string;
+	title: string;
+	icon?: string;
+	children?: MenuItemData[];
+	path?: string;
+}
diff --git a/src/views/error/404.vue b/src/views/error/404.vue
index 886a1c1..39ad3a3 100644
--- a/src/views/error/404.vue
+++ b/src/views/error/404.vue
@@ -7,9 +7,9 @@
 						<div class="left-item-animation left-item-num">404</div>
 						<div class="left-item-animation left-item-title">{{ $t('message.notFound.foundTitle') }}</div>
 						<div class="left-item-animation left-item-msg">{{ $t('message.notFound.foundMsg') }}</div>
-						<div class="left-item-animation left-item-btn">
+						<!-- <div class="left-item-animation left-item-btn">
 							<el-button type="primary" size="default" round @click="onGoHome">{{ $t('message.notFound.foundBtn') }}</el-button>
-						</div>
+						</div> -->
 					</div>
 				</div>
 				<div class="right">
diff --git a/src/views/project/ch/gis/situation/index.vue b/src/views/project/ch/gis/situation/index.vue
new file mode 100644
index 0000000..3695295
--- /dev/null
+++ b/src/views/project/ch/gis/situation/index.vue
@@ -0,0 +1,16 @@
+<template>
+    <div class="gis-situation">
+        <div class="gis-situation-header">
+            <div class="gis-situation-header-title">
+                <span>GIS绯荤粺姒傚喌</span>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup lang="ts" name="GisSituation">
+import { ref } from 'vue';
+
+
+</script>
+<style scoped lang="scss"></style>
diff --git a/src/views/project/ch/workspace/situation/index.vue b/src/views/project/ch/workspace/situation/index.vue
new file mode 100644
index 0000000..c7b6c7a
--- /dev/null
+++ b/src/views/project/ch/workspace/situation/index.vue
@@ -0,0 +1,16 @@
+<template>
+    <div class="workspace-situation">
+        <div class="workspace-situation-header">
+            <div class="workspace-situation-header-title">
+                <span>涓汉宸ヤ綔鍙版鍐�</span>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup lang="ts" name="WorkspaceSituation">
+import { ref } from 'vue';
+
+
+</script>
+<style scoped lang="scss"></style>

--
Gitblit v1.9.3