123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- <template>
- <component :is="'el-form'" v-bind="_formOptions" ref="proFormRef" :model="formModel">
- <el-row :gutter="5">
- <template v-for="item in itemsOptions" :key="item.prop">
- <el-col :span="item.span || 24" g v-if="show(item.show)">
- <component :is="'el-form-item'" v-bind="item">
- <template #label>
- <el-space :size="4">
- <span class="label-span">{{ `${item.label}` }}</span>
- <!-- <el-tooltip :content="item.formItem.label" placement="top" :disabled="isShowTooltip">
- <span class="label-span" @mouseenter="visibleTooltip">{{ `${item.formItem.label}` }}</span>
- </el-tooltip> -->
- <el-tooltip v-if="item?.tooltip" effect="dark" :content="item?.tooltip" placement="top">
- <i :class="'iconfont icon-yiwen'"></i>
- </el-tooltip>
- </el-space>
- <span v-if="item.hideLabelSuffix">{{ `${item.hideLabelSuffix ? '' : ':'}` }}</span>
- <span v-else>{{ `${_formOptions.labelSuffix}` }}</span>
- </template>
- <!-- <template v-if="item.compOptions.elTagName === 'slot'"> -->
- <slot :name="item.prop" :form-model="formModel"></slot>
- <slot name="default" :form-model="formModel"></slot>
- <!-- </template> -->
- <template v-if="item.compOptions.elTagName === 'icon'">
- <SelectIcon v-model:icon-value="formModel[item.prop]" />
- </template>
- <template v-else-if="item.compOptions.elTagName === 'file-upload'">
- <FileUpload
- v-model:model-value="formModel[item.prop]"
- :file-size="item.compOptions.fileSize"
- :file-type="item.compOptions.fileType"
- v-bind="$attrs"
- />
- </template>
- <template v-else-if="item.compOptions.elTagName === 'img-upload'">
- <uploadImg v-model:image-url="formModel[item.prop]" />
- <!-- <el-button @click="() => {console.log(formModel[item.prop])}"/>-->
- </template>
- <template v-else-if="item.compOptions.elTagName === 'imgs-upload'">
- <Imgs v-model="formModel[item.prop]" v-bind="$attrs" />
- </template>
- <template v-else-if="item.compOptions.elTagName === 'file-upload-s3'">
- <FileUploadS3 v-model:model-value="formModel[item.prop]" />
- </template>
- <template v-else-if="item.compOptions.elTagName === 'imgs-upload-s3'">
- <ImgsS3 v-model="formModel[item.prop]" v-bind="$attrs" />
- </template>
- <Item v-else :item="item" :form-model="formModel" />
- </component>
- </el-col>
- </template>
- </el-row>
- <el-form-item v-if="_formOptions.hasFooter">
- <slot name="operation" :form-model="formModel" :model="formModel" :pro-form-ref="proFormRef">
- <el-button type="primary" v-if="_formOptions.showSubmitButton" @click="onSubmit(proFormRef)">{{ _formOptions.submitButtonText }}</el-button>
- <el-button v-if="_formOptions.showResetButton" type="info" @click="resetForm(proFormRef)">
- {{ _formOptions.resetButtonText }}
- </el-button>
- <el-button v-if="_formOptions.showCancelButton" @click="emits('cancel')">
- {{ _formOptions.cancelButtonText }}
- </el-button>
- </slot>
- </el-form-item>
- </component>
- </template>
- <script setup lang="ts" name="ProForm">
- import { ref, computed, ComputedRef, watch, unref, provide } from 'vue'
- import { ElMessage, type FormInstance } from 'element-plus'
- import Item from '@/components/ProForm/components/Item.vue'
- import SelectIcon from '@/components/SelectIcon/index.vue'
- import FileUpload from '@/components/Upload/File.vue'
- import uploadImg from '@/components/Upload/Img.vue'
- import Imgs from '@/components/Upload/Imgs.vue'
- import FileUploadS3 from '@/components/Upload/FileS3.vue'
- import ImgsS3 from '@/components/Upload/ImgsS3.vue'
- // import { handleProp } from '@/utils'
- // 表单整体配置项
- export interface ProFormProps {
- formOptions?: ProForm.FormOptions
- itemsOptions: ProForm.ItemsOptions[]
- model?: Record<ProForm.FormItem['prop'], ProForm.FormItem['value']>
- api?: (params: any) => Promise<any> // 表单提交api
- }
- // 表单的数据
- const formModel = ref<Record<string, any>>({})
- // 表单ref
- const proFormRef = ref<FormInstance>()
- // 默认值
- const props = withDefaults(defineProps<ProFormProps>(), {
- items: () => [],
- model: () => ({})
- })
- const show = (showFunction: any) => {
- if (!showFunction) return true
- if (typeof showFunction == 'function') {
- // 直接调用 showFunction 函数,并传入 formModel.value
- return showFunction(formModel.value)
- }
- // 如果 showFunction 不是函数,直接返回 true 显示该表单项
- return true
- }
- // 设置option默认值,如果传入自定义的配置则合并form配置项
- const _formOptions: ComputedRef<ProForm.FormOptions> = computed(() => {
- const form = {
- inline: false,
- labelPosition: 'right',
- labelWidth: 120,
- disabled: false,
- hasFooter: true,
- labelSuffix: ':',
- submitButtonText: '提交',
- resetButtonText: '重置',
- cancelButtonText: '取消'
- }
- return Object.assign(form, props?.formOptions)
- })
- // 事件传递定义
- interface EmitEvent {
- (e: 'submit', params: any): void
- (e: 'reset'): void
- (e: 'cancel'): void
- }
- const enumMap = ref(new Map<string, { [key: string]: any }[]>())
- const setEnumMap = async ({ prop, compOptions }: ProForm.ItemsOptions) => {
- const props = compOptions?.enumKey || prop
- const enumValue = compOptions?.enum
- if (!enumValue) return
- // 如果当前 enumMap 存在相同的值 return
- if (enumMap.value.has(props!) && (typeof enumValue === 'function' || enumMap.value.get(props!) === enumValue)) return
- // 当前 enum 为静态数据,则直接存储到 enumMap
- if (typeof enumValue !== 'function') return enumMap.value.set(props!, unref(enumValue!))
- // 为了防止接口执行慢,而存储慢,导致重复请求,所以预先存储为[],接口返回后再二次存储
- enumMap.value.set(props!, [])
- // 当前 enum 为后台数据需要请求数据,则调用该请求接口,并存储到 enumMap
- let { data } = await enumValue()
- enumMap.value.set(props!, data)
- }
- // 注入 enumMap
- provide('enumMap', enumMap)
- // 处理表单项需要的参数
- props.itemsOptions.forEach(async item => {
- await setEnumMap(item)
- })
- const emits = defineEmits<EmitEvent>()
- // 监听itemsOptions变化
- watch(props, () => {
- // 处理表单项需要的参数
- props.itemsOptions.forEach(async item => {
- await setEnumMap(item)
- })
- })
- // 根据items初始化model, 如果items有传值就用传递的model数据模型,否则就给上面声明的formModel设置相应的(key,value) [item.prop], item.value是表单的默认值(选填)
- watch(
- () => props.model,
- () => {
- props.itemsOptions.map((item: ProForm.ItemsOptions) => {
- // 如果类型为checkbox,默认值需要设置一个空数组
- let value = ['checkbox', 'transfer'].includes(item.compOptions.elTagName!) ? [] : props.model[item.prop]
- props.model[item.prop] ? (formModel.value = props.model) : (formModel.value[item.prop] = item.compOptions.value || value)
- })
- },
- { immediate: true }
- )
- // 提交按钮
- const onSubmit = (formEl: FormInstance | undefined) => {
- console.log('表单提交数据', formModel.value)
- if (!formEl) return
- formEl.validate(valid => {
- if (valid) {
- if (props.api) emits('submit', formModel.value)
- props.api!({ ...formModel }).then(res => {
- if (res.code == 200) {
- resetForm(formEl)
- ElMessage.success('操作成功')
- } else {
- console.log('message', res.message)
- }
- })
- } else {
- console.log('校验失败')
- }
- })
- }
- // 重置
- const resetForm = (formEl: FormInstance | undefined) => {
- if (!formEl) return
- formEl.resetFields()
- }
- // let isShowTooltip = ref(false)
- // const visibleTooltip = e => {
- // console.log('e', e)
- // isShowTooltip.value = e.target.offsetWidth + 18 - e.fromElement.clientWidth > 0 ? false : true //18为required *号引起的偏差。
- // console.log('isShowTooltip', isShowTooltip.value)
- // }
- // 暴露方法给父组件使用
- defineExpose({
- proFormRef,
- formModel,
- resetForm,
- onSubmit
- })
- </script>
- <style scoped lang="scss">
- @import './index.scss';
- </style>
|