feat: Form useForm 完成
This commit is contained in:
parent
097b32e1a9
commit
3e4e27c21f
|
@ -115,7 +115,7 @@ defineExpose({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="border-1 border-solid border-[var(--tags-view-border-color)] z-99">
|
<div class="border-1 border-solid border-[var(--tags-view-border-color)] z-10">
|
||||||
<!-- 工具栏 -->
|
<!-- 工具栏 -->
|
||||||
<Toolbar
|
<Toolbar
|
||||||
:editor="editorRef"
|
:editor="editorRef"
|
||||||
|
|
|
@ -41,6 +41,8 @@ export interface FormExpose {
|
||||||
setSchema: (schemaProps: FormSetProps[]) => void
|
setSchema: (schemaProps: FormSetProps[]) => void
|
||||||
formModel: Recordable
|
formModel: Recordable
|
||||||
getElFormRef: () => ComponentRef<typeof ElForm>
|
getElFormRef: () => ComponentRef<typeof ElForm>
|
||||||
|
getComponentExpose: (field: string) => any
|
||||||
|
getFormItemExpose: (field: string) => any
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Form }
|
export { Form }
|
||||||
|
|
|
@ -73,6 +73,12 @@ export default defineComponent({
|
||||||
return propsObj
|
return propsObj
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 存储表单实例
|
||||||
|
const formComponents = ref({})
|
||||||
|
|
||||||
|
// 存储form-item实例
|
||||||
|
const formItemComponents = ref({})
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const formModel = ref<Recordable>({})
|
const formModel = ref<Recordable>({})
|
||||||
|
|
||||||
|
@ -125,7 +131,37 @@ export default defineComponent({
|
||||||
|
|
||||||
const getOptions = async (fn: Function, field: string) => {
|
const getOptions = async (fn: Function, field: string) => {
|
||||||
const options = await fn()
|
const options = await fn()
|
||||||
console.log(field, options)
|
setSchema([
|
||||||
|
{
|
||||||
|
field,
|
||||||
|
path: 'componentProps.options',
|
||||||
|
value: options
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 获取表单组件实例
|
||||||
|
* @param filed 表单字段
|
||||||
|
*/
|
||||||
|
const getComponentExpose = (filed: string) => {
|
||||||
|
return unref(formComponents)[filed]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 获取formItem实例
|
||||||
|
* @param filed 表单字段
|
||||||
|
*/
|
||||||
|
const getFormItemExpose = (filed: string) => {
|
||||||
|
return unref(formItemComponents)[filed]
|
||||||
|
}
|
||||||
|
|
||||||
|
const setComponentRefMap = (ref: any, filed: string) => {
|
||||||
|
formComponents.value[filed] = ref
|
||||||
|
}
|
||||||
|
|
||||||
|
const setFormItemRefMap = (ref: any, filed: string) => {
|
||||||
|
formItemComponents.value[filed] = ref
|
||||||
}
|
}
|
||||||
|
|
||||||
expose({
|
expose({
|
||||||
|
@ -135,14 +171,15 @@ export default defineComponent({
|
||||||
delSchema,
|
delSchema,
|
||||||
addSchema,
|
addSchema,
|
||||||
setSchema,
|
setSchema,
|
||||||
getElFormRef
|
getElFormRef,
|
||||||
|
getComponentExpose,
|
||||||
|
getFormItemExpose
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听表单结构化数组,重新生成formModel
|
// 监听表单结构化数组,重新生成formModel
|
||||||
watch(
|
watch(
|
||||||
() => unref(getProps).schema,
|
() => unref(getProps).schema,
|
||||||
(schema = []) => {
|
(schema = []) => {
|
||||||
console.log('@@####')
|
|
||||||
formModel.value = initModel(schema, unref(formModel))
|
formModel.value = initModel(schema, unref(formModel))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -193,8 +230,8 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
const formItemSlots: Recordable = {
|
const formItemSlots: Recordable = {
|
||||||
default: () => {
|
default: () => {
|
||||||
if (slots[item.field]) {
|
if (item?.formItemProps?.slots?.default) {
|
||||||
return getSlot(slots, item.field, formModel.value)
|
return item?.formItemProps?.slots?.default(formModel.value)
|
||||||
} else {
|
} else {
|
||||||
const Com = componentMap[item.component as string] as ReturnType<typeof defineComponent>
|
const Com = componentMap[item.component as string] as ReturnType<typeof defineComponent>
|
||||||
|
|
||||||
|
@ -254,6 +291,7 @@ export default defineComponent({
|
||||||
return (
|
return (
|
||||||
<Com
|
<Com
|
||||||
vModel={formModel.value[item.field]}
|
vModel={formModel.value[item.field]}
|
||||||
|
ref={(el: any) => setComponentRefMap(el, item.field)}
|
||||||
{...(autoSetPlaceholder && setTextPlaceholder(item))}
|
{...(autoSetPlaceholder && setTextPlaceholder(item))}
|
||||||
{...setComponentProps(item)}
|
{...setComponentProps(item)}
|
||||||
style={item.componentProps?.style || {}}
|
style={item.componentProps?.style || {}}
|
||||||
|
@ -278,7 +316,12 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<ElFormItem {...(item.formItemProps || {})} prop={item.field} label={item.label || ''}>
|
<ElFormItem
|
||||||
|
ref={(el: any) => setFormItemRefMap(el, item.field)}
|
||||||
|
{...(item.formItemProps || {})}
|
||||||
|
prop={item.field}
|
||||||
|
label={item.label || ''}
|
||||||
|
>
|
||||||
{formItemSlots}
|
{formItemSlots}
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
)
|
)
|
||||||
|
|
|
@ -756,6 +756,7 @@ export interface FormSetProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormItemProps {
|
export interface FormItemProps {
|
||||||
|
ref?: any
|
||||||
labelWidth?: string | number
|
labelWidth?: string | number
|
||||||
required?: boolean
|
required?: boolean
|
||||||
rules?: FormItemRule | FormItemRule[]
|
rules?: FormItemRule | FormItemRule[]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { Form, FormExpose } from '@/components/Form'
|
import type { Form, FormExpose } from '@/components/Form'
|
||||||
import type { ElForm } from 'element-plus'
|
import type { ElForm, ElFormItem } from 'element-plus'
|
||||||
import { ref, unref, nextTick } from 'vue'
|
import { ref, unref, nextTick } from 'vue'
|
||||||
import { FormSchema, FormSetProps, FormProps } from '@/components/Form'
|
import { FormSchema, FormSetProps, FormProps } from '@/components/Form'
|
||||||
|
|
||||||
|
@ -74,12 +74,26 @@ export const useForm = () => {
|
||||||
getFormData: async <T = Recordable>(): Promise<T> => {
|
getFormData: async <T = Recordable>(): Promise<T> => {
|
||||||
const form = await getForm()
|
const form = await getForm()
|
||||||
return form?.formModel as T
|
return form?.formModel as T
|
||||||
|
},
|
||||||
|
|
||||||
|
getComponentExpose: async (field: string) => {
|
||||||
|
const form = await getForm()
|
||||||
|
return form?.getComponentExpose(field)
|
||||||
|
},
|
||||||
|
|
||||||
|
getFormItemExpose: async (field: string) => {
|
||||||
|
const form = await getForm()
|
||||||
|
return form?.getFormItemExpose(field) as ComponentRef<typeof ElFormItem>
|
||||||
|
},
|
||||||
|
|
||||||
|
getElFormExpose: async () => {
|
||||||
|
await getForm()
|
||||||
|
return unref(elFormRef)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
register,
|
register,
|
||||||
formRef: elFormRef,
|
|
||||||
methods
|
methods
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -285,7 +285,9 @@ export default {
|
||||||
richText: 'Rich text',
|
richText: 'Rich text',
|
||||||
form: 'Form',
|
form: 'Form',
|
||||||
// 远程加载
|
// 远程加载
|
||||||
remoteLoading: 'Remote loading'
|
remoteLoading: 'Remote loading',
|
||||||
|
// 聚焦
|
||||||
|
focus: 'Focus'
|
||||||
},
|
},
|
||||||
guideDemo: {
|
guideDemo: {
|
||||||
guide: 'Guide',
|
guide: 'Guide',
|
||||||
|
|
|
@ -284,7 +284,9 @@ export default {
|
||||||
richText: '富文本编辑器',
|
richText: '富文本编辑器',
|
||||||
form: '表单',
|
form: '表单',
|
||||||
// 远程加载
|
// 远程加载
|
||||||
remoteLoading: '远程加载'
|
remoteLoading: '远程加载',
|
||||||
|
// 聚焦
|
||||||
|
focus: '聚焦'
|
||||||
},
|
},
|
||||||
guideDemo: {
|
guideDemo: {
|
||||||
guide: '引导页',
|
guide: '引导页',
|
||||||
|
|
|
@ -14,7 +14,8 @@ import {
|
||||||
ElRadio,
|
ElRadio,
|
||||||
ElRadioButton,
|
ElRadioButton,
|
||||||
ElCheckbox,
|
ElCheckbox,
|
||||||
ElCheckboxButton
|
ElCheckboxButton,
|
||||||
|
ElInput
|
||||||
} from 'element-plus'
|
} from 'element-plus'
|
||||||
import { getDictOneApi } from '@/api/common'
|
import { getDictOneApi } from '@/api/common'
|
||||||
|
|
||||||
|
@ -1384,6 +1385,18 @@ const schema = reactive<FormSchema[]>([
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'field69-1',
|
||||||
|
component: 'Input',
|
||||||
|
label: `custom formItem`,
|
||||||
|
formItemProps: {
|
||||||
|
slots: {
|
||||||
|
default: (formModel: any) => {
|
||||||
|
return <ElInput v-model={formModel['field69-1']} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: 'field70',
|
field: 'field70',
|
||||||
component: 'Divider',
|
component: 'Divider',
|
||||||
|
@ -1401,6 +1414,45 @@ const schema = reactive<FormSchema[]>([
|
||||||
const res = await getDictOneApi()
|
const res = await getDictOneApi()
|
||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'field72',
|
||||||
|
label: `${t('formDemo.selectV2')}`,
|
||||||
|
component: 'SelectV2',
|
||||||
|
componentProps: {
|
||||||
|
options: []
|
||||||
|
},
|
||||||
|
// 远程加载option
|
||||||
|
optionApi: async () => {
|
||||||
|
const res = await getDictOneApi()
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'field73',
|
||||||
|
label: `${t('formDemo.checkboxGroup')}`,
|
||||||
|
component: 'CheckboxGroup',
|
||||||
|
componentProps: {
|
||||||
|
options: []
|
||||||
|
},
|
||||||
|
// 远程加载option
|
||||||
|
optionApi: async () => {
|
||||||
|
const res = await getDictOneApi()
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'field74',
|
||||||
|
label: `${t('formDemo.radioGroup')}`,
|
||||||
|
component: 'RadioGroup',
|
||||||
|
componentProps: {
|
||||||
|
options: []
|
||||||
|
},
|
||||||
|
// 远程加载option
|
||||||
|
optionApi: async () => {
|
||||||
|
const res = await getDictOneApi()
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { ContentWrap } from '@/components/ContentWrap'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { useForm } from '@/hooks/web/useForm'
|
import { useForm } from '@/hooks/web/useForm'
|
||||||
import { reactive, unref, ref } from 'vue'
|
import { reactive, unref, ref } from 'vue'
|
||||||
import { ElButton } from 'element-plus'
|
import { ElButton, ElInput } from 'element-plus'
|
||||||
import { useValidator } from '@/hooks/web/useValidator'
|
import { useValidator } from '@/hooks/web/useValidator'
|
||||||
import { getDictOneApi } from '@/api/common'
|
import { getDictOneApi } from '@/api/common'
|
||||||
|
|
||||||
|
@ -36,6 +36,9 @@ const schema = reactive<FormSchema[]>([
|
||||||
value: '2'
|
value: '2'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
formItemProps: {
|
||||||
|
rules: [required()]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -92,31 +95,37 @@ const schema = reactive<FormSchema[]>([
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
const { register, methods, elFormRef } = useForm()
|
const { register, methods } = useForm()
|
||||||
|
const {
|
||||||
|
setProps,
|
||||||
|
delSchema,
|
||||||
|
addSchema,
|
||||||
|
setValues,
|
||||||
|
setSchema,
|
||||||
|
getComponentExpose,
|
||||||
|
getFormItemExpose,
|
||||||
|
getElFormExpose
|
||||||
|
} = methods
|
||||||
|
|
||||||
const changeLabelWidth = (width: number | string) => {
|
const changeLabelWidth = (width: number | string) => {
|
||||||
const { setProps } = methods
|
|
||||||
setProps({
|
setProps({
|
||||||
labelWidth: width
|
labelWidth: width
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeSize = (size: string) => {
|
const changeSize = (size: string) => {
|
||||||
const { setProps } = methods
|
|
||||||
setProps({
|
setProps({
|
||||||
size
|
size
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeDisabled = (bool: boolean) => {
|
const changeDisabled = (bool: boolean) => {
|
||||||
const { setProps } = methods
|
|
||||||
setProps({
|
setProps({
|
||||||
disabled: bool
|
disabled: bool
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeSchema = (del: boolean) => {
|
const changeSchema = (del: boolean) => {
|
||||||
const { delSchema, addSchema } = methods
|
|
||||||
if (del) {
|
if (del) {
|
||||||
delSchema('field2')
|
delSchema('field2')
|
||||||
} else if (!del && schema[1].field !== 'field2') {
|
} else if (!del && schema[1].field !== 'field2') {
|
||||||
|
@ -143,10 +152,10 @@ const changeSchema = (del: boolean) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setValue = (reset: boolean) => {
|
const setValue = async (reset: boolean) => {
|
||||||
const { setValues } = methods
|
const elFormExpose = await getElFormExpose()
|
||||||
if (reset) {
|
if (reset) {
|
||||||
unref(elFormRef)?.resetFields()
|
elFormExpose?.resetFields()
|
||||||
} else {
|
} else {
|
||||||
setValues({
|
setValues({
|
||||||
field1: 'field1',
|
field1: 'field1',
|
||||||
|
@ -162,7 +171,6 @@ const setValue = (reset: boolean) => {
|
||||||
const index = ref(7)
|
const index = ref(7)
|
||||||
|
|
||||||
const setLabel = () => {
|
const setLabel = () => {
|
||||||
const { setSchema } = methods
|
|
||||||
setSchema([
|
setSchema([
|
||||||
{
|
{
|
||||||
field: 'field2',
|
field: 'field2',
|
||||||
|
@ -212,14 +220,16 @@ const addItem = () => {
|
||||||
index.value++
|
index.value++
|
||||||
}
|
}
|
||||||
|
|
||||||
const formValidation = () => {
|
const formValidation = async () => {
|
||||||
unref(elFormRef)!.validate((isValid) => {
|
const elFormExpose = await getElFormExpose()
|
||||||
|
elFormExpose?.validate((isValid) => {
|
||||||
console.log(isValid)
|
console.log(isValid)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifyReset = () => {
|
const verifyReset = async () => {
|
||||||
unref(elFormRef)?.resetFields()
|
const elFormExpose = await getElFormExpose()
|
||||||
|
elFormExpose?.resetFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
const getDictOne = async () => {
|
const getDictOne = async () => {
|
||||||
|
@ -235,6 +245,20 @@ const getDictOne = async () => {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inoutFocus = async () => {
|
||||||
|
const inputEl: ComponentRef<typeof ElInput> = await getComponentExpose('field1')
|
||||||
|
inputEl?.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
const inoutValidation = async () => {
|
||||||
|
const formItem = await getFormItemExpose('field1')
|
||||||
|
const inputEl: ComponentRef<typeof ElInput> = await getComponentExpose('field1')
|
||||||
|
inputEl?.focus()
|
||||||
|
formItem?.validate('focus', (val: boolean) => {
|
||||||
|
console.log(val)
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -270,6 +294,13 @@ const getDictOne = async () => {
|
||||||
<ElButton @click="getDictOne">
|
<ElButton @click="getDictOne">
|
||||||
{{ t('searchDemo.dynamicOptions') }}
|
{{ t('searchDemo.dynamicOptions') }}
|
||||||
</ElButton>
|
</ElButton>
|
||||||
|
|
||||||
|
<ElButton @click="inoutFocus">
|
||||||
|
{{ `${t('formDemo.input')} ${t('formDemo.focus')}` }}
|
||||||
|
</ElButton>
|
||||||
|
<ElButton @click="inoutValidation">
|
||||||
|
{{ `${t('formDemo.input')} ${t('formDemo.formValidation')}` }}
|
||||||
|
</ElButton>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
<ContentWrap :title="`UseForm ${t('formDemo.example')}`">
|
<ContentWrap :title="`UseForm ${t('formDemo.example')}`">
|
||||||
<Form :schema="schema" @register="register" />
|
<Form :schema="schema" @register="register" />
|
||||||
|
|
Loading…
Reference in New Issue