【DataSet】DataSet-如何优雅使用DataSet,看完此篇文章完全理解C7N/choerodon/猪齿鱼 UI中的DataSet

DataSet-看完此篇文章完全理解C7N/choerodon/猪齿鱼 UI中的DataSet

  • 此文定位
  • 前言
  • 一、DataSet是什么?
    • 1.DataSet相关的核心概念
      • (1) DataSet Props(DataSet配置对象)
      • (2)DataSet Field(字段对象)
      • (3) DataSet Record(记录对象)
  • 二、DataSet入门
    • 1.DataSet Props
      • (1) autoQuery属性(Boolean):false
      • (2) autoCreate属性(Boolean):false
      • (3) dataKey属性(string | null):row
      • (4) transport属性(Transport)
      • (4) events属性(DATASET EVENTS)
        • 4.1update:({ dataSet, record, name, value, oldValue }) => void
        • 4.2query: ({ dataSet, params, data }) => boolean
      • (5) feedback属性(Feedback)
      • (6) children属性({ name: DataSet })
      • (7) fields属性(object[])
      • (8) queryFields属性(object[])
      • (9) cacheSelection属性(Boolean)
    • 2.DataSet Values
      • (1) current属性
      • (2) records属性
      • (3) data属性
      • (4) selected属性
      • (4) queryDataSet属性 observable``
    • 3.DataSet Methods
      • (1).query(page, params, cache)
      • (2).reset
      • (2).submit
      • (3).create(data, index)
      • (4).delete(records, confirmMessage: ReactNode | ModalProps)
      • (5).remove(records, forceRemove)
      • (6).操作数据记录的方法
        • 6.1.pop
        • 6.2.shift
        • 6.3.find (record, index, array) => boolean
      • (7).validate()
      • (8).getField(fieldName)
      • (9).addField(fieldName, fieldProps)
      • (10)toJSONData()
      • (11).setQueryParameter(para, value)
      • (12).appendData(data, parentRecord)
      • (13).setState(key, value),getState(key)
    • 4.DataSet Events
      • (1).update(({ dataSet, record, name, value, oldValue }) => void)
      • (2).query(({ dataSet, params, data }) => boolean)
      • (3).beforeLoad(({ dataSet, data }) => void)
      • (4).submit(({ dataSet, data }) => boolean)
      • (5).submitSuccess(({ dataSet, data }) => void)
    • 5.Record Values
      • (1).status(observable `string`))
    • 6.Record Methods
      • (1).get(fieldName)
      • (2).set(fieldName, value)
      • (3).setState(key, value),getState(key)
      • (4).clone()
      • (5) toJSONData()
      • (6).reset()
    • 7.Field Props
      • (1).name(string)
      • (2).type(string)
      • (3).label(string)
      • (4).labelWidth(number)
      • (5).format(string)
      • (6).pattern(string | RegExp)
      • (7).maxLength(number),minLength(number)
      • (8).max,min(number | string | MomentInput | fieldName)
      • (9).validator((value, name, record) => boolean | string | undefined)
      • (10).required(string)
      • (11).valueField,textField(string)
      • (11).trueValue,falseValue(boolean |string |number)
      • (12).options(DataSet)
      • (13).defaultValue(any)
      • (14).multiple(boolean | string)
      • (15).range(boolean | [string, string])
      • (16).lovCode(string),lovPara(object)
      • (17).lookupCode(string)
      • (18).bind(string)
      • (19).dynamicProps({ fieldProp: ({ dataSet, record, name }) => value })
      • (20).cascadeMap(object)
      • (21).ignore(string)
      • (22).transformRequest((value: any, record: Record) => any)
      • (23).transformResponse((value: any, object: any) => any)
    • 8.Field Methods
      • (1).get(propsName, record)
      • (2).set(propsName, value)
  • 三、优雅的使用DataSet
    • 一。使用IntDataSet
    • 二。record.get时给定一个数组
    • 三。将反复使用的字符串抽离出来变为一个常量
    • 四。FieldsToColumns
    • 五。使用DataSet.addEventListener
  • 文档
      • 1.C7N FieldsToColumns文档
        • 设计目标
        • 实现的策略
        • 利弊点
        • 使用说明:
        • 源码:
      • 2.值集配置
      • 3.值集视图配置


此文定位

一。对DataSet的核心概念进行讲解帮助新手了解DataSet。
二。大量的实例,在一定程度上起到补充猪齿鱼UI的官方文档的作用。
三。提供给已熟悉DataSet的开发者们在部分使用的场景更优雅的使用DataSet。


前言

很多初次了解DataSet的小伙伴很容易被DataSet诸多的属性和方法绕晕了头脑,此文将开发中DataSet常见的的属性和方法进行讲解帮助新手了解DataSet,并通过大量的实例在一定程度上补充猪齿鱼UI的官方文档,同时提供给已熟悉DataSet的开发者们在部分使用的场景更优雅的使用DataSet。

choerodon-ui pro的核心点就是DataSet,首先理解了DataSet就相当于理解了choerodon-pro组件的核心的百分之八十,再结合查找符合不同应用的api就可以满足日常开发,至于此文中没有提到的属性和方法大家可以去C7N ui 官网查看,此文暂且不表。


一、DataSet是什么?

在此章节中仅对部分DataSet抽象概念进行讲解,具体使用请参考choerodon-ui官网

1.DataSet相关的核心概念

DataSet 可以理解为具有保存数据和部分行为的数据仓库,抽象的数据源仓库,类似于后端数据库中表的概念。DataSet 中封装了大量基础和特定的功能代码逻辑。表单校验、导出、基础增删改查接口请求等,通过属性配置一行代码一步到位,也可通过 Transport 自定义处理与后端通信。通俗的说,DataSet就相当于一个数据库表。DataSet Props就是用来配置数据库表属性的,DataSet Field就相当于数据库表中的字段,DataSet Records就是数据库表中的数据,在了解以上的属性后再学习DataSet中的数据怎么进来,如何出去(与后端交互)。

