@tmagic/editor 1.4.5 → 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.
@@ -510,388 +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
- items: [
630
- {
631
- name: "events",
632
- src: "datasource",
633
- type: "event-select"
634
- }
635
- ]
636
- },
637
- {
638
- title: "mock数据",
639
- items: [
640
- {
641
- name: "mocks",
642
- type: "data-source-mocks",
643
- defaultValue: () => []
644
- }
645
- ]
646
- },
647
- {
648
- title: "请求参数裁剪",
649
- display: (formState, { model }) => model.type === "http",
650
- items: [
651
- {
652
- name: "beforeRequest",
653
- type: "vs-code",
654
- parse: true,
655
- height: "600px"
656
- }
657
- ]
658
- },
659
- {
660
- title: "响应数据裁剪",
661
- display: (formState, { model }) => model.type === "http",
662
- items: [
663
- {
664
- name: "afterResponse",
665
- type: "vs-code",
666
- parse: true,
667
- height: "600px"
668
- }
669
- ]
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];
670
568
  }
671
- ]
672
- }
673
- ];
674
- const getFormConfig = (type, configs) => {
675
- switch (type) {
676
- case "base":
677
- return fillConfig$1([]);
678
- case "http":
679
- return fillConfig$1(HttpFormConfig);
680
- default:
681
- 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;
682
579
  }
683
580
  };
684
- const getFormValue = (type, values) => {
685
- if (type !== "http") {
686
- return values;
687
- }
688
- return {
689
- beforeRequest: `(options, context) => {
690
- /**
691
- * 用户可以直接编写函数,在原始接口调用之前,会运行该函数,将这个函数的返回值作为该数据源接口的入参
692
- *
693
- * options: HttpOptions
694
- *
695
- * interface HttpOptions {
696
- * // 请求链接
697
- * url: string;
698
- * // query参数
699
- * params?: Record<string, string>;
700
- * // body数据
701
- * data?: Record<string, any>;
702
- * // 请求头
703
- * headers?: Record<string, string>;
704
- * // 请求方法 GET/POST
705
- * method?: Method;
706
- * }
707
- *
708
- * context:上下文对象
709
- *
710
- * interface Content {
711
- * app: AppCore;
712
- * dataSource: HttpDataSource;
713
- * }
714
- *
715
- * return: HttpOptions
716
- */
717
-
718
- // 此处的返回值会作为这个接口的入参
719
- return options;
720
- }`,
721
- afterResponse: `(response, context) => {
722
- /**
723
- * 用户可以直接编写函数,在原始接口返回之后,会运行该函数,将这个函数的返回值作为该数据源接口的返回
724
-
725
- * context:上下文对象
726
- *
727
- * interface Content {
728
- * app: AppCore;
729
- * dataSource: HttpDataSource;
730
- * }
731
- *
732
- */
733
-
734
- // 此处的返回值会作为这个接口的返回值
735
- return response;
736
- }`,
737
- ...values
738
- };
739
- };
740
- const getDisplayField = (dataSources, key) => {
741
- const displayState = [];
742
- const matches = key.matchAll(/\$\{([\s\S]+?)\}/g);
743
- let index = 0;
744
- for (const match of matches) {
745
- if (typeof match.index === "undefined")
746
- break;
747
- displayState.push({
748
- type: "text",
749
- value: key.substring(index, match.index)
750
- });
751
- let dsText = "";
752
- let ds;
753
- let fields;
754
- match[1].split(".").forEach((item, index2) => {
755
- if (index2 === 0) {
756
- ds = dataSources.find((ds2) => ds2.id === item);
757
- dsText += ds?.title || item;
758
- fields = ds?.fields;
759
- return;
760
- }
761
- const field = fields?.find((field2) => field2.name === item);
762
- fields = field?.fields;
763
- dsText += `.${field?.title || item}`;
764
- });
765
- displayState.push({
766
- type: "var",
767
- value: dsText
768
- });
769
- index = match.index + match[0].length;
770
- }
771
- if (index < key.length) {
772
- displayState.push({
773
- type: "text",
774
- value: key.substring(index)
775
- });
776
- }
777
- return displayState;
778
- };
779
- const getCascaderOptionsFromFields = (fields = [], dataSourceFieldType = ["any"]) => {
780
- const child = [];
781
- fields.forEach((field) => {
782
- if (!dataSourceFieldType.length) {
783
- dataSourceFieldType.push("any");
784
- }
785
- const children = getCascaderOptionsFromFields(field.fields, dataSourceFieldType);
786
- const item = {
787
- label: field.title || field.name,
788
- value: field.name,
789
- children
790
- };
791
- const fieldType = field.type || "any";
792
- if (dataSourceFieldType.includes("any") || dataSourceFieldType.includes(fieldType)) {
793
- child.push(item);
794
- return;
795
- }
796
- if (!dataSourceFieldType.includes(fieldType) && !["array", "object", "any"].includes(fieldType)) {
797
- return;
798
- }
799
- if (!children.length && ["object", "array", "any"].includes(field.type || "")) {
800
- return;
801
- }
802
- child.push(item);
803
- });
804
- return child;
805
- };
806
-
807
- const compose = (middleware, isAsync) => {
808
- if (!Array.isArray(middleware))
809
- throw new TypeError("Middleware 必须是一个数组!");
810
- for (const fn of middleware) {
811
- if (typeof fn !== "function")
812
- throw new TypeError("Middleware 必须由函数组成!");
813
- }
814
- return (args, next) => {
815
- let index = -1;
816
- return dispatch(0);
817
- function dispatch(i) {
818
- if (i <= index) {
819
- const error = new Error("next() 被多次调用");
820
- if (isAsync) {
821
- return Promise.reject(error);
822
- }
823
- throw error;
824
- }
825
- index = i;
826
- let fn = middleware[i];
827
- if (i === middleware.length && next)
828
- fn = next;
829
- if (!fn) {
830
- if (isAsync) {
831
- return Promise.resolve();
832
- }
833
- return;
834
- }
835
- if (isAsync) {
836
- try {
837
- return Promise.resolve(fn(...args, dispatch.bind(null, i + 1)));
838
- } catch (err) {
839
- return Promise.reject(err);
840
- }
841
- }
842
- try {
843
- return fn(...args, dispatch.bind(null, i + 1));
844
- } catch (err) {
845
- throw err;
846
- }
847
- }
848
- };
849
- };
850
-
851
- const methodName = (prefix, name) => `${prefix}${name[0].toUpperCase()}${name.substring(1)}`;
852
- const isError = (error) => Object.prototype.toString.call(error) === "[object Error]";
853
- const doAction = (args, scope, sourceMethod, beforeMethodName, afterMethodName, fn) => {
854
- try {
855
- let beforeArgs = args;
856
- for (const beforeMethod of scope.pluginOptionsList[beforeMethodName]) {
857
- beforeArgs = beforeMethod(...beforeArgs) || [];
858
- if (isError(beforeArgs))
859
- throw beforeArgs;
860
- if (!Array.isArray(beforeArgs)) {
861
- beforeArgs = [beforeArgs];
862
- }
863
- }
864
- let returnValue = fn(beforeArgs, sourceMethod.bind(scope));
865
- for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
866
- returnValue = afterMethod(returnValue, ...beforeArgs);
867
- if (isError(returnValue))
868
- throw returnValue;
869
- }
870
- return returnValue;
871
- } catch (error) {
872
- throw error;
873
- }
874
- };
875
- const doAsyncAction = async (args, scope, sourceMethod, beforeMethodName, afterMethodName, fn) => {
876
- try {
877
- let beforeArgs = args;
878
- for (const beforeMethod of scope.pluginOptionsList[beforeMethodName]) {
879
- beforeArgs = await beforeMethod(...beforeArgs) || [];
880
- if (isError(beforeArgs))
881
- throw beforeArgs;
882
- if (!Array.isArray(beforeArgs)) {
883
- beforeArgs = [beforeArgs];
884
- }
885
- }
886
- let returnValue = await fn(beforeArgs, sourceMethod.bind(scope));
887
- for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
888
- returnValue = await afterMethod(returnValue, ...beforeArgs);
889
- if (isError(returnValue))
890
- throw returnValue;
891
- }
892
- return returnValue;
893
- } catch (error) {
894
- 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;
895
601
  }
896
602
  };
897
603
  class BaseService extends events.EventEmitter {
@@ -967,590 +673,500 @@
967
673
  }
968
674
  }
969
675
 
