bi-sdk-react 0.0.50 → 0.0.52
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/js/bi-sdk.es.js +123 -34
- package/dist/types/components/context/PageContext.d.ts +10 -0
- package/dist/types/components/hooks/event.d.ts +4 -0
- package/dist/types/components/hooks/method.d.ts +4 -0
- package/dist/types/components/layout/PageItem.d.ts +1 -0
- package/dist/types/components/panel/EventPanel.d.ts +2 -0
- package/dist/types/components/plugins/@antd/index.d.ts +2 -0
- package/dist/types/components/plugins/@antd/item-props/DrawerProps.d.ts +20 -0
- package/dist/types/components/plugins/@antd/item-props/ModalProps.d.ts +18 -0
- package/dist/types/components/plugins/@antd/item-props/index.d.ts +2 -0
- package/dist/types/components/plugins/@antd/items/DrawerRender.d.ts +19 -0
- package/dist/types/components/plugins/@antd/items/EchartsRender.d.ts +2 -2
- package/dist/types/components/plugins/@antd/items/ModalRender.d.ts +17 -0
- package/dist/types/components/plugins/@antd/items/index.d.ts +2 -0
- package/dist/types/components/typing.d.ts +13 -1
- package/dist/umd/js/bi-sdk.umd.min.js +124 -35
- package/package.json +1 -1
- package/src/components/PageDesigner.tsx +57 -41
- package/src/components/context/PageContext.tsx +44 -1
- package/src/components/dnd/DropContainer.tsx +33 -0
- package/src/components/hooks/event.ts +68 -0
- package/src/components/hooks/method.ts +13 -0
- package/src/components/icon/IconFont.tsx +1 -1
- package/src/components/layout/PageItem.tsx +17 -2
- package/src/components/panel/EventPanel.tsx +186 -0
- package/src/components/panel/LayerPanel.tsx +7 -6
- package/src/components/plugins/@antd/index.ts +65 -1
- package/src/components/plugins/@antd/item-props/DrawerProps.tsx +174 -0
- package/src/components/plugins/@antd/item-props/ModalProps.tsx +153 -0
- package/src/components/plugins/@antd/item-props/index.ts +2 -0
- package/src/components/plugins/@antd/items/ButtonRender.tsx +3 -0
- package/src/components/plugins/@antd/items/DrawerRender.tsx +175 -0
- package/src/components/plugins/@antd/items/EchartsRender.tsx +30 -3
- package/src/components/plugins/@antd/items/ModalRender.tsx +158 -0
- package/src/components/plugins/@antd/items/index.ts +2 -1
- package/src/components/typing.ts +13 -1
- package/src/example.tsx +2 -49
package/package.json
CHANGED
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
SchemaItemType,
|
|
45
45
|
} from "./typing";
|
|
46
46
|
import { uuid } from "./utils";
|
|
47
|
+
import { EventPanel } from "./panel/EventPanel";
|
|
47
48
|
|
|
48
49
|
export type PageDesignerProps = {
|
|
49
50
|
pageId: string;
|
|
@@ -267,7 +268,7 @@ export const PageDesigner = React.forwardRef<any, PageDesignerProps>(
|
|
|
267
268
|
| "dataset"
|
|
268
269
|
>("component");
|
|
269
270
|
const [rightPanelActiveKey, setRightPanelActiveKey] = useState<
|
|
270
|
-
"ai" | "props" | "cascade"
|
|
271
|
+
"ai" | "props" | "cascade" | "events"
|
|
271
272
|
>(fetch?.ai ? "ai" : "props");
|
|
272
273
|
const [showLeft, setShowLeft] = useState(true);
|
|
273
274
|
const [showRight, setShowRight] = useState(true);
|
|
@@ -467,6 +468,11 @@ export const PageDesigner = React.forwardRef<any, PageDesignerProps>(
|
|
|
467
468
|
return rightPanelActiveKey;
|
|
468
469
|
}, [selectedItem, rightPanelActiveKey]);
|
|
469
470
|
|
|
471
|
+
|
|
472
|
+
const selectedPlugin: PluginType | undefined = useMemo(() => {
|
|
473
|
+
return (plugins || []).find((p) => p.key === selectedItem?.type);
|
|
474
|
+
}, [plugins, selectedItem?.type]);
|
|
475
|
+
|
|
470
476
|
return (
|
|
471
477
|
<PageProvider
|
|
472
478
|
pageId={pageId}
|
|
@@ -593,47 +599,47 @@ export const PageDesigner = React.forwardRef<any, PageDesignerProps>(
|
|
|
593
599
|
}}
|
|
594
600
|
className="left-pane-tabs"
|
|
595
601
|
>
|
|
596
|
-
<
|
|
597
|
-
<
|
|
602
|
+
<Tooltip title="组件" placement="right">
|
|
603
|
+
<Radio.Button value="component">
|
|
598
604
|
<IconFont type="icon-puzzle" />
|
|
599
|
-
</
|
|
600
|
-
</
|
|
601
|
-
<
|
|
602
|
-
<
|
|
605
|
+
</Radio.Button>
|
|
606
|
+
</Tooltip>
|
|
607
|
+
<Tooltip title="图层" placement="right">
|
|
608
|
+
<Radio.Button value="layer">
|
|
603
609
|
<IconFont type="icon-outline" />
|
|
604
|
-
</
|
|
605
|
-
</
|
|
610
|
+
</Radio.Button>
|
|
611
|
+
</Tooltip>
|
|
606
612
|
{!!datasetPanel && (
|
|
607
|
-
<
|
|
608
|
-
<
|
|
613
|
+
<Tooltip title="数据集" placement="right">
|
|
614
|
+
<Radio.Button value="dataset">
|
|
609
615
|
<IconFont type="icon-dataset" />
|
|
610
|
-
</
|
|
611
|
-
</
|
|
616
|
+
</Radio.Button>
|
|
617
|
+
</Tooltip>
|
|
612
618
|
)}
|
|
613
619
|
{datasourceEnable && (
|
|
614
|
-
<
|
|
615
|
-
<
|
|
620
|
+
<Tooltip title="数据源" placement="right">
|
|
621
|
+
<Radio.Button value="datasource" title="数据源">
|
|
616
622
|
<IconFont type="icon-datasource" />
|
|
617
|
-
</
|
|
618
|
-
</
|
|
623
|
+
</Radio.Button>
|
|
624
|
+
</Tooltip>
|
|
619
625
|
)}
|
|
620
626
|
{scriptEnable && (
|
|
621
|
-
<
|
|
622
|
-
<
|
|
627
|
+
<Tooltip title="脚本" placement="right">
|
|
628
|
+
<Radio.Button value="script">
|
|
623
629
|
<IconFont type="icon-js" />
|
|
624
|
-
</
|
|
625
|
-
</
|
|
630
|
+
</Radio.Button>
|
|
631
|
+
</Tooltip>
|
|
626
632
|
)}
|
|
627
|
-
<
|
|
628
|
-
<
|
|
633
|
+
<Tooltip title="环境变量" placement="right">
|
|
634
|
+
<Radio.Button value="env">
|
|
629
635
|
<IconFont type="icon-variable" />
|
|
630
|
-
</
|
|
631
|
-
</
|
|
632
|
-
<
|
|
633
|
-
<
|
|
636
|
+
</Radio.Button>
|
|
637
|
+
</Tooltip>
|
|
638
|
+
<Tooltip title="源码" placement="right">
|
|
639
|
+
<Radio.Button value="code">
|
|
634
640
|
<IconFont type="icon-json" />
|
|
635
|
-
</
|
|
636
|
-
</
|
|
641
|
+
</Radio.Button>
|
|
642
|
+
</Tooltip>
|
|
637
643
|
</Radio.Group>
|
|
638
644
|
</div>
|
|
639
645
|
<div
|
|
@@ -733,6 +739,9 @@ export const PageDesigner = React.forwardRef<any, PageDesignerProps>(
|
|
|
733
739
|
{rightActiveKey === "cascade" && selectedItem?.cascadeIds && (
|
|
734
740
|
<CascadePanel />
|
|
735
741
|
)}
|
|
742
|
+
{rightActiveKey === "events" && selectedPlugin?.events && (
|
|
743
|
+
<EventPanel />
|
|
744
|
+
)}
|
|
736
745
|
</div>
|
|
737
746
|
<div
|
|
738
747
|
style={{
|
|
@@ -757,23 +766,30 @@ export const PageDesigner = React.forwardRef<any, PageDesignerProps>(
|
|
|
757
766
|
className="right-pane-tabs"
|
|
758
767
|
>
|
|
759
768
|
{fetch?.ai && (
|
|
760
|
-
<
|
|
761
|
-
<
|
|
769
|
+
<Tooltip title="智能助理" placement="right">
|
|
770
|
+
<Radio.Button value="ai">
|
|
762
771
|
<IconFont type="icon-assistant" />
|
|
763
|
-
</
|
|
764
|
-
</
|
|
772
|
+
</Radio.Button>
|
|
773
|
+
</Tooltip>
|
|
765
774
|
)}
|
|
766
|
-
<
|
|
767
|
-
<
|
|
775
|
+
<Tooltip title="属性" placement="right">
|
|
776
|
+
<Radio.Button value="props">
|
|
768
777
|
<IconFont type="icon-info" />
|
|
769
|
-
</
|
|
770
|
-
</
|
|
778
|
+
</Radio.Button>
|
|
779
|
+
</Tooltip>
|
|
771
780
|
{selectedItem?.cascadeIds && (
|
|
772
|
-
<
|
|
773
|
-
<
|
|
781
|
+
<Tooltip title="联动" placement="right">
|
|
782
|
+
<Radio.Button value="cascade">
|
|
774
783
|
<IconFont type="icon-cascade" />
|
|
775
|
-
</
|
|
776
|
-
</
|
|
784
|
+
</Radio.Button>
|
|
785
|
+
</Tooltip>
|
|
786
|
+
)}
|
|
787
|
+
{selectedPlugin?.events && (
|
|
788
|
+
<Tooltip title="事件" placement="right">
|
|
789
|
+
<Radio.Button value="events">
|
|
790
|
+
<IconFont type="icon-thunderbolt" />
|
|
791
|
+
</Radio.Button>
|
|
792
|
+
</Tooltip>
|
|
777
793
|
)}
|
|
778
794
|
</Radio.Group>
|
|
779
795
|
{/* <Popover
|
|
@@ -16,6 +16,9 @@ type InitCallbackParams = {
|
|
|
16
16
|
callback: CallbackType;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
type MethodParams = { sourceId: string; arg?: any };
|
|
20
|
+
type MethodType = Record<string, (params?: MethodParams) => any>;
|
|
21
|
+
|
|
19
22
|
type PageContextType = {
|
|
20
23
|
pageId: string;
|
|
21
24
|
designable: boolean;
|
|
@@ -42,6 +45,19 @@ type PageContextType = {
|
|
|
42
45
|
refresh?: boolean,
|
|
43
46
|
consume?: (item: Omit<SchemaItemType, "children">, data: any) => void,
|
|
44
47
|
) => void;
|
|
48
|
+
initMethod: ({
|
|
49
|
+
id,
|
|
50
|
+
method,
|
|
51
|
+
}: {
|
|
52
|
+
id: string;
|
|
53
|
+
method: MethodType;
|
|
54
|
+
}) => any;
|
|
55
|
+
handleMethod: (
|
|
56
|
+
item: SchemaItemType,
|
|
57
|
+
targetId: string,
|
|
58
|
+
methodName: string,
|
|
59
|
+
arg?: any,
|
|
60
|
+
) => any;
|
|
45
61
|
getVars: (id: string) => Record<string, any>;
|
|
46
62
|
setVar: (item: SchemaItemType, key: string, value: any) => void;
|
|
47
63
|
setItemData?: (id: string, data: any) => void;
|
|
@@ -75,6 +91,8 @@ export const PageContext = createContext<PageContextType>({
|
|
|
75
91
|
setDeviceWidth: () => {},
|
|
76
92
|
initCallback: () => {},
|
|
77
93
|
handleCallback: () => {},
|
|
94
|
+
initMethod: () => {},
|
|
95
|
+
handleMethod: () => {},
|
|
78
96
|
getVars: () => ({}),
|
|
79
97
|
setVar: () => {},
|
|
80
98
|
setItemData: () => {},
|
|
@@ -129,6 +147,30 @@ export const PageProvider: React.FC<PageProviderProps> = ({
|
|
|
129
147
|
|
|
130
148
|
const [env, setEnv] = useState<EnvType>({ global: {}, local: {} });
|
|
131
149
|
const [callbacks, setCallbacks] = useState<CallbacksType>({});
|
|
150
|
+
const [methods, setMethods] = useState<Record<string, MethodType>>({});
|
|
151
|
+
|
|
152
|
+
const initMethod = ({
|
|
153
|
+
id,
|
|
154
|
+
method,
|
|
155
|
+
}: {
|
|
156
|
+
id: string;
|
|
157
|
+
method: MethodType;
|
|
158
|
+
}) => {
|
|
159
|
+
setMethods((c) => ({ ...c, [id]: method }));
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const handleMethod = (
|
|
163
|
+
item: SchemaItemType,
|
|
164
|
+
targetId: string,
|
|
165
|
+
methodName: string,
|
|
166
|
+
arg?: any,
|
|
167
|
+
) => {
|
|
168
|
+
const method = methods[targetId][methodName];
|
|
169
|
+
if (typeof method === "function") {
|
|
170
|
+
return method({ sourceId: item.id!, arg });
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
};
|
|
132
174
|
|
|
133
175
|
const initCallback = ({
|
|
134
176
|
id,
|
|
@@ -173,7 +215,6 @@ export const PageProvider: React.FC<PageProviderProps> = ({
|
|
|
173
215
|
});
|
|
174
216
|
};
|
|
175
217
|
|
|
176
|
-
|
|
177
218
|
useEffect(() => {
|
|
178
219
|
const nextGlobal: Record<string, any> = {};
|
|
179
220
|
(schema.variables || []).forEach((item) => {
|
|
@@ -207,6 +248,8 @@ export const PageProvider: React.FC<PageProviderProps> = ({
|
|
|
207
248
|
setDeviceWidth: setDeviceWidth || (() => {}),
|
|
208
249
|
initCallback,
|
|
209
250
|
handleCallback,
|
|
251
|
+
initMethod,
|
|
252
|
+
handleMethod,
|
|
210
253
|
getVars,
|
|
211
254
|
setVar,
|
|
212
255
|
setItemData,
|
|
@@ -4,6 +4,7 @@ import { PageItem, PageItemProps } from "../layout/PageItem";
|
|
|
4
4
|
import { HtmlBaseProps, SchemaItemType } from "../typing";
|
|
5
5
|
import styled from "styled-components";
|
|
6
6
|
import { PageContext } from "../context/PageContext";
|
|
7
|
+
import { uuid } from "../utils";
|
|
7
8
|
|
|
8
9
|
type DropContainerProps = {
|
|
9
10
|
rootComponent?: React.ComponentType<any>;
|
|
@@ -441,6 +442,37 @@ export const DropContainer: React.FC<DropContainerProps> = ({
|
|
|
441
442
|
return () => cleanup();
|
|
442
443
|
}, []);
|
|
443
444
|
|
|
445
|
+
const onCopy = (item: SchemaItemType) => {
|
|
446
|
+
// 递归复制函数,确保所有子节点都有新的 ID
|
|
447
|
+
const cloneItem = (node: SchemaItemType): SchemaItemType => {
|
|
448
|
+
const newNode = { ...node, id: uuid() };
|
|
449
|
+
|
|
450
|
+
if (node.children) {
|
|
451
|
+
if (Array.isArray(node.children)) {
|
|
452
|
+
newNode.children = node.children.map(cloneItem);
|
|
453
|
+
} else {
|
|
454
|
+
const newChildren: Record<string, SchemaItemType[]> = {};
|
|
455
|
+
Object.keys(node.children).forEach((key) => {
|
|
456
|
+
newChildren[key] = (node.children as Record<string, SchemaItemType[]>)[
|
|
457
|
+
key
|
|
458
|
+
].map(cloneItem);
|
|
459
|
+
});
|
|
460
|
+
newNode.children = newChildren;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return newNode;
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const newItem = cloneItem(item);
|
|
467
|
+
const index = list.findIndex((i) => i.id === item.id);
|
|
468
|
+
|
|
469
|
+
if (index !== -1) {
|
|
470
|
+
const newList = [...list];
|
|
471
|
+
newList.splice(index + 1, 0, newItem);
|
|
472
|
+
onListChange?.(newList);
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
|
|
444
476
|
// Determine classes
|
|
445
477
|
const classes = [
|
|
446
478
|
className,
|
|
@@ -483,6 +515,7 @@ export const DropContainer: React.FC<DropContainerProps> = ({
|
|
|
483
515
|
parentList={memoList}
|
|
484
516
|
onListChange={onListChange}
|
|
485
517
|
index={index}
|
|
518
|
+
onCopy={onCopy}
|
|
486
519
|
onDelete={() =>
|
|
487
520
|
onListChange?.(memoList.filter((i: any) => i.id !== child.id))
|
|
488
521
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { SchemaItemType } from "../typing";
|
|
2
|
+
import { useMethods } from "./method";
|
|
3
|
+
|
|
4
|
+
export const useEvent = (item: SchemaItemType) => {
|
|
5
|
+
const events = (item.events || []).reduce(
|
|
6
|
+
(acc, cur) => {
|
|
7
|
+
acc[cur.name] = cur.script;
|
|
8
|
+
return acc;
|
|
9
|
+
},
|
|
10
|
+
{} as Record<string, string>,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const { callMethod } = useMethods(item);
|
|
14
|
+
|
|
15
|
+
const handleEvent = (eventName: string, arg?: any) => {
|
|
16
|
+
const script = events[eventName];
|
|
17
|
+
if (script) {
|
|
18
|
+
if (script) {
|
|
19
|
+
try {
|
|
20
|
+
const safeEval = new Function(
|
|
21
|
+
"console",
|
|
22
|
+
"Math",
|
|
23
|
+
"Date",
|
|
24
|
+
"Array",
|
|
25
|
+
"Object",
|
|
26
|
+
"String",
|
|
27
|
+
"Number",
|
|
28
|
+
"Boolean",
|
|
29
|
+
"callMethod",
|
|
30
|
+
"param",
|
|
31
|
+
`
|
|
32
|
+
"use strict";
|
|
33
|
+
const window = undefined;
|
|
34
|
+
const document = undefined;
|
|
35
|
+
const global = undefined;
|
|
36
|
+
const process = undefined;
|
|
37
|
+
const require = undefined;
|
|
38
|
+
const module = undefined;
|
|
39
|
+
const exports = undefined;
|
|
40
|
+
${script}
|
|
41
|
+
`,
|
|
42
|
+
);
|
|
43
|
+
return (safeEval as any)(
|
|
44
|
+
console,
|
|
45
|
+
Math,
|
|
46
|
+
Date,
|
|
47
|
+
Array,
|
|
48
|
+
Object,
|
|
49
|
+
String,
|
|
50
|
+
Number,
|
|
51
|
+
Boolean,
|
|
52
|
+
callMethod,
|
|
53
|
+
{
|
|
54
|
+
item,
|
|
55
|
+
sourceId: item.id,
|
|
56
|
+
arg,
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
} catch (evalError) {
|
|
60
|
+
console.error("点击事件脚本执行失败:", evalError);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return { handleEvent };
|
|
68
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { PageContext } from "../context/PageContext";
|
|
3
|
+
import { SchemaItemType } from "../typing";
|
|
4
|
+
|
|
5
|
+
export const useMethods = (item: SchemaItemType) => {
|
|
6
|
+
const { handleMethod } = useContext(PageContext);
|
|
7
|
+
|
|
8
|
+
const callMethod = (targetId: string, methodName: string, arg?: any) => {
|
|
9
|
+
return handleMethod(item, targetId, methodName, arg);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
return { callMethod };
|
|
13
|
+
};
|
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { createFromIconfontCN } from "@ant-design/icons";
|
|
3
3
|
|
|
4
4
|
const IconFontCN = createFromIconfontCN({
|
|
5
|
-
scriptUrl: "https://at.alicdn.com/t/c/
|
|
5
|
+
scriptUrl: "https://at.alicdn.com/t/c/font_5072483_3ofyyta7h1e.js",
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
export type IconFontProps = {
|
|
@@ -4,7 +4,7 @@ import { PageContext } from "../context/PageContext";
|
|
|
4
4
|
import { SchemaItemType } from "../typing";
|
|
5
5
|
import styled from "styled-components";
|
|
6
6
|
import { IconFont } from "../icon/IconFont";
|
|
7
|
-
import { DeleteOutlined } from "@ant-design/icons";
|
|
7
|
+
import { CopyOutlined, DeleteOutlined } from "@ant-design/icons";
|
|
8
8
|
import { Button, Dropdown, Modal, Space, Tooltip, Typography } from "antd";
|
|
9
9
|
import ReactDOM from "react-dom";
|
|
10
10
|
import { Editor } from "@monaco-editor/react";
|
|
@@ -12,6 +12,7 @@ import { Editor } from "@monaco-editor/react";
|
|
|
12
12
|
export type PageItemProps = {
|
|
13
13
|
id?: string;
|
|
14
14
|
item: SchemaItemType;
|
|
15
|
+
onCopy?: (item: SchemaItemType) => void;
|
|
15
16
|
onDelete?: () => void;
|
|
16
17
|
ancestors: SchemaItemType[];
|
|
17
18
|
parentList?: any[];
|
|
@@ -67,9 +68,10 @@ const Toolbar: React.FC<{
|
|
|
67
68
|
ancestors: SchemaItemType[];
|
|
68
69
|
onAddAt?: (e: any) => void;
|
|
69
70
|
onDelete?: (e: any) => void;
|
|
71
|
+
onCopy?: (e: any) => void;
|
|
70
72
|
onUpdate?: () => void;
|
|
71
73
|
onSelect?: (item: SchemaItemType) => void;
|
|
72
|
-
}> = ({ item, ancestors, onAddAt, onDelete, onUpdate, onSelect }) => {
|
|
74
|
+
}> = ({ item, ancestors, onAddAt, onDelete, onCopy, onUpdate, onSelect }) => {
|
|
73
75
|
const [code, setCode] = useState(JSON.stringify(item || {}, null, 2));
|
|
74
76
|
const [visible, setVisible] = useState(false);
|
|
75
77
|
const editRef = useRef<any>(null);
|
|
@@ -190,6 +192,11 @@ const Toolbar: React.FC<{
|
|
|
190
192
|
<IconFont type="icon-json" />
|
|
191
193
|
</a>
|
|
192
194
|
</Tooltip>
|
|
195
|
+
<Tooltip title="复制">
|
|
196
|
+
<a onClick={onCopy}>
|
|
197
|
+
<CopyOutlined />
|
|
198
|
+
</a>
|
|
199
|
+
</Tooltip>
|
|
193
200
|
<Tooltip title="删除">
|
|
194
201
|
<a onClick={onDelete}>
|
|
195
202
|
<DeleteOutlined />
|
|
@@ -218,6 +225,7 @@ const Toolbar: React.FC<{
|
|
|
218
225
|
*/
|
|
219
226
|
export const PageItem: React.FC<PageItemProps> = ({
|
|
220
227
|
item,
|
|
228
|
+
onCopy,
|
|
221
229
|
onDelete,
|
|
222
230
|
ancestors,
|
|
223
231
|
parentList,
|
|
@@ -252,6 +260,11 @@ export const PageItem: React.FC<PageItemProps> = ({
|
|
|
252
260
|
});
|
|
253
261
|
};
|
|
254
262
|
|
|
263
|
+
const handleCopy = (e: any) => {
|
|
264
|
+
e.stopPropagation();
|
|
265
|
+
if (onCopy) onCopy(item);
|
|
266
|
+
};
|
|
267
|
+
|
|
255
268
|
const handleAddAtItem = (e: any) => {
|
|
256
269
|
e.stopPropagation();
|
|
257
270
|
if (onAddAtItem) onAddAtItem(item);
|
|
@@ -303,6 +316,7 @@ export const PageItem: React.FC<PageItemProps> = ({
|
|
|
303
316
|
|
|
304
317
|
if (selectedItem && selectedItem === item && designable) {
|
|
305
318
|
const div = document.createElement("div");
|
|
319
|
+
div.setAttribute("id", "page-item-toolbar")
|
|
306
320
|
div.style.position = "absolute";
|
|
307
321
|
div.style.zIndex = "1000";
|
|
308
322
|
div.style.display = "flex";
|
|
@@ -378,6 +392,7 @@ export const PageItem: React.FC<PageItemProps> = ({
|
|
|
378
392
|
item={item}
|
|
379
393
|
ancestors={ancestors}
|
|
380
394
|
onAddAt={fetch?.ai?.chat ? handleAddAtItem : undefined}
|
|
395
|
+
onCopy={handleCopy}
|
|
381
396
|
onDelete={handleDelete}
|
|
382
397
|
onUpdate={forceUpdate}
|
|
383
398
|
onSelect={onSelect}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { CaretDownOutlined, PlusOutlined } from "@ant-design/icons";
|
|
2
|
+
import { Divider, Dropdown, Empty, Space } from "antd";
|
|
3
|
+
import React, { useContext, useMemo } from "react";
|
|
4
|
+
import styled from "styled-components";
|
|
5
|
+
import { PageContext } from "../context/PageContext";
|
|
6
|
+
import { PluginType } from "../typing";
|
|
7
|
+
import { PaneHeader } from "./PaneHeader";
|
|
8
|
+
import { Editor } from "@monaco-editor/react";
|
|
9
|
+
import { IconFont } from "../icon/IconFont";
|
|
10
|
+
|
|
11
|
+
const Root = styled.div`
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
height: 100%;
|
|
15
|
+
.body {
|
|
16
|
+
flex: 1 1 auto;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: 12px;
|
|
20
|
+
padding: 12px;
|
|
21
|
+
overflow-y: auto;
|
|
22
|
+
overflow-x: hidden;
|
|
23
|
+
|
|
24
|
+
.ant-divider {
|
|
25
|
+
margin: 0;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
ul {
|
|
29
|
+
list-style: none;
|
|
30
|
+
padding: 0;
|
|
31
|
+
margin: 0;
|
|
32
|
+
|
|
33
|
+
li {
|
|
34
|
+
list-style: none;
|
|
35
|
+
padding: 0;
|
|
36
|
+
margin: 0;
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
border-bottom: 1px dotted #d9d9d9;
|
|
40
|
+
padding-bottom: 8px;
|
|
41
|
+
|
|
42
|
+
& > div:first-child {
|
|
43
|
+
font-size: 14px;
|
|
44
|
+
color: var(--ant-color-text-label);
|
|
45
|
+
padding: 6px 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&:last-child {
|
|
49
|
+
border-bottom: none;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
ul.tips {
|
|
54
|
+
padding-left: 20px;
|
|
55
|
+
li {
|
|
56
|
+
display: list-item;
|
|
57
|
+
list-style: disc;
|
|
58
|
+
border-bottom: none;
|
|
59
|
+
color: var(--ant-color-text-secondary);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
a.toolbar {
|
|
63
|
+
font-size: 16px;
|
|
64
|
+
color: var(--ant-color-text-secondary);
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
export const EventPanel: React.FC = ({}) => {
|
|
69
|
+
const { selectedItem, setSelectedItem, plugins, forceUpdate } =
|
|
70
|
+
useContext(PageContext);
|
|
71
|
+
const selectedPlugin: PluginType | undefined = useMemo(() => {
|
|
72
|
+
return (plugins || []).find((p) => p.key === selectedItem?.type);
|
|
73
|
+
}, [plugins, selectedItem?.type]);
|
|
74
|
+
|
|
75
|
+
const events = selectedPlugin?.events || [];
|
|
76
|
+
const eventMap = events.reduce((prev: Record<string, string>, cur) => {
|
|
77
|
+
prev[cur.handler] = cur.name;
|
|
78
|
+
return prev;
|
|
79
|
+
}, {});
|
|
80
|
+
|
|
81
|
+
const onChange = (events: { name: string; script: string }[]) => {
|
|
82
|
+
if (!selectedItem) return;
|
|
83
|
+
setSelectedItem(Object.assign(selectedItem, { events }));
|
|
84
|
+
forceUpdate();
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<Root className="cascade-panel">
|
|
89
|
+
<PaneHeader
|
|
90
|
+
title="事件"
|
|
91
|
+
extra={
|
|
92
|
+
selectedPlugin?.events ? (
|
|
93
|
+
<Space>
|
|
94
|
+
<Dropdown
|
|
95
|
+
menu={{
|
|
96
|
+
onClick: ({ key }) => {
|
|
97
|
+
const nextEvents = [...(selectedItem?.events || [])];
|
|
98
|
+
nextEvents.push({ name: key, script: "" });
|
|
99
|
+
onChange && onChange(nextEvents);
|
|
100
|
+
},
|
|
101
|
+
items: events
|
|
102
|
+
.filter(
|
|
103
|
+
(e) =>
|
|
104
|
+
!(selectedItem?.events || [])?.some((i) => i.name === e.handler),
|
|
105
|
+
)
|
|
106
|
+
.map((it) => ({
|
|
107
|
+
key: it.handler,
|
|
108
|
+
label: `${it.name}(${it.handler})`,
|
|
109
|
+
})),
|
|
110
|
+
}}
|
|
111
|
+
placement="bottomRight"
|
|
112
|
+
trigger={["click"]}
|
|
113
|
+
>
|
|
114
|
+
<a className="toolbar">
|
|
115
|
+
<PlusOutlined />
|
|
116
|
+
<CaretDownOutlined
|
|
117
|
+
style={{ fontSize: 8, verticalAlign: "sub" }}
|
|
118
|
+
/>
|
|
119
|
+
</a>
|
|
120
|
+
</Dropdown>
|
|
121
|
+
</Space>
|
|
122
|
+
) : null
|
|
123
|
+
}
|
|
124
|
+
/>
|
|
125
|
+
<div className="body beautified_scrollbar">
|
|
126
|
+
<Divider>事件列表</Divider>
|
|
127
|
+
{!!selectedItem?.events?.length ? (
|
|
128
|
+
<ul>
|
|
129
|
+
{(selectedItem.events || []).map(
|
|
130
|
+
({ name, script }, index: number) => (
|
|
131
|
+
<li key={name}>
|
|
132
|
+
<div>
|
|
133
|
+
<IconFont type="icon-thunderbolt" /> {eventMap[name]}(
|
|
134
|
+
{name})
|
|
135
|
+
</div>
|
|
136
|
+
<div
|
|
137
|
+
style={{
|
|
138
|
+
border: "solid 1px var(--ant-color-border)",
|
|
139
|
+
borderRadius: "var(--ant-border-radius-sm)",
|
|
140
|
+
}}
|
|
141
|
+
>
|
|
142
|
+
<Editor
|
|
143
|
+
height="200px"
|
|
144
|
+
defaultLanguage="javascript"
|
|
145
|
+
value={script || ""}
|
|
146
|
+
onChange={(v) => {
|
|
147
|
+
const nextEvents = [...(selectedItem?.events || [])];
|
|
148
|
+
nextEvents[index].script = v || "";
|
|
149
|
+
onChange && onChange(nextEvents);
|
|
150
|
+
}}
|
|
151
|
+
options={{
|
|
152
|
+
minimap: { enabled: false },
|
|
153
|
+
scrollBeyondLastLine: false,
|
|
154
|
+
tabSize: 2,
|
|
155
|
+
}}
|
|
156
|
+
/>
|
|
157
|
+
</div>
|
|
158
|
+
</li>
|
|
159
|
+
),
|
|
160
|
+
)}
|
|
161
|
+
</ul>
|
|
162
|
+
) : (
|
|
163
|
+
<Empty description="暂无事件" image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
|
164
|
+
)}
|
|
165
|
+
<Divider>脚本说明</Divider>
|
|
166
|
+
<div>
|
|
167
|
+
<ul className="tips">
|
|
168
|
+
<li>
|
|
169
|
+
内置参数:param(类型:Object)
|
|
170
|
+
<ul style={{ margin: "8px 0 0 20px" }}>
|
|
171
|
+
<li>param.item: 当前事件触发的组件。</li>
|
|
172
|
+
<li>param.sourceId: 当前事件触发的组件ID。</li>
|
|
173
|
+
<li>param.arg: 事件触发时传递的参数。</li>
|
|
174
|
+
</ul>
|
|
175
|
+
</li>
|
|
176
|
+
<li>
|
|
177
|
+
脚本禁止使用以下 API:window, document, global, process, require,
|
|
178
|
+
module, exports, cookie, localStorage, sessionStorage。
|
|
179
|
+
</li>
|
|
180
|
+
<li>组件方法调用:callMethod(targetId, methodName, arg)。</li>
|
|
181
|
+
</ul>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</Root>
|
|
185
|
+
);
|
|
186
|
+
};
|