// ============================================================ // upload-widgets — 单文件上传控件 // 合并自 ImageInput.tsx + VideoInput.tsx // // 两者共享 ~90% 的 enable_switch + Upload 模式, // 差异仅在于:accept 类型、beforeUpload 校验、图标、默认 maxSize。 // 提取 useEnableSwitch() + SingleUpload 去重。 // ============================================================ import { useState, useCallback } from 'react'; import { Upload, Switch, Space, Typography } from 'antd'; import { UploadOutlined, VideoCameraOutlined } from '@ant-design/icons'; import type { UploadFile } from 'antd/es/upload'; import type { WidgetProps } from './types'; import { useFileUpload } from '@/shared/hooks/use-api'; import { buildAccept, buildVideoAccept, createMediaBeforeUpload, createVideoBeforeUpload, } from '@/shared/utils/media-upload'; const { Text } = Typography; // ════ enable_switch 共享 Hook ════ const ENABLED_EMPTY: UploadFile[] = []; function useEnableSwitch( hasEnableSwitch: boolean, disabled: boolean, onChange: WidgetProps['onChange'], ) { const [switchOn, setSwitchOn] = useState(!hasEnableSwitch); const isUploadDisabled = disabled || !switchOn; const handleSwitchChange = useCallback( (on: boolean) => { setSwitchOn(on); if (!on) { onChange?.(undefined); } else { onChange?.(ENABLED_EMPTY as unknown as UploadFile[]); } }, [onChange], ); return { switchOn, isUploadDisabled, handleSwitchChange }; } // ════ SingleUpload 内部组件 ════ interface SingleUploadProps { value: unknown; onChange: WidgetProps['onChange']; disabled: boolean; config: Record; /** 'image' | 'video' */ mediaType: 'image' | 'video'; icon: React.ReactNode; uploadLabel: string; defaultMaxSizeMb: number; } function SingleUpload({ value, onChange, disabled, config, mediaType, icon, uploadLabel, defaultMaxSizeMb, }: SingleUploadProps) { const fileList = (value as UploadFile[]) || []; const maxSizeMb = (config.maxFileSizeMb as number) || defaultMaxSizeMb; const filters = config.fileFilter as string[] | undefined; const hasEnableSwitch = (config.enableSwitch as boolean) || false; const { customRequest } = useFileUpload(); const { switchOn, isUploadDisabled, handleSwitchChange } = useEnableSwitch(hasEnableSwitch, disabled, onChange); const accept = mediaType === 'video' ? buildVideoAccept(filters) : buildAccept(filters); const beforeUpload = mediaType === 'video' ? createVideoBeforeUpload(maxSizeMb) : createMediaBeforeUpload(maxSizeMb, filters); return ( {hasEnableSwitch && ( {switchOn ? '已启用' : '点击开关启用上传'} )} { if (switchOn || !hasEnableSwitch) onChange?.(newList as unknown as UploadFile[]); }} disabled={isUploadDisabled} beforeUpload={beforeUpload} > {fileList.length < 1 && (
{icon}
{uploadLabel}
)}
); } // ════ 公开导出(保持原组件名兼容 WIDGET_MAP) ════ export function ImageInput(props: WidgetProps) { const { value, onChange, disabled, config } = props; return ( } uploadLabel="上传" defaultMaxSizeMb={10} /> ); } export function VideoInput(props: WidgetProps) { const { value, onChange, disabled, config } = props; return ( } uploadLabel="上传视频" defaultMaxSizeMb={50} /> ); }