Choerodon UI 介绍
Choerodon UI 是基于 Ant [email protected] 的 React 实现,开发和服务于企业级后台产品。
dataSet 学习
dataSet 就是一个store 可以随意处理
Transport 的使用
dataSet 中的 transport 里面封装了 dataset 与后端交互的一些基本方法,利用 transport 中的方法可以很容易实现数据的增删改查和校验等功能。
方法介绍
- read - 查询请求的 axios 配置或 url 字符串
- create - 新建请求的 axios 配置或 url 字符串
- update - 更新请求的 axios 配置或 url 字符串
- destroy - 删除请求的 axios 配置或 url 字符串
- validate - 唯一性校验请求的 axios 配置或 url 字符串
- submit - create, update, destroy 的默认配置或 url 字符串
基本使用方法
// DetailDS.js - 将 dataSet 单独抽离出来的文件
// 调用getCurrentOrganizationId 可以获取 CurrentOrganizationId
import { getCurrentOrganizationId } from 'utils/utils';
const API_LIST = '/lightkits';
const organizationId = getCurrentOrganizationId();
export default () => {
detailDS = new DataSet({
// 自动查询,开启即进入页面自动调用 transport 中的 read 方法
autoQuery: true,
fields: [
{
name: 'id',
// 当字段名相同时,ui 组件上则会展示对应的 label 标签,并且数据为通过 read 方法获取到的所对应字段的数据。
label: '员工ID',
// 字段类型,可选值:boolean number string date dateTime time week month year email url intl object
type: 'number',
},
{
name: 'employeeName',
label: '员工姓名',
type: 'string',
// 必输字段。如果没有输入,会有默认的必输提示。
required: true,
},
{
name: 'employeeNumber',
label: '员工编号',
type: 'string',
required: true,
},
{
name: 'hiredate',
label: '入职时间',
// 字段类型为日期类型时,组件中对应的输入框会变为日期选择框
type: 'date',
},
{
name: 'email',
label: '邮箱',
// 字段类型为 email 时,组件会校验输入内容是为 email
type: 'email',
required: true,
},
{
name: 'age',
label: '员工年龄',
/**
字段类型为 number 时,组件展示为数字输入框,只能输入数字,
并且后端对应字段的数据不为number时,就无法展示该数据。
*/
type: 'number',
// 步距,当字段类型为 number 时,在设置 step 之后,文本输入框会变为 NumberField。
step: 1,
// 最小值
min: 1,
// 最大值
max: 100,
required: true,
},
{
name: 'enable',
label: '是否有效',
// 字段类型为 boolean 时,组件展示为选择框
type: 'boolean',
// true 对应的值,类型:boolean|string|number,默认 true
trueValue: '1',
// false 对应的值,类型:boolean|string|number,默认 false
falseValue: '0',
},
],
transport: {
read: ({ data, params, dataSet }) => {
/*
触发 read 的两种方式:
1. 通过在 dataSet 中设置自动查询参数 autoQuery: true,这样每次组件渲染的时候就会自动去获取数据。
2. 通过 dataSet 的 query() 方法手动触发 read 获取数据,即在组件中调用 this.detailDS.query()。
*/
const url = `${API_LIST}/v1/${organizationId}/headers/getHeaderList`;
const axiosConfig = {
url,
method: 'GET',
params: {
/*
params为需要传递给后端的查询数据,会通过 ' ? ' 以键值对的形式自动拼接在 url 之后;
在Table组件中,该函数的形参 params 为 组件的 page 、size 和 sortable;
也可以通过组件中使用 this.detailDS.setQueryParameter(para, value) 的方式来设置查询参数。
para - 参数名、value - 参数值。
*/
},
};
return axiosConfig;
},
destroy: ({ data }) => {
return {
url: '',
data,
method: 'DELETE',
};
},
update: ({ data, params }) => {
return {
url: ``,
data,
params,
method: 'PUT',
};
},
create: ({ data, params, deatSet }) => {
return {
url: ``,
data,
params,
method: 'POST',
};
},
submit: ({ data, params, dataSet }) => {
// submit 适用于所有增删改操作,调用一次 submit 都会触发,后端通过 _status 来标识或识别 增/删/改 操作。
// data 为 dataSet 对应的组件中的数据,需要注意的是只有当页面有改动的的时候,调用 submit / update / create / destroy 才会触发网络请求。
const axiosConfig = {
url: '',
method: 'POST',
data, // data 为需要传递给后端的数据,默认为形参中的data,即组件中的数据。
},
return axiosConfig;
},
});
}
import React, { Component, Fragment } from 'react';
import { DataSet } from 'choerodon-ui/pro';
import DetailDS from './DetailDS'
export default class Detail extends Component {
detailDS = new DataSet({
...DetailDS()
})
// 在页面上展示表格有两种方式:一种是直接在 Table 标签里面去写 Column;一种是将 Column 单独提出来。
get columns() {
return [
{ name: 'id', lock: 'left' },
{ name: 'employeeName', lock: 'left' },
{ name: 'employeeNumber', width: 150, lock: 'left' },
{ name: 'hiredate', lock: 'left' },
{ name: 'age', width: 80 },
{ name: 'enable', width: 80 },
]
}
buttons = [ 'add', 'save', 'delete' ]
render() {
/**
buttons: Table 的功能按钮。可选值:add delete remove save query resetexpandAll collapseAll
export 或 数组 或 自定义按钮,数组为可选值字符串+按钮配置属性对象。
给出的关键字在使用后可直接渲染出其功能按钮,并且其点击事件可直接触发 transport 中对应的方法。
columns:表格的列。
queryFieldsLimit:头部显示的查询字段的数量,超出限制的查询字段放入弹出窗口。
columnResizable:可调整列宽。
*/
return (
)
}
}
问题记录
choerodon 组件的问题
- remove(removeAll)和 delete(deleteAll) 的区别
delete 为立即删除,会去调用接口;remove 为临时删除操作,类似于你编辑了,但是没有保存,不保存重新查询数据就还在。
- 自定义 table 的搜索框
table 自定义搜索条
- lovPara 传一个变量
使用 dynamicProps 动态属性钩子或者对象。对象为字段属性和返回该字段值的钩子的键值对,建议使用对象以提高性能。
// 使用方法
ItemDynamicProps({ record }) {
return {
lovPara: {
organizationId: record.get('organization')
? record.get('organization').organizationId
: record.get('organizationId')
}
}
}
fields: [
{
name: 'item',
type: 'object',
label: 'Item',
lovCode: 'LOV_FND_ITEM',
dynamicProps: this.ItemDynamicProps,
ignore: 'always'
},
]
- 自定义按钮,实现表格的新增、删除、查询和保存功能
让自定义的按钮触发注入在此表格的 dataSet 的对应 Method,Method 中的这些方法都是可以通过直接 ‘点’ 出来调用的。如 新增 - create、删除 - remove / removeAll / delete / deleteAll、查询 - query 等。
// 使用方法
add() {
this.DetailDS.create();
}
delete() {
// 需要注意的是,在调用 delete 和 remove 进行记录删除的时候,应传入需要删除的数据,此处以选择删除为例。
// dataSet 的 currentSelected 为当前页的选中记录,包含当前页已选中的所有数据
const { currentSelected } = this.DetailDS;
this.DetailDS.delete(currentSelected);
}
- 在Form中使用DataSet时,配置了submitUrl,请求后无法自动处理Token
修改 .babelrc 文件,同 .babelrc
- 项目启动时报 cross-env 错误
一般是因为缺少 cross-env 模块,执行 yarn add cross-env 或 npm install cross-env 即可解决。
- dynamicProps设置lov值的时候,为什么会反反复复多次调用为dynamicProps设定的方法?
field 在获取任何属性的时候都会执行 dynamicProps,所以不要在 dynamicProps 里调用接口,dynamicProps也不支持async await。
- 如何手动获取到 dataSet 中的 data。
有两种方法: toData() 和 toJSONData()。
两种方法的区别:
- toData() 会获取到当前 dataSet 中的全部数据,即使你的 fields 中有 ignore 属性,也不会过滤掉。
- toJSONData() 获取到的是用于提交的 json 数据。需要注意的是,如果当前 dataSet 对应组件中的数据没有任何改动,则无法获取到该 dataSet 中的数据,提交只会提交变更的数据。
- 动态添加 dataSet 的 childrenDS,并把动态添加出来的 childrenDS 的数据渲染到页面中
// 在 dataSet 的 children 属性下添加
export default () => ({
...
children: {
DemandDateDS: new DataSet({ ...DemandDateDS() }),
},
}
/*
operationResponseDTOList 为后端返回给我的工序的数组,因为每个工序下面都有对应的 Task 需要操作,
所以我在这块每一个工序对应添加一个 dataSet,使用工序的 Code 作为 dataSet 的名字。
*/
operationResponseDTOList.map(item => {
// 在组件中通过 “点” 的方式进行添加
this.taskSplitDS.children[item.operationCode] = new DataSet({
...TaskSplitChildrenDS(),
data: [].concat(item),
});
})
在动态渲染 dataSet 的时候,需要注意 dataSet 的持久化,否则可能会出现调用 create 去添加数据的时候,每次都是初始化的状态。
- Table 的列添加 lock 属性之后,出现空白格。
给添加的 lock 的列设置宽度
- 筛选框有数据全部展示出来,并且可以多选
demo 预览
代码地址
- 刷新 Swagger - 解决 403
iam -> Permission_Refresh -> 先运行第二个,再运行第一个接口。服务名:light***,版本号:对应需要解决403的接口的版本号 version - x.x.x.RELEASE
- Table 组件第一列添加序号
利用变量和闭包的方式都不能实现 +1,需要利用当前 record 的 index 属性实现
{
name: 'order',
renderer: ({ record }) => record.index + 1,
},
- c7n 功能按钮的属性
自定义 save、add等组件给定的这些功能按钮的点击事件和按钮的展示内容
buttons = [
[ 'add', { children: '新增', onClick: () = >{} ],
]
- 页面中使用别名引入图片
// images 是对应的路径别名
- 缓存当前页面的 state
c7n 缓存组件
import cacheComponent from 'components/CacheComponent';
// cacheKey: 需要缓存的页面的路由
@cacheComponent({ cacheKey: '/employee-performance/index' })
React 框架的问题
- React 条件渲染组件之后,通过父子组件传参,props 拿到的不是最新的数据
setState 是异步的,它的第二参数是操作成功之后的一个回调。
// 当父组件中的 showTaskFlag 为 true 的时候,展示子组件,并调用子组件中的 getTaskData 方法。
// 如果使用下面的写法,会出现 getTaskData 为 undefined
handleShowTask(record) {
const data = record.toData();
this.setState({
showTaskFlag: true,
});
this.getTaskData(data)
}
// 正确的写法应该是将页面渲染之后,需要调用的函数写在 setState 的回调当中。
handleShowTask(record) {
const data = record.toData();
this.setState({
showTaskFlag: true,
}, () => this.getTaskData(data));
}
- React 中遍历对象并渲染的方法
const obj = { name: '张小三' };
{
Object.keys(obj).map(key => {
return (
{ obj[key] }
)
});
}