@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.
@@ -6,12 +6,16 @@ import {
6
6
  CustomParamSchema,
7
7
  ValidationResult,
8
8
  validateCustomParams,
9
+ sortPropertiesByOrder,
9
10
  } from './types';
10
11
 
11
12
  export * from './types';
12
13
  export { FieldRenderer } from './FieldRenderer';
13
14
  export { ArrayField } from './ArrayField';
14
15
  export { ObjectField } from './ObjectField';
16
+ export { TimeField } from './TimeField';
17
+ export { FileField } from './FileField';
18
+ export { EnumField } from './EnumField';
15
19
 
16
20
  // ==================== Styled Components ====================
17
21
 
@@ -42,6 +46,9 @@ const EmptyState = styled.div`
42
46
  * name: { Type: 'string', Title: '名称', Description: '请输入名称' },
43
47
  * age: { Type: 'number', Title: '年龄' },
44
48
  * enabled: { Type: 'boolean', Title: '是否启用' },
49
+ * date: { Type: 'time', Title: '日期', TimeSubType: 'year-month-day' },
50
+ * file: { Type: 'file', Title: '文件', FileSubType: 'image' },
51
+ * status: { Type: 'string', Title: '状态', EnumValues: ['active', 'inactive'], EnumDisplayStyle: 'radio' },
45
52
  * tags: {
46
53
  * Type: 'array',
47
54
  * Title: '标签',
@@ -72,6 +79,8 @@ export const CustomParamsRenderer: React.FC<CustomParamsRendererProps> = ({
72
79
  onChange,
73
80
  disabled = false,
74
81
  errors = {},
82
+ uploadSender,
83
+ fileUploader,
75
84
  }) => {
76
85
  const Properties = schema?.Properties;
77
86
  const Required = schema?.Required || [];
@@ -96,9 +105,12 @@ export const CustomParamsRenderer: React.FC<CustomParamsRendererProps> = ({
96
105
  );
97
106
  }
98
107
 
108
+ // 使用排序后的属性列表进行渲染
109
+ const sortedProperties = sortPropertiesByOrder(Properties);
110
+
99
111
  return (
100
112
  <CustomParamsRendererContainer>
101
- {Object.entries(Properties).map(([propertyName, propertySchema]) => {
113
+ {sortedProperties?.map(([propertyName, propertySchema]) => {
102
114
  const isRequired = Required.includes(propertyName);
103
115
 
104
116
  return (
@@ -113,6 +125,8 @@ export const CustomParamsRenderer: React.FC<CustomParamsRendererProps> = ({
113
125
  level={0}
114
126
  errors={errors}
115
127
  fieldPath=""
128
+ uploadSender={uploadSender}
129
+ fileUploader={fileUploader}
116
130
  />
117
131
  );
118
132
  })}
@@ -163,4 +177,4 @@ export const useCustomParamsRenderer = (schema: CustomParamSchema) => {
163
177
  };
164
178
  };
165
179
 
166
- export default CustomParamsRenderer;
180
+ export default CustomParamsRenderer;
@@ -1,4 +1,102 @@
1
- export type ParamType = 'string' | 'number' | 'boolean' | 'array' | 'object';
1
+ // ============ 上传相关类型定义 ============
2
+
3
+ /**
4
+ * 上传请求参数
5
+ */
6
+ export interface UploadRequestParams {
7
+ /** 事件类型:uploadToken-获取上传凭证,uploadFile-获取文件ID */
8
+ eventType: 'uploadToken' | 'uploadFile';
9
+ /** 文件名 */
10
+ fileName: string;
11
+ /** 文件下载 URL(uploadFile 时需要) */
12
+ content?: string;
13
+ }
14
+
15
+ /**
16
+ * 上传 Token 响应
17
+ */
18
+ export interface UploadTokenResponse {
19
+ /** 预签名上传 URL */
20
+ uploadUrl: string;
21
+ /** 文件下载 URL */
22
+ downloadUrl: string;
23
+ }
24
+
25
+ /**
26
+ * 上传文件响应
27
+ */
28
+ export interface UploadFileResponse {
29
+ /** 文件 ID */
30
+ fileId: string;
31
+ }
32
+
33
+ /**
34
+ * 上传发送方法类型
35
+ * 用于发送上传相关的事件请求
36
+ */
37
+ export type UploadSender = (params: UploadRequestParams) => Promise<string | null>;
38
+
39
+ /**
40
+ * 文件上传方法类型
41
+ * 用于实际执行文件上传到 OSS
42
+ */
43
+ export type FileUploader = (file: Blob, uploadUrl: string) => Promise<void>;
44
+
45
+ /**
46
+ * 上传配置
47
+ */
48
+ export interface UploadConfig {
49
+ /** 上传发送方法 */
50
+ uploadSender?: UploadSender;
51
+ /** 文件上传方法 */
52
+ fileUploader?: FileUploader;
53
+ }
54
+
55
+ // ============ 基础类型定义 ============
56
+
57
+ // 基础类型
58
+ export type BasicParamType = 'string' | 'number' | 'boolean';
59
+
60
+ // 复合类型
61
+ export type ComplexParamType = 'array' | 'object';
62
+
63
+ // 扩展类型
64
+ export type ExtendedParamType = 'time' | 'file';
65
+
66
+ // 所有参数类型
67
+ export type ParamType = BasicParamType | ComplexParamType | ExtendedParamType;
68
+
69
+ // 时间子类型
70
+ export type TimeSubType = 'year-month' | 'year-month-day' | 'datetime';
71
+
72
+ // 文件子类型
73
+ export type FileSubType =
74
+ | 'default'
75
+ | 'jpg'
76
+ | 'png'
77
+ | 'svg'
78
+ | 'doc'
79
+ | 'ppt'
80
+ | 'excel'
81
+ | 'txt'
82
+ | 'markdown'
83
+ | 'zip';
84
+
85
+ // 枚举展示样式
86
+ export type EnumDisplayStyle = 'select' | 'checkbox' | 'radio' | 'multi-select';
87
+
88
+ /**
89
+ * AssociationPropertyMetadata 类型定义
90
+ * 包含枚举、时间、文件等扩展属性的元数据
91
+ */
92
+ export interface AssociationPropertyMetadata {
93
+ /** 子类型(用于 time 和 file 类型),现在是数组 */
94
+ SubType?: (TimeSubType | FileSubType)[];
95
+ /** 枚举值 */
96
+ EnumValues?: (string | number | boolean)[];
97
+ /** 枚举展示样式 */
98
+ EnumDisplayStyle?: EnumDisplayStyle;
99
+ }
2
100
 
3
101
  /**
4
102
  * CustomParam 的 Schema 定义
@@ -10,12 +108,29 @@ export interface CustomParamSchema {
10
108
  Required?: string[];
11
109
  Properties?: Record<string, CustomParamSchema>;
12
110
  Items?: CustomParamSchema;
111
+ // 排序字段(支持小写)
112
+ order?: number;
113
+ // 排序字段(支持大写,兼容实际数据格式)
114
+ Order?: string | number;
115
+ // 关联属性元数据(包含 SubType、EnumValues、EnumDisplayStyle)
116
+ AssociationPropertyMetadata?: AssociationPropertyMetadata;
117
+ AssociationProperty?: string;
118
+
119
+ // ============ 以下字段已废弃,保留用于向后兼容 ============
120
+ /** @deprecated 使用 AssociationPropertyMetadata.SubType 代替 */
121
+ TimeSubType?: TimeSubType;
122
+ /** @deprecated 使用 AssociationPropertyMetadata.SubType 代替 */
123
+ FileSubType?: FileSubType;
124
+ /** @deprecated 使用 AssociationPropertyMetadata.EnumValues 代替 */
125
+ EnumValues?: string[];
126
+ /** @deprecated 使用 AssociationPropertyMetadata.EnumDisplayStyle 代替 */
127
+ EnumDisplayStyle?: EnumDisplayStyle;
13
128
  }
14
129
 
15
130
  /**
16
131
  * CustomParamsRenderer 组件的 Props
17
132
  */
18
- export interface CustomParamsRendererProps {
133
+ export interface CustomParamsRendererProps extends UploadConfig {
19
134
  /** CustomParams 的 schema */
20
135
  schema: CustomParamSchema;
21
136
  /** 表单值 */
@@ -33,7 +148,7 @@ export interface CustomParamsRendererProps {
33
148
  /**
34
149
  * 单个字段渲染器的 Props
35
150
  */
36
- export interface FieldRendererProps {
151
+ export interface FieldRendererProps extends UploadConfig {
37
152
  /** 字段名 */
38
153
  name: string;
39
154
  /** 字段 Schema */
@@ -52,12 +167,14 @@ export interface FieldRendererProps {
52
167
  errors?: Record<string, string>;
53
168
  /** 字段路径(用于匹配错误信息) */
54
169
  fieldPath?: string;
170
+ /** 是否隐藏标签(用于数组项渲染时隐藏 Items 标题) */
171
+ hideLabel?: boolean;
55
172
  }
56
173
 
57
174
  /**
58
175
  * 数组字段的 Props
59
176
  */
60
- export interface ArrayFieldProps {
177
+ export interface ArrayFieldProps extends UploadConfig {
61
178
  /** 字段名 */
62
179
  name: string;
63
180
  /** 字段 Schema */
@@ -81,7 +198,7 @@ export interface ArrayFieldProps {
81
198
  /**
82
199
  * 对象字段的 Props
83
200
  */
84
- export interface ObjectFieldProps {
201
+ export interface ObjectFieldProps extends UploadConfig {
85
202
  /** 字段名 */
86
203
  name: string;
87
204
  /** 字段 Schema */
@@ -102,6 +219,60 @@ export interface ObjectFieldProps {
102
219
  fieldPath?: string;
103
220
  }
104
221
 
222
+ /**
223
+ * 时间字段的 Props
224
+ */
225
+ export interface TimeFieldProps {
226
+ /** 字段名 */
227
+ name: string;
228
+ /** 字段 Schema */
229
+ schema: CustomParamSchema;
230
+ /** 字段值 */
231
+ value?: string;
232
+ /** 值变化回调 */
233
+ onChange?: (value: string | null) => void;
234
+ /** 是否必填 */
235
+ required?: boolean;
236
+ /** 是否禁用 */
237
+ disabled?: boolean;
238
+ }
239
+
240
+ /**
241
+ * 文件字段的 Props
242
+ */
243
+ export interface FileFieldProps extends UploadConfig {
244
+ /** 字段名 */
245
+ name: string;
246
+ /** 字段 Schema */
247
+ schema: CustomParamSchema;
248
+ /** 字段值 */
249
+ value?: any;
250
+ /** 值变化回调 */
251
+ onChange?: (value: any) => void;
252
+ /** 是否必填 */
253
+ required?: boolean;
254
+ /** 是否禁用 */
255
+ disabled?: boolean;
256
+ }
257
+
258
+ /**
259
+ * 枚举字段的 Props
260
+ */
261
+ export interface EnumFieldProps {
262
+ /** 字段名 */
263
+ name: string;
264
+ /** 字段 Schema */
265
+ schema: CustomParamSchema;
266
+ /** 字段值 */
267
+ value?: any;
268
+ /** 值变化回调 */
269
+ onChange?: (value: any) => void;
270
+ /** 是否必填 */
271
+ required?: boolean;
272
+ /** 是否禁用 */
273
+ disabled?: boolean;
274
+ }
275
+
105
276
  /**
106
277
  * 校验错误信息
107
278
  */
@@ -150,6 +321,12 @@ export const validateCustomParams = (
150
321
  field: fieldName,
151
322
  message: `${fieldSchema?.Title || key} 是必填项`,
152
323
  });
324
+ } else if (Array.isArray(fieldValue) && fieldValue.length === 0) {
325
+ // 数组类型的必填检查
326
+ errors.push({
327
+ field: fieldName,
328
+ message: `${fieldSchema?.Title || key} 是必填项`,
329
+ });
153
330
  }
154
331
  }
155
332
 
@@ -194,6 +371,26 @@ export const validateCustomParams = (
194
371
  }
195
372
  });
196
373
  }
374
+
375
+ // 校验时间类型
376
+ if (fieldSchema?.Type === 'time' && isRequired) {
377
+ if (!fieldValue) {
378
+ errors.push({
379
+ field: fieldName,
380
+ message: `${fieldSchema?.Title || key} 是必填项`,
381
+ });
382
+ }
383
+ }
384
+
385
+ // 校验文件类型
386
+ if (fieldSchema?.Type === 'file' && isRequired) {
387
+ if (!fieldValue || (Array.isArray(fieldValue) && fieldValue.length === 0)) {
388
+ errors.push({
389
+ field: fieldName,
390
+ message: `${fieldSchema?.Title || key} 是必填项`,
391
+ });
392
+ }
393
+ }
197
394
  });
198
395
 
199
396
  return {
@@ -201,3 +398,32 @@ export const validateCustomParams = (
201
398
  errors,
202
399
  };
203
400
  };
401
+
402
+ /**
403
+ * 根据 Order 字段对属性进行排序
404
+ * 完全兼容没有 Order 字段的历史数据
405
+ *
406
+ * @param properties - 属性对象
407
+ * @returns 排序后的属性数组 [key, schema][]
408
+ */
409
+ export const sortPropertiesByOrder = (
410
+ properties: Record<string, CustomParamSchema>
411
+ ): [string, CustomParamSchema][] => {
412
+ return Object.entries(properties).sort(([, schemaA], [, schemaB]) => {
413
+ // 读取 Order 字段(支持大小写)
414
+ const orderA = schemaA.Order ?? schemaA.order;
415
+ const orderB = schemaB.Order ?? schemaB.order;
416
+
417
+ // 转换为数字(字符串 "12" -> 12)
418
+ const numA = orderA !== undefined ? Number(orderA) : Number.MAX_SAFE_INTEGER;
419
+ const numB = orderB !== undefined ? Number(orderB) : Number.MAX_SAFE_INTEGER;
420
+
421
+ // 如果两个都没有 Order,保持原有顺序(返回 0)
422
+ // 这样可以确保历史数据(所有字段都没有 Order)不会被打乱
423
+ if (numA === Number.MAX_SAFE_INTEGER && numB === Number.MAX_SAFE_INTEGER) {
424
+ return 0;
425
+ }
426
+
427
+ return numA - numB;
428
+ });
429
+ };
@@ -36,12 +36,8 @@ const StatusText = styled.div`
36
36
  // ==================== Types ====================
