今天主要介绍的是我们的 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"
搜索框 Input.Search
valueType="inputSearch"
密码框 Input.Password
valueType="inputPassword"
下拉框 Select
valueType="select"
对于下拉框,我们要关注的是:
- 数据从哪来?
- 展示什么?
- 给后端什么数据?
对于第一个问题,数据源是通过selectDataSource
提供。比如绑定一个selectDataSource: userList.value
。
对于第二个问题,也是我们经常要思考的,下拉的文本展示用哪个字段呢,是用姓名,还是身份证,还是其他的,这个是可以通过selectOption
来配置的。比如:
selectOption: {
// 展示 realName 字段
label: 'realName'
}
2
3
4
对于第三个问题,我们要清楚的是,一般来说,后端并不需要UI展示层面的名称,而是需要存储 id
, code
之类的字段,比如userId
, paramCode
。所以,我们需要指定一个值使用的字段。
selectOption: {
// 值取id字段
key: 'id'
}
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"
);
2
3
取到数据params
后,需要绑定到 ProForm 组件上。
<cl-pro-form :params="params" />
控件属性/事件
单纯地把表单控件渲染出来,仍然不能满足各种各样的业务,所以,对于控件的属性和事件,我们也必须全部支持。我们可以通过 props 属性进行属性和事件的传递,其中事件是以onXXX
形式给出。
对于事件来说,我们劫持了组件的原生事件,在回调中第一个参数位置提供了表单对象formModel
,用以支持个性化的业务逻辑。
{
title: "超时时间",
dataIndex: "executorTimeout",
valueType: "inputNumber",
rule: [REQUIRED_VALIDATOR_BLUR_FOR_NUMBER],
props: {
placeholder: "任务超时时间,单位秒,大于零时生效",
// 劫持了组件原生事件,原生事件支持的回调参数都往后推一个位置
onChange: (formModel, value) => console.log(value)
},
},
2
3
4
5
6
7
8
9
10
11
另外,props 也支持函数类型,适用于根据 formModel 动态设置 props 的场景。
props: (formModel) => {
return {
disabled: formModel.otherProp === true,
};
},
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"
/>
2
3
4
5
6
7
8
表单校验
表单校验首先需要配置规则,这个是通过rule
配置项实现的。我们把一些常用的配置做成常量,方便快速引用,比如REQUIRED_VALIDATOR_BLUR
是用于在控件失焦时触发必填校验。
如果需要自定义校验规则,也可以自己写,这个和 ant-design-vue 的表单校验规则是完全兼容的。
rule: [
{
trigger: 'change',
max: 10,
message: '超长了!'
}
]
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)
}
2
3
4
5
6
7
8
9
10
11
12
13
表单提交
ProForm 内置了两个按钮。
- 点击取消按钮时会触发
cancel
事件,绑定的事件处理函数onCancel
可以根据需要去写逻辑,比如关闭弹出框,返回上一个页面等等。 - 保存按钮是用于表单提交的,在设计上不是通过事件触发机制完成,而是需要通过
on-submit
属性传递一个异步函数,在这个异步函数中做表单提交操作。
// onSubmit 能接收到 formModel 参数
const onSubmit = async (formModel: PlainObject) => {
// 在这里可以进行数据处理,表单提交操作
}
2
3
4
表格ProTable
ProTable 的目标并非只是完成一个 table 的展示,而是覆盖 CRUD 场景。
一份数据,三处共用
ProTable 的 columns
配置影响三处,分别是查询表单,表格,详情。某些情况,某个字段并不需要在这三处都展示,这个可以通过 hideInXXX
来配置。
{
// 在查询表单中不显示
hideInSearch: true,
// 在Table中不显示
hideInTable: true,
// 在详情中不显示
hideInDescriptions: true
}
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,
},
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"
来实现自定义的数据展示。具体可以在项目中搜索一下相关的案例。