|
|
@ -6,16 +6,20 @@ |
|
|
:model="formModel" |
|
|
:model="formModel" |
|
|
@keypress.enter="handleEnterPress" |
|
|
@keypress.enter="handleEnterPress" |
|
|
> |
|
|
> |
|
|
<Row v-bind="{ ...getRow }"> |
|
|
<Tabs |
|
|
<slot name="formHeader"></slot> |
|
|
v-model:activeKey="activedTabKey" |
|
|
<Tabs v-model="activedTabKey" style="width: 100%"> |
|
|
:style="tabsStyle.style" |
|
|
<!-- fix bug: forceRender 必须强制渲染,否则form验证会失效 --> |
|
|
:tabBarStyle="tabsStyle.tabBarStyle" |
|
|
<TabPane |
|
|
> |
|
|
v-for="tabSchema in getTabSchema" |
|
|
<!-- fix bug: forceRender 必须强制渲染,否则form验证会失效 --> |
|
|
:key="tabSchema.key" |
|
|
<TabPane |
|
|
:tab="tabSchema.key" |
|
|
v-for="tabSchema in getTabSchema" |
|
|
:forceRender="true" |
|
|
:key="tabSchema.key" |
|
|
> |
|
|
:tab="tabSchema.key" |
|
|
|
|
|
:forceRender="true" |
|
|
|
|
|
> |
|
|
|
|
|
<Row v-bind="getRow"> |
|
|
|
|
|
<slot name="formHeader"></slot> |
|
|
<template v-for="schema in tabSchema.schemas" :key="schema.field"> |
|
|
<template v-for="schema in tabSchema.schemas" :key="schema.field"> |
|
|
<FormItem |
|
|
<FormItem |
|
|
:tableAction="tableAction" |
|
|
:tableAction="tableAction" |
|
|
@ -27,19 +31,21 @@ |
|
|
:setFormModel="setFormModel" |
|
|
:setFormModel="setFormModel" |
|
|
> |
|
|
> |
|
|
<template #[item]="data" v-for="item in Object.keys($slots)"> |
|
|
<template #[item]="data" v-for="item in Object.keys($slots)"> |
|
|
<slot :name="item" v-bind="data"></slot> |
|
|
<slot :name="item" v-bind="data || {}"></slot> |
|
|
</template> |
|
|
</template> |
|
|
</FormItem> |
|
|
</FormItem> |
|
|
</template> |
|
|
</template> |
|
|
</TabPane> |
|
|
</Row> |
|
|
</Tabs> |
|
|
</TabPane> |
|
|
|
|
|
</Tabs> |
|
|
|
|
|
|
|
|
|
|
|
<Row v-bind="getRow"> |
|
|
<FormAction v-bind="getFormActionBindProps" @toggle-advanced="handleToggleAdvanced"> |
|
|
<FormAction v-bind="getFormActionBindProps" @toggle-advanced="handleToggleAdvanced"> |
|
|
<template |
|
|
<template |
|
|
#[item]="data" |
|
|
#[item]="data" |
|
|
v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']" |
|
|
v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']" |
|
|
> |
|
|
> |
|
|
<slot :name="item" v-bind="data"></slot> |
|
|
<slot :name="item" v-bind="data || {}"></slot> |
|
|
</template> |
|
|
</template> |
|
|
</FormAction> |
|
|
</FormAction> |
|
|
<slot name="formFooter"></slot> |
|
|
<slot name="formFooter"></slot> |
|
|
@ -48,8 +54,7 @@ |
|
|
</template> |
|
|
</template> |
|
|
|
|
|
|
|
|
<script lang="ts"> |
|
|
<script lang="ts"> |
|
|
import dayjs from 'dayjs'; |
|
|
import type { TabFormActionType, TabFormProps, TabFormSchema } from './types/form'; |
|
|
import type { FormActionType, TabFormProps, FormSchema, TabFormSchema } from './types/form'; |
|
|
|
|
|
import type { AdvanceState } from './types/hooks'; |
|
|
import type { AdvanceState } from './types/hooks'; |
|
|
import type { Ref } from 'vue'; |
|
|
import type { Ref } from 'vue'; |
|
|
|
|
|
|
|
|
@ -70,11 +75,12 @@ |
|
|
import { useFormEvents } from './hooks/useFormEvents'; |
|
|
import { useFormEvents } from './hooks/useFormEvents'; |
|
|
import { createFormContext } from './hooks/useFormContext'; |
|
|
import { createFormContext } from './hooks/useFormContext'; |
|
|
import { useAutoFocus } from './hooks/useAutoFocus'; |
|
|
import { useAutoFocus } from './hooks/useAutoFocus'; |
|
|
|
|
|
import { useTabsStyle } from '/@/hooks/component/useStyles'; |
|
|
import { useModalContext } from '/@/components/Modal'; |
|
|
import { useModalContext } from '/@/components/Modal'; |
|
|
|
|
|
import { useDebounceFn } from '@vueuse/core'; |
|
|
|
|
|
|
|
|
import { tabProps } from './props'; |
|
|
import { tabProps } from './props'; |
|
|
import { useDesign } from '/@/hooks/web/useDesign'; |
|
|
import { useDesign } from '/@/hooks/web/useDesign'; |
|
|
import { mergeWith } from 'lodash-es'; |
|
|
|
|
|
|
|
|
|
|
|
export default defineComponent({ |
|
|
export default defineComponent({ |
|
|
name: 'TabForm', |
|
|
name: 'TabForm', |
|
|
@ -87,10 +93,11 @@ |
|
|
TabPane: Tabs.TabPane, |
|
|
TabPane: Tabs.TabPane, |
|
|
}, |
|
|
}, |
|
|
props: tabProps, |
|
|
props: tabProps, |
|
|
emits: ['advanced-change', 'reset', 'submit', 'register'], |
|
|
emits: ['advanced-change', 'reset', 'submit', 'register', 'field-value-change'], |
|
|
setup(props, { emit, attrs }) { |
|
|
setup(props, { emit, attrs }) { |
|
|
const formModel = reactive<Recordable>({}); |
|
|
const formModel = reactive<Recordable>({}); |
|
|
const modalFn = useModalContext(); |
|
|
const modalFn = useModalContext(); |
|
|
|
|
|
const tabsStyle = useTabsStyle(); |
|
|
|
|
|
|
|
|
const advanceState = reactive<AdvanceState>({ |
|
|
const advanceState = reactive<AdvanceState>({ |
|
|
isAdvanced: true, |
|
|
isAdvanced: true, |
|
|
@ -104,17 +111,13 @@ |
|
|
const activedTabKey = ref(''); |
|
|
const activedTabKey = ref(''); |
|
|
const propsRef = ref<Partial<TabFormProps>>({}); |
|
|
const propsRef = ref<Partial<TabFormProps>>({}); |
|
|
const schemaRef = ref<Nullable<TabFormSchema[]>>(null); |
|
|
const schemaRef = ref<Nullable<TabFormSchema[]>>(null); |
|
|
const formElRef = ref<Nullable<FormActionType>>(null); |
|
|
const formElRef = ref<Nullable<TabFormActionType>>(null); |
|
|
|
|
|
|
|
|
const { prefixCls } = useDesign('basic-form'); |
|
|
const { prefixCls } = useDesign('basic-form'); |
|
|
|
|
|
|
|
|
const getBindValue = computed( |
|
|
|
|
|
() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable), |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// Get the basic configuration of the form |
|
|
// Get the basic configuration of the form |
|
|
const getProps = computed((): TabFormProps => { |
|
|
const getProps = computed((): TabFormProps => { |
|
|
return mergeWith(props, unref(propsRef)) as TabFormProps; |
|
|
return { ...props, ...unref(propsRef) } as TabFormProps; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const getFormClass = computed(() => { |
|
|
const getFormClass = computed(() => { |
|
|
@ -135,6 +138,10 @@ |
|
|
}; |
|
|
}; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
const getBindValue = computed( |
|
|
|
|
|
() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable), |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
const getSchema = computed((): TabFormSchema[] => { |
|
|
const getSchema = computed((): TabFormSchema[] => { |
|
|
const schemas: TabFormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any); |
|
|
const schemas: TabFormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any); |
|
|
for (const schema of schemas) { |
|
|
for (const schema of schemas) { |
|
|
@ -144,7 +151,7 @@ |
|
|
if (!Array.isArray(defaultValue)) { |
|
|
if (!Array.isArray(defaultValue)) { |
|
|
schema.defaultValue = dateUtil(defaultValue); |
|
|
schema.defaultValue = dateUtil(defaultValue); |
|
|
} else { |
|
|
} else { |
|
|
const def: dayjs.Dayjs[] = []; |
|
|
const def: any[] = []; |
|
|
defaultValue.forEach((item) => { |
|
|
defaultValue.forEach((item) => { |
|
|
def.push(dateUtil(item)); |
|
|
def.push(dateUtil(item)); |
|
|
}); |
|
|
}); |
|
|
@ -153,15 +160,15 @@ |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
if (unref(getProps).showAdvancedButton) { |
|
|
if (unref(getProps).showAdvancedButton) { |
|
|
return schemas.filter((schema) => schema.component !== 'Divider') as TabFormSchema[]; |
|
|
return schemas.filter((schema) => schema.component !== 'Divider'); |
|
|
} else { |
|
|
} else { |
|
|
return schemas as TabFormSchema[]; |
|
|
return schemas; |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const getTabSchema = computed((): { key: string; schemas: FormSchema[] }[] => { |
|
|
const getTabSchema = computed((): { key: string; schemas: TabFormSchema[] }[] => { |
|
|
// const schemas = unref(getSchema); |
|
|
// const schemas = unref(getSchema); |
|
|
const tabSchemas: { key: string; schemas: FormSchema[] }[] = []; |
|
|
const tabSchemas: { key: string; schemas: TabFormSchema[] }[] = []; |
|
|
const group = groupBy(getSchema.value, 'tab'); |
|
|
const group = groupBy(getSchema.value, 'tab'); |
|
|
Object.keys(group).forEach((key) => { |
|
|
Object.keys(group).forEach((key) => { |
|
|
tabSchemas.push({ |
|
|
tabSchemas.push({ |
|
|
@ -192,7 +199,7 @@ |
|
|
getSchema, |
|
|
getSchema, |
|
|
getProps, |
|
|
getProps, |
|
|
isInitedDefault: isInitedDefaultRef, |
|
|
isInitedDefault: isInitedDefaultRef, |
|
|
formElRef: formElRef as Ref<FormActionType>, |
|
|
formElRef: formElRef as Ref<TabFormActionType>, |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const { |
|
|
const { |
|
|
@ -214,7 +221,7 @@ |
|
|
formModel, |
|
|
formModel, |
|
|
getSchema, |
|
|
getSchema, |
|
|
defaultValueRef, |
|
|
defaultValueRef, |
|
|
formElRef: formElRef as Ref<FormActionType>, |
|
|
formElRef: formElRef as Ref<TabFormActionType>, |
|
|
schemaRef: schemaRef as Ref<TabFormSchema[]>, |
|
|
schemaRef: schemaRef as Ref<TabFormSchema[]>, |
|
|
handleFormValues, |
|
|
handleFormValues, |
|
|
}); |
|
|
}); |
|
|
@ -227,10 +234,12 @@ |
|
|
watch( |
|
|
watch( |
|
|
() => unref(getProps).model, |
|
|
() => unref(getProps).model, |
|
|
() => { |
|
|
() => { |
|
|
const { model } = unref(getProps); |
|
|
const { model, schemas } = unref(getProps); |
|
|
|
|
|
if (schemas?.length) { |
|
|
|
|
|
activedTabKey.value = schemas[0].tab; |
|
|
|
|
|
} |
|
|
if (!model) return; |
|
|
if (!model) return; |
|
|
setFieldsValue(model); |
|
|
setFieldsValue(model); |
|
|
activedTabKey.value = ''; |
|
|
|
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
immediate: true, |
|
|
immediate: true, |
|
|
@ -241,6 +250,9 @@ |
|
|
() => unref(getProps).schemas, |
|
|
() => unref(getProps).schemas, |
|
|
(schemas) => { |
|
|
(schemas) => { |
|
|
resetSchema(schemas ?? []); |
|
|
resetSchema(schemas ?? []); |
|
|
|
|
|
if (schemas?.length) { |
|
|
|
|
|
activedTabKey.value = schemas[0].tab; |
|
|
|
|
|
} |
|
|
}, |
|
|
}, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
@ -257,16 +269,30 @@ |
|
|
if (schema?.length) { |
|
|
if (schema?.length) { |
|
|
initDefault(); |
|
|
initDefault(); |
|
|
isInitedDefaultRef.value = true; |
|
|
isInitedDefaultRef.value = true; |
|
|
|
|
|
activedTabKey.value = schema[0].tab; |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
watch( |
|
|
|
|
|
() => formModel, |
|
|
|
|
|
useDebounceFn(() => { |
|
|
|
|
|
unref(getProps).submitOnChange && handleSubmit(); |
|
|
|
|
|
}, 300), |
|
|
|
|
|
{ deep: true }, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
async function setProps(formProps: Partial<TabFormProps>): Promise<void> { |
|
|
async function setProps(formProps: Partial<TabFormProps>): Promise<void> { |
|
|
propsRef.value = deepMerge(unref(propsRef) || {}, formProps); |
|
|
propsRef.value = deepMerge(unref(propsRef) || {}, formProps); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function setFormModel(key: string, value: any) { |
|
|
function setFormModel(key: string, value: any) { |
|
|
formModel[key] = value; |
|
|
formModel[key] = value; |
|
|
|
|
|
const { validateTrigger } = unref(getBindValue); |
|
|
|
|
|
if (!validateTrigger || validateTrigger === 'change') { |
|
|
|
|
|
validateFields([key]).catch((_) => {}); |
|
|
|
|
|
} |
|
|
|
|
|
emit('field-value-change', key, value); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function handleEnterPress(e: KeyboardEvent) { |
|
|
function handleEnterPress(e: KeyboardEvent) { |
|
|
@ -280,7 +306,11 @@ |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const formActionType: Partial<FormActionType> = { |
|
|
function changeTab(tab: string) { |
|
|
|
|
|
activedTabKey.value = tab; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const formActionType: Partial<TabFormActionType> = { |
|
|
getFieldsValue, |
|
|
getFieldsValue, |
|
|
setFieldsValue, |
|
|
setFieldsValue, |
|
|
resetFields, |
|
|
resetFields, |
|
|
@ -294,6 +324,7 @@ |
|
|
validate, |
|
|
validate, |
|
|
submit: handleSubmit, |
|
|
submit: handleSubmit, |
|
|
scrollToField: scrollToField, |
|
|
scrollToField: scrollToField, |
|
|
|
|
|
changeTab: changeTab, |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
onMounted(() => { |
|
|
onMounted(() => { |
|
|
@ -314,6 +345,7 @@ |
|
|
formElRef, |
|
|
formElRef, |
|
|
getSchema, |
|
|
getSchema, |
|
|
getTabSchema, |
|
|
getTabSchema, |
|
|
|
|
|
tabsStyle, |
|
|
formActionType: formActionType as any, |
|
|
formActionType: formActionType as any, |
|
|
setFormModel, |
|
|
setFormModel, |
|
|
getFormClass, |
|
|
getFormClass, |
|
|
@ -325,3 +357,51 @@ |
|
|
}, |
|
|
}, |
|
|
}); |
|
|
}); |
|
|
</script> |
|
|
</script> |
|
|
|
|
|
|
|
|
|
|
|
<style lang="less"> |
|
|
|
|
|
@prefix-cls: ~'@{namespace}-basic-form'; |
|
|
|
|
|
|
|
|
|
|
|
.@{prefix-cls} { |
|
|
|
|
|
.ant-form-item { |
|
|
|
|
|
&-label label::after { |
|
|
|
|
|
margin: 0 6px 0 2px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
&-with-help { |
|
|
|
|
|
margin-bottom: 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
&:not(.ant-form-item-with-help) { |
|
|
|
|
|
margin-bottom: 20px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
&.suffix-item { |
|
|
|
|
|
.ant-form-item-children { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.ant-form-item-control { |
|
|
|
|
|
margin-top: 4px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.suffix { |
|
|
|
|
|
display: inline-flex; |
|
|
|
|
|
padding-left: 6px; |
|
|
|
|
|
margin-top: 1px; |
|
|
|
|
|
line-height: 1; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.ant-form-explain { |
|
|
|
|
|
font-size: 14px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
&--compact { |
|
|
|
|
|
.ant-form-item { |
|
|
|
|
|
margin-bottom: 8px !important; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
</style> |
|
|
|