bi-sdk-react 0.0.4 → 0.0.6

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 +296 -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 +102 -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 +300 -67
  21. package/package.json +3 -2
  22. package/src/components/PageDesigner.tsx +231 -37
  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 +656 -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 +117 -2
  44. package/src/components/utils.ts +40 -0
  45. package/src/example.tsx +344 -13
@@ -1,16 +1,16 @@
1
1
  import {
2
- DeleteOutlined,
3
- PaperClipOutlined,
4
- SendOutlined,
2
+ AudioOutlined,
3
+ DeleteOutlined as DelIcon, DeleteOutlined, FileExcelOutlined, InboxOutlined, SendOutlined
5
4
  } from "@ant-design/icons";
6
5
  import {
7
6
  Button,
8
7
  Dropdown,
9
8
  Input,
10
- Switch,
9
+ Modal,
11
10
  Tooltip,
12
11
  Typography,
13
12
  Upload,
13
+ message
14
14
  } from "antd";
15
15
  import React, {
16
16
  useEffect,
@@ -20,8 +20,8 @@ import React, {
20
20
  useState,
21
21
  } from "react";
22
22
  import styled from "styled-components";
23
- import * as XLSX from "xlsx";
24
23
  import { IconFont } from "../icon/IconFont";
24
+ import { FetchType } from "../typing";
25
25
 
26
26
  export type ChatInputProps = {
27
27
  value?: string;
@@ -30,22 +30,25 @@ export type ChatInputProps = {
30
30
  sending?: boolean;
31
31
  rows?: number;
32
32
  agentList?: { id: string | number; name: string }[];
33
- title?: string;
34
33
  agentId?: string | number | null;
35
34
  attachments?: any[];
36
35
  onInput?: (text: string) => void;
37
36
  onSubmit?: (data: {
38
- demand: string;
39
- title: string;
40
- agentIds?: (string | number)[];
41
- csvData: Record<string, string>;
37
+ message: string;
38
+ agents?: { id: string | number; name: string }[] | null;
39
+ files?: { id: string; name: string; extension: string }[] | null;
42
40
  }) => void;
43
- onUpdateTitle?: (title: string) => void;
44
41
  onUpdateAgentId?: (id: string | number | null) => void;
45
42
  onUpdateAttachments?: (list: any[]) => void;
43
+ onUploading?: FetchType["upload"];
44
+ style?: React.CSSProperties;
46
45
  };
47
46
 
48
- const Wrapper = styled.div`
47
+ const AgentListDropdownContent = styled.div`
48
+ max-height: 400px;
49
+ overflow: auto;
50
+ background: #fff;
51
+
49
52
  ul.agent-list {
50
53
  margin: 0;
51
54
  padding: 0;
@@ -70,6 +73,9 @@ const Wrapper = styled.div`
70
73
  text-overflow: ellipsis;
71
74
  white-space: nowrap;
72
75
  }
76
+ `;
77
+
78
+ const Wrapper = styled.div`
73
79
  .chat-input {
74
80
  display: flex;
75
81
  flex-direction: column;
@@ -241,21 +247,6 @@ const Wrapper = styled.div`
241
247
  .agent-trigger:hover {
242
248
  background-color: #f7f7f7;
243
249
  }
244
- .title-toggle {
245
- display: none;
246
- align-items: center;
247
- gap: 6px;
248
- background-color: #ffffff;
249
- border: solid 1px #dcdcdc;
250
- border-radius: 5px;
251
- padding: 0 4px;
252
- height: 26px;
253
- }
254
- .title-toggle .label {
255
- color: #666;
256
- font-size: 12px;
257
- user-select: none;
258
- }
259
250
  .attach-upload {
260
251
  display: inline-flex;
261
252
  align-items: center;
@@ -269,6 +260,9 @@ const Wrapper = styled.div`
269
260
  font-size: 12px;
270
261
  cursor: pointer;
271
262
  }
263
+ .ant-btn .anticon {
264
+ color: var(--ant-color-text-label);
265
+ }
272
266
  .tips {
273
267
  color: #999;
274
268
  font-size: 12px;
@@ -298,6 +292,154 @@ const Wrapper = styled.div`
298
292
  const DEFAULT_AGENT_LIST: any[] = [];
299
293
  const DEFAULT_ATTACHMENTS: any[] = [];
300
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
+
301
443
  export const ChatInput = React.forwardRef<
302
444
  { focus: () => void },
303
445
  ChatInputProps
@@ -310,19 +452,18 @@ export const ChatInput = React.forwardRef<
310
452
  sending = false,
311
453
  rows = 6,
312
454
  agentList = DEFAULT_AGENT_LIST,
313
- title = "",
314
455
  agentId = null,
315
456
  attachments = DEFAULT_ATTACHMENTS,
316
457
  onInput,
317
458
  onSubmit,
318
- onUpdateTitle,
319
459
  onUpdateAgentId,
320
460
  onUpdateAttachments,
461
+ onUploading,
462
+ style = {},
321
463
  },
322
464
  ref
323
465
  ) => {
324
466
  const inputRef = useRef<any>(null);
325
- const [localTitle, setLocalTitle] = useState(title || "");
326
467
  const [selectedAgent, setSelectedAgent] = useState<
327
468
  {
328
469
  id: string | number;
@@ -332,12 +473,14 @@ export const ChatInput = React.forwardRef<
332
473
  const [localValue, setLocalValue] = useState(value || "");
333
474
  const [lastSubmitAt, setLastSubmitAt] = useState(0);
334
475
  const minIntervalMs = 400;
335
- const [showTitle, setShowTitle] = useState(false);
336
- const acceptXlsx =
337
- ".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
338
- const [localAttachments, setLocalAttachments] = useState<any[]>(
339
- Array.isArray(attachments) ? attachments : []
340
- );
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);
341
484
  const isMobile = useMemo(() => false, []);
342
485
  useImperativeHandle(ref, () => ({
343
486
  focus() {
@@ -348,23 +491,14 @@ export const ChatInput = React.forwardRef<
348
491
  useEffect(() => {
349
492
  onInput && onInput(localValue);
350
493
  }, [localValue]);
351
- useEffect(() => setLocalTitle(title || ""), [title]);
352
- // useEffect(() => {
353
- // setSelectedAgent(
354
- // agentId ? agentList.find((s) => s.id === agentId) || [] : []
355
- // );
356
- // }, [agentId, agentList]);
494
+
357
495
  useEffect(
358
496
  () => setLocalAttachments(Array.isArray(attachments) ? attachments : []),
359
497
  [attachments]
360
498
  );
499
+
361
500
  const canSubmit = !!(localValue && localValue.trim());
362
501
 
363
- const onTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
364
- const v = e.target.value;
365
- setLocalTitle(v);
366
- onUpdateTitle && onUpdateTitle(v);
367
- };
368
502
  const onAgentSelect = (item: { id: string | number; name: string }) => {
369
503
  const id = item.id || null;
370
504
  onUpdateAgentId && onUpdateAgentId(id);
@@ -383,52 +517,30 @@ export const ChatInput = React.forwardRef<
383
517
  submit();
384
518
  }
385
519
  };
386
- const convertXlsxToCsv = async (arrayBuffer: ArrayBuffer) => {
387
- const workbook = XLSX.read(arrayBuffer, {
388
- type: "array",
389
- cellDates: true,
390
- dateNF: "yyyy-mm-dd hh:mm:ss",
391
- });
392
- const result: Record<string, string> = {};
393
- workbook.SheetNames.forEach((name) => {
394
- const sheet = workbook.Sheets[name];
395
- const csv = XLSX.utils.sheet_to_csv(sheet);
396
- result[name] = csv;
397
- });
398
- return result;
399
- };
400
- const beforeUploadXlsx = (file: File) => {
401
- const mimeOk =
402
- file.type ===
403
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
404
- !file.type;
405
- const extOk = (file.name || "").toLowerCase().endsWith(".xlsx");
406
- if (!mimeOk || !extOk) {
407
- return Upload.LIST_IGNORE;
408
- }
409
- const reader = new FileReader();
410
- 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) {
411
525
  try {
412
- const buffer = e.target?.result as ArrayBuffer;
413
- const converted = await convertXlsxToCsv(buffer);
414
- const next = [
415
- ...localAttachments,
416
- {
417
- uid: `${Date.now()}_${Math.random()}`,
526
+ const res = await onUploading(file);
527
+ if (res) {
528
+ newAttachments.push({
529
+ id: res.id,
418
530
  name: file.name,
419
531
  size: (file as any).size,
420
- csvBySheet: converted,
421
- },
422
- ];
423
- setLocalAttachments(next);
424
- onUpdateAttachments && onUpdateAttachments(next);
425
- } catch {}
426
- };
427
- reader.readAsArrayBuffer(file);
428
- return false;
532
+ });
533
+ }
534
+ } catch (err) {
535
+ console.error(err);
536
+ }
537
+ }
538
+ setLocalAttachments([...localAttachments, ...newAttachments]);
539
+ onUpdateAttachments &&
540
+ onUpdateAttachments([...localAttachments, ...newAttachments]);
429
541
  };
430
- const removeAttachment = (uid: string) => {
431
- const next = localAttachments.filter((a) => a.uid !== uid);
542
+ const removeAttachment = (id: string) => {
543
+ const next = localAttachments.filter((a) => a.id !== id);
432
544
  setLocalAttachments(next);
433
545
  onUpdateAttachments && onUpdateAttachments(next);
434
546
  };
@@ -440,19 +552,19 @@ export const ChatInput = React.forwardRef<
440
552
  const text = (localValue || "").trim();
441
553
  if (!text) return;
442
554
  const data = {
443
- demand: text,
444
- title: showTitle ? localTitle : "",
445
- agentIds: selectedAgent.map((it) => it.id),
446
- csvData: localAttachments.reduce(
447
- (acc, a) => ({ ...acc, [a.name]: a.csvBySheet }),
448
- {}
449
- ),
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
+ })),
450
562
  };
