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监听,用起来很麻烦,能不能把这个逻辑封装到组件中,大神看到了解答一下,多谢!

相关推荐
小小小小宇6 小时前
虚拟列表兼容老DOM操作
前端
悦悦子a啊6 小时前
Python之--基本知识
开发语言·前端·python
安全系统学习7 小时前
系统安全之大模型案例分析
前端·安全·web安全·网络安全·xss
涛哥码咖7 小时前
chrome安装AXURE插件后无效
前端·chrome·axure
OEC小胖胖8 小时前
告别 undefined is not a function:TypeScript 前端开发优势与实践指南
前端·javascript·typescript·web
行云&流水8 小时前
Vue3 Lifecycle Hooks
前端·javascript·vue.js
Sally璐璐8 小时前
零基础学HTML和CSS:网页设计入门
前端·css
老虎06278 小时前
JavaWeb(苍穹外卖)--学习笔记04(前端:HTML,CSS,JavaScript)
前端·javascript·css·笔记·学习·html
灿灿121388 小时前
CSS 文字浮雕效果:巧用 text-shadow 实现 3D 立体文字
前端·css
烛阴9 小时前
Babel 完全上手指南:从零开始解锁现代 JavaScript 开发的超能力!
前端·javascript