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