FieldControl.tsx 3.45 KB
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 />}
        />
      )
  }
}