布局: 左侧栏折叠与面包屑
前言
前一段时间, 写了强盛集团管理系统(基于 BPMN 引擎的工作流系统), 打算使用 qiankun 改造下项目架构, 迈向微前端。在上一章的基础上, 搭建基座应用的布局, 此章为折叠左侧栏和面包屑。
先来看下效果图吧!
简单实现下
考虑到左侧栏的折叠功能会影响后续(例如单独抽离主题设置面板)等工作, 以及在布局中, 折叠图标一般防置在<LayoutHeader>
组件中, 而实际控制的则是左侧栏</LayoutAside>
组件。
那我暂时选择将左侧栏折叠状态放在 Vuex 中,接下来则是实现部分
Vuex app 模块
- 设置全局标识, 新建
store/modules/app.js
export default {
namespaced: true,
state: {
sidebar: {
opened: true,
},
},
mutations: {
set_sidebar(state, { key, value }) {
state[key] = value;
},
},
actions: {
setSidebar({ commit }, { key, value }) {
commit("set_sidebar", { key, value });
},
},
getters: {
sidebar: (state) => state.sidebar,
},
};
设置左侧栏sidebar
折叠状态opened
, 同时sidebar
下可能会有较多状态值需要记录, 设置值时为单独设置, 取值时则是返回全部
- 引入
app
模块
import Vue from "vue";
import Vuex from "vuex";
import app from "./modules/app";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
app,
},
state: {},
getters: {},
mutations: {},
actions: {},
});
头部增加折叠图标
- 新增
icon
<!-- 头部栏 -->
<template>
<div class="layout-header">
<div @click="toggle" class="fold-btn">
<icon :name="sidebar.opened ? 's-fold' : 's-unfold'" scale="0.4"></icon>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
name: "",
data() {
return {};
},
computed: {
...mapGetters("app", ["sidebar"]),
},
methods: {
...mapActions("app", ["toggleCollapse"]),
toggle() {
this.toggleCollapse(!this.sidebar.opened);
},
},
created() {},
mounted() {},
};
</script>
<style lang="less" scoped>
.layout-header {
display: flex;
align-items: center;
height: 40px;
.fold-btn {
width: 24px;
height: 24px;
color: black;
margin: 0 5px;
}
}
</style>
通过 Vuex 的 mapGetters
、mapActions
可以比较方便的调用 app 模块的方法
s-fold
与s-unfold
为阿里矢量库的图标, 上一章是怎样制作图标
此时折叠栏的效果已经出来了。
样式变量
左侧栏<LayoutAside>
头部防止 logo 和顶部栏<LayoutHeader>
一般为同高度, 系统中还会有一些默认样式等。
那我选择将样式变量统一放置, 先来浅浅的实现一下
- 新建
src/assets/css/common-variables.less
/* 布局 */
@layout-header-height: 40px;
<LayoutAside>
组件中将头部高度设置下
<style lang="less" scoped>
@import url('@/assets/css/common-variables.less');
.layout-aside-logo {
height: @layout-header-height;
line-height: @layout-header-height;
...
}
.layout-aside-scrollbar {
height: calc(100vh - @layout-header-height);
...
}
</style>
调整左侧栏顶部 logo 的高和行高, 及左侧栏的滚动高度
<LayoutHeader>
组件中将高度调整
<style lang="less" scoped>
@import url('@/assets/css/common-variables.less');
.layout-header {
height: @layout-header-height;
box-shadow: 0 0 4px 0px #999;
...
}
</style>
增加了底部阴影
此时就可以通过变量统一设置顶栏的高度了。
面包屑
效果展示
- 新建公共组件
Breadcrumb.vue
<!-- 面包屑 -->
<template>
<el-breadcrumb separator="/" class="breadcrumb">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
<span
v-if="item.redirect === 'noRedirect' || index == levelList.length - 1"
class="no-redirect"
>{{ item.meta.title }}</span
>
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
直接使用 element
的 el-breadcrumb
即可, transition-group
则为方便设置转场动画
<script>
export default {
name: "Breadcrumb",
data() {
return {
levelList: null,
};
},
watch: {
$route(route) {
if (route.path.startsWith("/redirect/")) {
return;
}
this.getBreadcrumb();
},
},
methods: {
getBreadcrumb() {
let matched = this.$route.matched.filter(
(item) => item.meta && item.meta.title
);
const first = matched[0];
if (!this.isDashboard(first)) {
matched = [{ path: "/", meta: { title: "基座" } }].concat(matched);
}
this.levelList = matched.filter(
(item) => item.meta && item.meta.title && item.meta.breadcrumb !== false
);
console.log(this.levelList);
},
isDashboard(route) {
const name = route && route.name;
if (!name) {
return false;
}
return name.trim().toLocaleLowerCase() === "Base".toLocaleLowerCase();
},
handleLink(record) {
const { redirect, path } = record;
if (redirect) {
return this.$router.push(redirect);
}
this.$router.push(path);
},
},
created() {
console.log(this.$route);
this.getBreadcrumb();
},
mounted() {},
};
</script>
watch
监听$route
切换, 更新面包屑
handleLink
为跳转处理
<style lang="less" scoped>
@import url("@/assets/css/common-variables.less");
.breadcrumb {
line-height: @layout-header-height;
margin-left: 10px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>
- 引入面包屑
<!-- 头部栏 -->
<template>
<breadcrumb/>
</template>
<script>
import Breadcrumb from "@/components/Breadcrumb.vue";
export default{
components: { Breadcrumb },
}
<script>
此时面包屑已实现, 可以为面包屑转场添加动画
- 新建
assets/css/element.css
, 在 main.js 引入
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.28s;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
/* fade-transform */
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all 0.5s;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}
/* breadcrumb transition */
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all 0.5s;
}
.breadcrumb-enter,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(20px);
}
.breadcrumb-move {
transition: all 0.5s;
}
.breadcrumb-leave-active {
position: absolute;
}
总结
本篇简单实现了左侧栏的折叠和面包屑,接下来会实现个人中心,然后实现设置面板,之后会正式接入原有项目。
而本专栏就是打算将Vue2.x的老项目完成技术架构升级,从而达到技术栈更新、迁移、重构、重写等架构演进。
转载自:https://juejin.cn/post/7243609307857174587