vue基于el-select组件封装RemoteSelect,支持分页加载,支持编辑时的回显

一、组件功能

  1. 支持分页加载下拉选项。分页加载用的是自定义指令v-loadmore
  1. 当输入和删除关键词时,按当前最新的关键词进行查询,并且页码回到第一页,滚动条置顶。这里要注意el-selectremote-method没有处理clear的场景,所以我们需要自己添加@clear="remoteMethod('')"
  1. 编辑时支持回显。需要借助sync修饰符
  1. 校验:原本使用change触发,但是在输入字符或者是复制粘贴进来的内容,期望的场景是清除校验

问题场景:

实际我们期望的应该是这样:

思路:使用blur事件,但是在改变关键词时手动触发校验

二、组件封装

RemoteSelect.vue

js 复制代码
<template>
  <el-select
    v-model.trim="keyword"
    v-loadmore:el-select-dropdown__list="loadMore"
    class="remote-select"
    :popper-append-to-body="false"
    filterable
    remote
    reserve-keyword
    clearable
    placeholder="请输入关键字进行查询"
    :remote-method="remoteMethod"
    :loading="loading"
    @change="handleChange"
    @clear="remoteMethod('')"
  >
    <el-option
      v-for="item in categoryDs"
      :key="item.id"
      :label="item.sageCategoryName"
      :value="item.sageCategoryId"
    />
  </el-select>
</template>
<script>
  /*
    主体逻辑:
      1、第一次点击输入框,加载全部下拉,滚动到底部加载下一页
      2、输入框输入关键字时,按关键字查询,滚动加载下一页
      3、关键字进行补充和减少时,按当前最新的关键字进行查询,并且页码回到第一页,滚动加载下一页
      4、点击输入框右侧的清空按钮时,需要手动触发remoteMethod,此时的name是''
      5、编辑时的回显,需要父组件将name传过来,使用name作为关键字进行查询
  */
  import { productCategoryDsApi } from '@/api/product/catesManager'
  export default {
    props: {
      isEdit: { type: Boolean, default: false }, // 是否是编辑,编辑时需要回显
      value: { type: String, default: '' },
      sageCategoryName: { type: String, default: '' }, // 编辑时,必传此参数
    },
    data() {
      return {
        keyword: '', // 关键字模糊查询
        categoryDs: [], // 下拉选项
        loading: false,
        pageLe: 20,
        pageNo: 1,
        total: 0,
        flag: true,
      }
    },
    watch: {
      // 由于是分页加载,当编辑时,初次渲染需要将name传递过来,带上关键字查询列表。注意:只有编辑的初次渲染才需要设置categoryName
      value: {
        handler(val) {
          if (this.flag && this.isEdit) {
            this.keyword = this.sageCategoryName
            this.getOptions()
            this.flag = false
          }
        },
      },
    },
    created() {
      if (!this.isEdit) {
        this.getOptions()
      }
    },
    methods: {
      // 选中某一项
      handleChange(val) {
        const item = this.categoryDs.find((n) => n.sageCategoryId === val) || {}
        this.$emit('input', item.sageCategoryId)
        this.$emit('update:sageCategoryName', item.sageCategoryName)
        this.$emit('change', item)
      },
      // 加载更多
      loadMore() {
        if (this.total > this.categoryDs.length) {
          this.pageNo++
          this.getOptions()
        }
      },
      // 当输入框内容发生变化时会触发remoteMethod,但是清空需要手动触发
      remoteMethod(name) {
        this.loading = true
        this.keyword = name && name.trim()
        this.reset() // 当用户输入不同的字符时,意味着需要重新加载下拉,需要将当前数据重置
        this.getOptions()
        if (!this.keyword) {
          this.loading = false
        }
      },
      // 重置当前页码,总数,下拉,并且将滚动条置顶
      reset() {
        this.pageNo = 1
        this.total = 0
        this.categoryDs = []
        const targetDOM = document.querySelector(
          '.remote-select .el-select-dropdown__list'
        )
        targetDOM.scrollTop = 0
        this.$emit('clearValidate') // 清除校验
      },
      async getOptions() {
        try {
          const { keyword, pageLe, pageNo } = this
          const params = { categoryName: keyword, pageLe, pageNo }
          const { code, data } = await productCategoryDsApi(params)
          this.loading = false
          if (code === '000000' && Array.isArray(data.data)) {
            const list = data.data.map((n) => ({
              ...n,
              sageCategoryId: n.sageCategoryId + '',
              sageCategoryName: n.categoryName,
            }))
            if (pageNo === 1) {
              this.categoryDs = list
            } else {
              this.categoryDs.push(...list)
            }
            this.total = data.total
          }
        } catch (err) {
          console.log(err)
          this.loading = false
        }
      },
    },
  }
</script>
<style lang="scss" scoped>
  .remote-select {
    width: 100%;
    /deep/ .el-select-dropdown__list {
      height: 300px;
      overflow-y: auto;
      overflow-x: hidden;
      li {
        max-width: 470px;
      }
    }
  }
</style>

三、使用

js 复制代码
            <el-form-item label="SAGE 平台类目" prop="sageCategoryId">
              <RemoteSelect
                :isEdit="type === 'edit'"
                v-model="form.sageCategoryId"
                :sageCategoryName.sync="form.sageCategoryName"
                @clearValidate="clearValidate"
              />
            </el-form-item>
  1. isEdit表示当前是否是编辑
  2. sageCategoryName.sync用于回显时,带参查询列表
  3. v-model用于绑定所选中的id值
  4. clearValidate事件用于在输入或删除关键词时手动清除平台类目的校验
js 复制代码
        rules: {
          sageCategoryId: [
            { required: true, message: '请选择平台类目', trigger: 'blur' }, // 这里不要用change事件
          ],
        }
js 复制代码
      clearValidate() {
        this.$refs.form.clearValidate('sageCategoryId')
      }

因为使用了blur,这里我需要使用watch,实时地添加校验或去掉校验

js 复制代码
    watch: {
      'form.sageCategoryId': {
        handler(val) {
          if (val) {
            this.$refs.form.clearValidate('sageCategoryId')
          } else {
            this.$refs.form.validateField('sageCategoryId')
          }
        },
      },
    },

因为这个校验,在父组件中多写了一个clearValidate方法和watch监听,用起来很麻烦,能不能把这个逻辑封装到组件中,大神看到了解答一下,多谢!

相关推荐
吃杠碰小鸡7 分钟前
commitlint校验git提交信息
前端
虾球xz38 分钟前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇43 分钟前
HTML常用表格与标签
前端·html
疯狂的沙粒1 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员1 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐1 小时前
前端图像处理(一)
前端
程序猿阿伟1 小时前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
疯狂的沙粒1 小时前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪1 小时前
AJAX的基本使用
前端·javascript·ajax
力透键背1 小时前
display: none和visibility: hidden的区别
开发语言·前端·javascript