了解了DataSet的抽象概念之后我们再来看看DataSet的其他核心概念。

(1) DataSet Props(DataSet配置对象)

Props对应DataSet的配置属性;

怎么理解这个DataSet的Props呢,你可以把DataSet理解成一个组件,那么DataSet的Props可以理解成组件的props传值,而这个组件中又基于你的传值已经封装好了很多的方法和属性,又或者可以理解为是传入数据库表的配置。

(2)DataSet Field(字段对象)

Field 对应DataSet内部所存储的字段配置属性;

字段属性配置 Field Props就是用来配置字段的属性的,包括常用的name(对应发给后端以及定义在column内用于接收接口返回数据的name的字段命名),lovCode(值集视图代码配置),lookupCode(值集代码配置),required(是否必输)等

例如在书写DataSetProps中给Field的props中写入的name,defaultValue,required等都会作为Field的配置属性;

(3) DataSet Record(记录对象)

Record 对应DataSet的每条记录。

怎么理解record呢,把它想成是数据库里的一条数据就行,到了DataSet中,record被封装好了诸多的属性(例如record.props中的 disabled:是否禁用该条record)和方法(record.get方法根据字段名获取字段值,record.set方法设置字段值),一条记录就对应一个record。

例如获取某条record的某个字段的值:

假如后端传来的数据中有一id字段,我们可以通过DataSet实例.find获取到特定的record然后进行record.get(‘id’),这样就取到了指定的record的id字段的值,如果将DataSet绑定到表格上每条record就对应表格的一行数据。

在看完以上的概念或许大家还是不太能理解遇到需要设置一个属性时应该怎么实现,此时在心中默默地问自己三个问题,DataSet是什么,Field是什么,Record是什么,问完自己三个问题之后,再问自己,Props的作用是什么,Value的作用是什么,Method的作用是什么。 遇到一个属性需要设置的时候,首先应该搞明白,它应该设置到DataSet还是Field还是说是Record里,再问自己是通过哪种方式去设置,是直接写到Props中还是在自己组件中通过Method方法。


二、DataSet入门

此章节将介绍常见的DataSet已封装好的方法与属性,在看完本章节后能初步使用DataSet的开发。

1.DataSet Props

属性格式为属性名(类型):默认值(如有)

写在getXXXDsProps.ts(命名规范)文件中,一般格式为

const getXXXDsProps=()=>({
	DataSet Props
})

为什么要用({ })的形式呢,而不是我们常见的()=>{}这种类型的箭头函数呢,这里使用的是箭头函数的简写,因为调用了getXXXDsProps这个函数后需要返回一个对象,其实也等同于

const getXXXDsProps=()=>{
	return {
		DataSet Props
	}
}

DataSet Props在DataSet实例化时(也就是new DataSet时)作为Props传入DataSet中

(示例):

import getXXXDsProps from 'store/getXXXDsProps.ts'
	
const XXXDs=new DataSet({...getXXXDsProps()})const XXXDs=new DataSet(getXXXDsProps())

常用的几个属性:

(1) autoQuery属性(Boolean):false

初始化后自动查询,在大部分项目中都设置autoQuery为false且将autoCreate设置为true

(2) autoCreate属性(Boolean):false

初始化时,如果没有记录且 autoQuery 为 false,则自动创建记录

(3) dataKey属性(string | null):row

查询返回(查询接口返回值)的 json 中对应的数据的 key, 当为 null 时对应整个 json 数据, json 不是数组时自动作为新数组的第一条数据

假如有如下的接口返回值(示例):

	{
		rows:{
			content:[{id:1,material:'机械臂'}],
		}
	}

我们需要获取到id和material这两个字段的值,但此时不指定dataKey是无法正确获取到需要的数据DataSet会默认将整个返回值作为第一条数据,而默认的对象只创建了一条仅有一个rows属性的record,这显然是不正确的。

dataKey:‘rwos.content’ 放入DataSet的props后,DataSet就能通过dataKey找到我们需要的数据的位置,从而做到正确的创建record。

(4) transport属性(Transport)

是DataSet封装好的用于与后端进行交互的属性

Transport类型中常用的属性又包含

1.create(新增请求的 axios 配置或 url 字符串)

在调用DataSet.submit()方法后DataSet将会判断此条数据的状态,如果是新增数据则会调用create的请求地址

2.read(查询请求的 axios 配置或 url 字符串)

在调用DataSet.query()方法后将会调用此请求地址

3.update(更新请求的 axios 配置或 url 字符串)

在调用DataSet.submit()方法后DataSet将会判断此条数据的状态,如果是已存在的数据(record的status)则会调用update的请求地址

4.destroy(删除请求的 axios 配置或 url 字符串)

在调用DataSet.delete()方法后DataSet则会调用destroy的请求地址

以下是一些实际使用场景(示例):

transport: {
  read: () => {
            return {
                url: `/hpfm/v1/XX/XX/page?lovCode=${lovCode}&page=0&size=10&tenantId=0`,
                method: 'GET'
            }
        },
  create: (data) => {
  		  return {
    		   url: `/hpfm/v1/XX/XXX?tenantId=${tenantId}&viewCode=${viewCode}&viewHeaderId=${viewHeaderId}`,
 		       method: 'POST',
  			   data: { ...data.data[0] }
           }
       },
},

(4) events属性(DATASET EVENTS)

4.1update:({ dataSet, record, name, value, oldValue }) => void
值更新时会触发的事件。

例如:

当表格中的record被修改时,会触发DataSet的update事件,而在update方法中可以拿到dataset,record,name等等,这样我们就可以在更新值时去做很多操作。例如判断一个特定的字段值的更新后去禁用另一个值是否可以输入,或一个字段值更新后另一个字段值也要跟随前一个字段的值进行变化。

代码示例:


update:({ dataSet, record, name, value, oldValue }) => {
		if(name==='count'){
			switch value
				case >100
					record.set(name,100)
					break
				defalut
					break
			record.set('totalCount',count+1)
		}
}

