今天主要介绍的是我们的 ProCode 体系。对于中后台来说,最常见的业务场景就是表单,表格。

对于 ProForm 和 ProTable,其本质上是基于 ant-design-vue 的 Form 和 Table 组件的二次封装。在设计上,ProForm 和 ProTable 并没有裁剪掉 Form 和 Table 的任何能力,而是一种适合我们业务的能力加强。

表单ProForm

其中,表单会有水平表单,垂直表单,行内表单,行内栅格表单等几种呈现方式。

ProForm测试页面

路由:/test,比如 http://localhost:8081/test

水平表单

一行一个,就是经典的水平表单场景,也是 ProForm 默认的行为。

如果不希望表单控件占满一行,拉得太长,可以从两个方面去考虑。

  • 第一,限制 form 的宽度,比如通过 css 指定一个 width。
  • 第二,通过属性 :auto-fill="false" 控制,维持控件原有尺寸。实际上看起来效果并不好(如下图所示),所以,如有必要,推荐还是用第一种比较好!

垂直表单

垂直表单其实只是水平表单的一种表现形式,具体表现为 label 和 content 的上下排列。涉及的属性是layout="vertical"

行内表单

普通的行内表单是指表单按盒子模型排列,宽度不够时自动换行。需要用到属性layout="inline" + :use-grid="false" + :auto-fill="false"。展现形式如下:

虽然实现了行内效果,但是整体上看起来也是比较丑的,是不推荐使用的一种配置!

行内栅格表单

目前来说,针对行内表单场景,我们一般倾向于使用栅格表单,就是常见的 Row + Col 布局,支持响应式,看起来会更加整洁!涉及的属性是layout="inline"

固定 label 宽度

不同的表单项通常 label 的宽度是不一样的,但是为了保证视觉上的效果,我们会将 label 宽度统一,这里用到的是属性label-width,配置示例如下:

label-width="120px"

Form 本身的 Props

如本文开头所说,Form 本身的能力并没有被裁剪,原本支持的属性和事件依然得到支持。就比如前面一直提到的 layout 属性,其本身就是一个 Form 组件的属性,而不是 ProForm 提供的,只是 ProForm 在这个基础上去做了更多的判断和逻辑组合,以提供更强大的能力!

内置的控件类型

针对常见的业务类型,ProForm 内置支持了很多表单控件,通过valueType来控制渲染什么控件,具体解释如下。对于特殊的未支持的类型,也可以根据业务需要,通过 customRenderFormItem 自行渲染!

输入框 Input

valueType="input",也是默认行为,不传valueType也会渲染普通的 Input 输入框。

数字输入框 InputNumber

valueType="inputNumber"

文本框 Input.TextArea

valueType="textarea"

valueType="inputSearch"

密码框 Input.Password

valueType="inputPassword"

下拉框 Select

valueType="select"

对于下拉框,我们要关注的是:

  1. 数据从哪来?
  2. 展示什么?
  3. 给后端什么数据?

对于第一个问题,数据源是通过selectDataSource提供。比如绑定一个selectDataSource: userList.value

对于第二个问题,也是我们经常要思考的,下拉的文本展示用哪个字段呢,是用姓名,还是身份证,还是其他的,这个是可以通过selectOption来配置的。比如:

selectOption: {
	// 展示 realName 字段
	label: 'realName'
}
1
2
3
4

对于第三个问题,我们要清楚的是,一般来说,后端并不需要UI展示层面的名称,而是需要存储 id, code 之类的字段,比如userId, paramCode。所以,我们需要指定一个使用的字段。

selectOption: {
	// 值取id字段
	key: 'id'
}
1
2
3
4

参数字典下拉框 Params

valueType="params"

参数字典下拉框是一种特殊的下拉框业务类型。

因为下拉框场景很多,不可能所有的下拉场景都设计单独的DB表去维护,然后提供类似于xxxService.find之类的接口供前端查询。参数字典的出现就是为了满足这类通用的下拉,枚举场景。

参数字典下拉框需要通过配置paramCode去匹配数据。

当然,仅仅有paramCode也是不够的,你必须提供参数字典的数据源,这个数据源一般是通过listByCodeList接口查询得到,涉及的通用代码是:

const { params, trigger: triggerForParams } = useFetchParams(
	"TMC.TRIGGER_STATUS,TMC.EXECUTOR_BLOCK_STRATEGY,TMC.EXECUTOR_ROUTE_STRATEGY,TMC.GLUE_TYPE"
);
1
2
3

取到数据params后,需要绑定到 ProForm 组件上。

<cl-pro-form :params="params" />
1

控件属性/事件

单纯地把表单控件渲染出来,仍然不能满足各种各样的业务,所以,对于控件的属性和事件,我们也必须全部支持。我们可以通过 props 属性进行属性和事件的传递,其中事件是以onXXX形式给出。

对于事件来说,我们劫持了组件的原生事件,在回调中第一个参数位置提供了表单对象formModel,用以支持个性化的业务逻辑。

{
  title: "超时时间",
  dataIndex: "executorTimeout",
  valueType: "inputNumber",
  rule: [REQUIRED_VALIDATOR_BLUR_FOR_NUMBER],
  props: {
  	placeholder: "任务超时时间,单位秒,大于零时生效",
    // 劫持了组件原生事件,原生事件支持的回调参数都往后推一个位置
    onChange: (formModel, value) => console.log(value)
  },
},
1
2
3
4
5
6
7
8
9
10
11

另外,props 也支持函数类型,适用于根据 formModel 动态设置 props 的场景。

props: (formModel) => {
  return {
    disabled: formModel.otherProp === true,
  };
},
1
2
3
4
5

控件插槽

