@tmagic/editor 1.8.0-beta.23 → 1.8.0-beta.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +5 -4
- package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +3 -2
- package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +8 -5
- package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +10 -6
- package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +6 -4
- package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +6 -4
- package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +12 -7
- package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/hooks/use-compare-form.js +1 -2
- package/dist/es/index.js +3 -3
- package/dist/es/plugin.js +9 -1
- package/dist/es/services/editor.js +43 -37
- package/dist/es/services/props.js +1 -1
- package/dist/es/style.css +4 -0
- package/dist/es/utils/event.js +1 -1
- package/dist/es/utils/props.js +1 -29
- package/dist/es/utils/type-match-rules.js +21 -20
- package/dist/style.css +4 -0
- package/dist/themes/magic-admin.css +4 -0
- package/dist/tmagic-editor.umd.cjs +412 -398
- package/package.json +8 -8
- package/src/fields/CodeSelectCol.vue +4 -2
- package/src/fields/CondOpSelect.vue +3 -2
- package/src/fields/DataSourceFieldSelect/Index.vue +9 -4
- package/src/fields/DataSourceFields.vue +6 -0
- package/src/fields/DataSourceMethodSelect.vue +4 -1
- package/src/fields/DataSourceMethods.vue +11 -2
- package/src/fields/DataSourceMocks.vue +10 -1
- package/src/fields/DisplayConds.vue +11 -7
- package/src/fields/EventSelect.vue +3 -3
- package/src/fields/KeyValue.vue +4 -3
- package/src/fields/StyleSetter/pro/Font.vue +2 -2
- package/src/hooks/use-compare-form.ts +1 -4
- package/src/plugin.ts +9 -1
- package/src/services/editor.ts +53 -85
- package/src/type.ts +23 -0
- package/src/utils/event.ts +2 -2
- package/src/utils/props.ts +0 -32
- package/src/utils/type-match-rules.ts +29 -28
- package/types/index.d.ts +39 -61
package/src/services/editor.ts
CHANGED
|
@@ -56,6 +56,7 @@ import type {
|
|
|
56
56
|
StepValue,
|
|
57
57
|
StoreState,
|
|
58
58
|
StoreStateKey,
|
|
59
|
+
UpdateOptions,
|
|
59
60
|
} from '@editor/type';
|
|
60
61
|
import { canUsePluginMethods, LayerOffset, Layout } from '@editor/type';
|
|
61
62
|
import {
|
|
@@ -83,6 +84,10 @@ import { beforePaste, getAddParent } from '@editor/utils/operator';
|
|
|
83
84
|
|
|
84
85
|
type MoveItem = { node: MNode; parent: MContainer; pageForOp: { name: string; id: Id } | null };
|
|
85
86
|
|
|
87
|
+
/** 历史插回时把记录的下标收敛到 [0, length],越界(含未记录)一律追加到末尾 */
|
|
88
|
+
const clampIndex = (index: number | undefined, length: number): number =>
|
|
89
|
+
typeof index === 'number' && index >= 0 && index <= length ? index : length;
|
|
90
|
+
|
|
86
91
|
/**
|
|
87
92
|
* 把「变更前后节点快照」列表归一成 update 类型的 {@link StepDiffItem} 列表,供 {@link StepValue.diff} 使用。
|
|
88
93
|
* `changeRecords` 来自 form 端的 propPath/value 列表,撤销/重做时只对这些 propPath 做局部更新;
|
|
@@ -126,6 +131,11 @@ class Editor extends BaseService {
|
|
|
126
131
|
* 普通操作不会读取它,调用前由 *AndGetHistoryId 重置为 null。
|
|
127
132
|
*/
|
|
128
133
|
private lastPushedHistoryId: string | null = null;
|
|
134
|
+
/**
|
|
135
|
+
* 上一次 doAdd 的插入记忆:nodeId 为插入的节点,selectedId 为当时的选中节点。
|
|
136
|
+
* 仅在选中节点未变化时复用(连续 add / doNotSelect / 批量粘贴),选中变化后自动失效,无需手动清理。
|
|
137
|
+
*/
|
|
138
|
+
private lastAdded: { selectedId: Id; nodeId: Id } | null = null;
|
|
129
139
|
|
|
130
140
|
constructor() {
|
|
131
141
|
super(
|
|
@@ -404,7 +414,7 @@ class Editor extends BaseService {
|
|
|
404
414
|
this.set('highlightNode', null);
|
|
405
415
|
}
|
|
406
416
|
|
|
407
|
-
public async doAdd(node: MNode, parent: MContainer): Promise<MNode> {
|
|
417
|
+
public async doAdd(node: MNode, parent: MContainer, _options: DslOpOptions = {}): Promise<MNode> {
|
|
408
418
|
const root = this.get('root');
|
|
409
419
|
|
|
410
420
|
if (!root) throw new Error('root为空');
|
|
@@ -418,13 +428,16 @@ class Editor extends BaseService {
|
|
|
418
428
|
throw new Error('app下不能添加组件');
|
|
419
429
|
}
|
|
420
430
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
431
|
+
// 连续 add 时接在上一个新增节点之后,否则接在当前选中节点之后;
|
|
432
|
+
// 锚点节点可能已不在 items 中(被删 / DSL 整体替换),回退到当前选中节点,都不在则追加到末尾
|
|
433
|
+
const anchorId = this.lastAdded?.selectedId === curNode.id ? this.lastAdded.nodeId : curNode.id;
|
|
434
|
+
const anchorIndex = isPageOrFragment(node)
|
|
435
|
+
? -1
|
|
436
|
+
: Math.max(getNodeIndex(anchorId, parent), getNodeIndex(curNode.id, parent));
|
|
437
|
+
const insertIndex = anchorIndex < 0 ? parent.items.length : anchorIndex + 1;
|
|
438
|
+
|
|
439
|
+
parent.items.splice(insertIndex, 0, node);
|
|
440
|
+
this.lastAdded = { selectedId: curNode.id, nodeId: node.id };
|
|
428
441
|
|
|
429
442
|
const layout = await this.getLayout(toRaw(parent), node as MNode);
|
|
430
443
|
node.style = getInitPositionStyle(node.style, layout);
|
|
@@ -434,6 +447,7 @@ class Editor extends BaseService {
|
|
|
434
447
|
parent: cloneDeep(parent),
|
|
435
448
|
parentId: parent.id,
|
|
436
449
|
root: cloneDeep(root),
|
|
450
|
+
index: insertIndex,
|
|
437
451
|
});
|
|
438
452
|
|
|
439
453
|
const newStyle = fixNodePosition(node, parent, stage);
|
|
@@ -452,24 +466,23 @@ class Editor extends BaseService {
|
|
|
452
466
|
* 向指点容器添加组件节点
|
|
453
467
|
* @param addConfig 将要添加的组件节点配置
|
|
454
468
|
* @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
|
|
455
|
-
* @param options
|
|
456
|
-
* @param options.doNotSelect 添加后是否不更新当前选中节点(默认 false,添加后会选中新增的节点)
|
|
457
|
-
* @param options.doNotSwitchPage 添加后是否不切换当前页面(默认 false;新增页面 / 跨页新增时为 true 会跳过会引发页面切换的选中操作)
|
|
458
|
-
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
469
|
+
* @param options 可选配置,见 {@link DslOpOptions}
|
|
459
470
|
* @returns 添加后的节点
|
|
460
471
|
*/
|
|
461
472
|
public async add(
|
|
462
473
|
addNode: AddMNode | MNode[],
|
|
463
474
|
parent?: MContainer | null,
|
|
464
|
-
{
|
|
475
|
+
options: DslOpOptions = {},
|
|
476
|
+
): Promise<MNode | MNode[]> {
|
|
477
|
+
this.captureSelectionBeforeOp();
|
|
478
|
+
|
|
479
|
+
const {
|
|
465
480
|
doNotSelect = false,
|
|
466
481
|
doNotSwitchPage = false,
|
|
467
482
|
doNotPushHistory = false,
|
|
468
483
|
historyDescription,
|
|
469
484
|
historySource,
|
|
470
|
-
}
|
|
471
|
-
): Promise<MNode | MNode[]> {
|
|
472
|
-
this.captureSelectionBeforeOp();
|
|
485
|
+
} = options;
|
|
473
486
|
|
|
474
487
|
const stage = this.get('stage');
|
|
475
488
|
|
|
@@ -485,17 +498,15 @@ class Editor extends BaseService {
|
|
|
485
498
|
addNodes.push(...addNode);
|
|
486
499
|
}
|
|
487
500
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}),
|
|
498
|
-
);
|
|
501
|
+
// 必须串行:每个节点的插入位置依赖上一个已插入的节点,并行时插入顺序会受
|
|
502
|
+
// doAdd 前置耗时(如异步 beforeDoAdd 插件钩子)影响而错乱
|
|
503
|
+
const newNodes: MNode[] = [];
|
|
504
|
+
for (const node of addNodes) {
|
|
505
|
+
const root = this.get('root');
|
|
506
|
+
const parentNode = isPageOrFragment(node) && root ? root : (parent ?? getAddParent(node));
|
|
507
|
+
if (!parentNode) throw new Error('未找到父元素');
|
|
508
|
+
newNodes.push(await this.doAdd(node, parentNode, options));
|
|
509
|
+
}
|
|
499
510
|
|
|
500
511
|
if (newNodes.length > 1) {
|
|
501
512
|
// 多选时只要任一新增节点位于非当前页面,触发的 multiSelect 就会引起页面切换
|
|
@@ -654,16 +665,8 @@ class Editor extends BaseService {
|
|
|
654
665
|
* @param options.doNotSwitchPage 删除后是否不切换当前页面(默认 false;删除页面 / 页面片段时为 true 会跳过自动切换到首个剩余页面)
|
|
655
666
|
* @param options.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
656
667
|
*/
|
|
657
|
-
public async remove(
|
|
658
|
-
|
|
659
|
-
{
|
|
660
|
-
doNotSelect = false,
|
|
661
|
-
doNotSwitchPage = false,
|
|
662
|
-
doNotPushHistory = false,
|
|
663
|
-
historyDescription,
|
|
664
|
-
historySource,
|
|
665
|
-
}: DslOpOptions = {},
|
|
666
|
-
): Promise<void> {
|
|
668
|
+
public async remove(nodeOrNodeList: MNode | MNode[], options: DslOpOptions = {}): Promise<void> {
|
|
669
|
+
const { doNotPushHistory = false, historyDescription, historySource } = options;
|
|
667
670
|
this.captureSelectionBeforeOp();
|
|
668
671
|
|
|
669
672
|
const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
|
|
@@ -690,7 +693,7 @@ class Editor extends BaseService {
|
|
|
690
693
|
}
|
|
691
694
|
}
|
|
692
695
|
|
|
693
|
-
await Promise.all(nodes.map((node) => this.doRemove(node,
|
|
696
|
+
await Promise.all(nodes.map((node) => this.doRemove(node, options)));
|
|
694
697
|
|
|
695
698
|
// 删除节点时同步清理其(含子树)的校验错误记录;置于 pushOpHistory 之前,使历史快照与本次删除对齐。
|
|
696
699
|
this.removeInvalidNodesBySubtree(nodes);
|
|
@@ -720,12 +723,9 @@ class Editor extends BaseService {
|
|
|
720
723
|
|
|
721
724
|
public async doUpdate(
|
|
722
725
|
config: MNode,
|
|
723
|
-
{
|
|
724
|
-
changeRecords = [],
|
|
725
|
-
historySource,
|
|
726
|
-
replace = false,
|
|
727
|
-
}: { changeRecords?: ChangeRecord[]; historySource?: HistoryOpSource; replace?: boolean } = {},
|
|
726
|
+
data: UpdateOptions = {},
|
|
728
727
|
): Promise<{ newNode: MNode; oldNode: MNode; changeRecords?: ChangeRecord[] }> {
|
|
728
|
+
const { changeRecords = [], historySource, replace = false } = data;
|
|
729
729
|
const root = this.get('root');
|
|
730
730
|
if (!root) throw new Error('root为空');
|
|
731
731
|
|
|
@@ -804,34 +804,10 @@ class Editor extends BaseService {
|
|
|
804
804
|
* 更新节点
|
|
805
805
|
* update后会触发依赖收集,收集完后会掉stage.update方法
|
|
806
806
|
* @param config 新的节点配置,配置中需要有id信息
|
|
807
|
-
* @param data
|
|
808
|
-
* @param data.changeRecords 单节点 form 端变更记录(多节点场景下被忽略,使用 changeRecordList)
|
|
809
|
-
* @param data.changeRecordList 多节点 form 端变更记录列表,按 config 数组同序对应每个节点;优先级高于 changeRecords
|
|
810
|
-
* @param data.doNotPushHistory 是否不写入历史记录(默认 false)
|
|
811
|
-
* @param data.historyDescription 入栈时附带的人类可读描述,用于历史面板展示(不影响 undo/redo 行为)
|
|
812
|
-
* @param data.replace 是否整节点替换:为 true 时跳过 mergeWith / toggleFixedPosition / setChildrenLayout,直接用传入配置覆盖(默认 false)
|
|
807
|
+
* @param data 额外数据,见 {@link UpdateOptions}
|
|
813
808
|
* @returns 更新后的节点配置
|
|
814
809
|
*/
|
|
815
|
-
public async update(
|
|
816
|
-
config: MNode | MNode[],
|
|
817
|
-
data: {
|
|
818
|
-
changeRecords?: ChangeRecord[];
|
|
819
|
-
changeRecordList?: ChangeRecord[][];
|
|
820
|
-
doNotPushHistory?: boolean;
|
|
821
|
-
historyDescription?: string;
|
|
822
|
-
historySource?: HistoryOpSource;
|
|
823
|
-
/**
|
|
824
|
-
* 为 true 时不做深合并等变换,直接用传入配置整节点替换现有节点。
|
|
825
|
-
* 适用于源码编辑、历史整节点快照回放等「完整 DSL」场景;默认 false(局部属性更新走 merge)。
|
|
826
|
-
*/
|
|
827
|
-
replace?: boolean;
|
|
828
|
-
/**
|
|
829
|
-
* 属性面板提交时携带的校验错误信息,在写入历史记录之前落库,
|
|
830
|
-
* 使历史快照与本次变更对齐,从而 undo/redo 能正确还原错误标记。
|
|
831
|
-
*/
|
|
832
|
-
invalidInfo?: { id: Id; source: NodeInvalidSource; error?: string };
|
|
833
|
-
} = {},
|
|
834
|
-
): Promise<MNode | MNode[]> {
|
|
810
|
+
public async update(config: MNode | MNode[], data: UpdateOptions = {}): Promise<MNode | MNode[]> {
|
|
835
811
|
this.captureSelectionBeforeOp();
|
|
836
812
|
|
|
837
813
|
const {
|
|
@@ -840,7 +816,6 @@ class Editor extends BaseService {
|
|
|
840
816
|
changeRecords,
|
|
841
817
|
historyDescription,
|
|
842
818
|
historySource,
|
|
843
|
-
replace = false,
|
|
844
819
|
invalidInfo,
|
|
845
820
|
} = data;
|
|
846
821
|
|
|
@@ -851,7 +826,7 @@ class Editor extends BaseService {
|
|
|
851
826
|
const updateData = await Promise.all(
|
|
852
827
|
nodes.map((node, index) => {
|
|
853
828
|
const recordsForNode = changeRecordList ? (changeRecordList[index] ?? []) : (changeRecords ?? []);
|
|
854
|
-
return this.doUpdate(node, { changeRecords: recordsForNode
|
|
829
|
+
return this.doUpdate(node, { ...data, changeRecords: recordsForNode });
|
|
855
830
|
}),
|
|
856
831
|
);
|
|
857
832
|
|
|
@@ -1360,14 +1335,7 @@ class Editor extends BaseService {
|
|
|
1360
1335
|
/** 等价于 {@link update},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
|
|
1361
1336
|
public async updateAndGetHistoryId(
|
|
1362
1337
|
config: MNode | MNode[],
|
|
1363
|
-
data: {
|
|
1364
|
-
changeRecords?: ChangeRecord[];
|
|
1365
|
-
changeRecordList?: ChangeRecord[][];
|
|
1366
|
-
doNotPushHistory?: boolean;
|
|
1367
|
-
historyDescription?: string;
|
|
1368
|
-
historySource?: HistoryOpSource;
|
|
1369
|
-
replace?: boolean;
|
|
1370
|
-
} = {},
|
|
1338
|
+
data: UpdateOptions = {},
|
|
1371
1339
|
): Promise<DslOpWithHistoryIdsResult<MNode | MNode[]>> {
|
|
1372
1340
|
this.lastPushedHistoryId = null;
|
|
1373
1341
|
const result = await this.update(config, data);
|
|
@@ -1987,17 +1955,15 @@ class Editor extends BaseService {
|
|
|
1987
1955
|
const parent = this.getNodeById(parentId, false) as MContainer | null;
|
|
1988
1956
|
if (parent?.items) {
|
|
1989
1957
|
const addedNode = cloneDeep(newSchema);
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
} else {
|
|
1993
|
-
parent.items.push(addedNode);
|
|
1994
|
-
}
|
|
1958
|
+
const insertIndex = clampIndex(index, parent.items.length);
|
|
1959
|
+
parent.items.splice(insertIndex, 0, addedNode);
|
|
1995
1960
|
addedNodes.push(addedNode);
|
|
1996
1961
|
await stage?.add({
|
|
1997
1962
|
config: cloneDeep(newSchema),
|
|
1998
1963
|
parent: cloneDeep(parent),
|
|
1999
1964
|
parentId: parent.id,
|
|
2000
1965
|
root: cloneDeep(root),
|
|
1966
|
+
index: insertIndex,
|
|
2001
1967
|
});
|
|
2002
1968
|
}
|
|
2003
1969
|
}
|
|
@@ -2016,13 +1982,15 @@ class Editor extends BaseService {
|
|
|
2016
1982
|
const parent = this.getNodeById(parentId, false) as MContainer | null;
|
|
2017
1983
|
if (parent?.items) {
|
|
2018
1984
|
const addedNode = cloneDeep(oldSchema);
|
|
2019
|
-
|
|
1985
|
+
const insertIndex = clampIndex(index, parent.items.length);
|
|
1986
|
+
parent.items.splice(insertIndex, 0, addedNode);
|
|
2020
1987
|
addedNodes.push(addedNode);
|
|
2021
1988
|
await stage?.add({
|
|
2022
1989
|
config: cloneDeep(oldSchema),
|
|
2023
1990
|
parent: cloneDeep(parent),
|
|
2024
1991
|
parentId,
|
|
2025
1992
|
root: cloneDeep(root),
|
|
1993
|
+
index: insertIndex,
|
|
2026
1994
|
});
|
|
2027
1995
|
}
|
|
2028
1996
|
}
|
package/src/type.ts
CHANGED
|
@@ -1464,6 +1464,29 @@ export interface DslOpOptions extends HistoryOpOptions {
|
|
|
1464
1464
|
}
|
|
1465
1465
|
// #endregion DslOpOptions
|
|
1466
1466
|
|
|
1467
|
+
// #region UpdateOptions
|
|
1468
|
+
/**
|
|
1469
|
+
* {@link EditorService.update} / {@link EditorService.doUpdate} 的额外数据
|
|
1470
|
+
* - changeRecords: 单节点 form 端变更记录(多节点场景下被忽略,使用 changeRecordList)
|
|
1471
|
+
* - changeRecordList: 多节点 form 端变更记录列表,按 config 数组同序对应每个节点;优先级高于 changeRecords
|
|
1472
|
+
* - replace: 为 true 时跳过 mergeWith / toggleFixedPosition / setChildrenLayout,直接用传入配置整节点替换
|
|
1473
|
+
* - invalidInfo: 属性面板提交时携带的校验错误信息,在写入历史记录之前落库
|
|
1474
|
+
*/
|
|
1475
|
+
export interface UpdateOptions extends HistoryOpOptionsWithChangeRecords {
|
|
1476
|
+
changeRecordList?: ChangeRecord[][];
|
|
1477
|
+
/**
|
|
1478
|
+
* 为 true 时不做深合并等变换,直接用传入配置整节点替换现有节点。
|
|
1479
|
+
* 适用于源码编辑、历史整节点快照回放等「完整 DSL」场景;默认 false(局部属性更新走 merge)。
|
|
1480
|
+
*/
|
|
1481
|
+
replace?: boolean;
|
|
1482
|
+
/**
|
|
1483
|
+
* 属性面板提交时携带的校验错误信息,在写入历史记录之前落库,
|
|
1484
|
+
* 使历史快照与本次变更对齐,从而 undo/redo 能正确还原错误标记。
|
|
1485
|
+
*/
|
|
1486
|
+
invalidInfo?: { id: Id; source: NodeInvalidSource; error?: string };
|
|
1487
|
+
}
|
|
1488
|
+
// #endregion UpdateOptions
|
|
1489
|
+
|
|
1467
1490
|
/** 差异对话框的入参 */
|
|
1468
1491
|
export interface DiffDialogPayload {
|
|
1469
1492
|
/** 表单类别 */
|
package/src/utils/event.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import type { EventOption, Id, MComponent, MContainer } from '@tmagic/core';
|
|
19
|
+
import type { EventOption, Id, MComponent, MContainer, MNode } from '@tmagic/core';
|
|
20
20
|
import type { CascaderOption } from '@tmagic/form';
|
|
21
21
|
import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX, traverseNode } from '@tmagic/utils';
|
|
22
22
|
|
|
@@ -56,7 +56,7 @@ export const getEventNameOptions = (
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
if (src === 'component') {
|
|
59
|
-
const sourceNode = editorService.getNodeById(formValue.id);
|
|
59
|
+
const sourceNode = editorService.getNodeById(formValue.id) || (formValue as MNode);
|
|
60
60
|
let events: EventOption[] | CascaderOption[] = eventsService.getEvent(formValue.type, { node: sourceNode }) || [];
|
|
61
61
|
|
|
62
62
|
if (formValue.type === 'page-fragment-container' && formValue.pageFragmentId) {
|
package/src/utils/props.ts
CHANGED
|
@@ -73,7 +73,6 @@ export const getCondOpOptionsByFieldType = (type: string) => {
|
|
|
73
73
|
|
|
74
74
|
export const styleTabConfig: TabPaneConfig = {
|
|
75
75
|
title: '样式',
|
|
76
|
-
lazy: true,
|
|
77
76
|
display: ({ services }: any) => !(services?.uiService?.get('showStylePanel') ?? true),
|
|
78
77
|
items: [
|
|
79
78
|
{
|
|
@@ -146,7 +145,6 @@ export const styleTabConfig: TabPaneConfig = {
|
|
|
146
145
|
|
|
147
146
|
export const eventTabConfig: TabPaneConfig = {
|
|
148
147
|
title: '事件',
|
|
149
|
-
lazy: true,
|
|
150
148
|
items: [
|
|
151
149
|
{
|
|
152
150
|
name: 'events',
|
|
@@ -160,7 +158,6 @@ export const eventTabConfig: TabPaneConfig = {
|
|
|
160
158
|
|
|
161
159
|
export const advancedTabConfig: TabPaneConfig = {
|
|
162
160
|
title: '高级',
|
|
163
|
-
lazy: true,
|
|
164
161
|
items: [
|
|
165
162
|
{
|
|
166
163
|
name: NODE_DISABLE_CODE_BLOCK_KEY,
|
|
@@ -360,32 +357,3 @@ export const fillConfig = (
|
|
|
360
357
|
|
|
361
358
|
return [tabConfig];
|
|
362
359
|
};
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* 将属性表单配置中「样式」tab-pane 的 `display` 强制置为 `true`。
|
|
366
|
-
*
|
|
367
|
-
* `propsService.getPropsConfig` 返回的样式 tab 默认带有
|
|
368
|
-
* `display: ({ services }) => !(services?.uiService?.get('showStylePanel') ?? true)`,
|
|
369
|
-
* 在对比 / 只读展示场景(CompareForm / ViewForm)下并不需要跟随 uiService 状态隐藏,
|
|
370
|
-
* 这里统一放开,保证样式 tab 始终可见。
|
|
371
|
-
*
|
|
372
|
-
* @param formConfig 组件属性表单配置
|
|
373
|
-
* @returns 处理后的表单配置(不修改入参,返回浅拷贝)
|
|
374
|
-
*/
|
|
375
|
-
export const removeStyleDisplayConfig = (formConfig: FormConfig): FormConfig =>
|
|
376
|
-
formConfig.map((item) => {
|
|
377
|
-
if (!('type' in item)) return item;
|
|
378
|
-
if (item?.type !== 'tab' || !Array.isArray(item.items)) return item;
|
|
379
|
-
|
|
380
|
-
return {
|
|
381
|
-
...item,
|
|
382
|
-
items: item.items.map((tabPane) => {
|
|
383
|
-
if (tabPane?.title !== '样式' || !Array.isArray(tabPane.items)) return tabPane;
|
|
384
|
-
|
|
385
|
-
return {
|
|
386
|
-
...tabPane,
|
|
387
|
-
display: true,
|
|
388
|
-
};
|
|
389
|
-
}),
|
|
390
|
-
};
|
|
391
|
-
});
|
|
@@ -209,7 +209,6 @@ const validateDataSourceFieldPath = (
|
|
|
209
209
|
path: string[],
|
|
210
210
|
options: {
|
|
211
211
|
dataSourceId?: string;
|
|
212
|
-
valueMode?: 'key' | 'value';
|
|
213
212
|
dataSourceFieldType?: DataSourceFieldType[];
|
|
214
213
|
message?: string;
|
|
215
214
|
} = {},
|
|
@@ -219,19 +218,11 @@ const validateDataSourceFieldPath = (
|
|
|
219
218
|
return undefined;
|
|
220
219
|
}
|
|
221
220
|
|
|
222
|
-
|
|
223
|
-
let fieldNames = path;
|
|
224
|
-
|
|
221
|
+
const dsId = options.dataSourceId;
|
|
225
222
|
if (!dsId) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const rawDsId = path[0];
|
|
230
|
-
dsId =
|
|
231
|
-
options.valueMode === 'key' || !`${rawDsId}`.startsWith(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX)
|
|
232
|
-
? `${rawDsId}`
|
|
233
|
-
: removeDataSourceFieldPrefix(`${rawDsId}`);
|
|
234
|
-
fieldNames = path.slice(1);
|
|
223
|
+
// 仅当取值为空数组或首项为空串时命中。表单校验管道里 validateTypeMatch 对空数组会短路,
|
|
224
|
+
// 因此这里只可能来自直接调用 validateDataSourceFieldSelect 的自定义 validator
|
|
225
|
+
return defaultMessage(options.message, '数据源不存在', dataSourceIdSuggestion());
|
|
235
226
|
}
|
|
236
227
|
|
|
237
228
|
const ds = findDataSource(`${dsId}`);
|
|
@@ -239,11 +230,11 @@ const validateDataSourceFieldPath = (
|
|
|
239
230
|
return defaultMessage(options.message, `数据源(${dsId})不存在`, dataSourceIdSuggestion());
|
|
240
231
|
}
|
|
241
232
|
|
|
242
|
-
if (!
|
|
233
|
+
if (!path.length) {
|
|
243
234
|
return undefined;
|
|
244
235
|
}
|
|
245
236
|
|
|
246
|
-
const { field, ok, fields, failedName } = resolveFieldByPath(ds.fields,
|
|
237
|
+
const { field, ok, fields, failedName } = resolveFieldByPath(ds.fields, path);
|
|
247
238
|
if (!ok) {
|
|
248
239
|
return defaultMessage(
|
|
249
240
|
options.message,
|
|
@@ -258,11 +249,15 @@ const validateDataSourceFieldPath = (
|
|
|
258
249
|
}
|
|
259
250
|
|
|
260
251
|
const leafType = field?.type || 'any';
|
|
261
|
-
if (leafType
|
|
262
|
-
|
|
263
|
-
}
|
|
252
|
+
if (leafType !== 'any' && !allowedTypes.includes(leafType)) {
|
|
253
|
+
const fieldName = field?.name || path[path.length - 1];
|
|
264
254
|
|
|
265
|
-
|
|
255
|
+
// 文案已点明当前字段类型与要求类型,无需再列同级可选字段
|
|
256
|
+
return defaultMessage(
|
|
257
|
+
options.message,
|
|
258
|
+
`请选择类型为${allowedTypes.join('或')}的字段,字段(${fieldName})的类型为${leafType}`,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
266
261
|
};
|
|
267
262
|
|
|
268
263
|
const validateDataSourceMethodTuple = (value: any, message?: string): string | undefined => {
|
|
@@ -367,7 +362,7 @@ const validateCondOpSelect: TypeMatchValidator = (value, { message, props }) =>
|
|
|
367
362
|
const parentFields = props.config?.parentFields || [];
|
|
368
363
|
const fieldPath = Array.isArray(props.model?.field) ? props.model.field : [];
|
|
369
364
|
const [id, ...fieldNames] = [...parentFields, ...fieldPath];
|
|
370
|
-
const ds = id ? findDataSource(`${id}`) : undefined;
|
|
365
|
+
const ds = id ? findDataSource(removeDataSourceFieldPrefix(`${id}`)) : undefined;
|
|
371
366
|
const fieldType = getFieldType(ds, fieldNames);
|
|
372
367
|
const allowed = getCondOpsByFieldType(fieldType);
|
|
373
368
|
|
|
@@ -464,7 +459,7 @@ export type ValidateDataSourceFieldSelectOptions = {
|
|
|
464
459
|
/**
|
|
465
460
|
* data-source-field-select 的 typeMatch 校验逻辑,可供自定义 rules.validator 复用。
|
|
466
461
|
*/
|
|
467
|
-
export const
|
|
462
|
+
export const validateDataSourceFieldSelect = (
|
|
468
463
|
value: any,
|
|
469
464
|
context: TypeMatchValidateContext,
|
|
470
465
|
options?: ValidateDataSourceFieldSelectOptions,
|
|
@@ -492,17 +487,22 @@ export const validateDataSourceFieldSelectValue = (
|
|
|
492
487
|
return defaultMessage(message, `${value}类型应为字符串数组`, dataSourceFieldPathSuggestion());
|
|
493
488
|
}
|
|
494
489
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
490
|
+
// 未指定 dataSourceId 时,路径首项为数据源 id,其余为字段名。
|
|
491
|
+
// value 模式的首项带 ds-field:: 前缀、key 模式不带,removeDataSourceFieldPrefix 对后者是幂等的,无需分支。
|
|
492
|
+
let dataSourceId = config.dataSourceId ? `${config.dataSourceId}` : undefined;
|
|
493
|
+
let fieldNames = value;
|
|
494
|
+
if (!dataSourceId) {
|
|
495
|
+
dataSourceId = value.length ? removeDataSourceFieldPrefix(value[0]) : undefined;
|
|
496
|
+
fieldNames = value.slice(1);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return validateDataSourceFieldPath(fieldNames, {
|
|
500
|
+
dataSourceId,
|
|
498
501
|
dataSourceFieldType: config.dataSourceFieldType,
|
|
499
502
|
message,
|
|
500
503
|
});
|
|
501
504
|
};
|
|
502
505
|
|
|
503
|
-
const validateDataSourceFieldSelect: TypeMatchValidator = (value, context) =>
|
|
504
|
-
validateDataSourceFieldSelectValue(value, context);
|
|
505
|
-
|
|
506
506
|
const validateDataSourceSelect: TypeMatchValidator = (value, { message, props }) => {
|
|
507
507
|
const config = props.config || {};
|
|
508
508
|
const dataSources = getDataSources();
|
|
@@ -678,7 +678,8 @@ export const editorTypeMatchRules: Record<string, TypeMatchValidator> = {
|
|
|
678
678
|
'ui-select': validateUiSelect,
|
|
679
679
|
'data-source-input': validateDataSourceInput,
|
|
680
680
|
'data-source-method-select': validateDataSourceMethodSelect,
|
|
681
|
-
|
|
681
|
+
/** 注册进 typeMatch 规则表的入口,收窄为 TypeMatchValidator,避免调用方误传第三个参数 */
|
|
682
|
+
'data-source-field-select': (value, context) => validateDataSourceFieldSelect(value, context),
|
|
682
683
|
'data-source-select': validateDataSourceSelect,
|
|
683
684
|
'code-select': validateCodeSelect,
|
|
684
685
|
'data-source-fields': validateDataSourceFields,
|