2026-07-02 17:04:52 +08:00

1158 lines
38 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div id="vol-container" :class="['vol-theme-' + theme]">
<!-- 顶部一级导航栏 -->
<div class="vol-top-header">
<div class="top-logo">
<img v-bind:src="logo" />
<span>跃动智慧体育平台</span>
</div>
<div class="top-nav-menus" ref="topNavMenusRef">
<span
v-for="item in topLevelMenus"
:key="item.id"
:class="['top-nav-item', { 'is-active': activeTopMenuId == item.id }]"
@click="selectTopMenu(item)"
>{{ item.name }}</span>
</div>
<div class="header-info">
<div class="h-link">
<a href="javascript:void(0)" @click="to(item)" v-for="(item, index) in links.filter((c) => { return c.icon; })" :key="index">
<span v-if="!item.icon"> {{ item.text }}</span>
<i v-else :class="item.icon"></i>
</a>
</div>
<div>
<img class="user-header" :src="userImg" :onerror="errorImg" />
</div>
<div class="user">
<span>{{ userName }}</span>
<br />
<span id="index-date"></span>
</div>
</div>
</div>
<!-- 侧边二级导航栏 -->
<div v-show="activeTopMenuHasChildren" class="vol-aside" :style="{ width: menuWidth + 'px' }">
<div class="vol-menu">
<el-scrollbar style="height: 100%">
<VolMenu
:rootId="activeTopMenuId + ''"
:currentMenuId="currentMenuId"
:on-select="onSelect"
:enable="true"
:open-select="false"
:isCollapse="isCollapse"
:list="menuOptions"
></VolMenu>
</el-scrollbar>
</div>
</div>
<div class="vol-container" :style="{ left: activeTopMenuHasChildren ? (menuWidth - 1) + 'px' : '0px' }">
<div class="vol-path">
<el-tabs @tab-click="selectNav" @tab-remove="removeNav" @contextmenu.prevent="bindRightClickMenu(false)"
type="border-card" class="header-navigation" v-model="selectId" :strtch="false">
<el-tab-pane v-for="(item, navIndex) in navigation" type="card" :name="navIndex + ''" :closable="navIndex > 0"
:key="navIndex" :label="item.name">
<span style="display: none">{{ navIndex }}</span>
</el-tab-pane>
</el-tabs>
<!-- 右键菜单 -->
<div v-show="contextMenuVisible">
<ul :style="{ left: menuLeft + 'px', top: menuTop + 'px' }" class="contextMenu">
<li v-show="visibleItem.all">
<el-button type="info" @click="closeTabs()" size="default">
<i class="el-icon-close"></i>
{{
navigation.length == 2 ? "关闭菜单" : "关闭所有"
}}</el-button>
</li>
<li v-show="visibleItem.left">
<el-button type="info" @click="closeTabs('left')" size="default"><i
class="el-icon-back"></i>关闭左边</el-button>
</li>
<li v-show="visibleItem.right">
<el-button type="info" @click="closeTabs('right')" size="default">
<i class="el-icon-right"></i>关闭右边</el-button>
</li>
<li v-show="visibleItem.other">
<el-button type="info" @click="closeTabs('other')"><i class="el-icon-right"></i>关闭其他
</el-button>
</li>
</ul>
</div>
</div>
<div class="vol-main" id="vol-main">
<el-scrollbar style="height: 100%" v-if="permissionInited">
<loading v-show="$store.getters.isLoading()"></loading>
<router-view v-if="!$route.meta ||
($route.meta && !$route.meta.hasOwnProperty('keepAlive'))
" v-slot="{ Component }">
<transition name="page-fade" mode="out-in">
<component :is="Component" :key="$route.path" />
</transition>
</router-view>
<router-view v-if="$route.meta && $route.meta.hasOwnProperty('keepAlive')"></router-view>
</el-scrollbar>
</div>
</div>
<!-- 溢出菜单已改为水平滚动,Teleport 面板保留备用 -->
<el-drawer title="选择主题" v-model="drawer_model" direction="rtl" destroy-on-close>
<div class="theme-selector">
<div @click="changeTheme(item.name)" class="item" v-for="(item, index) in theme_color" :key="index"
:style="{ background: item.color }">
<div v-show="item.leftColor" :style="{ background: item.leftColor }" style="height: 100%; width: 20px"
class="t-left"></div>
<div class="t-right"></div>
</div>
</div>
</el-drawer>
<el-drawer title="消息列表" v-model="messageModel" direction="rtl" destroy-on-close>
<Message :list="messageList"></Message>
</el-drawer>
</div>
</template>
<style lang="less" scoped>
@import "./index/index.less";
</style>
<!-- 页面切换动画:必须非 scoped,否则无法作用于子路由组件根元素 -->
<style lang="less">
/* ====== 页面切换动画 ====== */
.page-fade-enter-active {
transition: opacity 0.22s cubic-bezier(0.25, 0.46, 0.45, 0.94),
transform 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
will-change: opacity, transform;
}
.page-fade-leave-active {
transition: opacity 0.15s cubic-bezier(0.55, 0, 0.55, 0.2),
transform 0.15s cubic-bezier(0.55, 0, 0.55, 0.2);
will-change: opacity, transform;
}
.page-fade-enter-from {
opacity: 0;
transform: translateY(12px);
}
.page-fade-leave-to {
opacity: 0;
transform: translateY(-6px);
}
/* ====== 页面切换动画 END ====== */
</style>
<script>
import loading from "@/components/basic/RouterLoading.vue";
import VolMenu from "@/components/basic/VolElementMenu.vue";
import Message from "./index/Message.vue";
import MessageConfig from "./index/MessageConfig.js";
import eventBus from '@/eventBus.js'; // 导入事件总线
// import { openDB } from 'idb';
// const dbName = 'videoImg';
// const storeName = 'videofusion';
var imgUrl = require("@/assets/imgs/logo.png");
var $this;
var $interval;
var $indexDate;
import {
defineComponent,
reactive,
ref,
watch,
computed,
nextTick,
onMounted,
getCurrentInstance,
} from "vue";
import { useRouter, useRoute } from "vue-router";
import store from "../store/index";
import http from "@/../src/api/http.js";
import { ElMessage } from "element-plus";
export default defineComponent({
components: {
VolMenu,
loading,
Message,
},
data() {
return {
allTabs: true,
leftTabs: true,
rightTabs: true,
otherTabs: true,
menuLeft: 0,
menuTop: 0,
// contextMenuVisible: false, // 右键关闭显/隐
};
},
setup(props, context) {
// 获取全局属性和方法
const { proxy } = getCurrentInstance();
// 菜单导航默认宽度
const menuWidth = ref(200);
const topNavMenusRef = ref(null);
const contextMenuVisible = ref(false);
const isCollapse = ref(false);
const drawer_model = ref(false);
const messageModel = ref(false);
const theme_color = ref([
{ name: "blue", color: "rgb(45, 140, 240)" },
{ name: "blue2", color: "rgb(45, 140, 240)", leftColor: "#0068d6" },
{ name: "red", color: "rgb(237, 64, 20)" },
{ name: "red2", color: "rgb(237, 64, 20)", leftColor: "#a90000" },
{ name: "dark", color: "#272929" },
{ name: "orange", color: "#ff9900" },
{ name: "orange2", color: "#ff9900", leftColor: "rgb(232 141 5)" },
{ name: "green", color: "rgb(25, 190, 107)" },
{ name: "green2", color: "rgb(25, 190, 107)", leftColor: "#019e4f" },
{ name: "white", color: "#fff" },
]);
const links = ref([
{ text: "个人中心", path: "/UserInfo", id: -1, icon: "el-icon-s-custom" },
{
text: "安全退出",
path: "/login",
id: -4,
icon: "el-icon-switch-button",
},
]);
const errorImg = ref(
'this.src="' + require("@/assets/imgs/error-img.png") + '"'
);
const selectId = ref("1");
// 【首页】标签序号(当前右键选中的菜单)
const selectMenuIndex = ref("0");
//2022.05.29增加tab选项与菜单联动功能
const currentMenuId = ref(0);
const userName = ref("--");
const userInfo = ref({});
const visibleItem = reactive({
left: false,
right: false,
all: false,
other: false,
});
const userImg = ref("");
const navigation = reactive([
{ orderNo: "0", id: "1", name: "看板", path: "/bigScreen" },
// { orderNo: "0", id: "1", name: "双碳评估汇总", path: "/evaluationSummary" },
]);
const logo = ref(imgUrl);
const theme = ref("blue2");
const menuOptions = ref([]);
const activeTopMenuId = ref(0);
const topLevelMenus = computed(() => {
return menuOptions.value.filter(item => item.parentId == 0 && item.enable != 2);
});
const activeTopMenuHasChildren = computed(() => {
return menuOptions.value.some(item => item.parentId == activeTopMenuId.value);
});
const findTopLevelParent = (itemId) => {
const flatList = menuOptions.value;
let current = flatList.find(x => x.id == itemId);
while (current && current.parentId != 0 && current.parentId !== undefined && current.parentId !== null) {
current = flatList.find(x => x.id == current.parentId);
}
return current ? current.id : null;
};
const selectTopMenu = (item) => {
activeTopMenuId.value = item.id;
// 没有二级子菜单时直接跳转到该菜单对应的页面
const hasChildren = menuOptions.value.some(x => x.parentId == item.id);
if (!hasChildren && item.path) {
open(item , true);
router.push({ path: item.path});
}
};
// ====== 溢出菜单逻辑已移除(改为水平滚动)======
const routerList = ref(JSON.parse(localStorage.getItem("routerList")))
const permissionInited = ref(false);
const messageList = reactive([]);
let _config = getCurrentInstance().appContext.config.globalProperties;
let router = useRouter();
const toggleLeft = () => {
isCollapse.value = !isCollapse.value;
menuWidth.value = isCollapse.value ? 63 : 200;
};
//2021.08.28开放手动折叠菜单方法
_config.menu = {
show() {
toggleLeft();
},
hide() {
toggleLeft();
},
};
const changeTheme = (name) => {
if (theme.value != name) {
theme.value = name;
}
localStorage.setItem("vol3_theme", blue);
};
const to = (item) => {
if (item.path.indexOf("http") != -1) {
window.open(item.path);
return;
}
if (typeof item == "string" || item.path == "/login") {
if (item == "/login" || item.path == "/login") {
store.commit("clearUserInfo", "");
window.location.href = "/";
return;
}
router.push({ path: item });
return;
}
if (item.path == "#") return;
open(item);
};
const open = (item, useRoute) => {
/* 2020.07.31增加手动打开tabs*/
let _index = navigation?.findIndex((x) => {
return x.path == item.path;
});
if (_index == -1) {
navigation.push({
// orderNo: String(navigation.length),// 序号
id: item.id + "",
name: item.name || item.text || "无标题",
path: item.path,
query: item.query, //2021.03.20修复自定义二次打开$tabs时参数丢失的问题
});
//新打开的tab移至最后一个选项
selectId.value = navigation.length - 1 + "";
} else {
selectId.value = _index + "";
}
if (useRoute === undefined) {
//非标准菜单,记录最后一次跳转的页面,用于刷新
setItem(item);
router.push(item);
// this.$router.push(item);
}
currentMenuId.value = item.id * 1;
// tab菜单绑定右键事件
proxy.$nextTick(function (e) {
proxy.bindRightClickMenu(true);
});
};
const close = (path) => {
/* 2020.07.31增加手动打开tabs*/
let index = navigation.findIndex((x) => {
return x.path == path;
});
if (index == -1) {
return _config.$Message.error("未找到菜单");
}
removeNav(index);
};
const setItem = (item) => {
/* 2020.07.31增加手动打开tabs*/
localStorage.setItem(
window.location.origin + "_tabs",
JSON.stringify(item)
);
};
const getItem = () => {
/* 2020.07.31增加手动打开tabs*/
let nav = localStorage.getItem(window.location.origin + "_tabs");
return nav ? JSON.parse(nav) : null;
};
const selectNav = (item) => {
//升级element正式版修改
selectId.value = item.props.name;
let _path = navigation[item.index].path;
currentMenuId.value = (
menuOptions.value.find((c) => {
return c.path == _path;
}) || { id: 0 }
).id;
router.push({
path: navigation[item.index].path,
query: navigation[item.index].query,
});
};
const removeNav = (_index) => {
return new Promise(() => {
//关闭的当前项,跳转到前一个页面
if (selectId.value == _index + "") {
setItem(navigation[_index - 1]);
router.push({
path: navigation[_index - 1].path,
//2022.06.27修复tabs二次切换后参数丢失的问题
query: navigation[_index - 1].query,
});
navigation.splice(_index, 1);
selectId.value = selectId.value - 1 + "";
return;
}
if (_index < selectId.value) {
selectId.value = selectId.value - 1 + "";
}
navigation.splice(_index, 1);
currentMenuId.value = (
menuOptions.value.find((c) => {
return c.path == navigation[selectId.value * 1].path;
}) || { id: 0 }
).id;
});
};
const getSelectMenuName = (id) => {
return menuOptions.value.find(function (x) {
return x.id == id;
});
};
eventBus.on('toViewGrade', (res) => {
console.log(res, 'resss')
menuOptions.value.push({
"name": "年级数据统计",
"path": "/gradeView1",
"id": 240,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(240, ['240'])
localStorage.setItem('gradeId', JSON.stringify(res))
router.push({ name: 'gradeView1', params: res })
});
// 年级详情跳转班级详情
eventBus.on('toStudentView', (res) => {
console.log(res, 'ressstoStudentView')
let k = {
id:res.classId,
gradeId:res.gradeId
}
menuOptions.value.push({
"name": "班级数据统计",
"path": "/classView1",
"id": 242,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(242, ['242'])
localStorage.setItem('classView1', JSON.stringify(k))
router.push({ name: 'classView1', params: k })
});
eventBus.on('toView1', (res) => {
console.log(res, 'resss')
menuOptions.value.push({
"name": "教师数据统计",
"path": "/teacherView",
"id": 244,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(244, ['244'])
localStorage.setItem('teacherView', JSON.stringify(res))
router.push({ name: 'teacherView', params: res })
});
eventBus.on('toDetail', (res) => {
menuOptions.value.push({
"name": "班级数据统计",
"path": "/classView1",
"id": 242,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(242, ['242'])
localStorage.setItem('classId', JSON.stringify(res))
router.push({ name: 'classView1', params: res })
});
eventBus.on('toViewStudent', (res) => {
console.log(res,'ressstoViewStudent')
menuOptions.value.push({
"name": "学生数据统计",
"path": "/StudentDataStatistics",
"id": 245,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(245, ['245'])
localStorage.setItem('studentNo', JSON.stringify(res))
router.push({ name: 'StudentDataStatistics', params: res })
});
// 学生数据统计中的跳转训练记录 toRecord
eventBus.on('toRecord', (res) => {
menuOptions.value.push({
"name": "训练记录",
"path": "/trainingRecords",
"id": 286,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(286, ['286'])
localStorage.setItem('toRecord', JSON.stringify(res))
router.push({ name: 'trainingRecords', params: res })
});
// 学生数据统计中的跳转体测记录 toRecord
eventBus.on('toPhysical', (res) => {
menuOptions.value.push({
"name": "体测记录",
"path": "/examinationRecords",
"id": 287,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(287, ['287'])
localStorage.setItem('toRecord', JSON.stringify(res))
router.push({ name: 'examinationRecords', params: res })
});
eventBus.on('toEventDetail', (res) => {
menuOptions.value.push({
"name": "赛事活动详情",
"path": "/eventDetail",
"id": 255,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(255, ['255'])
router.push({ name: 'eventDetail', params: res })
});
// 跳转运动数据详情
eventBus.on('classRoomDetail', (res) => {
console.log(res, 'classRoomDetailclassRoomDetail')
menuOptions.value.push({
"name": "运动数据详情",
"path": "/trainingDetail",
"id": 276,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(276, ['276'])
localStorage.setItem('classRoomDetail', JSON.stringify(res))
router.push({ name: 'trainingDetail', params: res })
});
// 跳转体测数据详情
eventBus.on('phyClassRoomDetail', (res) => {
console.log(res, 'phyClassRoomDetail')
menuOptions.value.push({
"name": "体测数据详情",
"path": "/physicalDetail",
"id": 277,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(277, ['277'])
localStorage.setItem('physicalDetail', res)
router.push({ name: 'physicalDetail', params: res })
});
// 跳转专项管理详情
eventBus.on('specialDetails', (res) => {
console.log(res, 'specialDetails')
menuOptions.value.push({
"name": "专项管理详情",
"path": "/specialDetails",
"id": 280,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(280, ['280'])
localStorage.setItem('specialDetails', JSON.stringify(res))
router.push({ name: 'specialDetails', params: res })
});
eventBus.on('trainingDetails', (res) => {
console.log(res, 'trainingDetailstrainingDetails')
menuOptions.value.push({
"name": "训练详情",
"path": "/trainingDetails",
"id": 259,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(259, ['259'])
localStorage.setItem('studentNo', JSON.stringify(res))
router.push({ name: 'trainingDetails', params: res })
});
// 班级记录整体数据
eventBus.on('toExamEvaluation', (res) => {
console.log(res, 'examEvaluation')
menuOptions.value.push({
"name": "整体数据",
"path": "/overallData",
"id": 268,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(268, ['268'])
localStorage.setItem('studentNo', JSON.stringify(res))
router.push({ name: 'overallData', params: res })
});
// 班级记录表格详情
eventBus.on('ExamEvaluationTable', (res) => {
console.log(res, 'physicalDetails')
menuOptions.value.push({
"name": "体测详情",
"path": "/physicalDetails",
"id": 269,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(269, ['269'])
localStorage.setItem('studentNo', JSON.stringify(res))
router.push({ name: 'physicalDetails', params: res })
});
// 训练详情下的学生详情
eventBus.on('toStudentDetail', (res) => {
console.log(res, 'toStudentDetail')
menuOptions.value.push({
"name": "体测详情",
"path": "/studentDetails",
"id": 271,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(271, ['271'])
localStorage.setItem('studentNoDetail', JSON.stringify(res))
router.push({ name: 'studentDetails', params: res })
});
eventBus.on('toClassroomReportDetail', (res) => {
menuOptions.value.push({
"name": "课堂报告详情",
"path": "/ClassroomReportDetail",
"id": 272,
"icon": "iconfont icon-shuangtanyuce1"
})
onSelect(272, ['272'])
localStorage.setItem('ClassroomReportDetail', JSON.stringify(res))
router.push({ name: 'ClassroomReportDetail', params: res })
});
const onSelect = (treeId) => {
/* 2020.07.31增加手动打开tabs*/
var item = getSelectMenuName(treeId);
open(item, false);
const topId = findTopLevelParent(treeId);
if (topId !== null) activeTopMenuId.value = topId;
};
/**
* 显示右键菜单
* @param {*} e 事件对象
*/
const openTabsMenu = function (e) {
e.preventDefault(); // 防止默认菜单弹出
let tabId = e.target.id.split("-")[1] * 1;
//记录当前选中的菜单index
selectMenuIndex.value =
document.getElementById("pane-" + tabId).children[0].textContent * 1;
//只有首页时不显示
if (navigation.length == 1) {
return;
}
//首页设置显示关闭右边菜单
if (!selectMenuIndex.value) {
visibleItem.all = false;
visibleItem.right = true;
visibleItem.left = false;
visibleItem.other = false;
} else {
visibleItem.all = true;
//不是最后一个显示关闭右边菜单
visibleItem.right = selectMenuIndex.value != navigation.length - 1;
//只有两个菜单时不显示关闭左边
visibleItem.left = navigation.length != 2;
//只有两个菜单时不显示关闭其他
visibleItem.other = navigation.length != 2;
}
contextMenuVisible.value = true;
// 设置右键菜单显示的位置
proxy.menuLeft =
e.target.getBoundingClientRect().left - (isCollapse.value ? 63 : 198); //-e.target.clientWidth
proxy.menuTop = 36;
};
/**
* 关闭右键菜单
*/
const closeTabsMenu = () => {
contextMenuVisible.value = false;
};
const toHome = () => {
open({
text: navigation[0].name,
path: navigation[0].path,
});
};
setInterval(() => {
leftList.value.map((i) => {
deleteData(i.id)
})
rightList.value.map((i) => {
deleteData(i.id)
})
}, 24 * 60 * 60 * 1000)
/**
* 关闭其它标签页
* @param {*} par 关闭类型(left,right,other)
*/
const closeTabs = (value) => {
let _menuId = navigation[selectId.value * 1].id;
let currnetIndex = selectId.value * 1; // navigation.findIndex(c => { return c.id == selectId.value });
switch (value) {
case "left": {
// 删除左侧tab标签
navigation.splice(1, currnetIndex - 1); // 删除左侧tab标签
break;
}
case "right": {
// 删除右侧tab标签
if (selectMenuIndex.value == 0) {
navigation.splice(currnetIndex); // 删除右侧tab标签
toHome();
} else {
navigation.splice(currnetIndex + 1); // 删除右侧tab标签
if (selectMenuIndex.value < currnetIndex) {
navigation.splice(
selectMenuIndex.value,
currnetIndex - selectMenuIndex.value
);
}
}
break;
}
case "other": {
// 删除其他所有tab标签
navigation.splice(currnetIndex + 1); // 删除右侧tab标签(这里必须按照右→左顺序删除)
navigation.splice(1, currnetIndex - 1); // 删除左侧tab标签
break;
}
default: {
//关闭所有
navigation.splice(1, navigation.length);
toHome();
break;
}
}
selectId.value =
navigation.findIndex((c) => {
return c.id == _menuId;
}) + "";
closeTabsMenu();
};
watch(
() => contextMenuVisible.value,
(newVal, oldVal) => {
// 监视
if (newVal) {
document.body.addEventListener("click", closeTabsMenu);
} else {
document.body.removeEventListener("click", closeTabsMenu);
}
}
);
/**
* 系统创建开始
*/
const created = () => {
let _theme = localStorage.getItem("vol3_theme");
if (_theme) {
theme.value = _theme;
}
let _userInfo = store.getters.getUserInfo();
if (_userInfo) {
userName.value = _userInfo.userName;
if (_userInfo.img) {
userImg.value = _config.base.getImgSrc(_userInfo.img, http.ipAddress);
}
}
Object.assign(_config.$tabs, { open: open, close: close });
http.get("api/menu/getTreeMenu", {}, true).then((data) => {
// data = JSON.parse(data);
console.log(typeof (data), 'data.code')
if (typeof (data) != 'string') {
console.log(data, 'datatatagetTreeMenu')
data.push({ id: "1", name: "看板", url: "/bigScreen" }); // 为了获取选中id使用
// 页面路径与图标映射表
const iconMap = {
'/bigScreen': 'el-icon-s-home',
'/userManage': 'el-icon-user-solid',
'/teacherManage': 'el-icon-s-custom',
'/classManage': 'el-icon-s-grid',
'/studentsManage': 'el-icon-user',
'/gradeManage': 'el-icon-s-data',
'/schoolManage': 'el-icon-s-platform',
'/physicalManagement': 'el-icon-s-check',
'/gameManagement': 'el-icon-s-flag',
'/eventRegistration': 'el-icon-s-order',
'/eventRelease': 'el-icon-s-release',
'/eventStatistics': 'el-icon-s-marketing',
'/projectManage': 'el-icon-s-operation',
'/bodySideData': 'el-icon-s-data',
'/trainingRecords': 'el-icon-s-cooperation',
'/examinationRecords': 'el-icon-s-check',
'/ropeSkipping': 'el-icon-s-cooperation',
'/agile': 'el-icon-s-opportunity',
'/speed': 'el-icon-s-opportunity',
'/power': 'el-icon-s-opportunity',
'/balance': 'el-icon-s-opportunity',
'/heartRate': 'el-icon-s-opportunity',
'/examEvaluation': 'el-icon-s-data',
'/level': 'el-icon-s-flag',
'/record': 'el-icon-s-order',
'/studentResultRecord': 'el-icon-s-data',
'/articleManage': 'el-icon-s-comment',
'/resourceLibrary': 'el-icon-s-management',
'/homeWork': 'el-icon-s-order',
'/classroomReport': 'el-icon-s-data',
'/stadiumManage': 'el-icon-s-platform',
'/curricularTaxonomy': 'el-icon-s-grid',
'/curricularManage': 'el-icon-s-management',
'/popularCurricular': 'el-icon-star-on',
'/userActivity': 'el-icon-s-marketing',
'/physicalTestData': 'el-icon-s-data',
'/trainingData': 'el-icon-s-data',
'/visionData': 'el-icon-s-check',
'/specialManagement': 'el-icon-s-management',
'/ropeJumping': 'el-icon-s-cooperation',
'/sportsHallClass': 'el-icon-s-grid',
'/sportHallReport': 'el-icon-s-data',
'/TempTcc': 'el-icon-s-check',
'/summaryOfGrades': 'el-icon-s-data',
'/openReservation': 'el-icon-s-release',
'/PhysicalTestReport': 'el-icon-s-data',
'/AITraining': 'el-icon-s-opportunity',
'/Aitest': 'el-icon-s-opportunity',
'/eventDetail': 'el-icon-s-flag',
'/studentDetails': 'el-icon-user',
'/trainingDetail': 'el-icon-s-data',
'/physicalDetail': 'el-icon-s-data',
'/physicalDetails': 'el-icon-s-data',
'/overallData': 'el-icon-s-data',
'/classroomReportDetail': 'el-icon-s-data',
'/specialDetails': 'el-icon-s-data',
'/trainingDetails': 'el-icon-s-data',
'/StudentDataStatistics': 'el-icon-s-data',
'/classView1': 'el-icon-s-data',
'/gradeView1': 'el-icon-s-data',
'/teacherView': 'el-icon-s-data',
'/classDetail': 'el-icon-s-data',
'/enterHigh': 'el-icon-s-data',
'/signalR': 'el-icon-bell',
'/littleScreen': 'el-icon-s-home',
'/mapBigscreen': 'el-icon-s-home',
'/Sys_Log': 'el-icon-s-order',
'/Sys_User': 'el-icon-user-solid',
'/permission': 'el-icon-s-check',
'/Sys_Dictionary': 'el-icon-s-management',
'/Sys_Role': 'el-icon-s-custom',
'/Sys_Role1': 'el-icon-s-custom',
'/Sys_DictionaryList': 'el-icon-s-grid',
'/SellOrder': 'el-icon-s-order',
'/SellOrder2': 'el-icon-s-order',
'/SellOrder3': 'el-icon-s-order',
'/UserInfo': 'el-icon-s-custom',
'/sysMenu': 'el-icon-s-operation',
'/formDraggable': 'el-icon-s-operation',
'/formSubmit': 'el-icon-s-release',
'/formCollectionResultTree': 'el-icon-s-data',
};
data.forEach((d) => {
d.path = (d.url || "").replace("/Manager", "");
d.to = (d.url || "").replace("/Manager", "");
// 如果后端没有设置图标或图标不是 el- 开头,则从映射表匹配
if ((!d.icon || d.icon.substring(0, 3) !== 'el-') && iconMap[d.path]) {
d.icon = iconMap[d.path];
}
});
store.dispatch("setPermission", data);
// 过滤导航栏:隐藏 id < 200 的页面,但保留"用户管理"、"权限管理"、"角色管理"
const keepNames = ["用户管理", "权限管理", "角色管理"];
menuOptions.value = data.filter(item => item.id >= 200 && item.name != '我的' && item.name != 'AI赛场' && item.name != '学练赛评一体化平台' && item.name != '活跃度统计' && item.name != 'AI测试' && item.name != 'AI体适能训练系统' && item.name != '数据驾驶舱' && item.name != '考级测评' && item.name != '区域级看板' || keepNames.includes(item.name));
// 初始化顶部导航选中项
const topItems = menuOptions.value.filter(x => x.parentId == 0);
if (topItems.length > 0) {
activeTopMenuId.value = topItems[0].id;
}
permissionInited.value = true;
//开启消息推送(main.js中设置是否开启signalR)2022.05.05
if (_config.$global.signalR) {
MessageConfig(http, (result) => {
messageList.unshift(result);
});
}
//当前刷新是不是首页
if (router.currentRoute.value.path != navigation[0].path) {
//查找系统菜单
let item = menuOptions.value.find((x) => {
return x.path == router.currentRoute.value.path; //this.$route.path;
});
if (item) return onSelect(item.id);
//查找顶部快捷连接
item = links.value.find((x) => {
return x.path == router.currentRoute.value.path; //this.$route.path;
});
//查找最后一次跳转的页面
if (!item) {
item = getItem();
}
if (item) {
return open(item, false);
}
}
selectId.value = "1";
} else {
ElMessage.error('登录已过期,请重新登录')
router.push({ path: '/login' })
}
});
};
created();
return {
menuWidth,
isCollapse,
topNavMenusRef,
activeTopMenuId,
activeTopMenuHasChildren,
topLevelMenus,
selectTopMenu,
drawer_model,
theme_color,
errorImg,
userInfo,
userName,
userImg,
selectId,
selectMenuIndex,
navigation,
links,
onSelect,
openTabsMenu,
selectNav,
getSelectMenuName,
removeNav,
logo,
theme,
menuOptions,
permissionInited,
changeTheme,
to,
toggleLeft,
messageModel,
messageList,
contextMenuVisible,
visibleItem,
closeTabsMenu,
closeTabs,
currentMenuId,
};
},
/**
* 挂载钩子函数
*/
mounted() {
let _date = showTime();
this.bindRightClickMenu(true);
// 顶部导航栏:将鼠标竖向滚轮转换为横向滚动
const nav = this.topNavMenusRef;
if (nav) {
this._navWheelHandler = (e) => {
if (e.deltaY !== 0) {
e.preventDefault();
nav.scrollLeft += e.deltaY;
}
};
nav.addEventListener('wheel', this._navWheelHandler, { passive: false });
}
},
methods: {
/**
* 绑定右键事件
* @param {*} enable 是否启用右键事件[true:启用;false:禁用;]
* @param {*} $event 事件
*/
bindRightClickMenu(enable) {
if (!enable) return;
let that = this;
// 使用原生js 为单个dom绑定鼠标右击事件
that.$nextTick(() => {
let tab_top_dom = Object.assign(
[],
document.getElementsByClassName("el-tabs__item is-top")
);
tab_top_dom.forEach((item, index) => {
item.oncontextmenu = that.openTabsMenu;
});
});
},
},
/**
* 销毁钩子函数
*/
destroyed() {
$this = null;
clearInterval($interval);
if (this._navWheelHandler && this.topNavMenusRef) {
this.topNavMenusRef.removeEventListener('wheel', this._navWheelHandler);
}
},
});
const week = new Array(
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
"星期日"
);
function showTime() {
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
let hour = date.getHours();
let minutes = date.getMinutes();
let second = date.getSeconds();
return (
year +
"." +
(month < 10 ? "0" + month : month) +
"." +
(day < 10 ? "0" + day : day) + //202.08.08修复日期天数小于10时添加0
"" +
" " +
(hour < 10 ? "0" + hour : hour) +
":" +
(minutes < 10 ? "0" + minutes : minutes) +
":" +
(second < 10 ? "0" + second : second) +
" " + //2020.08.30修复首页日期星期天不显示的问题
(week[date.getDay() - 1] || week[6])
);
}
</script>
<style lang="less" scoped>
.vol-container .vol-path ::v-deep(.el-tabs__content) {
padding: 0;
// border-bottom: 1px solid #e4e4e7;
}
.contextMenu {
width: 130px;
margin: 0;
border: 1px solid #e8ecf2;
background: #fff;
z-index: 30000;
position: absolute;
list-style-type: none;
padding: 4px;
border-radius: 10px;
font-size: 13px;
color: #333;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);
i,
button {
font-size: 13px !important;
}
}
.contextMenu li {
margin: 0;
padding: 2px 4px;
}
.contextMenu li:hover {
background: #f0f7ff;
cursor: pointer;
border-radius: 6px;
}
.contextMenu li button {
color: #626060;
font-size: 14px;
letter-spacing: 1px;
}
.el-tabs.el-tabs--top.el-tabs--border-card.header-navigation>.el-tabs__header .el-tabs__item:last-child,
.el-tabs--top.el-tabs--border-card.header-navigation>.el-tabs__header .el-tabs__item:nth-child(2) {
padding: 0 12px !important;
}
.header-navigation ::v-deep(.el-tabs__item.is-top) {
padding: 0 12px !important;
}
</style>
<style>
.horizontal-collapse-transition {
transition: 0s width ease-in-out, 0s padding-left ease-in-out,
0s padding-right ease-in-out;
}
/* ── 溢出导航下拉面板(Teleport 到 body,需全局样式) ── */
.top-nav-overflow-panel {
min-width: 160px;
background: #fff;
border-radius: 12px;
box-shadow: 0 10px 32px rgba(0, 0, 0, 0.14), 0 2px 8px rgba(0, 0, 0, 0.06);
padding: 5px;
border: 1px solid rgba(0, 0, 0, 0.06);
}
.top-nav-overflow-item {
padding: 9px 14px;
cursor: pointer;
border-radius: 8px;
color: #333;
font-size: 14px;
transition: all 0.18s;
white-space: nowrap;
}
.top-nav-overflow-item:hover {
background: #f0f7ff;
color: #2d8cf0;
}
.top-nav-overflow-item.is-active {
color: #2d8cf0;
font-weight: 600;
background: #e8f4ff;
}
.top-nav-more-fade-enter-active,
.top-nav-more-fade-leave-active {
transition: opacity 0.2s ease, transform 0.2s ease;
}
.top-nav-more-fade-enter-from,
.top-nav-more-fade-leave-to {
opacity: 0;
transform: translateY(-8px);
}
.header-navigation.el-tabs--border-card > .el-tabs__header .el-tabs__item .is-icon-close{
margin-left: 8px;
color: #000;
}
.header-navigation.el-tabs--border-card > .el-tabs__header .el-tabs__item .is-icon-close:hover{
color: #fff;
}
/* ── 标签栏 Chrome 风格激活色(全局高优先级,压过 EP border-card 内置样式) ── */
.header-navigation.el-tabs--border-card > .el-tabs__header .el-tabs__item {
color: #5f6368;
background: transparent;
border: none !important;
border-radius: 8px 8px 0 0 !important;
overflow: visible !important;
}
.header-navigation.el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active {
color: #2d8cf0 !important;
background: linear-gradient(90deg, #dbeeff 0%, #f0f8ff 100%) !important;
font-weight: normal;
border: none !important;
}
.header-navigation.el-tabs--border-card > .el-tabs__header .el-tabs__item:not(.is-active):hover {
color: #2d8cf0 !important;
background: #ebf4ff !important;
border-radius: 8px !important;
}
.header-navigation.el-tabs--border-card > .el-tabs__header {
background: #fff;
border-bottom: none;
}
</style>