iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Vue3+ElementPlus表单组件的封装实例
  • 334
分享到

Vue3+ElementPlus表单组件的封装实例

2024-04-02 19:04:59 334人浏览 泡泡鱼
摘要

目录fORM文件夹FormItem.tsx在页面中引用总结在系统中,表单作为用户与后端交互的重要传递组件使用频率极高,故对其进行封装是必然的,也是一个编写规范代码的前端程序员必须做的

在系统中,表单作为用户与后端交互的重要传递组件使用频率极高,故对其进行封装是必然的,也是一个编写规范代码的前端程序员必须做的一件事。

vue3中封装组件时,能感受到与Vue2有着很大的不同,故作此记录。

form文件夹

在这里插入图片描述

  • FormItem.tsx文件是typescript中的新特性之一,详细可查阅TS中文文档
  • index.vue是主体文件
  • type.ts表单的规约

FormItem.tsx

import filter from '@/utils/filters'
import {
  ElCheckbox,
  ElCheckboxGroup,
  ElDatePicker,
  ElInput,
  ElInputNumber,
  ElOption,
  ElRadio,
  ElRadioGroup,
  ElSelect,
  ElTimePicker
} from 'element-plus'
import { defineComponent } from 'vue'
// 普通显示
const Span = (form: Record<string, any>, data: Record<string, any>) => (
  <span>{data.valueProp ? form[data.valueProp] : (data.filter ? filter(form[data.prop], data.filter) : form[data.prop] || '无')}</span>
)
// 输入框
const Input = (form: Record<string, any>, data: Record<string, any>) => (
  <ElInput
    v-model={form[data.prop]}
    type={data.type}
    size='small'
    show-passWord={data.type == 'password'}
    clearable
    placeholder={'请输入' + data.label}
    autosize = {{
      minRows: 3,
      maxRows: 4,
    }}
    {...data.props}
  >
  </ElInput>
)
// 数字输入框
const InputNumber = (form: Record<string, any>, data: Record<string, any>) => (
  <ElInputNumber
    size='small'
    v-model={form[data.prop]}
    controls-position="right"
    {...data.props}
  />
)
const setLabelValue = (_item: any, { optionsKey }: any = {}) => {
  return {
    label: optionsKey ? _item[optionsKey.label] : _item.label,
    value: optionsKey ? _item[optionsKey.value] : _item.value,
  }
}
// 选择框
const Select = (form: Record<string, any>, data: Record<string, any>) => (
  <ElSelect
    size='small'
    v-model={form[data.prop]}
    filterable
    clearable 
    placeholder={'请选择' + data.label}
    {...data.props}
  >
    {data.options.map((item: any) => {
      return <ElOption {...setLabelValue(item, data)} />
    })}
  </ElSelect>
)
// 单选/区间日期
const Date = (form: Record<string, any>, data: Record<string, any>) => (
  <ElDatePicker
    size='small'
    v-model={form[data.prop]}
    type={data.type}
    value-format={data.valueFormat}
    format = {data.format}
    range-separator="至"
    start-placeholder={data.startPlaceholder}
    end-placeholder={data.endPlaceholder}
    placeholder={'请选择' + data.label}
    {...data.props}
  />
)
// 单选/区间时间
const Time = (form: Record<string, any>, data: Record<string, any>) => (
  <ElTimePicker
    size='small'
    v-model={[form[data.prop]]}
    value-format={data.valueFormat}
    format = {data.format}
    range-separator="至"
    disabled = {form.editable}
    start-placeholder={data.start}
    is-range={data.isRange}
    end-placeholder={data.end}
    {...data.props}
  />
)
// 单选
const Radio = (form: Record<string, any>, data: Record<string, any>) => (
  <ElRadioGroup v-model={form[data.prop]}>
    {data.radiOS.map(
      (item: { label: string | number | boolean; value: any }) => {
        return (
          <ElRadio label={setLabelValue(item, data.prop).label}>
            {setLabelValue(item, data.prop).value}
          </ElRadio>
        )
      },
    )}
  </ElRadioGroup>
)
// 多选
const Checkbox = (form: Record<string, any>, data: Record<string, any>) => (
  <ElCheckboxGroup size='small' v-model={form[data.prop]}>
    {data.checkboxs.map(
      (item: { label: string | number | boolean; value: any }) => {
        return (
          <ElCheckbox label={setLabelValue(item, data.prop).label}>
            {setLabelValue(item, data.prop).value}
          </ElCheckbox>
        )
      },
    )}
  </ElCheckboxGroup>
)
const setFormItem = (
  form: Record<string, any> | undefined,
  data: Record<string, any>,
  editable: Boolean,
) => {
  if (!form) return null
  if (!editable) return Span(form, data)
  switch (data.type) {
    case 'input':
      return Input(form, data)
    case 'textarea':
      return Input(form, data)
    case 'password':
      return Input(form, data)
    case 'inputNumber':
      return InputNumber(form, data)
    case 'select':
      return Select(form, data)
    case 'date':
    case 'daterange':
      return Date(form, data)
    case 'time':
      return Time(form, data)
    case 'radio':
      return Radio(form, data)
    case 'checkbox':
      return Checkbox(form, data)
    default:
      return null
  }
}
export default () =>
  defineComponent({
    props: {
      data: Object,
      formData: Object,
      editable: Boolean,
    },
    setup(props) {
      return () =>
        props.data
          ? setFormItem(props.formData, props.data, props.editable)
          : null
    },
  })

