mySelect

97 阅读1分钟
<template>
  <div class="selected" :style="{width:(width?width+'px':'100%')}">
    <div class="select-container" @click="openCtrl=!openCtrl" @mouseenter="mouseenter" @mouseleave="closeIconCtrl=false;">
      <p v-show="(selectArr.length==0&&type==='multiple')||(!selectedObj[keyName]&&type==='single')" class="select-placeholder">{{ placeholder }}</p>
      <Icon v-show="!closeIconCtrl" type="ios-arrow-down" :class="arrowDownClass" size="16" />
      <Icon v-if="closeIconCtrl" type="ios-close-circle" class="clear-selected" @click.stop="clearSelected" />
      <!-- 多选样式 -->
      <template v-if="type=='multiple'">
        <div v-for="(item,index) in selectArr" :key="index" class="select-tag">
          <span class="tag-text">{{ item[keyName] }}</span><Icon type="ios-close" class="ios-close" size="16" @click.stop="deleteItem(item[keyId])" />
        </div>
      </template>
      <!-- 单选样式 -->
      <p v-if="type=='single'" class="selected-text textover">{{ selectedObj[keyName] }}</p>
    </div>
    <!-- 搜索框 -->
    <Input v-show="openCtrl" v-model="keyword" type="text" class="select-search" :placeholder="placeholderInput" @on-keyup="searchKeyword" />
    <!-- scroll列表 -->
    <div :class="selectListClass" :style="{bottom:data.length==0&&openCtrl?'-84px':-(height+32)+'px'}">
      <Scroll v-if="data.length!=0" v-show="openCtrl" :on-reach-bottom="handleReachBottom"
              :height="height" :distance-to-edge="10"
      >
        <div v-for="(item, index) in data"
             :key="index" :class="[(tempIdArr.indexOf(item[keyId])>-1||selectedObj[keyId]==item[keyId])?'list-item-active':'',
                                   (tempDisabledIdArr&&tempDisabledIdArr.indexOf(item[keyId])>-1||tempDisabledIdArr[0]==item[keyId])?'list-item-disabled':'',
                                   'list-item']"
             @click="(tempDisabledIdArr&&tempDisabledIdArr.indexOf(item[keyId])>-1||tempDisabledIdArr[0]==item[keyId])?'':chooseItem(item)"
        >
          <div>
            <p style="line-height:18px;">{{ item[keyName] }}<br />
              <slot name="moreText" :item="item"></slot>
            </p>
          </div>
          <Icon type="md-checkmark" class="checked-icon" />
        </div>
      </Scroll>
      <Row v-if="data.length==0&&openCtrl" class="no-data">暂无数据</Row>
    </div>
  </div>
</template>
<script>
export default {
  components: {},
  props: {
    path: {
      type: String,
      required: true
    },
    keyName: {
      type: String, // 需要渲染的字段
      required: true
    },
    keyId: {
      type: String, // 需要渲染的字段
      default: 'id'
    },
    keyCode: {
      type: String
    },
    placeholder: {
      type: String,
      default: '请选择成员组'
    },
    placeholderInput: {
      type: String,
      default: '请输入关键字搜索'
    },
    type: {
      type: String,
      default: 'single' // multiple  single 类型
    },
    height: {
      type: [Number, String],
      default: 200 // 高度
    },
    width: {
      type: Number
    },
    params: { // 除了keyword是否还有另外的参数
      type: Object
    },
    pagingMode: { // 是否分页
      type: Boolean,
      default: true
    },
    defaultObj: {
      type: [Object, Array] // 赋默认值
    },
    inputKey: { // 搜索框的key值
      type: String,
      default: 'keyword'
    },
    method: {
      type: String,
      default: 'GET'
    },
    disabledList: { // 禁用的id
      type: Array
    }
  },
  data () {
    return {
      openCtrl: false,
      selectArr: [], // 多选数组
      tempIdArr: [],
      tempDisabledIdArr: [],
      selectedObj: {}, // 单选对象
      keyword: '', // 关键字
      closeIconCtrl: false,
      inputScroll: {
        pageNum: 1,
        pageSize: this.config.pageSize,
        total: 0
      }, // 分页参数
      data: [], // 初始数据
      timer: 0
    }
  },
  computed: {
    arrowDownClass() {
      return [
        'arrow-down',
        {
          'arrow-down-visible': this.openCtrl
        }
      ]
    },
    selectListClass() {
      return [
        'select-list',
        {
          'select-list-open': this.openCtrl
        }
      ]
    }
  },
  watch: {
    path(newVal, oldVal) {
      this.getMemberGroupList('', true)
    },
    params: {
      deep: true,
      handler(newVal, oldVal) {
        if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
          this.getMemberGroupList('', true)
        }
      }
    }
  },
  created() {},
  beforeMount() {
    this.getMemberGroupList('', true)
  },
  mounted() {
    document.addEventListener('click', e => {
      if (!this.$el.contains(e.target)) {
        this.openCtrl = false// 点击其他区域关闭
      }
    })
  },
  updated() {},
  methods: {
    // 请求成员组列表数据
    getMemberGroupList(keyword, flag) {
      if (flag) {
        this.inputScroll.pageNum = 1
      }
      const url = this.path
      const ajaxData = {
        current: this.inputScroll.pageNum,
        size: this.inputScroll.pageSize,
        [this.inputKey]: keyword || ''
      }
      if (this.params) {
        for (const key in this.params) {
          ajaxData[key] = this.params[key]
        }
      }
      if ((this.inputScroll.pageNum - 1) * this.inputScroll.pageSize > this.inputScroll.total) {
        return
      }
      if (this.method === 'GET') {
        this.$get(url, ajaxData).then(res => {
          if (res.code === 0) {
            if (flag) {
              if (res.data.records) {
                this.data = res.data.records
              } else {
                this.data = res.data
              }
            } else {
              if (res.data.records) {
                this.data = this.data.concat(res.data.records)
              } else {
                this.data = res.data
              }
            }
            // this.data = flag?res.data.records:this.data.concat(res.data.records);
            this.inputScroll.pageNum = res.data.current + 1
            this.inputScroll.total = res.data.total
          }
        })
      } else {
        this.$post(url, ajaxData).then(res => {
          if (res.code === 0) {
            if (flag) {
              if (res.data.records) {
                this.data = res.data.records
              } else {
                this.data = res.data
              }
            } else {
              if (res.data.records) {
                this.data = this.data.concat(res.data.records)
              } else {
                this.data = res.data
              }
            }
            // this.data = flag?res.data.records:this.data.concat(res.data.records);
            this.inputScroll.pageNum = res.data.current + 1
            this.inputScroll.total = res.data.total
          }
        })
      }
    },
    // 下拉加载数据
    handleReachBottom () {
      return new Promise(resolve => {
        this.getMemberGroupList(this.keyword, false)
        resolve()
      })
    },
    // 单个选中
    chooseItem(item) {
      const keyId = this.keyId
      if (this.type === 'multiple') {
        const index = this.tempIdArr.indexOf(item[keyId])
        if (index > -1) {
          this.selectArr.splice(index, 1)
          this.tempIdArr.splice(index, 1)
        } else {
          this.selectArr.push(item)
          this.tempIdArr.push(item[keyId])
        }
        this.$emit('getValue', this.selectArr)
      } else {
        this.selectedObj = item
        this.openCtrl = false
        this.$emit('getValue', this.selectedObj)
      }
      if (this.keyword) {
        this.keyword = ''
        // 因为关键字搜索之后需要重新加载第一页数据  不能删
        this.getMemberGroupList(this.keyword, true)
      }
    },
    // 删除单个
    deleteItem(id) {
      const index = this.tempIdArr.indexOf(id)
      this.selectArr.splice(index, 1)
      this.tempIdArr.splice(index, 1)
      this.$emit('getValue', this.selectArr)
    },
    // 清除所有
    clearSelected() {
      if (this.type === 'multiple') {
        this.tempIdArr.splice(0)
        this.selectArr.splice(0)
        this.$emit('getValue', this.selectArr)
      } else {
        this.selectedObj = {}
        this.$emit('getValue', this.selectedObj)
      }
    },
    // 搜索关键词 重新定位到第一页
    searchKeyword() {
      this.timer = new Date().getTime()
      setTimeout(() => {
        if (new Date().getTime() - this.timer >= 1000) {
          this.getMemberGroupList(this.keyword, true)
        }
      }, 1000)
    },
    mouseenter() {
      if (this.selectArr.length > 0 || this.selectedObj.name || this.selectedObj.team) {
        this.closeIconCtrl = true
      } else {
        this.closeIconCtrl = false
      }
    },
    voluation() {
      if (this.type === 'single' && this.defaultObj) {
        this.selectedObj[this.keyName] = this.defaultObj.name
        this.selectedObj[this.keyId] = this.defaultObj.id
      } else if (this.type !== 'single' && this.defaultObj) {
        const key = this.keyName
        this.tempIdArr = []
        this.selectArr = this.defaultObj.map(item => {
          this.tempIdArr.push(item[this.keyId])
          if (this.keyCode) {
            return { [key]: item.name, id: item[this.keyId], [this.keyCode]: item[this.keyCode] }
          } else {
            return { [key]: item.name, id: item[this.keyId] }
          }
        })
      }
      if (this.disabledList && this.disabledList.length > 0) {
        this.tempDisabledIdArr = this.disabledList.map(item => {
          return item
        })
      }
    }
  }
}
</script>
<style scoped lang="sass">
 @import './mySelect.scss'
</style>

@import '@/assets/globalStyle.scss';
.selected{
    width:300px;
    background: #fff;
    position: relative;
    .select-container{
        position: relative;
        overflow: auto;
        max-height: 60px;
        line-height: 1;
        padding: 0 30px 0 4px;
        border:1px solid $borderColor;
        border-radius: 2px;
        cursor: pointer;
        &:hover{
            border-color:$globalMainColor;
        }
        .select-tag{
            position: relative;
            height: 22px;
            line-height: 22px;
            // max-width: 99%;
            display: inline-block;
            margin: 4px 4px 2px 0;
            padding: 0 8px;
            border: 1px solid $borderColor;
            border-radius: 3px;
            background: #f7f7f7;
            font-size: 12px;
            vertical-align: middle;
            opacity: 1;
            overflow: hidden;
            .tag-text{
                display: block;
                margin-right: 14px;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
            }
            .ios-close{
                position: absolute;
                display: block;
                right: 4px;
                top: 3px;
            }
        }
        .selected-text{
            line-height: 30px;
            padding-left:3px;
        }
        .arrow-down{
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            right: 8px;
            line-height: 1;
            transition: all .2s ease-in-out;      
        }
        .arrow-down-visible{
            transform: translateY(-50%) rotate(180deg);
        }
        .clear-selected{
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            right: 8px;
            line-height: 1;
            color:#808695;
        }
        .select-placeholder{
            line-height: 30px;
            padding-left:3px;
            color:#9E9E9E;
        }
    }
    .select-search{
        position: absolute;
        bottom: -32px;
        z-index: 2;
        width:100%;
        height: 30px;
        box-sizing: border-box;
        // padding-left:5px;
        // border:1px solid $globalMainColor;
    }
    .select-list{
        position: absolute;
        // bottom: -235px;
        width:100%;
        background: #fff;
        will-change: top, left;
        transform-origin: center top;
        box-shadow: 0 4px 6px rgba(0,0,0,.2);
        transform: all 0.2s ease-in-out;
        z-index: 999;
        .list-item{
            padding:7px 16px;
            cursor: pointer;
            position: relative;
            &:hover{
                background: #f3f3f3;
                color:$globalMainColor;
            }
            &.list-item-active{
                background: #f3f3f3;
                color:$globalMainColor;
            }
            &.list-item-disabled{
                cursor:not-allowed;
                color: $disabledColor;
            }
            &.list-item-active .checked-icon{
                display: block;
            }
            .checked-icon{
                position: absolute;
                top:50%;
                transform: translateY(-50%);
                right:20px;
                line-height: 1;
                color:$globalMainColor;
                display: none;
            }
        }
    }
    .no-data{
        height: 52px;
    }
}
 <Col span="18">
                  <mySelect ref="projectSelect"
                            :path="'/report/projects'"
                            :placeholder="'请选择项目'"
                            :placeholder-input="'请输入项目'"
                            :key-name="'name'"
                            @getValue="getProjectId"
                  />
                </Col>
                ````