上面这段代码的意思是当DataSet中的count字段更新时,不允许count的值大于100,且totalCount会比更新后count的值大一。

4.2query: ({ dataSet, params, data }) => boolean
查询时会触发的事件,返回false会阻止此次查询。

例如


query:({ dataSet, params, data }) => {
		if(data?.companyName='汉得信息){
			return true
		}
		return false
}

上面这段代码的意思是当DataSet中的查询字段companyName字段等于汉得信息时才允许查询。

(5) feedback属性(Feedback)

查询和提交数据的反馈配置,在查询或提交事件后会自动执行feedback可以获取到反馈

1.loadSuccess(resp)

查询成功的回调

2.loadFailed(error)

查询失败的反馈

3.submitSuccess(resp)

提交成功的反馈

4.submitFailed(error)

提交失败的反馈

以下是一些实际使用场景,在提交失败后,从接口的返回值拿到提示用户失败的原因(示例):

 feedback: {
     submitFailed:(error)=> {
       notification.error(       //提示弹窗
         {
      		 message:error.message
         }
       )
     }
 },

(6) children属性({ name: DataSet })

级联行数据集,在DataSet提交时级联的DataSet也会一并提交。

多用于头行结构的表格中,用法:在创建DataSet实例的时候放入级联的DataSet,同时DataSet中的primaryKey属性将会作为级联DataSet的查询字段,在父级DataSet提交时,children DataSet也会提交。

以下是一些实际使用场景(示例):

const childrenDs=new DataSet({...getChildrenDsProps()})
const Ds=new DataSet({...getDsProps(),children:{childrenDs}})
---上面使用了es6的对象简化写法---

(7) fields属性(object[])

详情见 Field Props

字段属性数组,在此处定义的字段将被放入DataSet的field中,同时可以对field进行许多设置和取值。

以下是一些实际使用场景(示例):

fields: [
       		 {
                name: 'tenant',
                type: 'object',
                label: intl.get('hzero.common.model.tenantName').d('租户'),
                lovCode: 'XXXXX',
                ignore: 'always'
      		  }, 
  ]

(8) queryFields属性(object[])

详情见 Field Props

查询字段属性数组,在内部生成 queryDataSet,优先级低于 queryDataSet 属性

以下是一些实际使用场景(示例):

queryFields: [
       			{
                	name: 'tenant',
               		type: 'object',
               	 	label: intl.get('hzero.common.model.tenantName').d('租户'),
               		lovCode: 'XXXXX',
               	 	ignore: 'always'
      		  	}, 
  ]

(9) cacheSelection属性(Boolean)

缓存选中记录,使切换分页时仍保留选中状态。当设置了 primaryKey 或有字段设置了 unique 才起作用。

需要跨页勾选时用到的属性,需要设置primaryKey 或有字段设置了 unique,被设置的字段必须是唯一标识的字段,否则会出现勾选一条数据后字段相同值的数据同时被勾选的情况。


2.DataSet Values

对已实例化的DataSet进行取值,格式为DataSet.属性名

以下是几种常用的属性:

(1) current属性

current从英文理解起来就是当前的意思,延伸到DataSet中的意思是什么意思呢? 假如把DataSet.current打印出来我们可以发现原来它是record类型,到了DataSet中是当前的记录的意思,这样一来record相应的方法和属性就可以用在current上了,同时DataSet中也有封装好的方法来设置和获取current

代码如下(示例):

const record=Ds.current
	 record.get('id') 
	 record.set('id',1)

(2) records属性

record属性是DataSet的最核心最关键的点之一,在record上封装了很多的方法,使用频率也是极高的,与数据相关的操作等都会涉及到record,而records则是record的集合,是数组类型的数据,也是被观察者,如何理解这个被观察者,也就是类似于消息订阅,当被观察者发生变化时,会依据设定好的逻辑进入到已设定好的代码流中。

注意:包括删除状态的 Record

一般而言不建议直接操作records,获取数据建议通过record.get的方法,后续会说明为什么要用record.get。

ds.records.map(
	record=>{
		return	record.get('id')
	}
)

上面的代码表示拿到ds的所有记录中的id字段的值并放到一个数组中,可以用records筛选数据后发送请求。

(3) data属性

数据, 不包括删除状态的 Record

一般而言不建议直接操作data,获取数据建议通过record.get的方法,后续会说明为什么要用record.get。

(4) selected属性

选中记录,包括 isAllPageSelection 为 false 时缓存的选中记录

当记录被勾选后,可以通过ds.selected属性拿到被勾选的记录。常用于需要批量删除,提交等操作的场景。

 service(ds.selected.map(      //提交数据的函数
	record=>{
		return	record.get('id')
	})
)

(4) queryDataSet属性 observable

查询数据源,是DataSet类型的属性
使用queryDataSet时需要注意一点,queryDataSet是DataSet类型的所以在ds内部也需要new一个实例出来, 而在初始化ds时就去取queryDataSet往往取到的是underfined。但此时添加一个判断条件ds.queryDataSet?.ready().then(),此时在.then里面拿到的queryDataSet就不是underfined了。

例如在组件加载后需要将一个查询条件根据接口的返回值设置默认查询条件。

代码如下(示例):

useEffect(()=>{
	(async function(){
		const res=await service(params) 
		if(res){
			ds.queryDataSet?.ready().then(
			data=>{
			if(data){
						ds.queryDataSet.current.set('name',res.name)
					}
				}
			)	
		}
	})()
},[])

这样一来就可以将异步,useEffect,还有queryDataSet结合起来使用了,假如项目中使用到设置默认值的地方较多可以将这个方法封装起来作为公用方法。


3.DataSet Methods

(1).query(page, params, cache)

/*
page - 指定页码
params - 临时查询参数
cache(1.5.0-beta.0) - 是否保留缓存的变更记录
*/

需要查询数据时调用的方法。

使用实例:当调用了ds的submit方法后,数据成功发送给了后端,后端也返回了修改成功的提示后查询当前页面的数据。

代码如下(示例):

