@tmagic/editor 1.4.4 → 1.4.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 (52) hide show
  1. package/dist/style.css +2 -2
  2. package/dist/tmagic-editor.js +2393 -2242
  3. package/dist/tmagic-editor.umd.cjs +2403 -2248
  4. package/package.json +27 -27
  5. package/src/editorProps.ts +5 -1
  6. package/src/fields/DataSourceFieldSelect.vue +3 -41
  7. package/src/fields/EventSelect.vue +30 -9
  8. package/src/hooks/use-code-block-edit.ts +0 -2
  9. package/src/hooks/use-stage.ts +1 -1
  10. package/src/index.ts +1 -0
  11. package/src/initService.ts +12 -2
  12. package/src/layouts/workspace/viewer/Stage.vue +7 -6
  13. package/src/services/codeBlock.ts +77 -9
  14. package/src/services/dataSource.ts +53 -4
  15. package/src/services/editor.ts +2 -2
  16. package/src/services/props.ts +10 -5
  17. package/src/services/stageOverlay.ts +1 -0
  18. package/src/type.ts +1 -0
  19. package/src/utils/content-menu.ts +7 -3
  20. package/src/utils/data-source/index.ts +39 -3
  21. package/src/utils/editor.ts +16 -7
  22. package/types/components/CodeBlockEditor.vue.d.ts +21 -34
  23. package/types/components/CodeParams.vue.d.ts +2 -2
  24. package/types/components/ContentMenu.vue.d.ts +1 -1
  25. package/types/components/FloatingBox.vue.d.ts +41 -38
  26. package/types/components/ScrollViewer.vue.d.ts +1 -1
  27. package/types/components/ToolButton.vue.d.ts +4 -4
  28. package/types/components/Tree.vue.d.ts +2 -2
  29. package/types/editorProps.d.ts +5 -1
  30. package/types/fields/CodeLink.vue.d.ts +2 -2
  31. package/types/fields/DataSourceFields.vue.d.ts +32 -78
  32. package/types/fields/DataSourceInput.vue.d.ts +2 -2
  33. package/types/fields/DataSourceMethods.vue.d.ts +2 -2
  34. package/types/fields/DataSourceMocks.vue.d.ts +31 -76
  35. package/types/fields/KeyValue.vue.d.ts +2 -2
  36. package/types/fields/UISelect.vue.d.ts +2 -2
  37. package/types/hooks/use-code-block-edit.d.ts +18 -6
  38. package/types/hooks/use-data-source-edit.d.ts +18 -6
  39. package/types/hooks/use-data-source-method.d.ts +18 -6
  40. package/types/index.d.ts +1 -0
  41. package/types/layouts/CodeEditor.vue.d.ts +1 -1
  42. package/types/layouts/PropsPanel.vue.d.ts +14 -14
  43. package/types/layouts/sidebar/data-source/DataSourceConfigPanel.vue.d.ts +20 -30
  44. package/types/layouts/sidebar/layer/LayerMenu.vue.d.ts +6 -6
  45. package/types/layouts/workspace/viewer/Stage.vue.d.ts +4 -4
  46. package/types/layouts/workspace/viewer/ViewerMenu.vue.d.ts +6 -6
  47. package/types/services/codeBlock.d.ts +22 -2
  48. package/types/services/dataSource.d.ts +13 -1
  49. package/types/services/ui.d.ts +1 -1
  50. package/types/type.d.ts +1 -0
  51. package/types/utils/data-source/index.d.ts +3 -2
  52. package/types/utils/editor.d.ts +2 -0
@@ -8,13 +8,13 @@ import * as monaco from 'monaco-editor';
8
8
  import { MContainer, MForm, filterFunction, createValues, MFormBox, MCascader, MSelect, MPanel } from '@tmagic/form';
9
9
  import { HookCodeType, HookType, NodeType, ActionType } from '@tmagic/schema';
10
10
  import { MagicTable } from '@tmagic/table';
11
- import { toLine, guid, isPage, isPageFragment, isPop, getNodePath, isNumber, getValueByKeyPath, setValueByKeyPath, convertToNumber, getDefaultValueFromFields, DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX, getKeys, addClassName, removeClassName, removeClassNameByClassName, getNodes } from '@tmagic/utils';
11
+ import { toLine, guid, getNodePath, getValueByKeyPath, setValueByKeyPath, isPage, isPageFragment, isPop, isNumber, calcValueByFontsize, convertToNumber, getDefaultValueFromFields, DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX, DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX, getKeys, addClassName, removeClassName, removeClassNameByClassName, getNodes } from '@tmagic/utils';
12
12
  import VanillaMoveable from 'moveable';
13
- import { Watcher, DepTargetType, createRelatedCompTarget, createCodeBlockTarget, createDataSourceTarget, createDataSourceMethodTarget, createDataSourceCondTarget } from '@tmagic/dep';
13
+ import { Watcher, DepTargetType, createRelatedTargetForCopy, createCodeBlockTarget, createDataSourceTarget, createDataSourceMethodTarget, createDataSourceCondTarget } from '@tmagic/dep';
14
14
  export { DepTargetType } from '@tmagic/dep';
15
15
  import { EventEmitter } from 'events';
16
16
  import Gesto from 'gesto';
17
- import StageCore, { GuidesType, getOffset, calcValueByFontsize, RenderType, CONTAINER_HIGHLIGHT_CLASS_NAME, ContainerHighlightType } from '@tmagic/stage';
17
+ import StageCore, { GuidesType, getOffset, RenderType, CONTAINER_HIGHLIGHT_CLASS_NAME, ContainerHighlightType } from '@tmagic/stage';
18
18
  import { DEFAULT_EVENTS, DEFAULT_METHODS } from '@tmagic/core';
19
19
  import KeyController from 'keycon';
20
20
 
@@ -505,362 +505,94 @@ const _sfc_main$Q = /* @__PURE__ */ defineComponent({
505
505
  }
506
506
  });
507
507
 
