Vue实现Element UI框架的自定义输入框或下拉框在输入时对列表选项进行过滤,以及右键列表选项弹出菜单进行删除

        需求就是Element UI的输入框或下拉框在输入时过滤列表,还要支持右键删除列表的某一项,记录一下。

Html代码

<el-autocomplete
  size="mini"
  style="width:250px"
  v-model="product"
  clearable
  placeholder="请输入商品"
  :fetch-suggestions="filterProducts"
  @select="handleSelectProduct"
  @clear="handleRemoveProduct">
  <template slot-scope="{ item }">
    <h1 @contextmenu.prevent="openProductMenu($event)">{{item.name}}</h1>
  </template>
</el-autocomplete>

<!-- <el-select
  v-model="product"
  placeholder="请输入商品"
  filterable
  style="width:250px">
  <el-option
    v-for="item in productList"
    :key="item.name"
    :label="item.name"
    :title="item.name"
    :value="item.name" 
    style="width:250px;position:relative"
    @contextmenu.prevent.native="openProductMenu($event)">
  </el-option>
</el-select> -->

<!-- 商品右键菜单 -->
<ul
  v-show="productMenuVisible"
  :style="{left: productMenuLeft + 0 + 'px', top: productMenuTop - 10 + 'px'}"
  class="productline-menu">
  <li @click="handleDeleteProduct" style="padding: 0;"><el-button icon="el-icon-error" style="height: 30px;border: unset; width: 100px; display: block;padding: 0 10px; margin: 0;">删除</el-button></li>
</ul>

JS代码

data: () => ({
  // 产品
  product: ""

  // 右键菜单是否可见
  productMenuVisible: false,

  // 右键菜单左位移
  productMenuLeft: 0,

  // 右键菜单上位移
  productMenuTop: 0,

  // 右键选中的产品
  productObj: ""

  // 产品列表
  productList: [
    {
      "id": 1,
      "name": "金"
    },
    {
      "id": 2,
      "name": "木"
    },
    {
      "id": 3,
      "name": "水"
    },
    {
      "id": 4,
      "name": "火"
    },
    {
      "id": 5,
      "name": "土"
    }
  ]
}),

watch: {
  /**
    * 监听点击右键菜单区域
    */
  productMenuVisible(value) {
    if (value) {
      document.body.addEventListener('click', this.closeProductMenu);
    } else {
      document.body.removeEventListener('click', this.closeProductMenu);
    }
  }
},

methods: {
  /**
   * 打开商品右键菜单
   */
  openProductMenu(e) {
    const menuMinWidth = 105
    const offsetLeft = this.$el.getBoundingClientRect().left
    const offsetWidth = this.$el.offsetWidth
    const maxLeft = offsetWidth - menuMinWidth
    const left = e.clientX - offsetLeft
    if (left > maxLeft) {
      this.productMenuLeft = maxLeft
    } else {
      this.productMenuLeft = left
    }
    this.productMenuTop = e.clientY
    this.productMenuVisible = true
    this.productObj = e.currentTarget.innerHTML
  },

  /**
   * 关闭商品右键菜单
   */
  closeProductMenu() {
    this.productMenuVisible = false;
  },

  /**
   * 右键点击删除商品
   */
  handleDeleteProduct() {
    this.$confirm("此操作将删除【" + this.productObj + "】商品, 是否继续?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    })
    .then(() => {
      console.log("You are deleting the product of ", "'" + this.productObj + "'");
      // this.deleteProductByName(this.productObj);
    })
    .catch(() => {
      // ...
    });
  },

  /**
   * 根据查询字符串过滤商品
   */
  filterProducts(queryString, cb) {
    console.log("this.productList =>", this.productList)
    var restaurants = this.productList;
    var results = queryString
      ? restaurants.filter(this.createFilter(queryString))
      : restaurants;
    cb(results);
  },

  /**
   * 根据查询字符串过滤商品规则
   */
  createFilter(queryString) {
    return (restaurant) => {
      return (
        (restaurant.name.indexOf(queryString) != -1) || (restaurant.name.indexOf(queryString) != -1)
      );
    };
  },

  /**
   * 左键点击商品选项
   */
  handleSelectProduct(item) {
    this.form.product = item.name;
  },

  /**
   * 清空输入框
   */
  handleRemoveProduct() {
    this.form.product = '';
  }
}

Css代码

<style lang="less">
.productline-menu {
  position: absolute;
  margin: 0;
  background: #fff;
  z-index: 9999;
  list-style-type: none;
  padding: 5px 0;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 400;
  color: #333;
  box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);

  li {
    margin: 0;
    padding: 7px 16px;
    cursor: pointer;

    &:hover {
      background-color: #eee;
    }
  }
}
</style>

