7 changed files with 490 additions and 27 deletions
-
2index.html
-
237package-lock.json
-
2package.json
-
32src/App.vue
-
BINsrc/assets/images/deepChart.png
-
229src/layout/Layout.vue
-
15src/main.js
@ -1,30 +1,14 @@ |
|||||
<script setup> |
|
||||
import HelloWorld from './components/HelloWorld.vue' |
|
||||
</script> |
|
||||
|
|
||||
<template> |
<template> |
||||
<div> |
|
||||
<a href="https://vite.dev" target="_blank"> |
|
||||
<img src="/vite.svg" class="logo" alt="Vite logo" /> |
|
||||
</a> |
|
||||
<a href="https://vuejs.org/" target="_blank"> |
|
||||
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" /> |
|
||||
</a> |
|
||||
</div> |
|
||||
<HelloWorld msg="Vite + Vue" /> |
|
||||
|
<router-view /> |
||||
</template> |
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
</script> |
||||
|
|
||||
<style scoped> |
<style scoped> |
||||
.logo { |
|
||||
height: 6em; |
|
||||
padding: 1.5em; |
|
||||
will-change: filter; |
|
||||
transition: filter 300ms; |
|
||||
} |
|
||||
.logo:hover { |
|
||||
filter: drop-shadow(0 0 2em #646cffaa); |
|
||||
} |
|
||||
.logo.vue:hover { |
|
||||
filter: drop-shadow(0 0 2em #42b883aa); |
|
||||
|
* { |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
box-sizing: border-box; |
||||
} |
} |
||||
</style> |
</style> |
||||
|
After Width: 48 | Height: 48 | Size: 1.9 KiB |
@ -0,0 +1,229 @@ |
|||||
|
<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="currentRoutePath" |
||||
|
:unique-opened="true" |
||||
|
> |
||||
|
<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> |
||||
|
|
||||
|
<!-- 子目录 --> |
||||
|
<el-menu-item |
||||
|
v-for="childRoute in parentRoute.filteredChildren" |
||||
|
:key="childRoute.name" |
||||
|
:index="`/${parentRoute.path}/${childRoute.path}`" |
||||
|
:to="`/${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> |
||||
|
</el-sub-menu> |
||||
|
</el-menu> |
||||
|
</el-aside> |
||||
|
|
||||
|
<!-- 主页面 --> |
||||
|
<el-main class="main-content"> |
||||
|
<router-view /> |
||||
|
</el-main> |
||||
|
</el-container> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { computed } from 'vue' |
||||
|
import { useRouter, useRoute } from 'vue-router' |
||||
|
|
||||
|
const router = useRouter() |
||||
|
const route = useRoute() |
||||
|
|
||||
|
// 递归收集父路由 |
||||
|
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 filteredSidebarRoutes = computed(() => { |
||||
|
const allParentRoutes = collectParentRoutes(router.options.routes); |
||||
|
return allParentRoutes.map(parentRoute => { |
||||
|
const filteredChildren = parentRoute.children?.filter(childRoute => |
||||
|
childRoute.meta?.showSidebar === true |
||||
|
) || []; |
||||
|
return { ...parentRoute, filteredChildren }; |
||||
|
}).filter(parentRoute => parentRoute.filteredChildren.length > 0); |
||||
|
}); |
||||
|
|
||||
|
// 计算当前路由的绝对路径 |
||||
|
const currentRoutePath = computed(() => route.path); |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 侧边栏核心样式 */ |
||||
|
.sidebar { |
||||
|
position: fixed; |
||||
|
left: 24px; |
||||
|
width: 336px; |
||||
|
height: 100vh; |
||||
|
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; |
||||
|
} |
||||
|
|
||||
|
/* 主内容区样式 */ |
||||
|
.main-content { |
||||
|
padding: 20px; |
||||
|
background-color: #FEE6E6; |
||||
|
height: 100vh; |
||||
|
margin-left: 50px; |
||||
|
box-shadow: 0 0 4px 0 #00000040; |
||||
|
} |
||||
|
|
||||
|
/* 父目录文字样式 */ |
||||
|
.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-child-container.is-active { |
||||
|
background: #FFFFFF !important; /* 选中后容器变为白色 */ |
||||
|
} |
||||
|
|
||||
|
/* 选中态文字样式 */ |
||||
|
.sidebar-child-container.is-active .sidebar-child-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; |
||||
|
} |
||||
|
</style> |
||||
@ -1,8 +1,19 @@ |
|||||
import {createApp} from 'vue' |
|
||||
import './style.css' |
|
||||
|
import { createApp } from 'vue' |
||||
import App from './App.vue' |
import App from './App.vue' |
||||
import router from './router' |
import router from './router' |
||||
|
// 导入 Element Plus 样式和组件
|
||||
|
import ElementPlus from 'element-plus' |
||||
|
import 'element-plus/dist/index.css' |
||||
|
// 导入 Element Plus 图标
|
||||
|
import * as ElementPlusIconsVue from '@element-plus/icons-vue' |
||||
|
|
||||
const app = createApp(App) |
const app = createApp(App) |
||||
|
|
||||
|
for (const [key, component] of Object.entries(ElementPlusIconsVue)) { |
||||
|
app.component(key, component) |
||||
|
} |
||||
|
|
||||
|
app.use(ElementPlus) |
||||
app.use(router) |
app.use(router) |
||||
|
|
||||
app.mount('#app') |
app.mount('#app') |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue