vue2+element封装一个搜索组件

993 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

因为后台管理系统中各种页面布局差不多,并且搜索模块用的地方很多,每次都重新写的话,重复工作太多,所以就封装一个组件,有很多不足,请见谅,并且提提建议

ps: vue3+ts的版话,因为目前我还在学习,所以就先用vue+element在项目中使用啦

1.封装一个叫 searchForm 的组件来适配常用的,(可以自行进行适当添加不同的内容)

<template>
  <div class="hs-from">
    <div class="header">
      <slot name="header" />
    </div>
    <el-form :label-width="labelWidth">
      <el-row>
        <div v-for="item in formItems" :key="item.label">
          <el-col v-bind="colLayout">
            <el-form-item :label="item.label" :style="itemStyle">
              <!-- 输入框 -->
              <template
                v-if="item.type === 'input' || item.type === 'password'"
              >
                <el-input
                  :show-password="item.type === 'password'"
                  :placeholder="item.placeHolder"
                  autocomplete="off"
                  :clearable="clearabled"
                  :value="formData[`${item.field}`]"
                  @input="handleModelValue($event, item.field)"
                />
                <!-- {{ formData[`${item.field}`] }} -->
              </template>
              <!-- 下拉框 -->
              <template v-else-if="item.type === 'select'">
                <el-select
                  :value="formData[`${item.field}`]"
                  @input="handleModelValue($event, item.field)"
                  :clearable="clearabled"
                >
                  <el-option
                    style="width: 100%"
                    v-for="option in item.options"
                    :key="option.id"
                    :label="option.label"
                    :value="option.id"
                  />
                </el-select>
              </template>
              <!-- 时间范围 type='daterange' -->
              <template v-if="item.type === 'datePicker'">
                <el-date-picker
                  :value="formData[`${item.field}`]"
                  @input="handleModelValue($event, item.field)"
                  :clearable="clearabled"
                  style="width: 100%"
                  v-bind="item.otherOptions"
                >
                </el-date-picker>
              </template>
            </el-form-item>
          </el-col>
        </div>
        <!-- 一行只有两个form-item时按钮展示在一行 -->
        <template v-if="formItems.length < 3">
          <slot name="inlineButton" />
        </template>
      </el-row>
    </el-form>
    <div class="hs-footer">
      <slot name="footer" />
    </div>
  </div>
</template>

<script>
export default {
  name: "searchFrom",
  //   module: { // 改变要触发的事件名称
  //     event: "changeValue",
  //   },
  props: {
    // 双向绑定的值 vue2中的默认是value,vue3中的是modelValue
    value: {
      type: Object,
      required: true,
    },
    // formItem的配置对象参数
    formItems: {
      type: Array,
      default: () => [],
    },
    // 是否显示清除按钮
    clearabled: {
      type: Boolean,
      default: true,
    },
    // label文本的宽度
    labelWidth: {
      type: String,
      default: "100px",
    },
    // 每个form-item之间的距离
    itemStyle: {
      type: Object,
      default: () => ({
        padding: "20px 30px",
      }),
    },
    // 适配
    colLayout: {
      type: Object,
      default: () => ({
        xl: 6,
        lg: 8,
        md: 12,
        sm: 24,
        xs: 24,
      }),
    },
  },
  //   created() {
  //     console.log(this.value);
  //   },
  data() {
    return {
      formData: this.value,
    };
  },

  methods: {
    handleModelValue(value, field) {
      // value: 输入的值,field:要改变的字段
      this.$emit("changeValue", { value, field });
    },
  },
};
</script>

<style></style>

  1. 在封装的时候可以通过 v-bind 来传入一个对象的所有 property,在二次封装组件时非常好用

  2. 可以通过 type 的类型来判断展示不同的内容

  3. :value="formData[${item.field}]"

    @input="handleModelValue($event, item.field)",实现双向绑定,父级监听

1.3 v-model的组件传值 vue3和vue2的区别

Vue3和vue2实现组件间双向绑定的方式不太一样,其原因是因为v-model内部本质的变化,vue2的v-model默认的属性名是value,事件是input,vue3的v-model默认的属性名是modelValue,事件名是update:modelValue,接下来使用v-model的属性名和事件名来实现组件之间的双向绑定。

vue3的双向绑定

当在自定义组件中使用v-model时,组件接收一个属性modelValue的值,然后通过触发update:modelValue事件来更新该值:

<custom-from v-model="msg"></custom-from>
<!-- 等价于 -->
<custom-from :model-value="msg" @update:model-value="msg = $event"></custom-from>
<!-- 建议命名按照kebab-cased规范,如:model-value,而不是modelValue -->
vue2的双向绑定

