deepchart后台管理系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

472 lines
13 KiB

<!-- @format -->
<template>
<el-container style="min-height: 100vh">
<el-aside class="sidebar" v-if="!$route.meta.hiddenSidebar">
<!-- 侧边栏头部 -->
<div class="sidebar-header">
<img src="../assets/images/deepChart.png" class="sidebar-logo" />
<span class="sidebar-title">DeepChart</span>
</div>
<!-- 侧边栏菜单 -->
<el-menu class="sidebar-menu" background-color="transparent" router :default-active="lastActivePath">
<!-- 第一层循环遍历父路由 (: 用户权限管理活动管理) -->
<el-sub-menu v-for="parentRoute in filteredSidebarRoutes" :key="parentRoute.name" :index="`/${parentRoute.path}`">
<!-- 父目录 -->
<template #title>
<el-icon>
<component :is="parentRoute.meta.icon" />
</el-icon>
<span class="sidebar-parent-text">{{ parentRoute.meta.title }}</span>
</template>
<!-- 第二层循环:遍历子路由 -->
<template v-for="childRoute in parentRoute.filteredChildren" :key="childRoute.name">
<!-- 如果有孙子菜单(如:新年幸运签),渲染为【子目录 el-sub-menu】 -->
<el-sub-menu v-if="childRoute.children && childRoute.children.length > 0" :index="`/${parentRoute.path}/${childRoute.path}`">
<template #title>
<el-icon class="sidebar-child-icon" />
<span class="sidebar-child-text">{{ childRoute.meta.title }}</span>
</template>
<!-- 第三层循环:遍历孙子路由 (如: 历史记录、内容配置) -->
<el-menu-item v-for="grandChild in childRoute.children" :key="grandChild.name" :index="`/${parentRoute.path}/${childRoute.path}/${grandChild.path}`" class="sidebar-grandchild-container">
<span class="sidebar-grandchild-text">{{ grandChild.meta.title }}</span>
</el-menu-item>
</el-sub-menu>
<!-- 如果没有子菜单(如:行情期限),渲染为【点击项 el-menu-item】 -->
<el-menu-item v-else :index="`/${parentRoute.path}/${childRoute.path}`" class="sidebar-child-container">
<el-icon class="sidebar-child-icon" />
<span class="sidebar-child-text">{{ childRoute.meta.title }}</span>
</el-menu-item>
</template>
</el-sub-menu>
</el-menu>
<div class="sidebar-logout" @click="handleLogout">
<el-icon style="bottom: -3px"><SwitchButton /></el-icon>
退出登录
</div>
<div class="sidebar-set" @click="handleSet" v-if="permission == '2'">
<el-icon style="bottom: -3px"><Setting /></el-icon>
设置
</div>
</el-aside>
<!-- 首页刷新时间弹窗 -->
<el-dialog v-model="setValue" title="设置" width="500">
<div class="refresh-time-container">
<el-button type="danger">首页刷新时间</el-button>
<el-form-item label="刷新时间" style="margin-top: 20px; margin-bottom: 40px">
<el-input-number v-model="refreshTime" :step="1" placeholder="请输入刷新时间" style="width: 200px">
<template #suffix>
<span>s</span>
</template>
</el-input-number>
</el-form-item>
<el-button type="danger">Android下载链接配置</el-button>
<el-form-item label="编辑链接" style="margin-top: 20px; margin-bottom: 40px">
<el-input v-model="Androidurl" style="width: 300px" placeholder="请输入最新Android下载链接" />
</el-form-item>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="setValue = false">取消</el-button>
<el-button type="danger" @click="setEnv">确认修改</el-button>
</div>
</template>
</el-dialog>
<!-- 主页面 -->
<el-main class="main-content">
<router-view />
</el-main>
</el-container>
</template>
<script setup>
import { computed, ref, watch, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import { ElMessage } from "element-plus";
import { getEnvApi, setEnvApi } from "../api/userPermissions";
import { usePermissionStore } from "../store/permission";
const permissionStore = usePermissionStore();
const router = useRouter();
const route = useRoute();
// token
const token = localStorage.getItem("token");
//permission
const permission = ref("-1");
// 递归收集父路由
const collectParentRoutes = (routes) => {
let parentRoutes = [];
routes.forEach((route) => {
if (route.meta?.isParentNav === true && route.meta?.showSidebar === true) {
parentRoutes.push(route);
}
if (route.children && route.children.length > 0) {
parentRoutes = [...parentRoutes, ...collectParentRoutes(route.children)];
}
});
return parentRoutes;
};
const getPermission = async () => {
const result = await getPermissionApi({ token: token });
console.log("result", result);
permission.value = result.permission_level;
};
// 过滤侧边栏路由
const filteredSidebarRoutes = computed(() => {
const allParentRoutes = collectParentRoutes(router.options.routes);
return allParentRoutes
.map((parentRoute) => {
// 先获取 children,如果为空则设为 []
const rawChildren = parentRoute.children || [];
const filteredChildren = rawChildren
.map((childRoute) => {
// 浅拷贝 childRoute,避免直接修改原始路由对象
const newChild = { ...childRoute };
if (newChild.children && newChild.children.length > 0) {
newChild.children = newChild.children.filter(
(grandChild) => grandChild.meta?.showSidebar === true
);
}
return newChild;
})
.filter((childRoute) => {
// 必须标记为显示侧边栏
if (childRoute.meta?.showSidebar !== true) return false;
// 权限判断
if (permission.value == "2") {
// 权限为2可以看到全部
return true;
} else if (permission.value == "1") {
// 权限为1只可以看到查看被邀请用户
return childRoute.name === "invitedLook";
}
// 默认情况(如未登录或无权限字段),根据实际需求处理,这里暂时隐藏
return false;
});
return { ...parentRoute, filteredChildren };
})
.filter((parentRoute) => parentRoute.filteredChildren.length > 0);
});
// 计算所有侧边栏有效菜单集合
const validMenuIndexes = computed(() => {
const indexes = [];
filteredSidebarRoutes.value.forEach((parentRoute) => {
// 收集父菜单
indexes.push(`/${parentRoute.path}`);
// 收集子菜单
parentRoute.filteredChildren.forEach((childRoute) => {
indexes.push(`/${parentRoute.path}/${childRoute.path}`);
// 收集孙子菜单
if (childRoute.children && childRoute.children.length > 0) {
childRoute.children.forEach((grandChild) => {
// 收集三级菜单路径 (确保拼接正确)
indexes.push(`/${parentRoute.path}/${childRoute.path}/${grandChild.path}`);
});
}
});
});
return indexes;
});
// 存储最后一次选中的「有效侧边栏路径」
const lastActivePath = ref("");
// 初始化+监听路由变化,更新最后有效路径
watch(
[() => route.path, validMenuIndexes],
([newPath, newIndexes]) => {
// 如果新路由是侧边栏有效路径,更新,否则不跟新
if (newIndexes.includes(newPath)) {
lastActivePath.value = newPath;
}
},
{ immediate: true } // 初始化时立即执行一次
);
// 退出登录
const handleLogout = () => {
try {
localStorage.removeItem("token");
router.push("/login");
ElMessage.success("退出登录成功");
} catch (error) {
ElMessage.error("退出登录失败,请重试");
}
};
// 设置弹窗开关
const setValue = ref(false);
// 首页刷新时间
const refreshTime = ref("");
// 安卓最新安装包
const Androidurl = ref("");
// 设置按钮
const handleSet = async () => {
const data1 = await getEnvApi({
token: token,
key: "SYNC_INTERVAL",
});
const data2 = await getEnvApi({
token: token,
key: "DOWNLOAD_URL",
});
refreshTime.value = data1;
Androidurl.value = data2;
setValue.value = true;
};
// 确认修改按钮
const setEnv = async () => {
try {
await setEnvApi({
token: token,
key: "SYNC_INTERVAL",
value: refreshTime.value,
});
await setEnvApi({
token: token,
key: "DOWNLOAD_URL",
value: Androidurl.value,
});
ElMessage.success("修改成功");
setValue.value = false;
} catch (error) {
ElMessage.error("修改失败");
setValue.value = false;
}
};
onMounted(async () => {
permission.value = await permissionStore.getPermission();
});
</script>
<style scoped>
/* 侧边栏核心样式 */
.sidebar {
position: fixed;
left: 24px;
top: 10px;
width: 336px;
height: calc(100vh - 20px);
flex-shrink: 0;
border-radius: 8px;
background: #fee6e6;
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.25);
overflow: hidden;
position: relative;
}
/* 侧边栏头部样式 */
.sidebar-header {
position: absolute;
left: 45px;
top: 59px;
display: flex;
align-items: center;
}
.sidebar-logo {
height: 40px;
width: auto;
object-fit: contain;
}
.sidebar-title {
margin-left: 10px;
color: #000000;
font-family: "PingFang SC", sans-serif;
font-size: 32px;
font-style: normal;
font-weight: 700;
line-height: 33.1px;
white-space: nowrap;
}
/* 侧边栏菜单容器 */
.sidebar-menu {
margin-left: 20px;
margin-top: 169px;
width: calc(100% - 20px);
border-right: none;
height: 70%;
overflow-y: auto;
overflow-x: hidden;
}
/* 主内容区样式 */
.main-content {
padding: 20px;
background-color: #fee6e6;
height: calc(100vh - 20px);
margin-left: 50px;
margin-right: 24px;
margin-top: 10px;
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.25);
}
/* 父目录文字样式 */
.sidebar-parent-text {
height: 33.1px;
flex: 1 0 0;
overflow: hidden;
color: #1f0303;
text-overflow: ellipsis;
white-space: nowrap;
font-family: "PingFang SC", sans-serif;
font-size: 21.06px;
font-style: normal;
font-weight: 700;
line-height: 33.1px;
display: inline-flex;
align-items: center;
}
/* 子目录核心样式 */
.sidebar-child-container {
width: 300px !important;
height: 60px !important;
margin-left: 2px !important;
background: #fee6e6 !important;
position: relative;
padding: 0 !important;
border-radius: 6.02px !important;
box-sizing: border-box !important;
}
/* 子目录图标样式 */
.sidebar-child-icon {
position: absolute;
left: 48px;
top: 50%;
transform: translateY(-50%);
font-size: 18px !important;
color: inherit;
}
/* 子目录文字样式(未选中) */
.sidebar-child-text {
position: absolute;
left: 58px;
top: 20px;
height: 33.1px;
overflow: hidden;
color: #03071f;
text-overflow: ellipsis;
white-space: nowrap;
font-family: "PingFang SC", sans-serif;
font-size: 18px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
.sidebar-grandchild-text {
position: absolute;
left: 78px;
top: 15px;
height: 33px;
overflow: hidden;
color: #03071f;
text-overflow: ellipsis;
white-space: nowrap;
font-family: "PingFang SC", sans-serif;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
/* 子目录选中态样式 */
.sidebar-child-container.is-active,.sidebar-grandchild-container.is-active {
background: #ffffff !important; /* 选中后容器变为白色 */
}
/* 选中态文字样式 */
.sidebar-child-container.is-active .sidebar-child-text,.sidebar-grandchild-container.is-active .sidebar-grandchild-text {
color: #ff0000 !important; /* 选中后文字红色 */
font-weight: 500;
}
/* 覆盖Element Plus默认样式 */
.el-sub-menu__title {
height: 33.1px !important;
line-height: 33.1px !important;
padding: 0 !important;
}
.el-menu-item {
border: none !important;
}
.el-menu--vertical .el-menu-item {
width: 300px !important;
}
/* 退出登录 */
.sidebar-logout {
position: absolute;
bottom: 30px;
left: 10%;
color: #1f0303;
font-family: "PingFang SC", sans-serif;
font-size: 21.06px;
font-style: normal;
font-weight: 700;
line-height: 33.1px;
cursor: pointer;
user-select: none;
transition: color 0.2s ease;
}
/* 退出登录hover效果 */
.sidebar-logout:hover {
color: #ff0000;
}
/* 设置 */
.sidebar-set {
position: absolute;
bottom: 30px;
left: 60%;
color: #1f0303;
font-family: "PingFang SC", sans-serif;
font-size: 21.06px;
font-style: normal;
font-weight: 700;
line-height: 33.1px;
cursor: pointer;
user-select: none;
transition: color 0.2s ease;
}
/* 设置hover效果 */
.sidebar-set:hover {
color: #ff0000;
}
</style>