index.vue

<template>
  <el-form ref="FormRef"
           :model="prop.data.data"
           :rules="editable ? prop.data.rules : {}"
           :inline="inline"
           :label-position="labelPosition"
           label-width="atuo">
    <el-row :gutter="prop.data.elRowGutter">
      <el-col v-for="item in prop.data.formItems"
              :span="item.span">
        <el-form-item :label="item.label ? item.label + ':' : ''"
                      :prop="item.prop"
                      :label-width="item.width">
          <FormItem :formData="prop.data.data"
                    :editable="editable"
                    :data="item">
          </FormItem>
        </el-form-item>
      </el-col>
      <el-col v-if="btnList && btnList.length"
              :span="24">
        <el-form-item>
          <template v-for="item in btnList">
            <Btn :props="item"
                 @click="onClick(item)"></Btn>
          </template>
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>
<script lang="ts" setup>
import { computed } from '@vue/Reactivity'
import type { FormInstance } from 'element-plus'
import { ref } from 'vue'
import formItem from './FormItem'
import type { commonForm } from './type'
interface Props {
  data: commonForm
}
const prop = defineProps<Props>()
const editable = computed(() => !!prop.data?.editable)
const inline = computed(() => !!prop.data.formProps?.inline)
const labelWidth = computed(() => prop.data.formProps?.labelWidth || '100px')
const labelPosition = computed(
  () => prop.data.formProps?.labelPosition || 'top',
)
const btnList = computed(() => {
  return prop.data.formProps?.btn
})
// tsx组件
const FormItem = formItem()
const FormRef = ref<FormInstance>()
// 表单按钮
function onClick(data: { onClick?: () => void }) {
  if (!data.onClick) return
  data.onClick()
}
// 表单校验
async function validate() {
  if (!FormRef.value) return
  const result = await FormRef.value.validate()
  return result
}
// 清除表单验证
async function resetFields() {
  return await FormRef.value.resetFields()
}
defineExpose({
  validate,
  resetFields,
})
</script>
<style scoped>
.el-form-item {
  margin: 0 10px !important;
}
.el-form-item__label {
  position: absolute;
}
.el-form-item__content {
  width: 100%;
  padding-left: 80px;
}
.el-select,
.el-input_inner {
  width: 100%;
}
</style>

type.ts

