FieldControl.tsx
3.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { Input, InputNumber, DatePicker, Switch, Select, Tooltip } from 'antd'
import { LockOutlined } from '@ant-design/icons'
import dayjs from 'dayjs'
import type { DragComponent, OptionItem } from '../types'
/**
* 组件注册表:把 M8 的 compType 原子组件映射到 Ant Design 控件。
* 这是"前端通用渲染引擎"的最底层——新增/替换控件只需在此登记,业务页面零改动。
*/
export function FieldControl({
comp,
value,
onChange,
optionsMap,
}: {
comp: DragComponent
value: any
onChange: (v: any) => void
optionsMap: Record<string, OptionItem[]>
}) {
const fb = comp.fieldBind || ({} as any)
const placeholder = fb.placeholder || `请输入${fb.formLabel || ''}`
// M1 类型兜底:当 M8 用通用文本控件、但领域类型是数值/布尔/时间时,据 M1 fieldType 升级控件。
// 于是"改 M1 的 type" 也会改变渲染出的控件(兑现 Help 步骤 3)。
const ft = fb.fieldType
const generic = comp.compType === 'input' || comp.compType === 'text_area'
if (generic && (ft === 'int' || ft === 'decimal')) {
return <InputNumber style={{ width: '100%' }} value={value} onChange={onChange} placeholder={placeholder} />
}
if (generic && ft === 'boolean') return <Switch checked={!!value} onChange={onChange} />
if (generic && ft === 'datetime') {
return (
<DatePicker style={{ width: '100%' }} showTime value={value ? dayjs(value) : null}
onChange={(d) => onChange(d ? d.format('YYYY-MM-DDTHH:mm:ss') : null)} />
)
}
switch (comp.compType) {
case 'number_input':
return <InputNumber style={{ width: '100%' }} value={value} onChange={onChange} placeholder={placeholder} />
case 'date_picker':
return (
<DatePicker
style={{ width: '100%' }}
showTime
value={value ? dayjs(value) : null}
onChange={(d) => onChange(d ? d.format('YYYY-MM-DDTHH:mm:ss') : null)}
/>
)
case 'switch':
return <Switch checked={!!value} onChange={onChange} />
case 'select': {
if (fb.optionsSource) {
const opts = optionsMap[fb.optionsSource.aggregateId] || []
return (
<Select
style={{ width: '100%' }}
showSearch
optionFilterProp="label"
placeholder={`请选择${fb.formLabel || ''}`}
value={value ?? undefined}
onChange={onChange}
options={opts.map((o) => ({ value: o.value, label: o.label }))}
/>
)
}
// 绑定到普通字符串字段的 select 在 M1 中无枚举来源,通用引擎降级为可输入选择
return (
<Select
style={{ width: '100%' }}
mode="tags"
maxCount={1}
placeholder={`请输入${fb.formLabel || ''}`}
value={value ? [value] : []}
onChange={(arr: string[]) => onChange(arr[arr.length - 1] ?? null)}
/>
)
}
case 'text_area':
return <Input.TextArea value={value} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} />
case 'input':
default:
return (
<Input
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
suffix={fb.masked ? (
<Tooltip title="M5 敏感字段:存储加密、展示脱敏">
<LockOutlined style={{ color: '#faad14' }} />
</Tooltip>
) : <span />}
/>
)
}
}