一、组件化封装

1.首先创建一个form文件夹,将搜索框组件的内容全部写在这个里面,然后再在需要的页面直接引入相应的组件即可

2.首先先在goods.vue文件里面写对应的文本数组formItems,将所定义的类型IFormItem引用进去,这个里面写的字段都是对应goods.vue文件里面的文本数组formItems里面所拥有的字段

 const formItems: IFormItem[] = [
      {
        field: "id",
        type: "input",
        label: "id",
        placeholder: "请输入账号"
      },
      {
        field: "realname",
        type: "input",
        label: "姓名",
        placeholder: "请输入姓名"
      },
      {
        field: "cellphone",
        type: "input",
        label: "电话号码",
        placeholder: "请输入电话号码"
      }
    ]

 3.然后在form.vue文件里面去判断获取到的prop的数据,也就是父组件goods.vue传给子组件form.vue的formItems,根据里面写的type的类型去进行组件的匹配

form.vue

form.vue

  <el-form label-width="100px">
      <el-row>
        <!--  :key="item.label"动态匹配-->
        <template v-for="item in formItems" :key="item.label">
          <el-col :span="8">
            <!-- 动态匹配label值 -->
            <el-form-item :label="item.label">
               <!-- 动态判断文本框类型,从而去使用对应的element组件 -->
              <template
                v-if="item.type === 'input' || item.type === 'password'"
              >
              <!-- type是文本框 -->
                <el-input
                  :placeholder="item.placeholder"
                  :show-password="item.type === 'password'"
                />
              </template>
              <template v-else-if="item.type === 'select'">
                <!-- type是下拉框 -->
                <el-select :placeholder="item.placeholder" style="width: 100%">
                  <el-option
                    v-for="option in item.options"
                    :key="option.value"
                    :value="option.value"
                    >{{ option.title }}</el-option
                  >
                </el-select>
              </template>
            </el-form-item>
          </el-col>
        </template>
      </el-row>
    </el-form>

 4.可以选择在form.vue里面接收相应的样式itemStyle和labelWidth,或者直接在form.vue里面给他们默认值,然后绑定到上面对应的组件上,就能进行响应

 5.一开始可以将所有的itemLayout、formItems、lableWidth和colLayout全部由父组件传给子组件,但是这样的话hy-form组件里面要传的东西就太多了,所以可以考虑把上面这些数据全部放在一个新变量里面,然后给这个新变量定义一个类型,这个类型里面就包括了这些变量值

6.虽然这样一定程度上简化了代码,但是goods.vue文件里面的代码还是太多了,为了更加简便,我们可以把formConfig单独抽离到一个文件里面,然后再在goods.vue里面去引用

search.config.ts

import { IForm } from '@/base-ui/form'
export const searchformConfig: IForm = {
  labelWidth: '120px',
  itemStyle: {
    padding: '10px 40px'
  },
  colLayout: {
    span: 8
  },
  formItems: [
    {
      field: "id",
      type: "input",
      label: "id",
      placeholder: "请输入商品id"
    },
    {
      field: "realname",
      type: "input",
      label: "商品名称",
      placeholder: "请输入商品名称"
    },
    {
      field: "cellphone",
      type: "input",
      label: "商品编号",
      placeholder: "请输入商品编号"
    }
  ]
}

 goods.vue

<template>
  <div class="main">
    <div class="search">
      <!-- <hy-form
        :formItems="formItems"
        :collayout="collayout"
        :lableWidth="lableWith"
        :itemLayout="itemLayout"
      ></hy-form> -->

      <!-- <hy-form v-bind="formConfig"></hy-form> -->
      <hy-form v-bind="searchformConfig"></hy-form>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent } from "vue"
// import HyForm, { IFormItem, IForm } from "@/base-ui/form"
import HyForm from "@/base-ui/form"
import { searchformConfig } from "./config/search.config"

export default defineComponent({
  name: "goods",
  components: {
    HyForm
  },
  setup() {
    // const formItems: IFormItem[] = [
    //   {
    //     field: "id",
    //     type: "input",
    //     label: "id",
    //     placeholder: "请输入账号"
    //   },
    //   {
    //     field: "realname",
    //     type: "input",
    //     label: "姓名",
    //     placeholder: "请输入姓名"
    //   },
    //   {
    //     field: "cellphone",
    //     type: "input",
    //     label: "电话号码",
    //     placeholder: "请输入电话号码"
    //   }
    // ]
    // const labelWidth = "120px"
    // const itemStyle = { padding: "10px 40px" }
    // const collayout = { span: 8 }
    // 简化后的变量
    // const formConfig: IForm = {
    //   formItems: [
    //     {
    //       field: "id",
    //       type: "input",
    //       label: "id",
    //       placeholder: "请输入商品id"
    //     },
    //     {
    //       field: "realname",
    //       type: "input",
    //       label: "商品名称",
    //       placeholder: "请输入商品名称"
    //     },
    //     {
    //       field: "cellphone",
    //       type: "input",
    //       label: "商品编号",
    //       placeholder: "请输入商品编号"
    //     }
    //   ],
    //   labelWidth: "120px",
    //   itemStyle: { padding: "10px 40px" },
    //   collayout: { span: 8 }
    // }
    return {
      // formItems,
      // labelWidth,
      // itemStyle,
      // collayout
      // formConfig
      searchformConfig
    }
  }
})
</script>

二、解决文本框的双向绑定问题

1.可以使用父组件向子组件传参数的办法,首先在一开始封装的formItem里面定义一个字段field,然后再在goods组件上去双向绑定

2.然后在form.vue文件里面,给文本框或下拉框这些动态匹配绑定V-model

goods.vue

search.config.ts

form.vue

 

