放假不停歇,这一系列会从0开始学习VUE3,使用Vite、TS、Pinia、Element-Plus等新知识点,既是查漏补缺,也是知识分享。
代码地址:
https://github.com/anjoy8/bcvp.vue3.git
这是每篇文章一节课一个分支,方便大家学习,会慢慢的将blog.admin项目进行翻新,使用的后端接口还是BlogCore。
系列文章:
本文参考的是开源项目
https://gitee.com/HalseySpicy/Geeker-Admin/tree/template
分步骤讲解前端框架中的每一个核心逻辑,今天说一下通过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组件中,引用主题设置组件
这个时候就能看到效果了:
接下来就需要对这个监听这个事件——目的就是唤起功能设置区域。
主要包括布局样式(目前支持横向布局和纵向布局)和界面设置,当前也可以设置其他的、样式相关的设置,比如暗黑模式,或者主题色等等,根据我个人经验,有这两个布局已经够用了。
新建文件
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组件即可
点击右上角的主题设置按钮,就能唤起设置菜单了。
在之前,我们写过一个纵向的布局,那现在追加一个新的横向菜单布局的效果,新建文件:
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可以实时响应对应的类型名,从而渲染不同的模板组件,从而实现不同的效果
页面切换布局效果,可以看到横向和纵向的动态变化,只不过还是老问题,刷新页面后,数据就回到了默认的纵向布局状态了,所以还是需要用到持久化方案。
这个例子又一次证明,使用Pinia和localstorage可以完美实现对所有的数据的操作和管理操作,还是建议多多使用。
在global.ts的状态管理器中,增加Pinia配置插件
最终的渲染效果,没问题,试试路由跳转和页面刷新都没问题
下篇文章我们继续对框架进行更新,开启页面级别的数据填充,实现表格渲染,敬请期待。