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

科技   科技   2024-10-07 16:13   北京  
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 创建一个事件总线实例:

新建src\utils\mittBus.ts

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

三、发起事件

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

新建文件

src\layouts\components\Header\components\ThemeSetting.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、设计主题设置组件


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

新建文件

src\layouts\components\ThemeDrawer\index.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



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

src\layouts\LayoutTransverse\index.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">        <!-- 不能直接使用 SubMenu 组件,无法触发 el-menu 隐藏省略功能 -->        <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配置插件


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

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

BCVP代码创新社
专注于 NetCore 相关技术栈的推广,致力于前后端之间的完全分离,从壹开始,让每一个程序员都能从这里学有所成。
 最新文章