BCVP.VUE3系列第十一课:基于总线实现框架多种布局样式

艺帆风顺 发布于 2025-04-03 106 次阅读


BCVP 开发者社区出品BCVP V3开发数字化服务化绿色化

放假不停歇,这一系列会从0开始学习VUE3,使用Vite、TS、Pinia、Element-Plus等新知识点,既是查漏补缺,也是知识分享。

代码地址:

https://github.com/anjoy8/bcvp.vue3.git

这是每篇文章一节课一个分支,方便大家学习,会慢慢的将blog.admin项目进行翻新,使用的后端接口还是BlogCore。

系列文章:

第一课:项目初始化与核心知识点说明

第二课:基于泛型基类封装Axios请求

第三课:封装Axios拦截器

第四课:登录页设计

第五课:获取用户信息

第六课:获取动态菜单接口

第七课:基于布局模式实现动态菜单渲染

第八课:丰富面包屑组件

第九课:实现tabs标签栏

第十课:个人中心模块

0、本文介绍

本文参考的是开源项目

https://gitee.com/HalseySpicy/Geeker-Admin/tree/template

分步骤讲解前端框架中的每一个核心逻辑,今天说一下通过mittBus工具实现框架多种样式布局,之前是纵向布局,今天实现横向布局,效果图:

因为有些时候,我们是需要这种横向布局的。

1、引入mittBus中间件

在 Vue 3 中,mittBus 是一个使用 mitt 库创建的事件总线(event bus)。mitt 是一个非常轻量的事件发射(emit)库,它可以用来在不同的组件之间发送和接收事件,从而实现组件之间的通信。

一、安装依赖:

二、创建事件总线:

通常,会在某个公共的地方(例如 utils/mittBus.js 文件)使用 mitt 创建一个事件总线实例:

新建srcutilsmittBus.ts

import mitt from 'mitt';const mittBus = mitt();export default mittBus;

三、发起事件

根据需要,在右上角功能Bar中,增加定义一个主题设置按钮:

新建文件

srclayoutscomponentsHeadercomponentsThemeSetting.vue

template>  div class="theme-setting">    i :class="'iconfont icon-zhuti'" class="toolBar-icon" @click="openDrawer">i>  div>template>
script setup lang="ts">import mittBus from "@/utils/mittBus";const openDrawer = () => { mittBus.emit("openThemeDrawer");};script>

这里当点击图标时,通过调用 mittBus.emit("openThemeDrawer") 发射一个名为 "openThemeDrawer" 的事件,

四、最后,在ToolBarRight.vue组件中,引用主题设置组件

这个时候就能看到效果了:

接下来就需要对这个监听这个事件——目的就是唤起功能设置区域。

2、设计主题设置组件

主要包括布局样式(目前支持横向布局和纵向布局)和界面设置,当前也可以设置其他的、样式相关的设置,比如暗黑模式,或者主题色等等,根据我个人经验,有这两个布局已经够用了。

新建文件

srclayoutscomponentsThemeDrawerindex.vue

template>  el-drawer v-model="drawerVisible" title="布局设置" size="290px">        el-divider class="divider" content-position="center">      el-icon>        Notification />      el-icon>      布局样式    el-divider>    div class="layout-box">      el-tooltip effect="dark" content="纵向" placement="top" :show-after="200">        div :class="['layout-item layout-vertical', { 'is-active': layout == 'vertical' }]"          @click="setLayout('vertical')">          div class="layout-dark">div>          div class="layout-container">            div class="layout-light">div>            div class="layout-content">div>          div>          el-icon v-if="layout == 'vertical'">            CircleCheckFilled />          el-icon>        div>      el-tooltip>      el-tooltip effect="dark" content="横向" placement="top" :show-after="200">        div :class="['layout-item layout-transverse', { 'is-active': layout == 'transverse' }]"          @click="setLayout('transverse')">          div class="layout-dark">div>          div class="layout-content">div>          el-icon v-if="layout == 'transverse'">            CircleCheckFilled />          el-icon>        div>      el-tooltip>    div>
el-divider class="divider" content-position="center"> el-icon> Setting /> el-icon> 界面设置 el-divider> div class="theme-item"> span>菜单折叠span> el-switch v-model="isCollapse" /> div> div class="theme-item"> span>菜单手风琴span> el-switch v-model="accordion" /> div> div class="theme-item"> span>面包屑span> el-switch v-model="breadcrumb" /> div> div class="theme-item"> span>面包屑图标span> el-switch v-model="breadcrumbIcon" /> div> div class="theme-item"> span>标签栏span> el-switch v-model="tabs" /> div> div class="theme-item"> span>标签栏图标span> el-switch v-model="tabsIcon" /> div> div class="theme-item"> span>页脚span> el-switch v-model="footer" /> div> el-drawer>template>
script setup lang="ts">import { ref } from "vue";import { storeToRefs } from "pinia";import { useGlobalStore } from "@/stores/modules/global";import type { LayoutType } from "@/stores/interface";import mittBus from "@/utils/mittBus";
const globalStore = useGlobalStore();const { layout, isCollapse, accordion, breadcrumb, breadcrumbIcon, tabs, tabsIcon, footer } = storeToRefs(globalStore);
// 设置布局方式const setLayout = (val: LayoutType) => { globalStore.setGlobalState("layout", val);};
// 打开主题设置const drawerVisible = ref(false);mittBus.on("openThemeDrawer", () => { drawerVisible.value = true});script>
style scoped lang="scss">@import "./index.scss";style>

