|
|
|
|
<!--
|
|
|
|
|
* @Author: qinzhenpen qzp1807@126.com
|
|
|
|
|
* @Date: 2024-08-09 17:36:23
|
|
|
|
|
* @LastEditors: qinzhenpen qzp1807@126.com
|
|
|
|
|
* @LastEditTime: 2025-05-21 17:19:46
|
|
|
|
|
* @FilePath: \vue3\src\layout\components\Sidebar\index.vue
|
|
|
|
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
|
|
|
|
-->
|
|
|
|
|
<template>
|
|
|
|
|
<div :class="{ 'has-logo': showLogo }" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
|
|
|
|
|
<!-- <logo v-if="showLogo" :collapse="isCollapse" /> -->
|
|
|
|
|
<el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
|
|
|
|
|
<el-menu :default-openeds="['/demand/demand', '/product/product', '/test/test', '/channel/channel', '/assessment/assessment']" :default-active="activeMenu" :collapse="isCollapse" :background-color="sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground" :text-color="sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor" :unique-opened="false" :active-text-color="theme" :collapse-transition="false" mode="vertical">
|
|
|
|
|
<sidebar-item v-for="(route, index) in sidebarRouters" :key="route.path + index" :item="route" :base-path="route.path" />
|
|
|
|
|
</el-menu>
|
|
|
|
|
</el-scrollbar>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<script setup>
|
|
|
|
|
import Logo from "./Logo";
|
|
|
|
|
import SidebarItem from "./SidebarItem";
|
|
|
|
|
import variables from "@/assets/styles/variables.module.scss";
|
|
|
|
|
import useAppStore from "@/store/modules/app";
|
|
|
|
|
import * as indexApi from "@/api/teacher";
|
|
|
|
|
import useSettingsStore from "@/store/modules/settings";
|
|
|
|
|
import usePermissionStore from "@/store/modules/permission";
|
|
|
|
|
import { constantRoutes, dynamicRoutes, teacherRoutes, platformAdminRoutes, schoolAdminRoutes } from "@/router/index.js";
|
|
|
|
|
import useUserStore from "@/store/modules/user";
|
|
|
|
|
const { proxy } = getCurrentInstance();
|
|
|
|
|
const userStore = useUserStore();
|
|
|
|
|
const route = useRoute();
|
|
|
|
|
const appStore = useAppStore();
|
|
|
|
|
const settingsStore = useSettingsStore();
|
|
|
|
|
const permissionStore = usePermissionStore();
|
|
|
|
|
const sidebarRouters = ref([...constantRoutes]);
|
|
|
|
|
const routerStatus = {
|
|
|
|
|
行业需求分析: [
|
|
|
|
|
{
|
|
|
|
|
id: "9f413263-e6ff-4ee5-984a-7a231cad6818",
|
|
|
|
|
module: "行业需求分析-蛋糕指数",
|
|
|
|
|
sort: 1,
|
|
|
|
|
status: 0,
|
|
|
|
|
parentDisabled: true,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
竞争环境分析: [
|
|
|
|
|
{
|
|
|
|
|
id: "ae274ec6-971a-4a33-aa63-2e449f146487",
|
|
|
|
|
module: "竞争环境分析-赫芬达尔指数",
|
|
|
|
|
sort: 2,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
消费人群分析: [
|
|
|
|
|
{
|
|
|
|
|
id: "b91026aa-275c-4779-8602-61051a931d3a",
|
|
|
|
|
module: "消费人群分析-波士顿矩阵分析",
|
|
|
|
|
sort: 3,
|
|
|
|
|
status: 1,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: "191026aa-275c-4779-8602-61051a931d3a",
|
|
|
|
|
classId: "1",
|
|
|
|
|
schoolId: "1",
|
|
|
|
|
module: "消费人群分析-RFM分析",
|
|
|
|
|
status: 0,
|
|
|
|
|
sort: 3,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
用户数据分析: [
|
|
|
|
|
{
|
|
|
|
|
id: "4b4a685a-b9a3-4edc-9a68-a670d8c84c94",
|
|
|
|
|
module: "用户数据分析-描述性统计",
|
|
|
|
|
sort: 4,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: "1c6099e7-fc8a-4485-b828-8d1aef8e34b4",
|
|
|
|
|
module: "用户数据分析-聚类分析",
|
|
|
|
|
sort: 5,
|
|
|
|
|
status: 1,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
数据化选品: [
|
|
|
|
|
{
|
|
|
|
|
id: "fc9f19cb-8f77-4810-a538-eec97f840c0a",
|
|
|
|
|
module: "数据化选品-线性预测法-销售预测",
|
|
|
|
|
sort: 6,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
产品定价: [
|
|
|
|
|
{
|
|
|
|
|
id: "b95af20c-6242-42cd-a7c2-ddec7b9a0957",
|
|
|
|
|
module: "产品定价-PSM模型",
|
|
|
|
|
sort: 7,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
产品差异化定位: [
|
|
|
|
|
{
|
|
|
|
|
id: "4ca53e70-c168-455a-8dbd-f7d6475c5903",
|
|
|
|
|
module: "产品差异化定位-SPAN模型",
|
|
|
|
|
sort: 8,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
产品结构规划: [
|
|
|
|
|
{
|
|
|
|
|
id: "31f289b7-04a7-4b8b-b5b4-6333f7c3d76e",
|
|
|
|
|
module: "产品结构规划",
|
|
|
|
|
sort: 9,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
发布渠道评估: [
|
|
|
|
|
{
|
|
|
|
|
id: "b58ef210-624b-43f2-bbac-f788c8d96cc8",
|
|
|
|
|
module: "发布渠道评估-指标评估法-熵值法",
|
|
|
|
|
sort: 10,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
产品推广测试: [
|
|
|
|
|
{
|
|
|
|
|
id: "de9206fe-1e95-454f-912c-54dbd0a383cc",
|
|
|
|
|
module: "产品推广测试-A/B测试",
|
|
|
|
|
sort: 11,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
渠道表现分析: [
|
|
|
|
|
{
|
|
|
|
|
id: "2063efde-f8ba-4f69-be9b-1d87586044f6",
|
|
|
|
|
module: "渠道表现分析-转化分析-漏斗模型",
|
|
|
|
|
sort: 12,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
产品营销定位: [
|
|
|
|
|
{
|
|
|
|
|
id: "e99dee9f-a7a9-4e8c-a77b-8db6dfbc64d7",
|
|
|
|
|
module: "产品营销定位-四象限法则",
|
|
|
|
|
sort: 13,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
采购需求预测: [
|
|
|
|
|
{
|
|
|
|
|
id: "c8835d65-db31-4952-9d8d-5d07bf4a9c90",
|
|
|
|
|
module: "采购需求预测-季节指数预测法",
|
|
|
|
|
sort: 14,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
采购产品定位: [
|
|
|
|
|
{
|
|
|
|
|
id: "acd195cf-5e43-4c5e-8ec9-e5ad85612063",
|
|
|
|
|
module: "采购产品定位-卡拉杰克模型",
|
|
|
|
|
sort: 15,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
供应商评估与选择: [
|
|
|
|
|
{
|
|
|
|
|
id: "206e5b9a-95a0-41d7-b2f2-19f5cf10331a",
|
|
|
|
|
module: "供应商评估与选择-线性权重法",
|
|
|
|
|
sort: 16,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
产品表现分析: [
|
|
|
|
|
{
|
|
|
|
|
id: "0aca8b42-c665-4759-b1b4-b35a0daf2e80",
|
|
|
|
|
module: "产品表现分析-功效系数法",
|
|
|
|
|
sort: 17,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
产品问题诊断: [
|
|
|
|
|
{
|
|
|
|
|
id: "55a0ffd4-9863-40d8-bc4c-7830c731eaa7",
|
|
|
|
|
module: "产品问题诊断-五维四率法",
|
|
|
|
|
sort: 18,
|
|
|
|
|
status: 1,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: "205f28ce-31d8-449a-8053-7d6f85e98a68",
|
|
|
|
|
module: "产品问题诊断-KANO模型",
|
|
|
|
|
sort: 19,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: "2768d846-dfe8-42c1-b31f-f4deef85f34d",
|
|
|
|
|
module: "产品问题诊断-市场机会矩阵",
|
|
|
|
|
sort: 20,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: "d0fc3cb4-2cde-401f-b243-33bdb5ef1094",
|
|
|
|
|
module: "产品问题诊断-流量数据分析法",
|
|
|
|
|
sort: 21,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: "25896e1a-b099-4778-bd87-3b92aa8845da",
|
|
|
|
|
module: "产品问题诊断-用户评论分析",
|
|
|
|
|
sort: 22,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
品类结构优化: [
|
|
|
|
|
{
|
|
|
|
|
id: "6bbeebf1-662a-46d6-aa4a-e5e8c4c3d5f1",
|
|
|
|
|
module: "品类结构优化-帕累托/ABC分析",
|
|
|
|
|
sort: 23,
|
|
|
|
|
status: 1,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: "90212d7c-d8b5-4455-bc7a-693ded4b3f8f",
|
|
|
|
|
module: "品类结构优化-购物篮分析",
|
|
|
|
|
sort: 24,
|
|
|
|
|
status: 0,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
const showLogo = computed(() => settingsStore.sidebarLogo);
|
|
|
|
|
const sideTheme = computed(() => settingsStore.sideTheme);
|
|
|
|
|
const theme = computed(() => settingsStore.theme);
|
|
|
|
|
const isCollapse = computed(() => !appStore.sidebar.opened);
|
|
|
|
|
const activeMenu = computed(() => {
|
|
|
|
|
const { meta, path } = route;
|
|
|
|
|
// if set path, the sidebar will highlight the path you set
|
|
|
|
|
if (meta.activeMenu) {
|
|
|
|
|
return meta.activeMenu;
|
|
|
|
|
}
|
|
|
|
|
return path;
|
|
|
|
|
});
|
|
|
|
|
function groupAndSortModules(modules) {
|
|
|
|
|
const grouped = modules.reduce((acc, item) => {
|
|
|
|
|
const prefix = item.module.split("-")[0].trim();
|
|
|
|
|
|
|
|
|
|
if (!acc[prefix]) {
|
|
|
|
|
acc[prefix] = [];
|
|
|
|
|
}
|
|
|
|
|
acc[prefix].push({
|
|
|
|
|
id: item.id,
|
|
|
|
|
module: item.module,
|
|
|
|
|
sort: item.sort,
|
|
|
|
|
status: item.disabledStatus,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return acc;
|
|
|
|
|
}, {});
|
|
|
|
|
|
|
|
|
|
// 对每个分组按sort排序
|
|
|
|
|
Object.keys(grouped).forEach((key) => {
|
|
|
|
|
grouped[key].sort((a, b) => a.sort - b.sort);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return grouped;
|
|
|
|
|
}
|
|
|
|
|
function processModuleStatus(groupedData) {
|
|
|
|
|
const result = {
|
|
|
|
|
data: JSON.parse(JSON.stringify(groupedData)), // 原始数据副本
|
|
|
|
|
disabledParents: [], // 被禁用的父模块列表(所有子模块都禁用)
|
|
|
|
|
activeParents: [], // 活跃的父模块列表
|
|
|
|
|
disabledChildren: [], // 被禁用的子模块(仅当父模块有多个子模块时)
|
|
|
|
|
parentStatus: {}, // 每个父模块的详细状态
|
|
|
|
|
};
|
|
|
|
|
// 分析每个父模块状态
|
|
|
|
|
for (const [parentModule, children] of Object.entries(result.data)) {
|
|
|
|
|
const disabledCount = children.filter((child) => child.status === 1).length;
|
|
|
|
|
const totalCount = children.length;
|
|
|
|
|
result.parentStatus[parentModule] = {
|
|
|
|
|
disabledCount,
|
|
|
|
|
totalCount,
|
|
|
|
|
isDisabled: disabledCount === totalCount && totalCount > 0,
|
|
|
|
|
};
|
|
|
|
|
// 只有当父模块有多个子模块时,才收集被禁用的子模块
|
|
|
|
|
if (totalCount > 1) {
|
|
|
|
|
children.forEach((child) => {
|
|
|
|
|
if (child.status === 1) {
|
|
|
|
|
result.disabledChildren.push({
|
|
|
|
|
parentModule,
|
|
|
|
|
...child,
|
|
|
|
|
module: child.module.split("-")[1].trim(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (result.parentStatus[parentModule].isDisabled) {
|
|
|
|
|
result.disabledParents.push(parentModule);
|
|
|
|
|
children.forEach((child) => {
|
|
|
|
|
child.parentDisabled = true; // 标记父模块禁用状态
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
result.activeParents.push(parentModule);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 过滤掉 children 中 meta.title 在排除列表里的路由
|
|
|
|
|
* @param {Array} routes 原始路由数组
|
|
|
|
|
* @param {Array} excludeTitles 要排除的 meta.title 列表
|
|
|
|
|
* @returns {Array} 新的路由数组
|
|
|
|
|
*/
|
|
|
|
|
function excludeRoutesByMetaTitle(routes, excludeTitles) {
|
|
|
|
|
return routes.reduce((result, route) => {
|
|
|
|
|
if (Array.isArray(route.children)) {
|
|
|
|
|
const filteredChildren = route.children.filter((child) => !excludeTitles.includes(child.meta?.title));
|
|
|
|
|
if (filteredChildren.length > 0) {
|
|
|
|
|
result.push({ ...route, children: filteredChildren });
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
result.push(route); // 没有 children 的保留原样
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}, []);
|
|
|
|
|
}
|
|
|
|
|
const getTaskList = () => {
|
|
|
|
|
if (userStore.userInfo.roleId == 1) {
|
|
|
|
|
sidebarRouters.value = [...constantRoutes, ...platformAdminRoutes];
|
|
|
|
|
} else if (userStore.userInfo.roleId == 2) {
|
|
|
|
|
sidebarRouters.value = [...constantRoutes, ...schoolAdminRoutes];
|
|
|
|
|
} else if (userStore.userInfo.roleId != 3) {
|
|
|
|
|
indexApi.getTaskList({ classId: userStore.userInfo.classId }).then((res) => {
|
|
|
|
|
const disabledTitles = processModuleStatus(groupAndSortModules(res.data)).disabledParents;
|
|
|
|
|
console.log(processModuleStatus(groupAndSortModules(res.data)));
|
|
|
|
|
proxy.$cache.session.setJSON("disabledChildren", processModuleStatus(groupAndSortModules(res.data)).disabledChildren);
|
|
|
|
|
proxy.$cache.session.setJSON("disabledTitles", disabledTitles);
|
|
|
|
|
sidebarRouters.value = [...constantRoutes, ...dynamicRoutes];
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
sidebarRouters.value = [...constantRoutes, ...teacherRoutes];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
getTaskList();
|
|
|
|
|
});
|
|
|
|
|
</script>
|