const handleSubmit=()=>{
	ds.submit().then(
		res=>{
			if(res&&!res.failed){
				ds.query(ds.currentPage)
			}
		}
	)
}

(2).reset

重置更改, 并清除校验状态

常用于自定义搜索栏form的重置按钮的回调中。

使用实例:点击重置按钮,自定义搜索栏form的内容清空。

代码如下(示例):

const handleReset=()=>{
	ds.reset()
}

(2).submit

将数据集中的增删改的记录先进行校验再进行远程提交

返回值为Promise类型,所以我们可以通过promise.then()方法拿到返回值。

提交失败有以下几种值:
false - 校验失败,
undefined - 无数据提交或提交相关配置不全。

使用实例:当record被修改后,需要提交到远程进行数据保存。

代码如下(示例):

const handleSubmit=()=>{
	ds.submit().then(
		res=>{
			if(res&&!res.failed){    //当返回值为true时才查询
				ds.query(ds.currentPage)
			}else{
				const { message }=res
				notification.error({   // 失败提示消息
					message
				})
			}
		}
	)
}

(3).create(data, index)

新建一条record,其中data为记录数据对象 index 为记录所在的索引

data为给定record的初始数据值,index为record需要在的位置。返回值为创建的record。

对于行内编辑来说,一般用法为

const record=ds.create(data,0)  			//表示创建index为0的record
record.setState('editing',true)     //设置可编辑状态需结合表格的editor使用

(4).delete(records, confirmMessage: ReactNode | ModalProps)

立即删除记录,会调用transport中的destroy方法。

records - 删除的记录或记录组
confirmMessage - 自定义提示信息或弹窗的属性, 设为false时不弹确认直接删除

在删除时,可以传入一个record的数组然后在destroy方法中拿到data进行处理。

使用实例:勾选表格行数据后点击删除进行批量删除操作。

代码如下(示例):

const handleDelete=()=>{
	ds.delete(ds.selected).then(
		res=>{
			if(res&&!res.failed){    //当返回值为true时才查询
				ds.query(ds.currentPage)
			}else{
				const { message }=res
				notification.error({   // 失败提示消息
					message
				})
			}
		}
	)
}
-------------------------ds中----------------------
destroy:({ data }) =>{
	const mapData=data.map(
		record=>record.get('xxid')
	)
	return {
		url:`xxx`,
		method:`DELETE`,
		data:mapData
	}
}

(5).remove(records, forceRemove)

临时删除记录

records - 删除的记录或记录组
forceRemove(1.5.1) - 是否强制删除

remove和delete的区别在于remove是临时删除记录,是不会掉接口的,但当remove后调用submit就相当于delete了,而且被remove的数据是不包含在data中的,但records中是有的。

当record的状态为add时,会被直接删除。

使用实例:表格行内操作时,点击取消,如果是新建数据就移除数据,否则就重置修改。

代码如下(示例):

const handleCancel=(record)=>{
	if(record.state==='add‘’){
		ds.remove(record)
	}else{
		record.reset()
	}
}

(6).操作数据记录的方法

以下操作方法都是针对DataSet的操作方法

6.1.pop
从记录堆栈顶部获取记录,返回值是记录堆的第一条数据,类型为Record

注意:此操作会改变DataSet的数据源。
相当于调用remove(ds.records[0])
而在前面讲过,remove会删除在data中的当前数据(效果为该条数据会变灰),但因为没调用接口,不会与后端进行交互。

6.2.shift
从记录堆栈底部获取记录,返回值是记录堆的最后一条数据,类型为Record

注意:此操作会改变DataSet的数据源。
相当于调用remove(ds.records[ds.records.length-1])
而在前面讲过,remove会删除在data中的当前数据(效果为该条数据会变灰),但因为没调用接口,不会与后端进行交互。

6.3.find (record, index, array) => boolean
根据函数查找并返回第一条记录

ds.find(fn) fn的参数为(record.index.array)
fn返回true后会返回当前的record,并停止循环
其中array是DataSet的记录数组,也就是会循环的record集合数组

使用实例:需要判断ds中是否至少有一条数据符合条件。

代码如下(示例):

