feat(form,editor): 表单新增修改数据记录

This commit is contained in:
roymondchen 2024-11-21 19:11:09 +08:00 committed by roymondchen
parent 0a1f03b5c7
commit 9f7d67b17b
29 changed files with 501 additions and 198 deletions

View File

@ -63,7 +63,13 @@ import { computed, inject, Ref, ref } from 'vue';
import type { CodeBlockContent } from '@tmagic/core';
import { TMagicButton, TMagicDialog, tMagicMessage, tMagicMessageBox, TMagicTag } from '@tmagic/design';
import { type FormConfig, type FormState, MFormBox, type TableColumnConfig } from '@tmagic/form';
import {
type ContainerChangeEventData,
type FormConfig,
type FormState,
MFormBox,
type TableColumnConfig,
} from '@tmagic/form';
import FloatingBox from '@editor/components/FloatingBox.vue';
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
@ -88,7 +94,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
submit: [values: CodeBlockContent];
submit: [values: CodeBlockContent, eventData: ContainerChangeEventData];
}>();
const services = inject<Services>('services');
@ -209,9 +215,9 @@ const functionConfig = computed<FormConfig>(() => [
},
]);
const submitForm = (values: CodeBlockContent) => {
const submitForm = (values: CodeBlockContent, data: ContainerChangeEventData) => {
changedValue.value = undefined;
emit('submit', values);
emit('submit', values, data);
};
const errorHandler = (error: any) => {
@ -238,7 +244,7 @@ const beforeClose = (done: (cancel?: boolean) => void) => {
distinguishCancelAndClose: true,
})
.then(() => {
changedValue.value && submitForm(changedValue.value);
changedValue.value && submitForm(changedValue.value, { changeRecords: formBox.value?.form?.changeRecords });
done();
})
.catch((action: string) => {

View File

@ -13,7 +13,7 @@
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { FormConfig, MForm } from '@tmagic/form';
import { type ContainerChangeEventData, type FormConfig, type FormValue, MForm } from '@tmagic/form';
import type { CodeParamStatement } from '@editor/type';
import { error } from '@editor/utils';
@ -59,10 +59,10 @@ const codeParamsConfig = computed(() =>
/**
* 参数值修改更新
*/
const onParamsChangeHandler = async () => {
const onParamsChangeHandler = async (v: FormValue, eventData: ContainerChangeEventData) => {
try {
const value = await form.value?.submitForm(true);
emit('change', value);
emit('change', value, eventData);
} catch (e) {
error(e);
}

View File

@ -25,7 +25,7 @@ const emit = defineEmits<{
change: [value: string | any];
}>();
const props = withDefaults(
withDefaults(
defineProps<
FieldProps<
{
@ -44,7 +44,6 @@ const props = withDefaults(
);
const save = (v: string | any) => {
props.model[props.name] = v;
emit('change', v);
};
</script>

View File

@ -21,8 +21,8 @@ import { isEmpty } from 'lodash-es';
import { HookCodeType, HookType } from '@tmagic/core';
import { TMagicCard } from '@tmagic/design';
import type { FieldProps, FormItem } from '@tmagic/form';
import { FormState, MContainer } from '@tmagic/form';
import type { ContainerChangeEventData, FieldProps, FormItem, GroupListConfig } from '@tmagic/form';
import { MContainer } from '@tmagic/form';
import type { Services } from '@editor/type';
@ -30,7 +30,9 @@ defineOptions({
name: 'MFieldsCodeSelect',
});
const emit = defineEmits(['change']);
const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData];
}>();
const services = inject<Services>('services');
@ -45,12 +47,12 @@ const props = withDefaults(
{},
);
const codeConfig = computed(() => ({
const codeConfig = computed<GroupListConfig>(() => ({
type: 'group-list',
name: 'hookData',
enableToggleMode: false,
expandAll: true,
title: (mForm: FormState, { model, index }: any) => {
title: (mForm, { model, index }: any) => {
if (model.codeType === HookCodeType.DATA_SOURCE_METHOD) {
if (Array.isArray(model.codeId)) {
if (model.codeId.length < 2) {
@ -78,11 +80,13 @@ const codeConfig = computed(() => ({
{ value: HookCodeType.DATA_SOURCE_METHOD, text: '数据源方法' },
],
defaultValue: 'code',
onChange: (mForm: FormState, v: HookCodeType, { model }: any) => {
onChange: (mForm, v: HookCodeType, { model, prop, changeRecords }) => {
if (v === HookCodeType.DATA_SOURCE_METHOD) {
model.codeId = [];
changeRecords.push({ propPath: prop.replace('codeType', 'codeId'), value: [] });
} else {
model.codeId = '';
changeRecords.push({ propPath: prop.replace('codeType', 'codeId'), value: '' });
}
return v;
@ -93,7 +97,7 @@ const codeConfig = computed(() => ({
name: 'codeId',
span: 18,
labelWidth: 0,
display: (mForm: FormState, { model }: any) => model.codeType !== HookCodeType.DATA_SOURCE_METHOD,
display: (mForm, { model }) => model.codeType !== HookCodeType.DATA_SOURCE_METHOD,
notEditable: () => !services?.codeBlockService.getEditStatus(),
},
{
@ -101,7 +105,7 @@ const codeConfig = computed(() => ({
name: 'codeId',
span: 18,
labelWidth: 0,
display: (mForm: FormState, { model }: any) => model.codeType === HookCodeType.DATA_SOURCE_METHOD,
display: (mForm, { model }) => model.codeType === HookCodeType.DATA_SOURCE_METHOD,
notEditable: () => !services?.dataSourceService.get('editable'),
},
],
@ -126,7 +130,5 @@ watch(
},
);
const changeHandler = async () => {
emit('change', props.model[props.name]);
};
const changeHandler = (v: any, eventData: ContainerChangeEventData) => emit('change', v, eventData);
</script>

View File

@ -7,7 +7,7 @@
:config="selectConfig"
:model="model"
:size="size"
@change="onParamsChangeHandler"
@change="onCodeIdChangeHandler"
></MContainer>
<!-- 查看/编辑按钮 -->
@ -41,7 +41,14 @@ import { isEmpty, map } from 'lodash-es';
import type { Id } from '@tmagic/core';
import { TMagicButton } from '@tmagic/design';
import { createValues, type FieldProps, filterFunction, type FormState, MContainer } from '@tmagic/form';
import {
type ContainerChangeEventData,
createValues,
type FieldProps,
filterFunction,
type FormState,
MContainer,
} from '@tmagic/form';
import CodeParams from '@editor/components/CodeParams.vue';
import MIcon from '@editor/components/Icon.vue';
@ -55,7 +62,9 @@ defineOptions({
const mForm = inject<FormState | undefined>('mForm');
const services = inject<Services>('services');
const eventBus = inject<EventBus>('eventBus');
const emit = defineEmits(['change']);
const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData];
}>();
const props = withDefaults(defineProps<FieldProps<CodeSelectColConfig>>(), {
disabled: false,
@ -125,12 +134,30 @@ const selectConfig = {
},
};
const onCodeIdChangeHandler = (value: any) => {
props.model.params = value.params;
emit('change', props.model, {
changeRecords: [
{
propPath: props.prop,
value: value[props.name],
},
],
});
};
/**
* 参数值修改更新
*/
const onParamsChangeHandler = (value: any) => {
const onParamsChangeHandler = (value: any, eventData: ContainerChangeEventData) => {
props.model.params = value.params;
emit('change', props.model);
emit('change', props.model, {
...eventData,
changeRecords: (eventData.changeRecords || []).map((item) => ({
prop: `${props.prop.replace(props.name, '')}${item.propPath}`,
value: item.value,
})),
});
};
const editCode = (id: string) => {

View File

@ -39,7 +39,10 @@ defineOptions({
name: 'MFieldsCondOpSelect',
});
const emit = defineEmits(['change']);
const emit = defineEmits<{
change: [value: string];
}>();
const { dataSourceService } = inject<Services>('services') || {};
const props = defineProps<FieldProps<CondOpSelectConfig>>();
@ -81,7 +84,7 @@ const options = computed(() => {
return [...arrayOptions, ...eqOptions, ...numberOptions];
});
const fieldChangeHandler = (v: string[]) => {
const fieldChangeHandler = (v: string) => {
emit('change', v);
};
</script>

View File

@ -51,7 +51,13 @@ import { inject, Ref, ref } from 'vue';
import type { DataSchema } from '@tmagic/core';
import { TMagicButton, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
import { type FieldProps, type FormConfig, type FormState, MFormBox } from '@tmagic/form';
import {
type ContainerChangeEventData,
type FieldProps,
type FormConfig,
type FormState,
MFormBox,
} from '@tmagic/form';
import { type ColumnConfig, MagicTable } from '@tmagic/table';
import { getDefaultValueFromFields } from '@tmagic/utils';
@ -75,7 +81,9 @@ const props = withDefaults(
},
);
const emit = defineEmits(['change']);
const emit = defineEmits<{
change: [v: any, eventData?: ContainerChangeEventData];
}>();
const services = inject<Services>('services');
@ -91,16 +99,29 @@ const newHandler = () => {
addDialogVisible.value = true;
};
const fieldChange = ({ index, ...value }: Record<string, any>) => {
if (index > -1) {
props.model[props.name][index] = value;
} else {
props.model[props.name].push(value);
}
const fieldChange = ({ index, ...value }: Record<string, any>, data: ContainerChangeEventData) => {
addDialogVisible.value = false;
emit('change', props.model[props.name]);
if (index > -1) {
emit('change', value, {
modifyKey: index,
changeRecords: (data.changeRecords || []).map((item) => ({
propPath: `${props.prop}.${index}.${item.propPath}`,
value: item.value,
})),
});
} else {
const modifyKey = props.model[props.name].length;
emit('change', value, {
modifyKey,
changeRecords: [
{
propPath: `${props.prop}.${modifyKey}`,
value,
},
],
});
}
};
const fieldColumns: ColumnConfig[] = [
@ -310,11 +331,9 @@ const addFromJsonFromChange = ({ data }: { data: string }) => {
try {
const value = JSON.parse(data);
props.model[props.name] = getFieldsConfig(value, props.model[props.name]);
addFromJsonDialogVisible.value = false;
emit('change', props.model[props.name]);
emit('change', getFieldsConfig(value, props.model[props.name]));
} catch (e: any) {
tMagicMessage.error(e.message);
}

View File

@ -21,14 +21,17 @@
</template>
<script setup lang="ts">
import { nextTick, ref } from 'vue';
import { cloneDeep } from 'lodash-es';
import type { CodeBlockContent } from '@tmagic/core';
import { TMagicButton } from '@tmagic/design';
import type { FieldProps } from '@tmagic/form';
import { TMagicButton, tMagicMessageBox } from '@tmagic/design';
import type { ContainerChangeEventData, FieldProps } from '@tmagic/form';
import { type ColumnConfig, MagicTable } from '@tmagic/table';
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
import { useDataSourceMethod } from '@editor/hooks/use-data-source-method';
import type { CodeParamStatement } from '@editor/type';
import { getEditorConfig } from '@editor/utils/config';
defineOptions({
name: 'MFieldsDataSourceMethods',
@ -47,7 +50,10 @@ const props = withDefaults(
const emit = defineEmits(['change']);
const { codeConfig, codeBlockEditor, createCode, editCode, deleteCode, submitCode } = useDataSourceMethod();
const codeConfig = ref<CodeBlockContent>();
const codeBlockEditor = ref<InstanceType<typeof CodeBlockEditor>>();
let editIndex = -1;
const methodColumns: ColumnConfig[] = [
{
@ -73,16 +79,31 @@ const methodColumns: ColumnConfig[] = [
actions: [
{
text: '编辑',
handler: (row: CodeBlockContent) => {
editCode(props.model, row.name);
emit('change', props.model[props.name]);
handler: (method: CodeBlockContent, index: number) => {
let codeContent = method.content || `({ params, dataSource, app }) => {\n // place your code here\n}`;
if (typeof codeContent !== 'string') {
codeContent = codeContent.toString();
}
codeConfig.value = {
...cloneDeep(method),
content: codeContent,
};
editIndex = index;
nextTick(() => {
codeBlockEditor.value?.show();
});
},
},
{
text: '删除',
buttonType: 'danger',
handler: (row: CodeBlockContent) => {
deleteCode(props.model, row.name);
handler: async (row: CodeBlockContent, index: number) => {
await tMagicMessageBox.confirm(`确定删除${row.name}?`, '提示');
props.model[props.name].splice(index, 1);
emit('change', props.model[props.name]);
},
},
@ -91,14 +112,51 @@ const methodColumns: ColumnConfig[] = [
];
const createCodeHandler = () => {
createCode(props.model);
codeConfig.value = {
name: '',
content: `({ params, dataSource, app, flowState }) => {\n // place your code here\n}`,
params: [],
};
emit('change', props.model[props.name]);
editIndex = -1;
nextTick(() => {
codeBlockEditor.value?.show();
});
};
const submitCodeHandler = (values: CodeBlockContent) => {
submitCode(values);
const submitCodeHandler = (value: CodeBlockContent, data: ContainerChangeEventData) => {
if (value.content) {
//
const parseDSL = getEditorConfig('parseDSL');
if (typeof value.content === 'string') {
value.content = parseDSL<(...args: any[]) => any>(value.content);
}
}
if (editIndex > -1) {
emit('change', value, {
modifyKey: editIndex,
changeRecords: (data.changeRecords || []).map((item) => ({
propPath: `${props.prop}.${editIndex}.${item.propPath}`,
value: item.value,
})),
});
} else {
const modifyKey = props.model[props.name].length;
emit('change', value, {
modifyKey,
changeRecords: [
{
propPath: `${props.prop}.${modifyKey}`,
value,
},
],
});
}
emit('change', props.model[props.name]);
editIndex = -1;
codeConfig.value = void 0;
codeBlockEditor.value?.hide();
};
</script>

View File

@ -17,6 +17,7 @@ import { computed, inject } from 'vue';
import type { DisplayCond } from '@tmagic/core';
import {
type ContainerChangeEventData,
type FieldProps,
type FilterFunction,
filterFunction,
@ -33,7 +34,7 @@ defineOptions({
});
const emit = defineEmits<{
change: [value: DisplayCond[]];
change: [value: DisplayCond[], eventData?: ContainerChangeEventData];
}>();
const props = withDefaults(
@ -149,7 +150,11 @@ const config = computed<GroupListConfig>(() => ({
],
}));
const changeHandler = (v: DisplayCond[]) => {
emit('change', v);
const changeHandler = (v: DisplayCond[], eventData?: ContainerChangeEventData) => {
if (!Array.isArray(props.model[props.name])) {
props.model[props.name] = [];
}
emit('change', v, eventData);
};
</script>

View File

@ -20,6 +20,7 @@
:key="index"
:disabled="disabled"
:size="size"
:prop="`${prop}.${index}`"
:config="actionsConfig"
:model="cardItem"
:label-width="config.labelWidth || '100px'"
@ -32,7 +33,8 @@
:model="cardItem"
:disabled="disabled"
:size="size"
@change="onChangeHandler"
:prop="`${prop}.${index}`"
@change="eventNameChangeHandler"
></MFormContainer>
<TMagicButton
style="color: #f56c6c"
@ -56,7 +58,14 @@ import { has } from 'lodash-es';
import type { EventOption, MComponent, MContainer } from '@tmagic/core';
import { ActionType } from '@tmagic/core';
import { TMagicButton } from '@tmagic/design';
import type { CascaderOption, ChildConfig, FieldProps, FormState, PanelConfig } from '@tmagic/form';
import type {
CascaderOption,
ChildConfig,
ContainerChangeEventData,
FieldProps,
FormState,
PanelConfig,
} from '@tmagic/form';
import { MContainer as MFormContainer, MPanel } from '@tmagic/form';
import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX, traverseNode } from '@tmagic/utils';
@ -69,7 +78,9 @@ defineOptions({
const props = defineProps<FieldProps<EventSelectConfig>>();
const emit = defineEmits(['change']);
const emit = defineEmits<{
change: [v: any, eventData?: ContainerChangeEventData];
}>();
const services = inject<Services>('services');
@ -355,21 +366,27 @@ const addEvent = () => {
name: '',
actions: [],
};
if (!props.model[props.name]) {
props.model[props.name] = [];
}
props.model[props.name].push(defaultEvent);
onChangeHandler();
emit('change', defaultEvent, {
modifyKey: props.model[props.name].length,
});
};
//
const removeEvent = (index: number) => {
if (!props.name) return;
props.model[props.name].splice(index, 1);
onChangeHandler();
emit('change', props.model[props.name]);
};
const onChangeHandler = () => {
emit('change', props.model);
const eventNameChangeHandler = (v: any, eventData: ContainerChangeEventData) => {
emit('change', props.model[props.name], eventData);
};
const onChangeHandler = (v: any, eventData: ContainerChangeEventData) =>
emit('change', props.model[props.name], eventData);
</script>

View File

@ -86,7 +86,9 @@ const props = withDefaults(
},
);
const emit = defineEmits<(e: 'change', value: Record<string, any>) => void>();
const emit = defineEmits<{
change: [value: Record<string, any>];
}>();
const records = ref<[string, string][]>([]);
const showCode = ref(false);

View File

@ -28,7 +28,7 @@
</template>
<script setup lang="ts">
import { provide, reactive, ref, toRaw, watch, watchEffect } from 'vue';
import { provide, reactive, ref, shallowRef, toRaw, watch, watchEffect } from 'vue';
import { cloneDeep, isEqual } from 'lodash-es';
import { TMagicForm } from '@tmagic/design';
@ -36,7 +36,7 @@ import { TMagicForm } from '@tmagic/design';
import Container from './containers/Container.vue';
import { getConfig } from './utils/config';
import { initValue } from './utils/form';
import type { FormConfig, FormState, FormValue, ValidateError } from './schema';
import type { ChangeRecord, ContainerChangeEventData, FormConfig, FormState, FormValue, ValidateError } from './schema';
defineOptions({
name: 'MForm',
@ -81,7 +81,7 @@ const props = withDefaults(
},
);
const emit = defineEmits(['change', 'error', 'field-input', 'field-change']);
const emit = defineEmits(['change', 'error', 'field-input', 'field-change', 'update:stepActive']);
const tMagicForm = ref<InstanceType<typeof TMagicForm>>();
const initialized = ref(false);
@ -135,9 +135,13 @@ watchEffect(async () => {
provide('mForm', formState);
const changeRecords = shallowRef<ChangeRecord[]>([]);
watch(
[() => props.config, () => props.initValues],
([config], [preConfig]) => {
changeRecords.value = [];
if (!isEqual(toRaw(config), toRaw(preConfig))) {
initialized.value = false;
}
@ -165,8 +169,11 @@ watch(
{ immediate: true },
);
const changeHandler = () => {
emit('change', values.value);
const changeHandler = (v: FormValue, eventData: ContainerChangeEventData) => {
if (eventData.changeRecords?.length) {
changeRecords.value.push(...eventData.changeRecords);
}
emit('change', values.value, eventData);
};
const submitHandler = (e: SubmitEvent) => {
@ -180,10 +187,14 @@ defineExpose({
lastValuesProcessed,
formState,
initialized,
changeRecords,
changeHandler,
resetForm: () => tMagicForm.value?.resetFields(),
resetForm: () => {
tMagicForm.value?.resetFields();
changeRecords.value = [];
},
submitForm: async (native?: boolean): Promise<any> => {
try {

View File

@ -40,7 +40,7 @@ import { computed, ref, watchEffect } from 'vue';
import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
import Form from './Form.vue';
import type { FormConfig } from './schema';
import type { ContainerChangeEventData, FormConfig, FormValue } from './schema';
defineOptions({
name: 'MFormBox',
@ -68,7 +68,11 @@ const props = withDefaults(
},
);
const emit = defineEmits(['submit', 'change', 'error']);
const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData];
submit: [v: any, eventData: ContainerChangeEventData];
error: [e: any];
}>();
const footerHeight = 60;
@ -98,14 +102,14 @@ watchEffect(() => {
const submitHandler = async () => {
try {
const values = await form.value?.submitForm();
emit('submit', values);
emit('submit', values, { changeRecords: form.value?.changeRecords });
} catch (e) {
emit('error', e);
}
};
const changeHandler = (value: any) => {
emit('change', value);
const changeHandler = (value: FormValue, eventData: ContainerChangeEventData) => {
emit('change', value, eventData);
};
const show = () => {};

View File

@ -65,7 +65,7 @@ import { computed, ref } from 'vue';
import { TMagicButton, TMagicCol, TMagicDialog, TMagicRow } from '@tmagic/design';
import Form from './Form.vue';
import { FormConfig, StepConfig } from './schema';
import { ContainerChangeEventData, FormConfig, FormValue, StepConfig } from './schema';
defineOptions({
name: 'MFormDialog',
@ -132,7 +132,7 @@ const closeHandler = () => {
const save = async () => {
try {
const values = await form.value?.submitForm();
emit('submit', values);
emit('submit', values, { changeRecords: form.value?.changeRecords });
} catch (e) {
emit('error', e);
}
@ -146,8 +146,8 @@ const nextStep = () => {
stepActive.value += 1;
};
const changeHandler = (value: any) => {
emit('change', value);
const changeHandler = (value: FormValue, eventData: ContainerChangeEventData) => {
emit('change', value, eventData);
};
const show = () => {

View File

@ -59,7 +59,7 @@ import { ref, watchEffect } from 'vue';
import { TMagicButton, TMagicCol, TMagicDrawer, TMagicRow } from '@tmagic/design';
import Form from './Form.vue';
import type { FormConfig } from './schema';
import type { ContainerChangeEventData, FormConfig, FormValue } from './schema';
defineOptions({
name: 'MFormDialog',
@ -110,14 +110,14 @@ watchEffect(() => {
const submitHandler = async () => {
try {
const values = await form.value?.submitForm();
emit('submit', values);
emit('submit', values, { changeRecords: form.value?.changeRecords });
} catch (e) {
emit('error', e);
}
};
const changeHandler = (value: any) => {
emit('change', value);
const changeHandler = (value: FormValue, eventData: ContainerChangeEventData) => {
emit('change', value, eventData);
};
const openHandler = () => {

View File

@ -21,7 +21,7 @@ import { computed, inject } from 'vue';
import { TMagicCol } from '@tmagic/design';
import { ChildConfig, FormState } from '../schema';
import type { ChildConfig, ContainerChangeEventData, FormState } from '../schema';
import { display as displayFunction } from '../utils/form';
import Container from './Container.vue';
@ -43,10 +43,13 @@ const props = defineProps<{
disabled?: boolean;
}>();
const emit = defineEmits(['change', 'addDiffCount']);
const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData];
addDiffCount: [];
}>();
const mForm = inject<FormState | undefined>('mForm');
const display = computed(() => displayFunction(mForm, props.config.display, props));
const changeHandler = () => emit('change', props.model);
const changeHandler = (v: any, eventData: ContainerChangeEventData) => emit('change', v, eventData);
const onAddDiffCount = () => emit('addDiffCount');
</script>

View File

@ -191,12 +191,12 @@
</template>
<template v-else-if="items && display">
<template v-if="name || name === 0 ? model[name] : model">
<template v-if="isValidName() ? model[name] : model">
<Container
v-for="item in items"
:key="key(item)"
:model="name || name === 0 ? model[name] : model"
:last-values="name || name === 0 ? lastValues[name] || {} : lastValues"
:model="isValidName() ? model[name] : model"
:last-values="isValidName() ? lastValues[name] || {} : lastValues"
:is-compare="isCompare"
:config="item"
:size="size"
@ -220,13 +220,20 @@
</template>
<script setup lang="ts">
import { computed, inject, ref, watch, watchEffect } from 'vue';
import { computed, inject, ref, toRaw, watch, watchEffect } from 'vue';
import { WarningFilled } from '@element-plus/icons-vue';
import { isEqual } from 'lodash-es';
import { TMagicButton, TMagicFormItem, TMagicIcon, TMagicTooltip } from '@tmagic/design';
import { ChildConfig, ContainerCommonConfig, FormState, FormValue, TypeFunction } from '../schema';
import type {
ChildConfig,
ContainerChangeEventData,
ContainerCommonConfig,
FormState,
FormValue,
TypeFunction,
} from '../schema';
import { display as displayFunction, filterFunction, getRules } from '../utils/form';
defineOptions({
@ -258,7 +265,10 @@ const props = withDefaults(
},
);
const emit = defineEmits(['change', 'addDiffCount']);
const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData];
addDiffCount: [];
}>();
const mForm = inject<FormState | undefined>('mForm');
@ -285,7 +295,12 @@ const itemProp = computed(() => {
} else {
return props.prop;
}
return `${props.prop}${props.prop ? '.' : ''}${n}`;
if (typeof props.prop !== 'undefined' && props.prop !== '') {
return `${props.prop}.${n}`;
}
return `${n}`;
});
const tagName = computed(() => `m-${items.value ? 'form' : 'fields'}-${type.value}`);
@ -360,18 +375,6 @@ const filterHandler = (filter: any, value: FormValue | number | string) => {
return value;
};
const changeHandler = (onChange: any, value: FormValue | number | string) => {
if (typeof onChange === 'function') {
return onChange(mForm, value, {
model: props.model,
values: mForm?.initValues,
formValue: mForm?.values,
prop: itemProp.value,
config: props.config,
});
}
};
const trimHandler = (trim: any, value: FormValue | number | string) => {
if (typeof value === 'string' && trim) {
return value.replace(/^\s*/, '').replace(/\s*$/, '');
@ -381,28 +384,80 @@ const trimHandler = (trim: any, value: FormValue | number | string) => {
//
const onAddDiffCount = () => emit('addDiffCount');
const onChangeHandler = async function (v: FormValue, key?: string) {
const { filter, onChange, trim, name, dynamicKey } = props.config as any;
let value: FormValue | number | string = v;
const hasModifyKey = (eventDataItem: ContainerChangeEventData) =>
typeof eventDataItem?.modifyKey !== 'undefined' && eventDataItem.modifyKey !== '';
const isValidName = () => {
const valueType = typeof name.value;
if (valueType !== 'string' && valueType !== 'symbol' && valueType !== 'number') {
return false;
}
if (name.value === '') {
return false;
}
if (typeof name.value === 'number') {
return name.value >= 0;
}
return true;
};
const onChangeHandler = async function (v: any, eventData: ContainerChangeEventData = {}) {
const { filter, onChange, trim, dynamicKey } = props.config as any;
let value: FormValue | number | string | any[] = toRaw(v);
const changeRecords = eventData.changeRecords || [];
const newChangeRecords = [...changeRecords];
try {
value = filterHandler(filter, v);
value = (await changeHandler(onChange, value)) ?? value;
if (typeof onChange === 'function') {
value =
(await onChange(mForm, value, {
model: props.model,
values: mForm?.initValues,
formValue: mForm?.values,
prop: itemProp.value,
config: props.config,
changeRecords: newChangeRecords,
})) ?? value;
}
value = trimHandler(trim, value) ?? value;
} catch (e) {
console.error(e);
}
// fieldfield-linkmodel===value,
if ((name || name === 0) && props.model !== value && (v !== value || props.model[name] !== value)) {
let valueProp = itemProp.value;
if (hasModifyKey(eventData)) {
if (dynamicKey) {
props.model[eventData.modifyKey!] = value;
} else if (isValidName()) {
props.model[name.value][eventData.modifyKey!] = value;
}
valueProp = valueProp ? `${valueProp}.${eventData.modifyKey}` : eventData.modifyKey!;
// modifyKey
delete eventData.modifyKey;
} else if (isValidName() && props.model !== value && (v !== value || props.model[name.value] !== value)) {
// fieldfield-linkmodel===value,
// eslint-disable-next-line vue/no-mutating-props
props.model[name] = value;
props.model[name.value] = value;
}
// valuekeymodel
if (key !== undefined && dynamicKey) {
// eslint-disable-next-line vue/no-mutating-props
props.model[key] = value;
if (changeRecords.length === 0) {
newChangeRecords.push({
propPath: valueProp,
value,
});
}
emit('change', props.model);
emit('change', props.model, {
...eventData,
changeRecords: newChangeRecords,
});
};
</script>

View File

@ -6,7 +6,7 @@
:prop="`${prop}${prop ? '.' : ''}${config.name}.value`"
:true-value="1"
:false-value="0"
@update:modelValue="change"
@update:modelValue="valueChangeHandler"
><span v-html="config.legend"></span><span v-if="config.extra" v-html="config.extra" class="m-form-tip"></span
></TMagicCheckbox>
</component>
@ -29,7 +29,7 @@
:disabled="disabled"
:labelWidth="lWidth"
:size="size"
@change="change"
@change="changeHandler"
@add-diff-count="onAddDiffCount()"
></Container>
</div>
@ -50,7 +50,7 @@
:labelWidth="lWidth"
:size="size"
:disabled="disabled"
@change="change"
@change="changeHandler"
@addDiffCount="onAddDiffCount()"
></Container>
</template>
@ -62,7 +62,7 @@ import { computed, inject } from 'vue';
import { TMagicCheckbox } from '@tmagic/design';
import { FieldsetConfig, FormState } from '../schema';
import { ContainerChangeEventData, FieldsetConfig, FormState } from '../schema';
import Container from './Container.vue';
@ -90,7 +90,10 @@ const props = withDefaults(
},
);
const emit = defineEmits(['change', 'addDiffCount']);
const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData];
addDiffCount: [];
}>();
const mForm = inject<FormState | undefined>('mForm');
@ -110,10 +113,12 @@ const lWidth = computed(() => {
return props.config.labelWidth || props.labelWidth || (props.config.text ? undefined : '0');
});
const change = () => {
emit('change', props.model);
const valueChangeHandler = (value: number | boolean) => {
emit('change', value, { modifyKey: 'value' });
};
const changeHandler = (v: any, eventData: ContainerChangeEventData) => emit('change', v, eventData);
const key = (item: any, index: number) => item[mForm?.keyProp || '__key'] ?? index;
const onAddDiffCount = () => emit('addDiffCount');

View File

@ -25,7 +25,14 @@
@addDiffCount="onAddDiffCount()"
></MFieldsGroupListItem>
<TMagicButton @click="addHandler" type="primary" :disabled="disabled" v-if="addable">新增</TMagicButton>
<TMagicButton
v-if="addable"
type="primary"
:size="config.enableToggleMode ? 'small' : 'default'"
:disabled="disabled"
@click="addHandler"
>新增</TMagicButton
>
<TMagicButton :icon="Grid" size="small" @click="toggleMode" v-if="config.enableToggleMode">切换为表格</TMagicButton>
</div>
@ -37,7 +44,7 @@ import { Grid } from '@element-plus/icons-vue';
import { TMagicButton } from '@tmagic/design';
import { FormState, GroupListConfig } from '../schema';
import type { ContainerChangeEventData, FormState, GroupListConfig } from '../schema';
import { initValue } from '../utils/form';
import MFieldsGroupListItem from './GroupListItem.vue';
@ -58,7 +65,10 @@ const props = defineProps<{
disabled?: boolean;
}>();
const emit = defineEmits(['change', 'addDiffCount']);
const emit = defineEmits<{
change: [v: any, eventData?: ContainerChangeEventData];
addDiffCount: [];
}>();
const mForm = inject<FormState | undefined>('mForm');
@ -77,10 +87,8 @@ const addable = computed(() => {
return typeof props.config.addable === 'undefined' ? true : props.config.addable;
});
const changeHandler = () => {
if (!props.name) return false;
emit('change', props.model[props.name]);
const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
emit('change', props.model, eventData);
};
const addHandler = async () => {
@ -105,14 +113,22 @@ const addHandler = async () => {
});
props.model[props.name].push(groupValue);
changeHandler();
emit('change', props.model[props.name], {
changeRecords: [
{
propPath: `${props.prop}.${props.model[props.name].length - 1}`,
value: groupValue,
},
],
});
};
const removeHandler = (index: number) => {
if (!props.name) return false;
props.model[props.name].splice(index, 1);
changeHandler();
emit('change', props.model[props.name]);
};
const swapHandler = (idx1: number, idx2: number) => {
@ -120,7 +136,7 @@ const swapHandler = (idx1: number, idx2: number) => {
const [currRow] = props.model[props.name].splice(idx1, 1);
props.model[props.name].splice(idx2, 0, currRow);
changeHandler();
emit('change', props.model[props.name]);
};
const toggleMode = () => {

View File

@ -48,7 +48,7 @@ import { CaretBottom, CaretRight, CaretTop, Delete } from '@element-plus/icons-v
import { TMagicButton, TMagicIcon } from '@tmagic/design';
import { FormState, GroupListConfig } from '../schema';
import type { ContainerChangeEventData, FormState, GroupListConfig } from '../schema';
import { filterFunction } from '../utils/form';
import Container from './Container.vue';
@ -103,7 +103,9 @@ const itemExtra = computed(() => filterFunction(mForm, props.config.itemExtra, p
const removeHandler = () => emit('remove-item', props.index);
const changeHandler = () => emit('change');
const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
emit('change', props.model, eventData);
};
const expandHandler = () => {
expand.value = !expand.value;

View File

@ -63,7 +63,7 @@ import { CaretBottom, CaretRight } from '@element-plus/icons-vue';
import { TMagicButton, TMagicCard } from '@tmagic/design';
import { FormState, PanelConfig } from '../schema';
import type { ContainerChangeEventData, FormState, PanelConfig } from '../schema';
import { filterFunction } from '../utils/form';
import Container from './Container.vue';
@ -84,7 +84,10 @@ const props = defineProps<{
disabled?: boolean;
}>();
const emit = defineEmits(['change', 'addDiffCount']);
const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData];
addDiffCount: [];
}>();
const mForm = inject<FormState | undefined>('mForm');
@ -94,6 +97,8 @@ const items = computed(() => props.config.items);
const filter = (config: any) => filterFunction(mForm, config, props);
const changeHandler = () => emit('change', props.model);
const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
emit('change', props.model, eventData);
};
const onAddDiffCount = () => emit('addDiffCount');
</script>

View File

@ -24,7 +24,7 @@ import { inject } from 'vue';
import { TMagicRow } from '@tmagic/design';
import { FormState, RowConfig } from '../schema';
import type { ContainerChangeEventData, FormState, RowConfig } from '../schema';
import Col from './Col.vue';
@ -45,10 +45,15 @@ const props = defineProps<{
disabled?: boolean;
}>();
const emit = defineEmits(['change', 'addDiffCount']);
const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData];
addDiffCount: [];
}>();
const mForm = inject<FormState | undefined>('mForm');
const changeHandler = () => emit('change', props.name ? props.model[props.name] : props.model);
const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
emit('change', props.name ? props.model[props.name] : props.model, eventData);
};
const onAddDiffCount = () => emit('addDiffCount');
</script>

View File

@ -37,7 +37,7 @@ import { inject, ref, watchEffect } from 'vue';
import { TMagicStep, TMagicSteps } from '@tmagic/design';
import { FormState, StepConfig } from '../schema';
import type { ContainerChangeEventData, FormState, StepConfig } from '../schema';
import Container from './Container.vue';
@ -48,6 +48,7 @@ defineOptions({
const props = withDefaults(
defineProps<{
model: any;
name?: string;
lastValues?: any;
isCompare?: boolean;
config: StepConfig;
@ -61,7 +62,10 @@ const props = withDefaults(
},
);
const emit = defineEmits(['change', 'addDiffCount']);
const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData];
addDiffCount: [];
}>();
const mForm = inject<FormState | undefined>('mForm');
const active = ref(1);
@ -75,8 +79,9 @@ const stepClick = (index: number) => {
mForm?.$emit('update:stepActive', active.value);
};
const changeHandler = () => {
emit('change', props.model);
const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
emit('change', props.model, eventData);
};
const onAddDiffCount = () => emit('addDiffCount');
</script>

View File

@ -113,7 +113,7 @@
:lastValues="lastData[scope.$index]"
:is-compare="isCompare"
:size="size"
@change="$emit('change', model[modelName])"
@change="changeHandler"
@addDiffCount="onAddDiffCount()"
></Container>
</template>
@ -206,7 +206,7 @@ import {
} from '@tmagic/design';
import { asyncLoadJs, sleep } from '@tmagic/utils';
import { FormState, SortProp, TableColumnConfig, TableConfig } from '../schema';
import type { ContainerChangeEventData, FormState, SortProp, TableColumnConfig, TableConfig } from '../schema';
import { display as displayFunc, initValue } from '../utils/form';
import Container from './Container.vue';
@ -395,7 +395,15 @@ const newHandler = async (row?: any) => {
}
props.model[modelName.value].push(inputs);
emit('change', props.model[modelName.value]);
emit('change', props.model[modelName.value], {
changeRecords: [
{
propPath: `${props.prop}.${props.model[modelName.value].length - 1}`,
value: inputs,
},
],
});
};
onMounted(() => {
@ -644,6 +652,10 @@ const getProp = (index: number) => {
const onAddDiffCount = () => emit('addDiffCount');
const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
emit('change', props.model, eventData);
};
defineExpose({
toggleRowSelection,
});

View File

@ -71,12 +71,12 @@
<script setup lang="ts">
import { computed, inject, ref, watchEffect } from 'vue';
import { cloneDeep, isEmpty } from 'lodash-es';
import { isEmpty } from 'lodash-es';
import { getDesignConfig, TMagicBadge } from '@tmagic/design';
import { FormState, TabConfig, TabPaneConfig } from '../schema';
import { display as displayFunc, filterFunction } from '../utils/form';
import type { ContainerChangeEventData, FormState, TabConfig, TabPaneConfig } from '../schema';
import { display as displayFunc, filterFunction, initValue } from '../utils/form';
import Container from './Container.vue';
@ -137,7 +137,10 @@ const props = withDefaults(
},
);
const emit = defineEmits(['change', 'addDiffCount']);
const emit = defineEmits<{
change: [v: any, eventData?: ContainerChangeEventData];
addDiffCount: [];
}>();
const mForm = inject<FormState | undefined>('mForm');
const activeTabName = ref(getActive(mForm, props, ''));
@ -166,8 +169,8 @@ const tabItems = (tab: TabPaneConfig) => (props.config.dynamic ? props.config.it
const tabClickHandler = (tab: any) => tabClick(mForm, tab, props);
const onTabAdd = () => {
if (!props.config.name) throw new Error('dynamic tab 必须配置name');
const onTabAdd = async () => {
if (!props.name) throw new Error('dynamic tab 必须配置name');
if (typeof props.config.onTabAdd === 'function') {
props.config.onTabAdd(mForm, {
@ -175,17 +178,32 @@ const onTabAdd = () => {
prop: props.prop,
config: props.config,
});
} else if (tabs.value.length > 0) {
const newObj = cloneDeep(tabs.value[0]);
emit('change', props.model);
} else {
const newObj = await initValue(mForm, {
config: props.config.items,
initValues: {},
});
newObj.title = `标签${tabs.value.length + 1}`;
props.model[props.config.name].push(newObj);
props.model[props.name].push(newObj);
emit('change', props.model[props.name], {
changeRecords: [
{
propPath: `${props.prop}.${props.model[props.name].length - 1}`,
value: newObj,
},
],
});
}
emit('change', props.model);
mForm?.$emit('field-change', props.prop, props.model[props.config.name]);
mForm?.$emit('field-change', props.prop, props.model[props.name]);
};
const onTabRemove = (tabName: string) => {
if (!props.config.name) throw new Error('dynamic tab 必须配置name');
if (!props.name) throw new Error('dynamic tab 必须配置name');
if (typeof props.config.onTabRemove === 'function') {
props.config.onTabRemove(mForm, tabName, {
@ -194,23 +212,20 @@ const onTabRemove = (tabName: string) => {
config: props.config,
});
} else {
props.model[props.config.name].splice(+tabName, 1);
props.model[props.name].splice(+tabName, 1);
//
if (tabName < activeTabName.value || activeTabName.value >= props.model[props.config.name].length) {
if (tabName < activeTabName.value || activeTabName.value >= props.model[props.name].length) {
activeTabName.value = (+activeTabName.value - 1).toString();
tabClick(mForm, { name: activeTabName.value }, props);
}
}
emit('change', props.model);
mForm?.$emit('field-change', props.prop, props.model[props.config.name]);
mForm?.$emit('field-change', props.prop, props.model[props.name]);
};
const changeHandler = () => {
emit('change', props.model);
if (typeof props.config.onChange === 'function') {
props.config.onChange(mForm, { model: props.model, prop: props.prop, config: props.config });
}
const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
emit('change', props.model, eventData);
};
// tabstab

View File

@ -59,7 +59,7 @@ const changeFieldMap = async () => {
let oldVal = props.model?.[v.name] || '';
if (!oldVal && v.defaultValue !== undefined) {
oldVal = v.defaultValue;
emit('change', oldVal, v.name);
emit('change', oldVal, { modifyKey: v.name });
}
fieldMap.value[v.name] = oldVal;
fieldLabelMap.value[v.name] = v.label || '';
@ -85,6 +85,8 @@ onBeforeUnmount(() => {
});
const inputChangeHandler = (key: string) => {
emit('change', fieldMap.value[key], key);
emit('change', fieldMap.value[key], {
modifyKey: key,
});
};
</script>

View File

@ -21,6 +21,16 @@ export interface ValidateError {
field: string;
}
export interface ChangeRecord {
propPath?: string;
value: any;
}
export interface ContainerChangeEventData {
modifyKey?: string;
changeRecords?: ChangeRecord[];
}
export interface FieldProps<T = any> {
config: T;
model: any;
@ -157,34 +167,34 @@ export interface Input {
export type TypeFunction = (
mForm: FormState | undefined,
data: {
model: Record<any, any>;
model: FormValue;
},
) => string;
export type FilterFunction<T = boolean> = (
mForm: FormState | undefined,
data: {
model: Record<any, any>;
values: Record<any, any>;
parent?: Record<any, any>;
formValue: Record<any, any>;
model: FormValue;
values: FormValue;
parent?: FormValue;
formValue: FormValue;
prop: string;
config: any;
index?: number;
},
) => T;
type OnChangeHandler = (
mForm: FormState | undefined,
value: any,
data: {
model: Record<any, any>;
values: Record<any, any>;
parent?: Record<any, any>;
formValue: Record<any, any>;
config: any;
},
) => any;
export interface OnChangeHandlerData {
model: FormValue;
values?: FormValue;
parent?: FormValue;
formValue?: FormValue;
config: any;
prop: string;
changeRecords: ChangeRecord[];
}
export type OnChangeHandler = (mForm: FormState | undefined, value: any, data: OnChangeHandlerData) => any;
type DefaultValueFunction = (mForm: FormState | undefined) => any;

View File

@ -152,6 +152,7 @@ const dsl: MApp = {
text: 'Tmagic editor 营销活动编辑器',
multiple: true,
events: [],
displayConds: [],
},
{
type: 'qrcode',
@ -176,6 +177,7 @@ const dsl: MApp = {
url: 'https://github.com/Tencent/tmagic-editor',
events: [],
created: [],
displayConds: [],
},
{
type: 'img',
@ -201,6 +203,7 @@ const dsl: MApp = {
url: '',
events: [],
created: [],
displayConds: [],
},
{
type: 'button',
@ -245,6 +248,7 @@ const dsl: MApp = {
},
],
created: [],
displayConds: [],
},
{
type: 'overlay',
@ -479,14 +483,6 @@ const dsl: MApp = {
afterResponse: '',
},
],
dataSourceDeps: {
ds_b64c92b5: {
button_430: {
name: '按钮',
keys: ['text'],
},
},
},
};
export default dsl;

View File

@ -189,6 +189,25 @@ export default createForm([
},
],
},
{
type: 'tab',
name: 'tab',
editable: true,
dynamic: true,
active: '0',
tabType: 'border-card',
items: [
{
name: 'xx',
items: [
{
name: 'text',
text: 'text',
},
],
},
],
},
{
type: 'table',
name: 'table',