This commit is contained in:
ChuXun
2026-01-29 03:53:39 +08:00
parent 670b7adba2
commit 8a87c3ebbc
3939 changed files with 3906 additions and 1528639 deletions

View File

@@ -0,0 +1,428 @@
<template>
<el-container class="layout-container" :class="layoutClass">
<el-header class="layout-header">
<div class="header-title">环境监督系统</div>
<div class="header-right">
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
{{ userInfoDisplay }}
<el-icon class="el-icon--right"><arrow-down /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
<el-container class="main-body-container">
<el-aside width="220px" class="layout-aside">
<el-menu
:default-active="$route.path"
class="el-menu-vertical"
@select="handleMenuSelect"
>
<template v-for="menu in visibleMenu" :key="menu.path">
<el-sub-menu v-if="menu.children && menu.children.length > 0" :index="menu.path">
<template #title>
<el-icon><component :is="menu.meta.icon" /></el-icon>
<span>{{ menu.meta.title }}</span>
</template>
<el-menu-item v-for="child in menu.children" :key="child.path" :index="child.path">
{{ child.meta.title }}
</el-menu-item>
</el-sub-menu>
<el-menu-item v-else :index="menu.path">
<el-icon><component :is="menu.meta.icon" /></el-icon>
<span>{{ menu.meta.title }}</span>
</el-menu-item>
</template>
</el-menu>
</el-aside>
<el-main class="layout-main">
<router-view v-slot="{ Component }">
<transition name="fade-transform" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</el-main>
</el-container>
</el-container>
</template>
<script setup lang="ts">
import {
HomeFilled,
InfoFilled,
ArrowDown,
Location,
List,
ChatDotSquare,
Setting,
User,
DataAnalysis,
Grid,
} from '@element-plus/icons-vue'
import { useRouter, useRoute } from 'vue-router'
import { useAuthStore } from '@/stores/auth';
import { computed, ref, markRaw, watch } from 'vue';
const router = useRouter();
const route = useRoute();
const authStore = useAuthStore();
const userRole = computed(() => authStore.user?.role);
const layoutClass = computed(() => {
if (userRole.value) {
// a simple convention would be `role-<role-name-in-lowercase>`
return `role-${userRole.value.toLowerCase()}`;
}
return '';
});
watch(
() => route.path,
(newPath, oldPath) => {
console.log(`路由从 ${oldPath} 切换到 ${newPath}`);
}
);
const allMenus = ref([
{
path: '/dashboard',
meta: { title: '仪表盘', icon: markRaw(DataAnalysis), roles: ['DECISION_MAKER', 'ADMIN'] }
},
{
path: '/map',
meta: { title: '网格地图', icon: markRaw(Grid), roles: ['ADMIN', 'DECISION_MAKER', 'SUPERVISOR', 'GRID_WORKER'] }
},
{
path: '/my-tasks',
meta: { title: '我的任务', icon: markRaw(List), roles: ['GRID_WORKER'] }
},
{
path: '/worker-map',
meta: { title: '地图管理', icon: markRaw(Grid), roles: ['GRID_WORKER'] }
},
{
path: '/submit-feedback',
meta: { title: '提交反馈', icon: markRaw(ChatDotSquare), roles: ['PUBLIC_SUPERVISOR'] }
},
{
path: '/feedback',
meta: { title: '我的反馈', icon: markRaw(List), roles: ['PUBLIC_SUPERVISOR'] }
},
{
path: '/feedback',
meta: { title: '反馈管理', icon: markRaw(ChatDotSquare), roles: ['ADMIN', 'SUPERVISOR'] }
},
{
path: '/system',
meta: { title: '系统管理', icon: markRaw(Setting), roles: ['ADMIN'] },
children: [
{ path: '/system/users', meta: { title: '人员管理', roles: ['ADMIN'] } },
{ path: '/system/roles', meta: { title: '操作日志', roles: ['ADMIN'] } },
]
},
{
path: '/settings',
meta: { title: '设置', icon: markRaw(User), roles: ['ADMIN', 'DECISION_MAKER', 'SUPERVISOR', 'GRID_WORKER', 'PUBLIC_SUPERVISOR'] },
children: [
{ path: '/settings/profile', meta: { title: '个人中心', roles: ['ADMIN', 'DECISION_MAKER', 'SUPERVISOR', 'GRID_WORKER', 'PUBLIC_SUPERVISOR'] } },
{ path: '/settings/my-logs', meta: { title: '我的操作日志', roles: ['ADMIN', 'DECISION_MAKER', 'SUPERVISOR', 'GRID_WORKER', 'PUBLIC_SUPERVISOR'] } },
{ path: '/settings/system', meta: { title: '系统设置', roles: ['ADMIN'] } },
]
},
]);
const visibleMenu = computed(() => {
if (!userRole.value) {
return [];
}
const role = userRole.value;
function filterMenu(menuItems: any[]) {
const accessibleMenu: any[] = [];
for (const item of menuItems) {
if (item.meta && item.meta.roles && item.meta.roles.includes(role)) {
const newItem = { ...item };
if (item.children) {
newItem.children = filterMenu(item.children);
if (newItem.children.length > 0 || newItem.path === '/settings') {
accessibleMenu.push(newItem);
}
} else {
accessibleMenu.push(newItem);
}
}
}
return accessibleMenu;
}
return filterMenu(allMenus.value);
});
const userInfoDisplay = computed(() => {
if (authStore.user) {
return `${authStore.user.name} (${authStore.user.role})`;
}
return '未登录';
});
const handleCommand = (command: string | number | object) => {
if (command === 'logout') {
authStore.logout();
router.push('/login');
}
}
const handleMenuSelect = (path: string) => {
console.log('菜单选择:', path);
if (route.path === '/grid-management' && path !== '/grid-management') {
console.log('从网格管理页面切换到其他页面使用location.href');
window.location.href = path;
} else {
router.push(path);
}
};
</script>
<style scoped>
.layout-container {
height: 100vh;
display: flex;
flex-direction: column;
/* The background color will be set by role-specific styles */
}
.layout-header {
/* background: #ffffff; */ /* Will be set by role-specific styles */
color: #303133;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 22px;
box-shadow: 0 1px 4px rgba(0,21,41,.08);
z-index: 10;
flex-shrink: 0;
}
.header-title {
font-weight: 600;
letter-spacing: 1px;
/* color: #004d40; */ /* Will be set by role-specific styles */
}
.header-right {
display: flex;
align-items: center;
}
.el-dropdown-link {
cursor: pointer;
color: #606266;
display: flex;
align-items: center;
font-size: 16px;
}
.main-body-container {
flex: 1;
overflow: hidden;
}
.layout-aside {
/* background-color: #ffffff; */ /* Will be set by role-specific styles */
box-shadow: 2px 0 6px rgba(0,21,41,.08);
transition: width 0.3s;
border-right: 1px solid #e6e6e6;
overflow-y: auto;
}
.el-menu {
border-right: none;
background-color: transparent;
}
.el-menu-item, .el-sub-menu__title {
/* color: #303133; */ /* Will be set by role-specific styles */
font-size: 15px;
height: 50px;
line-height: 50px;
}
.el-menu-item:hover, .el-sub-menu__title:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.el-menu-item.is-active {
/* background-color: #ecf5ff; */ /* Will be set by role-specific styles */
font-weight: bold;
}
.layout-main {
padding: 20px;
background-color: #f0f2f5;
flex: 1;
overflow-y: auto;
position: relative;
}
/* fade-transform */
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all 0.5s;
}
.fade-transform-enter-from {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>
<style>
/* Role-based theming */
/* ADMIN THEME (Dark Green) */
.role-admin .layout-header {
background-color: #2E7D32; /* Dark Green */
color: white;
}
.role-admin .header-title {
color: white;
}
.role-admin .el-dropdown-link {
color: white;
}
.role-admin .layout-aside {
background-color: #388E3C; /* Slightly lighter green */
}
.role-admin .el-menu-item,
.role-admin .el-sub-menu__title {
color: #E8F5E9; /* Muted green text */
}
.role-admin .el-menu-item:hover,
.role-admin .el-sub-menu__title:hover {
background-color: #45a049;
}
.role-admin .el-menu-item.is-active {
background-color: #2E7D32; /* Dark Green */
color: #fff;
}
/* DECISION_MAKER THEME (Corporate Green) */
.role-decision_maker .layout-header {
background-color: #00695C; /* Teal/Corporate Green */
color: white;
}
.role-decision_maker .header-title {
color: white;
}
.role-decision_maker .el-dropdown-link {
color: white;
}
.role-decision_maker .layout-aside {
background-color: #00796B;
}
.role-decision_maker .el-menu-item,
.role-decision_maker .el-sub-menu__title {
color: #B2DFDB;
}
.role-decision_maker .el-menu-item:hover,
.role-decision_maker .el-sub-menu__title:hover {
background-color: #00897b;
}
.role-decision_maker .el-menu-item.is-active {
background-color: #00695C;
color: #fff;
}
/* GRID_WORKER THEME (Vibrant Green) */
.role-grid_worker .layout-header {
background-color: #689F38; /* Vibrant Green */
color: white;
}
.role-grid_worker .header-title {
color: white;
}
.role-grid_worker .el-dropdown-link {
color: white;
}
.role-grid_worker .layout-aside {
background-color: #7CB342;
}
.role-grid_worker .el-menu-item,
.role-grid_worker .el-sub-menu__title {
color: #F1F8E9;
}
.role-grid_worker .el-menu-item:hover,
.role-grid_worker .el-sub-menu__title:hover {
background-color: #8bc34a;
}
.role-grid_worker .el-menu-item.is-active {
background-color: #689F38;
color: #fff;
}
/* SUPERVISOR THEME (Calming Green) */
.role-supervisor .layout-header {
background-color: #39796b; /* Calming Green */
color: white;
}
.role-supervisor .header-title {
color: white;
}
.role-supervisor .el-dropdown-link {
color: white;
}
.role-supervisor .layout-aside {
background-color: #4DB6AC;
}
.role-supervisor .el-menu-item,
.role-supervisor .el-sub-menu__title {
color: #E0F2F1;
}
.role-supervisor .el-menu-item:hover,
.role-supervisor .el-sub-menu__title:hover {
background-color: #26a69a;
}
.role-supervisor .el-menu-item.is-active {
background-color: #39796b;
color: #fff;
}
/* PUBLIC_SUPERVISOR THEME (Light Green) */
.role-public_supervisor .layout-header {
background-color: #AED581; /* Light Green */
color: #333;
}
.role-public_supervisor .header-title {
color: #333;
}
.role-public_supervisor .el-dropdown-link {
color: #333;
}
.role-public_supervisor .layout-aside {
background-color: #DCEDC8;
}
.role-public_supervisor .el-menu-item,
.role-public_supervisor .el-sub-menu__title {
color: #556B2F;
}
.role-public_supervisor .el-menu-item:hover,
.role-public_supervisor .el-sub-menu__title:hover {
background-color: #9ccc65;
}
.role-public_supervisor .el-menu-item.is-active {
background-color: #AED581;
color: #333;
}
</style>