const handleCheck=(record)=>{
	return record.get('name') === 汉得信息
}
if(ds.find(handleCheck){
	props.history.push({pathname:'/asd/asd')
}else{
	notification.warning({
		message:'没有查到相关信息'
	})
}

(7).validate()

校验数据记录是否有效,

返回值类型为Promise{boolean}
返回为promise类型的值我们可以通过.then或await的方法拿到校验结果从而判断是否此次校验是否成功,如果校验不成功或配置不完全会返回underfine

代码如下(示例):

const handleValidate=(record)=>{
	ds.validate().then(
		res=>{
			if(res){
				//通过校验后的操作
			}else{
				//没通过校验后的操作
			}
		}
	)
}

(8).getField(fieldName)

根据字段名获取字段,fieldName为字段名

比较常用的方法之一,返回值为Field类型,所以Field的methods和value都可以使用。

使用实例:某一字段需要根据事件动态设置是否必输。

代码如下(示例):

const handleClick=()=>{
	if(requiredFlag){
		ds.getField('someField').set('required',true)
	}
}

(9).addField(fieldName, fieldProps)

增加新字段,fieldName - 字段名,fieldProps - 字段属性

比较常用的方法之一,动态给DS添加字段。

使用实例:需要根据接口的返回值动态的添加字段。

代码如下(示例):

const handleClick=async()=>{
	const res= await service(参数)
	
	if(res?.content&&!res.failed){
		res.content.map(
			field=>{
				ds.addField(field.name,{
						required:field.required
						//剩余对Field的配置
					}
				)
			}
		)
	}
}

(10)toJSONData()

转换成用于提交的 json 数据

非常常用的方法之一,常用于发送网络请求时,对record记录的数据进行转换。将DataSet的数据转换为JSON格式。在DataSet的Methods中的返回值为object[],也就是[{},{}]这种类型。
代码如下(示例):

const handlexxxservice=()=>{
	xxservice(ds.toJSONData())	 //xxservice为发送请求的方法
}

(11).setQueryParameter(para, value)

设置查询参数,para - 参数名 value - 参数值

设置查询参数后,每次查询都会带上设置的参数,除非手动清空参数。

(12).appendData(data, parentRecord)

附加数据 data - 数据数组 parentRecord - 父节点,可选, 用于 childrenField 模式的树形数据

需要注意的是data需要是数组,数组中可以是对象也可以是record,当是childrenField的树形数据时可以用parentRecord来添加树节点的数据。

(13).setState(key, value),getState(key)

setState设置自定义状态值。key - 键名或者键值对对象;value - 值
getState获取自定义状态值。key - 键名

常用于借助DataSet的state进行一些判断,DataSet的setState使用频率低于Record的使用频率,Record的setState常用于行内编辑。


4.DataSet Events

(1).update(({ dataSet, record, name, value, oldValue }) => void)

值更新事件。dataSet - 数据集 record - 更新的记录 name - 更新的字段 value - 新值 oldValue - 旧值

DataSet 的任一record值更新时都会触发这个事件。常用于动态处理字段值。

使用实例:需要根据接口的返回值动态的添加字段。

代码如下(示例):

const handleClick=async()=>{
	const res= await service(参数)
	
	if(res?.content&&!res.failed){
		res.content.map(
			field=>{
				ds.addField(field.name,{
						required:field.required
						//剩余对Field的配置
					}
				)
			}
		)
	}
}

(2).query(({ dataSet, params, data }) => boolean)

查询事件,返回值为 false 将阻止查询。dataSet - 数据集 params - 查询参数 data - 查询参数

DataSet 查询时会触发这个事件。常用于判断是否进行这次查询,或在查询时添加额外的查询条件。
使用实例:如果传过来的参数的xx字段的值等于xx才可以查询。

代码如下(示例):

//ds中
events:{
	query:({data})=>{
		if(data?.xxx==='xx'){  //条件判断
			return true         //允许查询
		}
		return false			//默认阻止查询
	}
}

(3).beforeLoad(({ dataSet, data }) => void)

数据加载前的事件,用于处理请求数据。dataSet - 数据集 data - 请求数据

DataSet在发起查询请求之前触发的事件,data是触发查询之前携带的参数,return出去的就是发送请求时携带的参数。

使用实例:如果传过来的参数的xx字段的值等于xx那么就改变参数中的一个字段。

代码如下(示例):

//ds props中
events:{
	beforeLoad:({data})=>{
		if(data?.xxx==='xx'){  //条件判断
			const cloneData=data
			cloneData.xxx='123'
			return cloneData    //允许查询
		}
		return data
	}
}

(4).submit(({ dataSet, data }) => boolean)

提交事件,返回值为 false 将阻止提交。dataSet - 数据集 data - json 数据

DataSet提交时会触发的事件,可以使用的参数包括DataSet和提交时的数据data。
同样是return false就会阻止提交。

(5).submitSuccess(({ dataSet, data }) => void)

提交成功事件。dataSet - 数据集 data - 响应数据

提交成功后会触发的事件,data是接口返回的响应数据,可以在这里进行一些判断。


5.Record Values

(1).status(observable string))

record的状态,可选值 add | update | delete | sync 	

record的状态为add的时候,submit时会调用create的接口,同理为其他状态时会调用相应的接口。


6.Record Methods

(1).get(fieldName)

根据字段名获取字段值或根据字段名数组获取字段名与字段值的对象。注意:禁止通过 record.data[fieldName]的方式获取字段值。fieldName - 字段名 或 字段名数组

根据字段名获取字段值。最常用的方法之一。
注意不要通过record.data[fieldName]的方式获取字段值,首先DataSet禁止使用这种方法来获取字段值的,来说说他的坏处,就好比,一个篮子里有很多个水果,你只需要取到里面的苹果,使用record.data[fieldName]的方式就好比,你把篮子里的所有水果都拿出来一次,再取苹果,而使用record.get方法就像是直接取到苹果,不管是从性能方面还是代码可读性方面来说都只推荐get方法。

record.get('苹果')

(2).set(fieldName, value)

给指定字段赋值。fieldName - 字段名或者键值对对象;value - 值

设置字段值。最常用的方法之一。
如果字段类型是一个对象,则将值也给定为一个对象

record.set('苹果',123)
record.set('水果',{
		'苹果':1,
		'香蕉':2
	}
)

(3).setState(key, value),getState(key)

设置自定义状态值。key - 键名或者键值对对象;value - 值
获取自定义状态值。key - 键名	

设置字段自定义状态,常用于行内编辑。
代码如下(示例):

handleAdd=()=>{
    record.setState('editing', true)
    //剩余操作
}
handleSubmit=()=>{
    record.setState('editing', false)
    //剩余操作
}

(4).clone()

克隆记录,自动剔除主键值	

返回值为剔除主键值的Record。

(5) toJSONData()

转换成用于提交的 json 数据, 受 DataSet 的 dataToJSON 属性影响。

非常常用的方法之一,常用于提交数据时,对数据的转换,返回值为JSON格式的数据。

(6).reset()

重置更改

重置所有对record的更改。


7.Field Props

(1).name(string)

字段名

name为字段名,是唯一的。

(2).type(string)

字段类型,可选值:boolean | number | string | date | dateTime | time | week | month | year | email | url | intl | object | attachment | json | bigNumber(1.5.1)

类型没指定正确,可能会导致没有值。
为object时,可以指定,valueField和textField。
行内编辑时在editor只指定true时,会根据字段的类型来匹配对应的编辑器

(3).label(string)

字段标签。类型为string | ReactNode

字段的标签,也就是置于冒号左边的东西。
在表格中label就是表头标题。

(4).labelWidth(number)

字段标签宽度。

标签的宽度,number类型。

(5).format(string)

字符串类型和日期类型字段值格式化。 字符串类型格式化可选值: 'uppercase' 'lowercase' 'capitalize'

格式化。
uppercase会将字段串变为大写。
lowercase会将字段串变为小写。
capitalize会将字段串的第一个字符大写,其他的都是小写。

(6).pattern(string | RegExp)

正则校验

正则校验,可以指定是字符串,也可以使用正则表达式来限制。

(7).maxLength(number),minLength(number)

最大长度,最小长度

针对字符串类型的字段的最大长度和最小长度,超过最大长度后将不能再输入。

(8).max,min(number | string | MomentInput | fieldName)

最大值,最小值。 fieldName 指向当前记录的 fieldName 值作为最大值。type 为 bigNumber 时,传入 string 类型。

针对数字类型的字段的最大最小值。

(9).validator((value, name, record) => boolean | string | undefined)

校验器,当返回值为 false 或 涵盖错误信息的字符串,则为校验失败

可以在这里进行一些自定义的校验,当return出去为FALSE或报错消息时为校验失败。
代码如下(示例):

//ds中
{
	fields:[
		{
			name:'xx',
			validator:(value) => {
				if(value?.length>10){
					return '超出最大长度,请重新输入'
				}
			}
		}
	]
}

(10).required(string)

是否必选

(11).valueField,textField(string)

值列表的文本字段,值列表的值字段。默认值 meaning value

值集,值集视图都会用到这个,通俗点说,textField就是在输入框中显示的字段。
valueField就是在列表中选择后的值字段。

一般来说,值集的值字段为value,文本字段为meaning。

(11).trueValue,falseValue(boolean |string |number)

类型为 boolean 时,false 对应的值,	true 对应的值

trueValue是为true时的值,
falseValue是为false时的值。

(12).options(DataSet)

下拉框组件的菜单数据集

DataSet类型。
options的DataSet中的data就是下拉框里的数据。
(示例):

//ds中
const xxDs=new DataSet({
	selection:'single'
	data:[
		{
			value:'Y',
			meaning:'是'
		},
		{
			value:'N',
			meaning:'否'
		}
	]
})

{
	fields:[
		{
			name:'xx',
			options:xxDs
		}
	]
}

(13).defaultValue(any)

默认值

没有值时的值

(14).multiple(boolean | string)

是否为值数组。 当为字符串时,作为数据分隔符,查询时会将字符串分割成数组,提交时会将数组拼接成字符串

无论是值集(下拉框)需要多选还是值集视图(弹窗表格)需要多选,都需要设置multiple为true。

(15).range(boolean | [string, string])

是否为范围值。 当为 true 时,则值为[startValue, endValue];当为数组时,例如['start', 'end']时,则值为{ start: startValue, end: endValue }

当设置日期为范围时可以指定类型为日期并range设置为true。

(16).lovCode(string),lovPara(object)

LOV 配置代码
LOV 或 Lookup 查询参数对象
Lov 配置请求地址

设置值集视图的属性,需要在hzero平台先配置好值集视图后(设置值集视图的步骤在此文末尾),设置字段为对象类型,再给定lovCode也就是值集视图代码,一个最简单的使用值集视图的设置就算完成了。

lovPara是值集视图或值集查询时所携带的参数。

其实不管是值集还是值集视图都只是一种保存数据的方式,而我们需要请求到数据就需要通过接口来获取,而这种发送请求不需要我们书写是DataSet封装好的,我们只需要配置lovCode或者lookupCode,在理解了值集和值集视图之后,lovPara就好理解了,查询的时候请求接口难免会有需要特殊配置的地方,例如,值集视图中保留了几个类别的数据集需要通过发送的参数进行区分返回值,而我们在请求值集视图时,需要请求哪个数据集就需要在lovPara中设置。

(示例):

//ds props中
{
	fields:[
		{
			name:'xx',
			type:'object'
			lovCode:'Hxxxxx.xxx'  //值集视图代码
			lovPara:{ 
				type:'xxx'   //此时请求值集视图时会携带上?type='xxx'的查询参数。
			}
		}
	]
}

(17).lookupCode(string)

值列表请求地址

值集视图展示效果是Modal弹窗,而值集也就是lookupCode展示效果是下拉框。

(18).bind(string)

内部字段别名绑定

别名是什么意思,就是说值其实是相等的,但只有名字不一样。
例如现在有个工厂对象,工厂对象下有工厂名称和工厂Id,而我们只需要在选择工厂时发送给后端选择的工厂ID,这时候就需要用到bind。
(示例):

//ds props中
{
	fields:[
		{
			name:'siteObj',
			type:'object',
			lovCode:'Hxxxxx.xxx'  //值集视图代码
			lovPara:{ 
				type:'xxx'   //此时请求值集视图时会携带上?type='xxx'的查询参数。
			},
			ignore:'always'   //忽略提交,只提交siteId
		},
		{
			name:'bindSiteId',
			type:'number'
			bind:'siteObj.siteId'    //内部字段绑定,siteObj值发生变化时bindSiteId的值也会变化且值等于siteId,因为bindSiteId绑定了siteObj下的siteId所以siteId变化时bindSiteId也会变化。
		}
	]
}

(19).dynamicProps({ fieldProp: ({ dataSet, record, name }) => value })

动态属性对象。对象为字段属性和返回该字段值的钩子的键值对。

动态计算对象,怎么理解这个动态计算对象呢,就是所有需要动态变化的属性都必须写在这里,只讲理论大家可能无法理解,什么叫做动态变化,在DataSet中一般指的是,此字段的某些属性,例如required,是否必输,需要根据其他条件来判断是否必输,而根据我们之前所学的知识,只能将required赋值true或false不能动态变化,而有了这个动态计算属性后,我们就可以根据条件来赋予属性不同的值,那么怎么才能动态的赋值到正确的属性上呢,我们直接通过例子来理解。
(示例):

//ds props中
{
	fields:[
		{
			name:'site',
			type:'object',
			dynamicProps:{
				required:({record})=>{
					return record.get('material')?true:false    //record中的material如果不为空则必输
				}
			}
		},
		{
			name:'material',
			type:'object',
			dynamicProps:{
				required:({record})=>{
					return record.get('site')?true:false  //record中的site如果不为空则必输
				}
			}
		}
	]
}

怎么理解上面的代码,也就是这两个字段本身都不是必输的。但如果其中一个字段有值,则另一个字段就是必输的了。这就是利用了动态计算属性来控制是否必输,而动态计算属性的作用远远不止于此,包括我们之前讲的lovPara也可以通过动态计算来确定,只要是Fields Props中的属性都可以通过态计算属性动态的设置,

(20).cascadeMap(object)

快码和 LOV 查询时的级联参数映射。

怎么理解这个级联参数映射,cascadeMap级联的子级参数没有值时会禁用,同时子级的参数是父级的值来动态变化的,这么说还是不太好理解,直接看例子。
(示例):

//ds props中
{
	fields:[
		{
			name:'site',
			type:'object',
		},
		{
			name:'siteId',
			type:'number',
			bind:'site.siteId'
		},
		{
			name:'material',
			type:'object',
			lovCode:'xxxx'
			cascadeMap:{
				siteId:'siteId'    //键名为请求值集时的参数名,键值为site选择后的siteId的值
			}
		}
	]
}

上面这段代码的意思是我在选择了site这个字段后,material才可以被选择,同时,material请求值集视图时所携带的参数为?siteId=‘site选择后的siteId的值’。
级联的效果类似于lovPara,但级联是根据于已有字段的值来动态变化请求参数的,同时级联的子级需要在选择父级之后才可以选择。

(21).ignore(string)

忽略提交, 可选值: always - 总是忽略 clean - 值未变化时忽略 never - 从不忽略

忽略提交,在Field中定义过的字段,只要是有值的在新建或删除时都会被提交到后端,但有时,我们只想提交我们需要提交的参数,比如在选择site值集视图时,我们不想将site这个对象提交上去,而是只需要提交在选择了site之后的siteId,此时就需要用到ignore这个属性,而ignore可以给定三个值,第一个always,在提交时会直接忽略掉,第二个是clean,只有在值未变化时才会忽略,第三个是从不忽略。
(示例):

//ds props中
{
	fields:[
		{
			name:'site',
			type:'object',
			ignore:'always'
		},
		{
			name:'siteId',
			type:'number',
			bind:'site.siteId'
		},
	]
}

(22).transformRequest((value: any, record: Record) => any)

在发送请求之前对数据进行处理

value就是提交时的值,return 出去的就是发送请求时的值,我们可以在这里进行一些操作,例如根据条件来判断最后发送到接口应该是什么样的值。
(示例):

//ds props中
{
	fields:[
		{
			name:'site',
			type:'object',
			transformRequest:(value) => {
				return value==='xxx'?符合条件的值:不符合条件的值  //这一步就是来判断value应该等于什么
			}
		},
	
	]
}

(23).transformResponse((value: any, object: any) => any)

在获得响应之后对数据进行处理

在获取响应也就是查询到数据后,可以在这里进行对查询到的数据更改的操作,例如,我们需要在查询到值为F时将其转换为不合格,就可以在这里操作。
(示例):

//ds props中
{
	fields:[
		{
			name:'site',
			type:'object',
			transformResponse:(value) => {
				return value==='F'?'不合格':'合格'  //这一步就是来判断value应该等于什么
			}
		},
	]
}

8.Field Methods

这里的方法都是对Field操作的方法,注意与DataSet Methods和Record Methods区分开来。

(1).get(propsName, record)

根据属性名获取属性值,propsName - 属性名 record - 记录

这个和record的get又不一样,这里需要给指定的record,并且获取的是属性的值而不是字段的值。
(示例):

const handleGetFieldProps=()=>{
	ds.getField('xx字段').get('required',ds.current) // ds当前record的xx字段的required属性值
}

(2).set(propsName, value)

设置属性值,propsName - 属性名;value - 属性值

设置属性的值,可以在dynamicProps中动态设置属性,也可以在set时设置属性。
(示例):

const handleGetFieldProps=()=>{
	ds.getField('xx字段').set('required',true) // ds的xx字段的required属性值设置为true
}

三、优雅的使用DataSet

一。使用IntDataSet

在使用DataSet时我们经常会遇到需要DataSet Props中(大多数情况是在transport也就是和后端交互中)需要使用DataSet Props之外的参数例如路由参数或id等,大多数人的写法都是作为形参给到DataSet Props中,我在这提出一种更加优雅的新解法。
例如:
需要从路由中取到参数,作为DataSet Props 中的Transport 的create的参数。

一般写法:
const 参数=this.props.xxxxx
const const ds=new DataSet(dsProps(实参))//dsProps是DataSet Props
//ds props 中
const dsProps=(形参)=>({
	transport:{
		create:()=>{
			return {
				url:`xxxxx/形参`
			}
		}
	}
})

优雅写法:
const 参数=this.props.xxxxx
const initDataSet=(dsProps)=>{
	const cloneDsProps={...dsProps()}
	cloneDsProps.transport.create=()=>{
		return {
			url:`xxxxx/参数`
		}
	}
	return cloneDsProps
}

const ds=new DataSet(initDataSet(dsProps))//dsProps是DataSet Props

二。record.get时给定一个数组

在record.get时要一次get到多个参数,我们可以通过record.get给进一个数组,返回值会是一个对象,键名为参数的名字,键值为参数的值。
例如:

获取三个参数的值

一般写法:
record.get('x')
record.get('xx')
record.get('xxx')

优雅写法:
record.get(['x','xx','xxx'])

三。将反复使用的字符串抽离出来变为一个常量

例如在DataSet Props中的transport的接口服务前缀基本都是一样的这时可以把它抽离出来作为一个常量,方便下次修改。
例如:

//Ds props中

一般写法:
transport:{
	create:()=>{
		return {
			url:`/hand-china/xxxxx`
		}
	},
	update:()=>{
		return {
			url:`/hand-china/xxxxx`
		}
	},
	destoryed:()=>{
		return {
			url:`/hand-china/xxxxx`
		}
	}
}
优雅写法:
const prefix='/hand-china'
transport:{
	create:()=>{
		return {
			url:`${prefix}/xxxxx`
		}
	},
	update:()=>{
		return {
			url:`${prefix}/xxxxx`
		}
	},
	destoryed:()=>{
		return {
			url:`${prefix}/xxxxx`
		}
	}
}

四。FieldsToColumns

从字面意思字面的理解就是将字段转换为Columns。
它优雅在哪里?
当我们对面一个普通表格时,需要一个一个的定义column,哪怕它只有一个name属性。
当使用FieldsToColumns仅需要对需要特殊配置的字段进行配置,极大的提升了我们的开发效率,同时在编写column的思想方面也有所提升,从之前的命令式编译变为了声明式的编译。
说了这么多大家肯定都好奇这个FieldsToColumns应该怎么用吧。
文档我将放到最后大家有兴趣的可以了解一下。

五。使用DataSet.addEventListener

【DataSet】DataSet-如何优雅使用DataSet,看完此篇文章完全理解C7N/choerodon/猪齿鱼 UI中的DataSet_第1张图片
可以看到DataSet构造器的原型上有addEventListener这个属性,name如何在DataSet中使用这个属性呢,addEventListener接收两个参数,第一个是字符串类型的事件名称,第二个是事件触发时的回调。

例如:使用addEventListener实现删除按钮在至少勾选一行数据后解除禁用。

DataSet.addEventListener('select',(e)=>{
		this.setState({
			selectLength:e.dataSet.selected.length
		 })
	}
)

文档

1.C7N FieldsToColumns文档

  1. 设计目标

    极简代码量,重新定义设置column的方式,从一定的角度上转变开发者对columns的掌控和开发模式,从需要定义所有的表格字段,转变为只需配置我们需要的字段。

  2. 实现的策略

    获取到传入的DataSet的字段后,剔除ignoreFields的字段,将config中的配置给到对应的字段中。

  3. 利弊点

    优点:

    当不需要特殊配置时,实现一行代码搞定columns,需要特殊配置时,也提供了可靠的解决方法。

    缺点:

    1.需要根据表格列展示顺序书写Fields

    2.不适用于复杂的表格,例如从接口获取到动态字段等场景。

使用说明:
*参数* *类型* *默认值* *说明*
参数一(必输) DataSet 表格的DataSet
参数二(可选) Array [ ] 需要忽略的字段
参数三(可选) { String: ColumnsProps } { } 一个对象,对象里的键为字段名,值为需要传给字段的表格配置

使用示例:

1.不需要特殊配置时:

const getColumns=()=>{return FieldsToColumns(DataSet)

}	

2.需要对表格列进行特殊配置时:

const getColumns=()=>{

	const ignoreFields=['siteName','siteId']

	const config={ 

​		demension:{

			align:'center'}

	}return FieldsToColumns(DataSet,ignoreFields,config)

}


源码:
	import { DataSet } from 'choerodon-ui/pro';
    import { ColumnProps } from 'choerodon-ui/pro/lib/table/Column';

    interface Config {
        [key: string]: ColumnProps;
    }

    interface T { }

    export const FieldsToColumns = (
        dataSet: DataSet,
        ignoreFields: Array<T> = [],
        config: Config = {}
    ) => {
        if (ignoreFields instanceof Array && config instanceof Object && dataSet instanceof DataSet) {
            const nameArray = Array.from(dataSet.fields.keys());
            const cloneNameArray=[...nameArray ]
            // 去重
       	    let ignoreNum=0
    	    cloneNameArray.map((name, index) => {
     	    ignoreFields.map((ignoreField) => {
      	    if (ignoreField === name) {
           		 nameArray.splice(index-ignoreNum, 1)
           		 ignoreNum++
       		 }
   		     return null;
    	  });
    		  return null;
    });
        return nameArray.map((name) => ({
                name,
                ...config[name],
            }));
        } else {
            return [];
        }
    };

2.值集配置

在开发管理中的值集配置中设置。
【DataSet】DataSet-如何优雅使用DataSet,看完此篇文章完全理解C7N/choerodon/猪齿鱼 UI中的DataSet_第2张图片
值集分为三种值集
1.独立值集
2.URL值集
3.自定义SQL值集

3.值集视图配置

在开发管理中的值集视图配置中设置。

值字段名的含义是你选择了一条数据之后发送给后端的是哪个字段和它的值,显示字段名的含义是你选择了一条数据之后显示在输入框中的是哪个字段的值。

值集视图配置时,视图代码需要符合项目上设置的规范,如项目上没有设置规范则要遵守汉得开发平台的规范。

如何从头配置一个值集视图,第一步需要先配置值集,配置完值集后,点击值集视图配置的新建按钮,会出现以下弹窗。
【DataSet】DataSet-如何优雅使用DataSet,看完此篇文章完全理解C7N/choerodon/猪齿鱼 UI中的DataSet_第3张图片

根据值集来填写好所有输入框后,点击确认会跳转到这样的界面。
【DataSet】DataSet-如何优雅使用DataSet,看完此篇文章完全理解C7N/choerodon/猪齿鱼 UI中的DataSet_第4张图片
在这里配置值集视图的字段,包括值集视图的查询字段和表格列字段。

点击新建表格字段,会出现以下的弹窗。
【DataSet】DataSet-如何优雅使用DataSet,看完此篇文章完全理解C7N/choerodon/猪齿鱼 UI中的DataSet_第5张图片
表格字段名对应你值集中的字段名,表格列标题对应你这一列表格的表头,列宽度就是你这一列表格的宽度,列序号就是你所有表格列的序号,表格列会根据你列序号来排序,勾选了查询字段就会在值集视图的查询区域出现这个字段,勾选了表格列就会在表格中出现这一列,红框中就是查询区域,蓝框中就是表格列区域。
【DataSet】DataSet-如何优雅使用DataSet,看完此篇文章完全理解C7N/choerodon/猪齿鱼 UI中的DataSet_第6张图片

你可能感兴趣的:(ui,数据库,dataset,react)