451
563
  onSubmit && onSubmit(data);
452
564
  setLocalValue("");
453
565
  };
454
566
  const overlay = (
455
- <div style={{ maxHeight: 400, overflow: "auto", background: "#fff" }}>
567
+ <AgentListDropdownContent>
456
568
  <ul className="agent-list">
457
569
  {agentList.map((item) => (
458
570
  <li key={String(item.id)}>
@@ -460,17 +572,22 @@ export const ChatInput = React.forwardRef<
460
572
  </li>
461
573
  ))}
462
574
  </ul>
463
- </div>
575
+ </AgentListDropdownContent>
464
576
  );
465
577
  return (
466
- <Wrapper>
578
+ <Wrapper style={style}>
467
579
  <div className="chat-input">
468
580
  <div className="input-stack">
469
581
  {localAttachments.length > 0 && (
470
582
  <div className="attachment-bar">
471
583
  {localAttachments.map((a) => (
472
- <div className="attachment-card">
473
- <IconFont type="icon-excel" />
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
+
474
591
  <Typography.Text
475
592
  className="name"
476
593
  ellipsis={{ tooltip: a.name }}
@@ -479,7 +596,7 @@ export const ChatInput = React.forwardRef<
479
596
  </Typography.Text>
480
597
  <a
481
598
  className="remove"
482
- onClick={() => removeAttachment(a.uid)}
599
+ onClick={() => removeAttachment(a.id)}
483
600
  >
484
601
  <DeleteOutlined />
485
602
  </a>
@@ -499,15 +616,6 @@ export const ChatInput = React.forwardRef<
499
616
  ))}
500
617
  </div>
501
618
  )}
502
- {showTitle && (
503
- <Input
504
- className="title-input"
505
- placeholder="请输入报告标题(可选)"
506
- disabled={disabled || sending}
507
- value={localTitle}
508
- onChange={onTitleChange}
509
- />
510
- )}
511
619
  <Input.TextArea
512
620
  ref={inputRef}
513
621
  className="input textarea-stacked"
@@ -521,43 +629,47 @@ export const ChatInput = React.forwardRef<
521
629
  </div>
522
630
  <div className="footer">
523
631
  <div className="left">
524
- <Dropdown
525
- trigger={["click"]}
526
- popupRender={() => overlay}
527
- placement="topLeft"
528
- >
529
- <Tooltip title="选择助理">
530
- <Button
531
- className="agent-trigger"
532
- onClick={(e) => e.preventDefault()}
533
- >
534
- @
535
- </Button>
536
- </Tooltip>
537
- </Dropdown>
538
- <div className="title-toggle">
539
- <span className="label">标题</span>
540
- <Switch
541
- size="small"
542
- checked={showTitle}
543
- disabled={disabled || sending}
544
- onChange={(v) => setShowTitle(v)}
545
- />
546
- </div>
547
- <Upload
548
- multiple
549
- accept={acceptXlsx}
550
- beforeUpload={beforeUploadXlsx}
551
- showUploadList={false}
552
- disabled={disabled || sending}
553
- >
554
- <Tooltip title="上传 Excel 数据">
555
- <Button
556
- className="attach-upload"
557
- icon={<PaperClipOutlined />}
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}
558
670
  />
