基于element-ui中Search组件的二次封装

182 阅读3分钟

1. 起因

当一个项目不断进行需求迭代时,避免不了有相似功能的功能出现,这些功能可能重复率很高,只有部分内容不同,重复去复制粘贴代码会让整个项目变的更加臃肿,积重难返,所以需要根据项目的功能需求,基于element-ui进行二次功能开发,并在封装组件中进行不断的迭代,减少重复代码,使项目变得更加整洁,干净,避免臃肿。

2.实现思路

根据需要,在根目录组件文件夹下面,创建一个独立页面进行代码开发。将常用的功能进行封装,并将可选参数暴露出来,同时通过插槽留好空缺,遇到个别特殊的功能可以通过插槽来实现功能。

3. 开发过程

<template>
  <div>
    <el-form
      ref="searchForm"
      :model="searchForm"
      class="searchForm"
      label-width="120px"
    >
      <el-form-item
        v-for="(v, i) in columns"
        :key="i"
        :label="v.label"
        :style="{ width: (v.width ? v.width : v.inputWidth ? v.inputWidth + 120 : 320) + 'px' }"
      >
        <!-- 插槽 -->
        <template v-if="v.type === 'slot'">
          <slot :name="v.slotName"> </slot>
        </template>
        <!-- 输入框 -->
        <template v-else-if="v.type === 'input'">
          <el-input
            v-model.trim="searchForm[v.prop]"
            clearable
            :placeholder="v.placeholder || `请输入` + v.label"
            :style="{ width: (v.inputWidth || 200) + 'px' }"
          ></el-input>
        </template>
        <!-- 下拉框 -->
        <template v-else-if="v.type === 'select'">
          <el-select
            v-model="searchForm[v.prop]"
            clearable
            filterable
            collapse-tags
            :multiple="!!v.multiple"
            :style="{ width: (v.inputWidth || 200) + 'px' }"
            :placeholder="v.placeholder || `请选择` + v.label"
          >
            <el-option
              v-for="(item, idx) in v.selList"
              :key="idx"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </template>
        <!-- 时间日期选择框 -->
        <template v-else-if="v.type === 'datePicker'">
          <el-date-picker
            v-model="searchForm[v.prop]"
            clearable
            :type="v.dateType || 'daterange'"
            align="right"
            :placeholder="v.placeholder || `请选择` + v.label"
            :style="{ width: (v.inputWidth || 300) + 'px' }"
            unlink-panels
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            :value-format="v.valueFormat || 'yyyy-MM-dd'"
            :picker-options="v.pickerOptions"
          >
          </el-date-picker>
        </template>
        <template v-else-if="v.type === 'button'">
          <div class="search-btn">
            <!-- 默认按钮 -->
            <el-button
              type="primary"
              :icon="v.iconFlag ? 'el-icon-search' : ''"
              @click="handleClickSearch"
            >查询</el-button>
            <el-button
              type="danger"
              :icon="v.iconFlag ? 'el-icon-delete' : ''"
              @click="handleClickReset"
            >重置</el-button>
            <!-- 附加插槽按钮 -->
            <slot name="btnSlot"> </slot>
          </div>
        </template>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
export default {
  name: "SearchCom",
  props: {
    /**
     * columns: array 搜索栏目 (必传)
     * item项目:
     * {
     * 公共传参:
     *  type: string 搜索项类型 (必传)
     *    type类型 input(输入框)
     *            select(单/多选)
     *            datePicker(日期框)
     *            button(按钮项)
     *            slot(插槽)
     *  label:string 搜索项中文(必传)
     *  prop:  string 搜索项对应val (必传)
     *    input和select类型建议传接口参数明 搜索fn返回对应key
     *  width: number 搜索项宽度单位px 默认 340
     *    340 = lable 宽度120 + input/select默认宽度200
     *    datePicker 如果占两项宽度简易建议给640
     *  inputWidth: number 输入/选择/日期框宽度 单位px
     *    搜索框默认200 日期框默认300
     * placeholder: string
     *
     * type === 'selsect' 传参:
     *  selList: array  下拉列表 (必传) {label,value}
     *  multiple: boolean 是否多选
     *
     * type === 'datePicker' 传参:
     *  dateType: string 日期框类型 默认daterange
     *  pickerOptions: object
     *  valueFormat: string 日期格式 默认 yyyy-MM-dd
     *  defaultValue: array 默认日期
     *
     * type === 'slot' 传参:
     *  slotName: string 插槽名(必传)
     * }
     *
     * type === ‘button’ 如果加额外按钮 插槽名用btnSlot
     *  iconFlag: boolean 搜索和重置按钮是否展示icon
     **/
    columns: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      // 查询项
      searchForm: {}
    };
  },
  created() {
    this.initData();
    this.handleClickSearch();
  },
  methods: {
    // 初始化数据
    initData() {
      const form = {};
      const types = ["input", "select", "datePicker"];
      for (const v of this.columns) {
        if (types.includes(v.type)) {
          form[v.prop] = v.defaultValue || null;
        }
      }
      this.searchForm = form;
    },
    /**
     *  点击搜索
     *  $emit传值不包含type==='slot'项
     * */
    handleClickSearch() {
      this.$emit("onHandleSearch", {
        ...this.searchForm
      });
    },
    /**
     *  点击重置
     *  $emit传值不包含type==='slot'项
     *  需使用页面自行处理重置操作
     * */
    handleClickReset() {
      this.initData();
      this.$emit("onHandleReset", {
        ...this.searchForm
      });
    }
  }
};
</script>
<style lang="scss" scoped>
.searchForm {
  display: flex;
  align-items: center;
  flex-wrap: wrap;

  .search-btn {
    display: flex;
    flex-wrap: wrap;
  }
}
</style>

4. 使用方法

在需要使用的地方,使用import引入,根据自己的功能传入不同的参数,如果需要给组件传值则使用$emit传值。

5. 后续迭代

如果有多个通用的功能可以对公共组件进行修改开发,如果只是特殊的功能建议使用插槽去进行开发。