37
37
 
38
38
  export interface HistoryCardProps {
39
- /** 审批状态 */
40
- approvalStatus?: string;
41
- /** 表单值 */
42
- formValues?: Record<string, any>;
43
- /** 表单 Schema */
44
- formSchema?: CustomParamSchema;
39
+ /** 消息数据对象 */
40
+ data: any;
45
41
  }
46
42
 
47
43
  /**
@@ -68,7 +64,35 @@ export const convertSchemaToUpperCase = (schema: any): CustomParamSchema | undef
68
64
  result.Required = schema.required;
69
65
  }
70
66
 
71
- if (schema.properties && typeof schema.properties === 'object') {
67
+ // 排序字段
68
+ if (schema.order !== undefined) {
69
+ result.order = schema.order;
70
+ }
71
+
72
+ // 处理 AssociationPropertyMetadata
73
+ if (schema.associationPropertyMetadata) {
74
+ const metadata = schema.associationPropertyMetadata;
75
+ result.AssociationPropertyMetadata = {};
76
+
77
+ // SubType(数组格式)
78
+ if (metadata.subType) {
79
+ const subType = metadata.subType;
80
+ // 确保是数组格式
81
+ result.AssociationPropertyMetadata.SubType = Array.isArray(subType) ? subType : [subType];
82
+ }
83
+
84
+ // EnumValues
85
+ if (metadata.enumValues) {
86
+ result.AssociationPropertyMetadata.EnumValues = metadata.enumValues;
87
+ }
88
+
89
+ // EnumDisplayStyle
90
+ if (metadata.enumDisplayStyle) {
91
+ result.AssociationPropertyMetadata.EnumDisplayStyle = metadata.enumDisplayStyle;
92
+ }
93
+ }
94
+
95
+ if ((schema.properties && typeof schema.properties === 'object')) {
72
96
  result.Properties = {};
73
97
  for (const [key, value] of Object.entries(schema.properties)) {
74
98
  const converted = convertSchemaToUpperCase(value);
@@ -88,21 +112,15 @@ export const convertSchemaToUpperCase = (schema: any): CustomParamSchema | undef
88
112
  /**
89
113
  * HistoryCard 历史卡片组件 (SDK 版本)
90
114
  * 用于展示历史对话中的 card 类型消息(只读模式)
91
- *
92
- * @example
93
- * ```tsx
94
- * <HistoryCard
95
- * approvalStatus="approved"
96
- * formValues={{ name: 'test', age: 18 }}
97
- * formSchema={schema}
98
- * />
99
- * ```
100
115
  */
