1、一个列表页面都是查询和列表。查询的功能是比较常用的。对于一个项目来说把这块做成公共的组件更好维护,而且通过注册不同的搜索控件,避免大家重复写相同功能的代码.
支持容器和列自适应布局,支持自定义注册和字段值的收集转换和值有效验证,支持多字段名
2、移动端广告位自定义布局的组件
先看效果:
页面DEMO例子代码:
addExample("魔方", ['./common.tsx?tsx', 'antd4', 'ant-design-icons', 'ahooks'], function (common, antd, icons, ahooks) {
addLessText(`
.cube-wrapper{
position:relative;
}
.cube-tiles{
width:100%;
height:100%;
}
.cube-row{
display:flex;
}
.cube-cell{
border-top:solid 1px #d9d9d9;
border-left:solid 1px #d9d9d9;
}
.cube-row .cube-cell:last-child{
border-right:solid 1px #d9d9d9;
}
.cube-row:last-child .cube-cell{
border-bottom:solid 1px #d9d9d9;
}
.cube-cell-hover{
background:#e8f7fd
}
.cube-places{
width:100%;
height:100%;
}
.place-item{
position:absolute;
background:#e8f7fd;
border:solid 1px #d9d9d9;
box-sizing:initial;
}
.place-item-selected{
border-color:#1890ff;
z-index:3;
}
.place-item-remove{
position:absolute;
right:-5px;
top:-10px;
display:none;
color:#69c0ff;
cursor:pointer;
}
.place-item-selected .place-item-remove{
display:block;
}
.tmp-ul{
}
.tmp-item{
border: 1px solid #efefef;
padding:5px;
float:left;
width:80px;
height:80px;
display:flex;
flex-direction: column;
}
.tmp-item:nth-child(n+2){
margin-left:5px;
}
.tmp-row{
display:flex;
flex:1;
}
.tmp-row:nth-child(n+2){
margin-top:5px;
}
.tmp-col{
flex:1;
height:100%;
background-color:#44aadd;
}
.tmp-col:nth-child(n+2){
margin-left:5px;
}
`)
let { Form, Row, Col, Input, Select, Card } = antd
let { UploadImage } = common
let { CloseCircleOutlined, CloseCircleFilled } = icons
let { useControllableValue } = ahooks
let { useState, useCallback, useEffect, useMemo } = React
window.ahooks = ahooks
let Cube = (props, ref) => {
let { size, rows, cols, type = "customize" } = props; // type fixed,customize
let [currentPlaceActiveIndex, setCurrentPlaceActiveIndex] = React.useState(0)
let [tiles, setTiles] = React.useState([])
let [places, setPlaces] = useControllableValue(props, {
defaultValue: [],
defaultValuePropName: 'defaultValue',
valuePropName: 'places',
trigger: 'onChange',
})
let instance = React.useRef({
currentTileValue: 0,
startTile: null,
mounted: false,
}).current
let [tileWidth, tileHeight] = React.useMemo(() => {
return [size[0] / cols, size[1] / rows]
}, [size[0], size[1], rows, cols])
React.useLayoutEffect(() => {
let tiles = Array.from(new Array(rows), (v, r) => {
return Array.from(new Array(cols), (v, c) => {
return {
id: 'row_' + r + '_col_' + c,
row: r,
col: c,
value: 0,// 0 空白,大于0表示被占位
active: false
}
})
})
setTiles(tiles)
}, [rows, cols])
let [canHover, setCanHover] = React.useState(false)
let eachTiles = React.useCallback((callback) => {
rowLoop:
for (let r = 0, rowLength = tiles.length; r < rowLength; r++) {
colLoop:
for (let c = 0, colLength = tiles[r].length; c < colLength; c++) {
let result = callback(tiles[r][c], r, c)
if (result === false) {
break rowLoop
}
}
}
}, [tiles])
let boxBoxIntersect = React.useCallback((x1, y1, w1, h1, x2, y2, w2, h2) => {
// 性能最好的矩形相交判断
return !(x1 > x2 + w2 || x1 + w1 < x2 || y1 > y2 + h2 || y1 + h1 < y2)
}, [])
let paintTiles = React.useCallback((currentTile) => {
let startTile = instance.startTile
let minRow = Math.min(startTile.row, currentTile.row)
let maxRow = Math.max(startTile.row, currentTile.row)
let minCol = Math.min(startTile.col, currentTile.col)
let maxCol = Math.max(startTile.col, currentTile.col)
if (!places.some(place => {
let x1 = place.min[0], y1 = place.min[1], w1 = place.max[0] - place.min[0], h1 = place.max[1] - place.min[1]
let x2 = minCol, y2 = minRow, w2 = maxCol - minCol, h2 = maxRow - minRow
let result = boxBoxIntersect(x1, y1, w1, h1, x2, y2, w2, h2)
return result
})) {
eachTiles(tile => {
let x1 = tile.col, y1 = tile.row, w1 = 0, h1 = 0
let x2 = minCol, y2 = minRow, w2 = maxCol - minCol, h2 = maxRow - minRow
let result = boxBoxIntersect(x1, y1, w1, h1, x2, y2, w2, h2)
if (result) {
tile.active = true
} else {
tile.active = false
}
})
setTiles([...tiles])
}
}, [places, tiles, eachTiles])
let eachTilesRange = React.useCallback((startCol, startRow, endCol, endRow, callback) => {
let minRow = Math.min(startRow, endRow)
let maxRow = Math.max(startRow, endRow)
let minCol = Math.min(startCol, endCol)
let maxCol = Math.max(startCol, endCol)
// 检查当前区域是否存在占位
eachTiles((tile, row) => {
if (tile.row >= minRow && tile.row <= maxRow && tile.col >= minCol && tile.col <= maxCol) {
callback(tile)
}
})
}, [eachTiles])
let onMouseDown = React.useCallback((tile, e) => {
if (tile.value > 0) {
return
}
if (!instance.startTile) {
instance.startTile = tile
tile.active = true;
setTiles([...tiles])
return
}
// 结束
if (instance.startTile) {
instance.startTile = null
// instance.currentTileValue++
let minTile, maxTile;
eachTiles((tile) => {
if (tile.active && tile.value === 0) {
tile.active = false
tile.value = 1
if (!minTile) {
minTile = tile
}
if (!maxTile) {
maxTile = tile
}
if (minTile.row > tile.row || minTile.col > tile.col) {
minTile = tile
}
if (maxTile.row < tile.row || maxTile.col < tile.col) {
maxTile = tile
}
}
})
let newPlaces = [...places]
newPlaces.push({
value: instance.currentTileValue,
min: [minTile.col, minTile.row],
max: [maxTile.col, maxTile.row]
})
setPlaces(newPlaces)
setTiles([...tiles])
}
}, [tiles, places, eachTiles])
let onMouseEnter = React.useCallback((tile, e) => {
if (instance.startTile) {
paintTiles(tile)
}
}, [paintTiles])
let onSelectPalceHandle = React.useCallback((place, index) => {
setCurrentPlaceActiveIndex(index)
}, [])
let placeNodes = React.useMemo(() => {
return places.map(place => {
let min = place.min
let max = place.max
let left = min[0] * tileWidth, top = min[1] * tileHeight;
let width = (max[0] - min[0] + 1) * tileWidth, height = (max[1] - min[1] + 1) * tileHeight
return {
value: place.value,
min: min,
max: max,
style: {
left: left,
top: top,
width: width,
height: height
}
}
})
}, [places, tileWidth, tileHeight])
let renderTiles = React.useCallback(() => {
return <div className="cube-tiles">{tiles.map((row, r) => {
return <div key={r} className="cube-row">
{row.map((tile, c) => {
let cls = classNames('cube-cell', {
'cube-cell-hover': tile.active
})
return <div onMouseDown={onMouseDown.bind(null, tile)} onMouseEnter={onMouseEnter.bind(null, tile)} className={cls} key={tile.id} style={{
width: tileWidth,
height: tileHeight
}}></div>
})}
</div>
})}</div>
}, [tiles, tileWidth, tileHeight])
let renderPlaces = React.useCallback(() => {
return <div className="cube-places">{
placeNodes.map((place, index) => {
let cls = classNames('place-item', {
'place-item-selected': currentPlaceActiveIndex === index
})
return <div onClick={onSelectPalceHandle.bind(null, place, index)} key={index} className={cls} style={place.style}>
{type === 'customize' ? <span className="place-item-remove" onClick={(e) => {
e.stopPropagation()
places.splice(index, 1)
eachTilesRange(place.min[0], place.min[1], place.max[0], place.max[1], (d => {
if (d.value > 0) {
d.value = 0
}
}))
setTiles([...tiles])
setPlaces([...places])
}}><CloseCircleFilled /></span> : null}
</div>
})
}</div>
}, [placeNodes, eachTiles, currentPlaceActiveIndex, type])
React.useLayoutEffect(() => {
if (instance.mounted) {
} else {
instance.mounted = true
}
}, [rows, cols])
return <div className="cube-wrapper" style={{ width: size[0], height: size[1] }}>
{renderTiles()}
{renderPlaces()}
</div>
}
Cube = React.forwardRef(Cube)
let CubeTemplate = (props) => {
let { data, onSelect } = props
let onSelectTemplate = useCallback((item) => {
if (onSelect) {
onSelect(item)
}
}, [onSelect])
let renderRowCol = (d) => {
return Array.from(new Array(d.rows), (row, rowIndex) => {
return <div key={rowIndex} className="tmp-row">
{Array.from(new Array(d.cols), (col, colIndex) => {
return <div key={colIndex} className="tmp-col"></div>
})}
</div>
})
}
return <ul className="tmp-ul">
{data.map((d, index) => {
return <li key={index} className="tmp-item" onClick={onSelectTemplate.bind(null, d)}>
{renderRowCol(d)}
</li>
})}
</ul>
}
return function (props) {
let { children, ...restProps } = props
let [places, setPlaces] = useState([])
let [{ rows, cols }, setGridRowCol] = React.useState({
rows: 4,
cols: 4,
})
let grids = React.useMemo(() => [[4, 4], [5, 5], [6, 6]].map((d, i) => {
return {
rows: d[0],
cols: d[1],
value: d.join('x'),
}
}), [])
let tempaltes = React.useMemo(() => {
return [{
rows: 1,
cols: 2,
text: "1行2列",
places: [{
min: [0, 0],
max: [0, 0]
}, {
min: [1, 0],
max: [1, 0]
}]
}, {
rows: 1,
cols: 3,
text: "1行3列",
places: [{
min: [0, 0],
max: [0, 0]
}, {
min: [1, 0],
max: [1, 0]
}, {
min: [2, 0],
max: [2, 0]
}]
}, {
rows: 1,
cols: 4,
text: "1行4列",
places: [{
min: [0, 0],
max: [0, 0]
}, {
min: [1, 0],
max: [1, 0]
}, {
min: [2, 0],
max: [2, 0]
}, {
min: [3, 0],
max: [3, 0]
}]
}, {
rows: 2,
cols: 2,
text: "2行2列",
places: [{
min: [0, 0],
max: [0, 0]
}, {
min: [1, 0],
max: [1, 0]
}, {
min: [0, 1],
max: [0, 1]
}, {
min: [1, 1],
max: [1, 1]
}]
}]
}, [])
let onSelectTempalte = useCallback((temp) => {
setGridRowCol({
rows: temp.rows,
cols: temp.cols
})
setPlaces(temp.places)
}, [])
let onPlaceChange = useCallback((places) => {
setPlaces(places)
}, [])
return <Card>
<Form wrapperCol={{ span: 18 }} labelCol={{ span: 6 }}>
<Form.Item label="方案名称">
<Input />
</Form.Item>
<Form.Item label="选择模板">
<CubeTemplate onSelect={onSelectTempalte} data={tempaltes}></CubeTemplate>
</Form.Item>
<Form.Item label="魔方密度">
<Select onChange={(value) => {
setGridRowCol({
rows: parseInt(value.split('x')[0]),
cols: parseInt(value.split('x')[1])
})
}}>
{grids.map((d, i) => <Select.Option key={d.value} value={d.value}>{d.value}</Select.Option>)}
</Select>
</Form.Item>
<Form.Item label="布局">
<Cube places={places} onChange={onPlaceChange} size={[375, 375]} rows={rows} cols={cols}></Cube>
</Form.Item>
</Form>
</Card>
}
})
addExample("标准列表", ['antd4', './common.tsx?tsx'], function (antd, common) {
let { useRequest, useModal, ORDER, ImageView, DTable, FilterForm, useTableSelection } = common
let { Tabs, Table, Button, Card, Space, message, Modal, Descriptions, Typography, Pagination, Badge, Tooltip, Image } = antd
let { useState, useCallback, useMemo, useRef, useEffect, forwardRef } = React
let ImageGroupPreview = (props) => {
let { images, visible, onVisibleChange, privewProps } = props
return <div style={{ display: 'none' }}>
<Image.PreviewGroup preview={{
...(privewProps || {}), visible: visible, onVisibleChange: (visible, preVisible) => {
if (onVisibleChange) {
onVisibleChange(visible, preVisible)
}
}
}}>
{images.map((src, index) => <Image key={index + src} src={src} />)}
</Image.PreviewGroup>
</div>
}
let List = () => {
let [{ tableProps, dataSource, update }, { query: showList }] = useRequest({
service: () => Promise.resolve({
list: [{
id: 1,
buyerName: "鲜花",
score: 4,
scoreTaste: 4,
scorePack: 3,
scoreFlow: 3,
content: Mock.Random.cword(100),
reviewPicUrl: [Mock.Random.image('300x300', '#ff0000'), Mock.Random.image('300x300', '#00ff00'), Mock.Random.image('300x300', '#0000ff')].join(','),
status: 1,
createdTime: "2021-09-14 11:00:00"
}, {
id: 2,
buyerName: "鲜花",
score: 4,
scoreTaste: 4,
scorePack: 3,
scoreFlow: 3,
content: Mock.Random.cword(100),
reviewPicUrl: '',
status: 1,
createdTime: "2021-09-14 11:00:00"
}],
total: 0
}),
transform: (d) => {
return {
data: d.list,
total: d.total
}
}
})
let [{ rowSelection }, { clearAllSelection }] = useTableSelection({ keep: true })
let [{ visible: visibleImage, images }, setVisibleImage] = useState({ visible: false, images: [] })
let viewImages = useCallback((images) => {
setVisibleImage({ images: images, visible: true })
}, [])
let updateStatusHandle = useCallback((status, ids, isBatch = false) => {
}, [])
const fields = useMemo(() => [[{
type: "text",
name: "name_list",
label: "名称"
}, {
type: "select",
name: "name_list2",
label: "编码",
data: [{ text: "禁用", value: 1 }, { text: "启用", value: 2 }]
}], {
type: "text",
name: "name2",
label: "名工地"
}, {
type: "text",
name: "name3",
label: "名称城工地"
}, {
type: "text",
name: "name4",
label: "名称发大水城"
}, {
type: "text",
name: "name5",
label: "名称大水城"
}], [])
const columns = useMemo(() => [
{
title: '用户昵称',
dataIndex: 'buyerName',
ellipsis: {
showTitle: false,
},
render: buyerName => (
<Tooltip placement="topLeft" title={buyerName}>
{buyerName}
</Tooltip>
),
},
{
title: '评分',
width: 240,
render(record) {
return (
<>
<div>
<Space>
<span>综合评分{record.score}星</span>
<span>口味评分{record.scoreTaste}星</span>
</Space>
</div>
<div>
<Space>
<span>包装评分{record.scorePack}星</span>
<span>物流评分{record.scoreFlow}星</span>
</Space>
</div>
</>
);
},
},
{
title: '文字评价',
dataIndex: 'content',
// ellipsis:{showTitle:true}
responsive: ['md'],//小于 768 不显示
render(content) {
// return <Typography.Paragraph ellipsis={{
// rows: 3,
// expandable: true,
// }}>{content}</Typography.Paragraph>
// return <Typography.Paragraph ellipsis={{
// rows: 3,
// expandable: true,
// symbol:"展示"
// }}>{content}</Typography.Paragraph>
return <Tooltip title={content}>
<Typography.Paragraph ellipsis={{
rows: 3,
expandable: false
}}>{content}</Typography.Paragraph>
</Tooltip>
},
},
{
title: '图片评价',
dataIndex: 'reviewPicUrl',
width: 120,
render(reviewPicUrl) {
let images = reviewPicUrl.split(',').filter(Boolean)
return <Button type="link" onClick={viewImages.bind(null, images)}>查看图片({images.length})</Button>;
},
},
{
title: '状态',
width: 100,
render(record) {
let color = record.status == 1 ? 'green' : 'red';
let text = record.status == 1 ? '展示中' : '已隐藏';
return <Badge text={text} color={color}></Badge>;
},
},
{
title: '评价时间',
dataIndex: 'createdTime',
width: 180
},
{
title: '操作',
width: 80,
render: (record) => {
let status = record.status == 1 ? 0 : 1;
return (
<Space>
<Button
style={{ padding: 0 }}
type="link"
onClick={() => {
updateStatusHandle(status, [record.id]);
}}
>
{status == 0 ? '隐藏' : '显示'}
</Button>
</Space>
);
},
},
], [viewImages, updateStatusHandle])
let viewImageElement = <div style={{ display: 'none' }}>
<Image.PreviewGroup preview={{
visible: visibleImage, onVisibleChange: vis => {
if (!vis) {
setVisibleImage({ images: [], visible: vis })
}
}
}}>
{images.map((src, index) => <Image key={index} src={src} />)}
</Image.PreviewGroup>
</div>
return <Card>
<FilterForm fields={fields} autoBind onQuery={showList} ></FilterForm>
<Table {...tableProps} rowKey="id" columns={columns} rowSelection={rowSelection}></Table>
<ImageGroupPreview images={images} visible={visibleImage} onVisibleChange={(vis) => {
if (!vis) {
setVisibleImage({ images: [], visible: vis })
}
}}></ImageGroupPreview>
</Card>
}
return function (props) {
let { children, ...restProps } = props
return <List></List>
}
})
组件代码:
type FilterFormFieldOptions = {
[key: string]: any
type?: String
name: string | string[], // name 是fieldName的便捷写法
label: string
key?: string // 唯一key,不设默认为name或index
props: any
fields?:Array<Pick<FilterFormFieldOptions,'fields'>>
initialValue?: any
itemProps?: any
layoutProps?:any
noLabel?: boolean // 是否显示babel
noName?: boolean // 是否绑定字段名
render?: (field: FilterFormFieldOptions) => any,// 返回输入渲染控件
valid: (value) => boolean // 值是否有效,无效的话,不会附加到查询结果
}
interface FilterFormProps {
fields: Array<FilterFormFieldOptions>
onQuery: (queryParams: any) => void,
autoBind?: boolean
containerProps?: any
fieldLayoutProps?: any
searchLayoutProps?:any
filterComponents?: any
}
let FilterFormField = (props) => {
let { field, noLabel = false,children } = props
children = field.render?field.render(field, noLabel):children
let formItemProps = {
...(field.itemProps||{})
}
noLabel = field.noLabel || noLabel
if (noLabel !== true) {
formItemProps.label = field.label
}
if (field.noName !== true) {
formItemProps.name = field.name
}
if (field.noName !== true && field.initialValue !== void 0) {
formItemProps.initialValue = field.initialValue
}
return <Form.Item {...formItemProps}>
{children}
</Form.Item>
}
let FilterFormArrayField = (props) => {
let { fields } = props
let [activeField, setActiveField] = useState(() => fields[0])
const menu = (
<Menu>
{fields.map(field => <Menu.Item key={field.key} onClick={() => {
setActiveField(field)
}}>{field.label}</Menu.Item>)}
</Menu>
);
return <Form.Item label={<Dropdown overlay={menu} trigger="click"><div>{activeField.label}<DownOutlined /></div></Dropdown>}>
<FilterFormField field={activeField} noLabel={true}></FilterFormField>
</Form.Item>
}
const FilterFormContext = React.createContext()
export let FilterForm: FC<FilterFormProps> = forwardRef((props, ref) => {
let { fields, onQuery, autoBind = false, filterComponents,searchLayoutProps, fieldLayoutProps, containerProps, ...formProps } = props
let [firstBreakpoint,activeBreakpoints]=useActiveBreakpoint()
let [form]=Form.useForm()
//normalization Normalize standard
let normalizeFields = useCallback((fields, callback) => {
return fields.map((field, index) => {
if(Array.isArray(field)){
field={
fields:field
}
}
let baseField = filterComponents[field.type] || {}
let newField = _.assignWith({
key: field.key || (Array.isArray(field.name) ? field.name.join('_$_') : field.name) || index,
props: {},
//itemProps: {},
//layoutProps:{},
noLabel: false,
noName: false
}, baseField, field, (objValue, srcValue, key, obj, source) => {
if (_.isPlainObject(srcValue)) {
return _.assign(objValue, srcValue)
}
})
if (newField.fields&&Array.isArray(newField.fields)) {
newField.fields=normalizeFields(newField.fields, callback)
newField.key=newField.fields.map(d=>d.key).join('_')
newField.isMultField=true;
}else{
callback&&callback(newField)
}
return newField
})
}, [filterComponents])
let [mergeFields, fieldMap] = useMemo(() => {
let fieldMap = new Map()
let newFields = normalizeFields(fields, (field) => {
if (Array.isArray(field.name)) {
field.name.forEach((cname) => {
fieldMap.set(cname, field)
})
} else {
fieldMap.set(field.name, field)
}
})
return [newFields, fieldMap]
}, [fields, normalizeFields])
let getFieldColSpan=useCallback((field)=>{
let layoutProps=field.layoutProps||fieldLayoutProps
let span=layoutProps.span
let breakpoint=activeBreakpoints.find(breakpoint=>layoutProps[breakpoint])
if(breakpoint){
span=layoutProps[breakpoint].span
}
return span
},[activeBreakpoints,fieldLayoutProps])
let renderField = (field, index) => {
let layoutProps=field.layoutProps||fieldLayoutProps
return <Col {...layoutProps} key={field.key}>
{ field.isMultField ? <FilterFormArrayField fields={field.fields} ></FilterFormArrayField> : <FilterFormField field={field}></FilterFormField>}
</Col>
}
let onFinishHandle = useCallback((values) => {
let fieldKeys = Object.keys(values)
let queryParams = {}
fieldKeys.forEach((fieldName) => {
let value = values[fieldName]
let field = fieldMap.get(fieldName)
if (!field) {
return
}
let isValidValue = true
if(field.valid){
isValidValue=field.valid(value, fieldName, field)
}else{
isValidValue=value!==undefined
}
// 如果值有效,就设置
if (isValidValue) {
queryParams[fieldName] = value
}
})
if (onQuery) {
console.log('queryParams',queryParams)
onQuery(queryParams)
}
}, [onQuery, fieldMap])
let onQueryHandle=useCallback(()=>{
form.validateFields().then(onFinishHandle).catch(()=>{
})
},[form,onFinishHandle])
let onResetHandle=useCallback(()=>{
form.resetFields()
},[form])
mergeFields=useMemo(()=>{
let menu=(<Menu>
<Menu.Item key="reset" onClick={onResetHandle}>重置</Menu.Item>
</Menu>)
let layoutProps=searchLayoutProps||fieldLayoutProps
let searchField={
type:"search",
key:"__search__",
noName:true,
label:<div></div>,
itemProps:{
colon:false,
},
layoutProps:layoutProps,
props:{
overlay:menu,
trigger:"click",
type:"primary",
onClick:onQueryHandle,
children:'搜索'
},
render(field){
return <Dropdown.Button {...field.props}></Dropdown.Button>
}
}
let allFieldTotalSpan=mergeFields.reduce((a,b)=>a+getFieldColSpan(b),0)
let currentSpan=getFieldColSpan(searchField)
let p=allFieldTotalSpan%24
if(p>0&¤tSpan+p<=24){
searchField.label=null
searchField.props.style={
marginLeft:20
}
}
return mergeFields.concat(normalizeFields([searchField]))
},[getFieldColSpan,searchLayoutProps,fieldLayoutProps,onQueryHandle,onResetHandle])
useEffect(()=>{
if(autoBind){
onQueryHandle()
}
},[])
return <Row>
<Col {...containerProps}>
<Form component={false} colon={false} {...formProps} form={form} onFinish={onFinishHandle}>
<Row>
{mergeFields.map(renderField)}
</Row>
</Form>
</Col>
</Row>
})
FilterForm.fieldComponents = {}
FilterForm.registerField = function (type: string, config: FilterFormFieldOptions) {
FilterForm.fieldComponents[type] = {
...config
}
}
FilterForm.defaultProps = {
filterComponents: FilterForm.fieldComponents,
wrapperCol: {
flex: 1
},
labelCol: {
flex: '0 1 120px'
},
containerProps: {
span: 24
},
fieldLayoutProps: {
span: 8
}
}
FilterForm.registerField('text', {
render(field) {
return <Input {...field.props}></Input>
}
})
FilterForm.registerField('select', {
render(field) {
let children = field.data ? field.data.map(d => <Select.Option key={d.value} value={d.value}>{d.text}</Select.Option>) : null
return <Select {...field.props}>{children}</Select>
}
})
|