vue開發個性化select,帶分頁以及搜索功能的組件

前提:必須先安裝了element-ui

自定義開發帶分頁以及搜索框的select

 

 

用法

select.vue文件

<template>
  <div v-clickoutside="()=>visible=false" class="com-select">
    <div ref="reference" class="com-select-input" @click="clickSelectInput">
      <div class="one"></div>
      <!-- tags展示 -->
      <div class="two">
        <template v-if="isCheckAll">
          <el-tag
            size="small"
            closable
            hit
            type="info"
            disable-transitions
            @close="cancelCheckAll()">
            <span class="el-select__tags-text">全選</span>
          </el-tag>
        </template>
        <template v-else>
          <el-tag
            v-for="item in selectedArr"
            :key="item.value"
            size="small"
            closable
            hit
            type="info"
            disable-transitions
            @close="deleteTag(item)">
            <span class="el-select__tags-text">{{ item.label }}</span>
          </el-tag>
        </template>
      </div>
      <div
        class="three"
        @mouseenter.stop="inputHovering = true"
        @mouseleave.stop="inputHovering = false"
      >
        <i v-if="showClose" class="el-select__caret el-input__icon el-icon-circle-close" @click.stop="cancelCheckAll"></i>
        <i v-else :class="`el-icon-arrow-${visible ? 'up' : 'down'} el-select__caret el-input__icon`"></i>
      </div>
    </div>
    <transition name="el-zoom-in-top">
      <!-- 下拉框 -->
      <the-select-menu
        v-show="visible"
        ref="popper"
        append-to-body>
        <!-- 搜索框 -->
        <el-input v-model="queryVal" placeholder="請輸入" size="small" clearable @input="handleSearch" @keyup.enter.native="handleSearch">
          <el-button slot="append" icon="el-icon-search" @click="handleSearch"></el-button>
        </el-input>
        <!-- select選項 -->
        <div class="com-select-body">
          <div
            v-for="(item, i) in list"
            v-show="isShowFilterArr ? item._isShow : item.isShow"
            :key="item.value"
            :title="item.label"
            :style="formatColumn()"
            :class="['com-option', {'selected': item.selected,}]"
            @click="selectOptionClick(item, i)"
          >
            {{ item.label }}
          </div>
          <div v-if="list.length === 0" class="com-select-nodata">暫無數據</div>
          <template v-if="nextPageStaus">
            <i :style="formatColumn()" v-for="v in 3" :key="'AA' + v"></i>
          </template>
        </div>
        <!-- 分頁 -->
        <div class="com-footer-page">
          <el-button size="small" :disabled="prePageStaus" @click="prePage">上一頁</el-button>
          <el-button type="primary" size="small" @click="checkAll">全選</el-button>
          <el-button size="small" :disabled="nextPageStaus" @click="nextPage">下一頁</el-button>
        </div>
      </the-select-menu>
    </transition>
  </div>
</template>

