任务协同设计 🌕

记录了工作中的「任务协同系统」前端架构的设计

产品需求

任务系统,一般会有这么几个流程:创建任务,完成任务,任务结束。任务本身一般会有几种状态:任务开始,任务进行中,任务结束(任务完成,任务取消),任务异常(如超时未完成)。任务一般也有创建人,可以是具体人,也可以是系统。任务也会有接收人,以及指定的完成人。

根据业务的具体需求,设计的业务流程大致如下:

关键的逻辑都是在后端实现的,而前端需要提供一些界面的支持。

场景设计

根据业务需求前端大概又这么几个应用前景:任务列表、创建任务、查看任务、编辑任务、取消任务、完成任务。而转化为实际的场景大概有这么几个:
查看任务、创建/编辑任务、完成任务。

此文只涉及运营后台的设计,用户端的会提到,不会细讲。

查看任务

根据业务的设计,每个用户都会有自己的任务。所以,整个任务系统的入口就在用户的详情页面,会有一个用户的任务列表。下面分别是运营后台的用户详情页面的任务列表和用户端用户操作台的任务列表页。

我们可以看到每一个任务都有不同的名称、类型、状态等,而作为入口,不同的任务自然也会进入不同的页面。

在用户端,点击单个任务之后,会跳转到完成任务,或者任务详情页面。

在运营后台,点击单个任务之后,如果任务类型是随访任务,会直接打开一个完成任务的页面;如果是患者任务,会默认会打开一个弹窗,即查看任务弹窗。如下图:

弹窗分成三部分:头部显示任务标题,中间是个 iframe,展示的是用户端的当前任务详情, 下面是三个行动按钮。

创建/编辑任务

任务一般有两种创建方式:手动创建和系统创建。系统创建是后端检测到用户数据变化,或者定时任务触发而自动创建的任务,手动创建时运营后台的操作人员,直接创建的。创建任务的入口,在上面的任务列表的截图中有,但不是唯一的。其他页面也有创建任务入口,所以创建任务设计成了一个弹窗。如下图:

一开始,会让选择任务的大类型:患者任务或者随访任务。患者任务有很多,随访任务目前只有一个。选择患者任务之后,回到第二张图片,会根据下拉框选择不同的具体患者任务类型,而加载出不同的表单,填写完表单,点击创建任务即可创建任务。创建成功的任务会添加到用户的任务列表。

编辑任务的入口在查看任务的弹窗下面,之前图片已有展示。点击之后,关闭查看任务弹窗,打开编辑/创建任务弹窗,会先去服务端获取之前创建任务已经填写的数据,有些数据时不能改动的,其他操作与创建类似(因为它俩本来就一套代码)。如下图:

完成任务

完成任务有两种方式:取消任务和在完成任务页面完成任务。

取消任务

如果任务类型是患者任务,可在查看任务弹窗下面的「取消任务」按钮取消任务;如果是其他任务,可在完成任务页面取消任务。取消任务有可能需要填写取消原因。

帮患者完成任务

运营后台是可以帮助患者去完成某些特定任务的,入口在查看任务弹窗下面的「帮助患者完成任务」按钮。点击后,关闭查看任务弹窗,打开帮患者完成任务。如下图:

弹窗是左右结构。左侧是辅助完成任务的数据展示,不可操作。可能是用户端页面,也可能是其他数据展示模块,也可能没有。右侧是操作区域,需要填写任务对应表单完成任务。

完成任务

这是整个流程里最麻烦的一个环节。因为这是对所有数据的汇总和整理。我们先来看一下完成任务页面:

也是左右结构。左侧是展示区域,不能操作,左侧分为两个区域,上面是完成任务的辅助数据,与「帮患者完成任务」左侧类似,下面是此任务从开始到结束所有操作的日志记录。

右侧是完成任务需要填写的表单,右侧下面是行动按钮。暂存任务和完成任务的区别是暂存任务不会校验表单数据。如果该任务状态是已完成,那么右侧也不能操作。并且右侧下面的行动按钮不会展示。

前端设计

对应着使用场景,前端架构设计如下:

名称 形式 地址
查看任务 弹窗 view-task-dialog
创建/编辑任务 弹窗 create-task
帮患者完成任务 弹窗 help-finish-task
完成任务 页面 页面 mixins 动态表单

任务列表只是写展示性的东西,略过不提

下面,我会挨个对这些组件/页面进行说明,只会提及一下有必要说一下的东西。

查看任务

  • 是否可以「帮助患者完成任务」有两个判断条件:当前账号是否有权限,这个是服务端返回;另一个是业务是否需要,这个服务端没存储,有一个前端维护的列表,如果添加了不需要帮助完成的新任务,把 ID 加入进去即可。详见

  • 当前患者的任务列表的数据存储到了 Vuex,并且暴露了getPatientTaskList方法用于刷新列表。详见

创建/编辑任务

  1. 患者任务和随访任务。
  • 两种任务类型对应完全不同的业务数据,所以分开处理。会根据用户一开始的选择,渲染不同的组件。详见
  • 但是所以任务都有一些共同的操作,如初始化、提交、可复用的校验、公用的方法、样式等等,于是使用mixin整理出来 mixin 文件。详见
  1. 患者任务
  • 患者任务也有很多子类型,所以抛去公用表单项,其他的写成了动态组件包裹表单项的形式。详见

  • 这样做有诸多好处。每个表单都单独分开,一个任务对应一个单独的文件,文件内只需要写表单项和校验规则即可。如有需要对外暴露一个提交数据时的处理方法handlePostData。更有甚者,一个任务只有基础表单项,那么你只要写个名字就行。详见

  • 因为每个任务表单项都不甚相同,还有对应的 id、名称也是不同的,所以专门有一个配置文件来配置这些项目。这也是患者任务类型选择列表的数据来源。详见

帮助患者完成任务

  • 每个弹窗都是独立的的,所以根据传入参数动态调用组件。详见

  • 因为患者任务相对简单,所以对应的表单也简单,并且有大量的可以复用的东西。所以又重度使用了mixin详见包括组件、fetch 方法、提交表单、校验表单、数据处理、更新列表、props 等等。

  • 因为使用了mixin,所以,单个独立的任务就相对简单了许多,只有一些与表单相关UI 相关的 data、一些需要初始准备的数据,最多还有一个处理提交表单数据的方法

完成任务

从结构上说,完成任务其实和「帮助患者完成任务」差不多,无论 UI 界面还是交互流程,区别是一个是弹窗组件,一个是单个页面。但从复杂度上来说,完成任务则相对复杂的多的多,因为每一个任务都有一个极其复杂的表单。

下面是一张关于完成任务主要的三个交互流程的示意图:

动态表单组件

这一块已业务抽出的单独业务子表单,会被应用在不同的任务中,这些子表单的数量,理论上可以有 N 个,甚至可以在大表单之外单独提交!所以,拆出这些表单是十分有必要的。

当然,我们又使用了mixin。每个子表单都是一个独立的表单,只是对外暴露接口。

由于,每个子表单可以出现 N 次,为了区分它们,我们会在创建这些子表单的时候,赋予它们一个唯一 ID。

然后,根据需要,在每个大表单之中通过配置数据,动态引入它们。

最后,就是这些动态表单组的初始化校验处理提交数据提交。基本上就是一个正常的处理数组数据的过程。

总结

我们看到了mixin在这个项目中的重度使用,由此,我们也发现,在可复用东西较多的项目中,无论从项目的拓展性、开发速度、设计架构等等方面考虑,mixin都绝对是一把利器!

总之,mixin大法万岁!