做后台管理项目期间,遇到一些略微复杂的场景,原则就是尽可能的把逻辑放在模板之外,根据特点,做成可配置的方式。
下面选取了两个场景,分别来说明配置的使用,解决方式都差不多。
场景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-buttonv-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
