mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-07-21 05:27:52 +00:00
refactor(editor): 完善内置 typeMatch 校验规则并规范文件名
- 重命名 typeMatchRules.ts 为 type-match-rules.ts - 为 event-select、display-conds 等字段补充结构校验规则 - advancedTabConfig 的 code-select 字段增加 hookType 校验 - 修复 update 未携带 invalidInfo 时清除对应节点全部来源错误 - 将 code-select/display-conds 字段 typeMatch 触发时机调整为 change
This commit is contained in:
parent
0e4669261f
commit
ce43fface8
@ -93,7 +93,7 @@ const codeConfig = computed<GroupListConfig>(() => ({
|
||||
{ value: HookCodeType.CODE, text: '代码块' },
|
||||
{ value: HookCodeType.DATA_SOURCE_METHOD, text: '数据源方法' },
|
||||
],
|
||||
rules: [{ typeMatch: true, trigger: 'blur' }],
|
||||
rules: [{ typeMatch: true, trigger: 'change' }],
|
||||
defaultValue: HookCodeType.CODE,
|
||||
onChange: (_mForm, v: HookCodeType, { setModel }) => {
|
||||
if (v === HookCodeType.DATA_SOURCE_METHOD) {
|
||||
@ -112,7 +112,7 @@ const codeConfig = computed<GroupListConfig>(() => ({
|
||||
labelWidth: 0,
|
||||
display: (_mForm, { model }) => model.codeType !== HookCodeType.DATA_SOURCE_METHOD,
|
||||
notEditable: () => !codeBlockService.getEditStatus(),
|
||||
rules: [{ typeMatch: true, trigger: 'blur' }],
|
||||
rules: [{ typeMatch: true, trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
type: 'data-source-method-select',
|
||||
@ -121,7 +121,7 @@ const codeConfig = computed<GroupListConfig>(() => ({
|
||||
labelWidth: 0,
|
||||
display: (_mForm, { model }) => model.codeType === HookCodeType.DATA_SOURCE_METHOD,
|
||||
notEditable: () => !dataSourceService.get('editable'),
|
||||
rules: [{ typeMatch: true }],
|
||||
rules: [{ typeMatch: true, trigger: 'change' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@ -98,9 +98,10 @@ const config = computed<GroupListConfig>(() => ({
|
||||
label: '字段',
|
||||
checkStrictly: false,
|
||||
onChange: fieldOnChange,
|
||||
defaultValue: () => [],
|
||||
rules: [
|
||||
{ required: true, trigger: 'blur', message: '请选择字段' },
|
||||
{ typeMatch: true, trigger: 'blur' },
|
||||
{ typeMatch: true, trigger: 'change' },
|
||||
],
|
||||
}
|
||||
: {
|
||||
@ -111,9 +112,10 @@ const config = computed<GroupListConfig>(() => ({
|
||||
checkStrictly: false,
|
||||
dataSourceFieldType: ['string', 'number', 'boolean', 'any'],
|
||||
onChange: fieldOnChange,
|
||||
defaultValue: () => [],
|
||||
rules: [
|
||||
{ required: true, trigger: 'blur', message: '请选择字段' },
|
||||
{ typeMatch: true, trigger: 'blur' },
|
||||
{ typeMatch: true, trigger: 'change' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -124,7 +126,7 @@ const config = computed<GroupListConfig>(() => ({
|
||||
name: 'op',
|
||||
rules: [
|
||||
{ required: true, trigger: 'blur', message: '请选择条件' },
|
||||
{ typeMatch: true, trigger: 'blur' },
|
||||
{ typeMatch: true, trigger: 'change' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@ -167,7 +167,7 @@ const submit = async (
|
||||
changeRecords: eventData?.changeRecords,
|
||||
historySource,
|
||||
// 启用校验联动时,仅校验失败(error 存在)才把错误信息随更新传入 editorService 记录;
|
||||
// CodeEditor 源码保存与表单校验成功均不携带 invalidInfo,保持已有错误状态不变。
|
||||
// 其余情况(含表单校验成功、CodeEditor 源码保存)不携带 invalidInfo,由 editorService 在执行 update 时统一清除该节点错误。
|
||||
...(enablePropsFormValidate && error ? { invalidInfo: { id: newValue.id, source, error: error?.message } } : {}),
|
||||
});
|
||||
} catch (e: any) {
|
||||
|
||||
@ -44,7 +44,7 @@ import StyleSetter from './fields/StyleSetter/Index.vue';
|
||||
import uiSelect from './fields/UISelect.vue';
|
||||
import CodeEditor from './layouts/CodeEditor.vue';
|
||||
import { setEditorConfig } from './utils/config';
|
||||
import { editorTypeMatchRules } from './utils/typeMatchRules';
|
||||
import { editorTypeMatchRules } from './utils/type-match-rules';
|
||||
import Editor from './Editor.vue';
|
||||
import type { EditorInstallOptions } from './type';
|
||||
|
||||
|
||||
@ -815,7 +815,7 @@ class Editor extends BaseService {
|
||||
);
|
||||
|
||||
// 校验错误信息在 pushOpHistory 之前落库,保证历史快照包含本次变更对应的错误状态。
|
||||
this.applyInvalidInfo(invalidInfo);
|
||||
this.applyInvalidInfo(config, invalidInfo);
|
||||
|
||||
if (updateData[0].oldNode?.type !== NodeType.ROOT) {
|
||||
const curNodes = this.get('nodes');
|
||||
@ -1666,10 +1666,21 @@ class Editor extends BaseService {
|
||||
|
||||
/**
|
||||
* 应用一次属性面板提交携带的校验错误信息(在写入历史记录之前调用,使历史快照与本次变更对齐)。
|
||||
* error 非空则记录错误,为空则清除对应来源的错误。
|
||||
* - invalidInfo 为空(调用方未携带):认为本次更新对应的节点已无校验错误,清除其全部来源的错误;
|
||||
* - error 非空则记录错误,为空则清除对应来源的错误。
|
||||
*/
|
||||
private applyInvalidInfo(invalidInfo?: { id: Id; source: NodeInvalidSource; error?: string }) {
|
||||
if (!invalidInfo) return;
|
||||
private applyInvalidInfo(
|
||||
config: MNode | MNode[],
|
||||
invalidInfo?: { id: Id; source: NodeInvalidSource; error?: string },
|
||||
) {
|
||||
if (!invalidInfo) {
|
||||
// 调用方未携带 invalidInfo:本次更新对应节点不应再保留校验错误,清除其全部来源。
|
||||
const ids = (Array.isArray(config) ? config : [config])
|
||||
.map((node) => node.id)
|
||||
.filter((id): id is Id => id !== undefined);
|
||||
ids.forEach((id) => this.deleteInvalidNode(id));
|
||||
return;
|
||||
}
|
||||
const { id, source, error } = invalidInfo;
|
||||
if (error) {
|
||||
this.setInvalidNode(id, source, error);
|
||||
|
||||
@ -30,6 +30,6 @@ export * from './undo-redo';
|
||||
export * from './indexed-db';
|
||||
export * from './history';
|
||||
export * from './const';
|
||||
export * from './typeMatchRules';
|
||||
export * from './type-match-rules';
|
||||
export * from './event';
|
||||
export { default as loadMonaco } from './monaco-editor';
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
HookType,
|
||||
NODE_CONDS_KEY,
|
||||
NODE_CONDS_RESULT_KEY,
|
||||
NODE_DISABLE_CODE_BLOCK_KEY,
|
||||
@ -182,7 +183,17 @@ export const advancedTabConfig: TabPaneConfig = {
|
||||
labelPosition: 'top',
|
||||
type: 'code-select',
|
||||
extra: '组件初始化时执行',
|
||||
rules: [{ typeMatch: true }],
|
||||
rules: [
|
||||
{ typeMatch: true, trigger: 'change' },
|
||||
{
|
||||
validator: ({ value, callback }: any) => {
|
||||
if (value && value.hookType !== HookType.CODE) {
|
||||
return callback('hookType 必须是 code');
|
||||
}
|
||||
callback();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'mounted',
|
||||
@ -190,7 +201,17 @@ export const advancedTabConfig: TabPaneConfig = {
|
||||
labelPosition: 'top',
|
||||
type: 'code-select',
|
||||
extra: '组件挂载到dom时执行',
|
||||
rules: [{ typeMatch: true }],
|
||||
rules: [
|
||||
{ typeMatch: true, trigger: 'change' },
|
||||
{
|
||||
validator: ({ value, callback }: any) => {
|
||||
if (value && value.hookType !== HookType.CODE) {
|
||||
return callback('hookType 必须是 code');
|
||||
}
|
||||
callback();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'display',
|
||||
@ -198,7 +219,17 @@ export const advancedTabConfig: TabPaneConfig = {
|
||||
extra: '控制组件是否渲染,关系的代码块返回值为false时不渲染',
|
||||
labelPosition: 'top',
|
||||
type: 'code-select',
|
||||
rules: [{ typeMatch: true }],
|
||||
rules: [
|
||||
{ typeMatch: true, trigger: 'change' },
|
||||
{
|
||||
validator: ({ value, callback }: any) => {
|
||||
if (value && value.hookType !== HookType.CODE) {
|
||||
return callback('hookType 必须是 code');
|
||||
}
|
||||
callback();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
import type { DataSourceFieldType, DataSourceSchema, Id } from '@tmagic/core';
|
||||
import { HookCodeType, HookType, NodeType } from '@tmagic/core';
|
||||
import { NodeType } from '@tmagic/core';
|
||||
import type { TypeMatchValidateContext, TypeMatchValidator } from '@tmagic/form';
|
||||
import {
|
||||
DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX,
|
||||
@ -376,7 +376,7 @@ const validateDataSourceSelect: TypeMatchValidator = (value, { message, props })
|
||||
};
|
||||
|
||||
const validateCodeSelect: TypeMatchValidator = (value, { message }) => {
|
||||
if (!isPlainObject(value) || value.hookType !== HookType.CODE || !Array.isArray(value.hookData)) {
|
||||
if (!isPlainObject(value) || !Array.isArray(value.hookData)) {
|
||||
return defaultMessage(message, `${value}类型不合法`);
|
||||
}
|
||||
|
||||
@ -387,29 +387,9 @@ const validateCodeSelect: TypeMatchValidator = (value, { message }) => {
|
||||
return defaultMessage(message, '钩子项结构不合法');
|
||||
}
|
||||
|
||||
if (item.codeType !== HookCodeType.CODE && item.codeType !== HookCodeType.DATA_SOURCE_METHOD) {
|
||||
return defaultMessage(message, '钩子项结构不合法');
|
||||
}
|
||||
|
||||
if (typeof item.params !== 'undefined' && !isPlainObject(item.params)) {
|
||||
return defaultMessage(message, '钩子项结构不合法');
|
||||
}
|
||||
|
||||
if (item.codeType === HookCodeType.CODE) {
|
||||
if (typeof item.codeId !== 'string') {
|
||||
return defaultMessage(message, '钩子项结构不合法');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// DATA_SOURCE_METHOD:仅校验元组形态,存在性交给单元格
|
||||
if (
|
||||
!Array.isArray(item.codeId) ||
|
||||
item.codeId.length !== 2 ||
|
||||
item.codeId.some((part: any) => typeof part !== 'string')
|
||||
) {
|
||||
return defaultMessage(message, '钩子项结构不合法');
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@ -77,7 +77,7 @@ describe('plugin install', () => {
|
||||
|
||||
test('install 调用 design/form/table 插件并注册全局组件', async () => {
|
||||
const { registerTypeMatchRules } = await import('@tmagic/form');
|
||||
const { editorTypeMatchRules } = await import('@editor/utils/typeMatchRules');
|
||||
const { editorTypeMatchRules } = await import('@editor/utils/type-match-rules');
|
||||
const { app, components } = buildApp();
|
||||
editorPlugin.install(app, { someOption: true } as any);
|
||||
expect(app.use).toHaveBeenCalledTimes(3);
|
||||
|
||||
@ -1235,12 +1235,12 @@ describe('invalidNodeIds 校验错误状态', () => {
|
||||
expect(editorService.getInvalidNodeInfo(NodeId.NODE_ID)).toBeUndefined();
|
||||
});
|
||||
|
||||
test('update 未携带 invalidInfo 时不改动已有错误状态', async () => {
|
||||
test('update 未携带 invalidInfo 时清除对应节点的全部来源错误', async () => {
|
||||
await editorService.select(NodeId.PAGE_ID);
|
||||
editorService.setInvalidNode(NodeId.NODE_ID, 'props', 'props 错误');
|
||||
|
||||
await editorService.update({ id: NodeId.NODE_ID, type: 'text', text: 'z' });
|
||||
|
||||
expect(editorService.getInvalidNodeInfo(NodeId.NODE_ID)).toEqual({ props: 'props 错误' });
|
||||
expect(editorService.getInvalidNodeInfo(NodeId.NODE_ID)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@ -21,7 +21,7 @@ import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
import { HookCodeType, HookType, NodeType } from '@tmagic/core';
|
||||
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX, DATA_SOURCE_SET_DATA_METHOD_NAME } from '@tmagic/utils';
|
||||
|
||||
import { ALL_COND_OPS, editorTypeMatchRules } from '@editor/utils/typeMatchRules';
|
||||
import { ALL_COND_OPS, editorTypeMatchRules } from '@editor/utils/type-match-rules';
|
||||
|
||||
const codeDslState = vi.hoisted(() => ({ value: null as Record<string, any> | null }));
|
||||
const dataSourcesState = vi.hoisted(() => ({ value: [] as any[] }));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user