当前位置: 首页 > 工具软件 > el-admin > 使用案例 >

从开源框架el-admin中学习状态模式

夹谷英奕
2023-12-01

状态模式的定义与特点

状态(State)模式的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

State抽象状态角色

  • 接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换。

ConcreteState具体状态角色

  • 具体状态主要有两个职责:一是处理本状态下的事情,二是从本状态如何过渡到其他状态。

Context环境角色

  • 定义客户端需要的接口,并且负责具体状态的切换。

eladmin框架混入及封装用的比较多,导致我也不能很具体的判断出来具体的角色指的是谁,如果有知道的还请留言告诉我,谢谢。

在eladmin中,框架对数据的新增表单的编辑、删除都通过状态模式去处理了部分逻辑判断。框架并不是针对某个具体的组件进行的状态处理,而是针对的整个过程进行的状态处理。不同组件通过该过程的不同状态,也会有不同的表现形式,例如按钮的loading显示、表单的标题与显示、方法处理的逻辑等

下面主要以新增数据为例,具体介绍下eladmin中状态模式的实现。

数据新增

在eladmin中的状态定义

// src/compents/Crud/crud.js
/**
 * CRUD状态
 */
CRUD.STATUS = {
  NORMAL: 0,
  PREPARED: 1,
  PROCESSING: 2
}

关键组件

// src/compents/Crud/CRUD.operation.vue
<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>
// 该处通过混入的方式将crud的方法引进来
import CRUD, { crud } from '@/compents/Crud//crud.js'
export default {
  mixins: [crud()],
}
</script>

这里主要混入的是一些逻辑处理的方法CRUD状态定义和一些关键的data数据
混入后的关键属性有下面这些

// src/compents/Crud/CRUD.operation.vue
<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,
      // 添加或编辑状态
      // 表单、按钮loading等组件的状态都是根据这里判断的
      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')
      },
      // 标题,控制表单Dialog组件标题在不同状态下的显示
      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
      }
      // 更改add的状态,从NORMAL变为PREPARED
      crud.status.add = CRUD.STATUS.PREPARED
      // 此时会弹出来el-dialog。该弹框通过:visible.sync="crud.status.cu > 0"控制显示
      
      // 对自定义钩子进行处理,如果设置了就执行
      callVmHook(crud, CRUD.HOOK.afterToAdd, crud.form)
      callVmHook(crud, CRUD.HOOK.afterToCU, crud.form)
    },
    /**
     * 提交新增/编辑
     */
    submitCU() {
      if (!callVmHook(crud, CRUD.HOOK.beforeValidateCU)) {
        return
      }
      // 找到当前根据status显示的form组件,并进行校验
      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
      }
      // 更改整个添加过程的状态为PROCESSING
      crud.status.add = CRUD.STATUS.PROCESSING
      // 调取添加接口(此处eladmin做了一定处理,通过crudMethod和其属性即可调取自己定义的接口地址)
      crud.crudMethod.add(crud.form).then(() => {
      	// 成功后将状态更改为初始状态 = 关闭表单,停止loading
        crud.status.add = CRUD.STATUS.NORMAL
        crud.resetForm()
        crud.addSuccessNotify()
        callVmHook(crud, CRUD.HOOK.afterSubmit)
        crud.toQuery()
      }).catch(() => {
        // 出错后就更改为PREPARED状态(新增的第二个状态,此时表单仍然存在)
        crud.status.add = CRUD.STATUS.PREPARED
        callVmHook(crud, CRUD.HOOK.afterAddError)
      })
    },
  }
}
</script>

简单来说,eladmin通过状态模式针对整个添加的流程做了如下规定:

NORMAL阶段

  • 表单控件不显示
  • 按钮loading为false

点击新增,进入PREPARED阶段

  • 表单显示,获取该阶段表单对应的标题

点击确定,进入PROCESSING阶段

  • 显示loading
  • 发出请求
    • 如果成功,重新设置为NORMAL阶段
    • 如果失败,则回到PREPARED阶段

总结

整个过程涉及到的组件有很多,只不过表单和按钮是最显眼的,并且按钮有很多个。
框架通过状态模式将很多按钮的判断逻辑抽离了出来,减少了不少工作量。
通过状态的判断去控制了各个组件自己的表现形式,在后期维护时也更加容易。

后面有人接手的话也只需要关注关键方法的逻辑。
而不是散落在各个组件中的小按钮和表单样式的逻辑了(假如不使用该模式)。

 类似资料: