|
|
@@ -1,21 +1,78 @@
|
|
|
<template>
|
|
|
<div class="header-bar">
|
|
|
<div class="header-bar__menu">
|
|
|
- <div v-for="(item, index) in menu" :key="index" class="header-bar__menu-item" @click="getItemClick(item)">
|
|
|
- <img v-if="getItemIcon(item)" :src="getItemIcon(item)" class="header-bar__menu-icon" />
|
|
|
- <span class="header-bar__menu-name">{{ getItemName(item) }}</span>
|
|
|
+ <div v-for="(item, index) in menu" :key="index" class="header-bar__menu-item">
|
|
|
+ <div
|
|
|
+ v-if="!item.children || item.children.length === 0"
|
|
|
+ @click="getItemClick(item)"
|
|
|
+ class="header-bar__menu-item-content flex"
|
|
|
+ >
|
|
|
+ <img v-if="getItemIcon(item)" :src="getItemIcon(item)" class="header-bar__menu-icon" />
|
|
|
+ <span class="header-bar__menu-name">{{ getItemName(item) }}</span>
|
|
|
+ </div>
|
|
|
+ <div v-else class="header-bar__submenu">
|
|
|
+ <div
|
|
|
+ class="header-bar__submenu-header"
|
|
|
+ @click.stop="toggleSubmenu(index)"
|
|
|
+ >
|
|
|
+ <img v-if="getItemIcon(item)" :src="getItemIcon(item)" class="header-bar__menu-icon" />
|
|
|
+ <span class="header-bar__menu-name">{{ getItemName(item) }}</span>
|
|
|
+ </div>
|
|
|
+ <!-- 二级菜单 -->
|
|
|
+ <div
|
|
|
+ class="header-bar__submenu-body"
|
|
|
+ :class="{ 'submenu-open': submenuOpen[index] }"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ v-for="(child, childIndex) in item.children"
|
|
|
+ :key="childIndex"
|
|
|
+ class="header-bar__submenu-item"
|
|
|
+ @click="getItemClick(child)"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ v-if="!child.children || child.children.length === 0"
|
|
|
+ class="header-bar__submenu-item-content flex left"
|
|
|
+ >
|
|
|
+ <img v-if="getItemIcon(child)" :src="getItemIcon(child)" class="header-bar__menu-icon" />
|
|
|
+ <span class="header-bar__menu-name">{{ getItemName(child) }}</span>
|
|
|
+ </div>
|
|
|
+ <div v-else class="header-bar__submenu-third-level">
|
|
|
+ <div
|
|
|
+ class="header-bar__submenu-third-header"
|
|
|
+ @click.stop="toggleThirdLevel(index, childIndex)"
|
|
|
+ >
|
|
|
+ <img v-if="getItemIcon(child)" :src="getItemIcon(child)" class="header-bar__menu-icon" />
|
|
|
+ <span class="header-bar__menu-name">{{ getItemName(child) }}</span>
|
|
|
+ </div>
|
|
|
+ <!-- 三级菜单 -->
|
|
|
+ <div
|
|
|
+ class="header-bar__submenu-third-body"
|
|
|
+ :class="{ 'submenu-open': thirdLevelOpen[`${index}-${childIndex}`] }"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ v-for="(grandChild, grandChildIndex) in child.children"
|
|
|
+ :key="grandChildIndex"
|
|
|
+ class="header-bar__submenu-third-item"
|
|
|
+ @click="getItemClick(grandChild)"
|
|
|
+ >
|
|
|
+ <img v-if="getItemIcon(grandChild)" :src="getItemIcon(grandChild)" class="header-bar__menu-icon" />
|
|
|
+ <span class="header-bar__menu-name">{{ getItemName(grandChild) }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="header-bar__title">
|
|
|
<span class="header-bar__text">{{ title }}</span>
|
|
|
</div>
|
|
|
- <div class="header-bar__buttons" >
|
|
|
- <div class="header-bar__button header-bar__button__user" v-if="showUser">
|
|
|
-
|
|
|
-
|
|
|
+ <div class="header-bar__buttons" v-if="showUser">
|
|
|
+ <div class="header-bar__button header-bar__button__user">
|
|
|
<el-dropdown>
|
|
|
<span class="el-dropdown-link">
|
|
|
- {{useUserInfoStore.userInfo.account_name || useUserInfoStore.userInfo.real_name || useUserInfoStore.userInfo.login_name}}
|
|
|
+ {{ useUserInfoStore.userInfo.account_name || useUserInfoStore.userInfo.real_name || useUserInfoStore.userInfo.login_name }}
|
|
|
</span>
|
|
|
<template #dropdown>
|
|
|
<el-dropdown-menu>
|
|
|
@@ -30,26 +87,26 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { defineProps, reactive } from 'vue';
|
|
|
-import useUserInfo from "@/stores/modules/user";
|
|
|
-import { useRouter} from "vue-router";
|
|
|
+import { defineProps, reactive } from 'vue'
|
|
|
+import useUserInfo from '@/stores/modules/user'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
import iconsz from './assets/shezhi@2x.png'
|
|
|
import iconykq from './assets/yaokong@2x.png'
|
|
|
import gengxin from './assets/gengxin.svg'
|
|
|
-/*import iconMinimize from './assets/suoxiao@2x.png' // 新增
|
|
|
-import iconClose from './assets/guanbi@2x.png' // 新增*/
|
|
|
import icpList from '@/utils/ipc'
|
|
|
import { getRouterUrl } from '@/utils/appfun'
|
|
|
-import client from "@/stores/modules/client";
|
|
|
-const clientStore = client();
|
|
|
-const useUserInfoStore = useUserInfo();
|
|
|
+import client from '@/stores/modules/client'
|
|
|
+
|
|
|
+const clientStore = client()
|
|
|
+const useUserInfoStore = useUserInfo()
|
|
|
|
|
|
// 定义 menu 项的类型
|
|
|
interface MenuItem {
|
|
|
- name?: string; //名称
|
|
|
- icon?: string; // 图标
|
|
|
- click?: () => void; // 点击事件
|
|
|
- type?: keyof typeof menuType; // 类型 menuType里面的值
|
|
|
+ name?: string //名称
|
|
|
+ icon?: string // 图标
|
|
|
+ click?: () => void // 点击事件
|
|
|
+ type?: keyof typeof menuType // 类型 menuType里面的值
|
|
|
+ children?: MenuItem[] // 支持嵌套菜单
|
|
|
}
|
|
|
|
|
|
// 定义 props
|
|
|
@@ -62,12 +119,26 @@ const props = defineProps({
|
|
|
type: Array as () => MenuItem[],
|
|
|
default: () => []
|
|
|
},
|
|
|
- showUser:{
|
|
|
+ showUser: {
|
|
|
type: Boolean,
|
|
|
default: false
|
|
|
}
|
|
|
-});
|
|
|
+})
|
|
|
+
|
|
|
const Router = useRouter()
|
|
|
+
|
|
|
+const submenuOpen = reactive<{ [key: number]: boolean }>({})
|
|
|
+const thirdLevelOpen = reactive<{ [key: string]: boolean }>({})
|
|
|
+
|
|
|
+const toggleSubmenu = (index: number) => {
|
|
|
+ submenuOpen[index] = !submenuOpen[index]
|
|
|
+}
|
|
|
+
|
|
|
+const toggleThirdLevel = (parentIndex: number, childIndex: number) => {
|
|
|
+ const key = `${parentIndex}-${childIndex}`
|
|
|
+ thirdLevelOpen[key] = !thirdLevelOpen[key]
|
|
|
+}
|
|
|
+
|
|
|
const menuType = reactive({
|
|
|
setting: {
|
|
|
name: '设置',
|
|
|
@@ -89,124 +160,108 @@ const menuType = reactive({
|
|
|
icon: gengxin,
|
|
|
click: openOTA
|
|
|
}
|
|
|
-});
|
|
|
+})
|
|
|
|
|
|
function getItemClick(item: MenuItem) {
|
|
|
- const menuItem = item.type ? { ...menuType[item.type], ...item } : item;
|
|
|
+ const menuItem = item.type ? { ...menuType[item.type], ...item } : item
|
|
|
if (menuItem && menuItem.click) {
|
|
|
- menuItem.click();
|
|
|
+ menuItem.click()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function getItemName(item: MenuItem) {
|
|
|
- const menuItem = item.type ? { ...menuType[item.type], ...item } : item;
|
|
|
- return menuItem.name;
|
|
|
+ const menuItem = item.type ? { ...menuType[item.type], ...item } : item
|
|
|
+ return menuItem.name
|
|
|
}
|
|
|
|
|
|
function getItemIcon(item: MenuItem) {
|
|
|
- const menuItem = item.type ? { ...menuType[item.type], ...item } : item;
|
|
|
- return menuItem.icon;
|
|
|
+ const menuItem = item.type ? { ...menuType[item.type], ...item } : item
|
|
|
+ return menuItem.icon
|
|
|
}
|
|
|
|
|
|
function openSetting() {
|
|
|
-
|
|
|
const { href } = Router.resolve({
|
|
|
name: 'setting',
|
|
|
- query:{
|
|
|
- type:0,
|
|
|
+ query: {
|
|
|
+ type: 0
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- clientStore.ipc.removeAllListeners(icpList.utils.openMain);
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.utils.openMain)
|
|
|
let params = {
|
|
|
title: '设置',
|
|
|
width: 1920,
|
|
|
height: 1080,
|
|
|
frame: true,
|
|
|
- id: "seeting",
|
|
|
+ id: 'seeting',
|
|
|
url: getRouterUrl(href)
|
|
|
}
|
|
|
- clientStore.ipc.send(icpList.utils.openMain, params);
|
|
|
+ clientStore.ipc.send(icpList.utils.openMain, params)
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-function openRemoteControl(){
|
|
|
-
|
|
|
+function openRemoteControl() {
|
|
|
const { href } = Router.resolve({
|
|
|
- name: 'RemoteControl',
|
|
|
+ name: 'RemoteControl'
|
|
|
})
|
|
|
|
|
|
- clientStore.ipc.removeAllListeners(icpList.utils.openMain);
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.utils.openMain)
|
|
|
let params = {
|
|
|
title: '模拟遥控器',
|
|
|
width: 350,
|
|
|
height: 600,
|
|
|
frame: true,
|
|
|
- id: "RemoteControl",
|
|
|
+ id: 'RemoteControl',
|
|
|
url: getRouterUrl(href)
|
|
|
}
|
|
|
- clientStore.ipc.send(icpList.utils.openMain, params);
|
|
|
+ clientStore.ipc.send(icpList.utils.openMain, params)
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-function openDeveloper(){
|
|
|
-
|
|
|
+function openDeveloper() {
|
|
|
const { href } = Router.resolve({
|
|
|
- name: 'developer',
|
|
|
+ name: 'developer'
|
|
|
})
|
|
|
|
|
|
- clientStore.ipc.removeAllListeners(icpList.utils.openMain);
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.utils.openMain)
|
|
|
let params = {
|
|
|
title: '初始设备调频设置',
|
|
|
width: 900,
|
|
|
height: 700,
|
|
|
frame: true,
|
|
|
- id: "developer",
|
|
|
+ id: 'developer',
|
|
|
url: getRouterUrl(href)
|
|
|
}
|
|
|
- clientStore.ipc.send(icpList.utils.openMain, params);
|
|
|
+ clientStore.ipc.send(icpList.utils.openMain, params)
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-function openOTA(){
|
|
|
-
|
|
|
+function openOTA() {
|
|
|
const { href } = Router.resolve({
|
|
|
- name: 'ota',
|
|
|
+ name: 'ota'
|
|
|
})
|
|
|
|
|
|
- clientStore.ipc.removeAllListeners(icpList.utils.openMain);
|
|
|
+ clientStore.ipc.removeAllListeners(icpList.utils.openMain)
|
|
|
let params = {
|
|
|
title: '版本更新',
|
|
|
width: 900,
|
|
|
height: 700,
|
|
|
frame: true,
|
|
|
- id: "ota",
|
|
|
+ id: 'ota',
|
|
|
url: getRouterUrl(href)
|
|
|
}
|
|
|
- clientStore.ipc.send(icpList.utils.openMain, params);
|
|
|
+ clientStore.ipc.send(icpList.utils.openMain, params)
|
|
|
}
|
|
|
|
|
|
-function loginOut(){
|
|
|
- useUserInfoStore.loginOut();
|
|
|
+function loginOut() {
|
|
|
+ useUserInfoStore.loginOut()
|
|
|
useUserInfoStore.updateLoginShow(true)
|
|
|
}
|
|
|
-
|
|
|
-// 新增
|
|
|
-function minimizeWindow() {
|
|
|
- clientStore.ipc.send(icpList.utils.minimizeWindow);
|
|
|
-}
|
|
|
-
|
|
|
-// 新增
|
|
|
-function closeWindow() {
|
|
|
- clientStore.ipc.send(icpList.utils.closeWindow);
|
|
|
-}
|
|
|
</script>
|
|
|
|
|
|
-<style lang="scss" scoped>
|
|
|
+<style lang="scss" scoped>
|
|
|
.header-bar_blank {
|
|
|
width: 100%;
|
|
|
- height:30px;
|
|
|
+ height: 30px;
|
|
|
}
|
|
|
+
|
|
|
.header-bar {
|
|
|
position: fixed;
|
|
|
app-drag: drag;
|
|
|
@@ -295,6 +350,7 @@ function closeWindow() {
|
|
|
.header-bar__button__user {
|
|
|
padding: 0 10px;
|
|
|
}
|
|
|
+
|
|
|
.header-bar__button:hover {
|
|
|
background-color: #e0e0e0;
|
|
|
}
|
|
|
@@ -303,4 +359,79 @@ function closeWindow() {
|
|
|
width: 16px;
|
|
|
height: 16px;
|
|
|
}
|
|
|
+
|
|
|
+.header-bar__submenu {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.header-bar__submenu-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 2px 10px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.header-bar__submenu-body {
|
|
|
+ position: absolute;
|
|
|
+ top: 100%;
|
|
|
+ left: 0;
|
|
|
+ background: white;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
|
+ z-index: 1000;
|
|
|
+ min-width: 160px;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ display: none;
|
|
|
+
|
|
|
+ &.submenu-open {
|
|
|
+ display: block;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.header-bar__submenu-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 6px 12px;
|
|
|
+ white-space: nowrap;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background-color: #f0f0f0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.header-bar__submenu-third-level {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.header-bar__submenu-third-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background-color: #f0f0f0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.header-bar__submenu-third-body {
|
|
|
+ position: fixed;
|
|
|
+ margin-left: 150px;
|
|
|
+ top: 0;
|
|
|
+ left: 100%;
|
|
|
+ background: white;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
|
+ z-index: 1000;
|
|
|
+ min-width: 160px;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ display: none;
|
|
|
+
|
|
|
+ &.submenu-open {
|
|
|
+ display: block;
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|