一、需求。
最近在开发一个新系统,有很多功能以及表单需要处理,为了偷懒,想着封装一个组件统一处理下相关逻辑,这样下来,每个功能只需写一个列表查询的组件和一个表单组件,省时省心。
最终效果:
二、需要实现的点:
1、 增加、修改、查看三个功能都在这里进行处理
2、每个功能只需要写好表单以及对应事件,用该组件统一处理外部样式以及触发子组件中的表单事件(这里的表单作为子组件)
3、因为有左侧边栏的缘故,还需要动态调整宽度
三、具体实现
<!--基于element封装的抽屉组件,具体api见:https://element.eleme.io/#/zh-CN/component/drawer-->
<template>
<div>
<el-drawer ref="drawer"
append-to-body
:size="drawer.width"
:before-close="beforeClose"
:visible="drawer.show"
:direction="direction"
:destroy-on-close="destroy"
:wrapperClosable="wrapperClosable"
@open="getNewWidth"
@close="closeHander">
<!-- 标题部分 -->
<template slot="title">
<p>{{ titleText }}</p>
</template>
<!-- 主要内容 -->
<slot name="main"></slot>
<div class="drawer__footer">
<Button size="large"
icon="md-close"
@click="CloseDrawer">{{
showSubmitBtn ? "取消" : "关闭"
}}</Button>
<Button v-if="showSubmitBtn"
size="large"
type="primary"
@click="submit"
icon="md-checkbox-outline"
:loading="loading">{{ loading ? "提交中 ..." : "确 定" }}</Button>
</div>
</el-drawer>
</div>
</template>
<script>
export default {
name: 'JcDrawer',
props: {
//模式:1新增 2修改 3查看
mode: {
default: 1,
type: Number,
},
//是否显示
show: {
default: false,
},
//标题
title: {
type: String,
},
//打开方向
direction: {
default: 'rtl',
type: String,
},
//控制是否在关闭 Drawer 之后将子元素全部销毁
destroy: {
default: true,
type: Boolean,
},
//点击遮罩层是否可以关闭 Drawer
wrapperClosable: {
default: false,
type: Boolean,
},
//是否显示提交按钮(有的界面无需提交)
showSubmitBtn: {
default: true,
type: Boolean,
},
},
data() {
return {
isAuto: false, // 是否自动关闭
Bus: this.$BusFactory(this),
loading: false,
drawer: {
width: 0,
show: this.show,
mode: this.mode,
},
}
},
mounted() {
//初始化完成时添加一个事件用于监听屏幕大小变化,自适应调整宽度
window.onresize = () => {
return (() => {
this.getNewWidth()
})()
}
},
watch: {
//监听父组件的值控制是否显示
show: {
immediate: true,
deep: true,
handler(newvalue, oldvalue) {
this.drawer.show = newvalue
},
},
mode: {
immediate: true,
handler(newvalue, oldvalue) {
this.drawer.mode = newvalue
},
},
},
computed: {
/**
* @description 标题
*/
titleText() {
if (this.drawer.mode === 1) return `添加${this.title}信息`
if (this.drawer.mode === 2) return `修改${this.title}信息`
if (this.drawer.mode === 3) return `查看${this.title}信息`
},
},
methods: {
/**
* @description 关闭之后的事件
*/
closeHander() {
//取消自动关闭
this.isAuto = false
},
/**
* @description 关闭窗口
*/
CloseDrawer() {
if (this.showSubmitBtn) {
this.isAuto = false
} else {
this.isAuto = true
}
this.$refs.drawer.closeDrawer()
},
/**
* @description 关闭抽屉的事件
* @param {function} done 关闭的回调
*/
beforeClose(done) {
if (!this.isAuto && this.showSubmitBtn) {
this.$confirm('确定要关闭吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then((_) => {
this.$emit('close')
done()
})
} else {
this.$emit('close')
done()
}
},
//提交事件
submit(callback) {
var that = this
that.loading = true
if (this.drawer.mode == 1) {
//使用事件总线的方式触发表单组件中的事件
this.Bus.$emit('Add', function (res) {
if (res.success) {
that
.$confirm('添加成功!', '提示', {
confirmButtonText: '确定',
showCancelButton: false,
type: 'success',
closeOnClickModal: false,
closeOnPressEscape: false,
showClose: false,
})
.then(() => {
//点击确认之后直接退出并销毁表单
that.isAuto = true
that.$refs.drawer.closeDrawer()
})
}
})
}
if (this.drawer.mode == 2) {
this.Bus.$emit('Edit', function (res) {
if (res.success) {
that
.$confirm('修改成功!', '提示', {
confirmButtonText: '确定',
showCancelButton: false,
type: 'success',
closeOnClickModal: false,
closeOnPressEscape: false,
showClose: false,
})
.then(() => {
that.isAuto = true
that.$refs.drawer.closeDrawer()
})
}
})
}
if (this.drawer.mode == 3) {
this.Bus.$emit('Detail', function () {
that.isAuto = true
that.$refs.drawer.closeDrawer()
})
that.loading = false
}
},
/**
* @description 设置主界面的宽度
*/
getNewWidth() {
//组件宽度=当前屏幕宽度-侧边栏宽度
this.drawer.width =
document.body.clientWidth -
(parseInt(document.getElementById('left_nav')?.style?.width) || 0)
},
},
}
</script>
<style scoped>
.drawer__footer {
width: 100%;
border-top: 1px solid #e8eaec;
position: absolute;
bottom: 0;
padding: 10px;
display: flex;
justify-content: center;
background-color: white;
z-index: 99;
}
.drawer__footer button {
margin: 0 10px;
}
</style>
使用(列表查询组件):
<!--列表组件-->
<template>
<!--...列表展示内容-->
<jc-drawer :show="formShow"
@close="formShow = false"
:title="title"
:mode="mode">
<!-- 使用插槽插入表单 -->
<template slot="main">
<role-form :roleId="roleId"
@getList="getList"
:mode="mode"></role-form>
</template>
</jc-drawer>
</template>
<script>
import jcDrawer from '@/components/jc-cpn/jc-drawer.vue'
import RoleForm from './form'
export default {
components: { jcDrawer, RoleForm },
name: 'role',
data() {
return {
roleId: null,
mode: 1,
title: '角色',
formShow: false,
}
},
methods: {
// 添加角色
roleAdd() {
this.mode = 1
this.formShow = true
},
//编辑角色
roleEdit() {
this.mode = 2
this.roleId = 点击编辑按钮时行的id
this.formShow = true
},
//查看角色
roleDetail() {
this.mode = 3
this.roleId = 点击查看按钮时行的id
this.formShow = true
},
},
}
</script>
表单组件:
<!-- 角色的form表单 -->
<template>
<div>
<!-- 表单元素 -->
</div>
</template>
<script>
export default {
name: 'RoleForm',
props: {
roleId: {
type: Number,
default: null,
},
mode: {
type: Number,
default: 1,
},
},
data() {
return {
// 事件总线实例。详情见:https://zhuanlan.zhihu.com/p/32029461
Bus: this.$BusFactory(this),
loading: false,
}
},
watch: {
//监听父组件的值控制是否显示
roleId: {
immediate: true,
deep: true,
handler(newvalue, oldvalue) {
if (this.mode == 2) {
//回填数据
this.setCurrentData()
}
},
},
},
mounted() {
//采用事件总线的方式绑定drawer组件中定义的方法,采用事件回调的方式通知drawer此次事件的完成情况,在drawer中统一处理
//新增方法
this.Bus.$on('Add', (callback) => {
this.RoleAdd(callback)
})
//新增方法
this.Bus.$on('Edit', (callback) => {
this.RoleEdit(callback)
})
},
methods: {
/**
* @description 角色添加
* @param {function} callback 回调函数,请求完成后用于通知抽屉组件完成状态
*/
RoleAdd(callback) {
callback(...res)
},
/**
* @description 角色修改
* @param {function} callback 回调函数,请求完成后用于通知抽屉组件完成状态
*/
RoleEdit(name, callback) {
callback(...res)
},
},
}
</script>
总结:完成这样一套逻辑之后,对应功能节点只需要完成如图表单组件和列表查询组件,而且表单中只需处理数据,提示或者报错都放到drawer组件中进行处理,极大程度上保护了自己的头发。
另:有关事件总线不明白的地方可参考:Vue自动销毁的vue event Bus。