@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,9 +1,13 @@
1
- import React, { useCallback, useContext } from 'react';
2
- import { Input, InputNumber, Switch, ConfigProvider } from 'antd';
3
- import styled from 'styled-components';
4
- import { FieldRendererProps } from './types';
1
+ import React, { useCallback, useContext, useMemo } from 'react';
2
+ import { ConfigProvider, Input, InputNumber, Switch } from 'antd';
3
+ import { FieldRendererProps, CustomParamSchema } from './types';
5
4
  import { ArrayField } from './ArrayField';
6
5
  import { ObjectField } from './ObjectField';
6
+ import { EnumField } from './EnumField';
7
+ import styled from 'styled-components';
8
+ import TimeField from './TimeField';
9
+ import FileField from './FileField';
10
+
7
11
 
8
12
  // ==================== Styled Components ====================
9
13
 
@@ -45,6 +49,42 @@ const FieldError = styled.div`
45
49
  margin-top: 4px;
46
50
  `;
47
51
 
52
+ // 数组字段容器(用于多选枚举数组)
53
+ const ArrayFieldContainer = styled.div`
54
+ border: 1px solid #dbdbdb;
55
+ border-radius: 10px;
56
+ padding: 24px 16px;
57
+ position: relative;
58
+ margin-top: 20px;
59
+ margin-bottom: 20px;
60
+ background-color: #fff;
61
+ `;
62
+
63
+ // 数组标题
64
+ const ArrayTitle = styled.div`
65
+ position: absolute;
66
+ top: -12px;
67
+ left: 15px;
68
+ background: #fff;
69
+ padding: 2px 10px;
70
+ white-space: nowrap;
71
+ text-overflow: ellipsis;
72
+ overflow: hidden;
73
+ word-break: break-all;
74
+ max-width: calc(100% - 30px);
75
+ font-size: 14px;
76
+ font-weight: 500;
77
+ color: #1f2329;
78
+ `;
79
+
80
+ // 数组描述
81
+ const ArrayDescription = styled.div`
82
+ font-size: 12px;
83
+ color: #8f959e;
84
+ margin-bottom: 12px;
85
+ word-break: break-word;
86
+ `;
87
+
48
88
  /**
49
89
  * InputWrapper 组件
50
90
  * 支持动态 prefixCls,自动继承用户项目的 ConfigProvider 配置
@@ -61,6 +101,15 @@ const InputWrapper = styled.div<{ $prefixCls: string }>`
61
101
  }
62
102
  `;
63
103
 
104
+ /**
105
+ * 判断是否有枚举值
106
+ * 优先从 AssociationPropertyMetadata 中读取,兼容旧的 EnumValues 字段
107
+ */
108
+ const hasEnumValues = (schema: CustomParamSchema): boolean => {
109
+ const enumValues = schema.AssociationPropertyMetadata?.EnumValues || schema.EnumValues;
110
+ return Array.isArray(enumValues) && enumValues.length > 0;
111
+ };
112
+
64
113
  /**
65
114
  * 单个字段渲染器
66
115
  * 根据字段类型渲染对应的输入组件
@@ -75,6 +124,9 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
75
124
  level = 0,
76
125
  errors = {},
77
126
  fieldPath = '',
127
+ uploadSender,
128
+ fileUploader,
129
+ hideLabel = false,
78
130
  }) => {
79
131
  const { Type, Title, Description } = schema;
80
132
  const displayTitle = Title || name;
@@ -97,8 +149,25 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
97
149
  [onChange]
98
150
  );
99
151
 
152
+ // 判断是否为枚举类型
153
+ const isEnum = useMemo(() => hasEnumValues(schema), [schema]);
154
+
100
155
  // 渲染基础类型输入框
101
156
  const renderInput = () => {
157
+ // 优先处理枚举类型
158
+ if (isEnum) {
159
+ return (
160
+ <EnumField
161
+ name={name}
162
+ schema={schema}
163
+ value={value}
164
+ onChange={handleChange}
165
+ required={required}
166
+ disabled={disabled}
167
+ />
168
+ );
169
+ }
170
+
102
171
  switch (Type) {
103
172
  case 'string':
104
173
  return (
@@ -107,6 +176,7 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
107
176
  value={value}
108
177
  onChange={(e) => handleChange(e.target.value)}
109
178
  disabled={disabled}
179
+ placeholder={`请输入${displayTitle}`}
110
180
  />
111
181
  </InputWrapper>
112
182
  );
@@ -119,6 +189,7 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
119
189
  onChange={handleChange}
120
190
  disabled={disabled}
121
191
  style={{ width: '100%' }}
192
+ placeholder={`请输入${displayTitle}`}
122
193
  />
123
194
  </InputWrapper>
124
195
  );
@@ -126,14 +197,75 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
126
197
  case 'boolean':
127
198
  return (
128
199
  <Switch
129
- style={{borderRadius: 16}}
200
+ style={{ borderRadius: 16 }}
130
201
  checked={value}
131
202
  onChange={handleChange}
132
203
  disabled={disabled}
133
204
  />
134
205
  );
135
206
 
136
- case 'array':
207
+ case 'time':
208
+ return (
209
+ <TimeField
210
+ name={name}
211
+ schema={schema}
212
+ value={value}
213
+ onChange={handleChange}
214
+ required={required}
215
+ disabled={disabled}
216
+ />
217
+ );
218
+
219
+ case 'file':
220
+ return (
221
+ <FileField
222
+ name={name}
223
+ schema={schema}
224
+ value={value}
225
+ onChange={handleChange}
226
+ required={required}
227
+ disabled={disabled}
228
+ uploadSender={uploadSender}
229
+ fileUploader={fileUploader}
230
+ />
231
+ );
232
+
233
+ case 'array': {
234
+ // 检查是否为多选枚举类型的数组(multi-select 或 checkbox)
235
+ // 这种情况下,数组本身就是多选的结果,不需要再套一层数组逻辑
236
+ const itemsSchema = schema?.Items;
237
+ const itemsEnumValues = itemsSchema?.AssociationPropertyMetadata?.EnumValues;
238
+ const itemsEnumDisplayStyle = itemsSchema?.AssociationPropertyMetadata?.EnumDisplayStyle;
239
+ const isMultiSelectEnumArray = Array.isArray(itemsEnumValues) && itemsEnumValues.length > 0 &&
240
+ (itemsEnumDisplayStyle === 'multi-select' || itemsEnumDisplayStyle === 'checkbox');
241
+
242
+ if (isMultiSelectEnumArray && itemsSchema) {
243
+ // 使用数组样式外框包裹 EnumField,保持数组的视觉一致性
244
+ // 但不渲染增加/删除按钮,因为多选组件本身已经支持多选
245
+ return (
246
+ <ArrayFieldContainer>
247
+ <ArrayTitle>
248
+ {required && <Required>*</Required>}
249
+ <span>{displayTitle}</span>
250
+ </ArrayTitle>
251
+ {Description && (
252
+ <ArrayDescription>{Description}</ArrayDescription>
253
+ )}
254
+ <EnumField
255
+ name={name}
256
+ schema={{
257
+ ...itemsSchema,
258
+ Title: schema.Title || itemsSchema.Title,
259
+ }}
260
+ value={value}
261
+ onChange={handleChange}
262
+ required={required}
263
+ disabled={disabled}
264
+ />
265
+ </ArrayFieldContainer>
266
+ );
267
+ }
268
+
137
269
  return (
138
270
  <ArrayField
139
271
  name={name}
@@ -145,8 +277,11 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
145
277
  level={level}
146
278
  errors={errors}
147
279
  fieldPath={fieldPath}
280
+ uploadSender={uploadSender}
281
+ fileUploader={fileUploader}
148
282
  />
149
283
  );
284
+ }
150
285
 
151
286
  case 'object':
152
287
  return (
@@ -160,6 +295,8 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
160
295
  level={level}
161
296
  errors={errors}
162
297
  fieldPath={fieldPath}
298
+ uploadSender={uploadSender}
299
+ fileUploader={fileUploader}
163
300
  />
164
301
  );
165
302
 
@@ -170,17 +307,24 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
170
307
  value={value}
171
308
  onChange={(e) => handleChange(e.target.value)}
172
309
  disabled={disabled}
310
+ placeholder={`请输入${displayTitle}`}
173
311
  />
174
312
  </InputWrapper>
175
313
  );
176
314
  }
177
315
  };
178
316
 
179
- // 对于 object 和 array 类型,直接渲染,不需要外层包装
317
+ // 对于 object 和 array 类型(包括多选枚举数组),直接渲染,不需要外层包装
318
+ // 因为它们内部已经有自己的样式容器
180
319
  if (Type === 'object' || Type === 'array') {
181
320
  return renderInput();
182
321
  }
183
322
 
323
+ // 如果隐藏标签,直接返回输入组件(用于数组项渲染)
324
+ if (hideLabel) {
325
+ return renderInput();
326
+ }
327
+
184
328
  // 基础类型渲染带标签的表单项
185
329
  return (
186
330
  <FieldItem>
@@ -199,4 +343,4 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
199
343
  );
200
344
  };
201
345
 
202
- export default FieldRenderer;
346
+ export default FieldRenderer;