fix(editor): 表单组件保持单向数据流

This commit is contained in:
roymondchen 2025-10-31 11:56:32 +08:00
parent 57a634a687
commit 09663c8320
6 changed files with 78 additions and 77 deletions

View File

@ -2,13 +2,15 @@
<div class="m-fields-code-select-col"> <div class="m-fields-code-select-col">
<div class="code-select-container"> <div class="code-select-container">
<!-- 代码块下拉框 --> <!-- 代码块下拉框 -->
<MContainer <MSelect
class="select" class="select"
:config="selectConfig" :config="selectConfig"
:name="name"
:model="model" :model="model"
:size="size" :size="size"
:prop="prop"
@change="onCodeIdChangeHandler" @change="onCodeIdChangeHandler"
></MContainer> ></MSelect>
<!-- 查看/编辑按钮 --> <!-- 查看/编辑按钮 -->
<TMagicButton <TMagicButton
@ -28,6 +30,7 @@
:key="model[name]" :key="model[name]"
:model="model" :model="model"
:size="size" :size="size"
:disabled="disabled"
:params-config="paramsConfig" :params-config="paramsConfig"
@change="onParamsChangeHandler" @change="onParamsChangeHandler"
></CodeParams> ></CodeParams>
@ -48,7 +51,8 @@ import {
type FieldProps, type FieldProps,
filterFunction, filterFunction,
type FormState, type FormState,
MContainer, MSelect,
type SelectConfig,
} from '@tmagic/form'; } from '@tmagic/form';
import CodeParams from '@editor/components/CodeParams.vue'; import CodeParams from '@editor/components/CodeParams.vue';
@ -108,7 +112,7 @@ watch(
}, },
); );
const selectConfig = { const selectConfig: SelectConfig = {
type: 'select', type: 'select',
name: props.name, name: props.name,
disable: props.disabled, disable: props.disabled,
@ -122,33 +126,26 @@ const selectConfig = {
} }
return []; return [];
}, },
onChange: (formState: any, codeId: Id, { setModel, model }: any) => {
// codeIdmodelcodeIdparams
paramsConfig.value = getParamItemsConfig(codeId);
if (paramsConfig.value.length) {
setModel('params', createValues(formState, paramsConfig.value, {}, model.params));
} else {
setModel('params', {});
}
return codeId;
},
}; };
const onCodeIdChangeHandler = (value: any, eventData: ContainerChangeEventData) => { const onCodeIdChangeHandler = (value: any) => {
props.model.params = value.params; // codeIdmodelcodeIdparams
paramsConfig.value = getParamItemsConfig(value);
emit('change', props.model, { const changeRecords = [
changeRecords: eventData.changeRecords?.map((item) => ({ {
prop: `${props.prop.replace(props.name, '')}${item.propPath}`, propPath: props.prop,
value: item.value, value,
})) || [ },
{ ];
propPath: props.prop,
value: value[props.name], changeRecords.push({
}, propPath: props.prop.replace(`${props.name}`, 'params'),
], value: paramsConfig.value.length ? createValues(mForm, paramsConfig.value, {}, props.model.params) : {},
});
emit('change', value, {
changeRecords,
}); });
}; };
@ -156,14 +153,10 @@ const onCodeIdChangeHandler = (value: any, eventData: ContainerChangeEventData)
* 参数值修改更新 * 参数值修改更新
*/ */
const onParamsChangeHandler = (value: any, eventData: ContainerChangeEventData) => { const onParamsChangeHandler = (value: any, eventData: ContainerChangeEventData) => {
props.model.params = value.params; eventData.changeRecords?.forEach((record) => {
emit('change', props.model, { record.propPath = `${props.prop.replace(`${props.name}`, '')}${record.propPath}`;
...eventData,
changeRecords: (eventData.changeRecords || []).map((item) => ({
prop: `${props.prop.replace(props.name, '')}${item.propPath}`,
value: item.value,
})),
}); });
emit('change', props.model[props.name], eventData);
}; };
const editCode = (id: string) => { const editCode = (id: string) => {

View File

@ -1,6 +1,6 @@
<template> <template>
<TMagicSelect <TMagicSelect
v-model="model[name]" :model-value="model[name]"
clearable clearable
filterable filterable
:size="size" :size="size"

View File

@ -1,13 +1,16 @@
<template> <template>
<div class="m-fields-data-source-method-select"> <div class="m-fields-data-source-method-select">
<div class="data-source-method-select-container"> <div class="data-source-method-select-container">
<MContainer <MCascader
class="select" class="select"
:config="cascaderConfig" :config="cascaderConfig"
:model="model" :model="model"
:name="name"
:size="size" :size="size"
:disabled="disabled"
:prop="prop"
@change="onChangeHandler" @change="onChangeHandler"
></MContainer> ></MCascader>
<TMagicTooltip <TMagicTooltip
v-if="model[name] && isCustomMethod && hasDataSourceSidePanel" v-if="model[name] && isCustomMethod && hasDataSourceSidePanel"
@ -22,11 +25,12 @@
<CodeParams <CodeParams
v-if="paramsConfig.length" v-if="paramsConfig.length"
name="params" name="params"
:key="model[name]"
:model="model" :model="model"
:size="size" :size="size"
:disabled="disabled" :disabled="disabled"
:params-config="paramsConfig" :params-config="paramsConfig"
@change="onChangeHandler" @change="onParamsChangeHandler"
></CodeParams> ></CodeParams>
</div> </div>
</template> </template>
@ -38,13 +42,14 @@ import { Edit, View } from '@element-plus/icons-vue';
import type { Id } from '@tmagic/core'; import type { Id } from '@tmagic/core';
import { TMagicButton, TMagicTooltip } from '@tmagic/design'; import { TMagicButton, TMagicTooltip } from '@tmagic/design';
import { import {
type CascaderConfig,
type ContainerChangeEventData,
createValues, createValues,
type DataSourceMethodSelectConfig, type DataSourceMethodSelectConfig,
type FieldProps, type FieldProps,
filterFunction, filterFunction,
type FormState, type FormState,
MContainer, MCascader,
type OnChangeHandlerData,
} from '@tmagic/form'; } from '@tmagic/form';
import CodeParams from '@editor/components/CodeParams.vue'; import CodeParams from '@editor/components/CodeParams.vue';
@ -100,21 +105,6 @@ const getParamItemsConfig = ([dataSourceId, methodName]: [Id, string] = ['', '']
const paramsConfig = ref<CodeParamStatement[]>(getParamItemsConfig(props.model[props.name || 'dataSourceMethod'])); const paramsConfig = ref<CodeParamStatement[]>(getParamItemsConfig(props.model[props.name || 'dataSourceMethod']));
const setParamsConfig = (
dataSourceMethod: [Id, string],
formState: any = {},
setModel: OnChangeHandlerData['setModel'],
) => {
// codeIdmodelcodeIdparams
paramsConfig.value = dataSourceMethod ? getParamItemsConfig(dataSourceMethod) : [];
if (paramsConfig.value.length) {
setModel('params', createValues(formState, paramsConfig.value, {}, props.model.params));
} else {
setModel('params', {});
}
};
const methodsOptions = computed( const methodsOptions = computed(
() => () =>
dataSources.value dataSources.value
@ -132,24 +122,42 @@ const methodsOptions = computed(
})) || [], })) || [],
); );
const cascaderConfig = computed(() => ({ const cascaderConfig = computed<CascaderConfig>(() => ({
type: 'cascader', type: 'cascader',
name: props.name,
options: methodsOptions.value, options: methodsOptions.value,
disable: props.disabled,
onChange: (formState: any, dataSourceMethod: [Id, string], { setModel }: OnChangeHandlerData) => {
setParamsConfig(dataSourceMethod, formState, setModel);
return dataSourceMethod;
},
})); }));
/** /**
* 参数值修改更新 * 参数值修改更新
*/ */
const onChangeHandler = (value: any) => { const onChangeHandler = (value: any) => {
props.model.params = value.params; paramsConfig.value = getParamItemsConfig(value);
emit('change', props.model);
const changeRecords = [
{
propPath: props.prop,
value,
},
];
changeRecords.push({
propPath: props.prop.replace(`${props.name}`, 'params'),
value: paramsConfig.value.length ? createValues(mForm, paramsConfig.value, {}, props.model.params) : {},
});
emit('change', value, {
changeRecords,
});
};
/**
* 参数值修改更新
*/
const onParamsChangeHandler = (value: any, eventData: ContainerChangeEventData) => {
eventData.changeRecords?.forEach((record) => {
record.propPath = `${props.prop.replace(`${props.name}`, '')}${record.propPath}`;
});
emit('change', props.model[props.name], eventData);
}; };
const editCodeHandler = () => { const editCodeHandler = () => {

View File

@ -219,7 +219,6 @@ const targetCompConfig = computed(() => {
display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP, display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP,
onChange: (MForm: FormState, v: string, { setModel }: OnChangeHandlerData) => { onChange: (MForm: FormState, v: string, { setModel }: OnChangeHandlerData) => {
setModel('method', ''); setModel('method', '');
return v;
}, },
}; };
return { ...defaultTargetCompConfig, ...props.config.targetCompConfig }; return { ...defaultTargetCompConfig, ...props.config.targetCompConfig };

View File

@ -2,13 +2,16 @@
<div class="m-fields-page-fragment-select"> <div class="m-fields-page-fragment-select">
<div class="page-fragment-select-container"> <div class="page-fragment-select-container">
<!-- 页面片下拉框 --> <!-- 页面片下拉框 -->
<m-form-container <MSelect
class="select" class="select"
:config="selectConfig" :config="selectConfig"
:model="model" :model="model"
:name="name"
:size="size" :size="size"
:prop="prop"
:disabled="disabled"
@change="changeHandler" @change="changeHandler"
></m-form-container> ></MSelect>
<!-- 编辑按钮 --> <!-- 编辑按钮 -->
<Icon v-if="model[name]" class="icon" :icon="Edit" @click="editPageFragment(model[name])"></Icon> <Icon v-if="model[name]" class="icon" :icon="Edit" @click="editPageFragment(model[name])"></Icon>
</div> </div>
@ -20,7 +23,7 @@ import { computed } from 'vue';
import { Edit } from '@element-plus/icons-vue'; import { Edit } from '@element-plus/icons-vue';
import { Id, NodeType } from '@tmagic/core'; import { Id, NodeType } from '@tmagic/core';
import { FieldProps, type PageFragmentSelectConfig } from '@tmagic/form'; import { FieldProps, MSelect, type PageFragmentSelectConfig, type SelectConfig } from '@tmagic/form';
import Icon from '@editor/components/Icon.vue'; import Icon from '@editor/components/Icon.vue';
import { useServices } from '@editor/hooks/use-services'; import { useServices } from '@editor/hooks/use-services';
@ -32,16 +35,16 @@ defineOptions({
const { editorService } = useServices(); const { editorService } = useServices();
const emit = defineEmits(['change']); const emit = defineEmits(['change']);
const props = withDefaults(defineProps<FieldProps<PageFragmentSelectConfig>>(), { withDefaults(defineProps<FieldProps<PageFragmentSelectConfig>>(), {
disabled: false, disabled: false,
}); });
const pageList = computed(() => const pageList = computed(() =>
editorService.get('root')?.items.filter((item) => item.type === NodeType.PAGE_FRAGMENT), editorService.get('root')?.items.filter((item) => item.type === NodeType.PAGE_FRAGMENT),
); );
const selectConfig = { const selectConfig: SelectConfig = {
type: 'select', type: 'select',
name: props.name,
options: () => { options: () => {
if (pageList.value) { if (pageList.value) {
return pageList.value.map((item) => ({ return pageList.value.map((item) => ({
@ -53,8 +56,8 @@ const selectConfig = {
return []; return [];
}, },
}; };
const changeHandler = async () => { const changeHandler = (v: Id) => {
emit('change', props.model[props.name]); emit('change', v);
}; };
const editPageFragment = (id: Id) => { const editPageFragment = (id: Id) => {

View File

@ -79,7 +79,6 @@ const clickHandler = ({ detail }: Event & { detail: HTMLElement | MNode }) => {
id = getIdFromEl()(detail as HTMLElement) || id; id = getIdFromEl()(detail as HTMLElement) || id;
} }
if (id) { if (id) {
props.model[props.name] = id;
emit('change', id); emit('change', id);
mForm?.$emit('field-change', props.prop, id); mForm?.$emit('field-change', props.prop, id);
} }
@ -102,7 +101,6 @@ const startSelect = () => {
const deleteHandler = () => { const deleteHandler = () => {
if (props.model) { if (props.model) {
props.model[props.name] = '';
emit('change', ''); emit('change', '');
mForm?.$emit('field-change', props.prop, ''); mForm?.$emit('field-change', props.prop, '');
} }