这个UI使用比较少,只提供一个参照。记录使用中遇到的部分常用场景,以及ds的使用。确实不咋好用.......
文档链接地址:
一、DS使用
1、ds基础使用
2、ds中刷新下拉接口
javascript
formDataDs.getField('demo')?.options?.query();
// 修改下拉接口的值?
ds.getField('demo')?.options?.setQueryParameter('custCode', custCode);
ds.getField('demo')?.options?.setQueryParameter('sybCode', belongIndustry);
3、ds中添加正则校验 validator
javascript
{
name: 'cashAndBill',
type: FieldType.string,
label: languageConfig('register.label.cashAndBill', '现金余额(含票据)'),
placeholder: languageConfig(
'register.placeholder.cashAndBill',
'请输入现金余额(含票据)',
),
validator: value => {
if (!isvalidRegexNumber.test(value)) {
return languageConfig(
'tips.regexHintNumber',
'小数点前最多8位,小数点后最多两位',
);
}
return true;
},
computedProps: {
required() {
return pageType ? false : true;
},
},
},
4、ds中必填校验 required
javascript
{
name: 'overseasChannel',
type: FieldType.string,
label: languageConfig('register.label.overseasChannel', '是否海外渠道'),
lookupCode: 'LTC_YES_OR_NO',
},
{
name: 'currency',
type: FieldType.string,
label: languageConfig('register.label.currency', '主要交易币种'),
placeholder: languageConfig('register.placeholder.select', '请选择'),
lookupCode: 'PRM-CR-CURRENCY',
computedProps: {
required({ record }) {
return record.get('overseasChannel') === 'Y';
},
},
},
5、页面直接获取ds中 options下拉接口
javascript
const getFetch = async () => {
await formDataDs.getField('demo')?.options?.query();
let options = formDataDs.getField('demo')?.options?.toData();
};
javascript
const options = formDs.current?.getField('otherBrandCode')?.options?.toData();
6、ds中根据接口获取下拉 options
javascript
{
name: 'authRegion',
type: FieldType.string,
label: languageConfig('register.label.authRegion', '授权区域'),
placeholder: languageConfig('register.placeholder.select', '请选择'),
textField: 'name',
valueField: 'code',
options: cityOptionsDs,
required: true,
},
/**
- 获取城市列表
*/
export const cityOptionsDs = new DataSet({
autoQuery: true,
idField: 'code',
fields: [
{ name: 'code', type: FieldType.string },
{ name: 'name', type: FieldType.string },
],
paging: false,
transport: {
read: () => {
return {
...getCountryListApi(),
};
},
},
});
7、ds中options的获取下拉出现勾选时一闪一闪
(1)一闪一闪效果图

