@alicloud/appflow-chat 0.0.1-beta.2 → 0.0.1-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,21 @@
1
1
  import { default as default_2 } from 'react';
2
2
  import { Provider } from 'react';
3
3
 
4
+ /**
5
+ * AssociationPropertyMetadata 类型定义
6
+ * 包含枚举、时间、文件等扩展属性的元数据
7
+ */
8
+ declare interface AssociationPropertyMetadata {
9
+ /** 子类型(用于 time 和 file 类型),现在是数组 */
10
+ SubType?: (TimeSubType | FileSubType)[];
11
+ /** 枚举值 */
12
+ EnumValues?: (string | number | boolean)[];
13
+ /** 枚举展示样式 */
14
+ EnumDisplayStyle?: EnumDisplayStyle;
15
+ }
16
+
17
+ declare type BasicParamType = 'string' | 'number' | 'boolean';
18
+
4
19
  /**
5
20
  * BubbleContent - 消息气泡核心展示组件
6
21
  *
@@ -213,6 +228,8 @@ export declare interface ChatStreamCallbacks {
213
228
  onError?: (error: Error) => void;
214
229
  }
215
230
 
231
+ declare type ComplexParamType = 'array' | 'object';
232
+
216
233
  /**
217
234
  * 将后端返回的小写 schema 转换为组件需要的大写格式
218
235
  * @param schema 后端返回的 schema(小写字段名)
@@ -230,6 +247,18 @@ export declare interface CustomParamSchema {
230
247
  Required?: string[];
231
248
  Properties?: Record<string, CustomParamSchema>;
232
249
  Items?: CustomParamSchema;
250
+ order?: number;
251
+ Order?: string | number;
252
+ AssociationPropertyMetadata?: AssociationPropertyMetadata;
253
+ AssociationProperty?: string;
254
+ /** @deprecated 使用 AssociationPropertyMetadata.SubType 代替 */
255
+ TimeSubType?: TimeSubType;
256
+ /** @deprecated 使用 AssociationPropertyMetadata.SubType 代替 */
257
+ FileSubType?: FileSubType;
258
+ /** @deprecated 使用 AssociationPropertyMetadata.EnumValues 代替 */
259
+ EnumValues?: string[];
260
+ /** @deprecated 使用 AssociationPropertyMetadata.EnumDisplayStyle 代替 */
261
+ EnumDisplayStyle?: EnumDisplayStyle;
233
262
  }
234
263
 
235
264
  /**
@@ -244,6 +273,9 @@ export declare interface CustomParamSchema {
244
273
  * name: { Type: 'string', Title: '名称', Description: '请输入名称' },
245
274
  * age: { Type: 'number', Title: '年龄' },
246
275
  * enabled: { Type: 'boolean', Title: '是否启用' },
276
+ * date: { Type: 'time', Title: '日期', TimeSubType: 'year-month-day' },
277
+ * file: { Type: 'file', Title: '文件', FileSubType: 'image' },
278
+ * status: { Type: 'string', Title: '状态', EnumValues: ['active', 'inactive'], EnumDisplayStyle: 'radio' },
247
279
  * tags: {
248
280
  * Type: 'array',
249
281
  * Title: '标签',
@@ -273,7 +305,7 @@ export declare const CustomParamsRenderer: default_2.FC<CustomParamsRendererProp
273
305
  /**
274
306
  * CustomParamsRenderer 组件的 Props
275
307
  */
