后台基本框架都是菜单栏和显示内容区域,如下图
下面我们来搭建一个统一的框架,可以入手cv直接使用
页面布局搭建,创建一个layout的主界面,分侧边栏块和主内容块
<template>
<div class="root">
<div class="leftPage">
<MenuHome />
</div>
<div class="contentPage">
<ContentHome />
</div>
</div>
</template>
<script>
import MenuHome from "./LeftMenu/MenuHome.vue";
import ContentHome from "./Content/ContentHome.vue";
export default {
components: {
MenuHome,
ContentHome
}
};
</script>
<style scoped lang="scss">
.root {
width: 100%;
height: 100%;
display: flex;
.leftPage {
width: 300px;
height: 100%;
}
.contentPage {
width: calc(100% - 300px);
height: 100%;
}
}
</style>
编写侧边栏组件MenuHome
注意:Icon 组件在上一篇文章有介绍,具体可以参看上篇文章
<template>
<div class="menu">
<a-button
type="primary"
style="margin-bottom: 16px"
@click="toggleCollapsed"
>
<MenuUnfoldOutlined v-if="collapsed" />
<MenuFoldOutlined v-else />
</a-button>
<a-menu
v-model:openKeys="openKeys"
v-model:selectedKeys="selectedKeys"
mode="inline"
theme="light"
:inlineCollapsed="collapsed"
>
<template v-for="item in routerList" :key="item.path">
<template v-if="!item.hidden">
<template v-if="!item.children">
<a-menu-item :key="item.path">
<template #icon>
<Icon :icon="item.meta && item.meta.icon"></Icon>
</template>
<router-link :to="item.path">
{{ item.meta && item.meta.title }}</router-link
>
</a-menu-item>
</template>
<template v-else>
<SubMenu :key="item.path" :menu-info="item" />
</template>
</template>
</template>
<!-- <SlidebarItem v-for="item in routerList" :key="item.path" :child="item" /> -->
</a-menu>
</div>
</template>
<script setup>
import { useRouter, useRoute } from "vue-router";
// import SlidebarItem from "./SlidebarItem.vue";
import SubMenu from "./SubMenu.vue";
import { Icon } from "../Icon";
// 菜单数组
const router = useRouter();
const routerList = ref(router.options.routes);
routerList.value = routerList.value[1].children || [];
// 路由信息
const route = useRoute();
// 展开&收起按钮
const collapsed = ref(false);
// 展开keys
let openKeys = ref([]);
let selectedKeys = ref([route.path]);
// 保存老keys
let preOpenKeys = ref([]);
openKeys.value = route.matched
.slice(1, route.matched.length - 1)
.reduce((pre, next) => {
pre.push(next.path);
return pre;
}, []);
watch(
() => openKeys.value,
(_val, oldVal) => {
preOpenKeys.value = oldVal;
}
);
// 展开&收起菜单
const toggleCollapsed = () => {
collapsed.value = !collapsed.value;
openKeys.value = collapsed.value ? [] : preOpenKeys.value;
};
</script>
<style scoped>
.menu {
width: 100%;
height: 100%;
border: 1px solid pink;
}
</style>
编写侧边栏的SubMenu 子菜单的组件,由于菜单个数不确定性,采用递归渲染组件
<template>
<a-sub-menu :key="menuInfo.path">
<template #icon>
<Icon :icon="menuInfo.meta && menuInfo.meta.icon"></Icon>
</template>
<template #title>{{ menuInfo.meta && menuInfo.meta.title }}</template>
<template v-for="item in menuInfo.children" :key="item.path">
<template v-if="!item.hidden">
<template v-if="!item.children">
<a-menu-item :key="item.path">
<template #icon>
<Icon :icon="item.meta && item.meta.icon"></Icon>
</template>
<router-link :to="item.path">
{{ item.meta && item.meta.title }}</router-link
>
</a-menu-item>
</template>
<template v-else>
<SubMenu :menu-info="item" :key="item.path" />
</template>
</template>
</template>
</a-sub-menu>
</template>
<script setup>
import { Icon } from "../Icon";
const props = defineProps(["menu-info"]);
</script>
路由文件内容
import * as VueRouter from "vue-router";
import NotFound from "../components/NotFound.vue";
import Layout from "../layout/Layout.vue";
const routes: any = [
{
path: "/login",
component: () => import("../components/Login.vue"),
hidden: true,
meta: { title: "登录", icon: "HomeOutlined" }
},
{
path: "/",
name: "Layout",
component: Layout,
meta: { title: "首页", icon: "SettingFilled" },
redirect: "/table/index/rows",
children: [
{
path: "/table",
name: "Table",
meta: { title: "表格嵌套", icon: "SmileOutlined" },
children: [
{
path: "/table/index",
name: "TableRows",
meta: { title: "表格1", icon: "SyncOutlined" },
children: [
{
path: "/table/index/cols",
name: "TableCols",
meta: { title: "表格1-1", icon: "SkinOutlined" },
component: () =>
import("../views/table/components/ColsTable.vue")
},
{
path: "/table/index/rows",
name: "TableRows",
meta: { title: "表格1-2", icon: "AppleFilled" },
component: () =>
import("../views/table/components/RowsTable.vue")
}
]
},
{
path: "/table/home",
name: "TableHome",
meta: { title: "表格2", icon: "AndroidFilled" },
component: () => import("../views/table/TableHome.vue")
}
]
},
{
path: "/form",
name: "Form",
meta: { title: "表单", icon: "WifiOutlined" },
children: [
{
path: "/form/home",
name: "FormHome",
meta: { title: "表单首页", icon: "ShoppingOutlined" },
component: () => import("../views/form/FormHome.vue")
},
{
path: "/form/add",
name: "FormAdd",
meta: { title: "新增表单", icon: "StarOutlined" },
hidden: false,
component: () => import("../views/form/AddForm.vue")
}
]
},
{
path: "/icon",
name: "Icon",
meta: { title: "图标", icon: "ThunderboltOutlined" },
component: () => import("../views/icon/IconHome.vue")
}
]
},
// v2中使用* v3中*不能直接使用,需要正则
{
path: "/:pathMatch(.*)",
component: NotFound,
hidden: true,
meta: { title: "404", icon: "<area-chart-outlined />" }
}
];
export default VueRouter.createRouter({
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
history: VueRouter.createWebHashHistory(),
routes // `routes: routes` 的缩写
});
展示出来效果