对于支持插槽的表单控件,我们也提供了配置能力。属性名是 v-slots,保持与 Vue JSX Slot 一致。

其他

所有内置控件就不一一说明了,可以关注 pro.ts 文件中的 ValueType 类型。

如果需要定制样式,可以自己加 class 去实现,不再赘述!

表单默认值和表单回写

表单默认值是指,即便表单啥也没操作,我们可以根据需要,先填充一个默认值,这个是通过属性defaultValue实现的。

表单回写不同于默认值,是整个表单对象的回写操作,一般是指从后端取到数据后,把相应的值回填到表单。这个是通过write-back-form属性控制的。具体用法可以在项目里面搜一搜!

<cl-pro-form
  ref="proFormRef"
  :write-back-form="writeBackForm"
  :form-items="formItems"
  :params="params"
  :on-submit="onSubmit"
  @cancel="onCancel"
/>
1
2
3
4
5
6
7
8

表单校验

表单校验首先需要配置规则,这个是通过rule配置项实现的。我们把一些常用的配置做成常量,方便快速引用,比如REQUIRED_VALIDATOR_BLUR是用于在控件失焦时触发必填校验。

如果需要自定义校验规则,也可以自己写,这个和 ant-design-vue 的表单校验规则是完全兼容的。

rule: [
  {
  	trigger: 'change',
    max: 10,
    message: '超长了!'
  }
]
1
2
3
4
5
6
7

默认情况下,会在点击保存按钮时自动进行表单校验。

实际上,有时候我们会通过 :use-buttons="false" 隐藏掉内置的表单按钮,根据自己的业务需要自行提交表单。这个时候,仍然可能需要校验表单后再调用提交接口,那么怎么使用呢?

我们可以通过 ref 取到 ProForm 组件的引用,通过 validate 方法触发校验。

const customSubmit = async () => {
	await proFormRef.value.validate();
  // 校验通过,看情况balabala写一堆代码
  // 取到formModel对象
  const formModel = proFormRef.value.getProcessedFormModel()
  // 也可以考虑进行对象加工
  const params = {
  	...formModel,
    xxxId: someId
  }
  // 然后调接口进行提交
  await xxxService.xxxMethod(params)
}
1
2
3
4
5
6
7
8
9
10
11
12
13

表单提交

ProForm 内置了两个按钮。

  • 点击取消按钮时会触发cancel事件,绑定的事件处理函数 onCancel可以根据需要去写逻辑,比如关闭弹出框,返回上一个页面等等。
  • 保存按钮是用于表单提交的,在设计上不是通过事件触发机制完成,而是需要通过on-submit属性传递一个异步函数,在这个异步函数中做表单提交操作。
// onSubmit 能接收到 formModel 参数
const onSubmit = async (formModel: PlainObject) => {
	// 在这里可以进行数据处理,表单提交操作
}
1
2
3
4

表格ProTable

ProTable 的目标并非只是完成一个 table 的展示,而是覆盖 CRUD 场景。

一份数据,三处共用

ProTable 的 columns 配置影响三处,分别是查询表单,表格,详情。某些情况,某个字段并不需要在这三处都展示,这个可以通过 hideInXXX 来配置。

{
  // 在查询表单中不显示
  hideInSearch: true,
  // 在Table中不显示
  hideInTable: true,
  // 在详情中不显示
  hideInDescriptions: true
}
1
2
3
4
5
6
7
8

查询表单部分

可以看到,查询表单部分也就是一个表单,按我们的设计,自然是充分利用 ProForm。

如果不需要展示查询表单,可以通过:use-header="false"控制。

Toolbar 部分

toolbar 默认是一些辅助性工具,但是你可以通过插槽 toolbar 去渲染更多的内容,比如各种功能性按钮。

如果不需要展示 Toolbar ,可以通过:use-toolbar="false"控制。

Table 部分

table 的展示区域其实比表单更简单,主要就是文本的展示,但是也有这么几种基本类型,可以通过 columnType 配置。

  • params:这个和表单类似,不过是优先寻找行数据中的 ${dataIndex}ByTran,如果找不到,需要配合paramCode去做一个前端的翻译工作。
  • translate:纯翻译功能,适用于一些id翻译的场景,直接找${dataIndex}ByTran,找不到会默认展示dataIndex
  • actions:操作列,这个不多说了,参考现有代码看就行。
  • text:默认值,不做任何加工处理,直接取dataIndex能索引到的值。
  • link:链接类型。
  • image:图片。

有特殊渲染需求的,当然还是要靠customRender实现。

PS: dataIndex支持多级索引,比如:

{
  title: "当前节点",
  dataIndex: "currentTasks[0].name",
  width: "120px",
  hideInSearch: true,
},
1
2
3
4
5
6

Pagination 部分

可以通过 customPagination 传递一些配置,具体见 Pagination API

如果不需要展示 Pagination,可以通过:use-pagination="false"控制。

其他可预见的插槽

实际开发过程中,难免会遇到这里加一点,那里加一点的情况。所以,对于一些特殊的位置,我们会设计一些插槽,以满足定制开发场景。

prepend 插槽

位置上是在查询表单上方。类似这种:

toolbar 插槽

这个在 toolbar 部分已经说过了。

before-table 插槽

位置上是在 table 上方,toolbar 下方。

default 插槽

紧跟 table 后面,其实主要是给弹出层用的插槽位。

非分页情况

ProTable 默认情况下会认为这是一个分页模型。实际上,有些时候我们不需要分页模型,仅仅需要展示一个 List Table。这个时候,可以通过上述配置隐藏该隐藏的部分,然后通过 :use-provided="true" 以及 :provided-data="yourDataList" 来实现自定义的数据展示。具体可以在项目中搜索一下相关的案例。