508
- function BaseFormConfig() {
509
- return [
510
- {
511
- name: "id",
512
- type: "hidden"
513
- },
514
- {
515
- name: "type",
516
- text: "类型",
517
- type: "hidden",
518
- defaultValue: "base"
519
- },
520
- {
521
- name: "title",
522
- text: "名称",
523
- rules: [
524
- {
525
- required: true,
526
- message: "请输入名称"
508
+ const compose = (middleware, isAsync) => {
509
+ if (!Array.isArray(middleware))
510
+ throw new TypeError("Middleware 必须是一个数组!");
511
+ for (const fn of middleware) {
512
+ if (typeof fn !== "function")
513
+ throw new TypeError("Middleware 必须由函数组成!");
514
+ }
515
+ return (args, next) => {
516
+ let index = -1;
517
+ return dispatch(0);
518
+ function dispatch(i) {
519
+ if (i <= index) {
520
+ const error = new Error("next() 被多次调用");
521
+ if (isAsync) {
522
+ return Promise.reject(error);
527
523
  }
528
- ]
529
- },
530
- {
531
- name: "description",
532
- text: "描述"
533
- }
534
- ];
535
- }
536
-
537
- const HttpFormConfig = [
538
- {
539
- name: "autoFetch",
540
- text: "自动请求",
541
- type: "switch",
542
- defaultValue: true
543
- },
544
- {
545
- name: "responseOptions",
546
- items: [
547
- {
548
- name: "dataPath",
549
- text: "数据路径"
524
+ throw error;
550
525
  }
551
- ]
552
- },
553
- {
554
- type: "fieldset",
555
- name: "options",
556
- legend: "HTTP 配置",
557
- items: [
558
- {
559
- name: "url",
560
- text: "URL"
561
- },
562
- {
563
- name: "method",
564
- text: "Method",
565
- type: "select",
566
- options: [
567
- { text: "GET", value: "GET" },
568
- { text: "POST", value: "POST" },
569
- { text: "PUT", value: "PUT" },
570
- { text: "DELETE", value: "DELETE" }
571
- ]
572
- },
573
- {
574
- name: "params",
575
- type: "key-value",
576
- defaultValue: {},
577
- text: "参数"
578
- },
579
- {
580
- name: "data",
581
- type: "key-value",
582
- defaultValue: {},
583
- advanced: true,
584
- text: "请求体"
585
- },
586
- {
587
- name: "headers",
588
- type: "key-value",
589
- defaultValue: {},
590
- text: "请求头"
526
+ index = i;
527
+ let fn = middleware[i];
528
+ if (i === middleware.length && next)
529
+ fn = next;
530
+ if (!fn) {
531
+ if (isAsync) {
532
+ return Promise.resolve();
533
+ }
534
+ return;
591
535
  }
592
- ]
593
- }
594
- ];
536
+ if (isAsync) {
537
+ try {
538
+ return Promise.resolve(fn(...args, dispatch.bind(null, i + 1)));
539
+ } catch (err) {
540
+ return Promise.reject(err);
541
+ }
542
+ }
543
+ try {
544
+ return fn(...args, dispatch.bind(null, i + 1));
545
+ } catch (err) {
546
+ throw err;
547
+ }
548
+ }
549
+ };
550
+ };
595
551
 
596
- const fillConfig$1 = (config) => [
597
- ...BaseFormConfig(),
598
- ...config,
599
- {
600
- type: "tab",
601
- items: [
602
- {
603
- title: "数据定义",
604
- items: [
605
- {
606
- name: "fields",
607
- type: "data-source-fields",
608
- defaultValue: () => []
609
- }
610
- ]
611
- },
612
- {
613
- title: "方法定义",
614
- items: [
615
- {
616
- name: "methods",
617
- type: "data-source-methods",
618
- defaultValue: () => []
619
- }
620
- ]
621
- },
622
- {
623
- title: "事件配置",
624
- display: false,
625
- items: [
626
- {
627
- name: "events",
628
- src: "datasource",
629
- type: "event-select"
630
- }
631
- ]
632
- },
633
- {
634
- title: "mock数据",
635
- items: [
636
- {
637
- name: "mocks",
638
- type: "data-source-mocks",
639
- defaultValue: () => []
640
- }
641
- ]
642
- },
643
- {
644
- title: "请求参数裁剪",
645
- display: (formState, { model }) => model.type === "http",
646
- items: [
647
- {
648
- name: "beforeRequest",
649
- type: "vs-code",
650
- parse: true,
651
- height: "600px"
652
- }
653
- ]
654
- },
655
- {
656
- title: "响应数据裁剪",
657
- display: (formState, { model }) => model.type === "http",
658
- items: [
659
- {
660
- name: "afterResponse",
661
- type: "vs-code",
662
- parse: true,
663
- height: "600px"
664
- }
665
- ]
552
+ const methodName = (prefix, name) => `${prefix}${name[0].toUpperCase()}${name.substring(1)}`;
553
+ const isError = (error) => Object.prototype.toString.call(error) === "[object Error]";
554
+ const doAction = (args, scope, sourceMethod, beforeMethodName, afterMethodName, fn) => {
555
+ try {
556
+ let beforeArgs = args;
557
+ for (const beforeMethod of scope.pluginOptionsList[beforeMethodName]) {
558
+ beforeArgs = beforeMethod(...beforeArgs) || [];
559
+ if (isError(beforeArgs))
560
+ throw beforeArgs;
561
+ if (!Array.isArray(beforeArgs)) {
562
+ beforeArgs = [beforeArgs];
666
563
  }
667
- ]
668
- }
669
- ];
670
- const getFormConfig = (type, configs) => {
671
- switch (type) {
672
- case "base":
673
- return fillConfig$1([]);
674
- case "http":
675
- return fillConfig$1(HttpFormConfig);
676
- default:
677
- return fillConfig$1(configs[type] || []);
564
+ }
565
+ let returnValue = fn(beforeArgs, sourceMethod.bind(scope));
566
+ for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
567
+ returnValue = afterMethod(returnValue, ...beforeArgs);
568
+ if (isError(returnValue))
569
+ throw returnValue;
570
+ }
571
+ return returnValue;
572
+ } catch (error) {
573
+ throw error;
678
574
  }
679
575
  };
680
- const getFormValue = (type, values) => {
681
- if (type !== "http") {
682
- return values;
683
- }
684
- return {
685
- beforeRequest: `(options, context) => {
686
- /**
687
- * 用户可以直接编写函数,在原始接口调用之前,会运行该函数,将这个函数的返回值作为该数据源接口的入参
688
- *
689
- * options: HttpOptions
690
- *
691
- * interface HttpOptions {
692
- * // 请求链接
693
- * url: string;
694
- * // query参数
695
- * params?: Record<string, string>;
696
- * // body数据
697
- * data?: Record<string, any>;
698
- * // 请求头
699
- * headers?: Record<string, string>;
700
- * // 请求方法 GET/POST
701
- * method?: Method;
702
- * }
703
- *
704
- * context:上下文对象
705
- *
706
- * interface Content {
707
- * app: AppCore;
708
- * dataSource: HttpDataSource;
709
- * }
710
- *
711
- * return: HttpOptions
712
- */
713
-
714
- // 此处的返回值会作为这个接口的入参
715
- return options;
716
- }`,
717
- afterResponse: `(response, context) => {
718
- /**
719
- * 用户可以直接编写函数,在原始接口返回之后,会运行该函数,将这个函数的返回值作为该数据源接口的返回
720
-
721
- * context:上下文对象
722
- *
723
- * interface Content {
724
- * app: AppCore;
725
- * dataSource: HttpDataSource;
726
- * }
727
- *
728
- */
729
-
730
- // 此处的返回值会作为这个接口的返回值
731
- return response;
732
- }`,
733
- ...values
734
- };
735
- };
736
- const getDisplayField = (dataSources, key) => {
737
- const displayState = [];
738
- const matches = key.matchAll(/\$\{([\s\S]+?)\}/g);
739
- let index = 0;
740
- for (const match of matches) {
741
- if (typeof match.index === "undefined")
742
- break;
743
- displayState.push({
744
- type: "text",
745
- value: key.substring(index, match.index)
746
- });
747
- let dsText = "";
748
- let ds;
749
- let fields;
750
- match[1].split(".").forEach((item, index2) => {
751
- if (index2 === 0) {
752
- ds = dataSources.find((ds2) => ds2.id === item);
753
- dsText += ds?.title || item;
754
- fields = ds?.fields;
755
- return;
756
- }
757
- const field = fields?.find((field2) => field2.name === item);
758
- fields = field?.fields;
759
- dsText += `.${field?.title || item}`;
760
- });
761
- displayState.push({
762
- type: "var",
763
- value: dsText
764
- });
765
- index = match.index + match[0].length;
766
- }
767
- if (index < key.length) {
768
- displayState.push({
769
- type: "text",
770
- value: key.substring(index)
771
- });
772
- }
773
- return displayState;
774
- };
775
-
776
- const compose = (middleware, isAsync) => {
777
- if (!Array.isArray(middleware))
778
- throw new TypeError("Middleware 必须是一个数组!");
779
- for (const fn of middleware) {
780
- if (typeof fn !== "function")
781
- throw new TypeError("Middleware 必须由函数组成!");
782
- }
783
- return (args, next) => {
784
- let index = -1;
785
- return dispatch(0);
786
- function dispatch(i) {
787
- if (i <= index) {
788
- const error = new Error("next() 被多次调用");
789
- if (isAsync) {
790
- return Promise.reject(error);
791
- }
792
- throw error;
793
- }
794
- index = i;
795
- let fn = middleware[i];
796
- if (i === middleware.length && next)
797
- fn = next;
798
- if (!fn) {
799
- if (isAsync) {
800
- return Promise.resolve();
801
- }
802
- return;
803
- }
804
- if (isAsync) {
805
- try {
806
- return Promise.resolve(fn(...args, dispatch.bind(null, i + 1)));
807
- } catch (err) {
808
- return Promise.reject(err);
809
- }
810
- }
811
- try {
812
- return fn(...args, dispatch.bind(null, i + 1));
813
- } catch (err) {
814
- throw err;
815
- }
816
- }
817
- };
818
- };
819
-
820
- const methodName = (prefix, name) => `${prefix}${name[0].toUpperCase()}${name.substring(1)}`;
821
- const isError = (error) => Object.prototype.toString.call(error) === "[object Error]";
822
- const doAction = (args, scope, sourceMethod, beforeMethodName, afterMethodName, fn) => {
823
- try {
824
- let beforeArgs = args;
825
- for (const beforeMethod of scope.pluginOptionsList[beforeMethodName]) {
826
- beforeArgs = beforeMethod(...beforeArgs) || [];
827
- if (isError(beforeArgs))
828
- throw beforeArgs;
829
- if (!Array.isArray(beforeArgs)) {
830
- beforeArgs = [beforeArgs];
831
- }
832
- }
833
- let returnValue = fn(beforeArgs, sourceMethod.bind(scope));
834
- for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
835
- returnValue = afterMethod(returnValue, ...beforeArgs);
836
- if (isError(returnValue))
837
- throw returnValue;
838
- }
839
- return returnValue;
840
- } catch (error) {
841
- throw error;
842
- }
843
- };
844
- const doAsyncAction = async (args, scope, sourceMethod, beforeMethodName, afterMethodName, fn) => {
845
- try {
846
- let beforeArgs = args;
847
- for (const beforeMethod of scope.pluginOptionsList[beforeMethodName]) {
848
- beforeArgs = await beforeMethod(...beforeArgs) || [];
849
- if (isError(beforeArgs))
850
- throw beforeArgs;
851
- if (!Array.isArray(beforeArgs)) {
852
- beforeArgs = [beforeArgs];
853
- }
854
- }
855
- let returnValue = await fn(beforeArgs, sourceMethod.bind(scope));
856
- for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
857
- returnValue = await afterMethod(returnValue, ...beforeArgs);
858
- if (isError(returnValue))
859
- throw returnValue;
860
- }
861
- return returnValue;
862
- } catch (error) {
863
- throw error;
576
+ const doAsyncAction = async (args, scope, sourceMethod, beforeMethodName, afterMethodName, fn) => {
577
+ try {
578
+ let beforeArgs = args;
579
+ for (const beforeMethod of scope.pluginOptionsList[beforeMethodName]) {
580
+ beforeArgs = await beforeMethod(...beforeArgs) || [];
581
+ if (isError(beforeArgs))
582
+ throw beforeArgs;
583
+ if (!Array.isArray(beforeArgs)) {
584
+ beforeArgs = [beforeArgs];
585
+ }
586
+ }
587
+ let returnValue = await fn(beforeArgs, sourceMethod.bind(scope));
588
+ for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
589
+ returnValue = await afterMethod(returnValue, ...beforeArgs);
590
+ if (isError(returnValue))
591
+ throw returnValue;
592
+ }
593
+ return returnValue;
594
+ } catch (error) {
595
+ throw error;
864
596
  }
865
597
  };
866
598
  class BaseService extends EventEmitter {
@@ -936,590 +668,500 @@ class BaseService extends EventEmitter {
936
668
  }
937
669
  }
938
670
 
939
- const canUsePluginMethods$6 = {
940
- async: [],
941
- sync: [
942
- "getFormConfig",
943
- "setFormConfig",
944
- "getFormValue",
945
- "setFormValue",
946
- "getFormEvent",
947
- "setFormEvent",
948
- "getFormMethod",
949
- "setFormMethod",
950
- "add",
951
- "update",
952
- "remove",
953
- "createId"
954
- ]
955
- };
956
- class DataSource extends BaseService {
957
- state = reactive({
958
- datasourceTypeList: [],
959
- dataSources: [],
960
- editable: true,
961
- configs: {},
962
- values: {},
963
- events: {},
964
- methods: {}
965
- });
966
- constructor() {
967
- super(canUsePluginMethods$6.sync.map((methodName) => ({ name: methodName, isAsync: false })));
671
+ class Dep extends BaseService {
672
+ watcher = new Watcher({ initialTargets: reactive({}) });
673
+ removeTargets(type = DepTargetType.DEFAULT) {
674
+ this.watcher.removeTargets(type);
675
+ this.emit("remove-target");
968
676
  }
969
- set(name, value) {
970
- this.state[name] = value;
677
+ getTargets(type = DepTargetType.DEFAULT) {
678
+ return this.watcher.getTargets(type);
971
679
  }
972
- get(name) {
973
- return this.state[name];
680
+ getTarget(id, type = DepTargetType.DEFAULT) {
681
+ return this.watcher.getTarget(id, type);
974
682
  }
975
- getFormConfig(type = "base") {
976
- return getFormConfig(toLine(type), this.get("configs"));
683
+ addTarget(target) {
684
+ this.watcher.addTarget(target);
685
+ this.emit("add-target", target);
977
686
  }
978
- setFormConfig(type, config) {
979
- this.get("configs")[toLine(type)] = config;
687
+ removeTarget(id, type = DepTargetType.DEFAULT) {
688
+ this.watcher.removeTarget(id, type);
689
+ this.emit("remove-target");
980
690
  }
981
- getFormValue(type = "base") {
982
- return getFormValue(toLine(type), this.get("values")[type]);
691
+ clearTargets() {
692
+ this.watcher.clearTargets();
983
693
  }
984
- setFormValue(type, value) {
985
- this.get("values")[toLine(type)] = value;
694
+ collect(nodes, deep = false, type) {
695
+ this.watcher.collect(nodes, deep, type);
696
+ this.emit("collected", nodes, deep);
986
697
  }
987
- getFormEvent(type = "base") {
988
- return this.get("events")[toLine(type)] || [];
698
+ clear(nodes) {
699
+ return this.watcher.clear(nodes);
989
700
  }
990
- setFormEvent(type, value = []) {
991
- this.get("events")[toLine(type)] = value;
701
+ clearByType(type, nodes) {
702
+ return this.watcher.clearByType(type, nodes);
992
703
  }
993
- getFormMethod(type = "base") {
994
- return this.get("methods")[toLine(type)] || [];
704
+ hasTarget(id, type = DepTargetType.DEFAULT) {
705
+ return this.watcher.hasTarget(id, type);
995
706
  }
996
- setFormMethod(type, value = []) {
997
- this.get("methods")[toLine(type)] = value;
707
+ hasSpecifiedTypeTarget(type = DepTargetType.DEFAULT) {
708
+ return this.watcher.hasSpecifiedTypeTarget(type);
998
709
  }
999
- add(config) {
1000
- const newConfig = {
1001
- ...config,
1002
- id: this.createId()
1003
- };
1004
- this.get("dataSources").push(newConfig);
1005
- this.emit("add", newConfig);
1006
- return newConfig;
710
+ }
711
+ const depService = new Dep();
712
+
713
+ const canUsePluginMethods$6 = {
714
+ async: [
715
+ "setPropsConfig",
716
+ "getPropsConfig",
717
+ "setPropsValue",
718
+ "getPropsValue",
719
+ "fillConfig",
720
+ "getDefaultPropsValue"
721
+ ],
722
+ sync: ["createId", "setNewItemId"]
723
+ };
724
+ class Props extends BaseService {
725
+ state = reactive({
726
+ propsConfigMap: {},
727
+ propsValueMap: {},
728
+ relateIdMap: {}
729
+ });
730
+ constructor() {
731
+ super([
732
+ ...canUsePluginMethods$6.async.map((methodName) => ({ name: methodName, isAsync: true })),
733
+ ...canUsePluginMethods$6.sync.map((methodName) => ({ name: methodName, isAsync: false }))
734
+ ]);
1007
735
  }
1008
- update(config) {
1009
- const dataSources = this.get("dataSources");
1010
- const index = dataSources.findIndex((ds) => ds.id === config.id);
1011
- dataSources[index] = cloneDeep(config);
1012
- this.emit("update", config);
1013
- return config;
736
+ setPropsConfigs(configs) {
737
+ Object.keys(configs).forEach((type) => {
738
+ this.setPropsConfig(toLine(type), configs[type]);
739
+ });
740
+ this.emit("props-configs-change");
1014
741
  }
1015
- remove(id) {
1016
- const dataSources = this.get("dataSources");
1017
- const index = dataSources.findIndex((ds) => ds.id === id);
1018
- dataSources.splice(index, 1);
1019
- this.emit("remove", id);
742
+ async fillConfig(config, labelWidth) {
743
+ return fillConfig(config, typeof labelWidth !== "function" ? labelWidth : "80px");
1020
744
  }
1021
- createId() {
1022
- return `ds_${guid()}`;
745
+ async setPropsConfig(type, config) {
746
+ this.state.propsConfigMap[toLine(type)] = await this.fillConfig(Array.isArray(config) ? config : [config]);
1023
747
  }
1024
- getDataSourceById(id) {
1025
- return this.get("dataSources").find((ds) => ds.id === id);
748
+ /**
749
+ * 获取指点类型的组件属性表单配置
750
+ * @param type 组件类型
751
+ * @returns 组件属性表单配置
752
+ */
753
+ async getPropsConfig(type) {
754
+ if (type === "area") {
755
+ return await this.getPropsConfig("button");
756
+ }
757
+ return cloneDeep(this.state.propsConfigMap[toLine(type)] || await this.fillConfig([]));
758
+ }
759
+ setPropsValues(values) {
760
+ Object.keys(values).forEach((type) => {
761
+ this.setPropsValue(toLine(type), values[type]);
762
+ });
763
+ }
764
+ /**
765
+ * 为指点类型组件设置组件初始值
766
+ * @param type 组件类型
767
+ * @param value 组件初始值
768
+ */
769
+ async setPropsValue(type, value) {
770
+ this.state.propsValueMap[toLine(type)] = value;
771
+ }
772
+ /**
773
+ * 获取指定类型的组件初始值
774
+ * @param type 组件类型
775
+ * @returns 组件初始值
776
+ */
777
+ async getPropsValue(componentType, { inputEvent, ...defaultValue } = {}) {
778
+ const type = toLine(componentType);
779
+ if (type === "area") {
780
+ const value = await this.getPropsValue("button");
781
+ value.className = "action-area";
782
+ value.text = "";
783
+ if (value.style) {
784
+ value.style.backgroundColor = "rgba(255, 255, 255, 0)";
785
+ }
786
+ return value;
787
+ }
788
+ const id = this.createId(type);
789
+ const defaultPropsValue = this.getDefaultPropsValue(type);
790
+ const data = this.setNewItemId(
791
+ cloneDeep({
792
+ type,
793
+ ...defaultValue
794
+ })
795
+ );
796
+ return {
797
+ id,
798
+ ...defaultPropsValue,
799
+ ...mergeWith({}, cloneDeep(this.state.propsValueMap[type] || {}), data)
800
+ };
801
+ }
802
+ createId(type) {
803
+ return `${type}_${guid()}`;
804
+ }
805
+ /**
806
+ * 将组件与组件的子元素配置中的id都设置成一个新的ID
807
+ * 如果没有相同ID并且force为false则保持不变
808
+ * @param {Object} config 组件配置
809
+ * @param {Boolean} force 是否强制设置新的ID
810
+ */
811
+ /* eslint no-param-reassign: ["error", { "props": false }] */
812
+ setNewItemId(config, force = true) {
813
+ if (force || editorService.getNodeById(config.id)) {
814
+ const newId = this.createId(config.type || "component");
815
+ this.setRelateId(config.id, newId);
816
+ config.id = newId;
817
+ }
818
+ if (config.items && Array.isArray(config.items)) {
819
+ for (const item of config.items) {
820
+ this.setNewItemId(item);
821
+ }
822
+ }
823
+ return config;
824
+ }
825
+ /**
826
+ * 获取默认属性配置
827
+ * @param type 组件类型
828
+ * @returns Object
829
+ */
830
+ getDefaultPropsValue(type) {
831
+ return ["page", "container"].includes(type) ? {
832
+ type,
833
+ layout: "absolute",
834
+ style: {},
835
+ name: type,
836
+ items: []
837
+ } : {
838
+ type,
839
+ style: {},
840
+ name: type
841
+ };
1026
842
  }
1027
843
  resetState() {
1028
- this.set("dataSources", []);
844
+ this.state.propsConfigMap = {};
845
+ this.state.propsValueMap = {};
846
+ }
847
+ /**
848
+ * 替换关联ID
849
+ * @param originConfigs 原组件配置
850
+ * @param targetConfigs 待替换的组件配置
851
+ */
852
+ replaceRelateId(originConfigs, targetConfigs) {
853
+ const relateIdMap = this.getRelateIdMap();
854
+ if (Object.keys(relateIdMap).length === 0)
855
+ return;
856
+ depService.clearByType(DepTargetType.RELATED_COMP_WHEN_COPY);
857
+ depService.collect(originConfigs, true, DepTargetType.RELATED_COMP_WHEN_COPY);
858
+ const target = depService.getTarget(DepTargetType.RELATED_COMP_WHEN_COPY, DepTargetType.RELATED_COMP_WHEN_COPY);
859
+ if (!target)
860
+ return;
861
+ originConfigs.forEach((config) => {
862
+ const newId = relateIdMap[config.id];
863
+ const path = getNodePath(newId, targetConfigs);
864
+ const targetConfig = path[path.length - 1];
865
+ if (!targetConfig)
866
+ return;
867
+ target.deps[config.id]?.keys?.forEach((fullKey) => {
868
+ const relateOriginId = getValueByKeyPath(fullKey, config);
869
+ const relateTargetId = relateIdMap[relateOriginId];
870
+ if (!relateTargetId)
871
+ return;
872
+ setValueByKeyPath(fullKey, relateTargetId, targetConfig);
873
+ });
874
+ if (config.items && Array.isArray(config.items)) {
875
+ this.replaceRelateId(config.items, targetConfigs);
876
+ }
877
+ });
878
+ }
879
+ /**
880
+ * 清除setNewItemId前后映射关系
881
+ */
882
+ clearRelateId() {
883
+ this.state.relateIdMap = {};
1029
884
  }
1030
885
  destroy() {
1031
- this.removeAllListeners();
1032
886
  this.resetState();
887
+ this.removeAllListeners();
1033
888
  this.removeAllPlugins();
1034
889
  }
1035
890
  usePlugin(options) {
1036
891
  super.usePlugin(options);
1037
892
  }
893
+ /**
894
+ * 获取setNewItemId前后映射关系
895
+ * @param oldId 原组件ID
896
+ * @returns 新旧ID映射
897
+ */
898
+ getRelateIdMap() {
899
+ return this.state.relateIdMap;
900
+ }
901
+ /**
902
+ * 记录setNewItemId前后映射关系
903
+ * @param oldId 原组件ID
904
+ * @param newId 分配的新ID
905
+ */
906
+ setRelateId(oldId, newId) {
907
+ this.state.relateIdMap[oldId] = newId;
908
+ }
1038
909
  }
1039
- const dataSourceService = new DataSource();
910
+ const propsService = new Props();
1040
911
 
1041
- const arrayOptions = [
1042
- { text: "包含", value: "include" },
1043
- { text: "不包含", value: "not_include" }
1044
- ];
1045
- const eqOptions = [
1046
- { text: "等于", value: "=" },
1047
- { text: "不等于", value: "!=" }
1048
- ];
1049
- const numberOptions = [
1050
- { text: "大于", value: ">" },
1051
- { text: "大于等于", value: ">=" },
1052
- { text: "小于", value: "<" },
1053
- { text: "小于等于", value: "<=" },
1054
- { text: "在范围内", value: "between" },
1055
- { text: "不在范围内", value: "not_between" }
1056
- ];
1057
- const styleTabConfig = {
1058
- title: "样式",
1059
- items: [
1060
- {
1061
- name: "style",
1062
- items: [
1063
- {
1064
- type: "fieldset",
1065
- legend: "位置",
1066
- items: [
1067
- {
1068
- type: "data-source-field-select",
1069
- name: "position",
1070
- text: "固定定位",
1071
- checkStrictly: false,
1072
- dataSourceFieldType: ["string"],
1073
- fieldConfig: {
1074
- type: "checkbox",
1075
- activeValue: "fixed",
1076
- inactiveValue: "absolute",
1077
- defaultValue: "absolute"
1078
- }
1079
- },
1080
- {
1081
- type: "data-source-field-select",
1082
- name: "left",
1083
- text: "left",
1084
- checkStrictly: false,
1085
- dataSourceFieldType: ["string", "number"],
1086
- fieldConfig: {
1087
- type: "text"
1088
- }
1089
- },
1090
- {
1091
- type: "data-source-field-select",
1092
- name: "top",
1093
- text: "top",
1094
- checkStrictly: false,
1095
- dataSourceFieldType: ["string", "number"],
1096
- fieldConfig: {
1097
- type: "text"
1098
- },
1099
- disabled: (vm, { model }) => model.position === "fixed" && model._magic_position === "fixedBottom"
1100
- },
1101
- {
1102
- type: "data-source-field-select",
1103
- name: "right",
1104
- text: "right",
1105
- checkStrictly: false,
1106
- dataSourceFieldType: ["string", "number"],
1107
- fieldConfig: {
1108
- type: "text"
1109
- }
1110
- },
1111
- {
1112
- type: "data-source-field-select",
1113
- name: "bottom",
1114
- text: "bottom",
1115
- checkStrictly: false,
1116
- dataSourceFieldType: ["string", "number"],
1117
- fieldConfig: {
1118
- type: "text"
1119
- },
1120
- disabled: (vm, { model }) => model.position === "fixed" && model._magic_position === "fixedTop"
1121
- }
1122
- ]
1123
- },
1124
- {
1125
- type: "fieldset",
1126
- legend: "盒子",
1127
- items: [
1128
- {
1129
- type: "data-source-field-select",
1130
- name: "width",
1131
- text: "宽度",
1132
- checkStrictly: false,
1133
- dataSourceFieldType: ["string", "number"],
1134
- fieldConfig: {
1135
- type: "text"
1136
- }
1137
- },
1138
- {
1139
- type: "data-source-field-select",
1140
- name: "height",
1141
- text: "高度",
1142
- checkStrictly: false,
1143
- dataSourceFieldType: ["string", "number"],
1144
- fieldConfig: {
1145
- type: "text"
1146
- }
1147
- },
1148
- {
1149
- type: "data-source-field-select",
1150
- text: "overflow",
1151
- name: "overflow",
1152
- checkStrictly: false,
1153
- dataSourceFieldType: ["string"],
1154
- fieldConfig: {
1155
- type: "select",
1156
- options: [
1157
- { text: "visible", value: "visible" },
1158
- { text: "hidden", value: "hidden" },
1159
- { text: "clip", value: "clip" },
1160
- { text: "scroll", value: "scroll" },
1161
- { text: "auto", value: "auto" },
1162
- { text: "overlay", value: "overlay" }
1163
- ]
1164
- }
1165
- }
1166
- ]
1167
- },
1168
- {
1169
- type: "fieldset",
1170
- legend: "边框",
1171
- items: [
1172
- {
1173
- type: "data-source-field-select",
1174
- name: "borderWidth",
1175
- text: "宽度",
1176
- defaultValue: "0",
1177
- checkStrictly: false,
1178
- dataSourceFieldType: ["string", "number"],
1179
- fieldConfig: {
1180
- type: "text"
1181
- }
1182
- },
1183
- {
1184
- type: "data-source-field-select",
1185
- name: "borderColor",
1186
- text: "颜色",
1187
- checkStrictly: false,
1188
- dataSourceFieldType: ["string"],
1189
- fieldConfig: {
1190
- type: "text"
1191
- }
1192
- },
1193
- {
1194
- type: "data-source-field-select",
1195
- name: "borderStyle",
1196
- text: "样式",
1197
- defaultValue: "none",
1198
- checkStrictly: false,
1199
- dataSourceFieldType: ["string"],
1200
- fieldConfig: {
1201
- type: "select",
1202
- options: [
1203
- { text: "none", value: "none" },
1204
- { text: "hidden", value: "hidden" },
1205
- { text: "dotted", value: "dotted" },
1206
- { text: "dashed", value: "dashed" },
1207
- { text: "solid", value: "solid" },
1208
- { text: "double", value: "double" },
1209
- { text: "groove", value: "groove" },
1210
- { text: "ridge", value: "ridge" },
1211
- { text: "inset", value: "inset" },
1212
- { text: "outset", value: "outset" }
1213
- ]
1214
- }
1215
- }
1216
- ]
1217
- },
1218
- {
1219
- type: "fieldset",
1220
- legend: "背景",
1221
- items: [
1222
- {
1223
- type: "data-source-field-select",
1224
- name: "backgroundImage",
1225
- text: "背景图",
1226
- checkStrictly: false,
1227
- dataSourceFieldType: ["string"],
1228
- fieldConfig: {
1229
- type: "text"
1230
- }
1231
- },
1232
- {
1233
- type: "data-source-field-select",
1234
- name: "backgroundColor",
1235
- text: "背景颜色",
1236
- checkStrictly: false,
1237
- dataSourceFieldType: ["string"],
1238
- fieldConfig: {
1239
- type: "colorPicker"
1240
- }
1241
- },
1242
- {
1243
- type: "data-source-field-select",
1244
- name: "backgroundRepeat",
1245
- text: "背景图重复",
1246
- defaultValue: "no-repeat",
1247
- checkStrictly: false,
1248
- dataSourceFieldType: ["string"],
1249
- fieldConfig: {
1250
- type: "select",
1251
- options: [
1252
- { text: "repeat", value: "repeat" },
1253
- { text: "repeat-x", value: "repeat-x" },
1254
- { text: "repeat-y", value: "repeat-y" },
1255
- { text: "no-repeat", value: "no-repeat" },
1256
- { text: "inherit", value: "inherit" }
1257
- ]
1258
- }
1259
- },
1260
- {
1261
- type: "data-source-field-select",
1262
- name: "backgroundSize",
1263
- text: "背景图大小",
1264
- defaultValue: "100% 100%",
1265
- checkStrictly: false,
1266
- dataSourceFieldType: ["string"],
1267
- fieldConfig: {
1268
- type: "text"
1269
- }
1270
- }
1271
- ]
1272
- },
1273
- {
1274
- type: "fieldset",
1275
- legend: "字体",
1276
- items: [
1277
- {
1278
- type: "data-source-field-select",
1279
- name: "color",
1280
- text: "颜色",
1281
- checkStrictly: false,
1282
- dataSourceFieldType: ["string"],
1283
- fieldConfig: {
1284
- type: "colorPicker"
1285
- }
1286
- },
1287
- {
1288
- type: "data-source-field-select",
1289
- name: "fontSize",
1290
- text: "大小",
1291
- checkStrictly: false,
1292
- dataSourceFieldType: ["string", "number"],
1293
- fieldConfig: {
1294
- type: "text"
1295
- }
1296
- },
1297
- {
1298
- type: "data-source-field-select",
1299
- name: "fontWeight",
1300
- text: "粗细",
1301
- checkStrictly: false,
1302
- dataSourceFieldType: ["string", "number"],
1303
- fieldConfig: {
1304
- type: "text"
1305
- }
1306
- }
1307
- ]
1308
- },
1309
- {
1310
- type: "fieldset",
1311
- legend: "变形",
1312
- name: "transform",
1313
- items: [
1314
- {
1315
- type: "data-source-field-select",
1316
- name: "rotate",
1317
- text: "旋转角度",
1318
- checkStrictly: false,
1319
- dataSourceFieldType: ["string"],
1320
- fieldConfig: {
1321
- type: "text"
1322
- }
1323
- },
1324
- {
1325
- type: "data-source-field-select",
1326
- name: "scale",
1327
- text: "缩放",
1328
- checkStrictly: false,
1329
- dataSourceFieldType: ["number", "string"],
1330
- fieldConfig: {
1331
- type: "text"
1332
- }
1333
- }
1334
- ]
1335
- }
1336
- ]
1337
- }
1338
- ]
1339
- };
1340
- const eventTabConfig = {
1341
- title: "事件",
1342
- items: [
1343
- {
1344
- name: "events",
1345
- src: "component",
1346
- labelWidth: "100px",
1347
- type: "event-select"
1348
- }
1349
- ]
1350
- };
1351
- const advancedTabConfig = {
1352
- title: "高级",
1353
- lazy: true,
1354
- items: [
1355
- {
1356
- name: "created",
1357
- text: "created",
1358
- type: "code-select"
1359
- },
1360
- {
1361
- name: "mounted",
1362
- text: "mounted",
1363
- type: "code-select"
1364
- }
1365
- ]
1366
- };
1367
- const displayTabConfig = {
1368
- title: "显示条件",
1369
- display: (vm, { model }) => model.type !== "page",
1370
- items: [
1371
- {
1372
- type: "groupList",
1373
- name: "displayConds",
1374
- titlePrefix: "条件组",
1375
- expandAll: true,
1376
- items: [
1377
- {
1378
- type: "table",
1379
- name: "cond",
1380
- items: [
1381
- {
1382
- type: "data-source-field-select",
1383
- name: "field",
1384
- value: "key",
1385
- label: "字段",
1386
- checkStrictly: false,
1387
- dataSourceFieldType: ["string", "number", "boolean", "any"]
1388
- },
1389
- {
1390
- type: "select",
1391
- options: (mForm, { model }) => {
1392
- const [id, ...fieldNames] = model.field;
1393
- const ds = dataSourceService.getDataSourceById(id);
1394
- let fields = ds?.fields || [];
1395
- let type = "";
1396
- (fieldNames || []).forEach((fieldName) => {
1397
- const field = fields.find((f) => f.name === fieldName);
1398
- fields = field?.fields || [];
1399
- type = field?.type || "";
1400
- });
1401
- if (type === "array") {
1402
- return arrayOptions;
1403
- }
1404
- if (type === "boolean") {
1405
- return [
1406
- { text: "是", value: "is" },
1407
- { text: "不是", value: "not" }
1408
- ];
1409
- }
1410
- if (type === "number") {
1411
- return [...eqOptions, ...numberOptions];
1412
- }
1413
- if (type === "string") {
1414
- return [...arrayOptions, ...eqOptions];
1415
- }
1416
- return [...arrayOptions, ...eqOptions, ...numberOptions];
1417
- },
1418
- label: "条件",
1419
- name: "op"
1420
- },
1421
- {
1422
- label: "值",
1423
- items: [
1424
- {
1425
- name: "value",
1426
- type: (mForm, { model }) => {
1427
- const [id, ...fieldNames] = model.field;
1428
- const ds = dataSourceService.getDataSourceById(id);
1429
- let fields = ds?.fields || [];
1430
- let type = "";
1431
- (fieldNames || []).forEach((fieldName) => {
1432
- const field = fields.find((f) => f.name === fieldName);
1433
- fields = field?.fields || [];
1434
- type = field?.type || "";
1435
- });
1436
- if (type === "number") {
1437
- return "number";
1438
- }
1439
- if (type === "boolean") {
1440
- return "select";
1441
- }
1442
- return "text";
1443
- },
1444
- options: [
1445
- { text: "true", value: true },
1446
- { text: "false", value: false }
1447
- ],
1448
- display: (vm, { model }) => !["between", "not_between"].includes(model.op)
1449
- },
1450
- {
1451
- name: "range",
1452
- type: "number-range",
1453
- display: (vm, { model }) => ["between", "not_between"].includes(model.op)
1454
- }
1455
- ]
1456
- }
1457
- ]
1458
- }
1459
- ]
912
+ class UndoRedo {
913
+ elementList;
914
+ listCursor;
915
+ listMaxSize;
916
+ constructor(listMaxSize = 20) {
917
+ const minListMaxSize = 2;
918
+ this.elementList = [];
919
+ this.listCursor = 0;
920
+ this.listMaxSize = listMaxSize > minListMaxSize ? listMaxSize : minListMaxSize;
921
+ }
922
+ pushElement(element) {
923
+ this.elementList.splice(this.listCursor, this.elementList.length - this.listCursor, cloneDeep(element));
924
+ this.listCursor += 1;
925
+ if (this.elementList.length > this.listMaxSize) {
926
+ this.elementList.shift();
927
+ this.listCursor -= 1;
1460
928
  }
1461
- ]
1462
- };
1463
- const fillConfig = (config = [], labelWidth = "80px") => [
1464
- {
1465
- type: "tab",
1466
- labelWidth,
1467
- items: [
1468
- {
1469
- title: "属性",
1470
- items: [
1471
- // 组件类型,必须要有
1472
- {
1473
- text: "type",
1474
- name: "type",
1475
- type: "hidden"
1476
- },
1477
- // 组件id,必须要有
1478
- {
1479
- name: "id",
1480
- type: "display",
1481
- text: "id"
1482
- },
1483
- {
1484
- name: "name",
1485
- text: "组件名称"
1486
- },
1487
- ...config
1488
- ]
1489
- },
1490
- { ...styleTabConfig },
1491
- { ...eventTabConfig },
1492
- { ...advancedTabConfig },
1493
- { ...displayTabConfig }
1494
- ]
1495
929
  }
1496
- ];
930
+ canUndo() {
931
+ return this.listCursor > 1;
932
+ }
933
+ // 返回undo后的当前元素
934
+ undo() {
935
+ if (!this.canUndo()) {
936
+ return null;
937
+ }
938
+ this.listCursor -= 1;
939
+ return this.getCurrentElement();
940
+ }
941
+ canRedo() {
942
+ return this.elementList.length > this.listCursor;
943
+ }
944
+ // 返回redo后的当前元素
945
+ redo() {
946
+ if (!this.canRedo()) {
947
+ return null;
948
+ }
949
+ this.listCursor += 1;
950
+ return this.getCurrentElement();
951
+ }
952
+ getCurrentElement() {
953
+ if (this.listCursor < 1) {
954
+ return null;
955
+ }
956
+ return cloneDeep(this.elementList[this.listCursor - 1]);
957
+ }
958
+ }
1497
959
 
1498
- const log = (...args) => {
1499
- if (process.env.NODE_ENV === "development") {
1500
- console.log("magic editor: ", ...args);
960
+ class History extends BaseService {
961
+ state = reactive({
962
+ pageSteps: {},
963
+ pageId: void 0,
964
+ canRedo: false,
965
+ canUndo: false
966
+ });
967
+ constructor() {
968
+ super([]);
969
+ this.on("change", this.setCanUndoRedo);
970
+ }
971
+ reset() {
972
+ this.state.pageSteps = {};
973
+ this.resetPage();
974
+ }
975
+ resetPage() {
976
+ this.state.pageId = void 0;
977
+ this.state.canRedo = false;
978
+ this.state.canUndo = false;
979
+ }
980
+ changePage(page) {
981
+ if (!page)
982
+ return;
983
+ this.state.pageId = page.id;
984
+ if (!this.state.pageSteps[this.state.pageId]) {
985
+ const undoRedo = new UndoRedo();
986
+ undoRedo.pushElement({
987
+ data: page,
988
+ modifiedNodeIds: /* @__PURE__ */ new Map(),
989
+ nodeId: page.id
990
+ });
991
+ this.state.pageSteps[this.state.pageId] = undoRedo;
992
+ }
993
+ this.setCanUndoRedo();
994
+ this.emit("page-change", this.state.pageSteps[this.state.pageId]);
995
+ }
996
+ resetState() {
997
+ this.state.pageId = void 0;
998
+ this.state.pageSteps = {};
999
+ this.state.canRedo = false;
1000
+ this.state.canUndo = false;
1001
+ }
1002
+ push(state) {
1003
+ const undoRedo = this.getUndoRedo();
1004
+ if (!undoRedo)
1005
+ return null;
1006
+ undoRedo.pushElement(state);
1007
+ this.emit("change", state);
1008
+ return state;
1009
+ }
1010
+ undo() {
1011
+ const undoRedo = this.getUndoRedo();
1012
+ if (!undoRedo)
1013
+ return null;
1014
+ const state = undoRedo.undo();
1015
+ this.emit("change", state);
1016
+ return state;
1017
+ }
1018
+ redo() {
1019
+ const undoRedo = this.getUndoRedo();
1020
+ if (!undoRedo)
1021
+ return null;
1022
+ const state = undoRedo.redo();
1023
+ this.emit("change", state);
1024
+ return state;
1025
+ }
1026
+ destroy() {
1027
+ this.resetState();
1028
+ this.removeAllListeners();
1029
+ this.removeAllPlugins();
1030
+ }
1031
+ getUndoRedo() {
1032
+ if (!this.state.pageId)
1033
+ return null;
1034
+ return this.state.pageSteps[this.state.pageId];
1035
+ }
1036
+ setCanUndoRedo() {
1037
+ const undoRedo = this.getUndoRedo();
1038
+ this.state.canRedo = undoRedo?.canRedo() || false;
1039
+ this.state.canUndo = undoRedo?.canUndo() || false;
1501
1040
  }
1041
+ }
1042
+ const historyService = new History();
1043
+
1044
+ var Protocol = /* @__PURE__ */ ((Protocol2) => {
1045
+ Protocol2["OBJECT"] = "object";
1046
+ Protocol2["JSON"] = "json";
1047
+ Protocol2["STRING"] = "string";
1048
+ Protocol2["NUMBER"] = "number";
1049
+ Protocol2["BOOLEAN"] = "boolean";
1050
+ return Protocol2;
1051
+ })(Protocol || {});
1052
+ const canUsePluginMethods$5 = {
1053
+ async: [],
1054
+ sync: ["getStorage", "getNamespace", "clear", "getItem", "removeItem", "setItem"]
1502
1055
  };
1503
- const info = (...args) => {
1504
- if (process.env.NODE_ENV === "development") {
1505
- console.info("magic editor: ", ...args);
1056
+ class WebStorage extends BaseService {
1057
+ storage = globalThis.localStorage;
1058
+ namespace = "tmagic";
1059
+ constructor() {
1060
+ super(canUsePluginMethods$5.sync.map((methodName) => ({ name: methodName, isAsync: false })));
1061
+ }
1062
+ /**
1063
+ * 获取数据存储对象,可以通过
1064
+ * const storageService = new StorageService();
1065
+ * storageService.usePlugin({
1066
+ * // 替换存储对象为 sessionStorage
1067
+ * async afterGetStorage(): Promise<Storage> {
1068
+ * return window.sessionStorage;
1069
+ * },
1070
+ * });
1071
+ */
1072
+ getStorage() {
1073
+ return this.storage;
1074
+ }
1075
+ getNamespace() {
1076
+ return this.namespace;
1077
+ }
1078
+ /**
1079
+ * 清理,支持storageService.usePlugin
1080
+ */
1081
+ clear() {
1082
+ const storage = this.getStorage();
1083
+ storage.clear();
1084
+ }
1085
+ /**
1086
+ * 获取存储项,支持storageService.usePlugin
1087
+ */
1088
+ getItem(key, options = {}) {
1089
+ const storage = this.getStorage();
1090
+ const namespace = this.getNamespace();
1091
+ const { protocol = options.protocol, item } = this.getValueAndProtocol(
1092
+ storage.getItem(`${options.namespace || namespace}:${key}`)
1093
+ );
1094
+ if (item === null)
1095
+ return null;
1096
+ switch (protocol) {
1097
+ case "object" /* OBJECT */:
1098
+ return getConfig("parseDSL")(`(${item})`);
1099
+ case "json" /* JSON */:
1100
+ return JSON.parse(item);
1101
+ case "number" /* NUMBER */:
1102
+ return Number(item);
1103
+ case "boolean" /* BOOLEAN */:
1104
+ return Boolean(item);
1105
+ default:
1106
+ return item;
1107
+ }
1108
+ }
1109
+ /**
1110
+ * 获取指定索引位置的key
1111
+ */
1112
+ key(index) {
1113
+ const storage = this.getStorage();
1114
+ return storage.key(index);
1115
+ }
1116
+ /**
1117
+ * 移除存储项,支持storageService.usePlugin
1118
+ */
1119
+ removeItem(key, options = {}) {
1120
+ const storage = this.getStorage();
1121
+ const namespace = this.getNamespace();
1122
+ storage.removeItem(`${options.namespace || namespace}:${key}`);
1506
1123
  }
1507
- };
1508
- const warn = (...args) => {
1509
- if (process.env.NODE_ENV === "development") {
1510
- console.warn("magic editor: ", ...args);
1124
+ /**
1125
+ * 设置存储项,支持storageService.usePlugin
1126
+ */
1127
+ setItem(key, value, options = {}) {
1128
+ const storage = this.getStorage();
1129
+ const namespace = this.getNamespace();
1130
+ let item = value;
1131
+ const protocol = options.protocol ? `${options.protocol}:` : "";
1132
+ if (typeof value === "string" /* STRING */ || typeof value === "number" /* NUMBER */) {
1133
+ item = `${protocol}${value}`;
1134
+ } else {
1135
+ item = `${protocol}${serialize(value)}`;
1136
+ }
1137
+ storage.setItem(`${options.namespace || namespace}:${key}`, item);
1511
1138
  }
1512
- };
1513
- const debug = (...args) => {
1514
- if (process.env.NODE_ENV === "development") {
1515
- console.debug("magic editor: ", ...args);
1139
+ destroy() {
1140
+ this.removeAllListeners();
1141
+ this.removeAllPlugins();
1516
1142
  }
1517
- };
1518
- const error = (...args) => {
1519
- if (process.env.NODE_ENV === "development") {
1520
- console.error("magic editor: ", ...args);
1143
+ usePlugin(options) {
1144
+ super.usePlugin(options);
1521
1145
  }
1522
- };
1146
+ getValueAndProtocol(value) {
1147
+ let protocol = "";
1148
+ if (value === null) {
1149
+ return {
1150
+ item: value,
1151
+ protocol
1152
+ };
1153
+ }
1154
+ const item = value.replace(new RegExp(`^(${Object.values(Protocol).join("|")})(:)(.+)`), (_$0, $1, _$2, $3) => {
1155
+ protocol = $1;
1156
+ return $3;
1157
+ });
1158
+ return {
1159
+ protocol,
1160
+ item
1161
+ };
1162
+ }
1163
+ }
1164
+ const storageService = new WebStorage();
1523
1165
 
1524
1166
  var ColumnLayout = /* @__PURE__ */ ((ColumnLayout2) => {
1525
1167
  ColumnLayout2["LEFT"] = "left";
@@ -1588,6 +1230,8 @@ var DragType = /* @__PURE__ */ ((DragType2) => {
1588
1230
  const UI_SELECT_MODE_EVENT_NAME = "ui-select";
1589
1231
 
1590
1232
  const COPY_STORAGE_KEY = "$MagicEditorCopyData";
1233
+ const COPY_CODE_STORAGE_KEY = "$MagicEditorCopyCode";
1234
+ const COPY_DS_STORAGE_KEY = "$MagicEditorCopyDataSource";
1591
1235
  const getPageList = (root) => {
1592
1236
  if (!root)
1593
1237
  return [];
@@ -1634,11 +1278,13 @@ const getMiddleTop = (node, parentNode, stage) => {
1634
1278
  height = 0;
1635
1279
  }
1636
1280
  const { height: parentHeight } = parentNode.style;
1281
+ const { scrollTop = 0, wrapperHeight } = stage.mask;
1282
+ const wrapperHeightDeal = calcValueByFontsize(stage.renderer.getDocument(), wrapperHeight);
1283
+ const scrollTopDeal = calcValueByFontsize(stage.renderer.getDocument(), scrollTop);
1637
1284
  if (isPage(parentNode)) {
1638
- const { scrollTop = 0, wrapperHeight } = stage.mask;
1639
- return (wrapperHeight - height) / 2 + scrollTop;
1285
+ return (wrapperHeightDeal - height) / 2 + scrollTopDeal;
1640
1286
  }
1641
- return (parentHeight - height) / 2;
1287
+ return (Math.min(parentHeight, wrapperHeightDeal) - height) / 2;
1642
1288
  };
1643
1289
  const getInitPositionStyle = (style = {}, layout) => {
1644
1290
  if (layout === Layout.ABSOLUTE) {
@@ -1739,8 +1385,12 @@ const fixNodeLeft = (config, parent, doc) => {
1739
1385
  const el = doc.getElementById(`${config.id}`);
1740
1386
  const parentEl = doc.getElementById(`${parent.id}`);
1741
1387
  const left = Number(config.style?.left) || 0;
1742
- if (el && parentEl && el.offsetWidth + left > parentEl.offsetWidth) {
1743
- return parentEl.offsetWidth - el.offsetWidth;
1388
+ if (el && parentEl) {
1389
+ const calcParentOffsetWidth = calcValueByFontsize(doc, parentEl.offsetWidth);
1390
+ const calcElOffsetWidth = calcValueByFontsize(doc, el.offsetWidth);
1391
+ if (calcElOffsetWidth + left > calcParentOffsetWidth) {
1392
+ return calcParentOffsetWidth - calcElOffsetWidth;
1393
+ }
1744
1394
  }
1745
1395
  return config.style.left;
1746
1396
  };
@@ -1768,1421 +1418,1855 @@ const traverseNode = (node, cb, parents = []) => {
1768
1418
  }
1769
1419
  };
1770
1420
 
1771
- class Dep extends BaseService {
1772
- watcher = new Watcher({ initialTargets: reactive({}) });
1773
- removeTargets(type = DepTargetType.DEFAULT) {
1774
- this.watcher.removeTargets(type);
1775
- this.emit("remove-target");
1776
- }
1777
- getTargets(type = DepTargetType.DEFAULT) {
1778
- return this.watcher.getTargets(type);
1779
- }
1780
- getTarget(id, type = DepTargetType.DEFAULT) {
1781
- return this.watcher.getTarget(id, type);
1782
- }
1783
- addTarget(target) {
1784
- this.watcher.addTarget(target);
1785
- this.emit("add-target", target);
1786
- }
1787
- removeTarget(id, type = DepTargetType.DEFAULT) {
1788
- this.watcher.removeTarget(id, type);
1789
- this.emit("remove-target");
1790
- }
1791
- clearTargets() {
1792
- this.watcher.clearTargets();
1793
- }
1794
- collect(nodes, deep = false, type) {
1795
- this.watcher.collect(nodes, deep, type);
1796
- this.emit("collected", nodes, deep);
1797
- }
1798
- clear(nodes) {
1799
- return this.watcher.clear(nodes);
1800
- }
1801
- clearByType(type, nodes) {
1802
- return this.watcher.clearByType(type, nodes);
1803
- }
1804
- hasTarget(id, type = DepTargetType.DEFAULT) {
1805
- return this.watcher.hasTarget(id, type);
1806
- }
1807
- hasSpecifiedTypeTarget(type = DepTargetType.DEFAULT) {
1808
- return this.watcher.hasSpecifiedTypeTarget(type);
1809
- }
1810
- }
1811
- const depService = new Dep();
1812
-
1813
- const canUsePluginMethods$5 = {
1814
- async: [
1815
- "setPropsConfig",
1816
- "getPropsConfig",
1817
- "setPropsValue",
1818
- "getPropsValue",
1819
- "fillConfig",
1820
- "getDefaultPropsValue"
1821
- ],
1822
- sync: ["createId", "setNewItemId"]
1823
- };
1824
- class Props extends BaseService {
1825
- state = reactive({
1826
- propsConfigMap: {},
1827
- propsValueMap: {},
1828
- relateIdMap: {}
1829
- });
1830
- constructor() {
1831
- super([
1832
- ...canUsePluginMethods$5.async.map((methodName) => ({ name: methodName, isAsync: true })),
1833
- ...canUsePluginMethods$5.sync.map((methodName) => ({ name: methodName, isAsync: false }))
1834
- ]);
1835
- }
1836
- setPropsConfigs(configs) {
1837
- Object.keys(configs).forEach((type) => {
1838
- this.setPropsConfig(toLine(type), configs[type]);
1839
- });
1840
- this.emit("props-configs-change");
1841
- }
1842
- async fillConfig(config, labelWidth) {
1843
- return fillConfig(config, typeof labelWidth !== "function" ? labelWidth : "80px");
1844
- }
1845
- async setPropsConfig(type, config) {
1846
- this.state.propsConfigMap[toLine(type)] = await this.fillConfig(Array.isArray(config) ? config : [config]);
1847
- }
1848
- /**
1849
- * 获取指点类型的组件属性表单配置
1850
- * @param type 组件类型
1851
- * @returns 组件属性表单配置
1852
- */
1853
- async getPropsConfig(type) {
1854
- if (type === "area") {
1855
- return await this.getPropsConfig("button");
1421
+ const beforePaste = (position, config) => {
1422
+ if (!config[0]?.style)
1423
+ return config;
1424
+ const curNode = editorService.get("node");
1425
+ const { left: referenceLeft, top: referenceTop } = config[0].style;
1426
+ const pasteConfigs = config.map((configItem) => {
1427
+ const { offsetX = 0, offsetY = 0, ...positionClone } = position;
1428
+ let pastePosition = positionClone;
1429
+ if (!isEmpty(pastePosition) && curNode?.items) {
1430
+ pastePosition = getPositionInContainer(pastePosition, curNode.id);
1856
1431
  }
1857
- return cloneDeep(this.state.propsConfigMap[toLine(type)] || await this.fillConfig([]));
1858
- }
1859
- setPropsValues(values) {
1860
- Object.keys(values).forEach((type) => {
1861
- this.setPropsValue(toLine(type), values[type]);
1862
- });
1863
- }
1864
- /**
1865
- * 为指点类型组件设置组件初始值
1866
- * @param type 组件类型
1867
- * @param value 组件初始值
1868
- */
1869
- async setPropsValue(type, value) {
1870
- this.state.propsValueMap[toLine(type)] = value;
1871
- }
1872
- /**
1873
- * 获取指定类型的组件初始值
1874
- * @param type 组件类型
1875
- * @returns 组件初始值
1876
- */
1877
- async getPropsValue(componentType, { inputEvent, ...defaultValue } = {}) {
1878
- const type = toLine(componentType);
1879
- if (type === "area") {
1880
- const value = await this.getPropsValue("button");
1881
- value.className = "action-area";
1882
- value.text = "";
1883
- if (value.style) {
1884
- value.style.backgroundColor = "rgba(255, 255, 255, 0)";
1432
+ if (pastePosition.left && configItem.style?.left) {
1433
+ pastePosition.left = configItem.style.left - referenceLeft + pastePosition.left;
1434
+ }
1435
+ if (pastePosition.top && configItem.style?.top) {
1436
+ pastePosition.top = configItem.style?.top - referenceTop + pastePosition.top;
1437
+ }
1438
+ const pasteConfig = propsService.setNewItemId(configItem, false);
1439
+ if (pasteConfig.style) {
1440
+ const { left, top } = pasteConfig.style;
1441
+ if (typeof left === "number" || !!left && !isNaN(Number(left))) {
1442
+ pasteConfig.style.left = Number(left) + offsetX;
1885
1443
  }
1886
- return value;
1444
+ if (typeof top === "number" || !!top && !isNaN(Number(top))) {
1445
+ pasteConfig.style.top = Number(top) + offsetY;
1446
+ }
1447
+ pasteConfig.style = {
1448
+ ...pasteConfig.style,
1449
+ ...pastePosition
1450
+ };
1887
1451
  }
1888
- const id = this.createId(type);
1889
- const defaultPropsValue = this.getDefaultPropsValue(type);
1890
- const data = this.setNewItemId(
1891
- cloneDeep({
1892
- type,
1893
- ...defaultValue
1894
- })
1452
+ const root = editorService.get("root");
1453
+ if ((isPage(pasteConfig) || isPageFragment(pasteConfig)) && root) {
1454
+ pasteConfig.name = generatePageNameByApp(root, isPage(pasteConfig) ? NodeType.PAGE : NodeType.PAGE_FRAGMENT);
1455
+ }
1456
+ return pasteConfig;
1457
+ });
1458
+ return pasteConfigs;
1459
+ };
1460
+ const getPositionInContainer = (position = {}, id) => {
1461
+ let { left = 0, top = 0 } = position;
1462
+ const parentEl = editorService.get("stage")?.renderer?.contentWindow?.document.getElementById(`${id}`);
1463
+ const parentElRect = parentEl?.getBoundingClientRect();
1464
+ left = left - (parentElRect?.left || 0);
1465
+ top = top - (parentElRect?.top || 0);
1466
+ return {
1467
+ left,
1468
+ top
1469
+ };
1470
+ };
1471
+ const getAddParent = (node) => {
1472
+ const curNode = editorService.get("node");
1473
+ let parentNode;
1474
+ if (isPage(node)) {
1475
+ parentNode = editorService.get("root");
1476
+ } else if (curNode?.items) {
1477
+ parentNode = curNode;
1478
+ } else if (curNode?.id) {
1479
+ parentNode = editorService.getParentById(curNode.id, false);
1480
+ }
1481
+ return parentNode;
1482
+ };
1483
+ const getDefaultConfig = async (addNode, parentNode) => {
1484
+ const { type, inputEvent, ...config } = addNode;
1485
+ const layout = await editorService.getLayout(toRaw(parentNode), addNode);
1486
+ const newNode = { ...toRaw(await propsService.getPropsValue(type, config)) };
1487
+ newNode.style = getInitPositionStyle(newNode.style, layout);
1488
+ return newNode;
1489
+ };
1490
+
1491
+ const canUsePluginMethods$4 = {
1492
+ async: [
1493
+ "getLayout",
1494
+ "highlight",
1495
+ "select",
1496
+ "multiSelect",
1497
+ "doAdd",
1498
+ "add",
1499
+ "doRemove",
1500
+ "remove",
1501
+ "doUpdate",
1502
+ "update",
1503
+ "sort",
1504
+ "copy",
1505
+ "paste",
1506
+ "doPaste",
1507
+ "doAlignCenter",
1508
+ "alignCenter",
1509
+ "moveLayer",
1510
+ "moveToContainer",
1511
+ "dragTo",
1512
+ "undo",
1513
+ "redo",
1514
+ "move"
1515
+ ],
1516
+ sync: []
1517
+ };
1518
+ class Editor extends BaseService {
1519
+ state = reactive({
1520
+ root: null,
1521
+ page: null,
1522
+ parent: null,
1523
+ node: null,
1524
+ nodes: [],
1525
+ stage: null,
1526
+ stageLoading: true,
1527
+ highlightNode: null,
1528
+ modifiedNodeIds: /* @__PURE__ */ new Map(),
1529
+ pageLength: 0,
1530
+ pageFragmentLength: 0,
1531
+ disabledMultiSelect: false
1532
+ });
1533
+ isHistoryStateChange = false;
1534
+ constructor() {
1535
+ super(
1536
+ canUsePluginMethods$4.async.map((methodName) => ({ name: methodName, isAsync: true })),
1537
+ // 需要注意循环依赖问题,如果函数间有相互调用的话,不能设置为串行调用
1538
+ ["select", "update", "moveLayer"]
1895
1539
  );
1896
- return {
1897
- id,
1898
- ...defaultPropsValue,
1899
- ...mergeWith({}, cloneDeep(this.state.propsValueMap[type] || {}), data)
1900
- };
1901
- }
1902
- createId(type) {
1903
- return `${type}_${guid()}`;
1904
1540
  }
1905
1541
  /**
1906
- * 将组件与组件的子元素配置中的id都设置成一个新的ID
1907
- * 如果没有相同ID并且force为false则保持不变
1908
- * @param {Object} config 组件配置
1909
- * @param {Boolean} force 是否强制设置新的ID
1542
+ * 设置当前指点节点配置
1543
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
1544
+ * @param value MNode
1910
1545
  */
1911
- /* eslint no-param-reassign: ["error", { "props": false }] */
1912
- setNewItemId(config, force = true) {
1913
- if (force || editorService.getNodeById(config.id)) {
1914
- const newId = this.createId(config.type || "component");
1915
- this.setRelateId(config.id, newId);
1916
- config.id = newId;
1546
+ set(name, value) {
1547
+ const preValue = this.state[name];
1548
+ this.state[name] = value;
1549
+ if (name === "nodes" && Array.isArray(value)) {
1550
+ this.set("node", value[0]);
1917
1551
  }
1918
- if (config.items && Array.isArray(config.items)) {
1919
- for (const item of config.items) {
1920
- this.setNewItemId(item);
1552
+ if (name === "root") {
1553
+ if (Array.isArray(value)) {
1554
+ throw new Error("root 不能为数组");
1555
+ }
1556
+ if (value && isObject(value)) {
1557
+ const app = value;
1558
+ this.state.pageLength = getPageList(app).length || 0;
1559
+ this.state.pageFragmentLength = getPageFragmentList(app).length || 0;
1560
+ this.state.stageLoading = this.state.pageLength !== 0;
1561
+ } else {
1562
+ this.state.pageLength = 0;
1563
+ this.state.pageFragmentLength = 0;
1564
+ this.state.stageLoading = false;
1921
1565
  }
1566
+ this.emit("root-change", value, preValue);
1922
1567
  }
1923
- return config;
1924
1568
  }
1925
1569
  /**
1926
- * 获取默认属性配置
1927
- * @param type 组件类型
1928
- * @returns Object
1570
+ * 获取当前指点节点配置
1571
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength'
1572
+ * @returns MNode
1929
1573
  */
1930
- getDefaultPropsValue(type) {
1931
- return ["page", "container"].includes(type) ? {
1932
- type,
1933
- layout: "absolute",
1934
- style: {},
1935
- name: type,
1936
- items: []
1937
- } : {
1938
- type,
1939
- style: {},
1940
- name: type
1941
- };
1942
- }
1943
- resetState() {
1944
- this.state.propsConfigMap = {};
1945
- this.state.propsValueMap = {};
1574
+ get(name) {
1575
+ return this.state[name];
1946
1576
  }
1947
1577
  /**
1948
- * 替换关联ID
1949
- * @param originConfigs 原组件配置
1950
- * @param targetConfigs 待替换的组件配置
1578
+ * 根据id获取组件、组件的父组件以及组件所属的页面节点
1579
+ * @param {number | string} id 组件id
1580
+ * @param {boolean} raw 是否使用toRaw
1581
+ * @returns {EditorNodeInfo}
1951
1582
  */
1952
- replaceRelateId(originConfigs, targetConfigs) {
1953
- const relateIdMap = this.getRelateIdMap();
1954
- if (Object.keys(relateIdMap).length === 0)
1955
- return;
1956
- const target = depService.getTarget(DepTargetType.RELATED_COMP_WHEN_COPY, DepTargetType.RELATED_COMP_WHEN_COPY);
1957
- if (!target)
1958
- return;
1959
- originConfigs.forEach((config) => {
1960
- const newId = relateIdMap[config.id];
1961
- const targetConfig = targetConfigs.find((targetConfig2) => targetConfig2.id === newId);
1962
- if (!targetConfig)
1583
+ getNodeInfo(id, raw = true) {
1584
+ let root = this.get("root");
1585
+ if (raw) {
1586
+ root = toRaw(root);
1587
+ }
1588
+ const info = {
1589
+ node: null,
1590
+ parent: null,
1591
+ page: null
1592
+ };
1593
+ if (!root)
1594
+ return info;
1595
+ if (id === root.id) {
1596
+ info.node = root;
1597
+ return info;
1598
+ }
1599
+ const path = getNodePath(id, root.items);
1600
+ if (!path.length)
1601
+ return info;
1602
+ path.unshift(root);
1603
+ info.node = path[path.length - 1];
1604
+ info.parent = path[path.length - 2];
1605
+ path.forEach((item) => {
1606
+ if (isPage(item) || isPageFragment(item)) {
1607
+ info.page = item;
1963
1608
  return;
1964
- target.deps[config.id]?.keys?.forEach((fullKey) => {
1965
- const relateOriginId = getValueByKeyPath(fullKey, config);
1966
- const relateTargetId = relateIdMap[relateOriginId];
1967
- if (!relateTargetId)
1968
- return;
1969
- setValueByKeyPath(fullKey, relateTargetId, targetConfig);
1970
- });
1609
+ }
1971
1610
  });
1611
+ return info;
1972
1612
  }
1973
1613
  /**
1974
- * 清除setNewItemId前后映射关系
1975
- */
1976
- clearRelateId() {
1977
- this.state.relateIdMap = {};
1978
- }
1979
- destroy() {
1980
- this.resetState();
1981
- this.removeAllListeners();
1982
- this.removeAllPlugins();
1983
- }
1984
- usePlugin(options) {
1985
- super.usePlugin(options);
1986
- }
1987
- /**
1988
- * 获取setNewItemId前后映射关系
1989
- * @param oldId 原组件ID
1990
- * @returns 新旧ID映射
1614
+ * 根据ID获取指点节点配置
1615
+ * @param id 组件ID
1616
+ * @param {boolean} raw 是否使用toRaw
1617
+ * @returns 组件节点配置
1991
1618
  */
1992
- getRelateIdMap() {
1993
- return this.state.relateIdMap;
1619
+ getNodeById(id, raw = true) {
1620
+ const { node } = this.getNodeInfo(id, raw);
1621
+ return node;
1994
1622
  }
1995
1623
  /**
1996
- * 记录setNewItemId前后映射关系
1997
- * @param oldId 原组件ID
1998
- * @param newId 分配的新ID
1624
+ * 根据ID获取指点节点的父节点配置
1625
+ * @param id 组件ID
1626
+ * @param {boolean} raw 是否使用toRaw
1627
+ * @returns 指点组件的父节点配置
1999
1628
  */
2000
- setRelateId(oldId, newId) {
2001
- this.state.relateIdMap[oldId] = newId;
2002
- }
2003
- }
2004
- const propsService = new Props();
2005
-
2006
- class UndoRedo {
2007
- elementList;
2008
- listCursor;
2009
- listMaxSize;
2010
- constructor(listMaxSize = 20) {
2011
- const minListMaxSize = 2;
2012
- this.elementList = [];
2013
- this.listCursor = 0;
2014
- this.listMaxSize = listMaxSize > minListMaxSize ? listMaxSize : minListMaxSize;
2015
- }
2016
- pushElement(element) {
2017
- this.elementList.splice(this.listCursor, this.elementList.length - this.listCursor, cloneDeep(element));
2018
- this.listCursor += 1;
2019
- if (this.elementList.length > this.listMaxSize) {
2020
- this.elementList.shift();
2021
- this.listCursor -= 1;
2022
- }
2023
- }
2024
- canUndo() {
2025
- return this.listCursor > 1;
2026
- }
2027
- // 返回undo后的当前元素
2028
- undo() {
2029
- if (!this.canUndo()) {
2030
- return null;
2031
- }
2032
- this.listCursor -= 1;
2033
- return this.getCurrentElement();
2034
- }
2035
- canRedo() {
2036
- return this.elementList.length > this.listCursor;
2037
- }
2038
- // 返回redo后的当前元素
2039
- redo() {
2040
- if (!this.canRedo()) {
2041
- return null;
2042
- }
2043
- this.listCursor += 1;
2044
- return this.getCurrentElement();
2045
- }
2046
- getCurrentElement() {
2047
- if (this.listCursor < 1) {
2048
- return null;
2049
- }
2050
- return cloneDeep(this.elementList[this.listCursor - 1]);
2051
- }
2052
- }
2053
-
2054
- class History extends BaseService {
2055
- state = reactive({
2056
- pageSteps: {},
2057
- pageId: void 0,
2058
- canRedo: false,
2059
- canUndo: false
2060
- });
2061
- constructor() {
2062
- super([]);
2063
- this.on("change", this.setCanUndoRedo);
2064
- }
2065
- reset() {
2066
- this.state.pageSteps = {};
2067
- this.resetPage();
2068
- }
2069
- resetPage() {
2070
- this.state.pageId = void 0;
2071
- this.state.canRedo = false;
2072
- this.state.canUndo = false;
1629
+ getParentById(id, raw = true) {
1630
+ const { parent } = this.getNodeInfo(id, raw);
1631
+ return parent;
2073
1632
  }
2074
- changePage(page) {
2075
- if (!page)
2076
- return;
2077
- this.state.pageId = page.id;
2078
- if (!this.state.pageSteps[this.state.pageId]) {
2079
- const undoRedo = new UndoRedo();
2080
- undoRedo.pushElement({
2081
- data: page,
2082
- modifiedNodeIds: /* @__PURE__ */ new Map(),
2083
- nodeId: page.id
2084
- });
2085
- this.state.pageSteps[this.state.pageId] = undoRedo;
1633
+ /**
1634
+ * 只有容器拥有布局
1635
+ */
1636
+ async getLayout(parent, node) {
1637
+ if (node && typeof node !== "function" && isFixed(node))
1638
+ return Layout.FIXED;
1639
+ if (parent.layout) {
1640
+ return parent.layout;
2086
1641
  }
2087
- this.setCanUndoRedo();
2088
- this.emit("page-change", this.state.pageSteps[this.state.pageId]);
2089
- }
2090
- resetState() {
2091
- this.state.pageId = void 0;
2092
- this.state.pageSteps = {};
2093
- this.state.canRedo = false;
2094
- this.state.canUndo = false;
1642
+ if (!parent.style?.position) {
1643
+ return Layout.RELATIVE;
1644
+ }
1645
+ return Layout.ABSOLUTE;
2095
1646
  }
2096
- push(state) {
2097
- const undoRedo = this.getUndoRedo();
2098
- if (!undoRedo)
2099
- return null;
2100
- undoRedo.pushElement(state);
2101
- this.emit("change", state);
2102
- return state;
1647
+ /**
1648
+ * 选中指定节点(将指定节点设置成当前选中状态)
1649
+ * @param config 指定节点配置或者ID
1650
+ * @returns 当前选中的节点配置
1651
+ */
1652
+ async select(config) {
1653
+ const { node, page, parent } = this.selectedConfigExceptionHandler(config);
1654
+ this.set("nodes", node ? [node] : []);
1655
+ this.set("page", page);
1656
+ this.set("parent", parent);
1657
+ if (page) {
1658
+ historyService.changePage(toRaw(page));
1659
+ } else {
1660
+ historyService.resetState();
1661
+ }
1662
+ if (node?.id) {
1663
+ this.get("stage")?.renderer.runtime?.getApp?.()?.page?.emit(
1664
+ "editor:select",
1665
+ {
1666
+ node,
1667
+ page,
1668
+ parent
1669
+ },
1670
+ getNodePath(node.id, this.get("root")?.items)
1671
+ );
1672
+ }
1673
+ this.emit("select", node);
1674
+ return node;
2103
1675
  }
2104
- undo() {
2105
- const undoRedo = this.getUndoRedo();
2106
- if (!undoRedo)
2107
- return null;
2108
- const state = undoRedo.undo();
2109
- this.emit("change", state);
2110
- return state;
1676
+ async selectNextNode() {
1677
+ const node = toRaw(this.get("node"));
1678
+ if (!node || isPage(node) || node.type === NodeType.ROOT)
1679
+ return node;
1680
+ const parent = toRaw(this.getParentById(node.id));
1681
+ if (!parent)
1682
+ return node;
1683
+ const index = getNodeIndex(node.id, parent);
1684
+ const nextNode = parent.items[index + 1] || parent.items[0];
1685
+ await this.select(nextNode);
1686
+ this.get("stage")?.select(nextNode.id);
1687
+ return nextNode;
2111
1688
  }
2112
- redo() {
2113
- const undoRedo = this.getUndoRedo();
2114
- if (!undoRedo)
2115
- return null;
2116
- const state = undoRedo.redo();
2117
- this.emit("change", state);
2118
- return state;
1689
+ async selectNextPage() {
1690
+ const root = toRaw(this.get("root"));
1691
+ const page = toRaw(this.get("page"));
1692
+ if (!page)
1693
+ throw new Error("page不能为空");
1694
+ if (!root)
1695
+ throw new Error("root不能为空");
1696
+ const index = getNodeIndex(page.id, root);
1697
+ const nextPage = root.items[index + 1] || root.items[0];
1698
+ await this.select(nextPage);
1699
+ this.get("stage")?.select(nextPage.id);
1700
+ return nextPage;
2119
1701
  }
2120
- destroy() {
2121
- this.resetState();
2122
- this.removeAllListeners();
2123
- this.removeAllPlugins();
1702
+ /**
1703
+ * 高亮指定节点
1704
+ * @param config 指定节点配置或者ID
1705
+ * @returns 当前高亮的节点配置
1706
+ */
1707
+ highlight(config) {
1708
+ const { node } = this.selectedConfigExceptionHandler(config);
1709
+ const currentHighlightNode = this.get("highlightNode");
1710
+ if (currentHighlightNode === node)
1711
+ return;
1712
+ this.set("highlightNode", node);
2124
1713
  }
2125
- getUndoRedo() {
2126
- if (!this.state.pageId)
2127
- return null;
2128
- return this.state.pageSteps[this.state.pageId];
1714
+ /**
1715
+ * 多选
1716
+ * @param ids 指定节点ID
1717
+ * @returns 加入多选的节点配置
1718
+ */
1719
+ multiSelect(ids) {
1720
+ const nodes = [];
1721
+ const idsUnique = uniq(ids);
1722
+ idsUnique.forEach((id) => {
1723
+ const { node } = this.getNodeInfo(id);
1724
+ if (!node)
1725
+ return;
1726
+ nodes.push(node);
1727
+ });
1728
+ this.set("nodes", nodes);
2129
1729
  }
2130
- setCanUndoRedo() {
2131
- const undoRedo = this.getUndoRedo();
2132
- this.state.canRedo = undoRedo?.canRedo() || false;
2133
- this.state.canUndo = undoRedo?.canUndo() || false;
1730
+ selectRoot() {
1731
+ const root = this.get("root");
1732
+ if (!root)
1733
+ return;
1734
+ this.set("nodes", [root]);
1735
+ this.set("parent", null);
1736
+ this.set("page", null);
1737
+ this.set("stage", null);
1738
+ this.set("highlightNode", null);
2134
1739
  }
2135
- }
2136
- const historyService = new History();
2137
-
2138
- var Protocol = /* @__PURE__ */ ((Protocol2) => {
2139
- Protocol2["OBJECT"] = "object";
2140
- Protocol2["JSON"] = "json";
2141
- Protocol2["STRING"] = "string";
2142
- Protocol2["NUMBER"] = "number";
2143
- Protocol2["BOOLEAN"] = "boolean";
2144
- return Protocol2;
2145
- })(Protocol || {});
2146
- const canUsePluginMethods$4 = {
2147
- async: [],
2148
- sync: ["getStorage", "getNamespace", "clear", "getItem", "removeItem", "setItem"]
2149
- };
2150
- class WebStorage extends BaseService {
2151
- storage = globalThis.localStorage;
2152
- namespace = "tmagic";
2153
- constructor() {
2154
- super(canUsePluginMethods$4.sync.map((methodName) => ({ name: methodName, isAsync: false })));
1740
+ async doAdd(node, parent) {
1741
+ const root = this.get("root");
1742
+ if (!root)
1743
+ throw new Error("root为空");
1744
+ const curNode = this.get("node");
1745
+ const stage = this.get("stage");
1746
+ if (!curNode)
1747
+ throw new Error("当前选中节点为空");
1748
+ if ((parent.type === NodeType.ROOT || curNode?.type === NodeType.ROOT) && !(isPage(node) || isPageFragment(node))) {
1749
+ throw new Error("app下不能添加组件");
1750
+ }
1751
+ if (parent.id !== curNode.id && !(isPage(node) || isPageFragment(node))) {
1752
+ const index = parent.items.indexOf(curNode);
1753
+ parent.items?.splice(index + 1, 0, node);
1754
+ } else {
1755
+ parent.items?.push(node);
1756
+ }
1757
+ const layout = await this.getLayout(toRaw(parent), node);
1758
+ node.style = getInitPositionStyle(node.style, layout);
1759
+ await stage?.add({
1760
+ config: cloneDeep(node),
1761
+ parent: cloneDeep(parent),
1762
+ parentId: parent.id,
1763
+ root: cloneDeep(root)
1764
+ });
1765
+ const newStyle = fixNodePosition(node, parent, stage);
1766
+ if (newStyle && (newStyle.top !== node.style.top || newStyle.left !== node.style.left)) {
1767
+ node.style = newStyle;
1768
+ await stage?.update({ config: cloneDeep(node), parentId: parent.id, root: cloneDeep(root) });
1769
+ }
1770
+ this.addModifiedNodeId(node.id);
1771
+ return node;
2155
1772
  }
2156
1773
  /**
2157
- * 获取数据存储对象,可以通过
2158
- * const storageService = new StorageService();
2159
- * storageService.usePlugin({
2160
- * // 替换存储对象为 sessionStorage
2161
- * async afterGetStorage(): Promise<Storage> {
2162
- * return window.sessionStorage;
2163
- * },
2164
- * });
1774
+ * 向指点容器添加组件节点
1775
+ * @param addConfig 将要添加的组件节点配置
1776
+ * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
1777
+ * @returns 添加后的节点
2165
1778
  */
2166
- getStorage() {
2167
- return this.storage;
2168
- }
2169
- getNamespace() {
2170
- return this.namespace;
1779
+ async add(addNode, parent) {
1780
+ const stage = this.get("stage");
1781
+ const addNodes = [];
1782
+ if (!Array.isArray(addNode)) {
1783
+ const { type, inputEvent, ...config } = addNode;
1784
+ if (!type)
1785
+ throw new Error("组件类型不能为空");
1786
+ addNodes.push({ ...toRaw(await propsService.getPropsValue(type, config)) });
1787
+ } else {
1788
+ addNodes.push(...addNode);
1789
+ }
1790
+ const newNodes = await Promise.all(
1791
+ addNodes.map((node) => {
1792
+ const root = this.get("root");
1793
+ if ((isPage(node) || isPageFragment(node)) && root) {
1794
+ return this.doAdd(node, root);
1795
+ }
1796
+ const parentNode = parent && typeof parent !== "function" ? parent : getAddParent(node);
1797
+ if (!parentNode)
1798
+ throw new Error("未找到父元素");
1799
+ return this.doAdd(node, parentNode);
1800
+ })
1801
+ );
1802
+ if (newNodes.length > 1) {
1803
+ const newNodeIds = newNodes.map((node) => node.id);
1804
+ stage?.multiSelect(newNodeIds);
1805
+ await this.multiSelect(newNodeIds);
1806
+ } else {
1807
+ await this.select(newNodes[0]);
1808
+ if (isPage(newNodes[0])) {
1809
+ this.state.pageLength += 1;
1810
+ } else if (isPageFragment(newNodes[0])) {
1811
+ this.state.pageFragmentLength += 1;
1812
+ } else {
1813
+ stage?.select(newNodes[0].id);
1814
+ }
1815
+ }
1816
+ if (!(isPage(newNodes[0]) || isPageFragment(newNodes[0]))) {
1817
+ this.pushHistoryState();
1818
+ }
1819
+ this.emit("add", newNodes);
1820
+ return Array.isArray(addNode) ? newNodes : newNodes[0];
2171
1821
  }
2172
- /**
2173
- * 清理,支持storageService.usePlugin
2174
- */
2175
- clear() {
2176
- const storage = this.getStorage();
2177
- storage.clear();
1822
+ async doRemove(node) {
1823
+ const root = this.get("root");
1824
+ if (!root)
1825
+ throw new Error("root不能为空");
1826
+ const { parent, node: curNode } = this.getNodeInfo(node.id, false);
1827
+ if (!parent || !curNode)
1828
+ throw new Error("找不要删除的节点");
1829
+ const index = getNodeIndex(curNode.id, parent);
1830
+ if (typeof index !== "number" || index === -1)
1831
+ throw new Error("找不要删除的节点");
1832
+ parent.items?.splice(index, 1);
1833
+ const stage = this.get("stage");
1834
+ stage?.remove({ id: node.id, parentId: parent.id, root: cloneDeep(root) });
1835
+ const selectDefault = async (pages) => {
1836
+ if (pages[0]) {
1837
+ await this.select(pages[0]);
1838
+ stage?.select(pages[0].id);
1839
+ } else {
1840
+ this.selectRoot();
1841
+ historyService.resetPage();
1842
+ }
1843
+ };
1844
+ const rootItems = root.items || [];
1845
+ if (isPage(node)) {
1846
+ this.state.pageLength -= 1;
1847
+ await selectDefault(getPageList(root));
1848
+ } else if (isPageFragment(node)) {
1849
+ this.state.pageFragmentLength -= 1;
1850
+ await selectDefault(getPageFragmentList(root));
1851
+ } else {
1852
+ await this.select(parent);
1853
+ stage?.select(parent.id);
1854
+ this.addModifiedNodeId(parent.id);
1855
+ }
1856
+ if (!rootItems.length) {
1857
+ this.resetModifiedNodeId();
1858
+ historyService.reset();
1859
+ }
2178
1860
  }
2179
1861
  /**
2180
- * 获取存储项,支持storageService.usePlugin
1862
+ * 删除组件
1863
+ * @param {Object} node
2181
1864
  */
2182
- getItem(key, options = {}) {
2183
- const storage = this.getStorage();
2184
- const namespace = this.getNamespace();
2185
- const { protocol = options.protocol, item } = this.getValueAndProtocol(
2186
- storage.getItem(`${options.namespace || namespace}:${key}`)
2187
- );
2188
- if (item === null)
2189
- return null;
2190
- switch (protocol) {
2191
- case "object" /* OBJECT */:
2192
- return getConfig("parseDSL")(`(${item})`);
2193
- case "json" /* JSON */:
2194
- return JSON.parse(item);
2195
- case "number" /* NUMBER */:
2196
- return Number(item);
2197
- case "boolean" /* BOOLEAN */:
2198
- return Boolean(item);
2199
- default:
2200
- return item;
1865
+ async remove(nodeOrNodeList) {
1866
+ const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
1867
+ await Promise.all(nodes.map((node) => this.doRemove(node)));
1868
+ if (!(isPage(nodes[0]) || isPageFragment(nodes[0]))) {
1869
+ this.pushHistoryState();
2201
1870
  }
1871
+ this.emit("remove", nodes);
2202
1872
  }
2203
- /**
2204
- * 获取指定索引位置的key
2205
- */
2206
- key(index) {
2207
- const storage = this.getStorage();
2208
- return storage.key(index);
1873
+ async doUpdate(config) {
1874
+ const root = this.get("root");
1875
+ if (!root)
1876
+ throw new Error("root为空");
1877
+ if (!config?.id)
1878
+ throw new Error("没有配置或者配置缺少id值");
1879
+ const info = this.getNodeInfo(config.id, false);
1880
+ if (!info.node)
1881
+ throw new Error(`获取不到id为${config.id}的节点`);
1882
+ const node = cloneDeep(toRaw(info.node));
1883
+ let newConfig = await this.toggleFixedPosition(toRaw(config), node, root);
1884
+ newConfig = mergeWith(cloneDeep(node), newConfig, (objValue, srcValue, key) => {
1885
+ if (typeof srcValue === "undefined" && Object.hasOwn(newConfig, key)) {
1886
+ return "";
1887
+ }
1888
+ if (isObject(srcValue) && Array.isArray(objValue)) {
1889
+ return srcValue;
1890
+ }
1891
+ if (Array.isArray(srcValue)) {
1892
+ return srcValue;
1893
+ }
1894
+ });
1895
+ if (!newConfig.type)
1896
+ throw new Error("配置缺少type值");
1897
+ if (newConfig.type === NodeType.ROOT) {
1898
+ this.set("root", newConfig);
1899
+ return newConfig;
1900
+ }
1901
+ const { parent } = info;
1902
+ if (!parent)
1903
+ throw new Error("获取不到父级节点");
1904
+ const parentNodeItems = parent.items;
1905
+ const index = getNodeIndex(newConfig.id, parent);
1906
+ if (!parentNodeItems || typeof index === "undefined" || index === -1)
1907
+ throw new Error("更新的节点未找到");
1908
+ const newLayout = await this.getLayout(newConfig);
1909
+ const layout = await this.getLayout(node);
1910
+ if (Array.isArray(newConfig.items) && newLayout !== layout) {
1911
+ newConfig = setChildrenLayout(newConfig, newLayout);
1912
+ }
1913
+ parentNodeItems[index] = newConfig;
1914
+ const nodes = this.get("nodes");
1915
+ const targetIndex = nodes.findIndex((nodeItem) => `${nodeItem.id}` === `${newConfig.id}`);
1916
+ nodes.splice(targetIndex, 1, newConfig);
1917
+ this.set("nodes", [...nodes]);
1918
+ this.get("stage")?.update({
1919
+ config: cloneDeep(newConfig),
1920
+ parentId: parent.id,
1921
+ root: cloneDeep(root)
1922
+ });
1923
+ if (isPage(newConfig) || isPageFragment(newConfig)) {
1924
+ this.set("page", newConfig);
1925
+ }
1926
+ this.addModifiedNodeId(newConfig.id);
1927
+ return newConfig;
2209
1928
  }
2210
1929
  /**
2211
- * 移除存储项,支持storageService.usePlugin
1930
+ * 更新节点
1931
+ * @param config 新的节点配置,配置中需要有id信息
1932
+ * @returns 更新后的节点配置
2212
1933
  */
2213
- removeItem(key, options = {}) {
2214
- const storage = this.getStorage();
2215
- const namespace = this.getNamespace();
2216
- storage.removeItem(`${options.namespace || namespace}:${key}`);
1934
+ async update(config) {
1935
+ const nodes = Array.isArray(config) ? config : [config];
1936
+ const newNodes = await Promise.all(nodes.map((node) => this.doUpdate(node)));
1937
+ if (newNodes[0]?.type !== NodeType.ROOT) {
1938
+ this.pushHistoryState();
1939
+ }
1940
+ this.emit("update", newNodes);
1941
+ return Array.isArray(config) ? newNodes : newNodes[0];
2217
1942
  }
2218
1943
  /**
2219
- * 设置存储项,支持storageService.usePlugin
1944
+ * 将id为id1的组件移动到id为id2的组件位置上,例如:[1,2,3,4] -> sort(1,3) -> [2,1,3,4]
1945
+ * @param id1 组件ID
1946
+ * @param id2 组件ID
1947
+ * @returns void
2220
1948
  */
2221
- setItem(key, value, options = {}) {
2222
- const storage = this.getStorage();
2223
- const namespace = this.getNamespace();
2224
- let item = value;
2225
- const protocol = options.protocol ? `${options.protocol}:` : "";
2226
- if (typeof value === "string" /* STRING */ || typeof value === "number" /* NUMBER */) {
2227
- item = `${protocol}${value}`;
2228
- } else {
2229
- item = `${protocol}${serialize(value)}`;
2230
- }
2231
- storage.setItem(`${options.namespace || namespace}:${key}`, item);
2232
- }
2233
- destroy() {
2234
- this.removeAllListeners();
2235
- this.removeAllPlugins();
2236
- }
2237
- usePlugin(options) {
2238
- super.usePlugin(options);
2239
- }
2240
- getValueAndProtocol(value) {
2241
- let protocol = "";
2242
- if (value === null) {
2243
- return {
2244
- item: value,
2245
- protocol
2246
- };
2247
- }
2248
- const item = value.replace(new RegExp(`^(${Object.values(Protocol).join("|")})(:)(.+)`), (_$0, $1, _$2, $3) => {
2249
- protocol = $1;
2250
- return $3;
2251
- });
2252
- return {
2253
- protocol,
2254
- item
2255
- };
2256
- }
2257
- }
2258
- const storageService = new WebStorage();
2259
-
2260
- const canUsePluginMethods$3 = {
2261
- async: [
2262
- "getLayout",
2263
- "highlight",
2264
- "select",
2265
- "multiSelect",
2266
- "doAdd",
2267
- "add",
2268
- "doRemove",
2269
- "remove",
2270
- "doUpdate",
2271
- "update",
2272
- "sort",
2273
- "copy",
2274
- "paste",
2275
- "doPaste",
2276
- "doAlignCenter",
2277
- "alignCenter",
2278
- "moveLayer",
2279
- "moveToContainer",
2280
- "dragTo",
2281
- "undo",
2282
- "redo",
2283
- "move"
2284
- ],
2285
- sync: []
2286
- };
2287
- class Editor extends BaseService {
2288
- state = reactive({
2289
- root: null,
2290
- page: null,
2291
- parent: null,
2292
- node: null,
2293
- nodes: [],
2294
- stage: null,
2295
- stageLoading: true,
2296
- highlightNode: null,
2297
- modifiedNodeIds: /* @__PURE__ */ new Map(),
2298
- pageLength: 0,
2299
- pageFragmentLength: 0,
2300
- disabledMultiSelect: false
2301
- });
2302
- isHistoryStateChange = false;
2303
- constructor() {
2304
- super(
2305
- canUsePluginMethods$3.async.map((methodName) => ({ name: methodName, isAsync: true })),
2306
- // 需要注意循环依赖问题,如果函数间有相互调用的话,不能设置为串行调用
2307
- ["select", "update", "moveLayer"]
2308
- );
1949
+ async sort(id1, id2) {
1950
+ const root = this.get("root");
1951
+ if (!root)
1952
+ throw new Error("root为空");
1953
+ const node = this.get("node");
1954
+ if (!node)
1955
+ throw new Error("当前节点为空");
1956
+ const parent = cloneDeep(toRaw(this.get("parent")));
1957
+ if (!parent)
1958
+ throw new Error("父节点为空");
1959
+ const index2 = parent.items.findIndex((node2) => `${node2.id}` === `${id2}`);
1960
+ if (index2 < 0)
1961
+ return;
1962
+ const index1 = parent.items.findIndex((node2) => `${node2.id}` === `${id1}`);
1963
+ parent.items.splice(index2, 0, ...parent.items.splice(index1, 1));
1964
+ await this.update(parent);
1965
+ await this.select(node);
1966
+ this.get("stage")?.update({
1967
+ config: cloneDeep(node),
1968
+ parentId: parent.id,
1969
+ root: cloneDeep(root)
1970
+ });
1971
+ this.addModifiedNodeId(parent.id);
1972
+ this.pushHistoryState();
2309
1973
  }
2310
1974
  /**
2311
- * 设置当前指点节点配置
2312
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
2313
- * @param value MNode
1975
+ * 将组件节点配置存储到localStorage中
1976
+ * @param config 组件节点配置
1977
+ * @returns
2314
1978
  */
2315
- set(name, value) {
2316
- const preValue = this.state[name];
2317
- this.state[name] = value;
2318
- if (name === "nodes" && Array.isArray(value)) {
2319
- this.set("node", value[0]);
2320
- }
2321
- if (name === "root") {
2322
- if (Array.isArray(value)) {
2323
- throw new Error("root 不能为数组");
2324
- }
2325
- if (value && isObject(value)) {
2326
- const app = value;
2327
- this.state.pageLength = getPageList(app).length || 0;
2328
- this.state.pageFragmentLength = getPageFragmentList(app).length || 0;
2329
- this.state.stageLoading = this.state.pageLength !== 0;
2330
- } else {
2331
- this.state.pageLength = 0;
2332
- this.state.pageFragmentLength = 0;
2333
- this.state.stageLoading = false;
2334
- }
2335
- this.emit("root-change", value, preValue);
2336
- }
1979
+ copy(config) {
1980
+ storageService.setItem(COPY_STORAGE_KEY, Array.isArray(config) ? config : [config], {
1981
+ protocol: Protocol.OBJECT
1982
+ });
2337
1983
  }
2338
1984
  /**
2339
- * 获取当前指点节点配置
2340
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength'
2341
- * @returns MNode
1985
+ * 复制时会带上组件关联的依赖
1986
+ * @param config 组件节点配置
1987
+ * @returns
2342
1988
  */
2343
- get(name) {
2344
- return this.state[name];
1989
+ copyWithRelated(config) {
1990
+ const copyNodes = Array.isArray(config) ? config : [config];
1991
+ depService.clearByType(DepTargetType.RELATED_COMP_WHEN_COPY);
1992
+ depService.collect(copyNodes, true, DepTargetType.RELATED_COMP_WHEN_COPY);
1993
+ const customTarget = depService.getTarget(
1994
+ DepTargetType.RELATED_COMP_WHEN_COPY,
1995
+ DepTargetType.RELATED_COMP_WHEN_COPY
1996
+ );
1997
+ if (customTarget) {
1998
+ Object.keys(customTarget.deps).forEach((nodeId) => {
1999
+ const node = this.getNodeById(nodeId);
2000
+ if (!node)
2001
+ return;
2002
+ customTarget.deps[nodeId].keys.forEach((key) => {
2003
+ const relateNodeId = get(node, key);
2004
+ const isExist = copyNodes.find((node2) => node2.id === relateNodeId);
2005
+ if (!isExist) {
2006
+ const relateNode = this.getNodeById(relateNodeId);
2007
+ if (relateNode) {
2008
+ copyNodes.push(relateNode);
2009
+ }
2010
+ }
2011
+ });
2012
+ });
2013
+ }
2014
+ storageService.setItem(COPY_STORAGE_KEY, copyNodes, {
2015
+ protocol: Protocol.OBJECT
2016
+ });
2345
2017
  }
2346
2018
  /**
2347
- * 根据id获取组件、组件的父组件以及组件所属的页面节点
2348
- * @param {number | string} id 组件id
2349
- * @param {boolean} raw 是否使用toRaw
2350
- * @returns {EditorNodeInfo}
2019
+ * 从localStorage中获取节点,然后添加到当前容器中
2020
+ * @param position 粘贴的坐标
2021
+ * @returns 添加后的组件节点配置
2351
2022
  */
2352
- getNodeInfo(id, raw = true) {
2353
- let root = this.get("root");
2354
- if (raw) {
2355
- root = toRaw(root);
2023
+ async paste(position = {}) {
2024
+ const config = storageService.getItem(COPY_STORAGE_KEY);
2025
+ if (!Array.isArray(config))
2026
+ return;
2027
+ const node = this.get("node");
2028
+ let parent = null;
2029
+ if (config.length === 1 && config[0].id === node?.id) {
2030
+ parent = this.get("parent");
2031
+ if (parent?.type === NodeType.ROOT) {
2032
+ parent = this.get("page");
2033
+ }
2356
2034
  }
2357
- const info = {
2358
- node: null,
2359
- parent: null,
2360
- page: null
2361
- };
2362
- if (!root)
2363
- return info;
2364
- if (id === root.id) {
2365
- info.node = root;
2366
- return info;
2035
+ const pasteConfigs = await this.doPaste(config, position);
2036
+ propsService.replaceRelateId(config, pasteConfigs);
2037
+ return this.add(pasteConfigs, parent);
2038
+ }
2039
+ async doPaste(config, position = {}) {
2040
+ propsService.clearRelateId();
2041
+ const pasteConfigs = beforePaste(position, cloneDeep(config));
2042
+ return pasteConfigs;
2043
+ }
2044
+ async doAlignCenter(config) {
2045
+ const parent = this.getParentById(config.id);
2046
+ if (!parent)
2047
+ throw new Error("找不到父节点");
2048
+ const node = cloneDeep(toRaw(config));
2049
+ const layout = await this.getLayout(parent, node);
2050
+ if (layout === Layout.RELATIVE) {
2051
+ return config;
2367
2052
  }
2368
- const path = getNodePath(id, root.items);
2369
- if (!path.length)
2370
- return info;
2371
- path.unshift(root);
2372
- info.node = path[path.length - 1];
2373
- info.parent = path[path.length - 2];
2374
- path.forEach((item) => {
2375
- if (isPage(item) || isPageFragment(item)) {
2376
- info.page = item;
2377
- return;
2053
+ if (!node.style)
2054
+ return config;
2055
+ const stage = this.get("stage");
2056
+ const doc = stage?.renderer.contentWindow?.document;
2057
+ if (doc) {
2058
+ const el = doc.getElementById(`${node.id}`);
2059
+ const parentEl = layout === Layout.FIXED ? doc.body : el?.offsetParent;
2060
+ if (parentEl && el) {
2061
+ node.style.left = calcValueByFontsize(doc, (parentEl.clientWidth - el.clientWidth) / 2);
2062
+ node.style.right = "";
2378
2063
  }
2379
- });
2380
- return info;
2064
+ } else if (parent.style && isNumber(parent.style?.width) && isNumber(node.style?.width)) {
2065
+ node.style.left = (parent.style.width - node.style.width) / 2;
2066
+ node.style.right = "";
2067
+ }
2068
+ return node;
2381
2069
  }
2382
2070
  /**
2383
- * 根据ID获取指点节点配置
2384
- * @param id 组件ID
2385
- * @param {boolean} raw 是否使用toRaw
2386
- * @returns 组件节点配置
2071
+ * 将指点节点设置居中
2072
+ * @param config 组件节点配置
2073
+ * @returns 当前组件节点配置
2387
2074
  */
2388
- getNodeById(id, raw = true) {
2389
- const { node } = this.getNodeInfo(id, raw);
2390
- return node;
2075
+ async alignCenter(config) {
2076
+ const nodes = Array.isArray(config) ? config : [config];
2077
+ const stage = this.get("stage");
2078
+ const newNodes = await Promise.all(nodes.map((node) => this.doAlignCenter(node)));
2079
+ const newNode = await this.update(newNodes);
2080
+ if (newNodes.length > 1) {
2081
+ await stage?.multiSelect(newNodes.map((node) => node.id));
2082
+ } else {
2083
+ await stage?.select(newNodes[0].id);
2084
+ }
2085
+ return newNode;
2391
2086
  }
2392
2087
  /**
2393
- * 根据ID获取指点节点的父节点配置
2394
- * @param id 组件ID
2395
- * @param {boolean} raw 是否使用toRaw
2396
- * @returns 指点组件的父节点配置
2088
+ * 移动当前选中节点位置
2089
+ * @param offset 偏移量
2397
2090
  */
2398
- getParentById(id, raw = true) {
2399
- const { parent } = this.getNodeInfo(id, raw);
2400
- return parent;
2091
+ async moveLayer(offset) {
2092
+ const root = this.get("root");
2093
+ if (!root)
2094
+ throw new Error("root为空");
2095
+ const parent = this.get("parent");
2096
+ if (!parent)
2097
+ throw new Error("父节点为空");
2098
+ const node = this.get("node");
2099
+ if (!node)
2100
+ throw new Error("当前节点为空");
2101
+ const brothers = parent.items || [];
2102
+ const index = brothers.findIndex((item) => `${item.id}` === `${node?.id}`);
2103
+ const layout = await this.getLayout(parent, node);
2104
+ const isRelative = layout === Layout.RELATIVE;
2105
+ let offsetIndex;
2106
+ if (offset === LayerOffset.TOP) {
2107
+ offsetIndex = isRelative ? 0 : brothers.length;
2108
+ } else if (offset === LayerOffset.BOTTOM) {
2109
+ offsetIndex = isRelative ? brothers.length : 0;
2110
+ } else {
2111
+ offsetIndex = index + (isRelative ? -offset : offset);
2112
+ }
2113
+ if (offsetIndex > 0 && offsetIndex > brothers.length || offsetIndex < 0) {
2114
+ return;
2115
+ }
2116
+ brothers.splice(index, 1);
2117
+ brothers.splice(offsetIndex, 0, node);
2118
+ const grandparent = this.getParentById(parent.id);
2119
+ this.get("stage")?.update({
2120
+ config: cloneDeep(toRaw(parent)),
2121
+ parentId: grandparent?.id,
2122
+ root: cloneDeep(root)
2123
+ });
2124
+ this.addModifiedNodeId(parent.id);
2125
+ this.pushHistoryState();
2126
+ this.emit("move-layer", offset);
2401
2127
  }
2402
2128
  /**
2403
- * 只有容器拥有布局
2129
+ * 移动到指定容器中
2130
+ * @param config 需要移动的节点
2131
+ * @param targetId 容器ID
2404
2132
  */
2405
- async getLayout(parent, node) {
2406
- if (node && typeof node !== "function" && isFixed(node))
2407
- return Layout.FIXED;
2408
- if (parent.layout) {
2409
- return parent.layout;
2410
- }
2411
- if (!parent.style?.position) {
2412
- return Layout.RELATIVE;
2133
+ async moveToContainer(config, targetId) {
2134
+ const root = this.get("root");
2135
+ const { node, parent } = this.getNodeInfo(config.id, false);
2136
+ const target = this.getNodeById(targetId, false);
2137
+ const stage = this.get("stage");
2138
+ if (root && node && parent && stage) {
2139
+ const index = getNodeIndex(node.id, parent);
2140
+ parent.items?.splice(index, 1);
2141
+ await stage.remove({ id: node.id, parentId: parent.id, root: cloneDeep(root) });
2142
+ const layout = await this.getLayout(target);
2143
+ const newConfig = mergeWith(cloneDeep(node), config, (objValue, srcValue) => {
2144
+ if (Array.isArray(srcValue)) {
2145
+ return srcValue;
2146
+ }
2147
+ });
2148
+ newConfig.style = getInitPositionStyle(newConfig.style, layout);
2149
+ target.items.push(newConfig);
2150
+ await stage.select(targetId);
2151
+ const targetParent = this.getParentById(target.id);
2152
+ await stage.update({ config: cloneDeep(target), parentId: targetParent?.id, root: cloneDeep(root) });
2153
+ await this.select(newConfig);
2154
+ stage.select(newConfig.id);
2155
+ this.addModifiedNodeId(target.id);
2156
+ this.addModifiedNodeId(parent.id);
2157
+ this.pushHistoryState();
2158
+ return newConfig;
2413
2159
  }
2414
- return Layout.ABSOLUTE;
2415
2160
  }
2416
- /**
2417
- * 选中指定节点(将指定节点设置成当前选中状态)
2418
- * @param config 指定节点配置或者ID
2419
- * @returns 当前选中的节点配置
2420
- */
2421
- async select(config) {
2422
- const { node, page, parent } = this.selectedConfigExceptionHandler(config);
2423
- this.set("nodes", node ? [node] : []);
2424
- this.set("page", page);
2425
- this.set("parent", parent);
2426
- if (page) {
2427
- historyService.changePage(toRaw(page));
2428
- } else {
2429
- historyService.resetState();
2161
+ async dragTo(config, targetParent, targetIndex) {
2162
+ if (!targetParent || !Array.isArray(targetParent.items))
2163
+ return;
2164
+ const { parent, node: curNode } = this.getNodeInfo(config.id, false);
2165
+ if (!parent || !curNode)
2166
+ throw new Error("找不要删除的节点");
2167
+ const index = getNodeIndex(curNode.id, parent);
2168
+ if (typeof index !== "number" || index === -1)
2169
+ throw new Error("找不要删除的节点");
2170
+ if (parent.id === targetParent.id) {
2171
+ if (index === targetIndex)
2172
+ return;
2173
+ if (index < targetIndex) {
2174
+ targetIndex -= 1;
2175
+ }
2430
2176
  }
2431
- if (node?.id) {
2432
- this.get("stage")?.renderer.runtime?.getApp?.()?.page?.emit(
2433
- "editor:select",
2434
- {
2435
- node,
2436
- page,
2437
- parent
2438
- },
2439
- getNodePath(node.id, this.get("root")?.items)
2440
- );
2177
+ const layout = await this.getLayout(parent);
2178
+ const newLayout = await this.getLayout(targetParent);
2179
+ if (newLayout !== layout) {
2180
+ setLayout(config, newLayout);
2441
2181
  }
2442
- this.emit("select", node);
2443
- return node;
2444
- }
2445
- async selectNextNode() {
2446
- const node = toRaw(this.get("node"));
2447
- if (!node || isPage(node) || node.type === NodeType.ROOT)
2448
- return node;
2449
- const parent = toRaw(this.getParentById(node.id));
2450
- if (!parent)
2451
- return node;
2452
- const index = getNodeIndex(node.id, parent);
2453
- const nextNode = parent.items[index + 1] || parent.items[0];
2454
- await this.select(nextNode);
2455
- this.get("stage")?.select(nextNode.id);
2456
- return nextNode;
2457
- }
2458
- async selectNextPage() {
2459
- const root = toRaw(this.get("root"));
2460
- const page = toRaw(this.get("page"));
2461
- if (!page)
2462
- throw new Error("page不能为空");
2463
- if (!root)
2464
- throw new Error("root不能为空");
2465
- const index = getNodeIndex(page.id, root);
2466
- const nextPage = root.items[index + 1] || root.items[0];
2467
- await this.select(nextPage);
2468
- this.get("stage")?.select(nextPage.id);
2469
- return nextPage;
2182
+ parent.items?.splice(index, 1);
2183
+ targetParent.items?.splice(targetIndex, 0, config);
2184
+ const page = this.get("page");
2185
+ const root = this.get("root");
2186
+ const stage = this.get("stage");
2187
+ if (stage && page && root) {
2188
+ stage.update({
2189
+ config: cloneDeep(page),
2190
+ parentId: root.id,
2191
+ root: cloneDeep(root)
2192
+ });
2193
+ }
2194
+ this.addModifiedNodeId(config.id);
2195
+ this.addModifiedNodeId(parent.id);
2196
+ this.pushHistoryState();
2197
+ this.emit("drag-to", { index, targetIndex, config, parent, targetParent });
2470
2198
  }
2471
2199
  /**
2472
- * 高亮指定节点
2473
- * @param config 指定节点配置或者ID
2474
- * @returns 当前高亮的节点配置
2200
+ * 撤销当前操作
2201
+ * @returns 上一次数据
2475
2202
  */
2476
- highlight(config) {
2477
- const { node } = this.selectedConfigExceptionHandler(config);
2478
- const currentHighlightNode = this.get("highlightNode");
2479
- if (currentHighlightNode === node)
2480
- return;
2481
- this.set("highlightNode", node);
2203
+ async undo() {
2204
+ const value = historyService.undo();
2205
+ await this.changeHistoryState(value);
2206
+ return value;
2482
2207
  }
2483
2208
  /**
2484
- * 多选
2485
- * @param ids 指定节点ID
2486
- * @returns 加入多选的节点配置
2209
+ * 恢复到下一步
2210
+ * @returns 下一步数据
2487
2211
  */
2488
- multiSelect(ids) {
2489
- const nodes = [];
2490
- const idsUnique = uniq(ids);
2491
- idsUnique.forEach((id) => {
2492
- const { node } = this.getNodeInfo(id);
2493
- if (!node)
2494
- return;
2495
- nodes.push(node);
2496
- });
2497
- this.set("nodes", nodes);
2212
+ async redo() {
2213
+ const value = historyService.redo();
2214
+ await this.changeHistoryState(value);
2215
+ return value;
2498
2216
  }
2499
- selectRoot() {
2500
- const root = this.get("root");
2501
- if (!root)
2217
+ async move(left, top) {
2218
+ const node = toRaw(this.get("node"));
2219
+ if (!node || isPage(node))
2502
2220
  return;
2503
- this.set("nodes", [root]);
2504
- this.set("parent", null);
2221
+ const { style, id, type } = node;
2222
+ if (!style || !["absolute", "fixed"].includes(style.position))
2223
+ return;
2224
+ const update = (style2) => this.update({
2225
+ id,
2226
+ type,
2227
+ style: style2
2228
+ });
2229
+ if (top) {
2230
+ if (isNumber(style.top)) {
2231
+ update({
2232
+ ...style,
2233
+ top: Number(style.top) + Number(top),
2234
+ bottom: ""
2235
+ });
2236
+ } else if (isNumber(style.bottom)) {
2237
+ update({
2238
+ ...style,
2239
+ bottom: Number(style.bottom) - Number(top),
2240
+ top: ""
2241
+ });
2242
+ }
2243
+ }
2244
+ if (left) {
2245
+ if (isNumber(style.left)) {
2246
+ update({
2247
+ ...style,
2248
+ left: Number(style.left) + Number(left),
2249
+ right: ""
2250
+ });
2251
+ } else if (isNumber(style.right)) {
2252
+ update({
2253
+ ...style,
2254
+ right: Number(style.right) - Number(left),
2255
+ left: ""
2256
+ });
2257
+ }
2258
+ }
2259
+ }
2260
+ resetState() {
2261
+ this.set("root", null);
2262
+ this.set("node", null);
2263
+ this.set("nodes", []);
2505
2264
  this.set("page", null);
2265
+ this.set("parent", null);
2506
2266
  this.set("stage", null);
2507
2267
  this.set("highlightNode", null);
2268
+ this.set("modifiedNodeIds", /* @__PURE__ */ new Map());
2269
+ this.set("pageLength", 0);
2508
2270
  }
2509
- async doAdd(node, parent) {
2510
- const root = this.get("root");
2511
- if (!root)
2512
- throw new Error("root为空");
2513
- const curNode = this.get("node");
2514
- const stage = this.get("stage");
2515
- if (!curNode)
2516
- throw new Error("当前选中节点为空");
2517
- if ((parent.type === NodeType.ROOT || curNode?.type === NodeType.ROOT) && !(isPage(node) || isPageFragment(node))) {
2518
- throw new Error("app下不能添加组件");
2271
+ destroy() {
2272
+ this.removeAllListeners();
2273
+ this.resetState();
2274
+ this.removeAllPlugins();
2275
+ }
2276
+ resetModifiedNodeId() {
2277
+ this.get("modifiedNodeIds").clear();
2278
+ }
2279
+ usePlugin(options) {
2280
+ super.usePlugin(options);
2281
+ }
2282
+ addModifiedNodeId(id) {
2283
+ if (!this.isHistoryStateChange) {
2284
+ this.get("modifiedNodeIds").set(id, id);
2519
2285
  }
2520
- if (parent.id !== curNode.id && !(isPage(node) || isPageFragment(node))) {
2521
- const index = parent.items.indexOf(curNode);
2522
- parent.items?.splice(index + 1, 0, node);
2523
- } else {
2524
- parent.items?.push(node);
2286
+ }
2287
+ pushHistoryState() {
2288
+ const curNode = cloneDeep(toRaw(this.get("node")));
2289
+ const page = this.get("page");
2290
+ if (!this.isHistoryStateChange && curNode && page) {
2291
+ historyService.push({
2292
+ data: cloneDeep(toRaw(page)),
2293
+ modifiedNodeIds: this.get("modifiedNodeIds"),
2294
+ nodeId: curNode.id
2295
+ });
2525
2296
  }
2526
- const layout = await this.getLayout(toRaw(parent), node);
2527
- node.style = getInitPositionStyle(node.style, layout);
2528
- await stage?.add({
2529
- config: cloneDeep(node),
2530
- parent: cloneDeep(parent),
2531
- parentId: parent.id,
2532
- root: cloneDeep(root)
2533
- });
2534
- const newStyle = fixNodePosition(node, parent, stage);
2535
- if (newStyle && (newStyle.top !== node.style.top || newStyle.left !== node.style.left)) {
2536
- node.style = newStyle;
2537
- await stage?.update({ config: cloneDeep(node), parentId: parent.id, root: cloneDeep(root) });
2297
+ this.isHistoryStateChange = false;
2298
+ }
2299
+ async changeHistoryState(value) {
2300
+ if (!value)
2301
+ return;
2302
+ this.isHistoryStateChange = true;
2303
+ await this.update(value.data);
2304
+ this.set("modifiedNodeIds", value.modifiedNodeIds);
2305
+ setTimeout(async () => {
2306
+ if (!value.nodeId)
2307
+ return;
2308
+ await this.select(value.nodeId);
2309
+ this.get("stage")?.select(value.nodeId);
2310
+ }, 0);
2311
+ this.emit("history-change", value.data);
2312
+ }
2313
+ async toggleFixedPosition(dist, src, root) {
2314
+ const newConfig = cloneDeep(dist);
2315
+ if (!isPop(src) && newConfig.style?.position) {
2316
+ if (isFixed(newConfig) && !isFixed(src)) {
2317
+ newConfig.style = change2Fixed(newConfig, root);
2318
+ } else if (!isFixed(newConfig) && isFixed(src)) {
2319
+ newConfig.style = await Fixed2Other(newConfig, root, this.getLayout);
2320
+ }
2538
2321
  }
2539
- this.addModifiedNodeId(node.id);
2540
- return node;
2322
+ return newConfig;
2541
2323
  }
2542
- /**
2543
- * 向指点容器添加组件节点
2544
- * @param addConfig 将要添加的组件节点配置
2545
- * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
2546
- * @returns 添加后的节点
2547
- */
2548
- async add(addNode, parent) {
2549
- const stage = this.get("stage");
2550
- const addNodes = [];
2551
- if (!Array.isArray(addNode)) {
2552
- const { type, inputEvent, ...config } = addNode;
2553
- if (!type)
2554
- throw new Error("组件类型不能为空");
2555
- addNodes.push({ ...toRaw(await propsService.getPropsValue(type, config)) });
2324
+ selectedConfigExceptionHandler(config) {
2325
+ let id;
2326
+ if (typeof config === "string" || typeof config === "number") {
2327
+ id = config;
2556
2328
  } else {
2557
- addNodes.push(...addNode);
2329
+ id = config.id;
2558
2330
  }
2559
- const newNodes = await Promise.all(
2560
- addNodes.map((node) => {
2561
- const root = this.get("root");
2562
- if ((isPage(node) || isPageFragment(node)) && root) {
2563
- return this.doAdd(node, root);
2564
- }
2565
- const parentNode = parent && typeof parent !== "function" ? parent : getAddParent(node);
2566
- if (!parentNode)
2567
- throw new Error("未找到父元素");
2568
- return this.doAdd(node, parentNode);
2569
- })
2570
- );
2571
- if (newNodes.length > 1) {
2572
- const newNodeIds = newNodes.map((node) => node.id);
2573
- stage?.multiSelect(newNodeIds);
2574
- await this.multiSelect(newNodeIds);
2575
- } else {
2576
- await this.select(newNodes[0]);
2577
- if (isPage(newNodes[0])) {
2578
- this.state.pageLength += 1;
2579
- } else if (isPageFragment(newNodes[0])) {
2580
- this.state.pageFragmentLength += 1;
2581
- } else {
2582
- stage?.select(newNodes[0].id);
2583
- }
2331
+ if (!id) {
2332
+ throw new Error("没有ID,无法选中");
2584
2333
  }
2585
- if (!(isPage(newNodes[0]) || isPageFragment(newNodes[0]))) {
2586
- this.pushHistoryState();
2334
+ const { node, parent, page } = this.getNodeInfo(id);
2335
+ if (!node)
2336
+ throw new Error("获取不到组件信息");
2337
+ if (node.id === this.state.root?.id) {
2338
+ throw new Error("不能选根节点");
2587
2339
  }
2588
- this.emit("add", newNodes);
2589
- return Array.isArray(addNode) ? newNodes : newNodes[0];
2590
- }
2591
- async doRemove(node) {
2592
- const root = this.get("root");
2593
- if (!root)
2594
- throw new Error("root不能为空");
2595
- const { parent, node: curNode } = this.getNodeInfo(node.id, false);
2596
- if (!parent || !curNode)
2597
- throw new Error("找不要删除的节点");
2598
- const index = getNodeIndex(curNode.id, parent);
2599
- if (typeof index !== "number" || index === -1)
2600
- throw new Error("找不要删除的节点");
2601
- parent.items?.splice(index, 1);
2602
- const stage = this.get("stage");
2603
- stage?.remove({ id: node.id, parentId: parent.id, root: cloneDeep(root) });
2604
- const selectDefault = async (pages) => {
2605
- if (pages[0]) {
2606
- await this.select(pages[0]);
2607
- stage?.select(pages[0].id);
2608
- } else {
2609
- this.selectRoot();
2610
- historyService.resetPage();
2611
- }
2340
+ return {
2341
+ node,
2342
+ parent,
2343
+ page
2612
2344
  };
2613
- const rootItems = root.items || [];
2614
- if (isPage(node)) {
2615
- this.state.pageLength -= 1;
2616
- await selectDefault(getPageList(root));
2617
- } else if (isPageFragment(node)) {
2618
- this.state.pageFragmentLength -= 1;
2619
- await selectDefault(getPageFragmentList(root));
2620
- } else {
2621
- await this.select(parent);
2622
- stage?.select(parent.id);
2623
- this.addModifiedNodeId(parent.id);
2624
- }
2625
- if (!rootItems.length) {
2626
- this.resetModifiedNodeId();
2627
- historyService.reset();
2345
+ }
2346
+ }
2347
+ const editorService = new Editor();
2348
+
2349
+ function BaseFormConfig() {
2350
+ return [
2351
+ {
2352
+ name: "id",
2353
+ type: "hidden"
2354
+ },
2355
+ {
2356
+ name: "type",
2357
+ text: "类型",
2358
+ type: "hidden",
2359
+ defaultValue: "base"
2360
+ },
2361
+ {
2362
+ name: "title",
2363
+ text: "名称",
2364
+ rules: [
2365
+ {
2366
+ required: true,
2367
+ message: "请输入名称"
2368
+ }
2369
+ ]
2370
+ },
2371
+ {
2372
+ name: "description",
2373
+ text: "描述"
2628
2374
  }
2375
+ ];
2376
+ }
2377
+
2378
+ const HttpFormConfig = [
2379
+ {
2380
+ name: "autoFetch",
2381
+ text: "自动请求",
2382
+ type: "switch",
2383
+ defaultValue: true
2384
+ },
2385
+ {
2386
+ name: "responseOptions",
2387
+ items: [
2388
+ {
2389
+ name: "dataPath",
2390
+ text: "数据路径"
2391
+ }
2392
+ ]
2393
+ },
2394
+ {
2395
+ type: "fieldset",
2396
+ name: "options",
2397
+ legend: "HTTP 配置",
2398
+ items: [
2399
+ {
2400
+ name: "url",
2401
+ text: "URL"
2402
+ },
2403
+ {
2404
+ name: "method",
2405
+ text: "Method",
2406
+ type: "select",
2407
+ options: [
2408
+ { text: "GET", value: "GET" },
2409
+ { text: "POST", value: "POST" },
2410
+ { text: "PUT", value: "PUT" },
2411
+ { text: "DELETE", value: "DELETE" }
2412
+ ]
2413
+ },
2414
+ {
2415
+ name: "params",
2416
+ type: "key-value",
2417
+ defaultValue: {},
2418
+ text: "参数"
2419
+ },
2420
+ {
2421
+ name: "data",
2422
+ type: "key-value",
2423
+ defaultValue: {},
2424
+ advanced: true,
2425
+ text: "请求体"
2426
+ },
2427
+ {
2428
+ name: "headers",
2429
+ type: "key-value",
2430
+ defaultValue: {},
2431
+ text: "请求头"
2432
+ }
2433
+ ]
2434
+ }
2435
+ ];
2436
+
2437
+ const fillConfig$1 = (config) => [
2438
+ ...BaseFormConfig(),
2439
+ ...config,
2440
+ {
2441
+ type: "tab",
2442
+ items: [
2443
+ {
2444
+ title: "数据定义",
2445
+ items: [
2446
+ {
2447
+ name: "fields",
2448
+ type: "data-source-fields",
2449
+ defaultValue: () => []
2450
+ }
2451
+ ]
2452
+ },
2453
+ {
2454
+ title: "方法定义",
2455
+ items: [
2456
+ {
2457
+ name: "methods",
2458
+ type: "data-source-methods",
2459
+ defaultValue: () => []
2460
+ }
2461
+ ]
2462
+ },
2463
+ {
2464
+ title: "事件配置",
2465
+ items: [
2466
+ {
2467
+ name: "events",
2468
+ src: "datasource",
2469
+ type: "event-select"
2470
+ }
2471
+ ]
2472
+ },
2473
+ {
2474
+ title: "mock数据",
2475
+ items: [
2476
+ {
2477
+ name: "mocks",
2478
+ type: "data-source-mocks",
2479
+ defaultValue: () => []
2480
+ }
2481
+ ]
2482
+ },
2483
+ {
2484
+ title: "请求参数裁剪",
2485
+ display: (formState, { model }) => model.type === "http",
2486
+ items: [
2487
+ {
2488
+ name: "beforeRequest",
2489
+ type: "vs-code",
2490
+ parse: true,
2491
+ height: "600px"
2492
+ }
2493
+ ]
2494
+ },
2495
+ {
2496
+ title: "响应数据裁剪",
2497
+ display: (formState, { model }) => model.type === "http",
2498
+ items: [
2499
+ {
2500
+ name: "afterResponse",
2501
+ type: "vs-code",
2502
+ parse: true,
2503
+ height: "600px"
2504
+ }
2505
+ ]
2506
+ }
2507
+ ]
2508
+ }
2509
+ ];
2510
+ const getFormConfig = (type, configs) => {
2511
+ switch (type) {
2512
+ case "base":
2513
+ return fillConfig$1([]);
2514
+ case "http":
2515
+ return fillConfig$1(HttpFormConfig);
2516
+ default:
2517
+ return fillConfig$1(configs[type] || []);
2518
+ }
2519
+ };
2520
+ const getFormValue = (type, values) => {
2521
+ if (type !== "http") {
2522
+ return values;
2629
2523
  }
2524
+ return {
2525
+ beforeRequest: `(options, context) => {
2630
2526
  /**
2631
- * 删除组件
2632
- * @param {Object} node
2527
+ * 用户可以直接编写函数,在原始接口调用之前,会运行该函数,将这个函数的返回值作为该数据源接口的入参
2528
+ *
2529
+ * options: HttpOptions
2530
+ *
2531
+ * interface HttpOptions {
2532
+ * // 请求链接
2533
+ * url: string;
2534
+ * // query参数
2535
+ * params?: Record<string, string>;
2536
+ * // body数据
2537
+ * data?: Record<string, any>;
2538
+ * // 请求头
2539
+ * headers?: Record<string, string>;
2540
+ * // 请求方法 GET/POST
2541
+ * method?: Method;
2542
+ * }
2543
+ *
2544
+ * context:上下文对象
2545
+ *
2546
+ * interface Content {
2547
+ * app: AppCore;
2548
+ * dataSource: HttpDataSource;
2549
+ * }
2550
+ *
2551
+ * return: HttpOptions
2633
2552
  */
2634
- async remove(nodeOrNodeList) {
2635
- const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
2636
- await Promise.all(nodes.map((node) => this.doRemove(node)));
2637
- if (!(isPage(nodes[0]) || isPageFragment(nodes[0]))) {
2638
- this.pushHistoryState();
2639
- }
2640
- this.emit("remove", nodes);
2641
- }
2642
- async doUpdate(config) {
2643
- const root = this.get("root");
2644
- if (!root)
2645
- throw new Error("root为空");
2646
- if (!config?.id)
2647
- throw new Error("没有配置或者配置缺少id值");
2648
- const info = this.getNodeInfo(config.id, false);
2649
- if (!info.node)
2650
- throw new Error(`获取不到id为${config.id}的节点`);
2651
- const node = cloneDeep(toRaw(info.node));
2652
- let newConfig = await this.toggleFixedPosition(toRaw(config), node, root);
2653
- newConfig = mergeWith(cloneDeep(node), newConfig, (objValue, srcValue, key) => {
2654
- if (typeof srcValue === "undefined" && Object.hasOwn(newConfig, key)) {
2655
- return "";
2656
- }
2657
- if (isObject(srcValue) && Array.isArray(objValue)) {
2658
- return srcValue;
2659
- }
2660
- if (Array.isArray(srcValue)) {
2661
- return srcValue;
2553
+
2554
+ // 此处的返回值会作为这个接口的入参
2555
+ return options;
2556
+ }`,
2557
+ afterResponse: `(response, context) => {
2558
+ /**
2559
+ * 用户可以直接编写函数,在原始接口返回之后,会运行该函数,将这个函数的返回值作为该数据源接口的返回
2560
+
2561
+ * context:上下文对象
2562
+ *
2563
+ * interface Content {
2564
+ * app: AppCore;
2565
+ * dataSource: HttpDataSource;
2566
+ * }
2567
+ *
2568
+ */
2569
+
2570
+ // 此处的返回值会作为这个接口的返回值
2571
+ return response;
2572
+ }`,
2573
+ ...values
2574
+ };
2575
+ };
2576
+ const getDisplayField = (dataSources, key) => {
2577
+ const displayState = [];
2578
+ const matches = key.matchAll(/\$\{([\s\S]+?)\}/g);
2579
+ let index = 0;
2580
+ for (const match of matches) {
2581
+ if (typeof match.index === "undefined")
2582
+ break;
2583
+ displayState.push({
2584
+ type: "text",
2585
+ value: key.substring(index, match.index)
2586
+ });
2587
+ let dsText = "";
2588
+ let ds;
2589
+ let fields;
2590
+ match[1].split(".").forEach((item, index2) => {
2591
+ if (index2 === 0) {
2592
+ ds = dataSources.find((ds2) => ds2.id === item);
2593
+ dsText += ds?.title || item;
2594
+ fields = ds?.fields;
2595
+ return;
2662
2596
  }
2597
+ const field = fields?.find((field2) => field2.name === item);
2598
+ fields = field?.fields;
2599
+ dsText += `.${field?.title || item}`;
2663
2600
  });
2664
- if (!newConfig.type)
2665
- throw new Error("配置缺少type值");
2666
- if (newConfig.type === NodeType.ROOT) {
2667
- this.set("root", newConfig);
2668
- return newConfig;
2601
+ displayState.push({
2602
+ type: "var",
2603
+ value: dsText
2604
+ });
2605
+ index = match.index + match[0].length;
2606
+ }
2607
+ if (index < key.length) {
2608
+ displayState.push({
2609
+ type: "text",
2610
+ value: key.substring(index)
2611
+ });
2612
+ }
2613
+ return displayState;
2614
+ };
2615
+ const getCascaderOptionsFromFields = (fields = [], dataSourceFieldType = ["any"]) => {
2616
+ const child = [];
2617
+ fields.forEach((field) => {
2618
+ if (!dataSourceFieldType.length) {
2619
+ dataSourceFieldType.push("any");
2620
+ }
2621
+ const children = getCascaderOptionsFromFields(field.fields, dataSourceFieldType);
2622
+ const item = {
2623
+ label: field.title || field.name,
2624
+ value: field.name,
2625
+ children
2626
+ };
2627
+ const fieldType = field.type || "any";
2628
+ if (dataSourceFieldType.includes("any") || dataSourceFieldType.includes(fieldType)) {
2629
+ child.push(item);
2630
+ return;
2669
2631
  }
2670
- const { parent } = info;
2671
- if (!parent)
2672
- throw new Error("获取不到父级节点");
2673
- const parentNodeItems = parent.items;
2674
- const index = getNodeIndex(newConfig.id, parent);
2675
- if (!parentNodeItems || typeof index === "undefined" || index === -1)
2676
- throw new Error("更新的节点未找到");
2677
- const newLayout = await this.getLayout(newConfig);
2678
- const layout = await this.getLayout(node);
2679
- if (Array.isArray(newConfig.items) && newLayout !== layout) {
2680
- newConfig = setChildrenLayout(newConfig, newLayout);
2632
+ if (!dataSourceFieldType.includes(fieldType) && !["array", "object", "any"].includes(fieldType)) {
2633
+ return;
2681
2634
  }
2682
- parentNodeItems[index] = newConfig;
2683
- const nodes = this.get("nodes");
2684
- const targetIndex = nodes.findIndex((nodeItem) => `${nodeItem.id}` === `${newConfig.id}`);
2685
- nodes.splice(targetIndex, 1, newConfig);
2686
- this.set("nodes", [...nodes]);
2687
- this.get("stage")?.update({
2688
- config: cloneDeep(newConfig),
2689
- parentId: parent.id,
2690
- root: cloneDeep(root)
2691
- });
2692
- if (isPage(newConfig) || isPageFragment(newConfig)) {
2693
- this.set("page", newConfig);
2635
+ if (!children.length && ["object", "array", "any"].includes(field.type || "")) {
2636
+ return;
2694
2637
  }
2695
- this.addModifiedNodeId(newConfig.id);
2638
+ child.push(item);
2639
+ });
2640
+ return child;
2641
+ };
2642
+
2643
+ const canUsePluginMethods$3 = {
2644
+ async: [],
2645
+ sync: [
2646
+ "getFormConfig",
2647
+ "setFormConfig",
2648
+ "getFormValue",
2649
+ "setFormValue",
2650
+ "getFormEvent",
2651
+ "setFormEvent",
2652
+ "getFormMethod",
2653
+ "setFormMethod",
2654
+ "add",
2655
+ "update",
2656
+ "remove",
2657
+ "createId"
2658
+ ]
2659
+ };
2660
+ class DataSource extends BaseService {
2661
+ state = reactive({
2662
+ datasourceTypeList: [],
2663
+ dataSources: [],
2664
+ editable: true,
2665
+ configs: {},
2666
+ values: {},
2667
+ events: {},
2668
+ methods: {}
2669
+ });
2670
+ constructor() {
2671
+ super(canUsePluginMethods$3.sync.map((methodName) => ({ name: methodName, isAsync: false })));
2672
+ }
2673
+ set(name, value) {
2674
+ this.state[name] = value;
2675
+ }
2676
+ get(name) {
2677
+ return this.state[name];
2678
+ }
2679
+ getFormConfig(type = "base") {
2680
+ return getFormConfig(toLine(type), this.get("configs"));
2681
+ }
2682
+ setFormConfig(type, config) {
2683
+ this.get("configs")[toLine(type)] = config;
2684
+ }
2685
+ getFormValue(type = "base") {
2686
+ return getFormValue(toLine(type), this.get("values")[type]);
2687
+ }
2688
+ setFormValue(type, value) {
2689
+ this.get("values")[toLine(type)] = value;
2690
+ }
2691
+ getFormEvent(type = "base") {
2692
+ return this.get("events")[toLine(type)] || [];
2693
+ }
2694
+ setFormEvent(type, value = []) {
2695
+ this.get("events")[toLine(type)] = value;
2696
+ }
2697
+ getFormMethod(type = "base") {
2698
+ return this.get("methods")[toLine(type)] || [];
2699
+ }
2700
+ setFormMethod(type, value = []) {
2701
+ this.get("methods")[toLine(type)] = value;
2702
+ }
2703
+ add(config) {
2704
+ const newConfig = {
2705
+ ...config,
2706
+ id: config.id && !this.getDataSourceById(config.id) ? config.id : this.createId()
2707
+ };
2708
+ this.get("dataSources").push(newConfig);
2709
+ this.emit("add", newConfig);
2696
2710
  return newConfig;
2697
2711
  }
2698
- /**
2699
- * 更新节点
2700
- * @param config 新的节点配置,配置中需要有id信息
2701
- * @returns 更新后的节点配置
2702
- */
2703
- async update(config) {
2704
- const nodes = Array.isArray(config) ? config : [config];
2705
- const newNodes = await Promise.all(nodes.map((node) => this.doUpdate(node)));
2706
- if (newNodes[0]?.type !== NodeType.ROOT) {
2707
- this.pushHistoryState();
2708
- }
2709
- this.emit("update", newNodes);
2710
- return Array.isArray(config) ? newNodes : newNodes[0];
2712
+ update(config) {
2713
+ const dataSources = this.get("dataSources");
2714
+ const index = dataSources.findIndex((ds) => ds.id === config.id);
2715
+ dataSources[index] = cloneDeep(config);
2716
+ this.emit("update", config);
2717
+ return config;
2718
+ }
2719
+ remove(id) {
2720
+ const dataSources = this.get("dataSources");
2721
+ const index = dataSources.findIndex((ds) => ds.id === id);
2722
+ dataSources.splice(index, 1);
2723
+ this.emit("remove", id);
2724
+ }
2725
+ createId() {
2726
+ return `ds_${guid()}`;
2727
+ }
2728
+ getDataSourceById(id) {
2729
+ return this.get("dataSources").find((ds) => ds.id === id);
2711
2730
  }
2712
- /**
2713
- * 将id为id1的组件移动到id为id2的组件位置上,例如:[1,2,3,4] -> sort(1,3) -> [2,1,3,4]
2714
- * @param id1 组件ID
2715
- * @param id2 组件ID
2716
- * @returns void
2717
- */
2718
- async sort(id1, id2) {
2719
- const root = this.get("root");
2720
- if (!root)
2721
- throw new Error("root为空");
2722
- const node = this.get("node");
2723
- if (!node)
2724
- throw new Error("当前节点为空");
2725
- const parent = cloneDeep(toRaw(this.get("parent")));
2726
- if (!parent)
2727
- throw new Error("父节点为空");
2728
- const index2 = parent.items.findIndex((node2) => `${node2.id}` === `${id2}`);
2729
- if (index2 < 0)
2730
- return;
2731
- const index1 = parent.items.findIndex((node2) => `${node2.id}` === `${id1}`);
2732
- parent.items.splice(index2, 0, ...parent.items.splice(index1, 1));
2733
- await this.update(parent);
2734
- await this.select(node);
2735
- this.get("stage")?.update({
2736
- config: cloneDeep(node),
2737
- parentId: parent.id,
2738
- root: cloneDeep(root)
2739
- });
2740
- this.addModifiedNodeId(parent.id);
2741
- this.pushHistoryState();
2731
+ resetState() {
2732
+ this.set("dataSources", []);
2742
2733
  }
2743
- /**
2744
- * 将组件节点配置存储到localStorage中
2745
- * @param config 组件节点配置
2746
- * @returns
2747
- */
2748
- copy(config) {
2749
- storageService.setItem(COPY_STORAGE_KEY, Array.isArray(config) ? config : [config], {
2750
- protocol: Protocol.OBJECT
2751
- });
2734
+ destroy() {
2735
+ this.removeAllListeners();
2736
+ this.resetState();
2737
+ this.removeAllPlugins();
2738
+ }
2739
+ usePlugin(options) {
2740
+ super.usePlugin(options);
2752
2741
  }
2753
2742
  /**
2754
- * 复制时会带上组件关联的依赖
2743
+ * 复制时会带上组件关联的数据源
2755
2744
  * @param config 组件节点配置
2756
2745
  * @returns
2757
2746
  */
2758
2747
  copyWithRelated(config) {
2759
2748
  const copyNodes = Array.isArray(config) ? config : [config];
2760
- depService.clearByType(DepTargetType.RELATED_COMP_WHEN_COPY);
2761
- depService.collect(copyNodes, true, DepTargetType.RELATED_COMP_WHEN_COPY);
2762
- const customTarget = depService.getTarget(
2763
- DepTargetType.RELATED_COMP_WHEN_COPY,
2764
- DepTargetType.RELATED_COMP_WHEN_COPY
2765
- );
2749
+ depService.clearByType(DepTargetType.RELATED_DS_WHEN_COPY);
2750
+ depService.collect(copyNodes, true, DepTargetType.RELATED_DS_WHEN_COPY);
2751
+ const customTarget = depService.getTarget(DepTargetType.RELATED_DS_WHEN_COPY, DepTargetType.RELATED_DS_WHEN_COPY);
2752
+ const copyData = [];
2766
2753
  if (customTarget) {
2767
2754
  Object.keys(customTarget.deps).forEach((nodeId) => {
2768
- const node = this.getNodeById(nodeId);
2755
+ const node = editorService.getNodeById(nodeId);
2769
2756
  if (!node)
2770
2757
  return;
2771
2758
  customTarget.deps[nodeId].keys.forEach((key) => {
2772
- const relateNodeId = get(node, key);
2773
- const isExist = copyNodes.find((node2) => node2.id === relateNodeId);
2759
+ const [relateDsId] = get(node, key);
2760
+ const isExist = copyData.find((dsItem) => dsItem.id === relateDsId);
2774
2761
  if (!isExist) {
2775
- const relateNode = this.getNodeById(relateNodeId);
2776
- if (relateNode) {
2777
- copyNodes.push(relateNode);
2762
+ const relateDs = this.getDataSourceById(relateDsId);
2763
+ if (relateDs) {
2764
+ copyData.push(relateDs);
2765
+ }
2766
+ }
2767
+ });
2768
+ });
2769
+ }
2770
+ storageService.setItem(COPY_DS_STORAGE_KEY, copyData, {
2771
+ protocol: Protocol.OBJECT
2772
+ });
2773
+ }
2774
+ /**
2775
+ * 粘贴数据源
2776
+ * @returns
2777
+ */
2778
+ paste() {
2779
+ const dataSource = storageService.getItem(COPY_DS_STORAGE_KEY);
2780
+ dataSource.forEach((item) => {
2781
+ if (!this.getDataSourceById(item.id)) {
2782
+ this.add(item);
2783
+ }
2784
+ });
2785
+ }
2786
+ }
2787
+ const dataSourceService = new DataSource();
2788
+
2789
+ const arrayOptions = [
2790
+ { text: "包含", value: "include" },
2791
+ { text: "不包含", value: "not_include" }
2792
+ ];
2793
+ const eqOptions = [
2794
+ { text: "等于", value: "=" },
2795
+ { text: "不等于", value: "!=" }
2796
+ ];
2797
+ const numberOptions = [
2798
+ { text: "大于", value: ">" },
2799
+ { text: "大于等于", value: ">=" },
2800
+ { text: "小于", value: "<" },
2801
+ { text: "小于等于", value: "<=" },
2802
+ { text: "在范围内", value: "between" },
2803
+ { text: "不在范围内", value: "not_between" }
2804
+ ];
2805
+ const styleTabConfig = {
2806
+ title: "样式",
2807
+ items: [
2808
+ {
2809
+ name: "style",
2810
+ items: [
2811
+ {
2812
+ type: "fieldset",
2813
+ legend: "位置",
2814
+ items: [
2815
+ {
2816
+ type: "data-source-field-select",
2817
+ name: "position",
2818
+ text: "固定定位",
2819
+ checkStrictly: false,
2820
+ dataSourceFieldType: ["string"],
2821
+ fieldConfig: {
2822
+ type: "checkbox",
2823
+ activeValue: "fixed",
2824
+ inactiveValue: "absolute",
2825
+ defaultValue: "absolute"
2826
+ }
2827
+ },
2828
+ {
2829
+ type: "data-source-field-select",
2830
+ name: "left",
2831
+ text: "left",
2832
+ checkStrictly: false,
2833
+ dataSourceFieldType: ["string", "number"],
2834
+ fieldConfig: {
2835
+ type: "text"
2836
+ }
2837
+ },
2838
+ {
2839
+ type: "data-source-field-select",
2840
+ name: "top",
2841
+ text: "top",
2842
+ checkStrictly: false,
2843
+ dataSourceFieldType: ["string", "number"],
2844
+ fieldConfig: {
2845
+ type: "text"
2846
+ },
2847
+ disabled: (vm, { model }) => model.position === "fixed" && model._magic_position === "fixedBottom"
2848
+ },
2849
+ {
2850
+ type: "data-source-field-select",
2851
+ name: "right",
2852
+ text: "right",
2853
+ checkStrictly: false,
2854
+ dataSourceFieldType: ["string", "number"],
2855
+ fieldConfig: {
2856
+ type: "text"
2857
+ }
2858
+ },
2859
+ {
2860
+ type: "data-source-field-select",
2861
+ name: "bottom",
2862
+ text: "bottom",
2863
+ checkStrictly: false,
2864
+ dataSourceFieldType: ["string", "number"],
2865
+ fieldConfig: {
2866
+ type: "text"
2867
+ },
2868
+ disabled: (vm, { model }) => model.position === "fixed" && model._magic_position === "fixedTop"
2869
+ }
2870
+ ]
2871
+ },
2872
+ {
2873
+ type: "fieldset",
2874
+ legend: "盒子",
2875
+ items: [
2876
+ {
2877
+ type: "data-source-field-select",
2878
+ name: "width",
2879
+ text: "宽度",
2880
+ checkStrictly: false,
2881
+ dataSourceFieldType: ["string", "number"],
2882
+ fieldConfig: {
2883
+ type: "text"
2884
+ }
2885
+ },
2886
+ {
2887
+ type: "data-source-field-select",
2888
+ name: "height",
2889
+ text: "高度",
2890
+ checkStrictly: false,
2891
+ dataSourceFieldType: ["string", "number"],
2892
+ fieldConfig: {
2893
+ type: "text"
2894
+ }
2895
+ },
2896
+ {
2897
+ type: "data-source-field-select",
2898
+ text: "overflow",
2899
+ name: "overflow",
2900
+ checkStrictly: false,
2901
+ dataSourceFieldType: ["string"],
2902
+ fieldConfig: {
2903
+ type: "select",
2904
+ options: [
2905
+ { text: "visible", value: "visible" },
2906
+ { text: "hidden", value: "hidden" },
2907
+ { text: "clip", value: "clip" },
2908
+ { text: "scroll", value: "scroll" },
2909
+ { text: "auto", value: "auto" },
2910
+ { text: "overlay", value: "overlay" }
2911
+ ]
2912
+ }
2913
+ }
2914
+ ]
2915
+ },
2916
+ {
2917
+ type: "fieldset",
2918
+ legend: "边框",
2919
+ items: [
2920
+ {
2921
+ type: "data-source-field-select",
2922
+ name: "borderWidth",
2923
+ text: "宽度",
2924
+ defaultValue: "0",
2925
+ checkStrictly: false,
2926
+ dataSourceFieldType: ["string", "number"],
2927
+ fieldConfig: {
2928
+ type: "text"
2929
+ }
2930
+ },
2931
+ {
2932
+ type: "data-source-field-select",
2933
+ name: "borderColor",
2934
+ text: "颜色",
2935
+ checkStrictly: false,
2936
+ dataSourceFieldType: ["string"],
2937
+ fieldConfig: {
2938
+ type: "text"
2939
+ }
2940
+ },
2941
+ {
2942
+ type: "data-source-field-select",
2943
+ name: "borderStyle",
2944
+ text: "样式",
2945
+ defaultValue: "none",
2946
+ checkStrictly: false,
2947
+ dataSourceFieldType: ["string"],
2948
+ fieldConfig: {
2949
+ type: "select",
2950
+ options: [
2951
+ { text: "none", value: "none" },
2952
+ { text: "hidden", value: "hidden" },
2953
+ { text: "dotted", value: "dotted" },
2954
+ { text: "dashed", value: "dashed" },
2955
+ { text: "solid", value: "solid" },
2956
+ { text: "double", value: "double" },
2957
+ { text: "groove", value: "groove" },
2958
+ { text: "ridge", value: "ridge" },
2959
+ { text: "inset", value: "inset" },
2960
+ { text: "outset", value: "outset" }
2961
+ ]
2962
+ }
2778
2963
  }
2779
- }
2780
- });
2781
- });
2782
- }
2783
- storageService.setItem(COPY_STORAGE_KEY, copyNodes, {
2784
- protocol: Protocol.OBJECT
2785
- });
2786
- }
2787
- /**
2788
- * 从localStorage中获取节点,然后添加到当前容器中
2789
- * @param position 粘贴的坐标
2790
- * @returns 添加后的组件节点配置
2791
- */
2792
- async paste(position = {}) {
2793
- const config = storageService.getItem(COPY_STORAGE_KEY);
2794
- if (!Array.isArray(config))
2795
- return;
2796
- const node = this.get("node");
2797
- let parent = null;
2798
- if (config.length === 1 && config[0].id === node?.id) {
2799
- parent = this.get("parent");
2800
- if (parent?.type === NodeType.ROOT) {
2801
- parent = this.get("page");
2802
- }
2803
- }
2804
- const pasteConfigs = await this.doPaste(config, position);
2805
- propsService.replaceRelateId(config, pasteConfigs);
2806
- return this.add(pasteConfigs, parent);
2807
- }
2808
- async doPaste(config, position = {}) {
2809
- propsService.clearRelateId();
2810
- const pasteConfigs = beforePaste(position, cloneDeep(config));
2811
- return pasteConfigs;
2812
- }
2813
- async doAlignCenter(config) {
2814
- const parent = this.getParentById(config.id);
2815
- if (!parent)
2816
- throw new Error("找不到父节点");
2817
- const node = cloneDeep(toRaw(config));
2818
- const layout = await this.getLayout(parent, node);
2819
- if (layout === Layout.RELATIVE) {
2820
- return config;
2821
- }
2822
- if (!node.style)
2823
- return config;
2824
- const stage = this.get("stage");
2825
- const doc = stage?.renderer.contentWindow?.document;
2826
- if (doc) {
2827
- const el = doc.getElementById(`${node.id}`);
2828
- const parentEl = layout === Layout.FIXED ? doc.body : el?.offsetParent;
2829
- if (parentEl && el) {
2830
- node.style.left = (parentEl.clientWidth - el.clientWidth) / 2;
2831
- node.style.right = "";
2832
- }
2833
- } else if (parent.style && isNumber(parent.style?.width) && isNumber(node.style?.width)) {
2834
- node.style.left = (parent.style.width - node.style.width) / 2;
2835
- node.style.right = "";
2836
- }
2837
- return node;
2838
- }
2839
- /**
2840
- * 将指点节点设置居中
2841
- * @param config 组件节点配置
2842
- * @returns 当前组件节点配置
2843
- */
2844
- async alignCenter(config) {
2845
- const nodes = Array.isArray(config) ? config : [config];
2846
- const stage = this.get("stage");
2847
- const newNodes = await Promise.all(nodes.map((node) => this.doAlignCenter(node)));
2848
- const newNode = await this.update(newNodes);
2849
- if (newNodes.length > 1) {
2850
- await stage?.multiSelect(newNodes.map((node) => node.id));
2851
- } else {
2852
- await stage?.select(newNodes[0].id);
2853
- }
2854
- return newNode;
2855
- }
2856
- /**
2857
- * 移动当前选中节点位置
2858
- * @param offset 偏移量
2859
- */
2860
- async moveLayer(offset) {
2861
- const root = this.get("root");
2862
- if (!root)
2863
- throw new Error("root为空");
2864
- const parent = this.get("parent");
2865
- if (!parent)
2866
- throw new Error("父节点为空");
2867
- const node = this.get("node");
2868
- if (!node)
2869
- throw new Error("当前节点为空");
2870
- const brothers = parent.items || [];
2871
- const index = brothers.findIndex((item) => `${item.id}` === `${node?.id}`);
2872
- const layout = await this.getLayout(parent, node);
2873
- const isRelative = layout === Layout.RELATIVE;
2874
- let offsetIndex;
2875
- if (offset === LayerOffset.TOP) {
2876
- offsetIndex = isRelative ? 0 : brothers.length;
2877
- } else if (offset === LayerOffset.BOTTOM) {
2878
- offsetIndex = isRelative ? brothers.length : 0;
2879
- } else {
2880
- offsetIndex = index + (isRelative ? -offset : offset);
2881
- }
2882
- if (offsetIndex > 0 && offsetIndex > brothers.length || offsetIndex < 0) {
2883
- return;
2884
- }
2885
- brothers.splice(index, 1);
2886
- brothers.splice(offsetIndex, 0, node);
2887
- const grandparent = this.getParentById(parent.id);
2888
- this.get("stage")?.update({
2889
- config: cloneDeep(toRaw(parent)),
2890
- parentId: grandparent?.id,
2891
- root: cloneDeep(root)
2892
- });
2893
- this.addModifiedNodeId(parent.id);
2894
- this.pushHistoryState();
2895
- this.emit("move-layer", offset);
2896
- }
2897
- /**
2898
- * 移动到指定容器中
2899
- * @param config 需要移动的节点
2900
- * @param targetId 容器ID
2901
- */
2902
- async moveToContainer(config, targetId) {
2903
- const root = this.get("root");
2904
- const { node, parent } = this.getNodeInfo(config.id, false);
2905
- const target = this.getNodeById(targetId, false);
2906
- const stage = this.get("stage");
2907
- if (root && node && parent && stage) {
2908
- const index = getNodeIndex(node.id, parent);
2909
- parent.items?.splice(index, 1);
2910
- await stage.remove({ id: node.id, parentId: parent.id, root: cloneDeep(root) });
2911
- const layout = await this.getLayout(target);
2912
- const newConfig = mergeWith(cloneDeep(node), config, (objValue, srcValue) => {
2913
- if (Array.isArray(srcValue)) {
2914
- return srcValue;
2964
+ ]
2965
+ },
2966
+ {
2967
+ type: "fieldset",
2968
+ legend: "背景",
2969
+ items: [
2970
+ {
2971
+ type: "data-source-field-select",
2972
+ name: "backgroundImage",
2973
+ text: "背景图",
2974
+ checkStrictly: false,
2975
+ dataSourceFieldType: ["string"],
2976
+ fieldConfig: {
2977
+ type: "text"
2978
+ }
2979
+ },
2980
+ {
2981
+ type: "data-source-field-select",
2982
+ name: "backgroundColor",
2983
+ text: "背景颜色",
2984
+ checkStrictly: false,
2985
+ dataSourceFieldType: ["string"],
2986
+ fieldConfig: {
2987
+ type: "colorPicker"
2988
+ }
2989
+ },
2990
+ {
2991
+ type: "data-source-field-select",
2992
+ name: "backgroundRepeat",
2993
+ text: "背景图重复",
2994
+ defaultValue: "no-repeat",
2995
+ checkStrictly: false,
2996
+ dataSourceFieldType: ["string"],
2997
+ fieldConfig: {
2998
+ type: "select",
2999
+ options: [
3000
+ { text: "repeat", value: "repeat" },
3001
+ { text: "repeat-x", value: "repeat-x" },
3002
+ { text: "repeat-y", value: "repeat-y" },
3003
+ { text: "no-repeat", value: "no-repeat" },
3004
+ { text: "inherit", value: "inherit" }
3005
+ ]
3006
+ }
3007
+ },
3008
+ {
3009
+ type: "data-source-field-select",
3010
+ name: "backgroundSize",
3011
+ text: "背景图大小",
3012
+ defaultValue: "100% 100%",
3013
+ checkStrictly: false,
3014
+ dataSourceFieldType: ["string"],
3015
+ fieldConfig: {
3016
+ type: "text"
3017
+ }
3018
+ }
3019
+ ]
3020
+ },
3021
+ {
3022
+ type: "fieldset",
3023
+ legend: "字体",
3024
+ items: [
3025
+ {
3026
+ type: "data-source-field-select",
3027
+ name: "color",
3028
+ text: "颜色",
3029
+ checkStrictly: false,
3030
+ dataSourceFieldType: ["string"],
3031
+ fieldConfig: {
3032
+ type: "colorPicker"
3033
+ }
3034
+ },
3035
+ {
3036
+ type: "data-source-field-select",
3037
+ name: "fontSize",
3038
+ text: "大小",
3039
+ checkStrictly: false,
3040
+ dataSourceFieldType: ["string", "number"],
3041
+ fieldConfig: {
3042
+ type: "text"
3043
+ }
3044
+ },
3045
+ {
3046
+ type: "data-source-field-select",
3047
+ name: "fontWeight",
3048
+ text: "粗细",
3049
+ checkStrictly: false,
3050
+ dataSourceFieldType: ["string", "number"],
3051
+ fieldConfig: {
3052
+ type: "text"
3053
+ }
3054
+ }
3055
+ ]
3056
+ },
3057
+ {
3058
+ type: "fieldset",
3059
+ legend: "变形",
3060
+ name: "transform",
3061
+ items: [
3062
+ {
3063
+ type: "data-source-field-select",
3064
+ name: "rotate",
3065
+ text: "旋转角度",
3066
+ checkStrictly: false,
3067
+ dataSourceFieldType: ["string"],
3068
+ fieldConfig: {
3069
+ type: "text"
3070
+ }
3071
+ },
3072
+ {
3073
+ type: "data-source-field-select",
3074
+ name: "scale",
3075
+ text: "缩放",
3076
+ checkStrictly: false,
3077
+ dataSourceFieldType: ["number", "string"],
3078
+ fieldConfig: {
3079
+ type: "text"
3080
+ }
3081
+ }
3082
+ ]
2915
3083
  }
2916
- });
2917
- newConfig.style = getInitPositionStyle(newConfig.style, layout);
2918
- target.items.push(newConfig);
2919
- await stage.select(targetId);
2920
- const targetParent = this.getParentById(target.id);
2921
- await stage.update({ config: cloneDeep(target), parentId: targetParent?.id, root: cloneDeep(root) });
2922
- await this.select(newConfig);
2923
- stage.select(newConfig.id);
2924
- this.addModifiedNodeId(target.id);
2925
- this.addModifiedNodeId(parent.id);
2926
- this.pushHistoryState();
2927
- return newConfig;
2928
- }
2929
- }
2930
- async dragTo(config, targetParent, targetIndex) {
2931
- if (!targetParent || !Array.isArray(targetParent.items))
2932
- return;
2933
- const { parent, node: curNode } = this.getNodeInfo(config.id, false);
2934
- if (!parent || !curNode)
2935
- throw new Error("找不要删除的节点");
2936
- const index = getNodeIndex(curNode.id, parent);
2937
- if (typeof index !== "number" || index === -1)
2938
- throw new Error("找不要删除的节点");
2939
- if (parent.id === targetParent.id) {
2940
- if (index === targetIndex)
2941
- return;
2942
- if (index < targetIndex) {
2943
- targetIndex -= 1;
2944
- }
2945
- }
2946
- const layout = await this.getLayout(parent);
2947
- const newLayout = await this.getLayout(targetParent);
2948
- if (newLayout !== layout) {
2949
- setLayout(config, newLayout);
2950
- }
2951
- parent.items?.splice(index, 1);
2952
- targetParent.items?.splice(targetIndex, 0, config);
2953
- const page = this.get("page");
2954
- const root = this.get("root");
2955
- const stage = this.get("stage");
2956
- if (stage && page && root) {
2957
- stage.update({
2958
- config: cloneDeep(page),
2959
- parentId: root.id,
2960
- root: cloneDeep(root)
2961
- });
2962
- }
2963
- this.addModifiedNodeId(config.id);
2964
- this.addModifiedNodeId(parent.id);
2965
- this.pushHistoryState();
2966
- this.emit("drag-to", { index, targetIndex, config, parent, targetParent });
2967
- }
2968
- /**
2969
- * 撤销当前操作
2970
- * @returns 上一次数据
2971
- */
2972
- async undo() {
2973
- const value = historyService.undo();
2974
- await this.changeHistoryState(value);
2975
- return value;
2976
- }
2977
- /**
2978
- * 恢复到下一步
2979
- * @returns 下一步数据
2980
- */
2981
- async redo() {
2982
- const value = historyService.redo();
2983
- await this.changeHistoryState(value);
2984
- return value;
2985
- }
2986
- async move(left, top) {
2987
- const node = toRaw(this.get("node"));
2988
- if (!node || isPage(node))
2989
- return;
2990
- const { style, id, type } = node;
2991
- if (!style || !["absolute", "fixed"].includes(style.position))
2992
- return;
2993
- const update = (style2) => this.update({
2994
- id,
2995
- type,
2996
- style: style2
2997
- });
2998
- if (top) {
2999
- if (isNumber(style.top)) {
3000
- update({
3001
- ...style,
3002
- top: Number(style.top) + Number(top),
3003
- bottom: ""
3004
- });
3005
- } else if (isNumber(style.bottom)) {
3006
- update({
3007
- ...style,
3008
- bottom: Number(style.bottom) - Number(top),
3009
- top: ""
3010
- });
3011
- }
3084
+ ]
3012
3085
  }
3013
- if (left) {
3014
- if (isNumber(style.left)) {
3015
- update({
3016
- ...style,
3017
- left: Number(style.left) + Number(left),
3018
- right: ""
3019
- });
3020
- } else if (isNumber(style.right)) {
3021
- update({
3022
- ...style,
3023
- right: Number(style.right) - Number(left),
3024
- left: ""
3025
- });
3026
- }
3086
+ ]
3087
+ };
3088
+ const eventTabConfig = {
3089
+ title: "事件",
3090
+ items: [
3091
+ {
3092
+ name: "events",
3093
+ src: "component",
3094
+ labelWidth: "100px",
3095
+ type: "event-select"
3027
3096
  }
3028
- }
3029
- resetState() {
3030
- this.set("root", null);
3031
- this.set("node", null);
3032
- this.set("nodes", []);
3033
- this.set("page", null);
3034
- this.set("parent", null);
3035
- this.set("stage", null);
3036
- this.set("highlightNode", null);
3037
- this.set("modifiedNodeIds", /* @__PURE__ */ new Map());
3038
- this.set("pageLength", 0);
3039
- }
3040
- destroy() {
3041
- this.removeAllListeners();
3042
- this.resetState();
3043
- this.removeAllPlugins();
3044
- }
3045
- resetModifiedNodeId() {
3046
- this.get("modifiedNodeIds").clear();
3047
- }
3048
- usePlugin(options) {
3049
- super.usePlugin(options);
3050
- }
3051
- addModifiedNodeId(id) {
3052
- if (!this.isHistoryStateChange) {
3053
- this.get("modifiedNodeIds").set(id, id);
3097
+ ]
3098
+ };
3099
+ const advancedTabConfig = {
3100
+ title: "高级",
3101
+ lazy: true,
3102
+ items: [
3103
+ {
3104
+ name: "created",
3105
+ text: "created",
3106
+ type: "code-select"
3107
+ },
3108
+ {
3109
+ name: "mounted",
3110
+ text: "mounted",
3111
+ type: "code-select"
3054
3112
  }
3055
- }
3056
- pushHistoryState() {
3057
- const curNode = cloneDeep(toRaw(this.get("node")));
3058
- const page = this.get("page");
3059
- if (!this.isHistoryStateChange && curNode && page) {
3060
- historyService.push({
3061
- data: cloneDeep(toRaw(page)),
3062
- modifiedNodeIds: this.get("modifiedNodeIds"),
3063
- nodeId: curNode.id
3064
- });
3113
+ ]
3114
+ };
3115
+ const displayTabConfig = {
3116
+ title: "显示条件",
3117
+ display: (vm, { model }) => model.type !== "page",
3118
+ items: [
3119
+ {
3120
+ type: "groupList",
3121
+ name: "displayConds",
3122
+ titlePrefix: "条件组",
3123
+ expandAll: true,
3124
+ items: [
3125
+ {
3126
+ type: "table",
3127
+ name: "cond",
3128
+ items: [
3129
+ {
3130
+ type: "data-source-field-select",
3131
+ name: "field",
3132
+ value: "key",
3133
+ label: "字段",
3134
+ checkStrictly: false,
3135
+ dataSourceFieldType: ["string", "number", "boolean", "any"]
3136
+ },
3137
+ {
3138
+ type: "select",
3139
+ options: (mForm, { model }) => {
3140
+ const [id, ...fieldNames] = model.field;
3141
+ const ds = dataSourceService.getDataSourceById(id);
3142
+ let fields = ds?.fields || [];
3143
+ let type = "";
3144
+ (fieldNames || []).forEach((fieldName) => {
3145
+ const field = fields.find((f) => f.name === fieldName);
3146
+ fields = field?.fields || [];
3147
+ type = field?.type || "";
3148
+ });
3149
+ if (type === "array") {
3150
+ return arrayOptions;
3151
+ }
3152
+ if (type === "boolean") {
3153
+ return [
3154
+ { text: "是", value: "is" },
3155
+ { text: "不是", value: "not" }
3156
+ ];
3157
+ }
3158
+ if (type === "number") {
3159
+ return [...eqOptions, ...numberOptions];
3160
+ }
3161
+ if (type === "string") {
3162
+ return [...arrayOptions, ...eqOptions];
3163
+ }
3164
+ return [...arrayOptions, ...eqOptions, ...numberOptions];
3165
+ },
3166
+ label: "条件",
3167
+ name: "op"
3168
+ },
3169
+ {
3170
+ label: "值",
3171
+ items: [
3172
+ {
3173
+ name: "value",
3174
+ type: (mForm, { model }) => {
3175
+ const [id, ...fieldNames] = model.field;
3176
+ const ds = dataSourceService.getDataSourceById(id);
3177
+ let fields = ds?.fields || [];
3178
+ let type = "";
3179
+ (fieldNames || []).forEach((fieldName) => {
3180
+ const field = fields.find((f) => f.name === fieldName);
3181
+ fields = field?.fields || [];
3182
+ type = field?.type || "";
3183
+ });
3184
+ if (type === "number") {
3185
+ return "number";
3186
+ }
3187
+ if (type === "boolean") {
3188
+ return "select";
3189
+ }
3190
+ return "text";
3191
+ },
3192
+ options: [
3193
+ { text: "true", value: true },
3194
+ { text: "false", value: false }
3195
+ ],
3196
+ display: (vm, { model }) => !["between", "not_between"].includes(model.op)
3197
+ },
3198
+ {
3199
+ name: "range",
3200
+ type: "number-range",
3201
+ display: (vm, { model }) => ["between", "not_between"].includes(model.op)
3202
+ }
3203
+ ]
3204
+ }
3205
+ ]
3206
+ }
3207
+ ]
3065
3208
  }
3066
- this.isHistoryStateChange = false;
3067
- }
3068
- async changeHistoryState(value) {
3069
- if (!value)
3070
- return;
3071
- this.isHistoryStateChange = true;
3072
- await this.update(value.data);
3073
- this.set("modifiedNodeIds", value.modifiedNodeIds);
3074
- setTimeout(async () => {
3075
- if (!value.nodeId)
3076
- return;
3077
- await this.select(value.nodeId);
3078
- this.get("stage")?.select(value.nodeId);
3079
- }, 0);
3080
- this.emit("history-change", value.data);
3209
+ ]
3210
+ };
3211
+ const fillConfig = (config = [], labelWidth = "80px") => [
3212
+ {
3213
+ type: "tab",
3214
+ labelWidth,
3215
+ items: [
3216
+ {
3217
+ title: "属性",
3218
+ items: [
3219
+ // 组件类型,必须要有
3220
+ {
3221
+ text: "type",
3222
+ name: "type",
3223
+ type: "hidden"
3224
+ },
3225
+ // 组件id,必须要有
3226
+ {
3227
+ name: "id",
3228
+ type: "display",
3229
+ text: "id"
3230
+ },
3231
+ {
3232
+ name: "name",
3233
+ text: "组件名称"
3234
+ },
3235
+ ...config
3236
+ ]
3237
+ },
3238
+ { ...styleTabConfig },
3239
+ { ...eventTabConfig },
3240
+ { ...advancedTabConfig },
3241
+ { ...displayTabConfig }
3242
+ ]
3081
3243
  }
3082
- async toggleFixedPosition(dist, src, root) {
3083
- const newConfig = cloneDeep(dist);
3084
- if (!isPop(src) && newConfig.style?.position) {
3085
- if (isFixed(newConfig) && !isFixed(src)) {
3086
- newConfig.style = change2Fixed(newConfig, root);
3087
- } else if (!isFixed(newConfig) && isFixed(src)) {
3088
- newConfig.style = await Fixed2Other(newConfig, root, this.getLayout);
3089
- }
3090
- }
3091
- return newConfig;
3244
+ ];
3245
+
3246
+ const log = (...args) => {
3247
+ if (process.env.NODE_ENV === "development") {
3248
+ console.log("magic editor: ", ...args);
3092
3249
  }
3093
- selectedConfigExceptionHandler(config) {
3094
- let id;
3095
- if (typeof config === "string" || typeof config === "number") {
3096
- id = config;
3097
- } else {
3098
- id = config.id;
3099
- }
3100
- if (!id) {
3101
- throw new Error("没有ID,无法选中");
3102
- }
3103
- const { node, parent, page } = this.getNodeInfo(id);
3104
- if (!node)
3105
- throw new Error("获取不到组件信息");
3106
- if (node.id === this.state.root?.id) {
3107
- throw new Error("不能选根节点");
3108
- }
3109
- return {
3110
- node,
3111
- parent,
3112
- page
3113
- };
3250
+ };
3251
+ const info = (...args) => {
3252
+ if (process.env.NODE_ENV === "development") {
3253
+ console.info("magic editor: ", ...args);
3114
3254
  }
3115
- }
3116
- const editorService = new Editor();
3117
-
3118
- const beforePaste = (position, config) => {
3119
- if (!config[0]?.style)
3120
- return config;
3121
- const curNode = editorService.get("node");
3122
- const { left: referenceLeft, top: referenceTop } = config[0].style;
3123
- const pasteConfigs = config.map((configItem) => {
3124
- const { offsetX = 0, offsetY = 0, ...positionClone } = position;
3125
- let pastePosition = positionClone;
3126
- if (!isEmpty(pastePosition) && curNode?.items) {
3127
- pastePosition = getPositionInContainer(pastePosition, curNode.id);
3128
- }
3129
- if (pastePosition.left && configItem.style?.left) {
3130
- pastePosition.left = configItem.style.left - referenceLeft + pastePosition.left;
3131
- }
3132
- if (pastePosition.top && configItem.style?.top) {
3133
- pastePosition.top = configItem.style?.top - referenceTop + pastePosition.top;
3134
- }
3135
- const pasteConfig = propsService.setNewItemId(configItem, false);
3136
- if (pasteConfig.style) {
3137
- const { left, top } = pasteConfig.style;
3138
- if (typeof left === "number" || !!left && !isNaN(Number(left))) {
3139
- pasteConfig.style.left = Number(left) + offsetX;
3140
- }
3141
- if (typeof top === "number" || !!top && !isNaN(Number(top))) {
3142
- pasteConfig.style.top = Number(top) + offsetY;
3143
- }
3144
- pasteConfig.style = {
3145
- ...pasteConfig.style,
3146
- ...pastePosition
3147
- };
3148
- }
3149
- const root = editorService.get("root");
3150
- if ((isPage(pasteConfig) || isPageFragment(pasteConfig)) && root) {
3151
- pasteConfig.name = generatePageNameByApp(root, isPage(pasteConfig) ? NodeType.PAGE : NodeType.PAGE_FRAGMENT);
3152
- }
3153
- return pasteConfig;
3154
- });
3155
- return pasteConfigs;
3156
3255
  };
3157
- const getPositionInContainer = (position = {}, id) => {
3158
- let { left = 0, top = 0 } = position;
3159
- const parentEl = editorService.get("stage")?.renderer?.contentWindow?.document.getElementById(`${id}`);
3160
- const parentElRect = parentEl?.getBoundingClientRect();
3161
- left = left - (parentElRect?.left || 0);
3162
- top = top - (parentElRect?.top || 0);
3163
- return {
3164
- left,
3165
- top
3166
- };
3256
+ const warn = (...args) => {
3257
+ if (process.env.NODE_ENV === "development") {
3258
+ console.warn("magic editor: ", ...args);
3259
+ }
3167
3260
  };
3168
- const getAddParent = (node) => {
3169
- const curNode = editorService.get("node");
3170
- let parentNode;
3171
- if (isPage(node)) {
3172
- parentNode = editorService.get("root");
3173
- } else if (curNode?.items) {
3174
- parentNode = curNode;
3175
- } else if (curNode?.id) {
3176
- parentNode = editorService.getParentById(curNode.id, false);
3261
+ const debug = (...args) => {
3262
+ if (process.env.NODE_ENV === "development") {
3263
+ console.debug("magic editor: ", ...args);
3177
3264
  }
3178
- return parentNode;
3179
3265
  };
3180
- const getDefaultConfig = async (addNode, parentNode) => {
3181
- const { type, inputEvent, ...config } = addNode;
3182
- const layout = await editorService.getLayout(toRaw(parentNode), addNode);
3183
- const newNode = { ...toRaw(await propsService.getPropsValue(type, config)) };
3184
- newNode.style = getInitPositionStyle(newNode.style, layout);
3185
- return newNode;
3266
+ const error = (...args) => {
3267
+ if (process.env.NODE_ENV === "development") {
3268
+ console.error("magic editor: ", ...args);
3269
+ }
3186
3270
  };
3187
3271
 
3188
3272
  const _sfc_main$P = /* @__PURE__ */ defineComponent({
@@ -3572,7 +3656,6 @@ const useCodeBlockEdit = (codeBlockService) => {
3572
3656
  if (!codeId.value)
3573
3657
  return;
3574
3658
  await codeBlockService?.setCodeDslById(codeId.value, values);
3575
- tMagicMessage.success("代码块保存成功");
3576
3659
  codeBlockEditor.value?.hide();
3577
3660
  };
3578
3661
  return {
@@ -3774,7 +3857,7 @@ const useStage = (stageOptions) => {
3774
3857
  const stage = new StageCore({
3775
3858
  render: stageOptions.render,
3776
3859
  runtimeUrl: stageOptions.runtimeUrl,
3777
- zoom: zoom.value,
3860
+ zoom: stageOptions.zoom ?? zoom.value,
3778
3861
  autoScrollIntoView: stageOptions.autoScrollIntoView,
3779
3862
  isContainer: stageOptions.isContainer,
3780
3863
  containerHighlightClassName: stageOptions.containerHighlightClassName,
@@ -4355,33 +4438,6 @@ const _sfc_main$L = /* @__PURE__ */ defineComponent({
4355
4438
  return value[0].replace(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX, "");
4356
4439
  });
4357
4440
  const dataSources = computed(() => services?.dataSourceService.get("dataSources"));
4358
- const getOptionChildren = (fields = [], dataSourceFieldType = ["any"]) => {
4359
- const child = [];
4360
- fields.forEach((field) => {
4361
- if (!dataSourceFieldType.length) {
4362
- dataSourceFieldType.push("any");
4363
- }
4364
- const children = getOptionChildren(field.fields, dataSourceFieldType);
4365
- const item = {
4366
- label: field.title || field.name,
4367
- value: field.name,
4368
- children
4369
- };
4370
- const fieldType = field.type || "any";
4371
- if (dataSourceFieldType.includes("any") || dataSourceFieldType.includes(fieldType)) {
4372
- child.push(item);
4373
- return;
4374
- }
4375
- if (!dataSourceFieldType.includes(fieldType) && !["array", "object", "any"].includes(fieldType)) {
4376
- return;
4377
- }
4378
- if (!children.length && ["object", "array", "any"].includes(field.type || "")) {
4379
- return;
4380
- }
4381
- child.push(item);
4382
- });
4383
- return child;
4384
- };
4385
4441
  const cascaderConfig = computed(() => {
4386
4442
  const valueIsKey = props.config.value === "key";
4387
4443
  return {
@@ -4392,7 +4448,7 @@ const _sfc_main$L = /* @__PURE__ */ defineComponent({
4392
4448
  const options = dataSources.value?.map((ds) => ({
4393
4449
  label: ds.title || ds.id,
4394
4450
  value: valueIsKey ? ds.id : `${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}${ds.id}`,
4395
- children: getOptionChildren(ds.fields, props.config.dataSourceFieldType)
4451
+ children: getCascaderOptionsFromFields(ds.fields, props.config.dataSourceFieldType)
4396
4452
  })) || [];
4397
4453
  return options.filter((option) => option.children.length);
4398
4454
  }
@@ -4444,7 +4500,6 @@ const _sfc_main$L = /* @__PURE__ */ defineComponent({
4444
4500
  return (_ctx, _cache) => {
4445
4501
  return openBlock(), createElementBlock("div", _hoisted_1$q, [
4446
4502
  (openBlock(), createBlock(resolveDynamicComponent(tagName.value), {
4447
- style: { "width": "100%" },
4448
4503
  config: showDataSourceFieldSelect.value || !_ctx.config.fieldConfig ? cascaderConfig.value : _ctx.config.fieldConfig,
4449
4504
  model: _ctx.model,
4450
4505
  name: _ctx.name,
@@ -5631,24 +5686,41 @@ const _sfc_main$E = /* @__PURE__ */ defineComponent({
5631
5686
  const eventsService = services?.eventsService;
5632
5687
  const codeBlockService = services?.codeBlockService;
5633
5688
  const eventNameConfig = computed(() => {
5689
+ const fieldType = props.config.src === "component" ? "select" : "cascader";
5634
5690
  const defaultEventNameConfig = {
5635
5691
  name: "name",
5636
5692
  text: "事件",
5637
- type: "select",
5693
+ type: fieldType,
5638
5694
  labelWidth: "40px",
5695
+ checkStrictly: true,
5696
+ valueSeparator: ".",
5639
5697
  options: (mForm, { formValue }) => {
5640
5698
  let events = [];
5641
5699
  if (!eventsService || !dataSourceService)
5642
5700
  return events;
5643
5701
  if (props.config.src === "component") {
5644
5702
  events = eventsService.getEvent(formValue.type);
5645
- } else if (props.config.src === "datasource") {
5703
+ return events.map((option) => ({
5704
+ text: option.label,
5705
+ value: option.value
5706
+ }));
5707
+ }
5708
+ if (props.config.src === "datasource") {
5646
5709
  events = dataSourceService.getFormEvent(formValue.type);
5710
+ const dataSource = dataSourceService.getDataSourceById(formValue.id);
5711
+ const fields = dataSource?.fields || [];
5712
+ if (fields.length > 0) {
5713
+ return [
5714
+ ...events,
5715
+ {
5716
+ label: "数据变化",
5717
+ value: DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX,
5718
+ children: getCascaderOptionsFromFields(fields)
5719
+ }
5720
+ ];
5721
+ }
5722
+ return events;
5647
5723
  }
5648
- return events.map((option) => ({
5649
- text: option.label,
5650
- value: option.value
5651
- }));
5652
5724
  }
5653
5725
  };
5654
5726
  return { ...defaultEventNameConfig, ...props.config.eventNameConfig };
@@ -8614,8 +8686,8 @@ const usePasteMenu = (menu) => ({
8614
8686
  const stage = services?.editorService?.get("stage");
8615
8687
  const rect = menu.value.$el.getBoundingClientRect();
8616
8688
  const parentRect = stage?.container?.getBoundingClientRect();
8617
- const initialLeft = (rect.left || 0) - (parentRect?.left || 0);
8618
- const initialTop = (rect.top || 0) - (parentRect?.top || 0);
8689
+ const initialLeft = calcValueByFontsize(stage?.renderer.getDocument(), (rect.left || 0) - (parentRect?.left || 0)) / services.uiService.get("zoom");
8690
+ const initialTop = calcValueByFontsize(stage?.renderer.getDocument(), (rect.top || 0) - (parentRect?.top || 0)) / services.uiService.get("zoom");
8619
8691
  services?.editorService?.paste({ left: initialLeft, top: initialTop });
8620
8692
  } else {
8621
8693
  services?.editorService?.paste();
@@ -9612,7 +9684,10 @@ const _sfc_main$a = /* @__PURE__ */ defineComponent({
9612
9684
  class: "m-editor-sidebar-content",
9613
9685
  key: config.$key ?? index
9614
9686
  }, [
9615
- config && !unref(floatBoxStates)[config.$key]?.status ? (openBlock(), createBlock(resolveDynamicComponent(config.component), mergeProps({ key: 0 }, config.props || {}, toHandlers(config?.listeners || {})), createSlots({ _: 2 }, [
9687
+ config && !unref(floatBoxStates)[config.$key]?.status ? (openBlock(), createBlock(resolveDynamicComponent(config.component), mergeProps({
9688
+ key: 0,
9689
+ ref_for: true
9690
+ }, config.props || {}, toHandlers(config?.listeners || {})), createSlots({ _: 2 }, [
9616
9691
  config.$key === "component-list" || config.slots?.componentListPanelHeader ? {
9617
9692
  name: "component-list-panel-header",
9618
9693
  fn: withCtx(() => [
@@ -9745,7 +9820,10 @@ const _sfc_main$a = /* @__PURE__ */ defineComponent({
9745
9820
  }, {
9746
9821
  body: withCtx(() => [
9747
9822
  createElementVNode("div", _hoisted_5$1, [
9748
- config && unref(floatBoxStates)[config.$key].status ? (openBlock(), createBlock(resolveDynamicComponent(config.component), mergeProps({ key: 0 }, config.props || {}, toHandlers(config?.listeners || {})), null, 16)) : createCommentVNode("", true)
9823
+ config && unref(floatBoxStates)[config.$key].status ? (openBlock(), createBlock(resolveDynamicComponent(config.component), mergeProps({
9824
+ key: 0,
9825
+ ref_for: true
9826
+ }, config.props || {}, toHandlers(config?.listeners || {})), null, 16)) : createCommentVNode("", true)
9749
9827
  ])
9750
9828
  ]),
9751
9829
  _: 2
@@ -10566,17 +10644,17 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
10566
10644
  position = "absolute";
10567
10645
  top = e.clientY - containerRect.top + scrollTop;
10568
10646
  left = e.clientX - containerRect.left + scrollLeft;
10569
- if (parentEl && doc) {
10647
+ if (parentEl) {
10570
10648
  const { left: parentLeft, top: parentTop } = getOffset(parentEl);
10571
- left = left - calcValueByFontsize(doc, parentLeft) * zoom.value;
10572
- top = top - calcValueByFontsize(doc, parentTop) * zoom.value;
10649
+ left = left - parentLeft * zoom.value;
10650
+ top = top - parentTop * zoom.value;
10573
10651
  }
10574
10652
  }
10575
10653
  config.data.style = {
10576
10654
  ...style,
10577
10655
  position,
10578
- top: top / zoom.value,
10579
- left: left / zoom.value
10656
+ top: calcValueByFontsize(doc, top / zoom.value),
10657
+ left: calcValueByFontsize(doc, left / zoom.value)
10580
10658
  };
10581
10659
  config.data.inputEvent = e;
10582
10660
  services?.editorService.add(config.data, parent);
@@ -10707,7 +10785,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
10707
10785
 
10708
10786
  const canUsePluginMethods$1 = {
10709
10787
  async: ["setCodeDslById", "setEditStatus", "setCombineIds", "setUndeleteableList", "deleteCodeDslByIds"],
10710
- sync: []
10788
+ sync: ["setCodeDslByIdSync"]
10711
10789
  };
10712
10790
  class CodeBlock extends BaseService {
10713
10791
  state = reactive({
@@ -10718,7 +10796,10 @@ class CodeBlock extends BaseService {
10718
10796
  paramsColConfig: void 0
10719
10797
  });
10720
10798
  constructor() {
10721
- super(canUsePluginMethods$1.async.map((methodName) => ({ name: methodName, isAsync: true })));
10799
+ super([
10800
+ ...canUsePluginMethods$1.async.map((methodName) => ({ name: methodName, isAsync: true })),
10801
+ ...canUsePluginMethods$1.sync.map((methodName) => ({ name: methodName, isAsync: false }))
10802
+ ]);
10722
10803
  }
10723
10804
  /**
10724
10805
  * 设置活动的代码块dsl数据源
@@ -10758,17 +10839,29 @@ class CodeBlock extends BaseService {
10758
10839
  * @returns {void}
10759
10840
  */
10760
10841
  async setCodeDslById(id, codeConfig) {
10842
+ this.setCodeDslByIdSync(id, codeConfig, true);
10843
+ }
10844
+ /**
10845
+ * 为了兼容历史原因
10846
+ * 设置代码块ID和代码内容到源dsl
10847
+ * @param {Id} id 代码块id
10848
+ * @param {CodeBlockContent} codeConfig 代码块内容配置信息
10849
+ * @param {boolean} force 是否强制写入,默认true
10850
+ * @returns {void}
10851
+ */
10852
+ setCodeDslByIdSync(id, codeConfig, force = true) {
10761
10853
  const codeDsl = this.getCodeDsl();
10762
10854
  if (!codeDsl) {
10763
10855
  throw new Error("dsl中没有codeBlocks");
10764
10856
  }
10765
- const codeConfigProcessed = codeConfig;
10766
- if (codeConfig.content) {
10857
+ if (codeDsl[id] && !force)
10858
+ return;
10859
+ const codeConfigProcessed = cloneDeep(codeConfig);
10860
+ if (codeConfigProcessed.content) {
10767
10861
  const parseDSL = getConfig("parseDSL");
10768
- if (typeof codeConfig.content === "string") {
10769
- codeConfig.content = parseDSL(codeConfig.content);
10862
+ if (typeof codeConfigProcessed.content === "string") {
10863
+ codeConfigProcessed.content = parseDSL(codeConfigProcessed.content);
10770
10864
  }
10771
- codeConfigProcessed.content = codeConfig.content;
10772
10865
  }
10773
10866
  const existContent = codeDsl[id] || {};
10774
10867
  codeDsl[id] = {
@@ -10880,6 +10973,51 @@ class CodeBlock extends BaseService {
10880
10973
  return newId;
10881
10974
  return await this.getUniqueId();
10882
10975
  }
10976
+ /**
10977
+ * 复制时会带上组件关联的代码块
10978
+ * @param config 组件节点配置
10979
+ * @returns
10980
+ */
10981
+ copyWithRelated(config) {
10982
+ const copyNodes = Array.isArray(config) ? config : [config];
10983
+ depService.clearByType(DepTargetType.RELATED_CODE_WHEN_COPY);
10984
+ depService.collect(copyNodes, true, DepTargetType.RELATED_CODE_WHEN_COPY);
10985
+ const customTarget = depService.getTarget(
10986
+ DepTargetType.RELATED_CODE_WHEN_COPY,
10987
+ DepTargetType.RELATED_CODE_WHEN_COPY
10988
+ );
10989
+ const copyData = {};
10990
+ if (customTarget) {
10991
+ Object.keys(customTarget.deps).forEach((nodeId) => {
10992
+ const node = editorService.getNodeById(nodeId);
10993
+ if (!node)
10994
+ return;
10995
+ customTarget.deps[nodeId].keys.forEach((key) => {
10996
+ const relateCodeId = get(node, key);
10997
+ const isExist = Object.keys(copyData).find((codeId) => codeId === relateCodeId);
10998
+ if (!isExist) {
10999
+ const relateCode = this.getCodeContentById(relateCodeId);
11000
+ if (relateCode) {
11001
+ copyData[relateCodeId] = relateCode;
11002
+ }
11003
+ }
11004
+ });
11005
+ });
11006
+ }
11007
+ storageService.setItem(COPY_CODE_STORAGE_KEY, copyData, {
11008
+ protocol: Protocol.OBJECT
11009
+ });
11010
+ }
11011
+ /**
11012
+ * 粘贴代码块
11013
+ * @returns
11014
+ */
11015
+ paste() {
11016
+ const codeDsl = storageService.getItem(COPY_CODE_STORAGE_KEY);
11017
+ Object.keys(codeDsl).forEach((codeId) => {
11018
+ this.setCodeDslByIdSync(codeId, codeDsl[codeId], false);
11019
+ });
11020
+ }
10883
11021
  resetState() {
10884
11022
  this.state.codeDsl = null;
10885
11023
  this.state.editable = true;
@@ -11221,6 +11359,7 @@ class StageOverlay extends BaseService {
11221
11359
  createStage(stageOptions = {}) {
11222
11360
  return useStage({
11223
11361
  ...stageOptions,
11362
+ zoom: 1,
11224
11363
  runtimeUrl: "",
11225
11364
  autoScrollIntoView: false,
11226
11365
  render: async (stage) => {
@@ -11650,7 +11789,17 @@ const initServiceEvents = (props, emit, { editorService, codeBlockService, dataS
11650
11789
  dataSourceService.on("update", dataSourceUpdateHandler);
11651
11790
  dataSourceService.on("remove", dataSourceRemoveHandler);
11652
11791
  if (props.collectorOptions && !depService.hasTarget(DepTargetType.RELATED_COMP_WHEN_COPY)) {
11653
- depService.addTarget(createRelatedCompTarget(props.collectorOptions));
11792
+ depService.addTarget(createRelatedTargetForCopy(props.collectorOptions, DepTargetType.RELATED_COMP_WHEN_COPY));
11793
+ }
11794
+ if (props.collectorOptionsForCode && !depService.hasTarget(DepTargetType.RELATED_CODE_WHEN_COPY)) {
11795
+ depService.addTarget(
11796
+ createRelatedTargetForCopy(props.collectorOptionsForCode, DepTargetType.RELATED_CODE_WHEN_COPY)
11797
+ );
11798
+ }
11799
+ if (props.collectorOptionsForDataSource && !depService.hasTarget(DepTargetType.RELATED_DS_WHEN_COPY)) {
11800
+ depService.addTarget(
11801
+ createRelatedTargetForCopy(props.collectorOptionsForDataSource, DepTargetType.RELATED_DS_WHEN_COPY)
11802
+ );
11654
11803
  }
11655
11804
  onBeforeUnmount(() => {
11656
11805
  depService.off("add-target", targetAddHandler);
@@ -11701,6 +11850,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
11701
11850
  codeOptions: {},
11702
11851
  disabledDragStart: { type: Boolean },
11703
11852
  collectorOptions: {},
11853
+ collectorOptionsForCode: {},
11854
+ collectorOptionsForDataSource: {},
11704
11855
  guidesOptions: {},
11705
11856
  disabledMultiSelect: { type: Boolean },
11706
11857
  disabledPageFragment: { type: Boolean },
@@ -11906,4 +12057,4 @@ const index = {
11906
12057
  }
11907
12058
  };
11908
12059
 
11909
- export { CODE_DRAFT_STORAGE_KEY, COPY_STORAGE_KEY, _sfc_main$J as CodeBlockEditor, _sfc_main$l as CodeBlockList, _sfc_main$k as CodeBlockListPanel, CodeDeleteErrorType, _sfc_main$Q as CodeSelect, _sfc_main$O as CodeSelectCol, ColumnLayout, _sfc_main$b as ComponentListPanel, _sfc_main$g as ContentMenu, _sfc_main$j as DataSourceConfigPanel, _sfc_main$L as DataSourceFieldSelect, _sfc_main$M as DataSourceFields, _sfc_main$K as DataSourceInput, _sfc_main$H as DataSourceMethodSelect, _sfc_main$I as DataSourceMethods, _sfc_main$G as DataSourceMocks, _sfc_main$F as DataSourceSelect, DragType, _sfc_main$E as EventSelect, Fixed2Other, H_GUIDE_LINE_STORAGE_KEY, _sfc_main$U as Icon, KeyBindingCommand, _sfc_main$C as KeyValue, Keys, LayerOffset, _sfc_main$c as LayerPanel, Layout, _sfc_main$y as LayoutContainer, _sfc_main$B as PageFragmentSelect, _sfc_main$p as PropsPanel, _sfc_main$z as Resizer, SideItemKey, _sfc_main$y as SplitView, _sfc_main$T as TMagicCodeEditor, _sfc_main as TMagicEditor, _sfc_main$x as ToolButton, UI_SELECT_MODE_EVENT_NAME, V_GUIDE_LINE_STORAGE_KEY, advancedTabConfig, beforePaste, change2Fixed, codeBlockService, dataSourceService, debug, index as default, depService, displayTabConfig, editorService, error, eventTabConfig, eventsService, fillConfig, fixNodeLeft, fixNodePosition, generatePageName, generatePageNameByApp, getAddParent, getConfig, getDefaultConfig, getDisplayField, getFormConfig, getFormValue, getGuideLineFromCache, getInitPositionStyle, getNodeIndex, getPageFragmentList, getPageList, getPageNameList, getPositionInContainer, getRelativeStyle, historyService, info, isFixed, log, propsService, serializeConfig, setChildrenLayout, setConfig, setLayout, stageOverlayService, storageService, styleTabConfig, traverseNode, uiService, useCodeBlockEdit, useDataSourceMethod, useEditorContentHeight, useFloatBox, useStage, useWindowRect, warn };
12060
+ export { CODE_DRAFT_STORAGE_KEY, COPY_CODE_STORAGE_KEY, COPY_DS_STORAGE_KEY, COPY_STORAGE_KEY, _sfc_main$J as CodeBlockEditor, _sfc_main$l as CodeBlockList, _sfc_main$k as CodeBlockListPanel, CodeDeleteErrorType, _sfc_main$Q as CodeSelect, _sfc_main$O as CodeSelectCol, ColumnLayout, _sfc_main$b as ComponentListPanel, _sfc_main$g as ContentMenu, _sfc_main$j as DataSourceConfigPanel, _sfc_main$L as DataSourceFieldSelect, _sfc_main$M as DataSourceFields, _sfc_main$K as DataSourceInput, _sfc_main$H as DataSourceMethodSelect, _sfc_main$I as DataSourceMethods, _sfc_main$G as DataSourceMocks, _sfc_main$F as DataSourceSelect, DragType, _sfc_main$E as EventSelect, Fixed2Other, _sfc_main$N as FloatingBox, H_GUIDE_LINE_STORAGE_KEY, _sfc_main$U as Icon, KeyBindingCommand, _sfc_main$C as KeyValue, Keys, LayerOffset, _sfc_main$c as LayerPanel, Layout, _sfc_main$y as LayoutContainer, _sfc_main$B as PageFragmentSelect, _sfc_main$p as PropsPanel, _sfc_main$z as Resizer, SideItemKey, _sfc_main$y as SplitView, _sfc_main$T as TMagicCodeEditor, _sfc_main as TMagicEditor, _sfc_main$x as ToolButton, UI_SELECT_MODE_EVENT_NAME, V_GUIDE_LINE_STORAGE_KEY, advancedTabConfig, beforePaste, change2Fixed, codeBlockService, dataSourceService, debug, index as default, depService, displayTabConfig, editorService, error, eventTabConfig, eventsService, fillConfig, fixNodeLeft, fixNodePosition, generatePageName, generatePageNameByApp, getAddParent, getCascaderOptionsFromFields, getConfig, getDefaultConfig, getDisplayField, getFormConfig, getFormValue, getGuideLineFromCache, getInitPositionStyle, getNodeIndex, getPageFragmentList, getPageList, getPageNameList, getPositionInContainer, getRelativeStyle, historyService, info, isFixed, log, propsService, serializeConfig, setChildrenLayout, setConfig, setLayout, stageOverlayService, storageService, styleTabConfig, traverseNode, uiService, useCodeBlockEdit, useDataSourceMethod, useEditorContentHeight, useFloatBox, useStage, useWindowRect, warn };