970
- const canUsePluginMethods$6 = {
971
- async: [],
972
- sync: [
973
- "getFormConfig",
974
- "setFormConfig",
975
- "getFormValue",
976
- "setFormValue",
977
- "getFormEvent",
978
- "setFormEvent",
979
- "getFormMethod",
980
- "setFormMethod",
981
- "add",
982
- "update",
983
- "remove",
984
- "createId"
985
- ]
986
- };
987
- class DataSource extends BaseService {
988
- state = vue.reactive({
989
- datasourceTypeList: [],
990
- dataSources: [],
991
- editable: true,
992
- configs: {},
993
- values: {},
994
- events: {},
995
- methods: {}
996
- });
997
- constructor() {
998
- 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");
999
681
  }
1000
- set(name, value) {
1001
- this.state[name] = value;
682
+ getTargets(type = dep.DepTargetType.DEFAULT) {
683
+ return this.watcher.getTargets(type);
1002
684
  }
1003
- get(name) {
1004
- return this.state[name];
685
+ getTarget(id, type = dep.DepTargetType.DEFAULT) {
686
+ return this.watcher.getTarget(id, type);
1005
687
  }
1006
- getFormConfig(type = "base") {
1007
- return getFormConfig(utils.toLine(type), this.get("configs"));
688
+ addTarget(target) {
689
+ this.watcher.addTarget(target);
690
+ this.emit("add-target", target);
1008
691
  }
1009
- setFormConfig(type, config) {
1010
- 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");
1011
695
  }
1012
- getFormValue(type = "base") {
1013
- return getFormValue(utils.toLine(type), this.get("values")[type]);
696
+ clearTargets() {
697
+ this.watcher.clearTargets();
1014
698
  }
1015
- setFormValue(type, value) {
1016
- 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);
1017
702
  }
1018
- getFormEvent(type = "base") {
1019
- return this.get("events")[utils.toLine(type)] || [];
703
+ clear(nodes) {
704
+ return this.watcher.clear(nodes);
1020
705
  }
1021
- setFormEvent(type, value = []) {
1022
- this.get("events")[utils.toLine(type)] = value;
706
+ clearByType(type, nodes) {
707
+ return this.watcher.clearByType(type, nodes);
1023
708
  }
1024
- getFormMethod(type = "base") {
1025
- return this.get("methods")[utils.toLine(type)] || [];
709
+ hasTarget(id, type = dep.DepTargetType.DEFAULT) {
710
+ return this.watcher.hasTarget(id, type);
1026
711
  }
1027
- setFormMethod(type, value = []) {
1028
- this.get("methods")[utils.toLine(type)] = value;
712
+ hasSpecifiedTypeTarget(type = dep.DepTargetType.DEFAULT) {
713
+ return this.watcher.hasSpecifiedTypeTarget(type);
1029
714
  }
1030
- add(config) {
1031
- const newConfig = {
1032
- ...config,
1033
- id: this.createId()
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
+ ]);
740
+ }
741
+ setPropsConfigs(configs) {
742
+ Object.keys(configs).forEach((type) => {
743
+ this.setPropsConfig(utils.toLine(type), configs[type]);
744
+ });
745
+ this.emit("props-configs-change");
746
+ }
747
+ async fillConfig(config, labelWidth) {
748
+ return fillConfig(config, typeof labelWidth !== "function" ? labelWidth : "80px");
749
+ }
750
+ async setPropsConfig(type, config) {
751
+ this.state.propsConfigMap[utils.toLine(type)] = await this.fillConfig(Array.isArray(config) ? config : [config]);
752
+ }
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)
1034
805
  };
1035
- this.get("dataSources").push(newConfig);
1036
- this.emit("add", newConfig);
1037
- return newConfig;
1038
806
  }
1039
- update(config) {
1040
- const dataSources = this.get("dataSources");
1041
- const index = dataSources.findIndex((ds) => ds.id === config.id);
1042
- dataSources[index] = lodashEs.cloneDeep(config);
1043
- this.emit("update", config);
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
+ }
1044
828
  return config;
1045
829
  }
1046
- remove(id) {
1047
- const dataSources = this.get("dataSources");
1048
- const index = dataSources.findIndex((ds) => ds.id === id);
1049
- dataSources.splice(index, 1);
1050
- this.emit("remove", id);
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
+ };
1051
847
  }
1052
- createId() {
1053
- return `ds_${utils.guid()}`;
848
+ resetState() {
849
+ this.state.propsConfigMap = {};
850
+ this.state.propsValueMap = {};
1054
851
  }
1055
- getDataSourceById(id) {
1056
- return this.get("dataSources").find((ds) => ds.id === id);
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
+ });
1057
883
  }
1058
- resetState() {
1059
- this.set("dataSources", []);
884
+ /**
885
+ * 清除setNewItemId前后映射关系
886
+ */
887
+ clearRelateId() {
888
+ this.state.relateIdMap = {};
1060
889
  }
1061
890
  destroy() {
1062
- this.removeAllListeners();
1063
891
  this.resetState();
892
+ this.removeAllListeners();
1064
893
  this.removeAllPlugins();
1065
894
  }
1066
895
  usePlugin(options) {
1067
896
  super.usePlugin(options);
1068
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
+ }
1069
914
  }
1070
- const dataSourceService = new DataSource();
915
+ const propsService = new Props();
1071
916
 
1072
- const arrayOptions = [
1073
- { text: "包含", value: "include" },
1074
- { text: "不包含", value: "not_include" }
1075
- ];
1076
- const eqOptions = [
1077
- { text: "等于", value: "=" },
1078
- { text: "不等于", value: "!=" }
1079
- ];
1080
- const numberOptions = [
1081
- { text: "大于", value: ">" },
1082
- { text: "大于等于", value: ">=" },
1083
- { text: "小于", value: "<" },
1084
- { text: "小于等于", value: "<=" },
1085
- { text: "在范围内", value: "between" },
1086
- { text: "不在范围内", value: "not_between" }
1087
- ];
1088
- const styleTabConfig = {
1089
- title: "样式",
1090
- items: [
1091
- {
1092
- name: "style",
1093
- items: [
1094
- {
1095
- type: "fieldset",
1096
- legend: "位置",
1097
- items: [
1098
- {
1099
- type: "data-source-field-select",
1100
- name: "position",
1101
- text: "固定定位",
1102
- checkStrictly: false,
1103
- dataSourceFieldType: ["string"],
1104
- fieldConfig: {
1105
- type: "checkbox",
1106
- activeValue: "fixed",
1107
- inactiveValue: "absolute",
1108
- defaultValue: "absolute"
1109
- }
1110
- },
1111
- {
1112
- type: "data-source-field-select",
1113
- name: "left",
1114
- text: "left",
1115
- checkStrictly: false,
1116
- dataSourceFieldType: ["string", "number"],
1117
- fieldConfig: {
1118
- type: "text"
1119
- }
1120
- },
1121
- {
1122
- type: "data-source-field-select",
1123
- name: "top",
1124
- text: "top",
1125
- checkStrictly: false,
1126
- dataSourceFieldType: ["string", "number"],
1127
- fieldConfig: {
1128
- type: "text"
1129
- },
1130
- disabled: (vm, { model }) => model.position === "fixed" && model._magic_position === "fixedBottom"
1131
- },
1132
- {
1133
- type: "data-source-field-select",
1134
- name: "right",
1135
- text: "right",
1136
- checkStrictly: false,
1137
- dataSourceFieldType: ["string", "number"],
1138
- fieldConfig: {
1139
- type: "text"
1140
- }
1141
- },
1142
- {
1143
- type: "data-source-field-select",
1144
- name: "bottom",
1145
- text: "bottom",
1146
- checkStrictly: false,
1147
- dataSourceFieldType: ["string", "number"],
1148
- fieldConfig: {
1149
- type: "text"
1150
- },
1151
- disabled: (vm, { model }) => model.position === "fixed" && model._magic_position === "fixedTop"
1152
- }
1153
- ]
1154
- },
1155
- {
1156
- type: "fieldset",
1157
- legend: "盒子",
1158
- items: [
1159
- {
1160
- type: "data-source-field-select",
1161
- name: "width",
1162
- text: "宽度",
1163
- checkStrictly: false,
1164
- dataSourceFieldType: ["string", "number"],
1165
- fieldConfig: {
1166
- type: "text"
1167
- }
1168
- },
1169
- {
1170
- type: "data-source-field-select",
1171
- name: "height",
1172
- text: "高度",
1173
- checkStrictly: false,
1174
- dataSourceFieldType: ["string", "number"],
1175
- fieldConfig: {
1176
- type: "text"
1177
- }
1178
- },
1179
- {
1180
- type: "data-source-field-select",
1181
- text: "overflow",
1182
- name: "overflow",
1183
- checkStrictly: false,
1184
- dataSourceFieldType: ["string"],
1185
- fieldConfig: {
1186
- type: "select",
1187
- options: [
1188
- { text: "visible", value: "visible" },
1189
- { text: "hidden", value: "hidden" },
1190
- { text: "clip", value: "clip" },
1191
- { text: "scroll", value: "scroll" },
1192
- { text: "auto", value: "auto" },
1193
- { text: "overlay", value: "overlay" }
1194
- ]
1195
- }
1196
- }
1197
- ]
1198
- },
1199
- {
1200
- type: "fieldset",
1201
- legend: "边框",
1202
- items: [
1203
- {
1204
- type: "data-source-field-select",
1205
- name: "borderWidth",
1206
- text: "宽度",
1207
- defaultValue: "0",
1208
- checkStrictly: false,
1209
- dataSourceFieldType: ["string", "number"],
1210
- fieldConfig: {
1211
- type: "text"
1212
- }
1213
- },
1214
- {
1215
- type: "data-source-field-select",
1216
- name: "borderColor",
1217
- text: "颜色",
1218
- checkStrictly: false,
1219
- dataSourceFieldType: ["string"],
1220
- fieldConfig: {
1221
- type: "text"
1222
- }
1223
- },
1224
- {
1225
- type: "data-source-field-select",
1226
- name: "borderStyle",
1227
- text: "样式",
1228
- defaultValue: "none",
1229
- checkStrictly: false,
1230
- dataSourceFieldType: ["string"],
1231
- fieldConfig: {
1232
- type: "select",
1233
- options: [
1234
- { text: "none", value: "none" },
1235
- { text: "hidden", value: "hidden" },
1236
- { text: "dotted", value: "dotted" },
1237
- { text: "dashed", value: "dashed" },
1238
- { text: "solid", value: "solid" },
1239
- { text: "double", value: "double" },
1240
- { text: "groove", value: "groove" },
1241
- { text: "ridge", value: "ridge" },
1242
- { text: "inset", value: "inset" },
1243
- { text: "outset", value: "outset" }
1244
- ]
1245
- }
1246
- }
1247
- ]
1248
- },
1249
- {
1250
- type: "fieldset",
1251
- legend: "背景",
1252
- items: [
1253
- {
1254
- type: "data-source-field-select",
1255
- name: "backgroundImage",
1256
- text: "背景图",
1257
- checkStrictly: false,
1258
- dataSourceFieldType: ["string"],
1259
- fieldConfig: {
1260
- type: "text"
1261
- }
1262
- },
1263
- {
1264
- type: "data-source-field-select",
1265
- name: "backgroundColor",
1266
- text: "背景颜色",
1267
- checkStrictly: false,
1268
- dataSourceFieldType: ["string"],
1269
- fieldConfig: {
1270
- type: "colorPicker"
1271
- }
1272
- },
1273
- {
1274
- type: "data-source-field-select",
1275
- name: "backgroundRepeat",
1276
- text: "背景图重复",
1277
- defaultValue: "no-repeat",
1278
- checkStrictly: false,
1279
- dataSourceFieldType: ["string"],
1280
- fieldConfig: {
1281
- type: "select",
1282
- options: [
1283
- { text: "repeat", value: "repeat" },
1284
- { text: "repeat-x", value: "repeat-x" },
1285
- { text: "repeat-y", value: "repeat-y" },
1286
- { text: "no-repeat", value: "no-repeat" },
1287
- { text: "inherit", value: "inherit" }
1288
- ]
1289
- }
1290
- },
1291
- {
1292
- type: "data-source-field-select",
1293
- name: "backgroundSize",
1294
- text: "背景图大小",
1295
- defaultValue: "100% 100%",
1296
- checkStrictly: false,
1297
- dataSourceFieldType: ["string"],
1298
- fieldConfig: {
1299
- type: "text"
1300
- }
1301
- }
1302
- ]
1303
- },
1304
- {
1305
- type: "fieldset",
1306
- legend: "字体",
1307
- items: [
1308
- {
1309
- type: "data-source-field-select",
1310
- name: "color",
1311
- text: "颜色",
1312
- checkStrictly: false,
1313
- dataSourceFieldType: ["string"],
1314
- fieldConfig: {
1315
- type: "colorPicker"
1316
- }
1317
- },
1318
- {
1319
- type: "data-source-field-select",
1320
- name: "fontSize",
1321
- text: "大小",
1322
- checkStrictly: false,
1323
- dataSourceFieldType: ["string", "number"],
1324
- fieldConfig: {
1325
- type: "text"
1326
- }
1327
- },
1328
- {
1329
- type: "data-source-field-select",
1330
- name: "fontWeight",
1331
- text: "粗细",
1332
- checkStrictly: false,
1333
- dataSourceFieldType: ["string", "number"],
1334
- fieldConfig: {
1335
- type: "text"
1336
- }
1337
- }
1338
- ]
1339
- },
1340
- {
1341
- type: "fieldset",
1342
- legend: "变形",
1343
- name: "transform",
1344
- items: [
1345
- {
1346
- type: "data-source-field-select",
1347
- name: "rotate",
1348
- text: "旋转角度",
1349
- checkStrictly: false,
1350
- dataSourceFieldType: ["string"],
1351
- fieldConfig: {
1352
- type: "text"
1353
- }
1354
- },
1355
- {
1356
- type: "data-source-field-select",
1357
- name: "scale",
1358
- text: "缩放",
1359
- checkStrictly: false,
1360
- dataSourceFieldType: ["number", "string"],
1361
- fieldConfig: {
1362
- type: "text"
1363
- }
1364
- }
1365
- ]
1366
- }
1367
- ]
1368
- }
1369
- ]
1370
- };
1371
- const eventTabConfig = {
1372
- title: "事件",
1373
- items: [
1374
- {
1375
- name: "events",
1376
- src: "component",
1377
- labelWidth: "100px",
1378
- type: "event-select"
1379
- }
1380
- ]
1381
- };
1382
- const advancedTabConfig = {
1383
- title: "高级",
1384
- lazy: true,
1385
- items: [
1386
- {
1387
- name: "created",
1388
- text: "created",
1389
- type: "code-select"
1390
- },
1391
- {
1392
- name: "mounted",
1393
- text: "mounted",
1394
- type: "code-select"
1395
- }
1396
- ]
1397
- };
1398
- const displayTabConfig = {
1399
- title: "显示条件",
1400
- display: (vm, { model }) => model.type !== "page",
1401
- items: [
1402
- {
1403
- type: "groupList",
1404
- name: "displayConds",
1405
- titlePrefix: "条件组",
1406
- expandAll: true,
1407
- items: [
1408
- {
1409
- type: "table",
1410
- name: "cond",
1411
- items: [
1412
- {
1413
- type: "data-source-field-select",
1414
- name: "field",
1415
- value: "key",
1416
- label: "字段",
1417
- checkStrictly: false,
1418
- dataSourceFieldType: ["string", "number", "boolean", "any"]
1419
- },
1420
- {
1421
- type: "select",
1422
- options: (mForm, { model }) => {
1423
- const [id, ...fieldNames] = model.field;
1424
- const ds = dataSourceService.getDataSourceById(id);
1425
- let fields = ds?.fields || [];
1426
- let type = "";
1427
- (fieldNames || []).forEach((fieldName) => {
1428
- const field = fields.find((f) => f.name === fieldName);
1429
- fields = field?.fields || [];
1430
- type = field?.type || "";
1431
- });
1432
- if (type === "array") {
1433
- return arrayOptions;
1434
- }
1435
- if (type === "boolean") {
1436
- return [
1437
- { text: "是", value: "is" },
1438
- { text: "不是", value: "not" }
1439
- ];
1440
- }
1441
- if (type === "number") {
1442
- return [...eqOptions, ...numberOptions];
1443
- }
1444
- if (type === "string") {
1445
- return [...arrayOptions, ...eqOptions];
1446
- }
1447
- return [...arrayOptions, ...eqOptions, ...numberOptions];
1448
- },
1449
- label: "条件",
1450
- name: "op"
1451
- },
1452
- {
1453
- label: "值",
1454
- items: [
1455
- {
1456
- name: "value",
1457
- type: (mForm, { model }) => {
1458
- const [id, ...fieldNames] = model.field;
1459
- const ds = dataSourceService.getDataSourceById(id);
1460
- let fields = ds?.fields || [];
1461
- let type = "";
1462
- (fieldNames || []).forEach((fieldName) => {
1463
- const field = fields.find((f) => f.name === fieldName);
1464
- fields = field?.fields || [];
1465
- type = field?.type || "";
1466
- });
1467
- if (type === "number") {
1468
- return "number";
1469
- }
1470
- if (type === "boolean") {
1471
- return "select";
1472
- }
1473
- return "text";
1474
- },
1475
- options: [
1476
- { text: "true", value: true },
1477
- { text: "false", value: false }
1478
- ],
1479
- display: (vm, { model }) => !["between", "not_between"].includes(model.op)
1480
- },
1481
- {
1482
- name: "range",
1483
- type: "number-range",
1484
- display: (vm, { model }) => ["between", "not_between"].includes(model.op)
1485
- }
1486
- ]
1487
- }
1488
- ]
1489
- }
1490
- ]
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;
1491
933
  }
1492
- ]
1493
- };
1494
- const fillConfig = (config = [], labelWidth = "80px") => [
1495
- {
1496
- type: "tab",
1497
- labelWidth,
1498
- items: [
1499
- {
1500
- title: "属性",
1501
- items: [
1502
- // 组件类型,必须要有
1503
- {
1504
- text: "type",
1505
- name: "type",
1506
- type: "hidden"
1507
- },
1508
- // 组件id,必须要有
1509
- {
1510
- name: "id",
1511
- type: "display",
1512
- text: "id"
1513
- },
1514
- {
1515
- name: "name",
1516
- text: "组件名称"
1517
- },
1518
- ...config
1519
- ]
1520
- },
1521
- { ...styleTabConfig },
1522
- { ...eventTabConfig },
1523
- { ...advancedTabConfig },
1524
- { ...displayTabConfig }
1525
- ]
1526
934
  }
1527
- ];
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
+ }
1528
964
 
