【组件封装-优化】vue+element plus:二次封装select组件,实现下拉列表有分页、自定义是否可搜索的一系列功能

第一章 前言

1.1 实现效果

1.2 需求及准备

  • 开发前言:由于公司针对下拉框都是直接使用组件,并且下拉数据都不是特别多。而小编的需求是接口直接返回了2000多条数据,一个原因是数据特别庞大,请求接口需要花费特别长时间;另一个是2000调数据我们也不好一次性都展示在下拉框,体验感不是很好;于是通过小编的研究与优化,与后端约定将数据改成提供分页接口,并且小编也利用element-plus组件提供了两个方法:一个是直接使用无限滚动组件,触底掉接口(Infinite Scroll);另一个是再二次封装一个分页的下拉框,经过最终讨论,确定方案用后者(原因:扩展性更高,且后续其他项目可通用
  • 核心需求:因下拉框中的数据过多,所以需要使用分页的方式来实现。
  • 工具:element-plus

设计 | Element Plus

 Infinite Scroll 无限滚动 | Element Plus

  • v-bind="$attrs" 理解

【vue】v-bind=“$attrs“理解与使用-CSDN博客

  • 插槽

【vue】slot插槽:灵活内容分发的艺术-CSDN博客 

第二章 源代码

2.1 组件开发

  • 实现思路
  1. 基于element-plus开发,必须需要两个组件el-selec和组件el-pagination
  2. 需要能继承到上面两个组件的所有属性,减少任务量
  3. 支持是否可搜索(可用props控制显隐或者插槽等);是否可分页(由于小编的需求是支持分页,所以小编的思路是默认就有,大家可根据需求添加配置项或者做成插槽);
  4. 分页与下拉框数据联动;
  5. 要学会利用v-model对某些必要数据双向绑定方便我们对数据的处理
  6. 除了以上必要功能,还有就是样式了,也要稍微处理
  • 注意:小编在封装时会已经使用一些element组件的配置项,如果父组件在使用时用了同名的属性,则会覆盖;大家需要留意一下
  • 组件原代码如下
<!-- 组件名:ui-select -->

<script setup>
// 接收一些必要的配置项
const props = defineProps({
  
  val: {
    type: String,
    default: ''
  },
  // 选择框宽度
  width: {
    type: String,
    default: ''
  },
  // options弹窗宽度
  optionsWidth: {
    type: String,
    default: ''
  },
  // 配置项
  options: {
    type: Array,
    default: () => [
      {
        id: 1,
        label: 'Option1',
        value: 'Option1'
      }
    ]
  },
  // 分页数据
  pageData: {
    type: Object,
    default: () => {
      return {
        pageNo: 1,
        pageSize: 10,
        total: 20
      }
    }
  }
})

const emit = defineEmits(['update:val', 'update:pageData', 'pageChange'])
const selectVal = ref('')
const page = ref({})
onMounted(() => {
  const { val, pageData } = props
  // element组件是支持select的双向绑定的,所以小编单独用了一个新的变量处理
  selectVal.value = val
  // 分页数据
  page.value = pageData
})
const selectChange = (item) => {
  // select选中值的更新
  emit('update:val', item)
}
const pageChange = () => {
  // 分页变化更新分页数据
  emit('update:pageData', page.value)
。// 同时emit一个方法,让父组件可以利用这个方法执行某些操作
  emit('pageChange')
}
</script>

<template>
  <div>
    <!-- v-bind="$attrs"小编前面有提到,就是完整的拿到父组件传的参数,从而不需要props再接一层 -->
    <!-- select 和 pagination都设置v-bind="$attrs",继承两个组件的配置项 -->
    <!-- 注意:小编在封装的时候已经使用了一些element组件的配置项,如果父组件在使用时用了同名的属性,则会覆盖 -->
    <el-select
      v-model="selectVal"
      v-bind="$attrs"
      :style="{
        width: width || '100%'
      }"
      @change="selectChange"
      class="custom_select"
    >
      <!-- 最小宽度是分页撑的宽度 -->
      <div
        :style="{
          width: optionsWidth || '100%'
        }"
        class="options_content"
      >
        <!-- 这里使用到了插槽,主要是扩展我们二次封装的select组件是否可搜索或者再前面需要加什么内容…… -->
        <slot name="header" class="header"></slot>
        <!-- element的select的options配置项 -->
        <div class="options_main">
          <el-option
            v-for="item in options"
            :key="item.id"
            :label="item.label"
            :value="item.value"
          />
        </div>
        <!-- 分页组件 -->
        <!-- 注意这里也有v-bind="$attrs"使用 -->
        <el-pagination
          v-model:current-page="page.pageNo"
          :page-size="page.pageSize"
          layout="prev, pager, next"
          :total="page.total"
          @current-change="pageChange"
          v-bind="$attrs"
          class="pagination_page"
        />
      </div>
    </el-select>
  </div>
</template>