效果:

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的vue element selected下拉框虚拟列表组件,可以根据需求进行修改和扩展: ```vue <template> <el-select v-model="selected" placeholder="请选择" @visible-change="handleVisibleChange"> <el-option v-for="(item, index) in options" :key="item.value" :label="item.label" :value="item.value" :disabled="item.disabled" :style="getOptionStyle(index)" ></el-option> </el-select> </template> <script> export default { data() { return { selected: null, options: [], visibleOptions: [], itemHeight: 36, // 每一项的高度 visibleItemCount: 10, // 可见项的数量 bufferItemCount: 3, // 缓冲项的数量,用于提高滚动性能 startIndex: 0, // 可见项的起始索引 endIndex: 0, // 可见项的结束索引 scrollTop: 0, // 滚动的距离 scrollHeight: 0, // 滚动区域的高度 lastScrollTime: 0, // 上一次滚动的间戳 } }, computed: { totalHeight() { return this.itemHeight * this.options.length }, }, watch: { options: { handler() { this.updateVisibleOptions() }, deep: true, }, scrollTop() { this.updateVisibleOptions() }, }, mounted() { this.updateVisibleOptions() window.addEventListener('resize', this.handleWindowResize) }, beforeDestroy() { window.removeEventListener('resize', this.handleWindowResize) }, methods: { // 更新可见选项 updateVisibleOptions() { // 计算可见项的起始索引和结束索引 const scrollTop = Math.max(0, Math.min(this.scrollTop, this.totalHeight - this.clientHeight)) const startIndex = Math.floor(scrollTop / this.itemHeight) const endIndex = Math.min( this.options.length - 1, startIndex + this.visibleItemCount + this.bufferItemCount * 2 - 1 ) const visibleOptions = this.options.slice(startIndex, endIndex + 1) // 更新状态 this.startIndex = startIndex this.endIndex = endIndex this.visibleOptions = visibleOptions }, // 获取选项的样式 getOptionStyle(index) { const style = {} if (index < this.startIndex - this.bufferItemCount || index > this.endIndex + this.bufferItemCount) { style.display = 'none' } else { style.display = 'block' style.height = this.itemHeight + 'px' style.transform = `translateY(${(index - this.startIndex) * this.itemHeight}px)` } return style }, // 获取滚动区域的高度 getScrollHeight() { return Math.min(this.totalHeight, this.visibleItemCount * this.itemHeight) }, // 处理下拉框的可见性变化 handleVisibleChange(visible) { if (visible) { this.scrollHeight = this.getScrollHeight() this.lastScrollTime = Date.now() } }, // 处理窗口大小变化 handleWindowResize() { this.scrollHeight = this.getScrollHeight() }, // 处理滚动事件 handleScroll(event) { const now = Date.now() if (now - this.lastScrollTime > 100) { this.scrollTop = event.target.scrollTop this.lastScrollTime = now } }, }, } </script> <style scoped> /* 设置下拉框的样式 */ .el-select__caret { display: none; } .el-select-dropdown .el-scrollbar__wrap { max-height: none !important; } .el-select-dropdown .el-scrollbar__thumb { background-color: #ccc; border-radius: 4px; } .el-select-dropdown .el-scrollbar__thumb:hover { background-color: #aaa; } </style> ``` 使用方法: ```vue <template> <div> <virtual-select :options="options" v-model="selected" /> </div> </template> <script> import VirtualSelect from './VirtualSelect' export default { components: { VirtualSelect, }, data() { return { selected: null, options: [ { label: '选项一', value: '1' }, { label: '选项二', value: '2' }, { label: '选项三', value: '3' }, // ... ], } }, } </script> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值