(2)代码实现
javascript
/** 授权产品 */
const authProductCache = new Map();
const getAuthProductOptions = (divisionCode: string, provinceName: string) => {
const cacheKey = `${divisionCode}-${provinceName}`;
if (authProductCache.has(cacheKey)) {
return authProductCache.get(cacheKey);
}
const ds = categoryOptionDs(divisionCode, provinceName);
authProductCache.set(cacheKey, ds);
return ds;
};
// ds
{
name: 'applyAuthProductId',
type: FieldType.string,
label: languageConfig(
'register.general.label.applyAuthProductId',
'拟申请授权产品',
),
placeholder: PLACEHOLDER_PLEASE_SELECT(),
textField: 'authCategoryName',
valueField: 'id',
options: new DataSet({}),
// computedProps: {
// options: ({ record }) => {
// const { divisionCode, province, city } = record.toData();
// const provinceName = province ? province : city;
// if (divisionCode) {
// return categoryOptionDs(divisionCode, provinceName);
// }
// return new DataSet({});
// },
// required() {
// return pageType ? false : true;
// },
// },
dynamicProps: {
options: ({ record }) => {
const { divisionCode, province, city } = record.toData();
const provinceName = province || city;
if (divisionCode) {
return getAuthProductOptions(divisionCode, provinceName);
}
return new DataSet({});
},
required: () => !pageType,
},
},
(3)dynamicProps 和 computedProps 的区别
dynamicProps
触发时机 :
在字段的 每次渲染时 动态计算属性(类似 React 的每次 render 都会执行)。
特点:
适合需要 实时响应 变化的场景(如表单交互、依赖字段变化)。
每次组件渲染时都会重新计算,保证数据最新。
性能开销稍高(因频繁执行)。
典型场景:
动态加载选项(如下拉框的
options
依赖其他字段值)。表单字段的联动(如显示/隐藏、禁用状态)。
javascript
dynamicProps: {
options: ({ record }) => {
const value = record.get('dependencyField');
return value ? fetchOptions(value) : new DataSet({});
},
disabled: ({ record }) => record.get('status') === 'disabled',
}
computedProps
触发时机 :
仅在 依赖的字段值发生变化时 计算(类似 Vue 的计算属性)。
特点:
适合 依赖项明确 且计算较重的场景。
只有依赖的字段变化时才会触发,性能更优。
不支持响应式更新(除非依赖的字段变化)。
典型场景:
计算派生数据(如汇总、格式化)。
条件性校验规则(如
required
依赖其他字段)。
javascript
computedProps: {
options: ({ record }) => {
// 仅在 divisionCode 或 province 变化时重新计算
const divisionCode = record.get('divisionCode');
const province = record.get('province');
return divisionCode ? fetchOptions(divisionCode, province) : new DataSet({});
},
required: ({ record }) => record.get('type') === 'VIP',
}
场景 | 推荐使用 |
---|---|
需要实时响应变化(如联动下拉框) | dynamicProps |
依赖字段变化时才需更新(如计算汇总值) | computedProps |
高频更新的属性(如禁用状态) | dynamicProps |
性能敏感且依赖项明确 | computedProps |
8、ds中动态配置'值集'
javascript
{
name: 'authProduct',
type: FieldType.string,
label: languageConfig('register.label.authProduct', '授权产品'),
placeholder: languageConfig('register.placeholder.select', '请选择'),
// PRM_MD_OVERSEAS_AUTHPRODUCT_DEVELOPED: 海外事业部(发达)值集:通用、机器人、工程机械
// PRM_MD_OVERSEAS_AUTHPRODUCT:海外事业部(新兴)值集:通用、机器人、电梯
dynamicProps: {
lookupCode: ({ record }) => {
const divisionCode = record.get('divisionCode');
// 根据'事业部'显示对应:值集下拉
return divisionCode === 'D000070'
? 'PRM_MD_OVERSEAS_AUTHPRODUCT_DEVELOPED'
: 'PRM_MD_OVERSEAS_AUTHPRODUCT';
},
},
required: true,
},
9、ds中详情的时候select中数据用tag标签显示
(1)效果图
(2)html代码
javascript
import React, { useEffect, useState } from 'react';
import formatterCollections from 'hzero-front/lib/utils/intl/formatterCollections';
import {
commonModelPrompt,
prmMdTemCode,
} from '@/language/language';
import '@/assets/styles/c7n.less';
import { Form, Output } from 'choerodon-ui/pro';
import { toJS } from 'mobx';
import { labelWidth } from '@/utils/utils';
import { LabelLayout } from 'choerodon-ui/pro/lib/form/enum';
import ItemGroup from 'choerodon-ui/pro/lib/form/ItemGroup';
const BaseInfo = (props: any) => {
const { formDataDs } = props;
const [show, setShow] = useState<boolean>(true); // 管理收缩状态
const [productTags, setProductTags] = useState<React.ReactNode>(null);
useEffect(() => {
const fetchProducts = async () => {
const record = formDataDs.current;
if (!record) return;
const applyAuthProductId = toJS(record.get('applyAuthProductId'));
const divisionCode = record.get('divisionCode');
if (applyAuthProductId && divisionCode) {
// 1、获取当前计算后的 options DataSet
const optionsDs = record.getField('applyAuthProductId')?.options;
if (optionsDs) {
// 2、执行查询并等待完成
await optionsDs.query({ divisionCode });
// 3、获取数据
const options = optionsDs.toData() || [];
console.log('最新选项:', options);
// 4、解析数据,匹配出数据,显示标签,未匹配出就显示输入框
const result = applyAuthProductId.reduce((acc, id) => {
const item = options.find(o => o.id === id);
return item ? [...acc, item.authCategoryName] : acc;
}, []);
// 5、有数据显示内容,无数据显示输入框
setProductTags(
result.length > 0 ? (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '6px' }}>
{result.map(item => (
<span
key={item}
style={{
background: '#E0F7FE',
borderRadius: 4,
padding: '0 8px',
lineHeight: '20px',
}}
>
{item}
</span>
))}
</div>
) : (
<Output name="applyAuthProductId" />
),
);
return;
}
}
setProductTags(<Output name="applyAuthProductId" />);
};
fetchProducts();
}, [formDataDs]);
return (
<>
<Form
dataSet={formDataDs}
columns={4}
labelWidth={labelWidth}
labelLayout={LabelLayout.vertical}
>
<Output name="divisionName" disabled />
<ItemGroup name="applyAuthProductId">
<div>{productTags}</div>
</ItemGroup>
</Form>
</>
);
};
export default formatterCollections({
code: [prmMdTemCode, commonModelPrompt],
})(BaseInfo);
(3)store代码
javascript
export const generalFormList = (pageType?: string) => {
return [
{
name: 'applyAuthProductId',
type: FieldType.string,
label: languageConfig(
'register.label.applyAuthProductId',
'拟申请授权产品',
),
placeholder: languageConfig('register.placeholder.select', '请选择'),
textField: 'authCategoryName',
valueField: 'id',
options: new DataSet({}),
computedProps: {
options: ({ record }) => {
if (record.get('divisionCode')) {
return categoryOptionDs(record.get('divisionCode'));
}
return new DataSet({});
},
required() {
return pageType ? false : true;
},
},
},
];
};
/** 产品授权类别(生效) */
const categoryOptionDs = (divisionCode: string) => {
return new DataSet({
autoQuery: true,
paging: false,
idField: 'code',
childrenField: 'directories',
fields: [
{ name: 'id', type: FieldType.string },
{ name: 'authCategoryName', type: FieldType.string },
],
transport: {
read: (config: AxiosRequestConfig): AxiosRequestConfig => {
return {
...getCategorySelectListApi({ divisionCode }),
...config,
};
},
},
});
};
10、动态监听ds
不用ds自带的监听,可用下面方法
javascript
const [processCategory, setProcessCategory] = useState('');
useEffect(() => {
const handler = () => {
const newValue = formDataDs.current?.get('processCategory');
console.log('newValue', newValue);
if (newValue !== processCategory) {
setProcessCategory(newValue);
}
};
formDataDs.addEventListener('update', handler);
return () => formDataDs.removeEventListener('update', handler);
}, [formDataDs, processCategory]);
二、table ds
1、table 添加查询 loading
(1)代码
javascript
/** 查询列表 */
const querySearchList = async (data: {
channelName: string;
status: string;
}) => {
if (tableDs.status === 'loading') return;
try {
const queryParameters = {
channelName: data.channelName,
status: data.status,
};
Object.entries(queryParameters).forEach(([key, value]): void => {
tableDs.setQueryParameter(key, value);
});
await tableDs.query();
} catch (error) {
message.error(API_RESPONSE_FAILED, 1.5, 'top');
return false;
} finally {
console.log(COMMON_PROCESS_COMPLETE);
}
};
const onChange = async ({ record }) => {
querySearchList(record.toData());
};
/** 查询 ds */
const queryListDs = useMemo(() => new DataSet(queryList(onChange)), []);
const queryConfig = [
{ name: 'channelName', dom: TextField, clearButton: true },
{ name: 'status', dom: Select, clearButton: true },
];
<QueryBar
onShowMore={onShowMore}
onQuery={querySearchList}
queryBarDs={queryListDs}
queryConfig={queryConfig}
/>
2、select 查询条件,模糊查询调用接口
(1)效果图