1529
- const log = (...args) => {
1530
- if (process.env.NODE_ENV === "development") {
1531
- 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);
975
+ }
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;
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;
1532
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"]
1533
1060
  };
1534
- const info = (...args) => {
1535
- if (process.env.NODE_ENV === "development") {
1536
- console.info("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}`);
1537
1128
  }
1538
- };
1539
- const warn = (...args) => {
1540
- if (process.env.NODE_ENV === "development") {
1541
- console.warn("magic editor: ", ...args);
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);
1542
1143
  }
1543
- };
1544
- const debug = (...args) => {
1545
- if (process.env.NODE_ENV === "development") {
1546
- console.debug("magic editor: ", ...args);
1144
+ destroy() {
1145
+ this.removeAllListeners();
1146
+ this.removeAllPlugins();
1547
1147
  }
1548
- };
1549
- const error = (...args) => {
1550
- if (process.env.NODE_ENV === "development") {
1551
- console.error("magic editor: ", ...args);
1148
+ usePlugin(options) {
1149
+ super.usePlugin(options);
1552
1150
  }
1553
- };
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();
1554
1170
 
1555
1171
  var ColumnLayout = /* @__PURE__ */ ((ColumnLayout2) => {
1556
1172
  ColumnLayout2["LEFT"] = "left";
@@ -1619,6 +1235,8 @@
1619
1235
  const UI_SELECT_MODE_EVENT_NAME = "ui-select";
1620
1236
 
1621
1237
  const COPY_STORAGE_KEY = "$MagicEditorCopyData";
1238
+ const COPY_CODE_STORAGE_KEY = "$MagicEditorCopyCode";
1239
+ const COPY_DS_STORAGE_KEY = "$MagicEditorCopyDataSource";
1622
1240
  const getPageList = (root) => {
1623
1241
  if (!root)
1624
1242
  return [];
@@ -1805,1421 +1423,1855 @@
1805
1423
  }
1806
1424
  };
1807
1425
 
1808
- class Dep extends BaseService {
1809
- watcher = new dep.Watcher({ initialTargets: vue.reactive({}) });
1810
- removeTargets(type = dep.DepTargetType.DEFAULT) {
1811
- this.watcher.removeTargets(type);
1812
- this.emit("remove-target");
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);
1813
1485
  }
1814
- getTargets(type = dep.DepTargetType.DEFAULT) {
1815
- return this.watcher.getTargets(type);
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
+ };
1495
+
1496
+ const canUsePluginMethods$4 = {
1497
+ async: [
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"
1520
+ ],
1521
+ sync: []
1522
+ };
1523
+ class Editor extends BaseService {
1524
+ state = vue.reactive({
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
1537
+ });
1538
+ isHistoryStateChange = false;
1539
+ constructor() {
1540
+ super(
1541
+ canUsePluginMethods$4.async.map((methodName) => ({ name: methodName, isAsync: true })),
1542
+ // 需要注意循环依赖问题,如果函数间有相互调用的话,不能设置为串行调用
1543
+ ["select", "update", "moveLayer"]
1544
+ );
1816
1545
  }
1817
- getTarget(id, type = dep.DepTargetType.DEFAULT) {
1818
- return this.watcher.getTarget(id, type);
1546
+ /**
1547
+ * 设置当前指点节点配置
1548
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
1549
+ * @param value MNode
1550
+ */
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]);
1556
+ }
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;
1570
+ }
1571
+ this.emit("root-change", value, preValue);
1572
+ }
1819
1573
  }
1820
- addTarget(target) {
1821
- this.watcher.addTarget(target);
1822
- this.emit("add-target", target);
1574
+ /**
1575
+ * 获取当前指点节点配置
1576
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength'
1577
+ * @returns MNode
1578
+ */
1579
+ get(name) {
1580
+ return this.state[name];
1581
+ }
1582
+ /**
1583
+ * 根据id获取组件、组件的父组件以及组件所属的页面节点
1584
+ * @param {number | string} id 组件id
1585
+ * @param {boolean} raw 是否使用toRaw
1586
+ * @returns {EditorNodeInfo}
1587
+ */
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;
1613
+ return;
1614
+ }
1615
+ });
1616
+ return info;
1617
+ }
1618
+ /**
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;
1823
1627
  }
1824
- removeTarget(id, type = dep.DepTargetType.DEFAULT) {
1825
- this.watcher.removeTarget(id, type);
1826
- this.emit("remove-target");
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;
1827
1637
  }
1828
- clearTargets() {
1829
- this.watcher.clearTargets();
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;
1830
1651
  }
1831
- collect(nodes, deep = false, type) {
1832
- this.watcher.collect(nodes, deep, type);
1833
- this.emit("collected", nodes, deep);
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;
1834
1680
  }
1835
- clear(nodes) {
1836
- return this.watcher.clear(nodes);
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;
1837
1693
  }
1838
- clearByType(type, nodes) {
1839
- return this.watcher.clearByType(type, nodes);
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;
1840
1706
  }
1841
- hasTarget(id, type = dep.DepTargetType.DEFAULT) {
1842
- return this.watcher.hasTarget(id, type);
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);
1843
1718
  }
1844
- hasSpecifiedTypeTarget(type = dep.DepTargetType.DEFAULT) {
1845
- return this.watcher.hasSpecifiedTypeTarget(type);
1719
+ /**
1720
+ * 多选
1721
+ * @param ids 指定节点ID
1722
+ * @returns 加入多选的节点配置
1723
+ */
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);
1846
1734
  }
1847
- }
1848
- const depService = new Dep();
1849
-
1850
- const canUsePluginMethods$5 = {
1851
- async: [
1852
- "setPropsConfig",
1853
- "getPropsConfig",
1854
- "setPropsValue",
1855
- "getPropsValue",
1856
- "fillConfig",
1857
- "getDefaultPropsValue"
1858
- ],
1859
- sync: ["createId", "setNewItemId"]
1860
- };
1861
- class Props extends BaseService {
1862
- state = vue.reactive({
1863
- propsConfigMap: {},
1864
- propsValueMap: {},
1865
- relateIdMap: {}
1866
- });
1867
- constructor() {
1868
- super([
1869
- ...canUsePluginMethods$5.async.map((methodName) => ({ name: methodName, isAsync: true })),
1870
- ...canUsePluginMethods$5.sync.map((methodName) => ({ name: methodName, isAsync: false }))
1871
- ]);
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);
1872
1744
  }
1873
- setPropsConfigs(configs) {
1874
- Object.keys(configs).forEach((type) => {
1875
- this.setPropsConfig(utils.toLine(type), configs[type]);
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)
1876
1769
  });
1877
- this.emit("props-configs-change");
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;
1878
1777
  }
1879
- async fillConfig(config, labelWidth) {
1880
- return fillConfig(config, typeof labelWidth !== "function" ? labelWidth : "80px");
1778
+ /**
1779
+ * 向指点容器添加组件节点
1780
+ * @param addConfig 将要添加的组件节点配置
1781
+ * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
1782
+ * @returns 添加后的节点
1783
+ */
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];
1881
1826
  }
1882
- async setPropsConfig(type, config) {
1883
- this.state.propsConfigMap[utils.toLine(type)] = await this.fillConfig(Array.isArray(config) ? config : [config]);
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
+ }
1884
1865
  }
1885
1866
  /**
1886
- * 获取指点类型的组件属性表单配置
1887
- * @param type 组件类型
1888
- * @returns 组件属性表单配置
1867
+ * 删除组件
1868
+ * @param {Object} node
1889
1869
  */
1890
- async getPropsConfig(type) {
1891
- if (type === "area") {
1892
- return await this.getPropsConfig("button");
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();
1893
1875
  }
1894
- return lodashEs.cloneDeep(this.state.propsConfigMap[utils.toLine(type)] || await this.fillConfig([]));
1876
+ this.emit("remove", nodes);
1895
1877
  }
1896
- setPropsValues(values) {
1897
- Object.keys(values).forEach((type) => {
1898
- this.setPropsValue(utils.toLine(type), values[type]);
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
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;
1900
1933
  }
1901
1934
  /**
1902
- * 为指点类型组件设置组件初始值
1903
- * @param type 组件类型
1904
- * @param value 组件初始值
1905
- */
1906
- async setPropsValue(type, value) {
1907
- this.state.propsValueMap[utils.toLine(type)] = value;
1908
- }
1909
- /**
1910
- * 获取指定类型的组件初始值
1911
- * @param type 组件类型
1912
- * @returns 组件初始值
1935
+ * 更新节点
1936
+ * @param config 新的节点配置,配置中需要有id信息
1937
+ * @returns 更新后的节点配置
1913
1938
  */
1914
- async getPropsValue(componentType, { inputEvent, ...defaultValue } = {}) {
1915
- const type = utils.toLine(componentType);
1916
- if (type === "area") {
1917
- const value = await this.getPropsValue("button");
1918
- value.className = "action-area";
1919
- value.text = "";
1920
- if (value.style) {
1921
- value.style.backgroundColor = "rgba(255, 255, 255, 0)";
1922
- }
1923
- return value;
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();
1924
1944
  }
1925
- const id = this.createId(type);
1926
- const defaultPropsValue = this.getDefaultPropsValue(type);
1927
- const data = this.setNewItemId(
1928
- lodashEs.cloneDeep({
1929
- type,
1930
- ...defaultValue
1931
- })
1932
- );
1933
- return {
1934
- id,
1935
- ...defaultPropsValue,
1936
- ...lodashEs.mergeWith({}, lodashEs.cloneDeep(this.state.propsValueMap[type] || {}), data)
1937
- };
1938
- }
1939
- createId(type) {
1940
- return `${type}_${utils.guid()}`;
1945
+ this.emit("update", newNodes);
1946
+ return Array.isArray(config) ? newNodes : newNodes[0];
1941
1947
  }
1942
1948
  /**
1943
- * 将组件与组件的子元素配置中的id都设置成一个新的ID
1944
- * 如果没有相同ID并且force为false则保持不变
1945
- * @param {Object} config 组件配置
1946
- * @param {Boolean} force 是否强制设置新的ID
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
1947
1953
  */
1948
- /* eslint no-param-reassign: ["error", { "props": false }] */
1949
- setNewItemId(config, force = true) {
1950
- if (force || editorService.getNodeById(config.id)) {
1951
- const newId = this.createId(config.type || "component");
1952
- this.setRelateId(config.id, newId);
1953
- config.id = newId;
1954
- }
1955
- if (config.items && Array.isArray(config.items)) {
1956
- for (const item of config.items) {
1957
- this.setNewItemId(item);
1958
- }
1959
- }
1960
- return config;
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)
1975
+ });
1976
+ this.addModifiedNodeId(parent.id);
1977
+ this.pushHistoryState();
1961
1978
  }
