搭建一个vue后台管理项目基本框架

后台基本框架都是菜单栏和显示内容区域,如下图

下面我们来搭建一个统一的框架,可以入手cv直接使用

  1. 页面布局搭建,创建一个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>
  1. 编写侧边栏组件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>
  1. 编写侧边栏的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>
  1. 路由文件内容

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` 的缩写
});

展示出来效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值