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