1962
1979
  /**
1963
- * 获取默认属性配置
1964
- * @param type 组件类型
1965
- * @returns Object
1980
+ * 将组件节点配置存储到localStorage中
1981
+ * @param config 组件节点配置
1982
+ * @returns
1966
1983
  */
1967
- getDefaultPropsValue(type) {
1968
- return ["page", "container"].includes(type) ? {
1969
- type,
1970
- layout: "absolute",
1971
- style: {},
1972
- name: type,
1973
- items: []
1974
- } : {
1975
- type,
1976
- style: {},
1977
- name: type
1978
- };
1979
- }
1980
- resetState() {
1981
- this.state.propsConfigMap = {};
1982
- this.state.propsValueMap = {};
1984
+ copy(config) {
1985
+ storageService.setItem(COPY_STORAGE_KEY, Array.isArray(config) ? config : [config], {
1986
+ protocol: Protocol.OBJECT
1987
+ });
1983
1988
  }
1984
1989
  /**
1985
- * 替换关联ID
1986
- * @param originConfigs 原组件配置
1987
- * @param targetConfigs 待替换的组件配置
1990
+ * 复制时会带上组件关联的依赖
1991
+ * @param config 组件节点配置
1992
+ * @returns
1988
1993
  */
1989
- replaceRelateId(originConfigs, targetConfigs) {
1990
- const relateIdMap = this.getRelateIdMap();
1991
- if (Object.keys(relateIdMap).length === 0)
1992
- return;
1993
- const target = depService.getTarget(dep.DepTargetType.RELATED_COMP_WHEN_COPY, dep.DepTargetType.RELATED_COMP_WHEN_COPY);
1994
- if (!target)
1995
- return;
1996
- originConfigs.forEach((config) => {
1997
- const newId = relateIdMap[config.id];
1998
- const targetConfig = targetConfigs.find((targetConfig2) => targetConfig2.id === newId);
1999
- if (!targetConfig)
2000
- return;
2001
- target.deps[config.id]?.keys?.forEach((fullKey) => {
2002
- const relateOriginId = utils.getValueByKeyPath(fullKey, config);
2003
- const relateTargetId = relateIdMap[relateOriginId];
2004
- if (!relateTargetId)
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)
2005
2006
  return;
2006
- utils.setValueByKeyPath(fullKey, relateTargetId, targetConfig);
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
+ });
2007
2017
  });
2018
+ }
2019
+ storageService.setItem(COPY_STORAGE_KEY, copyNodes, {
2020
+ protocol: Protocol.OBJECT
2008
2021
  });
2009
2022
  }
2010
2023
  /**
2011
- * 清除setNewItemId前后映射关系
2012
- */
2013
- clearRelateId() {
2014
- this.state.relateIdMap = {};
2015
- }
2016
- destroy() {
2017
- this.resetState();
2018
- this.removeAllListeners();
2019
- this.removeAllPlugins();
2020
- }
2021
- usePlugin(options) {
2022
- super.usePlugin(options);
2023
- }
2024
- /**
2025
- * 获取setNewItemId前后映射关系
2026
- * @param oldId 原组件ID
2027
- * @returns 新旧ID映射
2028
- */
2029
- getRelateIdMap() {
2030
- return this.state.relateIdMap;
2031
- }
2032
- /**
2033
- * 记录setNewItemId前后映射关系
2034
- * @param oldId 原组件ID
2035
- * @param newId 分配的新ID
2024
+ * 从localStorage中获取节点,然后添加到当前容器中
2025
+ * @param position 粘贴的坐标
2026
+ * @returns 添加后的组件节点配置
2036
2027
  */
2037
- setRelateId(oldId, newId) {
2038
- this.state.relateIdMap[oldId] = newId;
2039
- }
2040
- }
2041
- const propsService = new Props();
2042
-
2043
- class UndoRedo {
2044
- elementList;
2045
- listCursor;
2046
- listMaxSize;
2047
- constructor(listMaxSize = 20) {
2048
- const minListMaxSize = 2;
2049
- this.elementList = [];
2050
- this.listCursor = 0;
2051
- this.listMaxSize = listMaxSize > minListMaxSize ? listMaxSize : minListMaxSize;
2052
- }
2053
- pushElement(element) {
2054
- this.elementList.splice(this.listCursor, this.elementList.length - this.listCursor, lodashEs.cloneDeep(element));
2055
- this.listCursor += 1;
2056
- if (this.elementList.length > this.listMaxSize) {
2057
- this.elementList.shift();
2058
- this.listCursor -= 1;
2059
- }
2060
- }
2061
- canUndo() {
2062
- return this.listCursor > 1;
2063
- }
2064
- // 返回undo后的当前元素
2065
- undo() {
2066
- if (!this.canUndo()) {
2067
- return null;
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");
2038
+ }
2068
2039
  }
2069
- this.listCursor -= 1;
2070
- return this.getCurrentElement();
2040
+ const pasteConfigs = await this.doPaste(config, position);
2041
+ propsService.replaceRelateId(config, pasteConfigs);
2042
+ return this.add(pasteConfigs, parent);
2071
2043
  }
2072
- canRedo() {
2073
- return this.elementList.length > this.listCursor;
2044
+ async doPaste(config, position = {}) {
2045
+ propsService.clearRelateId();
2046
+ const pasteConfigs = beforePaste(position, lodashEs.cloneDeep(config));
2047
+ return pasteConfigs;
2074
2048
  }
2075
- // 返回redo后的当前元素
2076
- redo() {
2077
- if (!this.canRedo()) {
2078
- return null;
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;
2079
2057
  }
2080
- this.listCursor += 1;
2081
- return this.getCurrentElement();
2082
- }
2083
- getCurrentElement() {
2084
- if (this.listCursor < 1) {
2085
- return null;
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 = "";
2068
+ }
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 = "";
2086
2072
  }
2087
- return lodashEs.cloneDeep(this.elementList[this.listCursor - 1]);
2088
- }
2089
- }
2090
-
2091
- class History extends BaseService {
2092
- state = vue.reactive({
2093
- pageSteps: {},
2094
- pageId: void 0,
2095
- canRedo: false,
2096
- canUndo: false
2097
- });
2098
- constructor() {
2099
- super([]);
2100
- this.on("change", this.setCanUndoRedo);
2101
- }
2102
- reset() {
2103
- this.state.pageSteps = {};
2104
- this.resetPage();
2105
- }
2106
- resetPage() {
2107
- this.state.pageId = void 0;
2108
- this.state.canRedo = false;
2109
- this.state.canUndo = false;
2073
+ return node;
2110
2074
  }
2111
- changePage(page) {
2112
- if (!page)
2113
- return;
2114
- this.state.pageId = page.id;
2115
- if (!this.state.pageSteps[this.state.pageId]) {
2116
- const undoRedo = new UndoRedo();
2117
- undoRedo.pushElement({
2118
- data: page,
2119
- modifiedNodeIds: /* @__PURE__ */ new Map(),
2120
- nodeId: page.id
2121
- });
2122
- this.state.pageSteps[this.state.pageId] = undoRedo;
2075
+ /**
2076
+ * 将指点节点设置居中
2077
+ * @param config 组件节点配置
2078
+ * @returns 当前组件节点配置
2079
+ */
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);
2123
2089
  }
2124
- this.setCanUndoRedo();
2125
- this.emit("page-change", this.state.pageSteps[this.state.pageId]);
2126
- }
2127
- resetState() {
2128
- this.state.pageId = void 0;
2129
- this.state.pageSteps = {};
2130
- this.state.canRedo = false;
2131
- this.state.canUndo = false;
2132
- }
2133
- push(state) {
2134
- const undoRedo = this.getUndoRedo();
2135
- if (!undoRedo)
2136
- return null;
2137
- undoRedo.pushElement(state);
2138
- this.emit("change", state);
2139
- return state;
2140
- }
2141
- undo() {
2142
- const undoRedo = this.getUndoRedo();
2143
- if (!undoRedo)
2144
- return null;
2145
- const state = undoRedo.undo();
2146
- this.emit("change", state);
2147
- return state;
2148
- }
2149
- redo() {
2150
- const undoRedo = this.getUndoRedo();
2151
- if (!undoRedo)
2152
- return null;
2153
- const state = undoRedo.redo();
2154
- this.emit("change", state);
2155
- return state;
2156
- }
2157
- destroy() {
2158
- this.resetState();
2159
- this.removeAllListeners();
2160
- this.removeAllPlugins();
2161
- }
2162
- getUndoRedo() {
2163
- if (!this.state.pageId)
2164
- return null;
2165
- return this.state.pageSteps[this.state.pageId];
2166
- }
2167
- setCanUndoRedo() {
2168
- const undoRedo = this.getUndoRedo();
2169
- this.state.canRedo = undoRedo?.canRedo() || false;
2170
- this.state.canUndo = undoRedo?.canUndo() || false;
2171
- }
2172
- }
2173
- const historyService = new History();
2174
-
2175
- var Protocol = /* @__PURE__ */ ((Protocol2) => {
2176
- Protocol2["OBJECT"] = "object";
2177
- Protocol2["JSON"] = "json";
2178
- Protocol2["STRING"] = "string";
2179
- Protocol2["NUMBER"] = "number";
2180
- Protocol2["BOOLEAN"] = "boolean";
2181
- return Protocol2;
2182
- })(Protocol || {});
2183
- const canUsePluginMethods$4 = {
2184
- async: [],
2185
- sync: ["getStorage", "getNamespace", "clear", "getItem", "removeItem", "setItem"]
2186
- };
2187
- class WebStorage extends BaseService {
2188
- storage = globalThis.localStorage;
2189
- namespace = "tmagic";
2190
- constructor() {
2191
- super(canUsePluginMethods$4.sync.map((methodName) => ({ name: methodName, isAsync: false })));
2090
+ return newNode;
2192
2091
  }
2193
2092
  /**
2194
- * 获取数据存储对象,可以通过
2195
- * const storageService = new StorageService();
2196
- * storageService.usePlugin({
2197
- * // 替换存储对象为 sessionStorage
2198
- * async afterGetStorage(): Promise<Storage> {
2199
- * return window.sessionStorage;
2200
- * },
2201
- * });
2093
+ * 移动当前选中节点位置
2094
+ * @param offset 偏移量
2202
2095
  */
2203
- getStorage() {
2204
- return this.storage;
2205
- }
2206
- getNamespace() {
2207
- return this.namespace;
2096
+ async moveLayer(offset) {
2097
+ const root = this.get("root");
2098
+ if (!root)
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);
2117
+ }
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);
2208
2132
  }
2209
2133
  /**
2210
- * 清理,支持storageService.usePlugin
2134
+ * 移动到指定容器中
2135
+ * @param config 需要移动的节点
2136
+ * @param targetId 容器ID
2211
2137
  */
2212
- clear() {
2213
- const storage = this.getStorage();
2214
- storage.clear();
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
+ }
2215
2165
  }
2216
- /**
2217
- * 获取存储项,支持storageService.usePlugin
2218
- */
2219
- getItem(key, options = {}) {
2220
- const storage = this.getStorage();
2221
- const namespace = this.getNamespace();
2222
- const { protocol = options.protocol, item } = this.getValueAndProtocol(
2223
- storage.getItem(`${options.namespace || namespace}:${key}`)
2224
- );
2225
- if (item === null)
2226
- return null;
2227
- switch (protocol) {
2228
- case "object" /* OBJECT */:
2229
- return getConfig("parseDSL")(`(${item})`);
2230
- case "json" /* JSON */:
2231
- return JSON.parse(item);
2232
- case "number" /* NUMBER */:
2233
- return Number(item);
2234
- case "boolean" /* BOOLEAN */:
2235
- return Boolean(item);
2236
- default:
2237
- return item;
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)
2177
+ return;
2178
+ if (index < targetIndex) {
2179
+ targetIndex -= 1;
2180
+ }
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
+ });
2238
2198
  }