101
- export const HistoryCard: React.FC<HistoryCardProps> = ({
102
- approvalStatus,
103
- formValues = {},
104
- formSchema,
105
- }) => {
116
+ export const HistoryCard: React.FC<HistoryCardProps> = ({ data }) => {
117
+ // 从data中提取需要的参数
118
+ const approvalStatus = data?.approvalStatus;
119
+
120
+ // 使用测试数据或真实数据
121
+ const formValues = (data?.formValues || {});
122
+ const formSchema = data?.formSchema;
123
+
106
124
  // 判断是否已提交
107
125
  const isApproved = approvalStatus === 'approved';
108
126
 
@@ -1,7 +1,7 @@
1
1
  import React, { useCallback, useEffect, useState } from 'react';
2
2
  import { Button, message } from 'antd';
3
3
  import styled from 'styled-components';
4
- import CustomParamsRenderer, { validateCustomParams, CustomParamSchema } from './CustomParamsRenderer';
4
+ import CustomParamsRenderer, { validateCustomParams, UploadSender, FileUploader } from './CustomParamsRenderer';
5
5
 
6
6
  // ==================== Styled Components ====================
7
7
 
@@ -35,16 +35,12 @@ const StatusText = styled.div`
35
35
  // ==================== Types ====================
36
36
 
37
37
  export interface HumanVerifyProps {
38
- /** 验证ID */
39
- verifyId?: string;
40
- /** 会话 Webhook */
41
- sessionWebhook?: string;
42
- /** 是否已提交 */
43
- approved?: boolean;
44
- /** CustomParams 的 schema */
45
- customParamsSchema?: CustomParamSchema;
46
- /** CustomParams 的 key(用于提交时的字段名) */
47
- customParamsKey?: string;
38
+ /** 消息数据对象 */
39
+ data: any;
40
+ /** 上传发送方法 */
41
+ uploadSender?: UploadSender;
42
+ /** 文件上传方法 */
43
+ fileUploader?: FileUploader;
48
44
  /** 提交回调函数 */
49
45
  onSubmit?: (data: {
50
46
  verifyId: string;
@@ -58,39 +54,20 @@ export interface HumanVerifyProps {
58
54
  /**
59
55
  * HumanVerify 人工审核组件 (SDK 版本)
60
56
  * 用于展示需要人工审核的表单,并处理提交逻辑
61
- *
62
- * @example
63
- * ```tsx
64
- * <HumanVerify
65
- * verifyId="xxx"
66
- * sessionWebhook="https://..."
67
- * approved={false}
68
- * customParamsSchema={schema}
69
- * customParamsKey="customParams"
70
- * onSubmit={(data) => {
71
- * bot.postMessage({
72
- * msgType: 'cardCallBack',
73
- * data: {
74
- * sessionWebhook: data.sessionWebhook,
75
- * content: JSON.stringify({
76
- * verifyId: data.verifyId,
77
- * status: data.status,
78
- * [data.customParamsKey]: data.customParamsValue,
79
- * }),
80
- * },
81
- * });
82
- * }}
83
- * />
84
- * ```
85
57
  */
86
- export const HumanVerify: React.FC<HumanVerifyProps> = ({
87
- verifyId,
88
- sessionWebhook,
89
- approved = false,
90
- customParamsSchema,
91
- customParamsKey = 'customParams',
92
- onSubmit,
58
+ export const HumanVerify: React.FC<HumanVerifyProps> = ({
59
+ data,
60
+ uploadSender,
61
+ fileUploader,
62
+ onSubmit
93
63
  }) => {
64
+ // 从 data 中提取需要的参数
65
+ const verifyId = data?.verifyId;
66
+ const sessionWebhook = data?.sessionWebhook;
67
+ const approved = data?.approved || false;
68
+ const customParamsSchema = data?.customParams;
69
+ const customParamsKey = data?.customParamsKey;
70
+
94
71
  // CustomParams表单状态管理
95
72
  const [customParamsValue, setCustomParamsValue] = useState<Record<string, any>>({});
96
73
  // CustomParams验证错误状态
@@ -142,7 +119,7 @@ export const HumanVerify: React.FC<HumanVerifyProps> = ({
142
119
  }
143
120
 
144
121
  // 调用提交回调
145
- onSubmit({
122
+ onSubmit?.({
146
123
  verifyId,
147
124
  sessionWebhook: sessionWebhook || '',
148
125
  status: 'approve',
@@ -159,7 +136,8 @@ export const HumanVerify: React.FC<HumanVerifyProps> = ({
159
136
  value={customParamsValue}
160
137
  onChange={setCustomParamsValue}
161
138
  errors={validationErrors}
162
- disabled={approved}
139
+ uploadSender={uploadSender}
140
+ fileUploader={fileUploader}
163
141
  />
164
142
  )}
165
143
  <StatusContainer $approved={approved}>
@@ -181,4 +159,4 @@ export const HumanVerify: React.FC<HumanVerifyProps> = ({
181
159
  );
182
160
  };
183
161
 
184
- export default HumanVerify;
162
+ export default HumanVerify;
@@ -351,11 +351,7 @@ export const MessageBubble: React.FC<MessageBubbleProps> = ({
351
351
  {/* HumanVerify事件 - 人工审核表单 */}
352
352
  {eventType === 'humanVerify' && humanVerifyData && (
353
353
  <HumanVerify
354
- verifyId={humanVerifyData.verifyId}
355
- sessionWebhook={humanVerifyData.sessionWebhook}
356
- approved={humanVerifyData.approved}
357
- customParamsSchema={humanVerifyData.customParams}
358
- customParamsKey={humanVerifyData.customParamsKey}
354
+ data={humanVerifyData}
359
355
  onSubmit={onHumanVerifySubmit}
360
356
  />
361
357
  )}
@@ -363,14 +359,12 @@ export const MessageBubble: React.FC<MessageBubbleProps> = ({
363
359
  {/* HistoryCard 事件 - 历史对话中的审核卡片 */}
364
360
  {eventType === 'historyCard' && historyCardData && (
365
361
  <HistoryCard
366
- approvalStatus={historyCardData.approvalStatus}
367
- formValues={historyCardData.formValues}
368
- formSchema={historyCardData.formSchema}
362
+ data={historyCardData}
369
363
  />
370
364
  )}
371
365
 
372
366
  {/* 参考资料 */}
373
- {references.length > 0 && status === 'Success' && (
367
+ {references && references.length > 0 && status === 'Success' && (
374
368
  <ReferencesContainer>
375
369
  <DocReferences
376
370
  items={references}
@@ -397,4 +391,4 @@ export const MessageBubble: React.FC<MessageBubbleProps> = ({
397
391
  );
398
392
  };
399
393
 
400
- export default MessageBubble;
394
+ export default MessageBubble;
@@ -254,7 +254,7 @@ export const RichMessageBubble: React.FC<RichMessageBubbleProps> = ({
254
254
  role="bot"
255
255
  >
256
256
  {/* 参考资料 */}
257
- {references.length > 0 && status === 'Success' && (
257
+ {references && references.length > 0 && status === 'Success' && (
258
258
  <ReferencesContainer>
259
259
  <DocReferences
260
260
  items={references}
@@ -280,4 +280,4 @@ export const RichMessageBubble: React.FC<RichMessageBubbleProps> = ({
280
280
  );
281
281
  };
282
282
 
283
- export default RichMessageBubble;
283
+ export default RichMessageBubble;
package/src/index.ts CHANGED
@@ -78,3 +78,4 @@ export type {
78
78
 
79
79
  // ==================== 工具函数导出 ====================
80
80
  export { loadEchartsScript } from './utils/loadEcharts';
81
+ export { loadMermaidScript } from './utils/loadMermaid';