type itemType =
  | 'input'
  | 'select'
  | 'switch'
  | 'radio'
  | 'date'
  | 'time'
  | 'checkbox'
  | 'daterange'
interface FormProps {
  inline?: Boolean
  labelWidth?: string | number
  labelPosition?: 'left' | 'top' | 'right'
  btn?: object[]
}
interface FormItems {
  type: itemType
  label?: string
  prop: string
  valueProp?: string
  width?: string | number
  span?: number
  filter?: string
}
export class commonForm {
  public data: any
  private rules?: object
  public elRowGutter?: number
  public editable?: boolean
  public formProps?: FormProps
  public formItems: FormItems[]
  public dataArray?:object[]
  constructor({
    data = {},
    rules = {},
    editable = true,
    formProps = {},
    formItems = [],
    elRowGutter = 0,
  }: any) {
    this.data = data
    this.rules = rules
    this.elRowGutter = elRowGutter
    this.editable = editable
    this.formItems = formItems
    this.formProps = formProps
  }
}

在页面中引用

在这里插入图片描述

  • chanGCarrier.vue是主题页面,用来显示表单
  • userForm.ts是对表单进行渲染的数据项

index.vue

<template>
  <el-dialog v-model="show"
             v-if="show"
             :title="`${title}人员`"
             :before-close="handleClose"
             width="60%">
      <Form ref="FormRef"
            :data="formData"></Form>
    <template #footer>
      <el-button @click="handleClose">关 闭</el-button>
      <el-button type="primary"
                 v-show="!isDetail"
                 @click="submit">提 交</el-button>
    </template>
  </el-dialog>
</template>
<script lang="ts" setup>
import { reactive, ref, defineEmits } from 'vue'
// import api from '@/api'
import { ElMessage } from 'element-plus'
import useForm from './hooks/useForm'	//表单的
import api from '@/api/index'
enum types {
  'default' = '',
  'add' = '新增',
  'unData' = '编辑',
  'detail' = '详情',
}
const show = ref(false)	//控制表单开关
const title = ref(types.default)	//表单标题
const FormRef = ref()	//表单DOM
const emit = defineEmits(['refresh'])	//父组件传过来的方法,作用:在表单提交后触发,刷新数据
defineExpose({	//向父组件暴露其属性及方法,实例:父组件点击添加,触发formRef中的aDDData行为
  show,
  title,
  setData,
  addData,
  delData,
})
// 表单生成
let formData = useForm()
//新增
function addData() {
  handleOpen('add')
}
// 编辑设置数据
function setData(data: object) {	//父组件点击编辑,将值通过方法传过来
  formData.data = reactive({ ...data })	
  handleOpen('unData')
}
//删除
async function delData(data: number) {
  const res: any = await api.gasSite.deleteQueueApply({
    idList: [data],
  })
  emit('refresh')
}
// 请求
async function request() {
  let res: any
  // formData是否存在id值, 存在id值表示编辑, 不存在则为添加
  if (!formData.data?.id) {
    //编辑提交
    res = await api.gasSite.addQueueApply(formData.data)
  } else if (formData.data?.id) {
    //新增提交
    res = await api.gasSite.updateStartWarehouse(formData.data)
  }
  if (res?.status.state === '00') {
    ElMessage.success('操作成功')
    title.value = types.default
    emit('refresh')	//刷新数据
    show.value = false
  } else if (res?.status.state !== '00') {
    ElMessage.error(res?.status.state)
  }
  show.value = false
}
//清除验证信息
async function reset() {
  await FormRef.value.resetFields()
}
//新增表单打开事件
function handleOpen(type: any) {
  formData.formItems = useForm().formItems //表单item
  formData.editable = true //打开表单编辑
  title.value = types[type]
  show.value = true //表单的打开
}
//表单关闭事件
function handleClose() {
  show.value = false
  reset()	//重置该表单项,将其值重置为初始值,并移除校验结果
}
// 提交
async function submit() {
  const result = await FormRef.value.validate()
  if (result) request()
}
</script>

