| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- <!-- layout/components/Sidebar/index.vue -->
- <template>
- <div class="sidebar-container" :class="{ collapsed: isCollapse }">
- <div class="menu-list">
- <div
- v-for="item in menuList"
- :key="item.path"
- :class="['menu-item', { active: isActive(item.path) }]"
- @click="handleMenuClick(item)"
- >
- <!-- 使用原本的 svg 图标 -->
- <svg-icon
- v-if="item.meta?.icon"
- :icon-class="item.meta.icon"
- class="menu-icon"
- />
- <span class="menu-text">{{ item.meta?.title }}</span>
- </div>
- </div>
- <div class="collapse-btn" @click="toggleCollapse">
- <span class="btn-text">{{ isCollapse ? "▶" : "◀" }}</span>
- </div>
- </div>
- </template>
- <script>
- import { computed } from "vue";
- import { useRoute, useRouter } from "vue-router";
- import useAppStore from "@/store/modules/app";
- import usePermissionStore from "@/store/modules/permission";
- export default {
- name: "Sidebar",
- props: {
- topMenuKey: {
- type: String,
- default: "",
- },
- },
- setup(props) {
- const route = useRoute();
- const router = useRouter();
- const appStore = useAppStore();
- const permissionStore = usePermissionStore();
- const isCollapse = computed(() => appStore.sidebar.isCollapse);
- const menuList = computed(() => {
- const routes = permissionStore.sidebarRouters || [];
- if (!routes.length) return [];
- if (props.topMenuKey) {
- const current = routes.find((item) => item.path === props.topMenuKey);
- if (current && current.children) {
- return current.children.filter((child) => !child.hidden);
- }
- }
- const firstMenu = routes[0];
- if (firstMenu && firstMenu.children) {
- return firstMenu.children.filter((child) => !child.hidden);
- }
- return [];
- });
- const isActive = (path) => {
- return route.path.includes(path);
- };
- const handleMenuClick = (item) => {
- let fullPath = item.path;
- if (!fullPath.startsWith("/") && props.topMenuKey) {
- fullPath = props.topMenuKey + "/" + fullPath;
- }
- router.push(fullPath);
- };
- const toggleCollapse = () => {
- appStore.sidebar.isCollapse = !appStore.sidebar.isCollapse;
- };
- return {
- menuList,
- isCollapse,
- isActive,
- handleMenuClick,
- toggleCollapse,
- };
- },
- };
- </script>
- <style scoped>
- .sidebar-container {
- width: 210px;
- background-color: #304156;
- height: 100%;
- transition: width 0.2s;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- }
- .sidebar-container.collapsed {
- width: 64px;
- }
- .menu-list {
- flex: 1;
- overflow-y: auto;
- padding: 10px 0;
- }
- .menu-item {
- padding: 0 20px;
- height: 44px;
- line-height: 44px;
- color: #bfcbd9;
- cursor: pointer;
- white-space: nowrap;
- display: flex;
- align-items: center;
- gap: 12px;
- }
- /* 收起时的样式 */
- .sidebar-container.collapsed .menu-item {
- justify-content: center;
- padding: 0;
- }
- .menu-item:hover {
- background-color: #263445;
- color: #fff;
- }
- .menu-item.active {
- background-color: #409eff;
- color: #fff;
- }
- /* 图标样式 */
- .menu-icon {
- width: 20px;
- height: 20px;
- flex-shrink: 0;
- }
- /* 文字样式 */
- .menu-text {
- font-size: 14px;
- }
- /* 收起时隐藏文字 */
- .sidebar-container.collapsed .menu-text {
- display: none;
- }
- /* 收起时图标居中 */
- .sidebar-container.collapsed .menu-icon {
- margin: 0;
- }
- .collapse-btn {
- height: 44px;
- line-height: 44px;
- text-align: center;
- color: #bfcbd9;
- cursor: pointer;
- border-top: 1px solid #1f2d3d;
- }
- .collapse-btn:hover {
- background-color: #263445;
- color: #fff;
- }
- .btn-text {
- font-size: 16px;
- }
- </style>
|