feat(form-schema,form,editor,table): 完善表单配置类型

This commit is contained in:
roymondchen 2026-03-20 17:38:11 +08:00
parent feefd3779e
commit e8714c96c9
25 changed files with 278 additions and 240 deletions

View File

@ -63,7 +63,14 @@ import { computed, inject, nextTick, Ref, ref, useTemplateRef, watch } from 'vue
import type { CodeBlockContent } from '@tmagic/core'; import type { CodeBlockContent } from '@tmagic/core';
import { TMagicButton, TMagicDialog, tMagicMessage, tMagicMessageBox, TMagicTag } from '@tmagic/design'; import { TMagicButton, TMagicDialog, tMagicMessage, tMagicMessageBox, TMagicTag } from '@tmagic/design';
import { type ContainerChangeEventData, type FormConfig, type FormState, MFormBox } from '@tmagic/form'; import {
type ContainerChangeEventData,
defineFormConfig,
defineFormItem,
type FormConfig,
MFormBox,
type TableColumnConfig,
} from '@tmagic/form';
import FloatingBox from '@editor/components/FloatingBox.vue'; import FloatingBox from '@editor/components/FloatingBox.vue';
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height'; import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
@ -112,7 +119,7 @@ const diffChange = () => {
difVisible.value = false; difVisible.value = false;
}; };
const defaultParamColConfig = { const defaultParamColConfig = defineFormItem<TableColumnConfig>({
type: 'row', type: 'row',
label: '参数类型', label: '参数类型',
items: [ items: [
@ -140,76 +147,79 @@ const defaultParamColConfig = {
], ],
}, },
], ],
}; });
const functionConfig = computed<FormConfig>(() => [ const functionConfig = computed(
{ () =>
text: '名称', defineFormConfig([
name: 'name',
rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],
},
{
text: '描述',
name: 'desc',
},
{
text: '执行时机',
name: 'timing',
type: 'select',
options: () => {
const options = [
{ text: '初始化前', value: 'beforeInit' },
{ text: '初始化后', value: 'afterInit' },
];
if (props.dataSourceType !== 'base') {
options.push({ text: '请求前', value: 'beforeRequest' });
options.push({ text: '请求后', value: 'afterRequest' });
}
return options;
},
display: () => props.isDataSource,
},
{
type: 'table',
border: true,
text: '参数',
enableFullscreen: false,
enableToggleMode: false,
name: 'params',
dropSort: false,
items: [
{ {
type: 'text', text: '名称',
label: '参数名',
name: 'name', name: 'name',
rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],
}, },
{ {
type: 'text', text: '描述',
label: '描述', name: 'desc',
name: 'extra',
}, },
codeBlockService.getParamsColConfig() || defaultParamColConfig, {
], text: '执行时机',
}, name: 'timing',
{ type: 'select',
name: 'content', options: () => {
type: 'vs-code', const options = [
options: inject('codeOptions', {}), { text: '初始化前', value: 'beforeInit' },
autosize: { minRows: 10, maxRows: 30 }, { text: '初始化后', value: 'afterInit' },
onChange: (formState: FormState | undefined, code: string) => { ];
try { if (props.dataSourceType !== 'base') {
// js options.push({ text: '请求前', value: 'beforeRequest' });
getEditorConfig('parseDSL')(code); options.push({ text: '请求后', value: 'afterRequest' });
}
return options;
},
display: () => props.isDataSource,
},
{
type: 'table',
border: true,
text: '参数',
enableFullscreen: false,
enableToggleMode: false,
name: 'params',
dropSort: false,
items: [
{
type: 'text',
label: '参数名',
name: 'name',
},
{
type: 'text',
label: '描述',
name: 'extra',
},
codeBlockService.getParamsColConfig() || defaultParamColConfig,
],
},
{
name: 'content',
type: 'vs-code',
options: inject('codeOptions', {}),
autosize: { minRows: 10, maxRows: 30 },
onChange: (_formState, code: string) => {
try {
// js
getEditorConfig('parseDSL')(code);
return code; return code;
} catch (error: any) { } catch (error: any) {
tMagicMessage.error(error.message); tMagicMessage.error(error.message);
throw error; throw error;
} }
}, },
}, },
]); ]) as FormConfig,
);
const parseContent = (content: any) => { const parseContent = (content: any) => {
if (typeof content === 'string') { if (typeof content === 'string') {

View File

@ -13,7 +13,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, useTemplateRef } from 'vue'; import { computed, useTemplateRef } from 'vue';
import { type ContainerChangeEventData, type FormConfig, type FormValue, MForm } from '@tmagic/form'; import { type ContainerChangeEventData, type FormItemConfig, type FormValue, MForm } from '@tmagic/form';
import type { CodeParamStatement } from '@editor/type'; import type { CodeParamStatement } from '@editor/type';
import { error } from '@editor/utils'; import { error } from '@editor/utils';
@ -34,7 +34,7 @@ const emit = defineEmits(['change']);
const formRef = useTemplateRef<InstanceType<typeof MForm>>('form'); const formRef = useTemplateRef<InstanceType<typeof MForm>>('form');
const getFormConfig = (items: FormConfig = []) => [ const getFormConfig = (items: FormItemConfig[] = []) => [
{ {
type: 'fieldset', type: 'fieldset',
items, items,
@ -51,7 +51,7 @@ const codeParamsConfig = computed(() =>
name, name,
text, text,
extra, extra,
fieldConfig: config, fieldConfig: config as FormItemConfig,
})), })),
), ),
); );