useForm

import { commonForm } from '@/components/common/form/type'
import { reactive } from 'vue'
export default () => {
  const rules = {
    name: [
      { required: true, message: '人员名称', trigger: 'blur' }
    ]
  }
  const form = reactive(
    new commonForm({
      data: [],
      editable: true,
      rules: rules,
      formItems: [
        {
          label: '人员名称',
          type: 'select',
          prop: 'name',
        },
        {
          label: '日期范围',
          type: 'daterange',
          prop: 'queueDate',
          format:'YYYY-MM-DD',
          valueFormat:'YYYY-MM-DD',
          startPlaceholder:'开始时间',
          endPlaceholder:'结束时间',
          span: 6,
        },
        {
          label: '时间段范围',
          type: 'time',
          prop: 'timeSlot',
          format:'HH:mm',
          valueFormat:'HH:mm',
          start:'开始时间',
          end:'结束时间',
          isRange:true,
          span: 6,
        },
        {
          label: '允许排队数量',
          type: 'input',
          prop: 'queueNum',
          span: 6,
        },
        {
          label: '生效类型',
          type: 'select',
          prop: 'isDelay',
          options: [
            {
              label: '当日生效',
              value: 0,
            },
            {
              label: '次日生效',
              value: 1,
            }
          ],
          span: 6,
        },
        {
          label: '生效时间',
          type: 'date',
          prop: 'effectiveTime',
          format:'YYYY-MM-DD',
          valueFormat:'YYYY-MM-DD',
          span: 6,
        },
      ],
    }),
  )
  return form
}

总结

一百个人有一百个编写代码的习惯,其上实现是基于模块化的思想,可能看起来有点累,但是我相信能帮助到你。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: Vue3+ElementPlus表单组件的封装实例

