最近在写项目时,想起之前看到开源框架eladmin里面表单有一个特性,就是全局只有一份代码,一个表单组件(类似表单的外框把,只有title和button等,输入控件不在其内),但是有很多不同的展示效果,例如新建,编辑,提交中等。不管哪个模块都共用的是同一个表单。这样无疑有助于后期的维护,了解后发现是用到了状态模式。这次简单记录下eladmin是如何使用状态模式的,因为本次项目也涉及到了类似的场景,看看能不能用上。
状态模式的定义与特点
状态(State)模式的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
State抽象状态角色
- 接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换。
ConcreteState具体状态角色
- 具体状态主要有两个职责:一是处理本状态下的事情,二是从本状态如何过渡到其他状态。
Context环境角色
eladmin框架混入及封装用的比较多,导致我也不能很具体的判断出来具体的角色指的是谁,如果有知道的还请留言告诉我,谢谢。
在eladmin中,框架对数据的新增和表单的编辑、删除都通过状态模式去处理了部分逻辑判断。框架并不是针对某个具体的组件进行的状态处理,而是针对的整个过程进行的状态处理。不同组件通过该过程的不同状态,也会有不同的表现形式,例如按钮的loading显示、表单的标题与显示、方法处理的逻辑等。
下面主要以新增数据为例,具体介绍下eladmin中状态模式的实现。
数据新增
在eladmin中的状态定义
1 2 3 4 5 6 7 8 9
|
CRUD.STATUS = { NORMAL: 0, PREPARED: 1, PROCESSING: 2 }
|
关键组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <template> ... <el-button v-if="crud.optShow.add" v-permission="permission.add" class="filter-item" size="mini" type="primary" icon="el-icon-plus" @click="crud.toAdd" > 新增 </el-button> ... </template> <script>
import CRUD, { crud } from '@/compents/Crud//crud.js' export default { mixins: [crud()], } </script>
|
这里主要混入的是一些逻辑处理的方法、CRUD状态定义和一些关键的data数据
混入后的关键属性有下面这些
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| <template> ... <el-button v-if="crud.optShow.add" v-permission="permission.add" class="filter-item" size="mini" type="primary" icon="el-icon-plus" @click="crud.toAdd" > 新增 </el-button> ... </template> <script> export default { data() { return { status: { add: CRUD.STATUS.NORMAL, edit: CRUD.STATUS.NORMAL, get cu() { if (this.add === CRUD.STATUS.NORMAL && this.edit === CRUD.STATUS.NORMAL) { return CRUD.STATUS.NORMAL } else if (this.add === CRUD.STATUS.PREPARED || this.edit === CRUD.STATUS.PREPARED) { return CRUD.STATUS.PREPARED } else if (this.add === CRUD.STATUS.PROCESSING || this.edit === CRUD.STATUS.PROCESSING) { return CRUD.STATUS.PROCESSING } throw new Error('wrong crud\'s cu status') }, get title() { return this.add > CRUD.STATUS.NORMAL ? `新增${crud.title}` : this.edit > CRUD.STATUS.NORMAL ? `编辑${crud.title}` : crud.title } } } }, methods:{
toAdd() { crud.resetForm() if (!(callVmHook(crud, CRUD.HOOK.beforeToAdd, crud.form) && callVmHook(crud, CRUD.HOOK.beforeToCU, crud.form))) { return } crud.status.add = CRUD.STATUS.PREPARED callVmHook(crud, CRUD.HOOK.afterToAdd, crud.form) callVmHook(crud, CRUD.HOOK.afterToCU, crud.form) },
submitCU() { if (!callVmHook(crud, CRUD.HOOK.beforeValidateCU)) { return } crud.findVM('form').$refs['form'].validate(valid => { if (!valid) { return } if (!callVmHook(crud, CRUD.HOOK.afterValidateCU)) { return } if (crud.status.add === CRUD.STATUS.PREPARED) { crud.doAdd() } else if (crud.status.edit === CRUD.STATUS.PREPARED) { crud.doEdit() } }) },
doAdd() { if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) { return } crud.status.add = CRUD.STATUS.PROCESSING crud.crudMethod.add(crud.form).then(() => { crud.status.add = CRUD.STATUS.NORMAL crud.resetForm() crud.addSuccessNotify() callVmHook(crud, CRUD.HOOK.afterSubmit) crud.toQuery() }).catch(() => { crud.status.add = CRUD.STATUS.PREPARED callVmHook(crud, CRUD.HOOK.afterAddError) }) }, } } </script>
|
简单来说,eladmin通过状态模式针对整个添加的流程做了如下规定:
NORMAL阶段
点击新增,进入PREPARED阶段
点击确定,进入PROCESSING阶段
- 显示loading
- 发出请求
- 如果成功,重新设置为NORMAL阶段
- 如果失败,则回到PREPARED阶段
总结
整个过程涉及到的组件有很多,只不过表单和按钮是最显眼的,并且按钮有很多个。
框架通过状态模式将很多按钮的判断逻辑抽离了出来,减少了不少工作量。
通过状态的判断去控制了各个组件自己的表现形式,在后期维护时也更加容易。
后面有人接手的话也只需要关注关键方法的逻辑。
而不是散落在各个组件中的小按钮和表单样式的逻辑了(假如不使用该模式)。