bi-sdk-react 0.0.4 → 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.
Files changed (45) hide show
  1. package/dist/es/css/bi-sdk.css +1 -1
  2. package/dist/es/js/bi-sdk.es.js +270 -63
  3. package/dist/types/components/PageDesigner.d.ts +9 -1
  4. package/dist/types/components/context/DesignerContext.d.ts +5 -2
  5. package/dist/types/components/context/EnvContext.d.ts +2 -1
  6. package/dist/types/components/icon/IconFont.d.ts +2 -1
  7. package/dist/types/components/layout/PageCanvas.d.ts +2 -0
  8. package/dist/types/components/panel/AiPanel.d.ts +4 -0
  9. package/dist/types/components/panel/ChatInput.d.ts +13 -6
  10. package/dist/types/components/panel/DatasetPanel.d.ts +11 -0
  11. package/dist/types/components/panel/PaneHeader.d.ts +1 -0
  12. package/dist/types/components/panel/PropertiesPanel.d.ts +3 -1
  13. package/dist/types/components/plugins/@antd/item-props/EchartsProps.d.ts +2 -2
  14. package/dist/types/components/plugins/@antd/item-props/TextProps.d.ts +1 -0
  15. package/dist/types/components/plugins/@antd/items/TableRender.d.ts +1 -0
  16. package/dist/types/components/plugins/@antd/items/TextRender.d.ts +1 -0
  17. package/dist/types/components/typing.d.ts +97 -2
  18. package/dist/types/components/utils.d.ts +1 -0
  19. package/dist/umd/css/bi-sdk.css +1 -1
  20. package/dist/umd/js/bi-sdk.umd.min.js +274 -67
  21. package/package.json +1 -1
  22. package/src/components/PageDesigner.tsx +229 -36
  23. package/src/components/context/DesignerContext.tsx +15 -3
  24. package/src/components/context/EnvContext.tsx +4 -1
  25. package/src/components/icon/IconFont.tsx +15 -11
  26. package/src/components/layout/PageCanvas.tsx +4 -2
  27. package/src/components/layout/PageItem.tsx +1 -1
  28. package/src/components/panel/AiPanel.tsx +609 -43
  29. package/src/components/panel/ChatInput.tsx +259 -147
  30. package/src/components/panel/DatasetPanel.tsx +65 -0
  31. package/src/components/panel/PaneHeader.tsx +3 -2
  32. package/src/components/panel/PropertiesPanel.tsx +332 -125
  33. package/src/components/plugins/@antd/index.ts +12 -8
  34. package/src/components/plugins/@antd/item-props/EchartsProps.tsx +52 -22
  35. package/src/components/plugins/@antd/item-props/HtmlProps.tsx +8 -9
  36. package/src/components/plugins/@antd/item-props/TextProps.tsx +13 -1
  37. package/src/components/plugins/@antd/items/EchartsRender.tsx +9 -1
  38. package/src/components/plugins/@antd/items/HtmlRender.tsx +13 -1
  39. package/src/components/plugins/@antd/items/ListRender.tsx +18 -1
  40. package/src/components/plugins/@antd/items/TableRender.tsx +16 -1
  41. package/src/components/plugins/@antd/items/TextRender.tsx +3 -1
  42. package/src/components/styles.css +20 -0
  43. package/src/components/typing.ts +111 -2
  44. package/src/components/utils.ts +40 -0
  45. package/src/example.tsx +314 -13
@@ -1,12 +1,42 @@
1
- import React from "react";
1
+ import {
2
+ DeleteOutlined,
3
+ HistoryOutlined,
4
+ MessageOutlined,
5
+ ReloadOutlined,
6
+ RobotOutlined,
7
+ } from "@ant-design/icons";
8
+ import {
9
+ Button,
10
+ Card,
11
+ Dropdown,
12
+ Modal,
13
+ Popconfirm,
14
+ Skeleton,
15
+ Space,
16
+ Tag,
17
+ Tooltip,
18
+ } from "antd";
19
+ import React, {
20
+ forwardRef,
21
+ useContext,
22
+ useEffect,
23
+ useImperativeHandle,
24
+ useRef,
25
+ useState,
26
+ } from "react";
2
27
  import styled from "styled-components";