本文链接: https://www.lsjlt.com/news/150419.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Vue3+ElementPlus表单组件的封装实例
    目录form文件夹FormItem.tsx在页面中引用总结在系统中,表单作为用户与后端交互的重要传递组件使用频率极高,故对其进行封装是必然的,也是一个编写规范代码的前端程序员必须做的...
    99+
    2022-11-13
  • vue3封装放大镜组件的实例代码
    目录组件基础结构 目的:实现图片放大镜功能安装vueuse功能实现 完整代码 总结组件基础结构 结尾有完整代码可直接复制使用 目的:封装图片预览组件,实现鼠标悬停切换效果 落地代...
    99+
    2022-11-12
  • vue3如何封装input组件和统一表单数据
    本篇内容主要讲解“vue3如何封装input组件和统一表单数据”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“vue3如何封装input组件和统一表单数据”吧!准备工作用vue create ex...
    99+
    2023-07-06
  • vue3怎么封装input组件和统一表单数据
    准备工作用vue create example创建项目,参数大概如下:用原生 input原生的 input,主要是 value 和 change,数据在 change 的时候需要同步。App.tsx如下:import { ref } fro...
    99+
    2023-05-14
    Vue3 input
  • vue3简单封装input组件和统一表单数据详解
    目录前言准备工作用原生 input封装 Input封装表单数据使用表单数据总结前言 vue3 支持用 jsx 实现组件,摆脱了 vue 文件式的组件,不再需要额外的指令,写法非常接近...
    99+
    2022-11-13
  • Vue3封装回到顶部组件的实现示例
    我们在网页中应该经常可以看到回到顶部这个功能,这个功能也比较简单。 代码:  <template> <div class="page-content-...
    99+
    2022-11-13
  • Vue3 Composition API优雅封装第三方组件实例
    目录前言✨一、对于第三方组件的属性props、事件events二、对于第三方组件的插槽slots三、对于第三方组件的方法methods前言✨ 对于第三方组件,如何在保持第三方组件原有...
    99+
    2022-11-13
  • vue3 封装自定义组件v-model的示例
    首先要注意 vue3中 v-model 默认绑定的变量名变了,从原理的 value 改成了 modelValue,如果要改变变量的值,要执行一个事件 this.$emit("...
    99+
    2022-11-13
  • vue3实现H5表单验证组件的示例
    目录效果图描述实现思路与element-ui表单组件差异非uni-app平台的移植效果图 描述 基于vue.js,不依赖其他插件或库实现;基础功能使用保持和 element-ui ...
    99+
    2023-05-16
    vue3 H5表单验证 vue3 表单验证
  • vue3封装自己的分页组件
    本文实例为大家分享了vue3封装自己分页组件的具体代码,供大家参考,具体内容如下 背景 在浏览列表类型的数据的时候,如果数据比较多一次性全部请求会出现性能损耗以及加载延迟等问题,那...
    99+
    2022-11-12
  • 利用Vue3封装一个弹框组件简单吗
    目录总结放前面: 前言:公共全局弹框结语:总结放前面: Tipes: 封装弹框组件使用了Teleport,避免了组件嵌套可能导致的定位层级的隐患,还使用了defineProps,de...
    99+
    2022-11-12
  • 利用vue3自己实现计数功能组件封装实例
    目录前言一、封装的意义二、如何封装?1. 思路2. 准备2. 使用三、 效果演示总结前言 本文将带你用vue3自己封装一个实现计数功能的全局组件,其应用场景相信各位一看便知,那就是...
    99+
    2022-11-12
  • Vue3导航栏组件封装实现方法
    在Vue3中封装一个导航栏组件,并且实现,随着滚动条滚动实现一个吸顶效果,供大家参考 导航栏组件的效果图: 滚动条滚动以后的吸顶效果示意图: 具体代码展示: <temp...
    99+
    2022-11-12
  • vue3+Pinia+TypeScript 实现封装轮播图组件
    目录为什么封装?静态结构 后面再进行更改请求数据都存放在pinia里面类型检测页面级组件全局组件为什么封装? 迎合es6模块化开发思想注册为全局组件,可以更好地复用,需要用到的地方,...
    99+
    2022-11-13
  • vue3封装计时器组件的方法
    背景 在一些商城类网页中打开商品详情都会有一个计数器来选择购买的数量,这样的计时器不仅会在商品详情页面显示还会在购物车里面有,那就可以把计时器封装成组件,以便于更好的复用以及后期维护...
    99+
    2022-11-12
  • vue3封装轮播图组件的方法
    目的 封装轮播图组件,直接使用,具体内容如下 大致步骤 准备my-carousel组件基础布局,全局注册 准备home-banner组件,使用my-carousel...
    99+
    2022-11-12
  • vue3学习笔记简单封装axios示例实现
    目录简介openapi基本配置拦截器api请求数据渲染简介 axios是一个基于promise的网络请求库,管理后台使用的场景通常 获取后端api数据,然后交给页面渲染 还是在前面的...
    99+
    2022-11-13
  • vue封装form表单组件拒绝重复写form表单
    目录前言核心思想:实现重点:表单双向绑定的方式有两种: 1.使用v-model进行双向绑定2.使用v-model的语法糖配置项整体字段:效果浏览源码放送1. baseFor...
    99+
    2022-11-13
  • vue封装组件js实例分析
    本文小编为大家详细介绍“vue封装组件js实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue封装组件js实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。什么是组件化:组件化就是将一个页面拆分成一...
    99+
    2023-06-30
  • Vue3 Element-plus和el-menu无限级菜单组件怎么封装
    对于element中提供给我们的el-menu组件最多可以实现三层嵌套,如果多一层数据只能自己通过变量去加一层,如果加了两层、三层这种往往是行不通的,所以只能进行封装效果图 一、定义数据MenuData.tsexport default [...
    99+
    2023-05-14
    Vue3 Element-plus el-menu
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作