559
- </Tooltip>
560
- </Upload>
671
+ </>
672
+ )}
561
673
  </div>
562
674
  <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
563
675
  {!isMobile && (
@@ -0,0 +1,65 @@
1
+ import { Space } from "antd";
2
+ import React, { forwardRef, useImperativeHandle, useRef } from "react";
3
+ import styled from "styled-components";
4
+ import { PaneHeader } from "./PaneHeader";
5
+
6
+ const Root = styled.div`
7
+ display: flex;
8
+ flex-direction: column;
9
+ height: 100%;
10
+ .body {
11
+ flex: 1 1 auto;
12
+ display: flex;
13
+ flex-direction: column;
14
+ gap: 12px;
15
+ padding: 12px;
16
+ overflow: auto;
17
+ }
18
+ `;
19
+
20
+ type DatasetPanelType = {
21
+ renderNode: React.ForwardRefExoticComponent<
22
+ any &
23
+ React.RefAttributes<{
24
+ reload: () => void;
25
+ handleAdd: () => void;
26
+ }>
27
+ >;
28
+ };
29
+
30
+ export const DatasetPanel = React.forwardRef<
31
+ { reload: () => void },
32
+ DatasetPanelType
33
+ >(({ renderNode: DatasetPanelComponent }, forwardRef) => {
34
+ const ref = useRef<{ reload: () => void; handleAdd: () => void }>(null);
35
+
36
+ const handleAdd = () => {
37
+ ref.current?.handleAdd();
38
+ };
39
+
40
+ useImperativeHandle(forwardRef, () => ({
41
+ reload,
42
+ }));
43
+
44
+ const reload = () => {
45
+ ref.current?.reload();
46
+ };
47
+
48
+ return (
49
+ <Root className="dataset-panel">
50
+ <PaneHeader
51
+ title="数据集"
52
+ extra={
53
+ <Space>
54
+ <a className="toolbar" onClick={handleAdd}>
55
+ +
56
+ </a>
57
+ </Space>
58
+ }
59
+ />
60
+ <div className="body">
61
+ <DatasetPanelComponent ref={ref} />
62
+ </div>
63
+ </Root>
64
+ );
65
+ });
@@ -4,6 +4,7 @@ import styled from 'styled-components'
4
4
  export type PaneHeaderProps = {
5
5
  title?: string
6
6
  extra?: React.ReactNode
7
+ style?: React.CSSProperties
7
8
  }
8
9
 
9
10
  const Header = styled.div`
@@ -24,9 +25,9 @@ const Header = styled.div`
24
25
  }
25
26
  `
26
27
 
27
- export const PaneHeader: React.FC<PaneHeaderProps> = ({ title = '', extra }) => {
28
+ export const PaneHeader: React.FC<PaneHeaderProps> = ({ title = '', extra, style = {} }) => {
28
29
  return (
29
- <Header className="pane-header">
30
+ <Header className="pane-header" style={style}>
30
31
  <span className="title">{title}</span>
31
32
  <div className="extra">{extra}</div>
32
33
  </Header>