276
- export declare interface CustomParamsRendererProps {
308
+ export declare interface CustomParamsRendererProps extends UploadConfig {
277
309
  /** CustomParams 的 schema */
278
310
  schema: CustomParamSchema;
279
311
  /** 表单值 */
@@ -323,18 +355,21 @@ export declare interface DocReferencesProps {
323
355
  style?: default_2.CSSProperties;
324
356
  }
325
357
 
358
+ declare type EnumDisplayStyle = 'select' | 'checkbox' | 'radio' | 'multi-select';
359
+
360
+ declare type ExtendedParamType = 'time' | 'file';
361
+
362
+ declare type FileSubType = 'default' | 'jpg' | 'png' | 'svg' | 'doc' | 'ppt' | 'excel' | 'txt' | 'markdown' | 'zip';
363
+
364
+ /**
365
+ * 文件上传方法类型
366
+ * 用于实际执行文件上传到 OSS
367
+ */
368
+ declare type FileUploader = (file: Blob, uploadUrl: string) => Promise<void>;
369
+
326
370
  /**
327
371
  * HistoryCard 历史卡片组件 (SDK 版本)
328
372
  * 用于展示历史对话中的 card 类型消息(只读模式)
329
- *
330
- * @example
331
- * ```tsx
332
- * <HistoryCard
333
- * approvalStatus="approved"
334
- * formValues={{ name: 'test', age: 18 }}
335
- * formSchema={schema}
336
- * />
337
- * ```
338
373
  */
339
374
  export declare const HistoryCard: default_2.FC<HistoryCardProps>;
340
375
 
@@ -346,12 +381,8 @@ export declare interface HistoryCardData {
346
381
  }
347
382
 
348
383
  export declare interface HistoryCardProps {
349
- /** 审批状态 */
350
- approvalStatus?: string;
351
- /** 表单值 */
352
- formValues?: Record<string, any>;
353
- /** 表单 Schema */
354
- formSchema?: CustomParamSchema;
384
+ /** 消息数据对象 */
385
+ data: any;
355
386
  }
356
387
 
357
388
  export declare interface HistoryMessage {
@@ -371,30 +402,6 @@ export declare interface HistoryMessage {
371
402
  /**
372
403
  * HumanVerify 人工审核组件 (SDK 版本)
373
404
  * 用于展示需要人工审核的表单,并处理提交逻辑
374
- *
375
- * @example
376
- * ```tsx
377
- * <HumanVerify
378
- * verifyId="xxx"
379
- * sessionWebhook="https://..."
380
- * approved={false}
381
- * customParamsSchema={schema}
382
- * customParamsKey="customParams"
383
- * onSubmit={(data) => {
384
- * bot.postMessage({
385
- * msgType: 'cardCallBack',
386
- * data: {
387
- * sessionWebhook: data.sessionWebhook,
388
- * content: JSON.stringify({
389
- * verifyId: data.verifyId,
390
- * status: data.status,
391
- * [data.customParamsKey]: data.customParamsValue,
392
- * }),
393
- * },
394
- * });
395
- * }}
396
- * />
397
- * ```
398
405
  */
399
406
  export declare const HumanVerify: default_2.FC<HumanVerifyProps>;
400
407
 
@@ -408,16 +415,12 @@ export declare interface HumanVerifyData {
408
415
  }
409
416
 
410
417
  export declare interface HumanVerifyProps {
411
- /** 验证ID */
412
- verifyId?: string;
413
- /** 会话 Webhook */
414
- sessionWebhook?: string;
415
- /** 是否已提交 */
416
- approved?: boolean;
417
- /** CustomParams 的 schema */
418
- customParamsSchema?: CustomParamSchema;
419
- /** CustomParams 的 key(用于提交时的字段名) */
420
- customParamsKey?: string;
418
+ /** 消息数据对象 */
419
+ data: any;
420
+ /** 上传发送方法 */
421
+ uploadSender?: UploadSender;
422
+ /** 文件上传方法 */
423
+ fileUploader?: FileUploader;
421
424
  /** 提交回调函数 */
422
425
  onSubmit?: (data: {
423
426
  verifyId: string;
@@ -443,6 +446,8 @@ export declare interface HumanVerifySubmitData {
443
446
  */
444
447
  export declare const loadEchartsScript: () => Promise<void>;
445
448
 
449
+ export declare const loadMermaidScript: () => Promise<void>;
450
+
446
451
  /**
447
452
  * MarkdownRenderer - Markdown渲染组件
448
453
  *
@@ -558,7 +563,7 @@ export declare interface ModelInfo {
558
563
  };
559
564
  }
560
565
 
561
- declare type ParamType = 'string' | 'number' | 'boolean' | 'array' | 'object';
566
+ declare type ParamType = BasicParamType | ComplexParamType | ExtendedParamType;
562
567
 
563
568
  /**
564
569
  * RichBubbleContent - 富文本消息气泡核心展示组件
@@ -737,6 +742,36 @@ export declare interface SourceItem {
737
742
  index_id?: string;
738
743
  }
739
744
 
745
+ declare type TimeSubType = 'year-month' | 'year-month-day' | 'datetime';
746
+
747
+ /**
748
+ * 上传配置
749
+ */
750
+ declare interface UploadConfig {
751
+ /** 上传发送方法 */
752
+ uploadSender?: UploadSender;
753
+ /** 文件上传方法 */
754
+ fileUploader?: FileUploader;
755
+ }
756
+
757
+ /**
758
+ * 上传请求参数
759
+ */
760
+ declare interface UploadRequestParams {
761
+ /** 事件类型:uploadToken-获取上传凭证,uploadFile-获取文件ID */
762
+ eventType: 'uploadToken' | 'uploadFile';
763
+ /** 文件名 */
764
+ fileName: string;
765
+ /** 文件下载 URL(uploadFile 时需要) */
766
+ content?: string;
767
+ }
768
+
769
+ /**
770
+ * 上传发送方法类型
771
+ * 用于发送上传相关的事件请求
772
+ */
773
+ declare type UploadSender = (params: UploadRequestParams) => Promise<string | null>;
774
+
740
775
  /**
741
776
  * 使用 CustomParamsRenderer 的 Hook
742
777
  * 提供表单值管理和校验功能
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alicloud/appflow-chat",
3
- "version": "0.0.1-beta.2",
3
+ "version": "0.0.1-beta.4",
4
4
  "description": "Appflow-Chat AI聊天机器人组件库,提供聊天服务和UI组件",
5
5
  "type": "module",
6
6
  "main": "./dist/appflow-chat.cjs.js",
@@ -1,12 +1,18 @@
1
- import React, { useCallback } from 'react';
1
+ import React, { useCallback, useMemo, useRef } from 'react';
2
2
  import { Button, Input, InputNumber, Switch, Space } from 'antd';
3
3
  import { PlusOutlined, MinusOutlined } from '@ant-design/icons';
4
+ import { ArrayFieldProps, CustomParamSchema, FileSubType, sortPropertiesByOrder } from './types';
4
5
  import styled from 'styled-components';
5
- import { ArrayFieldProps, CustomParamSchema } from './types';
6
+
7
+ // 图片类型列表
8
+ const IMAGE_TYPES: FileSubType[] = ['jpg', 'png', 'svg'];
6
9
 
7
10
  // 前向声明,避免循环依赖
8
11
  const FieldRenderer = React.lazy(() => import('./FieldRenderer'));
9
12
 
13
+ // 全局计数器,用于生成稳定的数组项 key
14
+ let globalItemCounter = 0;
15
+
10
16
  // ==================== Styled Components ====================
11
17
 
12
18
  const ArrayFieldContainer = styled.div`
@@ -104,6 +110,37 @@ const ArrayActions = styled.div`
104
110
  gap: 8px;
105
111
  `;
106
112
 
113
+ // 复杂类型变体
114
+ type ComplexItemVariant = 'fileImage' | 'fileDoc' | 'time' | 'enum';
115
+
116
+ // 复杂类型数组项行
117
+ const ArrayItemRowComplex = styled.div`
118
+ display: flex;
119
+ align-items: flex-start;
120
+ gap: 8px;
121
+ margin-bottom: 8px;
122
+
123
+ &:last-child {
124
+ margin-bottom: 0;
125
+ }
126
+ `;
127
+
128
+ // 复杂类型的 Actions 容器,根据变体调整 padding-top
129
+ const ArrayItemActionsComplex = styled.div<{ $variant?: ComplexItemVariant }>`
130
+ flex-shrink: 0;
131
+ padding-top: ${({ $variant }) => {
132
+ switch ($variant) {
133
+ case 'fileDoc':
134
+ case 'time':
135
+ case 'enum':
136
+ return '4px';
137
+ case 'fileImage':
138
+ default:
139
+ return '38px';
140
+ }
141
+ }};
142
+ `;
143
+
107
144
  /**
108
145
  * 获取数组项的默认值
109
146
  */
@@ -149,6 +186,8 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
149
186
  level = 0,
150
187
  errors = {},
151
188
  fieldPath = '',
189
+ uploadSender,
190
+ fileUploader,
152
191
  }) => {
153
192
  const { Title, Description, Items } = schema;
154
193
  const displayTitle = Title || name;
@@ -156,11 +195,39 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
156
195
  // 计算当前数组的完整路径
157
196
  const currentPath = fieldPath ? `${fieldPath}.${name}` : name;
158
197
 
198
+ // 使用 ref 来存储每个数组项的唯一 key,避免在数据中添加额外字段
199
+ const itemKeysRef = useRef<Map<number, string>>(new Map());
200
+
201
+ // 获取或创建数组项的唯一 key
202
+ const getItemKey = useCallback((index: number, item: any): string => {
203
+ // 如果 item 有 url 或 uid,优先使用(已上传的文件)
204
+ if (item?.url) return item.url;
205
+ if (item?.uid) return item.uid;
206
+ if (item?.__arrayItemId) return item.__arrayItemId;
207
+
208
+ // 否则使用 ref 中存储的 key
209
+ if (!itemKeysRef.current.has(index)) {
210
+ itemKeysRef.current.set(index, `item-${++globalItemCounter}`);
211
+ }
212
+ return itemKeysRef.current.get(index)!;
213
+ }, []);
214
+
159
215
  // 添加数组项
160
216
  const handleAdd = useCallback(() => {
161
217
  if (!Items) return;
162
218
  const newItem = getDefaultValue(Items);
163
- onChange?.([...value, newItem]);
219
+ const newIndex = value.length;
220
+
221
+ // 为新项预先生成 key
222
+ itemKeysRef.current.set(newIndex, `item-${++globalItemCounter}`);
223
+
224
+ // 对于 object 类型,添加 __arrayItemId;对于其他类型,保持原值
225
+ if (typeof newItem === 'object' && newItem !== null) {
226
+ onChange?.([...value, { ...newItem, __arrayItemId: itemKeysRef.current.get(newIndex) }]);
227
+ } else {
228
+ // 对于 file、time 等类型,保持 undefined,不要包装成对象
229
+ onChange?.([...value, newItem]);
230
+ }
164
231
  }, [Items, value, onChange]);
165
232
 
166
233
  // 删除数组项
@@ -196,9 +263,35 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
196
263
  [value, onChange]
197
264
  );
198
265
 
266
+ // 判断数组项是否有枚举值(从 AssociationPropertyMetadata 或旧字段读取)
267
+ const hasItemEnumValues = (Items?.AssociationPropertyMetadata?.EnumValues?.length ?? 0) > 0 ||
268
+ (Array.isArray(Items?.EnumValues) && Items.EnumValues.length > 0);
269
+
199
270
  // 判断数组项类型(必须在 early return 之前)
200
271
  const isObjectItems = Items?.Type === 'object' && Items?.Properties;
201
272
  const isArrayItems = Items?.Type === 'array';
273
+ const isFileItems = Items?.Type === 'file';
274
+ const isTimeItems = Items?.Type === 'time';
275
+ // 需要使用 FieldRenderer 渲染的复杂类型
276
+ const needsFieldRenderer = isObjectItems || isArrayItems || isFileItems || isTimeItems || hasItemEnumValues;
277
+
278
+ // 判断文件类型是否为图片类型
279
+ const isImageFileType = useMemo((): boolean => {
280
+ if (!isFileItems) return false;
281
+
282
+ // 优先从 AssociationPropertyMetadata.SubType 读取
283
+ const subTypes = Items?.AssociationPropertyMetadata?.SubType;
284
+ if (Array.isArray(subTypes) && subTypes.length > 0) {
285
+ return subTypes.some(subType => IMAGE_TYPES.includes(subType as FileSubType));
286
+ }
287
+
288
+ // 兼容旧的 FileSubType 字段
289
+ if (Items?.FileSubType) {
290
+ return IMAGE_TYPES.includes(Items.FileSubType);
291
+ }
292
+
293
+ return false;
294
+ }, [isFileItems, Items]);
202
295
 
203
296
  if (!Items) {
204
297
  return null;
@@ -271,7 +364,10 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
271
364
  const renderObjectItemContent = (item: any, index: number) => {
272
365
  if (!Items.Properties) return null;
273
366
 
274
- return Object.entries(Items.Properties).map(([propertyName, propertySchema]) => {
367
+ // 使用排序后的属性列表进行渲染
368
+ const sortedProperties = sortPropertiesByOrder(Items.Properties);
369
+
370
+ return sortedProperties.map(([propertyName, propertySchema]) => {
275
371
  const isRequired = itemRequired.includes(propertyName);
276
372
  return (
277
373
  <React.Suspense key={propertyName} fallback={<div>加载中...</div>}>
@@ -285,6 +381,8 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
285
381
  level={level + 1}
286
382
  errors={errors}
287
383
  fieldPath={`${currentPath}[${index}]`}
384
+ uploadSender={uploadSender}
385
+ fileUploader={fileUploader}
288
386
  />
289
387
  </React.Suspense>
290
388
  );
@@ -304,6 +402,29 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
304
402
  level={level + 1}
305
403
  errors={errors}
306
404
  fieldPath={currentPath}
405
+ uploadSender={uploadSender}
406
+ fileUploader={fileUploader}
407
+ />
408
+ </React.Suspense>
409
+ );
410
+ };
411
+
412
+ // 渲染复杂类型的数组项(file、time、带枚举的类型)
413
+ const renderComplexItemContent = (item: any, index: number) => {
414
+ return (
415
+ <React.Suspense fallback={<div>加载中...</div>}>
416
+ <FieldRenderer
417
+ name={`[${index}]`}
418
+ schema={Items}
419
+ value={item}
420
+ onChange={(newValue) => handleItemChange(index, newValue)}
421
+ disabled={disabled}
422
+ level={level + 1}
423
+ errors={errors}
424
+ fieldPath={currentPath}
425
+ hideLabel={true}
426
+ uploadSender={uploadSender}
427
+ fileUploader={fileUploader}
307
428
  />
308
429
  </React.Suspense>
309
430
  );
@@ -322,6 +443,9 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
322
443
 
323
444
  <ArrayItems>
324
445
  {value.map((item, index) => {
446
+ // 获取数组项的唯一key
447
+ const itemKey = getItemKey(index, item);
448
+
325
449
  // 渲染数组项内容
326
450
  const renderItemContent = () => {
327
451
  if (isObjectItems) {
@@ -340,6 +464,37 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
340
464
  </ArrayItemContent>
341
465
  );
342
466
  }
467
+ if (isFileItems || isTimeItems || hasItemEnumValues) {
468
+ // 复杂类型(file、time、带枚举):使用 FieldRenderer 渲染
469
+ // 确定变体类型
470
+ let variant: ComplexItemVariant | undefined;
471
+ if (isFileItems) {
472
+ // 区分图片类型和非图片类型的文件上传
473
+ variant = isImageFileType ? 'fileImage' : 'fileDoc';
474
+ } else if (isTimeItems) {
475
+ // 时间类型
476
+ variant = 'time';
477
+ } else if (hasItemEnumValues) {
478
+ variant = 'enum';
479
+ }
480
+ return (
481
+ <ArrayItemRowComplex>
482
+ <ArrayItemInput>
483
+ {renderComplexItemContent(item, index)}
484
+ </ArrayItemInput>
485
+ <ArrayItemActionsComplex $variant={variant}>
486
+ <Button
487
+ type="text"
488
+ danger
489
+ icon={<MinusOutlined />}
490
+ onClick={() => handleRemove(index)}
491
+ disabled={disabled}
492
+ size="small"
493
+ />
494
+ </ArrayItemActionsComplex>
495
+ </ArrayItemRowComplex>
496
+ );
497
+ }
343
498
  // 基础类型:渲染输入框和删除按钮
344
499
  return (
345
500
  <ArrayItemRow>
@@ -361,7 +516,7 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
361
516
  };
362
517
 
363
518
  return (
364
- <ArrayItem key={index}>
519
+ <ArrayItem key={itemKey}>
365
520
  {renderItemContent()}
366
521
  </ArrayItem>
367
522
  );
@@ -376,7 +531,7 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
376
531
  shape="circle"
377
532
  icon={<PlusOutlined />}
378
533
  />
379
- {(isObjectItems || isArrayItems) && value.length > 0 && (
534
+ {needsFieldRenderer && value.length > 0 && (
380
535
  <Button
381
536
  onClick={() => handleRemove(value.length - 1)}
382
537
  shape="circle"
@@ -391,4 +546,4 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
391
546
  );
392
547
  };
393
548
 
394
- export default ArrayField;
549
+ export default ArrayField;
@@ -0,0 +1,199 @@
1
+ import React, { useCallback, useMemo } from 'react';
2
+ import { Select, Checkbox, Radio } from 'antd';
3
+ import type { RadioChangeEvent } from 'antd';
4
+ import { EnumFieldProps, EnumDisplayStyle } from './types';
5
+ import styled from 'styled-components';
6
+
7
+ const { Option } = Select;
8
+
9
+ // ==================== Styled Components ====================
10
+
11
+ // 枚举选择器容器
12
+ const EnumSelect = styled.div`
13
+ width: 100%;
14
+
15
+ .ant-select {
16
+ width: 100%;
17
+ }
18
+ `;
19
+
20
+ // 枚举复选框容器
21
+ const EnumCheckbox = styled.div`
22
+ width: 100%;
23
+
24
+ .ant-checkbox-group {
25
+ display: flex;
26
+ flex-wrap: wrap;
27
+ gap: 8px;
28
+ }
29
+
30
+ .ant-checkbox-wrapper {
31
+ margin-right: 0;
32
+
33
+ &:hover .ant-checkbox-inner {
34
+ border-color: #1890ff;
35
+ }
36
+ }
37
+ `;
38
+
39
+ // 枚举单选按钮容器
40
+ const EnumRadio = styled.div`
41
+ width: 100%;
42
+
43
+ .ant-radio-group {
44
+ display: flex;
45
+ flex-wrap: wrap;
46
+ gap: 8px;
47
+ }
48
+
49
+ .ant-radio-wrapper {
50
+ margin-right: 0;
51
+
52
+ &:hover .ant-radio-inner {
53
+ border-color: #1890ff;
54
+ }
55
+ }
56
+ `;
57
+
58
+ /**
59
+ * 枚举字段组件
60
+ * 根据 EnumDisplayStyle 渲染不同的选择器
61
+ * - select: 下拉选择框
62
+ * - multi-select: 多选下拉框
63
+ * - checkbox: 复选框组(多选)
64
+ * - radio: 单选按钮组
65
+ */
66
+ export const EnumField: React.FC<EnumFieldProps> = ({
67
+ schema,
68
+ value,
69
+ onChange,
70
+ disabled = false,
71
+ }) => {
72
+ const { Type } = schema;
73
+
74
+ // 优先从 AssociationPropertyMetadata 中读取,兼容旧的字段
75
+ const enumValues = useMemo(() => {
76
+ return schema.AssociationPropertyMetadata?.EnumValues || schema.EnumValues || [];
77
+ }, [schema]);
78
+
79
+ const displayStyle: EnumDisplayStyle = useMemo(() => {
80
+ return schema.AssociationPropertyMetadata?.EnumDisplayStyle || schema.EnumDisplayStyle || 'select';
81
+ }, [schema]);
82
+
83
+ // 处理 Select 变化
84
+ const handleSelectChange = useCallback(
85
+ (newValue: string | string[]) => {
86
+ onChange?.(newValue);
87
+ },
88
+ [onChange]
89
+ );
90
+
91
+ // 处理 Checkbox 变化
92
+ const handleCheckboxChange = useCallback(
93
+ (checkedValues: (string | number | boolean)[]) => {
94
+ onChange?.(checkedValues as string[]);
95
+ },
96
+ [onChange]
97
+ );
98
+
99
+ // 处理 Radio 变化
100
+ const handleRadioChange = useCallback(
101
+ (e: RadioChangeEvent) => {
102
+ onChange?.(e.target.value);
103
+ },
104
+ [onChange]
105
+ );
106
+
107
+ // 根据展示样式渲染不同的组件
108
+ const renderByDisplayStyle = () => {
109
+ switch (displayStyle) {
110
+ case 'checkbox':
111
+ // 复选框组 - 多选
112
+ return (
113
+ <EnumCheckbox>
114
+ <Checkbox.Group
115
+ value={Array.isArray(value) ? value : value ? [value] : []}
116
+ onChange={handleCheckboxChange}
117
+ disabled={disabled}
118
+ >
119
+ {enumValues.map((item) => (
120
+ <Checkbox key={String(item)} value={item}>
121
+ {String(item)}
122
+ </Checkbox>
123
+ ))}
124
+ </Checkbox.Group>
125
+ </EnumCheckbox>
126
+ );
127
+
128
+ case 'radio':
129
+ // 单选按钮组
130
+ return (
131
+ <EnumRadio>
132
+ <Radio.Group
133
+ value={value}
134
+ onChange={handleRadioChange}
135
+ disabled={disabled}
136
+ >
137
+ {enumValues.map((item) => (
138
+ <Radio key={String(item)} value={item}>
139
+ {String(item)}
140
+ </Radio>
141
+ ))}
142
+ </Radio.Group>
143
+ </EnumRadio>
144
+ );
145
+
146
+ case 'multi-select':
147
+ // 多选下拉框
148
+ return (
149
+ <EnumSelect>
150
+ <Select
151
+ value={Array.isArray(value) ? value : value ? [value] : []}
152
+ onChange={handleSelectChange}
153
+ disabled={disabled}
154
+ mode="multiple"
155
+ placeholder={`请选择`}
156
+ style={{ width: '100%' }}
157
+ allowClear
158
+ >
159
+ {enumValues.map((item) => (
160
+ <Option key={String(item)} value={item}>
161
+ {String(item)}
162
+ </Option>
163
+ ))}
164
+ </Select>
165
+ </EnumSelect>
166
+ );
167
+
168
+ case 'select':
169
+ default: {
170
+ // 下拉选择框
171
+ // 根据原始类型决定是否支持多选
172
+ const isMultiple = Type === 'array';
173
+ return (
174
+ <EnumSelect>
175
+ <Select
176
+ value={value}
177
+ onChange={handleSelectChange}
178
+ disabled={disabled}
179
+ mode={isMultiple ? 'multiple' : undefined}
180
+ placeholder={`请选择`}
181
+ style={{ width: '100%' }}
182
+ allowClear
183
+ >
184
+ {enumValues.map((item) => (
185
+ <Option key={String(item)} value={item}>
186
+ {String(item)}
187
+ </Option>
188
+ ))}
189
+ </Select>
190
+ </EnumSelect>
191
+ );
192
+ }
193
+ }
194
+ };
195
+
196
+ return renderByDisplayStyle();
197
+ };
198
+
199
+ export default EnumField;