View File

@ -50,6 +50,7 @@ import {
createValues, createValues,
type FieldProps, type FieldProps,
filterFunction, filterFunction,
type FormItemConfig,
type FormState, type FormState,
MSelect, MSelect,
type SelectConfig, type SelectConfig,
@ -141,7 +142,9 @@ const onCodeIdChangeHandler = (value: any) => {
changeRecords.push({ changeRecords.push({
propPath: props.prop.replace(`${props.name}`, 'params'), propPath: props.prop.replace(`${props.name}`, 'params'),
value: paramsConfig.value.length ? createValues(mForm, paramsConfig.value, {}, props.model.params) : {}, value: paramsConfig.value.length
? createValues(mForm, paramsConfig.value as unknown as FormItemConfig[], {}, props.model.params)
: {},
}); });
emit('change', value, { emit('change', value, {

View File

@ -98,7 +98,9 @@ const dataSources = computed(() => dataSourceService.get('dataSources') || []);
const disabledDataSource = computed(() => propsService.getDisabledDataSource()); const disabledDataSource = computed(() => propsService.getDisabledDataSource());
const type = computed((): string => { const type = computed((): string => {
let type = props.config.fieldConfig?.type; if (!props.config.fieldConfig) return '';
let type = 'type' in props.config.fieldConfig ? props.config.fieldConfig.type : '';
if (typeof type === 'function') { if (typeof type === 'function') {
type = type(mForm, { type = type(mForm, {
model: props.model, model: props.model,

View File

@ -48,6 +48,7 @@ import {
type DataSourceMethodSelectConfig, type DataSourceMethodSelectConfig,
type FieldProps, type FieldProps,
filterFunction, filterFunction,
type FormItemConfig,
type FormState, type FormState,
MCascader, MCascader,
} from '@tmagic/form'; } from '@tmagic/form';
@ -142,7 +143,9 @@ const onChangeHandler = (value: any) => {
changeRecords.push({ changeRecords.push({
propPath: props.prop.replace(`${props.name}`, 'params'), propPath: props.prop.replace(`${props.name}`, 'params'),
value: paramsConfig.value.length ? createValues(mForm, paramsConfig.value, {}, props.model.params) : {}, value: paramsConfig.value.length
? createValues(mForm, paramsConfig.value as unknown as FormItemConfig[], {}, props.model.params)
: {},
}); });
emit('change', value, { emit('change', value, {

View File

@ -62,14 +62,15 @@ import type {
CodeSelectColConfig, CodeSelectColConfig,
ContainerChangeEventData, ContainerChangeEventData,
DataSourceMethodSelectConfig, DataSourceMethodSelectConfig,
DynamicTypeConfig,
EventSelectConfig, EventSelectConfig,
FieldProps, FieldProps,
FormState, FormState,
OnChangeHandlerData,
PanelConfig, PanelConfig,
TableConfig, TableConfig,
UISelectConfig,
} from '@tmagic/form'; } from '@tmagic/form';
import { MContainer as MFormContainer, MPanel, MTable } from '@tmagic/form'; import { defineFormItem, MContainer as MFormContainer, MPanel, MTable } from '@tmagic/form';
import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX, traverseNode } from '@tmagic/utils'; import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX, traverseNode } from '@tmagic/utils';
import { useServices } from '@editor/hooks/use-services'; import { useServices } from '@editor/hooks/use-services';
@ -212,12 +213,12 @@ const actionTypeConfig = computed(() => {
// //
const targetCompConfig = computed(() => { const targetCompConfig = computed(() => {
const defaultTargetCompConfig = { const defaultTargetCompConfig: UISelectConfig = {
name: 'to', name: 'to',
text: '联动组件', text: '联动组件',
type: 'ui-select', type: 'ui-select',
display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP, display: (_mForm, { model }) => model.actionType === ActionType.COMP,
onChange: (MForm: FormState, v: string, { setModel }: OnChangeHandlerData) => { onChange: (_MForm, _v, { setModel }) => {
setModel('method', ''); setModel('method', '');
}, },
}; };
@ -226,7 +227,7 @@ const targetCompConfig = computed(() => {
// //
const compActionConfig = computed(() => { const compActionConfig = computed(() => {
const defaultCompActionConfig = { const defaultCompActionConfig: DynamicTypeConfig = {
name: 'method', name: 'method',
text: '动作', text: '动作',
type: (mForm: FormState | undefined, { model }: any) => { type: (mForm: FormState | undefined, { model }: any) => {
@ -304,62 +305,68 @@ const dataSourceActionConfig = computed(() => {
}); });
// //
const tableConfig = computed<TableConfig>(() => ({ const tableConfig = computed(
type: 'table', () =>
name: 'events', defineFormItem({
items: [ type: 'table',
{ name: 'events',
name: 'name', items: [
label: '事件名', {
type: eventNameConfig.value.type, name: 'name',
options: (mForm: FormState, { formValue }: any) => label: '事件名',
eventsService.getEvent(formValue.type).map((option: any) => ({ type: eventNameConfig.value.type,
text: option.label, options: (mForm: FormState, { formValue }: any) =>
value: option.value, eventsService.getEvent(formValue.type).map((option: any) => ({
})), text: option.label,
}, value: option.value,
{ })),
name: 'to', },
label: '联动组件', {
type: 'ui-select', name: 'to',
}, label: '联动组件',
{ type: 'ui-select',
name: 'method', },
label: '动作', {
type: compActionConfig.value.type, name: 'method',
options: (mForm: FormState, { model }: any) => { label: '动作',
const node = editorService.getNodeById(model.to); type: compActionConfig.value.type,
if (!node?.type) return []; options: (mForm: FormState, { model }: any) => {
const node = editorService.getNodeById(model.to);
if (!node?.type) return [];
return eventsService.getMethod(node.type, model.to).map((option: any) => ({ return eventsService.getMethod(node.type, model.to).map((option: any) => ({
text: option.label, text: option.label,
value: option.value, value: option.value,
})); }));
}, },
}, },
], ],
})); }) as TableConfig,
);
// //
const actionsConfig = computed<PanelConfig>(() => ({ const actionsConfig = computed(
type: 'panel', () =>
items: [ defineFormItem({
{ type: 'panel',
type: 'group-list',
name: 'actions',
expandAll: true,
enableToggleMode: false,
titlePrefix: '动作',
items: [ items: [
actionTypeConfig.value, {
targetCompConfig.value, type: 'group-list',
compActionConfig.value, name: 'actions',
codeActionConfig.value, expandAll: true,
dataSourceActionConfig.value, enableToggleMode: false,
titlePrefix: '动作',
items: [
actionTypeConfig.value,
targetCompConfig.value,
compActionConfig.value,
codeActionConfig.value,
dataSourceActionConfig.value,
],
},
], ],
}, }) as PanelConfig,
], );
}));
// //
const isOldVersion = computed(() => { const isOldVersion = computed(() => {

View File

@ -39,46 +39,48 @@
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import type { ContainerChangeEventData, FormValue } from '@tmagic/form'; import type { ContainerChangeEventData, FormValue } from '@tmagic/form';
import { MContainer } from '@tmagic/form'; import { defineFormItem, type MContainer } from '@tmagic/form';
import type { StyleSchema } from '@tmagic/schema'; import type { StyleSchema } from '@tmagic/schema';
const direction = ref(''); const direction = ref('');
const config = computed(() => ({ const config = computed(() =>
items: [ defineFormItem({
{ items: [
name: `border${direction.value}Width`, {
text: '边框宽度', name: `border${direction.value}Width`,
labelWidth: '68px', text: '边框宽度',
type: 'data-source-field-select', labelWidth: '68px',
fieldConfig: { type: 'data-source-field-select',
type: 'text', fieldConfig: {
type: 'text',
},
}, },
}, {
{ name: `border${direction.value}Color`,
name: `border${direction.value}Color`, text: '边框颜色',
text: '边框颜色', labelWidth: '68px',
labelWidth: '68px', type: 'data-source-field-select',
type: 'data-source-field-select', fieldConfig: {
fieldConfig: { type: 'colorPicker',
type: 'colorPicker', },
}, },
}, {
{ name: `border${direction.value}Style`,
name: `border${direction.value}Style`, text: '边框样式',
text: '边框样式', labelWidth: '68px',
labelWidth: '68px', type: 'data-source-field-select',
type: 'data-source-field-select', fieldConfig: {
fieldConfig: { type: 'select',
type: 'select', options: ['solid', 'dashed', 'dotted'].map((item) => ({
options: ['solid', 'dashed', 'dotted'].map((item) => ({ value: item,
value: item, text: item,
text: item, })),
})), },
}, },
}, ],
], }),
})); );
const selectDirection = (d?: string) => (direction.value = d || ''); const selectDirection = (d?: string) => (direction.value = d || '');

View File

@ -5,7 +5,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { markRaw } from 'vue'; import { markRaw } from 'vue';
import { ContainerChangeEventData, MContainer } from '@tmagic/form'; import { type ContainerChangeEventData, defineFormItem, type MContainer } from '@tmagic/form';
import type { StyleSchema } from '@tmagic/schema'; import type { StyleSchema } from '@tmagic/schema';
import BackgroundPosition from '../components/BackgroundPosition.vue'; import BackgroundPosition from '../components/BackgroundPosition.vue';
@ -21,7 +21,7 @@ const emit = defineEmits<{
change: [v: StyleSchema, eventData: ContainerChangeEventData]; change: [v: StyleSchema, eventData: ContainerChangeEventData];
}>(); }>();
const config = { const config = defineFormItem({
items: [ items: [
{ {
name: 'backgroundColor', name: 'backgroundColor',
@ -39,7 +39,7 @@ const config = {
type: 'data-source-field-select', type: 'data-source-field-select',
fieldConfig: { fieldConfig: {
type: 'img-upload', type: 'img-upload',
}, } as any,
}, },
{ {
name: 'backgroundSize', name: 'backgroundSize',
@ -74,7 +74,7 @@ const config = {
labelWidth: '68px', labelWidth: '68px',
}, },
], ],
}; });
const change = (value: StyleSchema, eventData: ContainerChangeEventData) => { const change = (value: StyleSchema, eventData: ContainerChangeEventData) => {
emit('change', value, eventData); emit('change', value, eventData);

View File

@ -4,7 +4,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { type ContainerChangeEventData, MContainer } from '@tmagic/form'; import { type ContainerChangeEventData, defineFormItem, type MContainer } from '@tmagic/form';
import type { StyleSchema } from '@tmagic/schema'; import type { StyleSchema } from '@tmagic/schema';
import Border from '../components/Border.vue'; import Border from '../components/Border.vue';
@ -19,7 +19,7 @@ const emit = defineEmits<{
change: [v: StyleSchema, eventData: ContainerChangeEventData]; change: [v: StyleSchema, eventData: ContainerChangeEventData];
}>(); }>();
const config = { const config = defineFormItem({
items: [ items: [
{ {
labelWidth: '68px', labelWidth: '68px',
@ -31,7 +31,7 @@ const config = {
}, },
}, },
], ],
}; });
const change = (value: StyleSchema, eventData: ContainerChangeEventData) => { const change = (value: StyleSchema, eventData: ContainerChangeEventData) => {
emit('change', value, eventData); emit('change', value, eventData);

View File

@ -5,7 +5,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { markRaw } from 'vue'; import { markRaw } from 'vue';
import { ContainerChangeEventData, MContainer } from '@tmagic/form'; import { type ContainerChangeEventData, defineFormItem, type MContainer } from '@tmagic/form';
import type { StyleSchema } from '@tmagic/schema'; import type { StyleSchema } from '@tmagic/schema';
import { AlignCenter, AlignLeft, AlignRight } from '../icons/text-align'; import { AlignCenter, AlignLeft, AlignRight } from '../icons/text-align';
@ -20,7 +20,7 @@ const emit = defineEmits<{
change: [v: StyleSchema, eventData: ContainerChangeEventData]; change: [v: StyleSchema, eventData: ContainerChangeEventData];
}>(); }>();
const config = { const config = defineFormItem({
items: [ items: [
{ {
type: 'row', type: 'row',
@ -86,7 +86,7 @@ const config = {
], ],
}, },
], ],
}; });
const change = (value: StyleSchema, eventData: ContainerChangeEventData) => { const change = (value: StyleSchema, eventData: ContainerChangeEventData) => {
emit('change', value, eventData); emit('change', value, eventData);

View File

@ -12,7 +12,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { markRaw } from 'vue'; import { markRaw } from 'vue';
import type { ContainerChangeEventData } from '@tmagic/form'; import type { ChildConfig, ContainerChangeEventData } from '@tmagic/form';
import { defineFormItem, MContainer } from '@tmagic/form'; import { defineFormItem, MContainer } from '@tmagic/form';
import type { StyleSchema } from '@tmagic/schema'; import type { StyleSchema } from '@tmagic/schema';
@ -180,7 +180,7 @@ const config = defineFormItem({
], ],
}, },
], ],
}); }) as ChildConfig;
const change = (value: string | StyleSchema, eventData: ContainerChangeEventData) => { const change = (value: string | StyleSchema, eventData: ContainerChangeEventData) => {
emit('change', value, eventData); emit('change', value, eventData);

View File

@ -3,7 +3,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ContainerChangeEventData, MContainer } from '@tmagic/form'; import { type ContainerChangeEventData, defineFormItem, type MContainer } from '@tmagic/form';
import type { StyleSchema } from '@tmagic/schema'; import type { StyleSchema } from '@tmagic/schema';
const props = defineProps<{ const props = defineProps<{
@ -24,7 +24,7 @@ const positionText: Record<string, string> = {
sticky: '粘性定位', sticky: '粘性定位',
}; };
const config = { const config = defineFormItem({
items: [ items: [
{ {
name: 'position', name: 'position',
@ -95,7 +95,7 @@ const config = {
}, },
}, },
], ],
}; });
const change = (value: string | StyleSchema, eventData: ContainerChangeEventData) => { const change = (value: string | StyleSchema, eventData: ContainerChangeEventData) => {
emit('change', value, eventData); emit('change', value, eventData);

View File

@ -1,6 +1,6 @@
import { defineFormConfig } from '@tmagic/form'; import { defineFormConfig, type FormConfig } from '@tmagic/form';
export default () => export default (): FormConfig =>
defineFormConfig([ defineFormConfig([
{ {
name: 'id', name: 'id',

View File

@ -1,11 +1,11 @@
import { DataSchema, DataSourceFieldType, DataSourceSchema } from '@tmagic/core'; import type { DataSchema, DataSourceFieldType, DataSourceSchema } from '@tmagic/core';
import { CascaderOption, FormConfig, FormState } from '@tmagic/form'; import { type CascaderOption, defineFormItem, type FormConfig } from '@tmagic/form';
import { dataSourceTemplateRegExp, getKeysArray, isNumber } from '@tmagic/utils'; import { dataSourceTemplateRegExp, getKeysArray, isNumber } from '@tmagic/utils';
import BaseFormConfig from './formConfigs/base'; import BaseFormConfig from './formConfigs/base';
import HttpFormConfig from './formConfigs/http'; import HttpFormConfig from './formConfigs/http';
const dataSourceFormConfig = { const dataSourceFormConfig = defineFormItem({
type: 'tab', type: 'tab',
items: [ items: [
{ {
@ -50,7 +50,7 @@ const dataSourceFormConfig = {
}, },
{ {
title: '请求参数裁剪', title: '请求参数裁剪',
display: (_formState: FormState, { model }: any) => model.type === 'http', display: (_formState, { model }) => model.type === 'http',
items: [ items: [
{ {
name: 'beforeRequest', name: 'beforeRequest',
@ -62,7 +62,7 @@ const dataSourceFormConfig = {
}, },
{ {
title: '响应数据裁剪', title: '响应数据裁剪',
display: (_formState: FormState, { model }: any) => model.type === 'http', display: (_formStat, { model }) => model.type === 'http',
items: [ items: [
{ {
name: 'afterResponse', name: 'afterResponse',
@ -73,7 +73,7 @@ const dataSourceFormConfig = {
], ],
}, },
], ],
}; });
const fillConfig = (config: FormConfig): FormConfig => [...BaseFormConfig(), ...config, dataSourceFormConfig]; const fillConfig = (config: FormConfig): FormConfig => [...BaseFormConfig(), ...config, dataSourceFormConfig];

View File

@ -24,7 +24,7 @@ import {
NODE_DISABLE_DATA_SOURCE_KEY, NODE_DISABLE_DATA_SOURCE_KEY,
} from '@tmagic/core'; } from '@tmagic/core';
import { tMagicMessage } from '@tmagic/design'; import { tMagicMessage } from '@tmagic/design';
import type { ChildConfig, FormConfig, TabConfig, TabPaneConfig } from '@tmagic/form'; import type { ChildConfig, DisplayCondsConfig, FormConfig, TabConfig, TabPaneConfig } from '@tmagic/form';
export const arrayOptions = [ export const arrayOptions = [
{ text: '包含', value: 'include' }, { text: '包含', value: 'include' },
@ -168,7 +168,7 @@ export const advancedTabConfig: TabPaneConfig = {
], ],
}; };
export const displayTabConfig: TabPaneConfig = { export const displayTabConfig: TabPaneConfig<DisplayCondsConfig> = {
title: '显示条件', title: '显示条件',
display: (_state, { model }) => model.type !== 'page', display: (_state, { model }) => model.type !== 'page',
items: [ items: [

View File

@ -91,12 +91,10 @@ export interface FormItem {
/** vnode的key值默认是遍历数组时的index */ /** vnode的key值默认是遍历数组时的index */
__key?: string | number; __key?: string | number;
/** 表单域标签的的宽度,例如 '50px'。支持 auto。 */ /** 表单域标签的的宽度,例如 '50px'。支持 auto。 */
labelWidth?: string; labelWidth?: string | number;
/** label 标签的title属性 */ /** label 标签的title属性 */
labelTitle?: string; labelTitle?: string;
className?: string; className?: string;
/** 表单组件类型 */
type?: string | TypeFunction;
/** 字段名 */ /** 字段名 */
name?: string | number; name?: string | number;
/** 额外的提示信息,和 help 类似,当提示文案同时出现时,可以使用这个。 */ /** 额外的提示信息,和 help 类似,当提示文案同时出现时,可以使用这个。 */
@ -132,7 +130,12 @@ export interface FormItem {
labelPosition?: 'top' | 'left' | 'right'; labelPosition?: 'top' | 'left' | 'right';
} }
export interface ContainerCommonConfig<T extends Record<string, any> = never> { export interface DynamicTypeConfig extends FormItem {
type: TypeFunction;
[key: string]: any;
}
export interface ContainerCommonConfig<T = never> extends FormItem {
items: FormConfig<T>; items: FormConfig<T>;
onInitValue?: ( onInitValue?: (
mForm: FormState | undefined, mForm: FormState | undefined,
@ -182,12 +185,12 @@ export interface Input {
placeholder?: string; placeholder?: string;
} }
export type TypeFunction = ( export type TypeFunction<T extends string = string> = (
mForm: FormState | undefined, mForm: FormState | undefined,
data: { data: {
model: FormValue; model: FormValue;
}, },
) => string; ) => T;
export type FilterFunction<T = boolean> = ( export type FilterFunction<T = boolean> = (
mForm: FormState | undefined, mForm: FormState | undefined,
@ -208,6 +211,7 @@ export type FilterFunction<T = boolean> = (
*/ */
export interface SelectConfigOption { export interface SelectConfigOption {
/** 选项的标签 */ /** 选项的标签 */
label?: string | SelectOptionTextFunction;
text: string | SelectOptionTextFunction; text: string | SelectOptionTextFunction;
/** 选项的值 */ /** 选项的值 */
value: any | SelectOptionValueFunction; value: any | SelectOptionValueFunction;
@ -499,7 +503,7 @@ export interface CheckboxGroupOption {
* *
*/ */
export interface CheckboxGroupConfig extends FormItem { export interface CheckboxGroupConfig extends FormItem {
type: 'checkbox-group'; type: 'checkbox-group' | 'checkboxGroup';
options: CheckboxGroupOption[] | FilterFunction<CheckboxGroupOption[]>; options: CheckboxGroupOption[] | FilterFunction<CheckboxGroupOption[]>;
} }
@ -546,7 +550,7 @@ export interface SelectConfig extends FormItem, Input {
/** /**
* *
*/ */
export interface LinkConfig<T extends Record<string, any> = never> extends FormItem { export interface LinkConfig<T = never> extends FormItem {
type: 'link'; type: 'link';
href?: string | ((model: Record<string, any>) => string); href?: string | ((model: Record<string, any>) => string);
css?: { css?: {
@ -615,7 +619,7 @@ export interface CascaderConfig extends FormItem, Input {
} }
export interface DynamicFieldConfig extends FormItem { export interface DynamicFieldConfig extends FormItem {
type: 'dynamic-field'; type: 'dynamic-field' | 'dynamicField';
returnFields: ( returnFields: (
config: DynamicFieldConfig, config: DynamicFieldConfig,
model: Record<any, any>, model: Record<any, any>,
@ -631,16 +635,16 @@ export interface DynamicFieldConfig extends FormItem {
/** /**
* *
*/ */
export interface RowConfig<T extends Record<string, any> = never> extends FormItem { export interface RowConfig<T = never> extends FormItem {
type: 'row'; type: 'row';
span: number; span: number;
items: ({ span?: number } & (ChildConfig<T> | EditorChildConfig | NoInfer<T>))[]; items: ({ span?: number } & (ChildConfig<T> | EditorChildConfig | T))[];
} }
/** /**
* *
*/ */
export interface TabPaneConfig<T extends Record<string, any> = never> { export interface TabPaneConfig<T = never> {
status?: string; status?: string;
/** 标签页名称,用于关联 model 中的数据 */ /** 标签页名称,用于关联 model 中的数据 */
name?: string | number; name?: string | number;
@ -652,7 +656,7 @@ export interface TabPaneConfig<T extends Record<string, any> = never> {
onTabClick?: (mForm: FormState | undefined, tab: any, data: any) => void; onTabClick?: (mForm: FormState | undefined, tab: any, data: any) => void;
} }
export interface TabConfig<T extends Record<string, any> = never> extends FormItem, ContainerCommonConfig<T> { export interface TabConfig<T = never> extends FormItem, ContainerCommonConfig<T> {
type: 'tab' | 'dynamic-tab'; type: 'tab' | 'dynamic-tab';
tabType?: string; tabType?: string;
editable?: boolean; editable?: boolean;
@ -673,7 +677,7 @@ export interface TabConfig<T extends Record<string, any> = never> extends FormIt
/** /**
* *
*/ */
export interface FieldsetConfig<T extends Record<string, any> = never> extends FormItem, ContainerCommonConfig<T> { export interface FieldsetConfig<T = never> extends FormItem, ContainerCommonConfig<T> {
type: 'fieldset'; type: 'fieldset';
checkbox?: checkbox?:
| boolean | boolean
@ -690,7 +694,7 @@ export interface FieldsetConfig<T extends Record<string, any> = never> extends F
/** /**
* *
*/ */
export interface PanelConfig<T extends Record<string, any> = never> extends FormItem, ContainerCommonConfig<T> { export interface PanelConfig<T = never> extends FormItem, ContainerCommonConfig<T> {
type: 'panel'; type: 'panel';
expand?: boolean; expand?: boolean;
title?: string; title?: string;
@ -705,6 +709,7 @@ export interface TableColumnConfig extends FormItem {
items?: FormConfig; items?: FormConfig;
itemsFunction?: (row: any) => FormConfig; itemsFunction?: (row: any) => FormConfig;
titleTip?: FilterFunction<string>; titleTip?: FilterFunction<string>;
type?: string;
} }
/** /**
@ -765,7 +770,7 @@ export interface TableConfig extends FormItem {
sortKey?: string; sortKey?: string;
} }
export interface GroupListConfig<T extends Record<string, any> = never> extends FormItem { export interface GroupListConfig<T = never> extends FormItem {
type: 'table' | 'groupList' | 'group-list'; type: 'table' | 'groupList' | 'group-list';
span?: number; span?: number;
enableToggleMode?: boolean; enableToggleMode?: boolean;
@ -801,11 +806,11 @@ export interface GroupListConfig<T extends Record<string, any> = never> extends
}; };
} }
interface StepItemConfig<T extends Record<string, any> = never> extends FormItem, ContainerCommonConfig<T> { interface StepItemConfig<T = never> extends FormItem, ContainerCommonConfig<T> {
title: string; title: string;
} }
export interface StepConfig<T extends Record<string, any> = never> extends FormItem { export interface StepConfig<T = never> extends FormItem {
type: 'step'; type: 'step';
/** 每个 step 的间距,不填写将自适应间距。支持百分比。 */ /** 每个 step 的间距,不填写将自适应间距。支持百分比。 */
space?: string | number; space?: string | number;
@ -820,14 +825,14 @@ export interface ComponentConfig extends FormItem {
component: any; component: any;
} }
export interface FlexLayoutConfig<T extends Record<string, any> = never> extends FormItem, ContainerCommonConfig<T> { export interface FlexLayoutConfig<T = never> extends FormItem, ContainerCommonConfig<T> {
type: 'flex-layout'; type: 'flex-layout';
/** flex 子项间距,默认 '16px' */ /** flex 子项间距,默认 '16px' */
gap?: string; gap?: string;
} }
export type ChildConfig<T extends Record<string, any> = never> = export type ChildConfig<T = never> =
| (FormItem & Partial<ContainerCommonConfig<T>>) | ContainerCommonConfig<T>
| TabConfig<T> | TabConfig<T>
| RowConfig<T> | RowConfig<T>
| FieldsetConfig<T> | FieldsetConfig<T>
@ -859,6 +864,6 @@ export type ChildConfig<T extends Record<string, any> = never> =
| ComponentConfig | ComponentConfig
| FlexLayoutConfig<T>; | FlexLayoutConfig<T>;
export type FormItemConfig<T extends Record<string, any> = never> = ChildConfig<T> | EditorChildConfig<T> | NoInfer<T>; export type FormItemConfig<T = never> = ChildConfig<T> | DynamicTypeConfig | EditorChildConfig<T> | T;
export type FormConfig<T extends Record<string, any> = never> = FormItemConfig<T>[]; export type FormConfig<T = never> = FormItemConfig<T>[];

View File

@ -2,7 +2,7 @@ import type { DataSourceFieldType, DataSourceSchema } from '@tmagic/schema';
import type { FilterFunction, FormItem, FormItemConfig, FormState, Input } from './base'; import type { FilterFunction, FormItem, FormItemConfig, FormState, Input } from './base';
export interface DataSourceFieldSelectConfig<T extends Record<string, any> = never> extends FormItem { export interface DataSourceFieldSelectConfig<T = never> extends FormItem {
type: 'data-source-field-select'; type: 'data-source-field-select';
/** /**
* data * data
@ -104,6 +104,7 @@ export interface DataSourceSelect extends FormItem, Input {
} }
export interface DisplayCondsConfig extends FormItem { export interface DisplayCondsConfig extends FormItem {
type: 'display-conds';
titlePrefix?: string; titlePrefix?: string;
parentFields?: string[] | FilterFunction<string[]>; parentFields?: string[] | FilterFunction<string[]>;
} }
@ -144,7 +145,7 @@ export interface StyleSetterConfig extends FormItem {
type: 'style-setter'; type: 'style-setter';
} }
export type EditorChildConfig<T extends Record<string, any> = never> = export type EditorChildConfig<T = never> =
| DataSourceFieldSelectConfig<T> | DataSourceFieldSelectConfig<T>
| CodeConfig | CodeConfig
| CodeLinkConfig | CodeLinkConfig

View File

@ -3,7 +3,6 @@ import type { FormConfig, FormItemConfig } from './base';
export * from './base'; export * from './base';
export * from './editor'; export * from './editor';
export const defineFormConfig = <T extends Record<string, any> = never>(config: FormConfig<T>): FormConfig<T> => config; export const defineFormConfig = <T = never>(config: FormConfig<T>): FormConfig<T> => config;
export const defineFormItem = <T extends Record<string, any> = never>(config: FormItemConfig<T>): FormItemConfig<T> => export const defineFormItem = <T = never>(config: FormItemConfig<T>): FormItemConfig<T> => config;
config;

View File

@ -117,19 +117,25 @@ const stepActive = ref(1);
const bodyHeight = ref(`${document.body.clientHeight - 194}px`); const bodyHeight = ref(`${document.body.clientHeight - 194}px`);
const stepCount = computed(() => { const stepCount = computed(() => {
const { length } = props.config; if (!Array.isArray(props.config)) {
for (let index = 0; index < length; index++) { return 0;
if (props.config[index].type === 'step') { }
return (props.config[index] as StepConfig).items.length;
for (const item of props.config) {
if ('type' in item && item.type === 'step') {
return (item as StepConfig).items.length;
} }
} }
return 0; return 0;
}); });
const hasStep = computed(() => { const hasStep = computed(() => {
const { length } = props.config; if (!Array.isArray(props.config)) {
for (let index = 0; index < length; index++) { return false;
if (props.config[index].type === 'step') { }
for (const item of props.config) {
if ('type' in item && item.type === 'step') {
return true; return true;
} }
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<TMagicCol v-show="display && config.type !== 'hidden'" :span="span"> <TMagicCol v-show="display && 'type' in config && config.type !== 'hidden'" :span="span">
<Container <Container
:model="model" :model="model"
:lastValues="lastValues" :lastValues="lastValues"
@ -21,7 +21,7 @@ import { computed, inject } from 'vue';
import { TMagicCol } from '@tmagic/design'; import { TMagicCol } from '@tmagic/design';
import type { ChildConfig, ContainerChangeEventData, FormState } from '../schema'; import type { ContainerChangeEventData, FormItemConfig, FormState } from '../schema';
import { display as displayFunction } from '../utils/form'; import { display as displayFunction } from '../utils/form';
import Container from './Container.vue'; import Container from './Container.vue';
@ -34,8 +34,8 @@ const props = defineProps<{
model: any; model: any;
lastValues?: any; lastValues?: any;
isCompare?: boolean; isCompare?: boolean;
config: ChildConfig; config: FormItemConfig;
labelWidth?: string; labelWidth?: string | number;
expandMore?: boolean; expandMore?: boolean;
span?: number; span?: number;
size?: string; size?: string;

View File

@ -175,10 +175,10 @@ import { getValueByKeyPath } from '@tmagic/utils';
import MHidden from '../fields/Hidden.vue'; import MHidden from '../fields/Hidden.vue';
import type { import type {
CheckboxConfig, CheckboxConfig,
ChildConfig,
ComponentConfig, ComponentConfig,
ContainerChangeEventData, ContainerChangeEventData,
ContainerCommonConfig, ContainerCommonConfig,
FormItemConfig,
FormState, FormState,
FormValue, FormValue,
ToolTipConfigType, ToolTipConfigType,
@ -198,10 +198,10 @@ const props = withDefaults(
model: FormValue; model: FormValue;
/** 需对比的值(开启对比模式时传入) */ /** 需对比的值(开启对比模式时传入) */
lastValues?: FormValue; lastValues?: FormValue;
config: ChildConfig; config: FormItemConfig;
prop?: string; prop?: string;
disabled?: boolean; disabled?: boolean;
labelWidth?: string; labelWidth?: string | number;
expandMore?: boolean; expandMore?: boolean;
stepActive?: string | number; stepActive?: string | number;
size?: string; size?: string;
@ -253,7 +253,7 @@ const itemProp = computed(() => {
}); });
const type = computed((): string => { const type = computed((): string => {
let { type } = props.config; let type = 'type' in props.config ? props.config.type : '';
type = type && filterFunction<string>(mForm, type, props); type = type && filterFunction<string>(mForm, type, props);
if (type === 'form') return ''; if (type === 'form') return '';
if (type === 'container') return ''; if (type === 'container') return '';

View File

@ -1,7 +1,7 @@
import { computed, inject } from 'vue'; import { computed, inject } from 'vue';
import { tMagicMessage } from '@tmagic/design'; import { tMagicMessage } from '@tmagic/design';
import type { FormState } from '@tmagic/form-schema'; import type { FormConfig, FormState } from '@tmagic/form-schema';
import { initValue } from '../utils/form'; import { initValue } from '../utils/form';
@ -86,7 +86,7 @@ export const useAdd = (
} }
inputs = await initValue(mForm, { inputs = await initValue(mForm, {
config: columns, config: columns as FormConfig,
initValues: inputs, initValues: inputs,
}); });
} }

View File

@ -3,7 +3,7 @@ import { WarningFilled } from '@element-plus/icons-vue';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { type TableColumnOptions, TMagicIcon, TMagicTooltip } from '@tmagic/design'; import { type TableColumnOptions, TMagicIcon, TMagicTooltip } from '@tmagic/design';
import type { FormState, TableColumnConfig } from '@tmagic/form-schema'; import type { FormItemConfig, FormState, TableColumnConfig } from '@tmagic/form-schema';
import Container from '../containers/Container.vue'; import Container from '../containers/Container.vue';
import type { ContainerChangeEventData } from '../schema'; import type { ContainerChangeEventData } from '../schema';
@ -68,7 +68,7 @@ export const useTableColumns = (
return `${props.prop}${props.prop ? '.' : ''}${index + 1 + currentPage.value * pageSize.value - 1}`; return `${props.prop}${props.prop ? '.' : ''}${index + 1 + currentPage.value * pageSize.value - 1}`;
}; };
const makeConfig = (config: TableColumnConfig, row: any) => { const makeConfig = (config: TableColumnConfig, row: any): TableColumnConfig => {
const newConfig = cloneDeep(config); const newConfig = cloneDeep(config);
if (typeof config.itemsFunction === 'function') { if (typeof config.itemsFunction === 'function') {
newConfig.items = config.itemsFunction(row); newConfig.items = config.itemsFunction(row);
@ -199,7 +199,7 @@ export const useTableColumns = (
disabled: props.disabled, disabled: props.disabled,
prop: getProp($index), prop: getProp($index),
rules: column.rules, rules: column.rules,
config: makeConfig(column, row), config: makeConfig(column, row) as FormItemConfig,
model: row, model: row,
lastValues: lastData.value[$index], lastValues: lastData.value[$index],
isCompare: props.isCompare, isCompare: props.isCompare,

View File

@ -23,13 +23,12 @@ import { cloneDeep } from 'lodash-es';
import { getValueByKeyPath } from '@tmagic/utils'; import { getValueByKeyPath } from '@tmagic/utils';
import { import type {
ChildConfig, ChildConfig,
ContainerCommonConfig, ContainerCommonConfig,
DaterangeConfig, DaterangeConfig,
FilterFunction, FilterFunction,
FormConfig, FormConfig,
FormItem,
FormState, FormState,
FormValue, FormValue,
HtmlField, HtmlField,
@ -120,7 +119,8 @@ const initValueItem = function (
) { ) {
const { items } = item as ContainerCommonConfig; const { items } = item as ContainerCommonConfig;
const { names } = item as DaterangeConfig; const { names } = item as DaterangeConfig;
const { type, name } = item as FormItem; const type = 'type' in item ? item.type : '';
const { name } = item;
if (isTableSelect(type) && name) { if (isTableSelect(type) && name) {
value[name] = initValue[name] ?? ''; value[name] = initValue[name] ?? '';
@ -172,8 +172,8 @@ export const createValues = function (
value: FormValue = {}, value: FormValue = {},
) { ) {
if (Array.isArray(config)) { if (Array.isArray(config)) {
config.forEach((item: ChildConfig | TabPaneConfig) => { config.forEach((item) => {
initValueItem(mForm, item, initValue, value); initValueItem(mForm, item as ChildConfig | TabPaneConfig, initValue, value);
}); });
} }

View File

@ -5,7 +5,7 @@
<MForm <MForm
v-else-if="(config.type || config.editInlineFormConfig) && editState[index]" v-else-if="(config.type || config.editInlineFormConfig) && editState[index]"
label-width="0" label-width="0"
:config="config.editInlineFormConfig ?? [config]" :config="config.editInlineFormConfig ?? [config as FormItemConfig]"
:init-values="editState[index]" :init-values="editState[index]"
@change="formChangeHandler" @change="formChangeHandler"
></MForm> ></MForm>
@ -46,7 +46,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { TMagicButton, TMagicTag, TMagicTooltip } from '@tmagic/design'; import { TMagicButton, TMagicTag, TMagicTooltip } from '@tmagic/design';
import { type ContainerChangeEventData, MForm } from '@tmagic/form'; import { type ContainerChangeEventData, MForm } from '@tmagic/form';
import type { FormValue } from '@tmagic/form-schema'; import type { FormItemConfig, FormValue } from '@tmagic/form-schema';
import { setValueByKeyPath } from '@tmagic/utils'; import { setValueByKeyPath } from '@tmagic/utils';
import { ColumnConfig } from './schema'; import { ColumnConfig } from './schema';