|
@@ -1,30 +1,38 @@
|
|
|
<template>
|
|
|
<div class="commonHeader">
|
|
|
- <div style="width: 155px">
|
|
|
+ <!-- Logo区域 -->
|
|
|
+ <div class="logo-area" @click="gotoMain">
|
|
|
<svg-icon
|
|
|
- v-if="routeMeta.back"
|
|
|
- icon-class="back"
|
|
|
- size="48"
|
|
|
- @click="commonBack"
|
|
|
+ icon-class="pdm-logo"
|
|
|
+ size="64"
|
|
|
+ style="height: 70px; width: 90px"
|
|
|
/>
|
|
|
- <!-- <svg-icon v-else icon-class="LOGO" style="height: 48px; width: 155px" /> -->
|
|
|
+ <h2 style="margin: 0;">综合管理平台</h2>
|
|
|
</div>
|
|
|
- <div class="middle-title">
|
|
|
- {{ routeMeta.title }}
|
|
|
+
|
|
|
+ <!-- 导航选项卡 -->
|
|
|
+ <div class="tab-container">
|
|
|
+ <div
|
|
|
+ v-for="tab in availableTabs"
|
|
|
+ :key="tab.name"
|
|
|
+ class="tab"
|
|
|
+ :class="{ active: activeTab === tab.name }"
|
|
|
+ @click="navigateTo(tab)"
|
|
|
+ >
|
|
|
+ {{ tab.name }}
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div v-if="routeMeta.back"></div>
|
|
|
- <div v-else>
|
|
|
+
|
|
|
+ <!-- 用户功能区 -->
|
|
|
+ <div class="user-area">
|
|
|
<el-space>
|
|
|
- <div>
|
|
|
- <svg-icon
|
|
|
- class="activeNotice"
|
|
|
- icon-class="lingdang"
|
|
|
- size="48"
|
|
|
- @click="messageStatus = !messageStatus"
|
|
|
- />
|
|
|
- </div>
|
|
|
- <div class="task"></div>
|
|
|
- <div>
|
|
|
+ <svg-icon
|
|
|
+ class="activeNotice"
|
|
|
+ icon-class="lingdang"
|
|
|
+ size="48"
|
|
|
+ @click="messageStatus = !messageStatus"
|
|
|
+ />
|
|
|
+ <div class="user-info">
|
|
|
<div class="name">{{ userStore.user.username }}</div>
|
|
|
<div class="work">{{ userStore.user.station }}</div>
|
|
|
</div>
|
|
@@ -33,30 +41,20 @@
|
|
|
trigger="contextmenu"
|
|
|
@command="handleCommand"
|
|
|
>
|
|
|
- <img
|
|
|
- v-if="userStore.user.avatar"
|
|
|
- :src="baseUrl + userStore.user.avatar"
|
|
|
- fit="cover"
|
|
|
- style="
|
|
|
- width: 48px;
|
|
|
- height: 48px;
|
|
|
- border-radius: 24px;
|
|
|
- border: 1px solid #ccc;
|
|
|
- "
|
|
|
- @click="showClick"
|
|
|
- />
|
|
|
- <svg-icon
|
|
|
- v-else
|
|
|
- icon-class="head"
|
|
|
- size="48"
|
|
|
- style="
|
|
|
- width: 48px;
|
|
|
- height: 48px;
|
|
|
- border-radius: 24px;
|
|
|
- border: 1px solid #ccc;
|
|
|
- "
|
|
|
- @click="showClick"
|
|
|
- />
|
|
|
+ <div class="avatar-wrapper">
|
|
|
+ <img
|
|
|
+ v-if="userStore.user.avatar"
|
|
|
+ :src="baseUrl + userStore.user.avatar"
|
|
|
+ class="avatar"
|
|
|
+ @click="showClick"
|
|
|
+ />
|
|
|
+ <svg-icon
|
|
|
+ v-else
|
|
|
+ icon-class="head"
|
|
|
+ class="avatar"
|
|
|
+ @click="showClick"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
<template #dropdown>
|
|
|
<el-dropdown-menu style="width: 150px">
|
|
|
<el-dropdown-item command="change">修改密码</el-dropdown-item>
|
|
@@ -66,49 +64,96 @@
|
|
|
</el-dropdown>
|
|
|
</el-space>
|
|
|
</div>
|
|
|
+
|
|
|
+ <!-- IP配置抽屉 -->
|
|
|
+ <el-drawer
|
|
|
+ v-model="drawerVisible"
|
|
|
+ :destroy-on-close="true"
|
|
|
+ title="配置IP地址"
|
|
|
+ >
|
|
|
+ <Addresss @finish="saveAddressFinish"/>
|
|
|
+ </el-drawer>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-// import dayjs from "dayjs";
|
|
|
-import type { DropdownInstance } from "element-plus";
|
|
|
+import {ref, computed} from 'vue';
|
|
|
+import {useRouter, useRoute} from 'vue-router';
|
|
|
import { logoutApi } from "@/api/auth";
|
|
|
import { useUserStore } from "@/store";
|
|
|
+import Addresss from "@/views/sets/address.vue";
|
|
|
|
|
|
+const router = useRouter();
|
|
|
+const route = useRoute();
|
|
|
const userStore = useUserStore();
|
|
|
|
|
|
const baseUrl = import.meta.env.VITE_APP_UPLOAD_URL;
|
|
|
+const drawerVisible = ref(false);
|
|
|
+const messageStatus = ref(false);
|
|
|
+const activeTab = ref('');
|
|
|
+const dropdown1 = ref();
|
|
|
+const showClick = () => {
|
|
|
+ if (!dropdown1.value) return;
|
|
|
+ dropdown1.value.handleOpen();
|
|
|
+};
|
|
|
+// 所有可能的选项卡
|
|
|
+const allTabs = [
|
|
|
+ {name: '首页', handler: () => router.push("/main")},
|
|
|
+ {name: '用户管理', permission: 'canSetPermission', handler: () => router.push("/users")},
|
|
|
+ {name: '角色管理', permission: 'setRoles', handler: () => router.push("/roles")},
|
|
|
+ {name: '消息管理', permission: 'canSetMessageOrg', handler: () => router.push("/messageOrg")},
|
|
|
+ {name: '文件管理', permission: 'canFileShare', handler: () => router.push("/files")},
|
|
|
+ {name: '配置IP', permission: 'canSetIP', handler: () => openDrawer()}
|
|
|
+];
|
|
|
|
|
|
-const router = useRouter();
|
|
|
-const route = useRoute();
|
|
|
-const routeMeta = computed(() => {
|
|
|
- return route.meta;
|
|
|
+// 根据权限过滤可用的选项卡
|
|
|
+const availableTabs = computed(() => {
|
|
|
+ return allTabs.filter(tab => {
|
|
|
+ // 如果没有 permission 属性,则默认显示(如首页)
|
|
|
+ if (!tab.permission) return true;
|
|
|
+ // 如果有 permission 属性,则检查用户是否有该权限
|
|
|
+ return userStore.user[tab.permission];
|
|
|
+ });
|
|
|
});
|
|
|
|
|
|
-const dropdown1 = ref<DropdownInstance>();
|
|
|
+// 初始化选中状态
|
|
|
+const initActiveTab = () => {
|
|
|
+ const currentPath = route.path;
|
|
|
+ const matchedTab = allTabs.find(tab =>
|
|
|
+ currentPath.includes(tab.name.replace('管理', '').toLowerCase())
|
|
|
+ );
|
|
|
+ activeTab.value = matchedTab?.name || '';
|
|
|
+};
|
|
|
|
|
|
-const processCount = ref(50);
|
|
|
-const messageStatus = ref(false);
|
|
|
+initActiveTab();
|
|
|
|
|
|
-const showClick = () => {
|
|
|
- if (!dropdown1.value) return;
|
|
|
- dropdown1.value.handleOpen();
|
|
|
+// 导航方法
|
|
|
+const navigateTo = (tab) => {
|
|
|
+ activeTab.value = tab.name;
|
|
|
+ tab.handler();
|
|
|
};
|
|
|
|
|
|
-const commonBack = (itemValue) => {
|
|
|
- router.back();
|
|
|
+const openDrawer = () => {
|
|
|
+ drawerVisible.value = true;
|
|
|
};
|
|
|
|
|
|
-const handleCommand = (command: string | number | object) => {
|
|
|
+const saveAddressFinish = () => {
|
|
|
+ drawerVisible.value = false;
|
|
|
+};
|
|
|
+
|
|
|
+const gotoMain = () => {
|
|
|
+ activeTab.value = '';
|
|
|
+ router.push("/main");
|
|
|
+};
|
|
|
+
|
|
|
+const handleCommand = (command: string) => {
|
|
|
if (command === "b") {
|
|
|
logoutApi().then(() => {
|
|
|
localStorage.setItem("token", "");
|
|
|
-
|
|
|
router.replace("/login");
|
|
|
userStore.$reset();
|
|
|
});
|
|
|
- }
|
|
|
- if (command === "change") {
|
|
|
+ } else if (command === "change") {
|
|
|
router.push({
|
|
|
name: "changePassword",
|
|
|
query: {
|
|
@@ -121,98 +166,129 @@ const handleCommand = (command: string | number | object) => {
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
-:deep(.el-dropdown-menu__item) {
|
|
|
- height: 60px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- font-weight: 500;
|
|
|
- font-size: 24px;
|
|
|
- color: rgba(0, 0, 0, 0.9);
|
|
|
-}
|
|
|
-
|
|
|
.commonHeader {
|
|
|
height: $navbar-height;
|
|
|
width: 100%;
|
|
|
- background-color: #409eff;
|
|
|
+ background: linear-gradient(to bottom, #2164ee, #1C59D5);
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
padding: 16px 24px;
|
|
|
- //border: 1px solid red;
|
|
|
-
|
|
|
- .middle-title {
|
|
|
- font-weight: 500;
|
|
|
- font-size: 38px;
|
|
|
- color: rgba(0, 0, 0, 0.9);
|
|
|
- line-height: 24px;
|
|
|
- text-align: center;
|
|
|
- }
|
|
|
|
|
|
- .date {
|
|
|
- font-weight: 500;
|
|
|
- font-size: 14px;
|
|
|
- color: rgba(0, 0, 0, 0.6);
|
|
|
- line-height: 14px;
|
|
|
- text-align: center;
|
|
|
- }
|
|
|
+ .logo-area {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ cursor: pointer;
|
|
|
|
|
|
- .time {
|
|
|
- font-weight: 500;
|
|
|
- font-size: 20px;
|
|
|
- color: rgba(0, 0, 0, 0.9);
|
|
|
- line-height: 23px;
|
|
|
+ h2 {
|
|
|
+ color: white;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- .head {
|
|
|
- width: 48px;
|
|
|
- height: 48px;
|
|
|
- border-radius: 24px;
|
|
|
- }
|
|
|
+ .tab-container {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ height: 180%;
|
|
|
+ align-items: flex-end;
|
|
|
+ padding: 0 20px 2px;
|
|
|
+ background: linear-gradient(to bottom, #2164ee, #1C59D5);
|
|
|
+ overflow-x: auto;
|
|
|
+ scrollbar-width: none;
|
|
|
|
|
|
- .name {
|
|
|
- font-weight: 500;
|
|
|
- font-size: 20px;
|
|
|
- color: rgba(0, 0, 0, 0.9);
|
|
|
- line-height: 14px;
|
|
|
- text-align: right;
|
|
|
- }
|
|
|
+ &::-webkit-scrollbar {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
|
|
|
- .work {
|
|
|
- font-weight: 500;
|
|
|
- font-size: 14px;
|
|
|
- color: rgba(0, 0, 0, 0.6);
|
|
|
- line-height: 14px;
|
|
|
- text-align: right;
|
|
|
- margin-top: 5px;
|
|
|
- }
|
|
|
+ &::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ left: 20px;
|
|
|
+ right: 20px;
|
|
|
+ bottom: 0;
|
|
|
+ height: 3px;
|
|
|
+ background: linear-gradient(90deg,
|
|
|
+ rgba(255, 255, 255, 0) 0%,
|
|
|
+ rgba(255, 255, 255, 0.4) 50%,
|
|
|
+ rgba(255, 255, 255, 0) 100%);
|
|
|
+ z-index: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tab {
|
|
|
+ padding: 16px 32px;
|
|
|
+ background: linear-gradient(#2164ee, #1C59D5);
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 20px;
|
|
|
+ color: #000000;
|
|
|
+ cursor: pointer;
|
|
|
+ position: relative;
|
|
|
+ flex-shrink: 0;
|
|
|
+ min-width: max-content;
|
|
|
+ text-align: center;
|
|
|
+ white-space: nowrap;
|
|
|
+ margin-bottom: 6px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: rgba(50, 140, 230, 0.7) !important;
|
|
|
+ backdrop-filter: blur(4px);
|
|
|
+ transform: translateY(-3px);
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
|
|
+ }
|
|
|
|
|
|
- .task {
|
|
|
- padding-top: 5px;
|
|
|
- margin-right: 10px;
|
|
|
+ &.active {
|
|
|
+ background: linear-gradient(to bottom, #2854ad, #133e8c);
|
|
|
+ color: #ffffff;
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: -6px;
|
|
|
+ height: 6px;
|
|
|
+ background: #ffa958;
|
|
|
+ z-index: 2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // .activeNotice {
|
|
|
- // animation: swing 0.15s infinite alternate ease-in-out;
|
|
|
- // }
|
|
|
-
|
|
|
- // @keyframes swing {
|
|
|
- // 0% {
|
|
|
- // transform: rotate(-45deg);
|
|
|
- // }
|
|
|
-
|
|
|
- // 100% {
|
|
|
- // transform: rotate(45deg);
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
- .process {
|
|
|
- font-weight: 500;
|
|
|
- font-size: 14px;
|
|
|
- color: rgba(0, 0, 0, 0.6);
|
|
|
- line-height: 14px;
|
|
|
- text-align: right;
|
|
|
- margin-top: 8px;
|
|
|
+ .user-area {
|
|
|
+ .name {
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 20px;
|
|
|
+ color: rgba(0, 0, 0, 0.9);
|
|
|
+ line-height: 14px;
|
|
|
+ text-align: right;
|
|
|
+ }
|
|
|
+
|
|
|
+ .work {
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 14px;
|
|
|
+ color: rgba(0, 0, 0, 0.6);
|
|
|
+ line-height: 14px;
|
|
|
+ text-align: right;
|
|
|
+ margin-top: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .avatar {
|
|
|
+ width: 48px;
|
|
|
+ height: 48px;
|
|
|
+ border-radius: 24px;
|
|
|
+ border: 1px solid #ccc;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+:deep(.el-dropdown-menu__item) {
|
|
|
+ height: 60px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 24px;
|
|
|
+ color: rgba(0, 0, 0, 0.9);
|
|
|
+}
|
|
|
</style>
|