wip: Search重构中

This commit is contained in:
kailong321200875 2023-06-21 17:59:37 +08:00
parent 77a3866248
commit c76f8bc494
8 changed files with 293 additions and 135 deletions

View File

@ -1,5 +1,4 @@
import Form from './src/Form.vue' import Form from './src/Form.vue'
import { ElForm } from 'element-plus'
import type { FormSchema, FormSetProps } from './src/types' import type { FormSchema, FormSetProps } from './src/types'
export type { export type {
ComponentNameEnum, ComponentNameEnum,

View File

@ -200,7 +200,7 @@ export default defineComponent({
const { schema = [], isCol } = unref(getProps) const { schema = [], isCol } = unref(getProps)
return schema return schema
.filter((v) => !v.hidden) .filter((v) => !v.remove)
.map((item) => { .map((item) => {
// Divider // Divider
const isDivider = item.component === 'Divider' const isDivider = item.component === 'Divider'
@ -312,6 +312,7 @@ export default defineComponent({
} }
return ( return (
<ElFormItem <ElFormItem
v-show={!item.hidden}
ref={(el: any) => setFormItemRefMap(el, item.field)} ref={(el: any) => setFormItemRefMap(el, item.field)}
{...(item.formItemProps || {})} {...(item.formItemProps || {})}
prop={item.field} prop={item.field}

View File

@ -144,7 +144,7 @@ export const initModel = (schema: FormSchema[], formModel: Recordable) => {
const model: Recordable = { ...formModel } const model: Recordable = { ...formModel }
schema.map((v) => { schema.map((v) => {
// 如果是hidden就删除对应的值 // 如果是hidden就删除对应的值
if (v.hidden) { if (v.remove) {
delete model[v.field] delete model[v.field]
} else if (v.component && v.component !== 'Divider') { } else if (v.component && v.component !== 'Divider') {
const hasField = Reflect.has(model, v.field) const hasField = Reflect.has(model, v.field)

View File

@ -785,11 +785,6 @@ export interface FormSchema {
*/ */
label?: string label?: string
/**
*
*/
labelMessage?: string
/** /**
* col组件属性 * col组件属性
*/ */
@ -834,7 +829,12 @@ export interface FormSchema {
value?: any value?: any
/** /**
* * truev-if
*/
remove?: boolean
/**
* v-show
*/ */
hidden?: boolean hidden?: boolean

View File

@ -1,16 +1,19 @@
<script setup lang="ts"> <script setup lang="tsx">
import { Form } from '@/components/Form' import { Form, FormSchema } from '@/components/Form'
import { PropType, computed, unref, ref } from 'vue' import { PropType, computed, unref, ref, watch, onMounted } from 'vue'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { ElButton } from 'element-plus' import { ElButton } from 'element-plus'
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 { useIcon } from '@/hooks/web/useIcon'
import { findIndex } from '@/utils' import { findIndex } from '@/utils'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { FormSchema } from '@/types/form' import { initModel } from '@/components/Form/src/helper'
const { t } = useI18n() const { t } = useI18n()
const formExpose = ref<ComponentRef<typeof Form>>()
const props = defineProps({ const props = defineProps({
// Form // Form
schema: { schema: {
@ -24,7 +27,7 @@ const props = defineProps({
// //
layout: propTypes.string.validate((v: string) => ['inline', 'bottom'].includes(v)).def('inline'), layout: propTypes.string.validate((v: string) => ['inline', 'bottom'].includes(v)).def('inline'),
// //
buttomPosition: propTypes.string buttonPosition: propTypes.string
.validate((v: string) => ['left', 'center', 'right'].includes(v)) .validate((v: string) => ['left', 'center', 'right'].includes(v))
.def('center'), .def('center'),
showSearch: propTypes.bool.def(true), showSearch: propTypes.bool.def(true),
@ -34,31 +37,69 @@ const props = defineProps({
// //
expandField: propTypes.string.def(''), expandField: propTypes.string.def(''),
inline: propTypes.bool.def(true), inline: propTypes.bool.def(true),
//
removeNoValueItem: propTypes.bool.def(true),
model: { model: {
type: Object as PropType<Recordable>, type: Object as PropType<Recordable>,
default: () => ({}) default: () => ({})
} }
}) })
const emit = defineEmits(['search', 'reset']) const emit = defineEmits(['search', 'reset', 'register'])
const visible = ref(true) const visible = ref(true)
//
const formModel = ref<Recordable>({})
const newSchema = computed(() => { const newSchema = computed(() => {
let schema: FormSchema[] = cloneDeep(props.schema) let schema: FormSchema[] = cloneDeep(props.schema)
if (props.expand && props.expandField && !unref(visible)) { if (props.expand && props.expandField && !unref(visible)) {
const index = findIndex(schema, (v: FormSchema) => v.field === props.expandField) const index = findIndex(schema, (v: FormSchema) => v.field === props.expandField)
if (index > -1) { schema.map((v, i) => {
const length = schema.length if (i >= index) {
schema.splice(index + 1, length) v.hidden = true
} } else {
v.hidden = false
}
return v
})
} }
if (props.layout === 'inline') { if (props.layout === 'inline') {
schema = schema.concat([ schema = schema.concat([
{ {
field: 'action', field: 'action',
formItemProps: { formItemProps: {
labelWidth: '0px' labelWidth: '0px',
slots: {
default: () => {
return (
<div>
{props.showSearch ? (
<ElButton type="primary" onClick={search} icon={useIcon({ icon: 'ep:search' })}>
{t('common.query')}
</ElButton>
) : null}
{props.showReset ? (
<ElButton onClick={reset} icon={useIcon({ icon: 'ep:refresh-right' })}>
{t('common.reset')}
</ElButton>
) : null}
{props.expand ? (
<ElButton
text
onClick={setVisible}
icon={useIcon({
icon: visible.value ? 'ant-design:up-outlined' : 'ant-design:down-outlined'
})}
>
{t(visible.value ? 'common.shrink' : 'common.expand')}
</ElButton>
) : null}
</div>
)
}
}
} }
} }
]) ])
@ -66,41 +107,69 @@ const newSchema = computed(() => {
return schema return schema
}) })
const { register, elFormRef, methods } = useForm({ const { register, methods } = useForm()
model: props.model || {} const { getElFormExpose, getFormData } = methods
})
// formModel
watch(
() => unref(newSchema),
async (schema = []) => {
formModel.value = initModel(schema, unref(formModel))
},
{
immediate: true,
deep: true
}
)
const filterModel = async () => {
const model = await getFormData()
props.removeNoValueItem &&
Object.keys(model).forEach((key) => {
if (model[key] === void 0 || model[key] === '') {
delete model[key]
}
})
return model
}
const search = async () => { const search = async () => {
await unref(elFormRef)?.validate(async (isValid) => { const elFormExpose = await getElFormExpose()
await elFormExpose?.validate(async (isValid) => {
if (isValid) { if (isValid) {
const { getFormData } = methods const model = await filterModel()
const model = await getFormData()
emit('search', model) emit('search', model)
} }
}) })
} }
const reset = async () => { const reset = async () => {
unref(elFormRef)?.resetFields() const elFormExpose = await getElFormExpose()
const { getFormData } = methods elFormExpose?.resetFields()
const model = await getFormData() const model = await filterModel()
emit('reset', model) emit('reset', model)
} }
const bottonButtonStyle = computed(() => { const bottomButtonStyle = computed(() => {
return { return {
textAlign: props.buttomPosition as unknown as 'left' | 'center' | 'right' textAlign: props.buttonPosition as unknown as 'left' | 'center' | 'right'
} }
}) })
const setVisible = () => { const setVisible = async () => {
unref(elFormRef)?.resetFields()
visible.value = !unref(visible) visible.value = !unref(visible)
} }
onMounted(async () => {
const elFormExpose = await getElFormExpose()
emit('register', formExpose, elFormExpose)
})
</script> </script>
<template> <template>
<Form <Form
ref="formExpose"
:model="formModel"
:is-custom="false" :is-custom="false"
:label-width="labelWidth" :label-width="labelWidth"
hide-required-asterisk hide-required-asterisk
@ -108,38 +177,28 @@ const setVisible = () => {
:is-col="isCol" :is-col="isCol"
:schema="newSchema" :schema="newSchema"
@register="register" @register="register"
> />
<template #action>
<div v-if="layout === 'inline'">
<ElButton v-if="showSearch" type="primary" @click="search">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.query') }}
</ElButton>
<ElButton v-if="showReset" @click="reset">
<Icon icon="ep:refresh-right" class="mr-5px" />
{{ t('common.reset') }}
</ElButton>
<ElButton v-if="expand" text @click="setVisible">
{{ t(visible ? 'common.shrink' : 'common.expand') }}
<Icon :icon="visible ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
</ElButton>
</div>
</template>
</Form>
<template v-if="layout === 'bottom'"> <template v-if="layout === 'bottom'">
<div :style="bottonButtonStyle"> <div :style="bottomButtonStyle">
<ElButton v-if="showSearch" type="primary" @click="search"> <ElButton
<Icon icon="ep:search" class="mr-5px" /> v-if="showSearch"
type="primary"
:icon="useIcon({ icon: 'ep:search' })"
@click="search"
>
{{ t('common.query') }} {{ t('common.query') }}
</ElButton> </ElButton>
<ElButton v-if="showReset" @click="reset"> <ElButton v-if="showReset" :icon="useIcon({ icon: 'ep:refresh-right' })" @click="reset">
<Icon icon="ep:refresh-right" class="mr-5px" />
{{ t('common.reset') }} {{ t('common.reset') }}
</ElButton> </ElButton>
<ElButton v-if="expand" text @click="setVisible"> <ElButton
v-if="expand"
:icon="useIcon({ icon: visible ? 'ant-design:up-outlined' : 'ant-design:down-outlined' })"
text
@click="setVisible"
>
{{ t(visible ? 'common.shrink' : 'common.expand') }} {{ t(visible ? 'common.shrink' : 'common.expand') }}
<Icon :icon="visible ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
</ElButton> </ElButton>
</div> </div>
</template> </template>

125
src/hooks/web/useSearch.ts Normal file
View File

@ -0,0 +1,125 @@
import type { Form, FormExpose } from '@/components/Form'
import type { ElForm, ElFormItem } from 'element-plus'
import { ref, unref, nextTick } from 'vue'
import { FormSchema, FormSetProps, FormProps } from '@/components/Form'
export const useSearch = () => {
// Search实例
const formRef = ref<typeof Form & FormExpose>()
// ElForm实例
const elFormRef = ref<ComponentRef<typeof ElForm>>()
/**
* @param ref Form实例
* @param elRef ElForm实例
*/
const register = (ref: typeof Form & FormExpose, elRef: ComponentRef<typeof ElForm>) => {
formRef.value = ref
elFormRef.value = elRef
}
const getForm = async () => {
await nextTick()
const form = unref(formRef)
if (!form) {
console.error('The Search is not registered. Please use the register method to register')
}
return form
}
// 一些内置的方法
const methods = {
/**
* @description form组件的props
* @param field FormItem的field
*/
setProps: async (props: FormProps = {}) => {
const form = await getForm()
form?.setProps(props)
if (props.model) {
form?.setValues(props.model)
}
},
/**
* @description form的值
* @param data
*/
setValues: async (data: Recordable) => {
const form = await getForm()
form?.setValues(data)
},
/**
* @description schema
* @param schemaProps schemaProps
*/
setSchema: async (schemaProps: FormSetProps[]) => {
const form = await getForm()
form?.setSchema(schemaProps)
},
/**
* @description schema
* @param formSchema
* @param index
*/
addSchema: async (formSchema: FormSchema, index?: number) => {
const form = await getForm()
form?.addSchema(formSchema, index)
},
/**
* @description schema
* @param field
*/
delSchema: async (field: string) => {
const form = await getForm()
form?.delSchema(field)
},
/**
* @description
* @returns form data
*/
getFormData: async <T = Recordable>(): Promise<T> => {
const form = await getForm()
return form?.formModel as T
},
/**
* @description
* @param field
* @returns component instance
*/
getComponentExpose: async (field: string) => {
const form = await getForm()
return form?.getComponentExpose(field)
},
/**
* @description formItem组件的实例
* @param field
* @returns formItem instance
*/
getFormItemExpose: async (field: string) => {
const form = await getForm()
return form?.getFormItemExpose(field) as ComponentRef<typeof ElFormItem>
},
/**
* @description ElForm组件的实例
* @returns ElForm instance
*/
getElFormExpose: async () => {
await getForm()
return unref(elFormRef)
}
}
return {
register,
methods
}
}

View File

@ -157,14 +157,6 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
title: 'UseForm' title: 'UseForm'
} }
} }
// {
// path: 'ref-form',
// component: () => import('@/views/Components/Form/RefForm.vue'),
// name: 'RefForm',
// meta: {
// title: 'RefForm'
// }
// }
] ]
}, },
// { // {
@ -222,16 +214,16 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
} }
} }
] ]
},
{
path: 'search',
component: () => import('@/views/Components/Search.vue'),
name: 'Search',
meta: {
title: t('router.search')
}
} }
// { // {
// path: 'search',
// component: () => import('@/views/Components/Search.vue'),
// name: 'Search',
// meta: {
// title: t('router.search')
// }
// },
// {
// path: 'descriptions', // path: 'descriptions',
// component: () => import('@/views/Components/Descriptions.vue'), // component: () => import('@/views/Components/Descriptions.vue'),
// name: 'Descriptions', // name: 'Descriptions',

View File

@ -3,23 +3,21 @@ import { ContentWrap } from '@/components/ContentWrap'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { Search } from '@/components/Search' import { Search } from '@/components/Search'
import { reactive, ref, unref } from 'vue' import { reactive, ref, unref } from 'vue'
import { useValidator } from '@/hooks/web/useValidator'
import { ElButton } from 'element-plus' import { ElButton } from 'element-plus'
import { getDictOneApi } from '@/api/common' import { getDictOneApi } from '@/api/common'
import { FormSchema } from '@/types/form' import { FormSchema } from '@/components/Form'
import { useSearch } from '@/hooks/web/useSearch'
const { required } = useValidator()
const { t } = useI18n() const { t } = useI18n()
const { register, methods } = useSearch()
const { setSchema } = methods
const schema = reactive<FormSchema[]>([ const schema = reactive<FormSchema[]>([
{ {
field: 'field1', field: 'field1',
label: t('formDemo.input'), label: t('formDemo.input'),
component: 'Input', component: 'Input'
formItemProps: {
rules: [required()]
}
}, },
{ {
field: 'field2', field: 'field2',
@ -36,15 +34,17 @@ const schema = reactive<FormSchema[]>([
value: '2' value: '2'
} }
], ],
onChange: (value: string) => { on: {
console.log(value) change: (value: string) => {
console.log(value)
}
} }
} }
}, },
{ {
field: 'field3', field: 'field3',
label: t('formDemo.radio'), label: t('formDemo.radio'),
component: 'Radio', component: 'RadioGroup',
componentProps: { componentProps: {
options: [ options: [
{ {
@ -74,90 +74,57 @@ const schema = reactive<FormSchema[]>([
{ {
field: 'field8', field: 'field8',
label: t('formDemo.input'), label: t('formDemo.input'),
component: 'Input', component: 'Input'
formItemProps: {
rules: [required()]
}
}, },
{ {
field: 'field9', field: 'field9',
label: t('formDemo.input'), label: t('formDemo.input'),
component: 'Input', component: 'Input'
formItemProps: {
rules: [required()]
}
}, },
{ {
field: 'field10', field: 'field10',
label: t('formDemo.input'), label: t('formDemo.input'),
component: 'Input', component: 'Input'
formItemProps: {
rules: [required()]
}
}, },
{ {
field: 'field11', field: 'field11',
label: t('formDemo.input'), label: t('formDemo.input'),
component: 'Input', component: 'Input'
formItemProps: {
rules: [required()]
}
}, },
{ {
field: 'field12', field: 'field12',
label: t('formDemo.input'), label: t('formDemo.input'),
component: 'Input', component: 'Input'
formItemProps: {
rules: [required()]
}
}, },
{ {
field: 'field13', field: 'field13',
label: t('formDemo.input'), label: t('formDemo.input'),
component: 'Input', component: 'Input'
formItemProps: {
rules: [required()]
}
}, },
{ {
field: 'field14', field: 'field14',
label: t('formDemo.input'), label: t('formDemo.input'),
component: 'Input', component: 'Input'
formItemProps: {
rules: [required()]
}
}, },
{ {
field: 'field15', field: 'field15',
label: t('formDemo.input'), label: t('formDemo.input'),
component: 'Input', component: 'Input'
formItemProps: {
rules: [required()]
}
}, },
{ {
field: 'field16', field: 'field16',
label: t('formDemo.input'), label: t('formDemo.input'),
component: 'Input', component: 'Input'
formItemProps: {
rules: [required()]
}
}, },
{ {
field: 'field17', field: 'field17',
label: t('formDemo.input'), label: t('formDemo.input'),
component: 'Input', component: 'Input'
formItemProps: {
rules: [required()]
}
}, },
{ {
field: 'field18', field: 'field18',
label: t('formDemo.input'), label: t('formDemo.input'),
component: 'Input', component: 'Input'
formItemProps: {
rules: [required()]
}
} }
]) ])
@ -173,24 +140,36 @@ const changeLayout = () => {
layout.value = unref(layout) === 'inline' ? 'bottom' : 'inline' layout.value = unref(layout) === 'inline' ? 'bottom' : 'inline'
} }
const buttomPosition = ref('left') const buttonPosition = ref('left')
const changePosition = (position: string) => { const changePosition = (position: string) => {
layout.value = 'bottom' layout.value = 'bottom'
buttomPosition.value = position buttonPosition.value = position
} }
const getDictOne = async () => { const getDictOne = async () => {
const res = await getDictOneApi() const res = await getDictOneApi()
if (res) { if (res) {
schema[1].componentProps!.options = res.data setSchema([
console.log(res.data) {
field: 'field2',
path: 'componentPorps.options',
value: res.data
}
])
} }
} }
const handleSearch = (data: any) => {
console.log(data)
}
</script> </script>
<template> <template>
<ContentWrap :title="`${t('searchDemo.search')} ${t('searchDemo.operate')}`"> <ContentWrap
:title="`${t('searchDemo.search')} ${t('searchDemo.operate')}`"
style="margin-bottom: 20px"
>
<ElButton @click="changeGrid(true)">{{ t('searchDemo.grid') }}</ElButton> <ElButton @click="changeGrid(true)">{{ t('searchDemo.grid') }}</ElButton>
<ElButton @click="changeGrid(false)"> <ElButton @click="changeGrid(false)">
{{ t('searchDemo.restore') }} {{ t('searchDemo.grid') }} {{ t('searchDemo.restore') }} {{ t('searchDemo.grid') }}
@ -219,9 +198,12 @@ const getDictOne = async () => {
:schema="schema" :schema="schema"
:is-col="isGrid" :is-col="isGrid"
:layout="layout" :layout="layout"
:buttom-position="buttomPosition" :button-position="buttonPosition"
expand expand
expand-field="field6" expand-field="field6"
@search="handleSearch"
@reset="handleSearch"
@register="register"
/> />
</ContentWrap> </ContentWrap>
</template> </template>