// 下面就是我们针对二次封装组件的一些其他样式
<style lang="scss" scoped>
.pagination_page {
  width: 100%;
  padding: 10px 20px 10px 20px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.options_main {
  // element select组件的弹窗是在根下的,不好直接设置弹窗的高,只能限制内容了
  height: 140px;
  margin: 0 20px;
  overflow: auto;
}
::-webkit-scrollbar {
  width: 5px;
}
</style>

2.2 组件使用

<!--
说明:
    1、placeholder、clearable是el-select组件的属性,
    由于我们使用了$attrs,所以再uiselect直接使用也是可以生效的;
    同理background是el-pagination的也是可以的
     2、小编使用了header插槽为分页组件提供了可搜索的功能
     3、要留意这些字段的含义
-->
<!--
解释一下字段:
aiForm.description: 下拉框绑定的值
scopePage: 分页数据对象
    {
      pageNo: 1,
      pageSize: 10,
      total: 100
    }
decOptions: 下拉数据
scopeSearchForm.description: 搜索关键字
-->
<UiSelect
  v-model:val="aiForm.description"
  v-model:pageData="scopePage"
  placeholder="行业表诉"
  class="ai_search-border margin_right_bottom"
  width="150px"
  :options="decOptions"
  background
  @pageChange="getDescriptions"
  clearable
>
  <template #header>
    <div class="scope_header">
      <el-input
        v-model="scopeSearchForm.description"
        class="ai_search-border margin_right_bottom"
        style="border-color: #dcdfe6"
        placeholder="请输入关键字"
        clearable
      >
        <template #append>
          <el-icon style="cursor: pointer" @click="getDescriptions"><Search /></el-icon>
        </template>
      </el-input>
    </div>
  </template>
</UiSelect>

Vue3中使用Element Plus以及Table组件实现上述功能是一个常见的前端任务。首先,你需要安装相关的依赖,如`vue-element-plus`: ```bash npm install vue-element-plus@latest @el-plus/components table ``` 接下来,我们将创建一个名为`TableComponent.vue`的组件,结合Element UI Plus的`<el-table>`和相应的功能: ```html <template> <el-table :data="tableData" style="width: 100%" :row-key="getRowKey" :loading="isLoading" @fetch-data="onFetchData" :filter-method="handleFilter" ref="table" > <!-- 添加列配置 --> <el-table-column prop="name" label="姓名" width="180"></el-table-column> <el-table-column prop="age" label="年龄" width="180"></el-table-column> <!-- ...其他列... --> <!-- 分页 --> <el-pagination v-if="total" @current-change="setCurrentPage" :current-page.sync="currentPage" :page-size="pageSize" layout="total, sizes, prev, pager, next, jumper" ></el-pagination> <!-- 行内编辑 --> <template #editRow="{ row }"> <el-input v-model="row.name" placeholder="请输入名字"></el-input> <el-select v-model="row.role" placeholder="请选择角色"> <el-option value="admin" label="管理员"></el-option> <el-option value="user" label="普通用户"></el-option> </el-select> </template> <!-- 多表头 --> <el-table-column type="expand" render="expandRowContent"> </el-table-column> </el-table> </template> <script> export default { name: "TableComponent", components: { ElPagination }, data() { return { tableData: [], isLoading: false, currentPage: 1, pageSize: 10, searchKeyword: "", columnHeadings: [ { field: "name", title: "姓名" }, { field: "role", title: "角色" }, // 更多字段... ], columns: [], // 根据columnHeadings动态生成列 }; }, computed: { total() { // 返回总记录数,例如从服务器获取数据时填充 return this.tableData.length; }, getRowKey(item) { // 如果需要唯一标识每一行,返回对应的属性值 return item.id || item.key; // 假设item有id属性 }, }, methods: { onFetchData(page, size) { // 这里可以去请求后台数据,并将结果赋值给tableData this.isLoading = true; setTimeout(() => { const result = { data: [...this.randomData(size)], total: this.total }; this.tableData = result.data; this.isLoading = false; }, 1000); }, handleFilter(value, column) { // 搜索函数,可以根据column.field和value进行过滤 return column.field === 'name' ? { name: { $like: `%${value}%` } } : {}; }, setCurrentPage(newPage) { this.currentPage = newPage; }, expandRowContent({ $index, row }) { // 展开行内容的实现 return ` <div class="expand-row"> <p>详细信息...</p> <!-- 可添加更多详细信息 --> </div> `; }, randomData(size) { // 示例数据生成 return Array.from({ length: size }, (_, i) => ({ id: i, name: `Name ${i}`, role: ['admin', 'user'][Math.floor(Math.random() * 2)], })); }, }, }; </script> <style scoped> .expand-row { /* 自定义展开行样式 */ } </style> ``` 这个组件已经包含了基本的功能,你可以直接导入并使用。为了使其样式更美观,可以在`<style scoped>`标签内的CSS部分自定义样式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值