bi-sdk-react 0.0.3 → 0.0.5
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.
- package/dist/es/css/bi-sdk.css +1 -1
- package/dist/es/js/bi-sdk.es.js +295 -67
- package/dist/types/components/PageDesigner.d.ts +9 -1
- package/dist/types/components/context/DesignerContext.d.ts +5 -2
- package/dist/types/components/context/EnvContext.d.ts +2 -1
- package/dist/types/components/icon/IconFont.d.ts +2 -1
- package/dist/types/components/layout/PageCanvas.d.ts +2 -0
- package/dist/types/components/panel/AiPanel.d.ts +4 -0
- package/dist/types/components/panel/ChatInput.d.ts +13 -6
- package/dist/types/components/panel/DatasetPanel.d.ts +11 -0
- package/dist/types/components/panel/PaneHeader.d.ts +1 -0
- package/dist/types/components/panel/PropertiesPanel.d.ts +3 -1
- package/dist/types/components/plugins/@antd/item-props/CheckboxProps.d.ts +3 -3
- package/dist/types/components/plugins/@antd/item-props/ColProps.d.ts +2 -2
- package/dist/types/components/plugins/@antd/item-props/EchartsProps.d.ts +2 -2
- package/dist/types/components/plugins/@antd/item-props/TextProps.d.ts +1 -0
- package/dist/types/components/plugins/@antd/items/TableRender.d.ts +1 -0
- package/dist/types/components/plugins/@antd/items/TextRender.d.ts +1 -0
- package/dist/types/components/typing.d.ts +97 -2
- package/dist/types/components/utils.d.ts +1 -0
- package/dist/umd/css/bi-sdk.css +1 -1
- package/dist/umd/js/bi-sdk.umd.min.js +299 -71
- package/package.json +1 -1
- package/src/components/PageDesigner.tsx +293 -35
- package/src/components/context/DesignerContext.tsx +15 -3
- package/src/components/context/EnvContext.tsx +4 -1
- package/src/components/icon/IconFont.tsx +15 -11
- package/src/components/layout/PageCanvas.tsx +4 -2
- package/src/components/layout/PageItem.tsx +1 -1
- package/src/components/panel/AiPanel.tsx +609 -43
- package/src/components/panel/ChatInput.tsx +322 -180
- package/src/components/panel/DatasetPanel.tsx +65 -0
- package/src/components/panel/PaneHeader.tsx +3 -2
- package/src/components/panel/PropertiesPanel.tsx +334 -127
- package/src/components/plugins/@antd/index.ts +12 -9
- package/src/components/plugins/@antd/item-props/CapsuleProps.tsx +1 -1
- package/src/components/plugins/@antd/item-props/CheckboxProps.tsx +90 -42
- package/src/components/plugins/@antd/item-props/ColProps.tsx +139 -25
- package/src/components/plugins/@antd/item-props/EchartsProps.tsx +52 -22
- package/src/components/plugins/@antd/item-props/HtmlProps.tsx +8 -9
- package/src/components/plugins/@antd/item-props/SelectProps.tsx +1 -1
- package/src/components/plugins/@antd/item-props/TextProps.tsx +14 -3
- package/src/components/plugins/@antd/items/EchartsRender.tsx +9 -1
- package/src/components/plugins/@antd/items/HtmlRender.tsx +13 -1
- package/src/components/plugins/@antd/items/ListRender.tsx +18 -1
- package/src/components/plugins/@antd/items/TableRender.tsx +16 -1
- package/src/components/plugins/@antd/items/TextRender.tsx +3 -1
- package/src/components/styles.css +20 -0
- package/src/components/typing.ts +111 -2
- package/src/components/utils.ts +40 -0
- package/src/example.tsx +314 -13
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AudioOutlined,
|
|
3
|
+
DeleteOutlined as DelIcon, DeleteOutlined, FileExcelOutlined, InboxOutlined, SendOutlined
|
|
4
|
+
} from "@ant-design/icons";
|
|
5
|
+
import {
|
|
6
|
+
Button,
|
|
7
|
+
Dropdown,
|
|
8
|
+
Input,
|
|
9
|
+
Modal,
|
|
10
|
+
Tooltip,
|
|
11
|
+
Typography,
|
|
12
|
+
Upload,
|
|
13
|
+
message
|
|
14
|
+
} from "antd";
|
|
1
15
|
import React, {
|
|
2
16
|
useEffect,
|
|
3
17
|
useImperativeHandle,
|
|
@@ -6,9 +20,8 @@ import React, {
|
|
|
6
20
|
useState,
|
|
7
21
|
} from "react";
|
|
8
22
|
import styled from "styled-components";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import * as XLSX from "xlsx";
|
|
23
|
+
import { IconFont } from "../icon/IconFont";
|
|
24
|
+
import { FetchType } from "../typing";
|
|
12
25
|
|
|
13
26
|
export type ChatInputProps = {
|
|
14
27
|
value?: string;
|
|
@@ -17,22 +30,25 @@ export type ChatInputProps = {
|
|
|
17
30
|
sending?: boolean;
|
|
18
31
|
rows?: number;
|
|
19
32
|
agentList?: { id: string | number; name: string }[];
|
|
20
|
-
title?: string;
|
|
21
33
|
agentId?: string | number | null;
|
|
22
34
|
attachments?: any[];
|
|
23
35
|
onInput?: (text: string) => void;
|
|
24
36
|
onSubmit?: (data: {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
csvData: Record<string, string>;
|
|
37
|
+
message: string;
|
|
38
|
+
agents?: { id: string | number; name: string }[] | null;
|
|
39
|
+
files?: { id: string; name: string; extension: string }[] | null;
|
|
29
40
|
}) => void;
|
|
30
|
-
onUpdateTitle?: (title: string) => void;
|
|
31
41
|
onUpdateAgentId?: (id: string | number | null) => void;
|
|
32
42
|
onUpdateAttachments?: (list: any[]) => void;
|
|
43
|
+
onUploading?: FetchType["upload"];
|
|
44
|
+
style?: React.CSSProperties;
|
|
33
45
|
};
|
|
34
46
|
|
|
35
|
-
const
|
|
47
|
+
const AgentListDropdownContent = styled.div`
|
|
48
|
+
max-height: 400px;
|
|
49
|
+
overflow: auto;
|
|
50
|
+
background: #fff;
|
|
51
|
+
|
|
36
52
|
ul.agent-list {
|
|
37
53
|
margin: 0;
|
|
38
54
|
padding: 0;
|
|
@@ -57,6 +73,9 @@ const Wrapper = styled.div`
|
|
|
57
73
|
text-overflow: ellipsis;
|
|
58
74
|
white-space: nowrap;
|
|
59
75
|
}
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
const Wrapper = styled.div`
|
|
60
79
|
.chat-input {
|
|
61
80
|
display: flex;
|
|
62
81
|
flex-direction: column;
|
|
@@ -93,6 +112,11 @@ const Wrapper = styled.div`
|
|
|
93
112
|
background-color: #fafafa;
|
|
94
113
|
font-size: 12px;
|
|
95
114
|
position: relative;
|
|
115
|
+
|
|
116
|
+
.anticon {
|
|
117
|
+
font-size: 36px;
|
|
118
|
+
color: var(--ant-green-8);
|
|
119
|
+
}
|
|
96
120
|
}
|
|
97
121
|
.attachment-card .name {
|
|
98
122
|
max-width: 80px;
|
|
@@ -108,9 +132,19 @@ const Wrapper = styled.div`
|
|
|
108
132
|
position: absolute;
|
|
109
133
|
top: 4px;
|
|
110
134
|
right: 2px;
|
|
135
|
+
|
|
136
|
+
.anticon {
|
|
137
|
+
font-size: 12px;
|
|
138
|
+
color: var(--ant-color-text-tertiary);
|
|
139
|
+
}
|
|
111
140
|
}
|
|
112
141
|
.attachment-card .remove:hover {
|
|
113
142
|
color: #666;
|
|
143
|
+
|
|
144
|
+
.anticon {
|
|
145
|
+
font-size: 12px;
|
|
146
|
+
color: var(--ant-color-text-secondary);
|
|
147
|
+
}
|
|
114
148
|
}
|
|
115
149
|
.agent-info {
|
|
116
150
|
box-sizing: border-box;
|
|
@@ -120,11 +154,17 @@ const Wrapper = styled.div`
|
|
|
120
154
|
border-bottom: none;
|
|
121
155
|
background-color: #eff1f9;
|
|
122
156
|
font-size: 14px;
|
|
123
|
-
color:
|
|
157
|
+
color: var(--ant-color-text-label);
|
|
124
158
|
display: flex;
|
|
125
159
|
align-items: center;
|
|
126
|
-
|
|
160
|
+
flex-wrap: wrap;
|
|
127
161
|
user-select: none;
|
|
162
|
+
gap: 8px;
|
|
163
|
+
|
|
164
|
+
a {
|
|
165
|
+
color: var(--ant-color-text-disabled);
|
|
166
|
+
margin-left: 4px;
|
|
167
|
+
}
|
|
128
168
|
}
|
|
129
169
|
.attachment-bar + .agent-info {
|
|
130
170
|
border-radius: 0;
|
|
@@ -207,21 +247,6 @@ const Wrapper = styled.div`
|
|
|
207
247
|
.agent-trigger:hover {
|
|
208
248
|
background-color: #f7f7f7;
|
|
209
249
|
}
|
|
210
|
-
.title-toggle {
|
|
211
|
-
display: none;
|
|
212
|
-
align-items: center;
|
|
213
|
-
gap: 6px;
|
|
214
|
-
background-color: #ffffff;
|
|
215
|
-
border: solid 1px #dcdcdc;
|
|
216
|
-
border-radius: 5px;
|
|
217
|
-
padding: 0 4px;
|
|
218
|
-
height: 26px;
|
|
219
|
-
}
|
|
220
|
-
.title-toggle .label {
|
|
221
|
-
color: #666;
|
|
222
|
-
font-size: 12px;
|
|
223
|
-
user-select: none;
|
|
224
|
-
}
|
|
225
250
|
.attach-upload {
|
|
226
251
|
display: inline-flex;
|
|
227
252
|
align-items: center;
|
|
@@ -235,6 +260,9 @@ const Wrapper = styled.div`
|
|
|
235
260
|
font-size: 12px;
|
|
236
261
|
cursor: pointer;
|
|
237
262
|
}
|
|
263
|
+
.ant-btn .anticon {
|
|
264
|
+
color: var(--ant-color-text-label);
|
|
265
|
+
}
|
|
238
266
|
.tips {
|
|
239
267
|
color: #999;
|
|
240
268
|
font-size: 12px;
|
|
@@ -249,7 +277,7 @@ const Wrapper = styled.div`
|
|
|
249
277
|
width: 50px;
|
|
250
278
|
height: 26px;
|
|
251
279
|
color: #fff;
|
|
252
|
-
font-size:
|
|
280
|
+
font-size: 14px;
|
|
253
281
|
background-color: rgb(184, 184, 191);
|
|
254
282
|
border: unset;
|
|
255
283
|
border-radius: 14px;
|
|
@@ -257,13 +285,161 @@ const Wrapper = styled.div`
|
|
|
257
285
|
cursor: pointer;
|
|
258
286
|
}
|
|
259
287
|
.send-btn.send-btn-active {
|
|
260
|
-
background-color: var(--
|
|
288
|
+
background-color: var(--ant-color-primary);
|
|
261
289
|
}
|
|
262
290
|
`;
|
|
263
291
|
|
|
264
292
|
const DEFAULT_AGENT_LIST: any[] = [];
|
|
265
293
|
const DEFAULT_ATTACHMENTS: any[] = [];
|
|
266
294
|
|
|
295
|
+
const ALLOWED_MIME_TYPES = [
|
|
296
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
297
|
+
"audio/wav",
|
|
298
|
+
];
|
|
299
|
+
|
|
300
|
+
const ALLOWED_EXTENSIONS = [".xlsx", ".wav"];
|
|
301
|
+
|
|
302
|
+
const FileUploadModal: React.FC<{
|
|
303
|
+
open: boolean;
|
|
304
|
+
onCancel: () => void;
|
|
305
|
+
onOk: (files: File[]) => void;
|
|
306
|
+
allowedExtensions: string[];
|
|
307
|
+
allowedMimeTypes: string[];
|
|
308
|
+
}> = ({ open, onCancel, onOk, allowedExtensions, allowedMimeTypes }) => {
|
|
309
|
+
const [fileList, setFileList] = useState<File[]>([]);
|
|
310
|
+
|
|
311
|
+
useEffect(() => {
|
|
312
|
+
if (open) {
|
|
313
|
+
setFileList([]);
|
|
314
|
+
}
|
|
315
|
+
}, [open]);
|
|
316
|
+
|
|
317
|
+
const handleOk = () => {
|
|
318
|
+
onOk(fileList);
|
|
319
|
+
onCancel();
|
|
320
|
+
};
|
|
321
|
+
const acceptFiles = [...ALLOWED_EXTENSIONS, ...ALLOWED_MIME_TYPES].join(",");
|
|
322
|
+
|
|
323
|
+
const props = {
|
|
324
|
+
name: "file",
|
|
325
|
+
multiple: true,
|
|
326
|
+
accept: acceptFiles,
|
|
327
|
+
fileList: [], // Managed manually
|
|
328
|
+
beforeUpload: (file: File) => {
|
|
329
|
+
const mimeOk = allowedMimeTypes.includes(file.type) || !file.type;
|
|
330
|
+
const ext = (file.name || "")
|
|
331
|
+
.substring(file.name.lastIndexOf("."))
|
|
332
|
+
.toLowerCase();
|
|
333
|
+
const extOk = allowedExtensions.includes(ext);
|
|
334
|
+
|
|
335
|
+
if (!mimeOk && !extOk) {
|
|
336
|
+
message.error(`不支持的文件类型: ${file.name}`);
|
|
337
|
+
return Upload.LIST_IGNORE;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
setFileList((prev) => [...prev, file]);
|
|
341
|
+
return false;
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const removeFile = (index: number) => {
|
|
346
|
+
setFileList((prev) => prev.filter((_, i) => i !== index));
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<Modal
|
|
351
|
+
title="上传文件"
|
|
352
|
+
open={open}
|
|
353
|
+
okText="上传"
|
|
354
|
+
cancelText="取消"
|
|
355
|
+
onOk={handleOk}
|
|
356
|
+
onCancel={onCancel}
|
|
357
|
+
width={600}
|
|
358
|
+
>
|
|
359
|
+
<div style={{ marginBottom: 16 }}>
|
|
360
|
+
<Typography.Text type="secondary">
|
|
361
|
+
支持的文件类型:{allowedExtensions.join(", ")}
|
|
362
|
+
<br />
|
|
363
|
+
单次支持上传多个文件,请确保文件内容合规。
|
|
364
|
+
<br />
|
|
365
|
+
Excel 暂不支持加密文件。
|
|
366
|
+
<br />
|
|
367
|
+
不支持 xls 电子表格,请自行转换为 xlsx 格式。
|
|
368
|
+
<br />
|
|
369
|
+
Excel 仅支持第一行为表头、第二行开始为数据,不支持合并单元格。
|
|
370
|
+
</Typography.Text>
|
|
371
|
+
</div>
|
|
372
|
+
<Upload.Dragger {...props}>
|
|
373
|
+
<p className="ant-upload-drag-icon">
|
|
374
|
+
<InboxOutlined />
|
|
375
|
+
</p>
|
|
376
|
+
<p className="ant-upload-text">点击或将文件拖拽到此处上传</p>
|
|
377
|
+
<p className="ant-upload-hint">
|
|
378
|
+
支持上传Excel表格数据或音频文件进行分析
|
|
379
|
+
</p>
|
|
380
|
+
</Upload.Dragger>
|
|
381
|
+
{fileList.length > 0 && (
|
|
382
|
+
<div style={{ marginTop: 16 }}>
|
|
383
|
+
<Typography.Title level={5} style={{ fontSize: 14, marginBottom: 8 }}>
|
|
384
|
+
已选文件 ({fileList.length})
|
|
385
|
+
</Typography.Title>
|
|
386
|
+
<div
|
|
387
|
+
style={{
|
|
388
|
+
display: "flex",
|
|
389
|
+
flexDirection: "column",
|
|
390
|
+
gap: 8,
|
|
391
|
+
maxHeight: 200,
|
|
392
|
+
overflowY: "auto",
|
|
393
|
+
}}
|
|
394
|
+
>
|
|
395
|
+
{fileList.map((file, index) => (
|
|
396
|
+
<div
|
|
397
|
+
key={`${file.name}-${index}`}
|
|
398
|
+
style={{
|
|
399
|
+
display: "flex",
|
|
400
|
+
alignItems: "center",
|
|
401
|
+
justifyContent: "space-between",
|
|
402
|
+
padding: "8px 12px",
|
|
403
|
+
background: "#f5f5f5",
|
|
404
|
+
borderRadius: 4,
|
|
405
|
+
}}
|
|
406
|
+
>
|
|
407
|
+
<div
|
|
408
|
+
style={{
|
|
409
|
+
display: "flex",
|
|
410
|
+
alignItems: "center",
|
|
411
|
+
gap: 8,
|
|
412
|
+
overflow: "hidden",
|
|
413
|
+
}}
|
|
414
|
+
>
|
|
415
|
+
{file.name.endsWith(".xlsx") ? (
|
|
416
|
+
<FileExcelOutlined style={{ color: "#52c41a" }} />
|
|
417
|
+
) : (
|
|
418
|
+
<AudioOutlined style={{ color: "#1890ff" }} />
|
|
419
|
+
)}
|
|
420
|
+
<Typography.Text ellipsis style={{ maxWidth: 300 }}>
|
|
421
|
+
{file.name}
|
|
422
|
+
</Typography.Text>
|
|
423
|
+
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
|
424
|
+
({(file.size / 1024).toFixed(1)} KB)
|
|
425
|
+
</Typography.Text>
|
|
426
|
+
</div>
|
|
427
|
+
<Button
|
|
428
|
+
type="text"
|
|
429
|
+
size="small"
|
|
430
|
+
icon={<DelIcon />}
|
|
431
|
+
onClick={() => removeFile(index)}
|
|
432
|
+
danger
|
|
433
|
+
/>
|
|
434
|
+
</div>
|
|
435
|
+
))}
|
|
436
|
+
</div>
|
|
437
|
+
</div>
|
|
438
|
+
)}
|
|
439
|
+
</Modal>
|
|
440
|
+
);
|
|
441
|
+
};
|
|
442
|
+
|
|
267
443
|
export const ChatInput = React.forwardRef<
|
|
268
444
|
{ focus: () => void },
|
|
269
445
|
ChatInputProps
|
|
@@ -276,35 +452,35 @@ export const ChatInput = React.forwardRef<
|
|
|
276
452
|
sending = false,
|
|
277
453
|
rows = 6,
|
|
278
454
|
agentList = DEFAULT_AGENT_LIST,
|
|
279
|
-
title = "",
|
|
280
455
|
agentId = null,
|
|
281
456
|
attachments = DEFAULT_ATTACHMENTS,
|
|
282
457
|
onInput,
|
|
283
458
|
onSubmit,
|
|
284
|
-
onUpdateTitle,
|
|
285
459
|
onUpdateAgentId,
|
|
286
460
|
onUpdateAttachments,
|
|
461
|
+
onUploading,
|
|
462
|
+
style = {},
|
|
287
463
|
},
|
|
288
464
|
ref
|
|
289
465
|
) => {
|
|
290
466
|
const inputRef = useRef<any>(null);
|
|
291
|
-
const [
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
name: string;
|
|
298
|
-
} | null>(null);
|
|
467
|
+
const [selectedAgent, setSelectedAgent] = useState<
|
|
468
|
+
{
|
|
469
|
+
id: string | number;
|
|
470
|
+
name: string;
|
|
471
|
+
}[]
|
|
472
|
+
>([]);
|
|
299
473
|
const [localValue, setLocalValue] = useState(value || "");
|
|
300
474
|
const [lastSubmitAt, setLastSubmitAt] = useState(0);
|
|
301
475
|
const minIntervalMs = 400;
|
|
302
|
-
const [
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
476
|
+
const [localAttachments, setLocalAttachments] = useState<
|
|
477
|
+
{
|
|
478
|
+
id: string;
|
|
479
|
+
name: string;
|
|
480
|
+
size: number;
|
|
481
|
+
}[]
|
|
482
|
+
>(Array.isArray(attachments) ? attachments : []);
|
|
483
|
+
const [uploadModalOpen, setUploadModalOpen] = useState<boolean>(false);
|
|
308
484
|
const isMobile = useMemo(() => false, []);
|
|
309
485
|
useImperativeHandle(ref, () => ({
|
|
310
486
|
focus() {
|
|
@@ -315,37 +491,22 @@ export const ChatInput = React.forwardRef<
|
|
|
315
491
|
useEffect(() => {
|
|
316
492
|
onInput && onInput(localValue);
|
|
317
493
|
}, [localValue]);
|
|
318
|
-
|
|
319
|
-
useEffect(() => {
|
|
320
|
-
setLocalAgentId(agentId || null);
|
|
321
|
-
setSelectedAgent(
|
|
322
|
-
agentId ? agentList.find((s) => s.id === agentId) || null : null
|
|
323
|
-
);
|
|
324
|
-
}, [agentId, agentList]);
|
|
494
|
+
|
|
325
495
|
useEffect(
|
|
326
496
|
() => setLocalAttachments(Array.isArray(attachments) ? attachments : []),
|
|
327
497
|
[attachments]
|
|
328
498
|
);
|
|
499
|
+
|
|
329
500
|
const canSubmit = !!(localValue && localValue.trim());
|
|
330
|
-
|
|
331
|
-
if (!localAgentId) return null;
|
|
332
|
-
return agentList.find((it) => it.id === localAgentId) || null;
|
|
333
|
-
}, [localAgentId, agentList]);
|
|
334
|
-
const onTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
335
|
-
const v = e.target.value;
|
|
336
|
-
setLocalTitle(v);
|
|
337
|
-
onUpdateTitle && onUpdateTitle(v);
|
|
338
|
-
};
|
|
501
|
+
|
|
339
502
|
const onAgentSelect = (item: { id: string | number; name: string }) => {
|
|
340
503
|
const id = item.id || null;
|
|
341
|
-
setLocalAgentId(id);
|
|
342
504
|
onUpdateAgentId && onUpdateAgentId(id);
|
|
343
|
-
setSelectedAgent(item);
|
|
505
|
+
setSelectedAgent((prev) => [...prev, item]);
|
|
344
506
|
};
|
|
345
|
-
const
|
|
346
|
-
setLocalAgentId(null);
|
|
507
|
+
const onRemoveAgent = (index: number) => {
|
|
347
508
|
onUpdateAgentId && onUpdateAgentId(null);
|
|
348
|
-
setSelectedAgent(
|
|
509
|
+
setSelectedAgent((prev) => prev.filter((_, i) => i !== index));
|
|
349
510
|
};
|
|
350
511
|
const onKeydown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
351
512
|
if (e.key === "Enter") {
|
|
@@ -356,52 +517,30 @@ export const ChatInput = React.forwardRef<
|
|
|
356
517
|
submit();
|
|
357
518
|
}
|
|
358
519
|
};
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
});
|
|
365
|
-
const result: Record<string, string> = {};
|
|
366
|
-
workbook.SheetNames.forEach((name) => {
|
|
367
|
-
const sheet = workbook.Sheets[name];
|
|
368
|
-
const csv = XLSX.utils.sheet_to_csv(sheet);
|
|
369
|
-
result[name] = csv;
|
|
370
|
-
});
|
|
371
|
-
return result;
|
|
372
|
-
};
|
|
373
|
-
const beforeUploadXlsx = (file: File) => {
|
|
374
|
-
const mimeOk =
|
|
375
|
-
file.type ===
|
|
376
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
|
|
377
|
-
!file.type;
|
|
378
|
-
const extOk = (file.name || "").toLowerCase().endsWith(".xlsx");
|
|
379
|
-
if (!mimeOk || !extOk) {
|
|
380
|
-
return Upload.LIST_IGNORE;
|
|
381
|
-
}
|
|
382
|
-
const reader = new FileReader();
|
|
383
|
-
reader.onload = async (e) => {
|
|
520
|
+
const handleUploadFiles = async (files: File[]) => {
|
|
521
|
+
setUploadModalOpen(false);
|
|
522
|
+
if (!onUploading || !files.length) return;
|
|
523
|
+
const newAttachments: typeof localAttachments = [];
|
|
524
|
+
for (const file of files) {
|
|
384
525
|
try {
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
{
|
|
390
|
-
uid: `${Date.now()}_${Math.random()}`,
|
|
526
|
+
const res = await onUploading(file);
|
|
527
|
+
if (res) {
|
|
528
|
+
newAttachments.push({
|
|
529
|
+
id: res.id,
|
|
391
530
|
name: file.name,
|
|
392
531
|
size: (file as any).size,
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
} catch (err) {
|
|
535
|
+
console.error(err);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
setLocalAttachments([...localAttachments, ...newAttachments]);
|
|
539
|
+
onUpdateAttachments &&
|
|
540
|
+
onUpdateAttachments([...localAttachments, ...newAttachments]);
|
|
402
541
|
};
|
|
403
|
-
const removeAttachment = (
|
|
404
|
-
const next = localAttachments.filter((a) => a.
|
|
542
|
+
const removeAttachment = (id: string) => {
|
|
543
|
+
const next = localAttachments.filter((a) => a.id !== id);
|
|
405
544
|
setLocalAttachments(next);
|
|
406
545
|
onUpdateAttachments && onUpdateAttachments(next);
|
|
407
546
|
};
|
|
@@ -413,19 +552,19 @@ export const ChatInput = React.forwardRef<
|
|
|
413
552
|
const text = (localValue || "").trim();
|
|
414
553
|
if (!text) return;
|
|
415
554
|
const data = {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
),
|
|
555
|
+
message: text,
|
|
556
|
+
agents: selectedAgent.map((it) => ({ id: it.id, name: it.name })),
|
|
557
|
+
files: localAttachments.map((file) => ({
|
|
558
|
+
id: file.id,
|
|
559
|
+
name: file.name,
|
|
560
|
+
extension: file.name.substring(file.name.lastIndexOf(".")),
|
|
561
|
+
})),
|
|
423
562
|
};
|
|
424
563
|
onSubmit && onSubmit(data);
|
|
425
564
|
setLocalValue("");
|
|
426
565
|
};
|
|
427
566
|
const overlay = (
|
|
428
|
-
<
|
|
567
|
+
<AgentListDropdownContent>
|
|
429
568
|
<ul className="agent-list">
|
|
430
569
|
{agentList.map((item) => (
|
|
431
570
|
<li key={String(item.id)}>
|
|
@@ -433,51 +572,50 @@ export const ChatInput = React.forwardRef<
|
|
|
433
572
|
</li>
|
|
434
573
|
))}
|
|
435
574
|
</ul>
|
|
436
|
-
</
|
|
575
|
+
</AgentListDropdownContent>
|
|
437
576
|
);
|
|
438
577
|
return (
|
|
439
|
-
<Wrapper>
|
|
578
|
+
<Wrapper style={style}>
|
|
440
579
|
<div className="chat-input">
|
|
441
580
|
<div className="input-stack">
|
|
442
581
|
{localAttachments.length > 0 && (
|
|
443
582
|
<div className="attachment-bar">
|
|
444
583
|
{localAttachments.map((a) => (
|
|
445
|
-
<
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
584
|
+
<div key={a.id} className="attachment-card">
|
|
585
|
+
{a.name.endsWith(".xlsx") ? (
|
|
586
|
+
<IconFont type="icon-excel" />
|
|
587
|
+
) : (
|
|
588
|
+
<IconFont type="icon-audio" />
|
|
589
|
+
)}
|
|
590
|
+
|
|
591
|
+
<Typography.Text
|
|
592
|
+
className="name"
|
|
593
|
+
ellipsis={{ tooltip: a.name }}
|
|
594
|
+
>
|
|
595
|
+
{a.name}
|
|
596
|
+
</Typography.Text>
|
|
454
597
|
<a
|
|
455
598
|
className="remove"
|
|
456
|
-
onClick={() => removeAttachment(a.
|
|
599
|
+
onClick={() => removeAttachment(a.id)}
|
|
457
600
|
>
|
|
458
|
-
|
|
601
|
+
<DeleteOutlined />
|
|
459
602
|
</a>
|
|
460
|
-
</
|
|
603
|
+
</div>
|
|
461
604
|
))}
|
|
462
605
|
</div>
|
|
463
606
|
)}
|
|
464
|
-
{selectedAgent && (
|
|
607
|
+
{selectedAgent.length > 0 && (
|
|
465
608
|
<div className="agent-info">
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
609
|
+
{selectedAgent.map((item, index) => (
|
|
610
|
+
<div key={String(item.id)}>
|
|
611
|
+
<span className="name">@ {item.name}</span>
|
|
612
|
+
<a className="remove" onClick={() => onRemoveAgent(index)}>
|
|
613
|
+
×
|
|
614
|
+
</a>
|
|
615
|
+
</div>
|
|
616
|
+
))}
|
|
470
617
|
</div>
|
|
471
618
|
)}
|
|
472
|
-
{showTitle && (
|
|
473
|
-
<Input
|
|
474
|
-
className="title-input"
|
|
475
|
-
placeholder="请输入报告标题(可选)"
|
|
476
|
-
disabled={disabled || sending}
|
|
477
|
-
value={localTitle}
|
|
478
|
-
onChange={onTitleChange}
|
|
479
|
-
/>
|
|
480
|
-
)}
|
|
481
619
|
<Input.TextArea
|
|
482
620
|
ref={inputRef}
|
|
483
621
|
className="input textarea-stacked"
|
|
@@ -491,43 +629,47 @@ export const ChatInput = React.forwardRef<
|
|
|
491
629
|
</div>
|
|
492
630
|
<div className="footer">
|
|
493
631
|
<div className="left">
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
632
|
+
{!!agentList?.length && (
|
|
633
|
+
<Dropdown
|
|
634
|
+
trigger={["click"]}
|
|
635
|
+
popupRender={() => overlay}
|
|
636
|
+
placement="topLeft"
|
|
637
|
+
styles={{
|
|
638
|
+
root: {
|
|
639
|
+
boxShadow: "var(--ant-box-shadow-card)",
|
|
640
|
+
},
|
|
641
|
+
}}
|
|
642
|
+
>
|
|
643
|
+
<Tooltip title="选择助理">
|
|
644
|
+
<Button
|
|
645
|
+
className="agent-trigger"
|
|
646
|
+
onClick={(e) => e.preventDefault()}
|
|
647
|
+
>
|
|
648
|
+
<IconFont type="icon-at" />
|
|
649
|
+
</Button>
|
|
650
|
+
</Tooltip>
|
|
651
|
+
</Dropdown>
|
|
652
|
+
)}
|
|
653
|
+
{onUploading && (
|
|
654
|
+
<>
|
|
655
|
+
<Tooltip title="上传文件">
|
|
656
|
+
<Button
|
|
657
|
+
className="attach-upload"
|
|
658
|
+
onClick={() => setUploadModalOpen(true)}
|
|
659
|
+
disabled={disabled || sending}
|
|
660
|
+
>
|
|
661
|
+
<IconFont type="icon-paper-clip" />
|
|
662
|
+
</Button>
|
|
663
|
+
</Tooltip>
|
|
664
|
+
<FileUploadModal
|
|
665
|
+
open={uploadModalOpen}
|
|
666
|
+
onCancel={() => setUploadModalOpen(false)}
|
|
667
|
+
onOk={handleUploadFiles}
|
|
668
|
+
allowedExtensions={ALLOWED_EXTENSIONS}
|
|
669
|
+
allowedMimeTypes={ALLOWED_MIME_TYPES}
|
|
528
670
|
/>
|
|
529
|
-
|
|
530
|
-
|
|
671
|
+
</>
|
|
672
|
+
)}
|
|
531
673
|
</div>
|
|
532
674
|
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
|
|
533
675
|
{!isMobile && (
|