(2)代码
javascript
// 查询input
{
name: 'channelName',
dom: Select,
clearButton: true,
searchable: true,
searchMatcher: 'channelName',
},
ds代码
export const queryList = (onChange): DataSetProps => {
return {
autoCreate: true,
fields: [
{
name: 'channelName',
type: FieldType.string,
placeholder: languageConfig('auth.channelName', '渠道名称'),
textField: 'channelName',
valueField: 'channelCode',
options: new DataSet({
autoQuery: true,
// paging: false,
queryFields: [
{
name: 'channelName',
type: FieldType.string,
label: '渠道名称',
},
],
transport: {
read: (config: AxiosRequestConfig): AxiosRequestConfig => {
const { params } = config;
return {
...config,
url: ${prefixPath}/common/plat/interface/getByChannelName,
method: 'GET',
params: {
page: params.page,
size: params.size,
channelName: params.channelName || '', // 获取用户输入的值
},
};
},
},
}),
},
],
events: {
update: onChange,
},
};
};
3、table 中行编辑时,设置编辑的输入框为inputNumber
(1)效果图

(2)代码
javascript
const columns = useMemo(() => {
return [
{
name: 'currentEmployeeCount',
// editor: type === 'view' ? false : true,
editor:
type === 'view' ? (
false
) : (
<NumberField precision={0} min={0} step={1} defaultValue={0} />
),
},
{
name: 'employeeOverThreeYears',
editor:
type === 'view' ? (
false
) : (
<NumberField precision={0} min={0} step={1} defaultValue={0} />
),
},
{
name: 'salesPersonCount',
editor:
type === 'view' ? (
false
) : (
<NumberField precision={0} min={0} step={1} defaultValue={0} />
),
},
{
name: 'technicalPersonCount',
editor:
type === 'view' ? (
false
) : (
<NumberField precision={0} min={0} step={1} defaultValue={0} />
),
},
{
name: 'certifiedEngineerCount',
editor:
type === 'view' ? (
false
) : (
<NumberField precision={0} min={0} step={1} defaultValue={0} />
),
},
];
}, [type]);
4、table的头部,文字过多换行显示
注:关键修改点: headerRowHeight={'auto'}

