| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- <!-- layout/components/Sidebar/SidebarItem.vue -->
- <template>
- <template v-if="!item.hidden">
- <!-- 有子菜单 -->
- <div v-if="item.children && item.children.length > 0" class="sub-menu">
- <div class="sub-menu-title" @click="toggleSubGroup">
- <svg-icon
- v-if="item.meta?.icon"
- :icon-class="item.meta.icon"
- class="menu-icon"
- />
- <span class="menu-text" v-if="!isCollapse">{{ item.meta?.title }}</span>
- <span class="sub-arrow" v-if="!isCollapse">
- {{ expandedSubGroup ? "▼" : "▶" }}
- </span>
- </div>
- <div class="sub-menu-items" v-show="expandedSubGroup || isCollapse">
- <sidebar-item
- v-for="child in item.children"
- :key="child.path"
- :item="child"
- :parent-path="getChildFullPath(child.path)"
- :is-collapse="isCollapse"
- :active-path="activePath"
- @menu-click="handleMenuClick"
- />
- </div>
- </div>
- <!-- 没有子菜单 -->
- <div
- v-else
- :class="[
- 'sub-menu-item',
- {
- active:
- activePath === getFullPath ||
- activePath.startsWith(getFullPath + '/'),
- },
- ]"
- @click="handleMenuClick(getFullPath)"
- >
- <svg-icon
- v-if="item.meta?.icon"
- :icon-class="item.meta.icon"
- class="menu-icon"
- />
- <span class="menu-text">{{ item.meta?.title }}</span>
- </div>
- </template>
- </template>
- <script>
- import { ref, watch } from "vue";
- export default {
- name: "SidebarItem",
- props: {
- item: {
- type: Object,
- required: true,
- },
- parentPath: {
- type: String,
- default: "",
- },
- isCollapse: {
- type: Boolean,
- default: false,
- },
- activePath: {
- type: String,
- default: "",
- },
- },
- emits: ["menu-click"],
- setup(props, { emit }) {
- const expandedSubGroup = ref(false);
- const getFullPath = computed(() => {
- let path = props.item.path;
- if (!path.startsWith("/") && props.parentPath) {
- path = props.parentPath + "/" + path;
- }
- return path;
- });
- const getChildFullPath = (childPath) => {
- if (childPath.startsWith("/")) {
- return childPath;
- }
- return getFullPath.value;
- };
- const toggleSubGroup = () => {
- expandedSubGroup.value = !expandedSubGroup.value;
- };
- const handleMenuClick = (path) => {
- emit("menu-click", path);
- };
- // 自动展开当前路由对应的父级菜单
- watch(
- () => props.activePath,
- (newPath) => {
- if (
- newPath === getFullPath.value ||
- newPath.startsWith(getFullPath.value + "/")
- ) {
- expandedSubGroup.value = true;
- }
- },
- { immediate: true },
- );
- return {
- expandedSubGroup,
- getFullPath,
- getChildFullPath,
- toggleSubGroup,
- handleMenuClick,
- };
- },
- };
- </script>
- <style scoped>
- .sub-menu {
- margin-bottom: 2px;
- }
- .sub-menu-title {
- padding: 0 20px 0 44px;
- height: 40px;
- line-height: 40px;
- color: #bfcbd9;
- cursor: pointer;
- display: flex;
- align-items: center;
- gap: 12px;
- position: relative;
- }
- .sub-menu-title:hover {
- background-color: #263445;
- color: #fff;
- }
- .sub-arrow {
- position: absolute;
- right: 20px;
- font-size: 12px;
- }
- .sub-menu-items {
- overflow: hidden;
- }
- .sub-menu-item {
- padding: 0 20px 0 68px;
- height: 36px;
- line-height: 36px;
- color: #bfcbd9;
- cursor: pointer;
- white-space: nowrap;
- display: flex;
- align-items: center;
- gap: 12px;
- }
- .sub-menu-item:hover {
- background-color: #263445;
- color: #fff;
- }
- .sub-menu-item.active {
- background-color: #409eff;
- color: #fff;
- }
- /* 收起时的样式 */
- .sidebar-container.collapsed .sub-menu-title,
- .sidebar-container.collapsed .sub-menu-item {
- justify-content: center;
- padding: 0;
- }
- .sidebar-container.collapsed .menu-text,
- .sidebar-container.collapsed .sub-arrow {
- display: none;
- }
- </style>
|