3.但是这样做的话会有一个弊端,就是直接修改从父组件传过来的数据,一定程度上不是很严谨,所以可以选择将从父组件获取到的数据变量赋值拷贝给另一个变量,然后在form.vue里面去动态监听这个变量是否发生变化,从而去发送监听事件

goods.vue

 

form.vue

 

 三、给搜索框动态绑定一个title标题

1.可以先给form.vue组件一个自定义的插槽

2.然后在之前的formItem数组中再多定义一个字段title

3.最后在引用form.vue组件对应的地方加入一个插槽template,然后动态匹配对应的title字段

 

 四、增加搜索和重置按钮

1、跟前面增加title标题一样的道理,先给form.vue增加一个底部插槽

2.在goods.vue组件对应的位置加一个template,里面对应相应的按钮

 五、组件的封装简化

1.为了使搜索组件能够多次使用,不用重复写代码,可以创建一个page-search文件夹,专门编写搜索组件的代码,这样方便之后直接引用这个组件到相应的页面即可

 简化后的goods.vue

<template>
  <div class="goods">
    <page-search :searchformConfig="searchformConfig"></page-search>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue"
import { searchformConfig } from "./config/search.config"
import PageSearch from "@/components/page-search"

export default defineComponent({
  name: "goods",
  components: {
    PageSearch
  },
  setup() {
    return {
      searchformConfig
    }
  }
})
</script>
<style scoped></style>

 简化后的page-search.vue

<template>
  <div class="page-search">
    <div class="search">
      <!-- <hy-form v-bind="formConfig"></hy-form> -->
      <!-- <hy-form v-bind="searchformConfig" :formData="formData"></hy-form> -->
      <hy-form v-bind="searchformConfig" v-model="formData">
        <!-- 插槽对应form.vue组件里面定义的一个插槽slot,这里的title可以自定义,从serch.config.ts传过来 -->
        <!-- 头部标题插槽 -->
        <template #header>
          <h1>{{ searchformConfig.title }}</h1>
        </template>
        <!-- 底部按钮插槽 -->
        <template #footer>
          <div class="handle-btns">
            <el-button @click="handleResetClick">
              <el-icon> <Refresh /> </el-icon>重置
            </el-button>
            <el-button type="primary" @click="handleQueryClick">
              <el-icon> <Search /> </el-icon>搜索
            </el-button>
            <slot></slot>
          </div>
        </template>
      </hy-form>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue"
import HyForm from "@/base-ui/form"
// import { searchformConfig } from './config/search.config'
import { Search, Refresh } from "@element-plus/icons"

export default defineComponent({
  props: {
    searchformConfig: {
      type: Object,
      required: true
    }
  },
  components: {
    HyForm,
    Search,
    Refresh
  },

  setup(props, { emit }) {
    const formData = ref({
      id: "",
      realname: "",
      cellphone: ""
    })
    return {
      formData
    }
  }
})
</script>
<style scoped>
.handle-btns {
  text-align: right;
  padding: 0 50px 20px 0;
}
</style>

 form.vue文件跟之前的一样

<template>
  <div class="hy-form">
    <div class="header">
      <slot name="header"></slot>
    </div>
    <el-form :label-width="labelWidth">
      <el-row>
        <!--  :key="item.label"动态匹配-->
        <template v-for="item in formItems" :key="item.label">
          <el-col v-bind="collayout">
            <!-- 动态匹配label值 -->
            <el-form-item :label="item.label" :style="itemStyle">
              <!-- 动态判断文本框类型,从而去使用对应的element组件 -->
              <template
                v-if="item.type === 'input' || item.type === 'password'"
              >
                <!-- type是文本框 -->
                <el-input
                  :placeholder="item.placeholder"
                  :show-password="item.type === 'password'"
                  v-model="formData[`${item.field}`]"
                />
              </template>
              <template v-else-if="item.type === 'select'">
                <!-- type是下拉框 -->
                <el-select
                  :placeholder="item.placeholder"
                  style="width: 100%"
                  v-model="formData[`${item.field}`]"
                >
                  <el-option
                    v-for="option in item.options"
                    :key="option.value"
                    :value="option.value"
                    >{{ option.title }}</el-option
                  >
                </el-select>
              </template>
            </el-form-item>
          </el-col>
        </template>
      </el-row>
    </el-form>
    <div class="footer">
      <slot name="footer"></slot>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType, ref, watch } from "vue"
import { IFormItem } from "../types"

export default defineComponent({
  props: {
    // 改进之后的formData
    modelValue: {
      type: Object,
      required: true
    },
    // 这是子组件接收父组件传过来的数组数据
    formItems: {
      // 默认传过来的数据是IFormItem类型
      type: Array as PropType<IFormItem[]>,
      // 如果默认数据是数组或者对象时需要用箭头函数表示
      default: () => []
    },
    labelWidth: {
      type: String,
      default: "100px"
    },
    title: {
      type: String,
      default: ""
    },
    itemStyle: {
      type: Object,
      default: () => ({ padding: "10px 40px" })
    },
    collayout: {
      type: Object,
      default: () => ({
        xl: 6, //>1920 显示4个
        lg: 8,
        md: 12,
        sm: 24,
        xs: 24
      })
    }
  },
  emits: ["update:modelValue"],
  setup(props, { emit }) {
    // 这里是将获取到的props.modelValue重新拷贝一份放在formData
    const formData = ref({ ...props.modelValue })
    // 深度监听,当formData发生变化,就发送事件给父组件
    watch(
      formData,
      (newValue: any) => {
        console.log(newValue)
        emit("update:modelValue", newValue)
      },
      {
        deep: true
      }
    )
    return {
      formData
    }
  }
})
</script>

<style scoped lang="less">
.hy-form {
  padding-top: 22px;
}
</style>

Logo

前往低代码交流专区

更多推荐