我是靠谱客的博主 谦让项链,最近开发中收集的这篇文章主要介绍vue3+element-plus 的form多表单组件及多Descriptions详情组件封装(可实现单页面有多个表单/详情按模块的方式展示并且可以收缩;Vue3项目),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、最终效果

在这里插入图片描述

二、组件集成了以下功能

1、可以多模块配置form表单——配置formOpts对象
2、每个模块可以收起或展开——模块不设置title值取消此功能(或者设置disabled:true3、每个模块可以自定义插槽设置
4、头部标题可以显示隐藏——有title则显示没有则隐藏
5、可以自定义设置footer操作按钮(默认:表单显示取消和保存按钮;详情显示取消按钮)——设置 :footer="null"
6、多表单校验不通过可以指定哪个模块
7、可以设置tabs(默认展示第一个tab;可以指定展示某一个根据setSelectedTab方法)
8、头部返回操作默认返回上一页,若需要自定义可以设置isGoBackEvent
9、多模块详情页面value值可以自定义插槽
10、多模块详情页面value值可以自定义tip(提示)
11、多模块表单或详情页面如果不使用手风琴收缩功能可以设置“disabled:true

三、实际组件是以下组件结合,并继承其Attributes

1、多模块表单是基于我之前封装的 t-form组件

2、多模块详情是基于我之前封装的 t-detail组件

四、参数配置

1、代码示例

<!-- 第一种 表单形式 -->
<t-module-form
  title="模块表单组件运用"
  subTitle="模块表单"
  ref="sourceForm"
  :formOpts="formOpts"
  :submit="submit"
  :tabs="tabs"
  @handleEvent="handleEvent"
/>
<!-- 第二种详情展示 根据handleType-->
<t-module-form
  title="详情模块组件运用"
  subTitle="模块详情"
  ref="sourceDetail"
  handleType="desc"
  :descData="descData"
  :tabs="tabs"
/>

2、配置参数(Attributes)继承 t-form/t-detail Attributes

参数说明类型默认值
title头部返回按钮标题string
titleSlot是否使用插槽显示 titleBooleanfalse
subTitle头部副标题string
extra操作区,位于 title 行的行尾(右侧)slot
footer底部操作区(默认展示“取消/保存”按钮;使用插槽则隐藏)footer="null"时隐藏底部操作String slot
tabs页面展示是否需要页签(并且 tabs 的 key 是插槽)Array
isGoBackEvent点击头部返回(默认返回上一页,设置此值可以自定义 back 事件)Booleanfalse
handleType显示方式(‘edit’:form 表单操作,‘desc’:表详情页面)stringedit
----edithandleType=edit 表 form 表单操作的属性--
------formOpts表单配置描述,支持多分组表单Object
------submit点击保存时返回所有表单数据(数据格式 promise 且可显示 loading 状态)function所有表单数据
-----deschandleType=desc 表详情页面的属性--
------descColumn详情页面展示每行显示几列(handleType= desc 生效)Number4
------descData详情页面配置描述,支持多分组表 (handleType= desc 生效)Object

2-1、formOpts 配置参数

参数说明类型默认值
title表单标题(是否显示控制折叠面板功能)String
slotName插槽(自定义表单数据)有插槽就无需配置 optsslot
name每组表单定义的名字(作用:是否默认展开)String
widthSize每行显示几个输入项(默认两项) 最大值 4Number3
disabled禁用时取消收缩功能及隐藏 icon)Booleanfalse
opts表单配置项Object
2-1-1、opts 配置参数
参数说明类型默认值
rules规则(可依据 element-plus el-form 配置————对应 formData 的值)Object/Array-
operatorList操作按钮 listArray-
listTypeInfo下拉选择数据源(type:'select’有效)Object-
labelPosition改变表单项 label 与输入框的布局方式(默认:right) /topStringright
labelWidthlabel 宽度(默认值 120px)String120px
formData表单提交数据(对应 fieldList 每一项的 value 值)Object-
fieldListform 表单每项 listArray-
----slotName自定义表单某一项输入框slot-
----compform 表单每一项组件是输入框还是下拉选择等(可使用第三方 UI 如 el-select/el-input 也可以使用自定义组件)String-
----bind表单每一项属性(继承第三方 UI 的 Attributes,如 el-input 中的 clearable 清空功能)默认清空及下拉过滤Object-
----typeform 表单每一项类型String-
----widthSizeform 表单某一项所占比例(如果一行展示可以设值:1)Number2
----widthform 表单某一项所占实际宽度String-
----arrLabeltype=select-arr 时,每个下拉显示的中文Stringlabel
----arrKeytype=select-arr 时,每个下拉显示的中文传后台的数字Stringkey
----labelform 表单每一项 titleString-
----labelRender自定义某一项 titlefunction-
----valueform 表单每一项传给后台的参数String-
----rules每一项输入框的表单校验规则Object/Array-
----list下拉选择数据源(仅仅对 type:'select’有效)String-
----event表单每一项事件标志(handleEvent 事件)String-

2-2、descData 配置参数

参数说明类型默认值
title详情标题(是否显示控制折叠面板功能)String-
slotName插槽(自定义详情数据)有插槽就无需配置 dataslot-
name每组详情定义的名字(作用:是否默认展开)String-
disabled禁用时取消收缩功能及隐藏 icon)Booleanfalse
data详情配置项Object-
----label详情字段说明标题String-
----value详情字段返回值String-
----slotName插槽(自定义 value)slot-
----span占用的列宽,默认占用 1 列,最多 4 列Number1
----tooltipvalue 值的提示语String/function-

3、events

事件名说明返回值
handleEvent单个查询条件触发事件fieldList 中的 event 值和对应输入的 value 值
tabsChange点击 tab 切换触发被选中的标签 tab 实例
validateError校验失败抛出事件obj——每个收缩块的对象
back头部标题点击返回事件-

4、Methods

事件名说明参数
resetFormFields重置表单-
clearValidate清空校验-
setSelectedTab默认选中 tab默认选中 tab 插槽名

五、具体代码

<template>
  <div class="t_module_form" :style="{marginBottom:footer!==null?'60px':''}">
    <div class="scroll_wrap">
      <!-- 头部 -->
      <el-page-header
        v-if="title||titleSlot"
        :title="title"
        @back="back"
        :class="{noContent:!isShow('extra')&&!subTitle,'isShowBack':isShowBack}"
      >
        <template #title v-if="titleSlot">
          <slot name="title"></slot>
        </template>
        <template #content>
          <div class="sub_title">{{subTitle}}</div>
          <div class="extra">
            <slot name="extra"></slot>
          </div>
        </template>
      </el-page-header>
      <!-- 表单页面 -->
      <module-form v-if="handleType==='edit'" v-bind="$attrs" ref="tForm">
        <template v-for="(index, name) in slots" v-slot:[name]="data">
          <slot :name="name" v-bind="data"></slot>
        </template>
      </module-form>
      <!-- 详情页面 -->
      <module-detail v-else v-bind="$attrs">
        <template v-for="(index, name) in slots" v-slot:[name]="data">
          <slot :name="name" v-bind="data"></slot>
        </template>
      </module-detail>
      <!-- tabs -->
      <div class="tabs" v-if="tabs">
        <el-tabs
          v-if="tabs&&tabs.length > 1"
          v-model="activeName"
          @tab-click="(tab, event) => emits('tabsChange', tab, event)"
        >
          <el-tab-pane v-for="tab in tabs" :key="tab.key" :name="tab.key" :label="tab.title">
            <slot :name="tab.key"></slot>
          </el-tab-pane>
        </el-tabs>
        <slot v-else :name="tabs&&tabs[0].key"></slot>
      </div>
      <slot name="default"></slot>
    </div>
    <!-- 按钮 -->
    <footer class="handle_wrap" v-if="footer!==null">
      <slot name="footer" />
      <div v-if="!slots.footer">
        <el-button @click="back">取消</el-button>
        <el-button
          type="primary"
          v-if="handleType==='edit'"
          @click="saveHandle"
          :loading="loading"
        >{{btnTxt}}</el-button>
      </div>
    </footer>
  </div>
</template>
<script lang="ts">
export default {
  name: 'TModuleForm',
}
</script>
<script setup lang="ts">
import {computed,watch,ref,useAttrs,useSlots,nextTick,onMounted,} from 'vue'
// import { useRouter } from 'vue-router'
import ModuleDetail from './moduleDetail.vue'
import ModuleForm from './moduleForm.vue'
const props: any = defineProps({
  handleType: {
    type: String,
    default: 'edit', // edit表form表单操作,desc表详情页面
  },
  // 是否使用插槽显示title
  titleSlot: {
    type: Boolean,
    default: false,
  },
  // 是否显示返回箭头
  isShowBack: {
    type: Boolean,
    default: false,
  },
  // 返回上一层触发方法
  isGoBackEvent: {
    type: Boolean,
    default: false,
  },
  // 操作按钮文字
  btnTxt: {
    type: String,
    default: '保存',
  },
  // 是否显示底部操作按钮 :footer="null"
  footer: Object,
  title: String,
  subTitle: String,
  tabs: Array,
  submit: Function,
})
const attrs: any = useAttrs()
const slots = useSlots()
const activeName = ref(props.tabs && props.tabs[0].key)
const loading = ref(false)
// 获取ref
const tForm: any = ref<HTMLElement | null>(null)
// const router = useRouter()

onMounted(() => {
  // console.log('router', router)
  // console.log('onMounted', attrs)
  // console.log('onMounted222', attrs.formOpts)
})
// 抛出事件
const emits = defineEmits(['validateError', 'back', 'tabsChange'])
// 点击保存
const saveHandle = async () => {
  let form = {}
  let formError = {}
  let formOpts = {}
  let successLength = 0
  loading.value = true
  // 过滤非插槽表单
  Object.keys(attrs.formOpts).forEach((key) => {
    if (attrs.formOpts[key].opts) {
      formOpts[key] = attrs.formOpts[key]
    }
  })
  Object.keys(formOpts).forEach(async (formIndex) => {
    const { valid, formData } = await tForm.value.getChildRef(formIndex).validate()
    // const { valid, formData } = await attrs.formOpts[formIndex].ref.validate()
    // console.log('11111', valid)
    if (valid) {
      successLength = successLength + 1
      form[formIndex] = attrs.formOpts[formIndex].opts.formData
      // console.log('2222', successLength)
      // console.log('3333', form)
    }
  })
  setTimeout(async () => {
    if (successLength === Object.keys(formOpts).length) {
      // 所有表单都校验成功
      const isSuccess = await props.submit(form)
      if (isSuccess) {
        // 成功
        back()
      }
    } else {
      // 校验失败抛出事件
      Object.keys(formOpts).forEach((key) => {
        if (Object.keys(form).length > 0) {
          Object.keys(form).map((val) => {
            if (key !== val) {
              formError[key] = formOpts[key]
            }
          })
        } else {
          formError[key] = formOpts[key]
        }
      })
      emits('validateError', formError)
    }
    loading.value = false
  }, 300)
}
// 点击头部返回或者取消
const back = () => {
  if (props.isShowBack) {
    return
  }
  emits('back')
  if (!props.isGoBackEvent) {
    // router.go(-1)
    history.go(-1)
  }
}
const show = (formType) => {
  nextTick(() => {
    updateFormFields()
    props.formType = formType
  })
}
// 获取默认选中tab
const setSelectedTab = (key) => {
  activeName.value = key
}
// 清空表单
const resetFormFields = () => {
  let formOpts = {}
  // 过滤非插槽表单
  Object.keys(attrs.formOpts).forEach((key) => {
    if (attrs.formOpts[key].opts) {
      formOpts[key] = attrs.formOpts[key]
    }
  })
  Object.keys(formOpts).forEach((formIndex) => {
    tForm.value.getChildRef(formIndex).resetFields()
  })
}
// 清空校验规则
const clearValidate = () => {
  let formOpts = {}
  // 过滤非插槽表单
  Object.keys(attrs.formOpts).forEach((key) => {
    if (attrs.formOpts[key].opts) {
      formOpts[key] = attrs.formOpts[key]
    }
  })
  Object.keys(formOpts).forEach((formIndex) => {
    tForm.value.getChildRef(formIndex).clearValidate()
  })
}
const updateFormFields = () => {
  let formOpts = {}
  // 过滤非插槽表单
  Object.keys(attrs.formOpts).forEach((key) => {
    if (attrs.formOpts[key].opts) {
      formOpts[key] = attrs.formOpts[key]
    }
  })
  Object.keys(formOpts).forEach((formIndex) => {
    tForm.value.getChildRef(formIndex).updateFields(false)
  })
}
const isShow = (name) => {
  return Object.keys(slots).includes(name)
}
// 暴露方法出去
defineExpose({clearValidate,resetFormFields,updateFormFields,setSelectedTab})
</script>

六、组件地址

gitHub组件地址

gitee码云组件地址

七、相关文章

基于ElementUi&antdUi再次封装基础组件文档


vue3+ts基于Element-plus再次封装基础组件文档


vue2/3集成qiankun微前端

最后

以上就是谦让项链为你收集整理的vue3+element-plus 的form多表单组件及多Descriptions详情组件封装(可实现单页面有多个表单/详情按模块的方式展示并且可以收缩;Vue3项目)的全部内容,希望文章能够帮你解决vue3+element-plus 的form多表单组件及多Descriptions详情组件封装(可实现单页面有多个表单/详情按模块的方式展示并且可以收缩;Vue3项目)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部