3
- import { Tooltip, Space, Card } from "antd";
4
- import { HistoryOutlined, MessageOutlined } from "@ant-design/icons";
5
- import { PaneHeader } from "./PaneHeader";
28
+ import { DesignerContext } from "../context/DesignerContext";
29
+ import { IconFont } from "../icon/IconFont";
30
+ import { ChatConversationType, ChatMessageType, ChatResponseAnswerEffectType, ChatResponseAnswerType } from "../typing";
31
+ import { formatTime, uuid } from "../utils";
6
32
  import { ChatInput } from "./ChatInput";
33
+ import { PaneHeader } from "./PaneHeader";
7
34
 
8
35
  export type AiPanelProps = {
9
36
  agentList?: { id: string; name: string }[];
37
+ headExtra?: React.ReactNode;
38
+ style?: React.CSSProperties;
39
+ onEffect?: (effect: ChatResponseAnswerEffectType) => void;
10
40
  };
11
41
 
12
42
  const Root = styled.div`
@@ -18,7 +48,7 @@ const Root = styled.div`
18
48
  display: flex;
19
49
  flex-direction: column;
20
50
  gap: 12px;
21
- padding: 12px;
51
+ padding: 0 !important;
22
52
  }
23
53
  .conversation-list {
24
54
  margin: 0;
@@ -54,63 +84,599 @@ const Root = styled.div`
54
84
 
55
85
  const DEFAULT_AGENT_LIST: any[] = [];
56
86
 
87
+ const BIZ_TYPE = "bi-design";
88
+
57
89
  export const AiPanel: React.FC<AiPanelProps> = ({
58
90
  agentList = DEFAULT_AGENT_LIST,
91
+ headExtra,
92
+ style = {},
93
+ onEffect,
59
94
  }) => {
95
+ const { pageId, fetch } = useContext(DesignerContext);
96
+ if (!fetch) return null;
97
+ const [conversationId, setConversationId] = useState<string | null>(null);
98
+ const [conversationList, setConversationList] = useState<
99
+ ChatConversationType[]
100
+ >([]);
101
+ const [messageList, setMessageList] = useState<ChatMessageType[]>([]);
102
+ const messageListRef = React.useRef<MessageListRef>(null);
103
+
104
+ const scrollToBottom = () => {
105
+ setTimeout(() => {
106
+ if (messageListRef.current) {
107
+ messageListRef.current.scrollToBottom();
108
+ }
109
+ }, 100);
110
+ };
111
+
112
+ const handleNewConversationClick = () => {
113
+ setConversationId(null);
114
+ setMessageList([]);
115
+ };
116
+ const handleDeleteConversation = async (id: string) => {
117
+ Modal.confirm({
118
+ title: "确认删除该对话吗?",
119
+ okText: "确认",
120
+ cancelText: "取消",
121
+ onOk: async () => {
122
+ await fetch.ai?.removeConversation(BIZ_TYPE, pageId, id);
123
+ setConversationList((list) => list.filter((i) => i.id !== id));
124
+ setMessageList((list) => list.filter((m) => m.conversationId !== id));
125
+ if (conversationId === id) {
126
+ handleNewConversationClick();
127
+ }
128
+ },
129
+ });
130
+ };
131
+
132
+ const handleActivateConversation = async (id: string) => {
133
+ setConversationId(id);
134
+ setConversationList((list) =>
135
+ list.map((i) => ({ ...i, isActived: i.id === id }))
136
+ );
137
+ const messages = await fetch.ai?.messageList(BIZ_TYPE, pageId, id);
138
+ setMessageList(messages || []);
139
+ };
140
+
141
+ const handleSendMessage = async (data: {
142
+ message: string;
143
+ agents?: { id: string | number; name: string }[] | null;
144
+ files?: { id: string; name: string; extension: string }[] | null;
145
+ }) => {
146
+ const questionMessage = {
147
+ id: uuid(),
148
+ conversationId: conversationId || "",
149
+ question: data.message,
150
+ createdAt: new Date().toISOString(),
151
+ files: data.files || [],
152
+ agents: data.agents || [],
153
+ sending: true,
154
+ } as ChatMessageType;
155
+ setMessageList((list) => [...list, questionMessage]);
156
+ scrollToBottom();
157
+
158
+ const { id, answer, createdAt, conversation } = await fetch.ai?.chat(
159
+ BIZ_TYPE,
160
+ pageId,
161
+ conversationId,
162
+ data
163
+ )!;
164
+ setConversationId(conversation?.id || "");
165
+ setConversationList((list) => [
166
+ ...list,
167
+ conversation || ({} as ChatConversationType),
168
+ ]);
169
+ Object.assign(questionMessage, {
170
+ id,
171
+ answer,
172
+ createdAt,
173
+ conversationId: conversation?.id || "",
174
+ sending: false,
175
+ });
176
+ if (answer.effect) {
177
+ onEffect?.(answer.effect);
178
+ }
179
+ setMessageList((list) => [...list]);
180
+ scrollToBottom();
181
+ };
182
+
183
+ const handleDeleteMessage = async (id: string) => {
184
+ await fetch.ai?.removeMessage(BIZ_TYPE, pageId, id);
185
+ setMessageList((list) => list.filter((i) => i.id !== id));
186
+ };
187
+
188
+ const groups = (() => {
189
+ const now = new Date();
190
+ const isSameDay = (d1: Date, d2: Date) =>
191
+ d1.getFullYear() === d2.getFullYear() &&
192
+ d1.getMonth() === d2.getMonth() &&
193
+ d1.getDate() === d2.getDate();
194
+ const yesterday = new Date(now);
195
+ yesterday.setDate(now.getDate() - 1);
196
+ const isSameMonth = (d1: Date, d2: Date) =>
197
+ d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth();
198
+ const todayList: ChatConversationType[] = [];
199
+ const yesterdayList: ChatConversationType[] = [];
200
+ const monthList: ChatConversationType[] = [];
201
+ const oldList: ChatConversationType[] = [];
202
+ conversationList.forEach((c) => {
203
+ const d = new Date(c.createdAt);
204
+ if (isSameDay(d, now)) todayList.push(c);
205
+ else if (isSameDay(d, yesterday)) yesterdayList.push(c);
206
+ else if (isSameMonth(d, now)) monthList.push(c);
207
+ else oldList.push(c);
208
+ });
209
+ return [
210
+ { title: "今天", items: todayList },
211
+ { title: "昨天", items: yesterdayList },
212
+ { title: "本月内", items: monthList },
213
+ { title: "很久以前", items: oldList },
214
+ ].filter((g) => g.items.length > 0);
215
+ })();
216
+
217
+ const init = async () => {
218
+ try {
219
+ const list = await fetch.ai?.conversationList(BIZ_TYPE, pageId);
220
+ setConversationList(list || []);
221
+ const active = list?.find((i) => i.isActived);
222
+ if (active) {
223
+ setConversationId(active.id);
224
+ const messages = await fetch.ai?.messageList(
225
+ BIZ_TYPE,
226
+ pageId,
227
+ active.id
228
+ );
229
+ setMessageList(messages || []);
230
+ }
231
+ } catch (error) {
232
+ console.error(error);
233
+ }
234
+ };
235
+
236
+ useEffect(() => {
237
+ init();
238
+ }, [fetch]);
239
+
240
+ useEffect(() => {
241
+ scrollToBottom();
242
+ }, []);
243
+
60
244
  return (
61
- <Root>
245
+ <Root style={style}>
62
246
  <PaneHeader
63
247
  title="智能助理"
64
248
  extra={
65
249
  <Space>
66
250
  <Tooltip title="新对话" placement="topRight">
67
- <a>
68
- <MessageOutlined />
251
+ <a onClick={handleNewConversationClick}>
252
+ <IconFont type="icon-chat-plus" />
69
253
  </a>
70
254
  </Tooltip>
71
- <Tooltip title="历史记录" placement="topRight">
255
+ <Dropdown
256
+ trigger={["click"]}
257
+ styles={{
258
+ root: {
259
+ boxShadow: "var(--ant-box-shadow-drawer-up)",
260
+ backgroundColor: "#ffffff",
261
+ borderRadius: "var(--ant-border-radius)",
262
+ },
263
+ }}
264
+ popupRender={() => (
265
+ <HistoryDropdownPanel>
266
+ {groups.map((g) => (
267
+ <div className="group" key={g.title}>
268
+ <div className="group-title">{g.title}</div>
269
+ <ul className="group-list">
270
+ {g.items.map((item) => (
271
+ <li
272
+ key={item.id}
273
+ className={item.isActived ? "actived" : ""}
274
+ >
275
+ <a
276
+ onClick={() =>
277
+ handleActivateConversation(item.id)
278
+ }
279
+ >
280
+ {item.name}
281
+ </a>
282
+ <small>
283
+ <a
284
+ onClick={() =>
285
+ handleDeleteConversation(item.id)
286
+ }
287
+ >
288
+ <DeleteOutlined />
289
+ </a>
290
+ </small>
291
+ </li>
292
+ ))}
293
+ </ul>
294
+ </div>
295
+ ))}
296
+ </HistoryDropdownPanel>
297
+ )}
298
+ placement="topRight"
299
+ >
72
300
  <a>
73
301
  <HistoryOutlined />
74
302
  </a>
75
- </Tooltip>
303
+ </Dropdown>
304
+ {headExtra}
76
305
  </Space>
77
306
  }
307
+ style={{ width: "initial" }}
78
308
  />
79
309
  <div className="body">
80
- <div style={{ flex: "1 1 auto" }} />
81
- <Card
82
- title="历史记录"
83
- size="small"
84
- variant="borderless"
85
- styles={{
86
- header: {
87
- padding: 0,
88
- border: "none",
89
- },
90
- body: {
91
- padding: 0,
92
- },
93
- }}
94
- >
95
- <ul className="conversation-list">
96
- <li>
97
- <a>
98
- <MessageOutlined />
99
- 你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好
100
- </a>
101
- <small>刚刚</small>
102
- </li>
103
- <li>
104
- <a>
105
- <MessageOutlined />
106
- 你好
107
- </a>
108
- <small>刚刚</small>
109
- </li>
110
- </ul>
111
- </Card>
112
- <ChatInput agentList={agentList} />
310
+ <div style={{ flex: "1 1 auto", overflowY: "auto" }}>
311
+ {messageList.length ? (
312
+ <MessageList
313
+ ref={messageListRef}
314
+ list={messageList}
315
+ onDeleteMessage={handleDeleteMessage}
316
+ />
317
+ ) : (
318
+ <EmptyMessage />
319
+ )}
320
+ </div>
321
+ {messageList.length === 0 && (
322
+ <Card
323
+ title="历史记录"
324
+ size="small"
325
+ variant="borderless"
326
+ styles={{
327
+ header: {
328
+ padding: 0,
329
+ border: "none",
330
+ },
331
+ body: {
332
+ padding: 0,
333
+ },
334
+ }}
335
+ style={{ padding: "12px 12px 0 12px" }}
336
+ >
337
+ <ul className="conversation-list">
338
+ {conversationList.slice(0, 3).map((item) => (
339
+ <li key={item.id}>
340
+ <a>
341
+ <MessageOutlined />
342
+ {item.name}
343
+ </a>
344
+ <small>{formatTime(item.createdAt)}</small>
345
+ </li>
346
+ ))}
347
+ </ul>
348
+ </Card>
349
+ )}
350
+ <ChatInput
351
+ agentList={agentList}
352
+ style={{ padding: 12 }}
353
+ onUploading={fetch?.upload}
354
+ onSubmit={handleSendMessage}
355
+ />
113
356
  </div>
114
357
  </Root>
115
358
  );
116
359
  };
360
+
361
+ const EmptyRoot = styled.div`
362
+ flex: 1 1 auto;
363
+ display: flex;
364
+ align-items: center;
365
+ justify-content: center;
366
+ height: 100%;
367
+ padding: 12px;
368
+
369
+ .content {
370
+ display: flex;
371
+ flex-direction: column;
372
+ align-items: center;
373
+ gap: 12px;
374
+ text-align: center;
375
+ }
376
+
377
+ .logo {
378
+ width: 64px;
379
+ height: 64px;
380
+ border-radius: 8px;
381
+ display: flex;
382
+ align-items: center;
383
+ justify-content: center;
384
+ // background: var(--link-bg-color);
385
+ // border: 1px solid var(--ant-color-border);
386
+ color: var(--ant-color-info-text-active);
387
+ }
388
+
389
+ .title {
390
+ font-size: 18px;
391
+ font-weight: 600;
392
+ color: var(--ant-color-text);
393
+ }
394
+
395
+ .desc {
396
+ font-size: 14px;
397
+ color: var(--ant-color-text-secondary);
398
+ }
399
+ `;
400
+
401
+ const EmptyMessage: React.FC = () => {
402
+ return (
403
+ <EmptyRoot>
404
+ <div className="content">
405
+ <div className="logo">
406
+ <IconFont type="icon-ai-compute" style={{ fontSize: 60 }} />
407
+ </div>
408
+ <div className="title">与 智能助理 协作</div>
409
+ <div className="desc">
410
+ 自动化执行常规开发任务,端到端高效完成,提高交付效率
411
+ </div>
412
+ </div>
413
+ </EmptyRoot>
414
+ );
415
+ };
416
+
417
+ const HistoryDropdownPanel = styled.div`
418
+ width: 260px;
419
+ padding: 8px;
420
+ .group {
421
+ display: flex;
422
+ flex-direction: column;
423
+ gap: 6px;
424
+ margin-bottom: 6px;
425
+ }
426
+ .group-title {
427
+ color: var(--ant-color-text-secondary);
428
+ font-size: 12px;
429
+ }
430
+ .group-list {
431
+ margin: 0;
432
+ padding: 0;
433
+ list-style: none;
434
+ display: flex;
435
+ flex-direction: column;
436
+ gap: 6px;
437
+ }
438
+ .group-list li {
439
+ display: flex;
440
+ align-items: center;
441
+ justify-content: space-between;
442
+ gap: 6px;
443
+ padding: 6px 8px;
444
+ border-radius: 6px;
445
+ background: var(--link-bg-color);
446
+ cursor: pointer;
447
+ font-size: 13px;
448
+ }
449
+ .group-list li.actived {
450
+ background: var(--ant-color-primary-bg);
451
+ }
452
+ .group-list li a {
453
+ color: var(--ant-color-text);
454
+ flex: auto;
455
+ overflow: hidden;
456
+ text-overflow: ellipsis;
457
+ white-space: nowrap;
458
+ }
459
+ .group-list li small a {
460
+ color: var(--ant-color-error);
461
+ }
462
+ `;
463
+
464
+ const MessageRoot = styled.div`
465
+ height: 100%;
466
+ display: flex;
467
+ flex-direction: column;
468
+ gap: 12px;
469
+ padding-right: 8px;
470
+ overflow: auto;
471
+ scrollbar-width: none;
472
+ &::-webkit-scrollbar {
473
+ width: 0;
474
+ height: 0;
475
+ }
476
+ &:hover {
477
+ scrollbar-width: thin;
478
+ }
479
+ &:hover::-webkit-scrollbar {
480
+ width: 8px;
481
+ height: 8px;
482
+ }
483
+ &:hover::-webkit-scrollbar-thumb {
484
+ background: var(--ant-color-border);
485
+ border-radius: 4px;
486
+ }
487
+ &:hover::-webkit-scrollbar-track {
488
+ background: transparent;
489
+ }
490
+ .msg {
491
+ display: flex;
492
+ flex-direction: column;
493
+ gap: 8px;
494
+ border-radius: var(--ant-border-radius);
495
+ // border: 1px solid var(--ant-color-border);
496
+ padding: 8px 10px;
497
+ background: var(--ant-color-bg-container);
498
+
499
+ .user-message {
500
+ background: var(--ant-color-primary-bg);
501
+ padding: 8px 10px;
502
+ max-width: 90%;
503
+ margin: 10px 0 10px auto;
504
+ position: relative;
505
+
506
+ .msg-tools {
507
+ position: absolute;
508
+ top: 0;
509
+ left: -25px;
510
+ display: none;
511
+ }
512
+
513
+ &:hover .msg-tools {
514
+ display: flex;
515
+ }
516
+
517
+ .msg-header {
518
+ justify-content: flex-end;
519
+ color: var(--ant-color-text-tertiary);
520
+ padding: 4px 0 8px 0;
521
+ }
522
+ }
523
+ .assistant-message {
524
+ .msg-header .msg-title {
525
+ color: var(--ant-color-primary);
526
+ padding: 8px 0;
527
+ }
528
+ }
529
+ }
530
+ .msg-header {
531
+ display: flex;
532
+ align-items: center;
533
+ justify-content: space-between;
534
+ color: var(--ant-color-text-secondary);
535
+ font-size: 12px;
536
+ }
537
+ .msg-title {
538
+ font-weight: 600;
539
+ color: var(--ant-color-text);
540
+ }
541
+ .msg-content {
542
+ color: var(--ant-color-text);
543
+ font-size: 13px;
544
+ line-height: 1.6;
545
+ white-space: pre-wrap;
546
+ }
547
+ .msg-meta {
548
+ display: flex;
549
+ align-items: center;
550
+ gap: 0 8px;
551
+ flex-wrap: wrap;
552
+
553
+ .ant-tag {
554
+ padding-inline: 0;
555
+ }
556
+ }
557
+ .msg-section {
558
+ display: flex;
559
+ flex-direction: column;
560
+ gap: 6px;
561
+ }
562
+ .extra-list {
563
+ display: flex;
564
+ flex-direction: column;
565
+ gap: 4px;
566
+ }
567
+ .extra-item {
568
+ // display: flex;
569
+ // align-items: center;
570
+ // gap: 6px;
571
+ font-size: 12px;
572
+ color: var(--ant-color-text-secondary);
573
+
574
+ .ant-tag {
575
+ margin-right: 6px;
576
+ }
577
+ }
578
+ `;
579
+
580
+ type MessageListRef = {
581
+ scrollToBottom: () => void;
582
+ };
583
+
584
+ const MessageList = forwardRef<
585
+ MessageListRef,
586
+ { list: ChatMessageType[]; onDeleteMessage: (id: string) => void }
587
+ >(({ list, onDeleteMessage }, forwardRef) => {
588
+ const ref = useRef<HTMLDivElement>(null);
589
+
590
+ const scrollToBottom = () => {
591
+ setTimeout(() => {
592
+ if (ref.current) {
593
+ ref.current.scrollTop = ref.current.scrollHeight;
594
+ }
595
+ }, 100);
596
+ };
597
+
598
+ useImperativeHandle(forwardRef, () => ({
599
+ scrollToBottom,
600
+ }));
601
+
602
+ return (
603
+ <MessageRoot className="message-list" ref={ref}>
604
+ {list.map((msg) => (
605
+ <div className="msg" key={msg.id}>
606
+ <div className="user-message">
607
+ <Space size={4} orientation="vertical" className="msg-tools">
608
+ <Popconfirm
609
+ title="确认删除吗?"
610
+ okText="确认"
611
+ cancelText="取消"
612
+ onConfirm={() => onDeleteMessage(msg.id)}
613
+ >
614
+ <Tooltip title="删除">
615
+ <Button type="link" size="small" icon={<DeleteOutlined />} />
616
+ </Tooltip>
617
+ </Popconfirm>
618
+ {/* <Tooltip title="重新发送">
619
+ <Button type="link" size="small" icon={<ReloadOutlined />} />
620
+ </Tooltip> */}
621
+ </Space>
622
+ <div className="msg-header">
623
+ {/* <span className="msg-title">用户</span> */}
624
+ <span>{formatTime(msg.createdAt)}</span>
625
+ </div>
626
+ <div className="msg-content">{msg.question}</div>
627
+ {msg.files?.length || msg.agents?.length ? (
628
+ <div className="msg-meta">
629
+ {msg.files?.map((f) => (
630
+ <Tag key={f.id} color="processing">
631
+ <IconFont type="icon-paper-clip" /> {f.name}
632
+ </Tag>
633
+ ))}
634
+ {msg.agents?.map((a) => (
635
+ <Tag key={a.id} color="blue">
636
+ <IconFont type="icon-at" /> {a.name}
637
+ </Tag>
638
+ ))}
639
+ </div>
640
+ ) : null}
641
+ </div>
642
+ {/* <div
643
+ style={{
644
+ height: 1,
645
+ background: "var(--ant-color-border)",
646
+ opacity: 0.5,
647
+ }}
648
+ /> */}
649
+ <div className="assistant-message">
650
+ <div className="msg-header">
651
+ <span className="msg-title">
652
+ <RobotOutlined /> 智能助理
653
+ </span>
654
+ {/* <span>{msg.createdAt}</span> */}
655
+ </div>
656
+ <div className="msg-section">
657
+ {msg.sending ? (
658
+ <Skeleton active />
659
+ ) : (
660
+ <>
661
+ <div className="msg-content">{msg.answer?.answer || ""}</div>
662
+ {!!msg.answer?.extra?.length && (
663
+ <div className="extra-list">
664
+ {msg.answer.extra.map((e: any, idx: number) => (
665
+ <div className="extra-item" key={idx}>
666
+ <Tag color="gold">{e.element}</Tag>
667
+ <span>{e.action}</span>
668
+ {/* <span>·</span>
669
+ <span>{e.area}</span> */}
670
+ </div>
671
+ ))}
672
+ </div>
673
+ )}
674
+ </>
675
+ )}
676
+ </div>
677
+ </div>
678
+ </div>
679
+ ))}
680
+ </MessageRoot>
681
+ );
682
+ });