(1)原先效果图

(2)效果图

(3)代码
javascript
const tableDs = useMemo(
() =>
new DataSet({
...tableList(),
events: {
load: () => {
// console.log('tableDs.toData()', tableDs.toData());
handleUploadTable();
},
},
}),
[],
);
const columns = useMemo(() => {
return [
{ name: 'brandName', width: '10%', align: 'center' },
{ name: 'productCategory', width: '10%', align: 'center' },
{
name: 'salesRevenue',
width: '17%',
align: 'center',
header: () => (
<div style={{ whiteSpace: 'normal', lineHeight: '16px' }}>
{languageConfig(
'register.general.business.label.salesRevenue',
'品牌产品(贸易)销售营业额(万元)',
)}
</div>
),
},
{
name: 'integratedRevenue',
width: '17%',
align: 'center',
header: () => (
<div style={{ whiteSpace: 'normal', lineHeight: '16px' }}>
{languageConfig(
'register.general.business.label.integratedRevenue',
'品牌产品系统集成营业额(万元)',
)}
</div>
),
},
{
name: 'totalRevenue',
width: '17%',
align: 'center',
header: () => (
<div style={{ whiteSpace: 'normal', lineHeight: '16px' }}>
{languageConfig(
'register.general.business.label.totalRevenue',
'品牌总营业额(万元)',
)}
</div>
),
},
{
name: 'brandRevenueRatio',
width: '17%',
align: 'center',
header: () => (
<div style={{ whiteSpace: 'normal', lineHeight: '16px' }}>
{languageConfig(
'register.general.business.label.brandRevenueRatio',
'该品牌占公司总营业额比例',
)}
</div>
),
renderer: ({ value }) => {
return <div>{value}%</div>;
},
},
{
header: TABLE_COLUMN_OPERATION,
lock: ColumnLock.right,
command: ({ record }) => {
return [
<Button
key="edit"
funcType={FuncType.link}
onClick={() => {
setVisible(true);
setRow(record.toData() || {});
}}
>
{COMMON_EDIT}
</Button>,
<Popconfirm
key="delete"
title={languageConfig(
'tips.confirmDeleteNowDate',
'确认是否删除当前数据',
)}
okText={STATUS_YES}
cancelText={STATUS_NO}
onConfirm={async () => {
await handleDelete(record);
}}
>
<Button funcType={FuncType.link} color={ButtonColor.red}>
{COMMON_DELETE}
</Button>
</Popconfirm>,
];
},
},
];
}, []);
<Table
dataSet={tableDs}
columns={columns}
selectionMode={SelectionMode.click}
pagination={false}
headerRowHeight={'auto'}
renderEmpty={() => {
return <div>{NO_DATA}</div>;
}}
footer={() => {
return tableDs.toData().length > 0 ? (
<>
{ <Table
dataSet={tableDs}
columns={columns}
selectionMode={SelectionMode.click}
buttons={tableButtons}
pagination={false}
headerRowHeight={'auto'}
renderEmpty={() => {
return <div>{NO_DATA}</div>;
}}
footer={() => {
return tableDs.toData().length > 0 ? (
<>
{renderFooterTotal(
totalSalesRevenue,
totalIntegratedRevenue,
totalRevenue,
)}
</>
) : (
<></>
);
}}
style={{ marginBottom: '16px' }}
/>(
totalSalesRevenue,
totalIntegratedRevenue,
totalRevenue,
)}
</>
) : (
<></>
);
}}
style={{ marginBottom: '16px' }}
/>
/** store.ts */
export const tableList = () => {
return {
autoQuery: true,
fields: [
{
name: 'brandName',
type: FieldType.string,
label: languageConfig(
'register.general.business.label.brandName',
'经营品牌',
),
},
{
name: 'brandCode',
type: FieldType.string,
},
{
name: 'productCategory',
type: FieldType.string,
label: languageConfig(
'register.general.business.label.productCategory',
'主要产品类别',
),
},
{
name: 'salesRevenue',
type: FieldType.string,
label: languageConfig(
'register.general.business.label.salesRevenue',
'品牌产品(贸易)销售营业额(万元)',
),
},
{
name: 'integratedRevenue',
type: FieldType.string,
label: languageConfig(
'register.general.business.label.integratedRevenue',
'品牌产品系统集成营业额(万元)',
),
},
{
name: 'totalRevenue',
type: FieldType.string,
label: languageConfig(
'register.general.business.label.totalRevenue',
'品牌总营业额(万元)',
),
},
{
name: 'brandRevenueRatio',
type: FieldType.string,
label: languageConfig(
'register.general.business.label.brandRevenueRatio',
'该品牌占公司总营业额比例',
),
},
],
};
};
/** hook */
import React from 'react';
import styles from '@/pages/register/detail/main.less';
import { languageConfig } from '@/language/language';
/** 表格:footer 合计 */
export const renderFooterTotal = (
totalSalesRevenue,
totalIntegratedRevenue,
totalRevenue,
) => {
return (
<>
<div className={styles.registerDetail_content_business}>
<div className={styles.registerDetail_content_business_title}>
{languageConfig('register.general.business.label.total', '合计')}:
</div>
{/* 品牌产品(贸易)销售营业额(万元) */}
<div className={styles.registerDetail_content_business_item}>
{totalSalesRevenue}
</div>
{/* 品牌产品系统集成营业额(万元) */}
<div className={styles.registerDetail_content_business_item}>
{totalIntegratedRevenue}
</div>
{/* 品牌总营业额(万元) */}
<div className={styles.registerDetail_content_business_item}>
{totalRevenue}
</div>
<div className={styles.registerDetail_content_business_item}></div>
</div>
</>
);
};
/** styles */
&_business {
width: 100%;
background: #f5faff;
display: flex;
line-height: 36px;
&_title {
// flex: 1 1 auto;
width: 20%;
text-align: right;
color: #788295;
padding-right: 40px;
}
&_item {
width: 17%;
color: #222222;
font-weight: 700;
text-align: center;
}
}
5、table勾选,添加禁用条件
(1)table添加属性
javascript
<Table
dataSet={tableDs}
columns={columns}
selectedHighLightRow
autoHeight={{ type: TableAutoHeightType.maxHeight, diff: 30 }}
rowHeight={36}
buttons={tableButtons}
// selectionMode={SelectionMode.click} // 这个是隐藏勾选功能
/>
import { FieldType } from 'choerodon-ui/dataset/data-set/enum';
import { languageConfig } from '@/language/language';
import { DataSetProps } from 'choerodon-ui/dataset/data-set/DataSet';
/** ds table */
export const tableList = () => {
return {
autoQuery: true,
fields: [
{
name: 'registerFlowNo',
type: FieldType.string,
label: languageConfig('register.label.registerFlowNo', '申请编号'),
},
{
name: 'status',
type: FieldType.string,
label: languageConfig('register.label.status', '状态'),
lookupCode: 'PRM_MD_STATUS',
},
],
transport: {
read: config => {
const params = {
page: config.params.page,
size: config.params.size,
...config?.data,
};
return {
...getRegisterList(),
};
},
},
// 禁用
record: {
dynamicProps: {
selectable: record => {
return !record.get('remark')
},
},
},
};
};