做后台管理项目期间,遇到一些略微复杂的场景,原则就是尽可能的把逻辑放在模板之外,根据特点,做成可配置的方式。
下面选取了两个场景,分别来说明配置的使用,解决方式都差不多。
场景1:一个状态对应多个操作
项目中会根据某一状态,展示几个可以操作的按钮,例如状态 A,对应是编辑、删除;状态 B,对应的是编辑、预览、撤回;状态 C,对应的是预览、撤回、记录。状态对应的操作,不是一成不变的,可能根据不同的权限,增加或者去除某个操作。
根据状态的不同,把逻辑判断写在模板中,会增加模板的复杂度。
以下是伪代码:
<template>
<el-button v-if="item.status === 'A' || item.status === 'B'">编辑</el-button>
<el-button v-if="item.status === 'A'">删除</el-button>
<el-button v-if="item.status === 'A' || item.status === 'C'">预览</el-button>
<el-button v-if="item.status === 'B' || item.status === 'C'">撤回</el-button>
<el-button v-if="item.status === 'A' || item.status === 'B' || item.status === 'C'">记录</el-button>
</template>
或者根据状态聚合操作,伪代码:
<template>
<div v-if="item.status === 'A'">
<el-button>编辑</el-button>
<el-button>删除</el-button>
</div>
<div v-else-if="item.status === 'B'">
<el-button>编辑</el-button>
<el-button>删除</el-button>
<el-button>预览</el-button>
</div>
<div v-else-if="item.status === 'C'">
<el-button>撤回</el-button>
<el-button>预览</el-button>
<el-button>记录</el-button>
</div>
</template>
第一种做法让操作来适应状态,在操作上做多个状态判断,维护起来困难。
第二种做法是根据状态找对应的操作,这种方式比较直观。写在模板中,增加去除不太灵活。
第二种做法可以放在js中做,先将状态对应的操作,抽离成一个映射表。状态映射到对应的操作列表,后期维护直接维护映射表即可,不需要动模板逻辑,这样数据逻辑和模板完全分离开。
类似这样定义,伪代码:
export const operationMapAll = {
0: ['bianjiAndTishen', 'shanchu', 'jilu', 'yulan'],
1: ['chehui', 'jilu', 'yulan'],
2: ['bianjiAndTishen', 'shanchu', 'jilu'],
4: ['jilu'],
5: ['bianjiAndTishen', 'jilu'],
6: ['jilu'],
7: ['jilu', 'yulan'],
8: ['jilu'],
31: ['chehui', 'jilu', 'yulan'],
32: ['shuju', 'jilu'],
33: ['shuju', 'jilu'],
}
key 为状态码,值为操作集合(这里用了拼音,哈哈,直观)。
某个权限下不展示给用户某些操作,可以先定义这个权限需要过滤的操作的配置:
// 没有操作权限配置
const guildActivityOperateMap = {
bianjiAndTishen: 1,
shanchu: 1,
chehui: 1,
}
写一个方法,过滤掉这里定义的规则:
// 过滤方法
const filterByguildActivityOperate = () => {
return Object.keys(operationMapAll).reduce((newMap, key) => {
newMap[key] = operationMapAll[key].filter(txt => {
return !guildActivityOperateMap[txt]
})
return newMap
}, {})
}
然后根据权限,拿到最终的操作映射表:
// 有权限,则走全部配置,否则过滤一遍
export const operationMap = guild_activity_operate ? operationMapAll : filterByguildActivityOperate()
如果有多个权限,只要增加一个过滤的配置和一个方法,根据权限执行该方法;如果需要增加某个操作,也是同样的道理。
定义每个操作要做的事情,伪代码:
export const operation = {
bianjiAndTishen:{
value: '编辑/提审',
handler(comInstall, params){
// TODO something
}
},
shanchu:{
value: '删除',
handler(comInstall, params){
// TODO something
}
},
jilu:{
value: '记录',
handler(comInstall, params){
// TODO something
}
}
... // 其他忽略
}
当后端返回的状态值,匹配到了定义的映射表,便会找到对应的操作列表,循环展示到模板中,模板的按钮统一都是点击触发,为了达到统一的效果,所有的方法都标配一个 handler 方法,点击时触发改方法。value 是显示在页面中的文案。如果按钮还有其他的事件处理,可以再指定一个方法,供该事件调用。
方法接收两个参数,第一个是组件实例,能够在时间触发后做到和组件 methods 中一样的事,需要用到实例的情况,例如触发 this.$message() 提示。当然你也可以通过 call 来改变 this 为组件实例;
第二个参数是处理的数据,可以拿到数据后发送请求之类。
模板渲染:
<el-button
v-for="item in scope.row.operation"
@click="operationHandler(item, scope.row)"
>
{{ item.value }}
</el-button>
methods 方法:
operationHandler(item, row) {
item?.handler(this, row)
// 或者 item?.handler.call(this, row)
},
场景2:多按钮对多视图
场景描述:三个按钮,每切换到一个按钮上会展示不同的 table,同时不同的按钮还会对应不同的筛选控件,以及不同的请求方法和参数。
对视图 table 的权衡,table 有重复的地方,也有不一样的地方。重复的地方改动频率不高,所以分成了三个组件文件。如果对重复功能改动频繁,则最好是重复的抽离成一个组件,方便改一次全部都更改。
只有三个视图,如果视图有多个或者日后要增加,为了日后维护扩展性好,不要分成太多的组件文件,而是改成用配置的思路,只有一个视图。抽离公共的配置要比视图更简洁些。
对按钮和对应视图进行配置,这样也方便通过权限进行过滤,伪代码:
export const tableMap = {
allTable: {
com: () => import('./allTable.vue'),
downUrl: '/exportAllTaskList',
new_perm: window.NEW_PERM.better_all_query,
title: '全部',
class: '',
data: {
starid: '',
level: '0',
page: 1,
intern_status: '0',
is_signed: '0',
},
fetch(params) { // 请求
return allTaskList(params)
},
},
probationTable: {
com: () => import('./probationTable.vue'),
downUrl: '/exportPreTaskList',
new_perm: window.NEW_PERM.m_betterstar_pre_task,
title: '试用期任务',
class: 'borderLeft borderRight',
data: {
starid: '',
level: '0',
page: 1,
status: '0',
},
fetch(params) { // 请求
return preTaskList(params)
},
},
signTable: {
com: () => import('./signTable.vue'),
downUrl: '/exportSignTaskList',
new_perm: window.NEW_PERM.m_betterstar_sign_task,
title: '签约考核任务',
class: '',
data: {
month: '',
starid: '',
level: '0',
page: 1,
is_signed: '0',
},
fetch(params) { // 请求
return signTaskList(params)
},
},
}
配置中的 com 字段是渲染的 table 视图,通过 Vue 中的
在 monted 中做过滤:
const keys = Object.keys(tableMap).filter(key => tableMap[key]?.new_perm)
this.tableMap = keys.reduce((obj, key) => {
obj[key] = tableMap[key]
return obj
}, {})
this.tableType = keys[0]
this.info = this.tableMap[this.tableType]
// 表单控件使用的字段,复制一份,防止防止保留上一次操作的数据
this.formData = { ...this.info?.data } || {}
按钮渲染后,只需要通过 tableType 找到过滤后 tableMap 的值即可。
总结
当出现以上场景时,建议走配置的方式,将逻辑放在 js 中做,减少在模板中做判断的逻辑。逻辑再js中后,再考虑如何进行抽象,达到可扩展的程度。
扩展阅读:
https://segmentfault.com/a/1190000015643488
https://blog.csdn.net/qq_37210523/article/details/109019664