可以看到,在90行代码,有一个对主题设置的总线监听方法,就是用来处理设置页面发射的事件的处理。

最后,在layout母版布局页,引用该setting组件即可

点击右上角的主题设置按钮,就能唤起设置菜单了。

3、设计横向布局layout

在之前,我们写过一个纵向的布局,那现在追加一个新的横向菜单布局的效果,新建文件:

srclayoutsLayoutTransverseindex.vue

template>  el-container class="layout">    el-header>      div class="logo flx-center">        img class="logo-img" src="@/assets/images/logo.svg" alt="logo" />        span class="logo-text">{{ title }}span>      div>      el-menu mode="horizontal" :router="false" :default-active="activeMenu">                template v-for="subItem in menuList" :key="subItem.name">          el-sub-menu v-if="subItem.children?.length" :key="subItem.name" :index="subItem.name + 'el-sub-menu'">            template #title>              el-icon>                component :is="subItem.meta.icon">component>              el-icon>              span>{{ subItem.meta.title }}span>            template>            SubMenu :menu-list="subItem.children" />          el-sub-menu>          el-menu-item v-else :key="subItem.path + 'el-menu-item'" :index="subItem.path"            @click="handleClickMenu(subItem)">            el-icon>              component :is="subItem.meta.icon">component>            el-icon>            template #title>              span>{{ subItem.meta.title }}span>            template>          el-menu-item>        template>      el-menu>      ToolBarRight />    el-header>    Main />  el-container>template>
script setup lang="ts" name="layoutTransverse">import { computed } from "vue";import { useAuthMenuStore } from "@/stores/modules/authMenu";import { useRoute, useRouter } from "vue-router";import Main from "@/layouts/components/Main/index.vue";import ToolBarRight from "@/layouts/components/Header/ToolBarRight.vue";import SubMenu from "@/layouts/components/Menu/SubMenu.vue";
const title = 'BlogVue3';
const route = useRoute();const router = useRouter();const authStore = useAuthMenuStore();const menuList = computed(() => authStore.showMenuListGet);const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string);
const handleClickMenu = (subItem: Menu.MenuOptions) => { if (subItem.meta.isLink) return window.open(subItem.meta.isLink, "_blank"); router.push(subItem.path);};script>
style scoped lang="scss">@import "./index.scss";style>

然后在layout母版中,引入第二种布局组件,当我们在唤起的样式布局中,切换样式的时候,传值到了Pinia状态管理器,这里通过computed可以实时响应对应的类型名,从而渲染不同的模板组件,从而实现不同的效果

页面切换布局效果,可以看到横向和纵向的动态变化,只不过还是老问题,刷新页面后,数据就回到了默认的纵向布局状态了,所以还是需要用到持久化方案。

4、模板文件引用,渲染效果

这个例子又一次证明,使用Pinia和localstorage可以完美实现对所有的数据的操作和管理操作,还是建议多多使用。

在global.ts的状态管理器中,增加Pinia配置插件

最终的渲染效果,没问题,试试路由跳转和页面刷新都没问题

下篇文章我们继续对框架进行更新,开启页面级别的数据填充,实现表格渲染,敬请期待。