2199
+ this.addModifiedNodeId(config.id);
2200
+ this.addModifiedNodeId(parent.id);
2201
+ this.pushHistoryState();
2202
+ this.emit("drag-to", { index, targetIndex, config, parent, targetParent });
2239
2203
  }
2240
2204
  /**
2241
- * 获取指定索引位置的key
2205
+ * 撤销当前操作
2206
+ * @returns 上一次数据
2242
2207
  */
2243
- key(index) {
2244
- const storage = this.getStorage();
2245
- return storage.key(index);
2208
+ async undo() {
2209
+ const value = historyService.undo();
2210
+ await this.changeHistoryState(value);
2211
+ return value;
2246
2212
  }
2247
2213
  /**
2248
- * 移除存储项,支持storageService.usePlugin
2214
+ * 恢复到下一步
2215
+ * @returns 下一步数据
2249
2216
  */
2250
- removeItem(key, options = {}) {
2251
- const storage = this.getStorage();
2252
- const namespace = this.getNamespace();
2253
- storage.removeItem(`${options.namespace || namespace}:${key}`);
2217
+ async redo() {
2218
+ const value = historyService.redo();
2219
+ await this.changeHistoryState(value);
2220
+ return value;
2254
2221
  }
2255
- /**
2256
- * 设置存储项,支持storageService.usePlugin
2257
- */
2258
- setItem(key, value, options = {}) {
2259
- const storage = this.getStorage();
2260
- const namespace = this.getNamespace();
2261
- let item = value;
2262
- const protocol = options.protocol ? `${options.protocol}:` : "";
2263
- if (typeof value === "string" /* STRING */ || typeof value === "number" /* NUMBER */) {
2264
- item = `${protocol}${value}`;
2265
- } else {
2266
- item = `${protocol}${serialize(value)}`;
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
+ }
2267
2248
  }
2268
- storage.setItem(`${options.namespace || namespace}:${key}`, item);
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
+ }
2263
+ }
2264
+ }
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);
2269
2275
  }
2270
2276
  destroy() {
2271
2277
  this.removeAllListeners();
2278
+ this.resetState();
2272
2279
  this.removeAllPlugins();
2273
2280
  }
2281
+ resetModifiedNodeId() {
2282
+ this.get("modifiedNodeIds").clear();
2283
+ }
2274
2284
  usePlugin(options) {
2275
2285
  super.usePlugin(options);
2276
2286
  }
2277
- getValueAndProtocol(value) {
2278
- let protocol = "";
2279
- if (value === null) {
2280
- return {
2281
- item: value,
2282
- protocol
2283
- };
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;
2333
+ } else {
2334
+ id = config.id;
2335
+ }
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("不能选根节点");
2284
2344
  }
2285
- const item = value.replace(new RegExp(`^(${Object.values(Protocol).join("|")})(:)(.+)`), (_$0, $1, _$2, $3) => {
2286
- protocol = $1;
2287
- return $3;
2288
- });
2289
2345
  return {
2290
- protocol,
2291
- item
2346
+ node,
2347
+ parent,
2348
+ page
2292
2349
  };
2293
2350
  }
2294
2351
  }
2295
- const storageService = new WebStorage();
2352
+ const editorService = new Editor();
2296
2353
 
2297
- const canUsePluginMethods$3 = {
2298
- async: [
2299
- "getLayout",
2300
- "highlight",
2301
- "select",
2302
- "multiSelect",
2303
- "doAdd",
2304
- "add",
2305
- "doRemove",
2306
- "remove",
2307
- "doUpdate",
2308
- "update",
2309
- "sort",
2310
- "copy",
2311
- "paste",
2312
- "doPaste",
2313
- "doAlignCenter",
2314
- "alignCenter",
2315
- "moveLayer",
2316
- "moveToContainer",
2317
- "dragTo",
2318
- "undo",
2319
- "redo",
2320
- "move"
2321
- ],
2322
- sync: []
2323
- };
2324
- class Editor extends BaseService {
2325
- state = vue.reactive({
2326
- root: null,
2327
- page: null,
2328
- parent: null,
2329
- node: null,
2330
- nodes: [],
2331
- stage: null,
2332
- stageLoading: true,
2333
- highlightNode: null,
2334
- modifiedNodeIds: /* @__PURE__ */ new Map(),
2335
- pageLength: 0,
2336
- pageFragmentLength: 0,
2337
- disabledMultiSelect: false
2338
- });
2339
- isHistoryStateChange = false;
2340
- constructor() {
2341
- super(
2342
- canUsePluginMethods$3.async.map((methodName) => ({ name: methodName, isAsync: true })),
2343
- // 需要注意循环依赖问题,如果函数间有相互调用的话,不能设置为串行调用
2344
- ["select", "update", "moveLayer"]
2345
- );
2346
- }
2347
- /**
2348
- * 设置当前指点节点配置
2349
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
2350
- * @param value MNode
2351
- */
2352
- set(name, value) {
2353
- const preValue = this.state[name];
2354
- this.state[name] = value;
2355
- if (name === "nodes" && Array.isArray(value)) {
2356
- this.set("node", value[0]);
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: [
2370
+ {
2371
+ required: true,
2372
+ message: "请输入名称"
2373
+ }
2374
+ ]
2375
+ },
2376
+ {
2377
+ name: "description",
2378
+ text: "描述"
2357
2379
  }
2358
- if (name === "root") {
2359
- if (Array.isArray(value)) {
2360
- throw new Error("root 不能为数组");
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: "数据路径"
2361
2396
  }
2362
- if (value && lodashEs.isObject(value)) {
2363
- const app = value;
2364
- this.state.pageLength = getPageList(app).length || 0;
2365
- this.state.pageFragmentLength = getPageFragmentList(app).length || 0;
2366
- this.state.stageLoading = this.state.pageLength !== 0;
2367
- } else {
2368
- this.state.pageLength = 0;
2369
- this.state.pageFragmentLength = 0;
2370
- this.state.stageLoading = false;
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
+ ]
2439
+ }
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
+ ]
2371
2511
  }
2372
- this.emit("root-change", value, preValue);
2373
- }
2512
+ ]
2374
2513
  }
2375
- /**
2376
- * 获取当前指点节点配置
2377
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength'
2378
- * @returns MNode
2379
- */
2380
- get(name) {
2381
- return this.state[name];
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] || []);
2382
2523
  }
2383
- /**
2384
- * 根据id获取组件、组件的父组件以及组件所属的页面节点
2385
- * @param {number | string} id 组件id
2386
- * @param {boolean} raw 是否使用toRaw
2387
- * @returns {EditorNodeInfo}
2388
- */
2389
- getNodeInfo(id, raw = true) {
2390
- let root = this.get("root");
2391
- if (raw) {
2392
- root = vue.toRaw(root);
2393
- }
2394
- const info = {
2395
- node: null,
2396
- parent: null,
2397
- page: null
2398
- };
2399
- if (!root)
2400
- return info;
2401
- if (id === root.id) {
2402
- info.node = root;
2403
- return info;
2404
- }
2405
- const path = utils.getNodePath(id, root.items);
2406
- if (!path.length)
2407
- return info;
2408
- path.unshift(root);
2409
- info.node = path[path.length - 1];
2410
- info.parent = path[path.length - 2];
2411
- path.forEach((item) => {
2412
- if (utils.isPage(item) || utils.isPageFragment(item)) {
2413
- info.page = item;
2524
+ };
2525
+ const getFormValue = (type, values) => {
2526
+ if (type !== "http") {
2527
+ return values;
2528
+ }
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)
2591
+ });
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;
2414
2600
  return;
2415
2601
  }
2602
+ const field = fields?.find((field2) => field2.name === item);
2603
+ fields = field?.fields;
2604
+ dsText += `.${field?.title || item}`;
2416
2605
  });
2417
- return info;
2418
- }
2419
- /**
2420
- * 根据ID获取指点节点配置
2421
- * @param id 组件ID
2422
- * @param {boolean} raw 是否使用toRaw
2423
- * @returns 组件节点配置
2424
- */
2425
- getNodeById(id, raw = true) {
2426
- const { node } = this.getNodeInfo(id, raw);
2427
- return node;
2606
+ displayState.push({
2607
+ type: "var",
2608
+ value: dsText
2609
+ });
2610
+ index = match.index + match[0].length;
2428
2611
  }
2429
- /**
2430
- * 根据ID获取指点节点的父节点配置
2431
- * @param id 组件ID
2432
- * @param {boolean} raw 是否使用toRaw
2433
- * @returns 指点组件的父节点配置
2434
- */
2435
- getParentById(id, raw = true) {
2436
- const { parent } = this.getNodeInfo(id, raw);
2437
- return parent;
2612
+ if (index < key.length) {
2613
+ displayState.push({
2614
+ type: "text",
2615
+ value: key.substring(index)
2616
+ });
2438
2617
  }
2439
- /**
2440
- * 只有容器拥有布局
2441
- */
2442
- async getLayout(parent, node) {
2443
- if (node && typeof node !== "function" && isFixed(node))
2444
- return Layout.FIXED;
2445
- if (parent.layout) {
2446
- return parent.layout;
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");
2447
2625
  }
2448
- if (!parent.style?.position) {
2449
- return Layout.RELATIVE;
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;
2450
2636
  }
2451
- return Layout.ABSOLUTE;
2452
- }
2453
- /**
2454
- * 选中指定节点(将指定节点设置成当前选中状态)
2455
- * @param config 指定节点配置或者ID
2456
- * @returns 当前选中的节点配置
2457
- */
2458
- async select(config) {
2459
- const { node, page, parent } = this.selectedConfigExceptionHandler(config);
2460
- this.set("nodes", node ? [node] : []);
2461
- this.set("page", page);
2462
- this.set("parent", parent);
2463
- if (page) {
2464
- historyService.changePage(vue.toRaw(page));
2465
- } else {
2466
- historyService.resetState();
2637
+ if (!dataSourceFieldType.includes(fieldType) && !["array", "object", "any"].includes(fieldType)) {
2638
+ return;
2467
2639
  }
2468
- if (node?.id) {
2469
- this.get("stage")?.renderer.runtime?.getApp?.()?.page?.emit(
2470
- "editor:select",
2471
- {
2472
- node,
2473
- page,
2474
- parent
2475
- },
2476
- utils.getNodePath(node.id, this.get("root")?.items)
2477
- );
2640
+ if (!children.length && ["object", "array", "any"].includes(field.type || "")) {
2641
+ return;
2478
2642
  }
2479
- this.emit("select", node);
2480
- return node;
2481
- }
2482
- async selectNextNode() {
2483
- const node = vue.toRaw(this.get("node"));
2484
- if (!node || utils.isPage(node) || node.type === schema.NodeType.ROOT)
2485
- return node;
2486
- const parent = vue.toRaw(this.getParentById(node.id));
2487
- if (!parent)
2488
- return node;
2489
- const index = getNodeIndex(node.id, parent);
2490
- const nextNode = parent.items[index + 1] || parent.items[0];
2491
- await this.select(nextNode);
2492
- this.get("stage")?.select(nextNode.id);
2493
- return nextNode;
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 })));
2494
2677
  }