<script>
import TheSelectMenu from './select-dropdown.vue';
import Clickoutside from 'element-ui/src/utils/clickoutside';
export default {
  name: 'SelectPro',
  components: { TheSelectMenu },
  directives: { Clickoutside },
  props: {
    value: {
      type: Array,
      default: () => [],
      required: true
    },
    options: {
      type: Array,
      default: () => []
    },
    column: {
      type: Number,
      default: 3
    }
  },
  data() {
    return {
      optionArr: this.initOptions(),
      inputHovering: false,
      selectedArr: [],
      filterArr: [],
      queryVal: '',
      visible: false,
      pageSize: this.column * 4,
      currentPage: 1,
      filterCurrentPage: 1
    };
  },
  computed: {
    list() {
      return this.isShowFilterArr ? this.filterArr : this.optionArr;
    },
    isCheckAll() {
      return this.selectedArr.length === this.optionArr.length;
    },
    isShowFilterArr() {
      return this.filterArr.length > 0 || this.queryVal !== '';
    },
    showClose() {
      return this.selectedArr.length > 0 && this.inputHovering;
    },
    prePageStaus() {
      if (this.isShowFilterArr ? this.filterCurrentPage === 1 : this.currentPage === 1) {
        return true;
      }
      return false;
    },
    nextPageStaus() {
      const len =  Math.ceil(this.list.length / this.pageSize);
      if (len === 0) return true;
      if (this.isShowFilterArr ? this.filterCurrentPage === len : this.currentPage === len) {
        return true;
      }
      return false;
    }
  },
  watch: {
    selectedArr(val) {
      const arr = val.map(item => {
        return item.value;
      });
      this.$emit('input', arr);
    },
    // value(val, oldVal) {
    //   console.log('v-model===>',val, oldVal);
    // }
  },
  mounted() {
    // 默認選中
    this.optionArr.forEach(item => {
      if (this.value.includes(item.value)) {
        this.selectedArr.push(item);
      }
    });
  },
  methods: {
    initOptions() {
      return this.options.map((item, i) => {
        return {
          isShow: i < this.column * 4,
          _isShow: false,
          selected: this.value.includes(item.value),
          ...item
        };
      });
    },
    deleteTag(item) { // 刪除當前tag
      this.selectedArr.some((v, i) => {
        if (v.value === item.value) {
          item.selected = false;
          this.selectedArr.splice(i, 1);
          return true;
        }
      });
    },
    handleSearch() { // 搜索過濾
      if (this.queryVal === '') { // 輸入框清空時
        this.filterArr = [];
        return;
      }
      console.log(this.queryVal);
      // 搜索前清除之前搜索的
      this.filterArr = [];
      // 重置搜索數組分頁
      this.filterCurrentPage = 1;
      let num = 0;
      this.optionArr.forEach(item => {
        if (item.label.indexOf(this.queryVal) !== -1) { // 如果like 輸入值
          item._isShow = num < this.pageSize; // 只展示一頁的結果
          this.filterArr.push(item);
          num++;
        }
      });
    },
    clickSelectInput() { // 點擊輸入框
      this.visible = !this.visible;
    },
    selectOptionClick(item, index) { // 單擊check
      item.selected = !item.selected;
      // 如果已經選過
      if (this.selectedArr.some(v => v.value === item.value)) {
        this.deleteTag(item);
      } else {
        this.selectedArr.push(item);
      }
    },
    checkAll() { // 全選
      this.selectedArr = [];
      this.optionArr.forEach(item => {
        item.selected = true;
        this.selectedArr.push(item);
      });
    },
    cancelCheckAll() { // 取消全選
      this.optionArr.forEach(item => {
        item.selected = false;
      });
      this.selectedArr = [];
    },
    prePage() { // 上一頁
      if (this.isShowFilterArr) { // 搜索展示時
        this.changeFilterPageShow(false);
        this.filterCurrentPage--;
        this.changeFilterPageShow(true);
      } else {
        this.changePageShow(false);
        this.currentPage--;
        this.changePageShow(true);
      }
    },
    nextPage() { // 下一頁
      if (this.isShowFilterArr) { // 搜索展示時
        this.changeFilterPageShow(false);
        this.filterCurrentPage++;
        this.changeFilterPageShow(true);
      } else {
        this.changePageShow(false);
        this.currentPage++
        this.changePageShow(true);
      }
    },
    changePageShow(isOrNo) { // 改變當前頁狀態
      const num = (this.currentPage - 1) * this.pageSize;
      const num2 = num + this.pageSize;
      for (let index = num; index < num2; index++) {
        if (this.optionArr[index]) this.optionArr[index].isShow = isOrNo;
      }
    },
    changeFilterPageShow(isOrNo) { // 改變搜索當前頁狀態
      const num = (this.filterCurrentPage - 1) * this.pageSize;
      const num2 = num + this.pageSize;
      for (let index = num; index < num2; index++) {
        if (this.filterArr[index]) this.filterArr[index]._isShow = isOrNo;
      }
    },
    formatColumn() {
      // 展示多少列
      if (this.column === 1) {
        return { width: '100%' };
      } else if (this.column === 2) {
        return { width: '49%' };
      } else if (this.column === 3) {
        return { width: '32%' };
      } else {
        return { width: '24%' };
      }
    }
  }
};
</script>
<style lang="less" scoped>
.com-select {
  box-sizing: border-box;
  border-radius: 4px;
  border: 1px solid #DCDFE6;
  cursor: pointer;
  background: #ffffff;
  .com-select-input {
    display: flex;
    align-items: center;
    .one {
      width: 10px;
    }
    .two {
      flex: 1;
      line-height: 29px;
      padding: 0 0 1px 0;
      /deep/ .el-tag {
        background-color: #f4f4f5;
        border-color: #e9e9eb;
        color: #909399;
        margin-right: 6px;
      }
      /deep/ .el-tag__close.el-icon-close {
        background-color: #C0C4CC;
      }
    }
    .three {
      width: 30px;
    }
  }
}
.el-select-dropdown {
  height: 280px;
  padding: 10px;
  border: 1px solid #E4E7ED;
  border-radius: 4px;
  box-shadow: 1px 2px 6px 0px #7d7d7d;
  box-sizing: border-box;
  cursor: default;
  .com-select-body {
    height: 178px;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-content: flex-start;
    .com-option {
      // width: 32%;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      color: #606266;
      background-color: rgb(240, 248, 250);
      border: 1px solid #d7e8fa;
      height: 32px;
      line-height: 32px;
      box-sizing: border-box;
      position: relative;
      text-align: center;
      margin-bottom: 10px;
      cursor: pointer;
    }
    // 選中樣式
    .selected {
      border-color: #409EFF;
      &::after{
        content: '';
        width: 15px;
        height: 14px;
        background: url("./check.png") no-repeat 0 0;
        position: absolute;
        right: 0;
        bottom: 0;
      }
    }
    .com-select-nodata {
      width: 100%;
      text-align: center;
    }
  }
  // 分頁樣式
  .com-footer-page {
    display: flex;
    justify-content: space-between;
  }
}
</style>

select-down.vue文件

<template>
  <div
    class="el-select-dropdown el-popper"
    :style="{ width: width }">
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: 'MySelectDropdown',
  data() {
    return {
      width: ''
    };
  },
  mounted() {
    this.width = this.$parent.$el.clientWidth + 'px';
    window.addEventListener('resize', this.resizeWindth);
  },
  methods: {
    resizeWindth() {
      this.width = this.$parent.$el.clientWidth + 'px';
    }
  }
};
</script>

  

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章