当在自定义组件中使用v-model时,组件接收一个属性value的值,然后通过触发input事件来更新该值,这样便实现了v-model

<custom-from v-model="msg"></custom-from>
<!-- 等价于 -->
<custom-from :value="msg" @input="msg = $event"></custom-from>

1.1 传递静态或动态 Prop(单个传值)

// 静态
<blog-post title="My journey with Vue"></blog-post>
可以通过 `v-bind` 动态赋值
<blog-post v-bind:title="post.title + ' by ' + post.author.name" \>

1.2 传入一个对象的所有 property

如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)。例如,对于一个给定的对象 post

post: {   id: 1,   title: 'My Journey with Vue' } 

下面的模板:

<blog-post v-bind="post"></blog-post> 

等价于:

<blog-post   v-bind:id="post.id"   v-bind:title="post.title" ></blog-post>

2. 注册全局组件 (因为通用吗,所以就全局注册啦)

创建一个注册全局组件的 js文件

import SearchFrom from './FormTable/searchFrom.vue'
import Vue from 'vue'
export default {
    install(){
        Vue.component("SearchFrom",SearchFrom)
    }
}

在manin.js中注册一下,因为vue中注册组件也是执行,install 方法,所以上面就导出一个 包含install 的对象

// 全局注册组件 main.js
import globalComponents from './components/globalComponents'
Vue.use(globalComponents)

3. 写一个配置对象,传给 SearchFrom 组件的 props

因为单独绑定看着很多字段,合并写一个对象,通过 v-bind 进行绑定

// 合并一个对象 porps 通过v-bind绑定传给组件
export default { //将所有要传的props合并为一个对象,通过v-bind传给 searchFrom 组件
  formItems: [
    {
      field:'time', // 字段名称
      label: "收款时间:",
      type: "datePicker", // 展示类型
      otherOptions: {
        startPlaceholder: "开始时间", //  起始时间占位
        endPlaceholder: "结束时间",
        type: "daterange", //  显示类型
        format: "yyyy 年 MM 月 dd 日", // 显示在输入框中的格式
        valueFormat: "timestamp", // 绑定值的格式。不指定则绑定值为 Date 对象
        unlinkPanels:true , // 是否解除联动
        defaultTime:['00:00:00', '23:59:59'] // 起始时间和结束时间的具体时刻
      },
    },
    {
      field:'shop_name',
      label: "商家名称:",
      placeHolder: "请输入商家名称",
      type: "input",
    },
    // {
    //     filed:'name',
    //     type:'select',
    //     label:'下拉框:',
    //     placeHolder:'请选择',
    // 下拉框的选项
    //     options:[
    //         {label:'下拉选项1',id:1},
    //         {label:'下拉选项2',id:2},
    //     ]
    // }
  ],
//   clearabled:false,
  itemStyle: {
    padding: "0px 5px",
  },
  colLayout: {
    span: 10,
  },
}

展示视图: image.png

4. 使用+ v-model组件传值的数据展示

<template>
<!-- 搜索项 -->
    <SearchForm
      v-bind="searchFormConfig"
      v-model="formData"
      @changeValue="changeValue"
    >
      <template #inlineButton>
        <el-row class="FR">
          <el-button type="primary" @click="search">搜索</el-button>
          <el-button>导出</el-button>
        </el-row>
      </template>
    </SearchForm>
<template>
<script>
import searchFormConfig from "./config/searchFrom"; // 搜索项的配置文件选项
export default {
  name: "technicalServiceFeeStatistics",
  data() {
    return {
      searchFormConfig, // 搜索项的配置对象
      formData: {
        // 创建一个 搜索参数的 对象
        shop_name: "",
        time: "",
        page: 1,
        page_size: 10,
      },
    };
  },
  // 这样增加对象中的字段,vue无法进行双向绑定
  //     mounted() {
  //     // 双向绑定的属性应该是由配置文件的field来决定
  //     const formItems = props.searchFormConfig?.formItems
  //     const formOriginData = {}
  //     for (const item of formItems) {
  //       formOriginData[item.field] = ''
  //     }
  //     this.formData = ref(formOriginData)
  //   },
  methods: {
    // 修改formData里面字段的值
    changeValue({ value, field }) {
      this.formData[field] = value;
      console.log(this.formData);
    },
  },
};
</script>

在 SearchFrom 中使用 this.$emit("changeValue", { value, field });,父组件通过 changeValue事件改变字段值,实现单向数据流