2495
- async selectNextPage() {
2496
- const root = vue.toRaw(this.get("root"));
2497
- const page = vue.toRaw(this.get("page"));
2498
- if (!page)
2499
- throw new Error("page不能为空");
2500
- if (!root)
2501
- throw new Error("root不能为空");
2502
- const index = getNodeIndex(page.id, root);
2503
- const nextPage = root.items[index + 1] || root.items[0];
2504
- await this.select(nextPage);
2505
- this.get("stage")?.select(nextPage.id);
2506
- return nextPage;
2678
+ set(name, value) {
2679
+ this.state[name] = value;
2507
2680
  }
2508
- /**
2509
- * 高亮指定节点
2510
- * @param config 指定节点配置或者ID
2511
- * @returns 当前高亮的节点配置
2512
- */
2513
- highlight(config) {
2514
- const { node } = this.selectedConfigExceptionHandler(config);
2515
- const currentHighlightNode = this.get("highlightNode");
2516
- if (currentHighlightNode === node)
2517
- return;
2518
- this.set("highlightNode", node);
2681
+ get(name) {
2682
+ return this.state[name];
2519
2683
  }
2520
- /**
2521
- * 多选
2522
- * @param ids 指定节点ID
2523
- * @returns 加入多选的节点配置
2524
- */
2525
- multiSelect(ids) {
2526
- const nodes = [];
2527
- const idsUnique = lodashEs.uniq(ids);
2528
- idsUnique.forEach((id) => {
2529
- const { node } = this.getNodeInfo(id);
2530
- if (!node)
2531
- return;
2532
- nodes.push(node);
2533
- });
2534
- this.set("nodes", nodes);
2684
+ getFormConfig(type = "base") {
2685
+ return getFormConfig(utils.toLine(type), this.get("configs"));
2535
2686
  }
2536
- selectRoot() {
2537
- const root = this.get("root");
2538
- if (!root)
2539
- return;
2540
- this.set("nodes", [root]);
2541
- this.set("parent", null);
2542
- this.set("page", null);
2543
- this.set("stage", null);
2544
- this.set("highlightNode", null);
2687
+ setFormConfig(type, config) {
2688
+ this.get("configs")[utils.toLine(type)] = config;
2545
2689
  }
2546
- async doAdd(node, parent) {
2547
- const root = this.get("root");
2548
- if (!root)
2549
- throw new Error("root为空");
2550
- const curNode = this.get("node");
2551
- const stage = this.get("stage");
2552
- if (!curNode)
2553
- throw new Error("当前选中节点为空");
2554
- if ((parent.type === schema.NodeType.ROOT || curNode?.type === schema.NodeType.ROOT) && !(utils.isPage(node) || utils.isPageFragment(node))) {
2555
- throw new Error("app下不能添加组件");
2556
- }
2557
- if (parent.id !== curNode.id && !(utils.isPage(node) || utils.isPageFragment(node))) {
2558
- const index = parent.items.indexOf(curNode);
2559
- parent.items?.splice(index + 1, 0, node);
2560
- } else {
2561
- parent.items?.push(node);
2562
- }
2563
- const layout = await this.getLayout(vue.toRaw(parent), node);
2564
- node.style = getInitPositionStyle(node.style, layout);
2565
- await stage?.add({
2566
- config: lodashEs.cloneDeep(node),
2567
- parent: lodashEs.cloneDeep(parent),
2568
- parentId: parent.id,
2569
- root: lodashEs.cloneDeep(root)
2570
- });
2571
- const newStyle = fixNodePosition(node, parent, stage);
2572
- if (newStyle && (newStyle.top !== node.style.top || newStyle.left !== node.style.left)) {
2573
- node.style = newStyle;
2574
- await stage?.update({ config: lodashEs.cloneDeep(node), parentId: parent.id, root: lodashEs.cloneDeep(root) });
2575
- }
2576
- this.addModifiedNodeId(node.id);
2577
- return node;
2690
+ getFormValue(type = "base") {
2691
+ return getFormValue(utils.toLine(type), this.get("values")[type]);
2578
2692
  }
2579
- /**
2580
- * 向指点容器添加组件节点
2581
- * @param addConfig 将要添加的组件节点配置
2582
- * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
2583
- * @returns 添加后的节点
2584
- */
2585
- async add(addNode, parent) {
2586
- const stage = this.get("stage");
2587
- const addNodes = [];
2588
- if (!Array.isArray(addNode)) {
2589
- const { type, inputEvent, ...config } = addNode;
2590
- if (!type)
2591
- throw new Error("组件类型不能为空");
2592
- addNodes.push({ ...vue.toRaw(await propsService.getPropsValue(type, config)) });
2593
- } else {
2594
- addNodes.push(...addNode);
2595
- }
2596
- const newNodes = await Promise.all(
2597
- addNodes.map((node) => {
2598
- const root = this.get("root");
2599
- if ((utils.isPage(node) || utils.isPageFragment(node)) && root) {
2600
- return this.doAdd(node, root);
2601
- }
2602
- const parentNode = parent && typeof parent !== "function" ? parent : getAddParent(node);
2603
- if (!parentNode)
2604
- throw new Error("未找到父元素");
2605
- return this.doAdd(node, parentNode);
2606
- })
2607
- );
2608
- if (newNodes.length > 1) {
2609
- const newNodeIds = newNodes.map((node) => node.id);
2610
- stage?.multiSelect(newNodeIds);
2611
- await this.multiSelect(newNodeIds);
2612
- } else {
2613
- await this.select(newNodes[0]);
2614
- if (utils.isPage(newNodes[0])) {
2615
- this.state.pageLength += 1;
2616
- } else if (utils.isPageFragment(newNodes[0])) {
2617
- this.state.pageFragmentLength += 1;
2618
- } else {
2619
- stage?.select(newNodes[0].id);
2620
- }
2621
- }
2622
- if (!(utils.isPage(newNodes[0]) || utils.isPageFragment(newNodes[0]))) {
2623
- this.pushHistoryState();
2624
- }
2625
- this.emit("add", newNodes);
2626
- return Array.isArray(addNode) ? newNodes : newNodes[0];
2693
+ setFormValue(type, value) {
2694
+ this.get("values")[utils.toLine(type)] = value;
2627
2695
  }
2628
- async doRemove(node) {
2629
- const root = this.get("root");
2630
- if (!root)
2631
- throw new Error("root不能为空");
2632
- const { parent, node: curNode } = this.getNodeInfo(node.id, false);
2633
- if (!parent || !curNode)
2634
- throw new Error("找不要删除的节点");
2635
- const index = getNodeIndex(curNode.id, parent);
2636
- if (typeof index !== "number" || index === -1)
2637
- throw new Error("找不要删除的节点");
2638
- parent.items?.splice(index, 1);
2639
- const stage = this.get("stage");
2640
- stage?.remove({ id: node.id, parentId: parent.id, root: lodashEs.cloneDeep(root) });
2641
- const selectDefault = async (pages) => {
2642
- if (pages[0]) {
2643
- await this.select(pages[0]);
2644
- stage?.select(pages[0].id);
2645
- } else {
2646
- this.selectRoot();
2647
- historyService.resetPage();
2648
- }
2649
- };
2650
- const rootItems = root.items || [];
2651
- if (utils.isPage(node)) {
2652
- this.state.pageLength -= 1;
2653
- await selectDefault(getPageList(root));
2654
- } else if (utils.isPageFragment(node)) {
2655
- this.state.pageFragmentLength -= 1;
2656
- await selectDefault(getPageFragmentList(root));
2657
- } else {
2658
- await this.select(parent);
2659
- stage?.select(parent.id);
2660
- this.addModifiedNodeId(parent.id);
2661
- }
2662
- if (!rootItems.length) {
2663
- this.resetModifiedNodeId();
2664
- historyService.reset();
2665
- }
2696
+ getFormEvent(type = "base") {
2697
+ return this.get("events")[utils.toLine(type)] || [];
2666
2698
  }
2667
- /**
2668
- * 删除组件
2669
- * @param {Object} node
2670
- */
2671
- async remove(nodeOrNodeList) {
2672
- const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
2673
- await Promise.all(nodes.map((node) => this.doRemove(node)));
2674
- if (!(utils.isPage(nodes[0]) || utils.isPageFragment(nodes[0]))) {
2675
- this.pushHistoryState();
2676
- }
2677
- this.emit("remove", nodes);
2699
+ setFormEvent(type, value = []) {
2700
+ this.get("events")[utils.toLine(type)] = value;
2678
2701
  }
2679
- async doUpdate(config) {
2680
- const root = this.get("root");
2681
- if (!root)
2682
- throw new Error("root为空");
2683
- if (!config?.id)
2684
- throw new Error("没有配置或者配置缺少id值");
2685
- const info = this.getNodeInfo(config.id, false);
2686
- if (!info.node)
2687
- throw new Error(`获取不到id为${config.id}的节点`);
2688
- const node = lodashEs.cloneDeep(vue.toRaw(info.node));
2689
- let newConfig = await this.toggleFixedPosition(vue.toRaw(config), node, root);
2690
- newConfig = lodashEs.mergeWith(lodashEs.cloneDeep(node), newConfig, (objValue, srcValue, key) => {
2691
- if (typeof srcValue === "undefined" && Object.hasOwn(newConfig, key)) {
2692
- return "";
2693
- }
2694
- if (lodashEs.isObject(srcValue) && Array.isArray(objValue)) {
2695
- return srcValue;
2696
- }
2697
- if (Array.isArray(srcValue)) {
2698
- return srcValue;
2699
- }
2700
- });
2701
- if (!newConfig.type)
2702
- throw new Error("配置缺少type值");
2703
- if (newConfig.type === schema.NodeType.ROOT) {
2704
- this.set("root", newConfig);
2705
- return newConfig;
2706
- }
2707
- const { parent } = info;
2708
- if (!parent)
2709
- throw new Error("获取不到父级节点");
2710
- const parentNodeItems = parent.items;
2711
- const index = getNodeIndex(newConfig.id, parent);
2712
- if (!parentNodeItems || typeof index === "undefined" || index === -1)
2713
- throw new Error("更新的节点未找到");
2714
- const newLayout = await this.getLayout(newConfig);
2715
- const layout = await this.getLayout(node);
2716
- if (Array.isArray(newConfig.items) && newLayout !== layout) {
2717
- newConfig = setChildrenLayout(newConfig, newLayout);
2718
- }
2719
- parentNodeItems[index] = newConfig;
2720
- const nodes = this.get("nodes");
2721
- const targetIndex = nodes.findIndex((nodeItem) => `${nodeItem.id}` === `${newConfig.id}`);
2722
- nodes.splice(targetIndex, 1, newConfig);
2723
- this.set("nodes", [...nodes]);
2724
- this.get("stage")?.update({
2725
- config: lodashEs.cloneDeep(newConfig),
2726
- parentId: parent.id,
2727
- root: lodashEs.cloneDeep(root)
2728
- });
2729
- if (utils.isPage(newConfig) || utils.isPageFragment(newConfig)) {
2730
- this.set("page", newConfig);
2731
- }
2732
- this.addModifiedNodeId(newConfig.id);
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);
2733
2715
  return newConfig;
2734
2716
  }
2735
- /**
2736
- * 更新节点
2737
- * @param config 新的节点配置,配置中需要有id信息
2738
- * @returns 更新后的节点配置
2739
- */
2740
- async update(config) {
2741
- const nodes = Array.isArray(config) ? config : [config];
2742
- const newNodes = await Promise.all(nodes.map((node) => this.doUpdate(node)));
2743
- if (newNodes[0]?.type !== schema.NodeType.ROOT) {
2744
- this.pushHistoryState();
2745
- }
2746
- this.emit("update", newNodes);
2747
- 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;
2748
2723
  }
2749
- /**
2750
- * 将id为id1的组件移动到id为id2的组件位置上,例如:[1,2,3,4] -> sort(1,3) -> [2,1,3,4]
2751
- * @param id1 组件ID
2752
- * @param id2 组件ID
2753
- * @returns void
2754
- */
2755
- async sort(id1, id2) {
2756
- const root = this.get("root");
2757
- if (!root)
2758
- throw new Error("root为空");
2759
- const node = this.get("node");
2760
- if (!node)
2761
- throw new Error("当前节点为空");
2762
- const parent = lodashEs.cloneDeep(vue.toRaw(this.get("parent")));
2763
- if (!parent)
2764
- throw new Error("父节点为空");
2765
- const index2 = parent.items.findIndex((node2) => `${node2.id}` === `${id2}`);
2766
- if (index2 < 0)
2767
- return;
2768
- const index1 = parent.items.findIndex((node2) => `${node2.id}` === `${id1}`);
2769
- parent.items.splice(index2, 0, ...parent.items.splice(index1, 1));
2770
- await this.update(parent);
2771
- await this.select(node);
2772
- this.get("stage")?.update({
2773
- config: lodashEs.cloneDeep(node),
2774
- parentId: parent.id,
2775
- root: lodashEs.cloneDeep(root)
2776
- });
2777
- this.addModifiedNodeId(parent.id);
2778
- this.pushHistoryState();
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);
2779
2729
  }
2780
- /**
2781
- * 将组件节点配置存储到localStorage中
2782
- * @param config 组件节点配置
2783
- * @returns
2784
- */
2785
- copy(config) {
2786
- storageService.setItem(COPY_STORAGE_KEY, Array.isArray(config) ? config : [config], {
2787
- protocol: Protocol.OBJECT
2788
- });
2730
+ createId() {
2731
+ return `ds_${utils.guid()}`;
2732
+ }
2733
+ getDataSourceById(id) {
2734
+ return this.get("dataSources").find((ds) => ds.id === id);
2735
+ }
2736
+ resetState() {
2737
+ this.set("dataSources", []);
2738
+ }
2739
+ destroy() {
2740
+ this.removeAllListeners();
2741
+ this.resetState();
2742
+ this.removeAllPlugins();
2743
+ }
2744
+ usePlugin(options) {
2745
+ super.usePlugin(options);
2789
2746
  }
2790
2747
  /**
2791
- * 复制时会带上组件关联的依赖
2748
+ * 复制时会带上组件关联的数据源
2792
2749
  * @param config 组件节点配置
2793
2750
  * @returns
2794
2751
  */
2795
2752
  copyWithRelated(config) {
2796
2753
  const copyNodes = Array.isArray(config) ? config : [config];
2797
- depService.clearByType(dep.DepTargetType.RELATED_COMP_WHEN_COPY);
2798
- depService.collect(copyNodes, true, dep.DepTargetType.RELATED_COMP_WHEN_COPY);
2799
- const customTarget = depService.getTarget(
2800
- dep.DepTargetType.RELATED_COMP_WHEN_COPY,
2801
- dep.DepTargetType.RELATED_COMP_WHEN_COPY
2802
- );
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 = [];
2803
2758
  if (customTarget) {
2804
2759
  Object.keys(customTarget.deps).forEach((nodeId) => {
2805
- const node = this.getNodeById(nodeId);
2760
+ const node = editorService.getNodeById(nodeId);
2806
2761
  if (!node)
2807
2762
  return;
2808
2763
  customTarget.deps[nodeId].keys.forEach((key) => {
2809
- const relateNodeId = lodashEs.get(node, key);
2810
- const isExist = copyNodes.find((node2) => node2.id === relateNodeId);
2764
+ const [relateDsId] = lodashEs.get(node, key);
2765
+ const isExist = copyData.find((dsItem) => dsItem.id === relateDsId);
2811
2766
  if (!isExist) {
2812
- const relateNode = this.getNodeById(relateNodeId);
2813
- if (relateNode) {
2814
- 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
+ }
2815
2968
  }
2816
- }
2817
- });
2818
- });
2819
- }
2820
- storageService.setItem(COPY_STORAGE_KEY, copyNodes, {
2821
- protocol: Protocol.OBJECT
2822
- });
2823
- }
2824
- /**
2825
- * 从localStorage中获取节点,然后添加到当前容器中
2826
- * @param position 粘贴的坐标
2827
- * @returns 添加后的组件节点配置
2828
- */
2829
- async paste(position = {}) {
2830
- const config = storageService.getItem(COPY_STORAGE_KEY);
2831
- if (!Array.isArray(config))
2832
- return;
2833
- const node = this.get("node");
2834
- let parent = null;
2835
- if (config.length === 1 && config[0].id === node?.id) {
2836
- parent = this.get("parent");
2837
- if (parent?.type === schema.NodeType.ROOT) {
2838
- parent = this.get("page");
2839
- }
2840
- }
2841
- const pasteConfigs = await this.doPaste(config, position);
2842
- propsService.replaceRelateId(config, pasteConfigs);
2843
- return this.add(pasteConfigs, parent);
2844
- }
2845
- async doPaste(config, position = {}) {
2846
- propsService.clearRelateId();
2847
- const pasteConfigs = beforePaste(position, lodashEs.cloneDeep(config));
2848
- return pasteConfigs;
2849
- }
2850
- async doAlignCenter(config) {
2851
- const parent = this.getParentById(config.id);
2852
- if (!parent)
2853
- throw new Error("找不到父节点");
2854
- const node = lodashEs.cloneDeep(vue.toRaw(config));
2855
- const layout = await this.getLayout(parent, node);
2856
- if (layout === Layout.RELATIVE) {
2857
- return config;
2858
- }
2859
- if (!node.style)
2860
- return config;
2861
- const stage = this.get("stage");
2862
- const doc = stage?.renderer.contentWindow?.document;
2863
- if (doc) {
2864
- const el = doc.getElementById(`${node.id}`);
2865
- const parentEl = layout === Layout.FIXED ? doc.body : el?.offsetParent;
2866
- if (parentEl && el) {
2867
- node.style.left = utils.calcValueByFontsize(doc, (parentEl.clientWidth - el.clientWidth) / 2);
2868
- node.style.right = "";
2869
- }
2870
- } else if (parent.style && utils.isNumber(parent.style?.width) && utils.isNumber(node.style?.width)) {
2871
- node.style.left = (parent.style.width - node.style.width) / 2;
2872
- node.style.right = "";
2873
- }
2874
- return node;
2875
- }
2876
- /**
2877
- * 将指点节点设置居中
2878
- * @param config 组件节点配置
2879
- * @returns 当前组件节点配置
2880
- */
2881
- async alignCenter(config) {
2882
- const nodes = Array.isArray(config) ? config : [config];
2883
- const stage = this.get("stage");
2884
- const newNodes = await Promise.all(nodes.map((node) => this.doAlignCenter(node)));
2885
- const newNode = await this.update(newNodes);
2886
- if (newNodes.length > 1) {
2887
- await stage?.multiSelect(newNodes.map((node) => node.id));
2888
- } else {
2889
- await stage?.select(newNodes[0].id);
2890
- }
2891
- return newNode;
2892
- }
2893
- /**
2894
- * 移动当前选中节点位置
2895
- * @param offset 偏移量
2896
- */
2897
- async moveLayer(offset) {
2898
- const root = this.get("root");
2899
- if (!root)
2900
- throw new Error("root为空");
2901
- const parent = this.get("parent");
2902
- if (!parent)
2903
- throw new Error("父节点为空");
2904
- const node = this.get("node");
2905
- if (!node)
2906
- throw new Error("当前节点为空");
2907
- const brothers = parent.items || [];
2908
- const index = brothers.findIndex((item) => `${item.id}` === `${node?.id}`);
2909
- const layout = await this.getLayout(parent, node);
2910
- const isRelative = layout === Layout.RELATIVE;
2911
- let offsetIndex;
2912
- if (offset === LayerOffset.TOP) {
2913
- offsetIndex = isRelative ? 0 : brothers.length;
2914
- } else if (offset === LayerOffset.BOTTOM) {
2915
- offsetIndex = isRelative ? brothers.length : 0;
2916
- } else {
2917
- offsetIndex = index + (isRelative ? -offset : offset);
2918
- }
2919
- if (offsetIndex > 0 && offsetIndex > brothers.length || offsetIndex < 0) {
2920
- return;
2921
- }
2922
- brothers.splice(index, 1);
2923
- brothers.splice(offsetIndex, 0, node);
2924
- const grandparent = this.getParentById(parent.id);
2925
- this.get("stage")?.update({
2926
- config: lodashEs.cloneDeep(vue.toRaw(parent)),
2927
- parentId: grandparent?.id,
2928
- root: lodashEs.cloneDeep(root)
2929
- });
2930
- this.addModifiedNodeId(parent.id);
2931
- this.pushHistoryState();
2932
- this.emit("move-layer", offset);
2933
- }
2934
- /**
2935
- * 移动到指定容器中
2936
- * @param config 需要移动的节点
2937
- * @param targetId 容器ID
2938
- */
2939
- async moveToContainer(config, targetId) {
2940
- const root = this.get("root");
2941
- const { node, parent } = this.getNodeInfo(config.id, false);
2942
- const target = this.getNodeById(targetId, false);
2943
- const stage = this.get("stage");
2944
- if (root && node && parent && stage) {
2945
- const index = getNodeIndex(node.id, parent);
2946
- parent.items?.splice(index, 1);
2947
- await stage.remove({ id: node.id, parentId: parent.id, root: lodashEs.cloneDeep(root) });
2948
- const layout = await this.getLayout(target);
2949
- const newConfig = lodashEs.mergeWith(lodashEs.cloneDeep(node), config, (objValue, srcValue) => {
2950
- if (Array.isArray(srcValue)) {
2951
- 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
+ ]
2952
3088
  }
2953
- });
2954
- newConfig.style = getInitPositionStyle(newConfig.style, layout);
2955
- target.items.push(newConfig);
2956
- await stage.select(targetId);
2957
- const targetParent = this.getParentById(target.id);
2958
- await stage.update({ config: lodashEs.cloneDeep(target), parentId: targetParent?.id, root: lodashEs.cloneDeep(root) });
2959
- await this.select(newConfig);
2960
- stage.select(newConfig.id);
2961
- this.addModifiedNodeId(target.id);
2962
- this.addModifiedNodeId(parent.id);
2963
- this.pushHistoryState();
2964
- return newConfig;
2965
- }
2966
- }
2967
- async dragTo(config, targetParent, targetIndex) {
2968
- if (!targetParent || !Array.isArray(targetParent.items))
2969
- return;
2970
- const { parent, node: curNode } = this.getNodeInfo(config.id, false);
2971
- if (!parent || !curNode)
2972
- throw new Error("找不要删除的节点");
2973
- const index = getNodeIndex(curNode.id, parent);
2974
- if (typeof index !== "number" || index === -1)
2975
- throw new Error("找不要删除的节点");
2976
- if (parent.id === targetParent.id) {
2977
- if (index === targetIndex)
2978
- return;
2979
- if (index < targetIndex) {
2980
- targetIndex -= 1;
2981
- }
2982
- }
2983
- const layout = await this.getLayout(parent);
2984
- const newLayout = await this.getLayout(targetParent);
2985
- if (newLayout !== layout) {
2986
- setLayout(config, newLayout);
2987
- }
2988
- parent.items?.splice(index, 1);
2989
- targetParent.items?.splice(targetIndex, 0, config);
2990
- const page = this.get("page");
2991
- const root = this.get("root");
2992
- const stage = this.get("stage");
2993
- if (stage && page && root) {
2994
- stage.update({
2995
- config: lodashEs.cloneDeep(page),
2996
- parentId: root.id,
2997
- root: lodashEs.cloneDeep(root)
2998
- });
2999
- }
3000
- this.addModifiedNodeId(config.id);
3001
- this.addModifiedNodeId(parent.id);
3002
- this.pushHistoryState();
3003
- this.emit("drag-to", { index, targetIndex, config, parent, targetParent });
3004
- }
3005
- /**
3006
- * 撤销当前操作
3007
- * @returns 上一次数据
3008
- */
3009
- async undo() {
3010
- const value = historyService.undo();
3011
- await this.changeHistoryState(value);
3012
- return value;
3013
- }
3014
- /**
3015
- * 恢复到下一步
3016
- * @returns 下一步数据
3017
- */
3018
- async redo() {
3019
- const value = historyService.redo();
3020
- await this.changeHistoryState(value);
3021
- return value;
3022
- }
3023
- async move(left, top) {
3024
- const node = vue.toRaw(this.get("node"));
3025
- if (!node || utils.isPage(node))
3026
- return;
3027
- const { style, id, type } = node;
3028
- if (!style || !["absolute", "fixed"].includes(style.position))
3029
- return;
3030
- const update = (style2) => this.update({
3031
- id,
3032
- type,
3033
- style: style2
3034
- });
3035
- if (top) {
3036
- if (utils.isNumber(style.top)) {
3037
- update({
3038
- ...style,
3039
- top: Number(style.top) + Number(top),
3040
- bottom: ""
3041
- });
3042
- } else if (utils.isNumber(style.bottom)) {
3043
- update({
3044
- ...style,
3045
- bottom: Number(style.bottom) - Number(top),
3046
- top: ""
3047
- });
3048
- }
3089
+ ]
3049
3090
  }
3050
- if (left) {
3051
- if (utils.isNumber(style.left)) {
3052
- update({
3053
- ...style,
3054
- left: Number(style.left) + Number(left),
3055
- right: ""
3056
- });
3057
- } else if (utils.isNumber(style.right)) {
3058
- update({
3059
- ...style,
3060
- right: Number(style.right) - Number(left),
3061
- left: ""
3062
- });
3063
- }
3091
+ ]
3092
+ };
3093
+ const eventTabConfig = {
3094
+ title: "事件",
3095
+ items: [
3096
+ {
3097
+ name: "events",
3098
+ src: "component",
3099
+ labelWidth: "100px",
3100
+ type: "event-select"
3064
3101
  }
3065
- }
3066
- resetState() {
3067
- this.set("root", null);
3068
- this.set("node", null);
3069
- this.set("nodes", []);
3070
- this.set("page", null);
3071
- this.set("parent", null);
3072
- this.set("stage", null);
3073
- this.set("highlightNode", null);
3074
- this.set("modifiedNodeIds", /* @__PURE__ */ new Map());
3075
- this.set("pageLength", 0);
3076
- }
3077
- destroy() {
3078
- this.removeAllListeners();
3079
- this.resetState();
3080
- this.removeAllPlugins();
3081
- }
3082
- resetModifiedNodeId() {
3083
- this.get("modifiedNodeIds").clear();
3084
- }
3085
- usePlugin(options) {
3086
- super.usePlugin(options);
3087
- }
3088
- addModifiedNodeId(id) {
3089
- if (!this.isHistoryStateChange) {
3090
- 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"
3091
3117
  }
3092
- }
3093
- pushHistoryState() {
3094
- const curNode = lodashEs.cloneDeep(vue.toRaw(this.get("node")));
3095
- const page = this.get("page");
3096
- if (!this.isHistoryStateChange && curNode && page) {
3097
- historyService.push({
3098
- data: lodashEs.cloneDeep(vue.toRaw(page)),
3099
- modifiedNodeIds: this.get("modifiedNodeIds"),
3100
- nodeId: curNode.id
3101
- });
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
+ ]
3102
3213
  }
3103
- this.isHistoryStateChange = false;
3104
- }
3105
- async changeHistoryState(value) {
3106
- if (!value)
3107
- return;
3108
- this.isHistoryStateChange = true;
3109
- await this.update(value.data);
3110
- this.set("modifiedNodeIds", value.modifiedNodeIds);
3111
- setTimeout(async () => {
3112
- if (!value.nodeId)
3113
- return;
3114
- await this.select(value.nodeId);
3115
- this.get("stage")?.select(value.nodeId);
3116
- }, 0);
3117
- 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
+ ]
3118
3248
  }
3119
- async toggleFixedPosition(dist, src, root) {
3120
- const newConfig = lodashEs.cloneDeep(dist);
3121
- if (!utils.isPop(src) && newConfig.style?.position) {
3122
- if (isFixed(newConfig) && !isFixed(src)) {
3123
- newConfig.style = change2Fixed(newConfig, root);
3124
- } else if (!isFixed(newConfig) && isFixed(src)) {
3125
- newConfig.style = await Fixed2Other(newConfig, root, this.getLayout);
3126
- }
3127
- }
3128
- return newConfig;
3249
+ ];
3250
+
3251
+ const log = (...args) => {
3252
+ if (process.env.NODE_ENV === "development") {
3253
+ console.log("magic editor: ", ...args);
3129
3254
  }
3130
- selectedConfigExceptionHandler(config) {
3131
- let id;
3132
- if (typeof config === "string" || typeof config === "number") {
3133
- id = config;
3134
- } else {
3135
- id = config.id;
3136
- }
3137
- if (!id) {
3138
- throw new Error("没有ID,无法选中");
3139
- }
3140
- const { node, parent, page } = this.getNodeInfo(id);
3141
- if (!node)
3142
- throw new Error("获取不到组件信息");
3143
- if (node.id === this.state.root?.id) {
3144
- throw new Error("不能选根节点");
3145
- }
3146
- return {
3147
- node,
3148
- parent,
3149
- page
3150
- };
3255
+ };
3256
+ const info = (...args) => {
3257
+ if (process.env.NODE_ENV === "development") {
3258
+ console.info("magic editor: ", ...args);
3151
3259
  }
3152
- }
3153
- const editorService = new Editor();
3154
-
3155
- const beforePaste = (position, config) => {
3156
- if (!config[0]?.style)
3157
- return config;
3158
- const curNode = editorService.get("node");
3159
- const { left: referenceLeft, top: referenceTop } = config[0].style;
3160
- const pasteConfigs = config.map((configItem) => {
3161
- const { offsetX = 0, offsetY = 0, ...positionClone } = position;
3162
- let pastePosition = positionClone;
3163
- if (!lodashEs.isEmpty(pastePosition) && curNode?.items) {
3164
- pastePosition = getPositionInContainer(pastePosition, curNode.id);
3165
- }
3166
- if (pastePosition.left && configItem.style?.left) {
3167
- pastePosition.left = configItem.style.left - referenceLeft + pastePosition.left;
3168
- }
3169
- if (pastePosition.top && configItem.style?.top) {
3170
- pastePosition.top = configItem.style?.top - referenceTop + pastePosition.top;
3171
- }
3172
- const pasteConfig = propsService.setNewItemId(configItem, false);
3173
- if (pasteConfig.style) {
3174
- const { left, top } = pasteConfig.style;
3175
- if (typeof left === "number" || !!left && !isNaN(Number(left))) {
3176
- pasteConfig.style.left = Number(left) + offsetX;
3177
- }
3178
- if (typeof top === "number" || !!top && !isNaN(Number(top))) {
3179
- pasteConfig.style.top = Number(top) + offsetY;
3180
- }
3181
- pasteConfig.style = {
3182
- ...pasteConfig.style,
3183
- ...pastePosition
3184
- };
3185
- }
3186
- const root = editorService.get("root");
3187
- if ((utils.isPage(pasteConfig) || utils.isPageFragment(pasteConfig)) && root) {
3188
- pasteConfig.name = generatePageNameByApp(root, utils.isPage(pasteConfig) ? schema.NodeType.PAGE : schema.NodeType.PAGE_FRAGMENT);
3189
- }
3190
- return pasteConfig;
3191
- });
3192
- return pasteConfigs;
3193
3260
  };
3194
- const getPositionInContainer = (position = {}, id) => {
3195
- let { left = 0, top = 0 } = position;
3196
- const parentEl = editorService.get("stage")?.renderer?.contentWindow?.document.getElementById(`${id}`);
3197
- const parentElRect = parentEl?.getBoundingClientRect();
3198
- left = left - (parentElRect?.left || 0);
3199
- top = top - (parentElRect?.top || 0);
3200
- return {
3201
- left,
3202
- top
3203
- };
3261
+ const warn = (...args) => {
3262
+ if (process.env.NODE_ENV === "development") {
3263
+ console.warn("magic editor: ", ...args);
3264
+ }
3204
3265
  };
3205
- const getAddParent = (node) => {
3206
- const curNode = editorService.get("node");
3207
- let parentNode;
3208
- if (utils.isPage(node)) {
3209
- parentNode = editorService.get("root");
3210
- } else if (curNode?.items) {
3211
- parentNode = curNode;
3212
- } else if (curNode?.id) {
3213
- parentNode = editorService.getParentById(curNode.id, false);
3266
+ const debug = (...args) => {
3267
+ if (process.env.NODE_ENV === "development") {
3268
+ console.debug("magic editor: ", ...args);
3214
3269
  }
3215
- return parentNode;
3216
3270
  };
3217
- const getDefaultConfig = async (addNode, parentNode) => {
3218
- const { type, inputEvent, ...config } = addNode;
3219
- const layout = await editorService.getLayout(vue.toRaw(parentNode), addNode);
3220
- const newNode = { ...vue.toRaw(await propsService.getPropsValue(type, config)) };
3221
- newNode.style = getInitPositionStyle(newNode.style, layout);
3222
- return newNode;
3271
+ const error = (...args) => {
3272
+ if (process.env.NODE_ENV === "development") {
3273
+ console.error("magic editor: ", ...args);
3274
+ }
3223
3275
  };
3224
3276
 
3225
3277
  const _sfc_main$P = /* @__PURE__ */ vue.defineComponent({
@@ -3609,7 +3661,6 @@
3609
3661
  if (!codeId.value)
3610
3662
  return;
3611
3663
  await codeBlockService?.setCodeDslById(codeId.value, values);
3612
- design.tMagicMessage.success("代码块保存成功");
3613
3664
  codeBlockEditor.value?.hide();
3614
3665
  };
3615
3666
  return {
@@ -3811,7 +3862,7 @@
3811
3862
  const stage = new StageCore({
3812
3863
  render: stageOptions.render,
3813
3864
  runtimeUrl: stageOptions.runtimeUrl,
3814
- zoom: zoom.value,
3865
+ zoom: stageOptions.zoom ?? zoom.value,
3815
3866
  autoScrollIntoView: stageOptions.autoScrollIntoView,
3816
3867
  isContainer: stageOptions.isContainer,
3817
3868
  containerHighlightClassName: stageOptions.containerHighlightClassName,
@@ -10739,7 +10790,7 @@
10739
10790
 
10740
10791
  const canUsePluginMethods$1 = {
10741
10792
  async: ["setCodeDslById", "setEditStatus", "setCombineIds", "setUndeleteableList", "deleteCodeDslByIds"],
10742
- sync: []
10793
+ sync: ["setCodeDslByIdSync"]
10743
10794
  };
10744
10795
  class CodeBlock extends BaseService {
10745
10796
  state = vue.reactive({
@@ -10750,7 +10801,10 @@
10750
10801
  paramsColConfig: void 0
10751
10802
  });
10752
10803
  constructor() {
10753
- 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
+ ]);
10754
10808
  }
10755
10809
  /**
10756
10810
  * 设置活动的代码块dsl数据源
@@ -10790,17 +10844,29 @@
10790
10844
  * @returns {void}
10791
10845
  */
10792
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) {
10793
10858
  const codeDsl = this.getCodeDsl();
10794
10859
  if (!codeDsl) {
10795
10860
  throw new Error("dsl中没有codeBlocks");
10796
10861
  }
10797
- const codeConfigProcessed = codeConfig;
10798
- if (codeConfig.content) {
10862
+ if (codeDsl[id] && !force)
10863
+ return;
10864
+ const codeConfigProcessed = lodashEs.cloneDeep(codeConfig);
10865
+ if (codeConfigProcessed.content) {
10799
10866
  const parseDSL = getConfig("parseDSL");
10800
- if (typeof codeConfig.content === "string") {
10801
- codeConfig.content = parseDSL(codeConfig.content);
10867
+ if (typeof codeConfigProcessed.content === "string") {
10868
+ codeConfigProcessed.content = parseDSL(codeConfigProcessed.content);
10802
10869
  }
10803
- codeConfigProcessed.content = codeConfig.content;
10804
10870
  }
10805
10871
  const existContent = codeDsl[id] || {};
10806
10872
  codeDsl[id] = {
@@ -10912,6 +10978,51 @@
10912
10978
  return newId;
10913
10979
  return await this.getUniqueId();
10914
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
+ }
10915
11026
  resetState() {
10916
11027
  this.state.codeDsl = null;
10917
11028
  this.state.editable = true;
@@ -11253,6 +11364,7 @@
11253
11364
  createStage(stageOptions = {}) {
11254
11365
  return useStage({
11255
11366
  ...stageOptions,
11367
+ zoom: 1,
11256
11368
  runtimeUrl: "",
11257
11369
  autoScrollIntoView: false,
11258
11370
  render: async (stage) => {
@@ -11682,7 +11794,17 @@
11682
11794
  dataSourceService.on("update", dataSourceUpdateHandler);
11683
11795
  dataSourceService.on("remove", dataSourceRemoveHandler);
11684
11796
  if (props.collectorOptions && !depService.hasTarget(dep.DepTargetType.RELATED_COMP_WHEN_COPY)) {
11685
- 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
+ );
11686
11808
  }
11687
11809
  vue.onBeforeUnmount(() => {
11688
11810
  depService.off("add-target", targetAddHandler);
@@ -11733,6 +11855,8 @@
11733
11855
  codeOptions: {},
11734
11856
  disabledDragStart: { type: Boolean },
11735
11857
  collectorOptions: {},
11858
+ collectorOptionsForCode: {},
11859
+ collectorOptionsForDataSource: {},
11736
11860
  guidesOptions: {},
11737
11861
  disabledMultiSelect: { type: Boolean },
11738
11862
  disabledPageFragment: { type: Boolean },
@@ -11943,6 +12067,8 @@
11943
12067
  get: () => dep.DepTargetType
11944
12068
  });
11945
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;
11946
12072
  exports.COPY_STORAGE_KEY = COPY_STORAGE_KEY;
11947
12073
  exports.CodeBlockEditor = _sfc_main$J;
11948
12074
  exports.CodeBlockList = _sfc_main$l;
@@ -11964,6 +12090,7 @@
11964
12090
  exports.DragType = DragType;
11965
12091
  exports.EventSelect = _sfc_main$E;
11966
12092
  exports.Fixed2Other = Fixed2Other;
12093
+ exports.FloatingBox = _sfc_main$N;
11967
12094
  exports.H_GUIDE_LINE_STORAGE_KEY = H_GUIDE_LINE_STORAGE_KEY;
11968
12095
  exports.Icon = _sfc_main$U;
11969
12096
  exports.KeyBindingCommand = KeyBindingCommand;