element-ui封装选择树状下拉组件

 需求:实现如下效果

el-popover和el-tree组合后,选中元素填入到el-input中,样式修改和el-select一样,但是会少了一个滚动定位元素的效果,所以还需要通过scrollIntoView实现一下

实现代码:

<template>
  <ElPopover
    v-model="popoverVisible"
    placement="bottom-start"
    trigger="click"
    :close-delay="0"
    popper-class="tree-popover"
    :visible-arrow="true"
  >
    <ElTree
      id="tree-option"
      ref="selectTree"
      class="tree-options"
      :accordion="accordion"
      :data="options"
      :props="props"
      :node-key="props.value"
      :default-expanded-keys="defaultExpandedKey"
      :expand-on-click-node="false"
      :style="{width: treeWidth}"
      @node-click="handleNodeClick"
    >
      <span slot-scope="{ node }" :title="node.label" class="text-truncate el-tree-node__label">{{ node.label }}</span>
    </ElTree>
    <ElInput
      slot="reference"
      v-model="inputVal"
      :placeholder="inputPlaceholder"
      :disabled="disabled"
      class="tree-select-input"
      :title="inputVal"
      readonly
    >
      <i
        slot="suffix"
        class="el-icon-arrow-down tree-select-input-suffix"
        :class="{
          'active':popoverVisible
        }"
      />
    </ElInput>
  </ElPopover>
</template>
<script>
import i18n from '@/i18n'
import { nextTick, onMounted, onUnmounted, ref, watch } from '@vue/composition-api'
import { defineComponent } from '@vue/runtime-core'
export default defineComponent({
  components: {
  },
  props: {
    inputPlaceholder: {
      type: String,
      default: i18n.t('Common_pay_select_title')
    },
    /* 选中的初始值 */
    value: {
      type: String,
      default: ''
    },
    /* 配置项 */
    props: {
      type: Object,
      default: () => {
        return {
          value: 'id', // ID字段名
          label: 'name', // 显示名称
          children: 'children' // 子级字段名
        }
      }
    },
    /* 选项列表数据(树形结构的对象数组) */
    options: {
      type: Array,
      default: () => []
    },
    /* 自动收起 */
    accordion: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    },
    /* 树结构的宽度 */
    treeWidth: {
      type: String,
      default: '100%'
    }
  },
  setup (props, { emit, refs }) {
    const inputVal = ref('')

    const defaultExpandedKey = ref([])

    const curNode = ref({
      id: '',
      name: ''
    })
    const curNodeDom = ref()
    const curNodeData = ref()

    const popoverVisible = ref(false)
    const selectTree = ref()

    const handleNodeClick = (node, data, dom) => {
      if (curNodeData.value) {
        curNodeData.value.isCurrent = false
      }
      curNodeData.value = data
      curNode.value = node
      curNodeDom.value = dom.$el
      inputVal.value = node.name
      emit('change', curNode.value)
      popoverVisible.value = false
    }

    const selectDomScroll = (dom) => {
      nextTick(() => {
        dom.scrollIntoView({ block: 'center' })
      })
    }

    const initData = () => {
      if (props.value) {
        const node = selectTree.value.getNode(props.value)
        if (node) {
          // 需要被选中
          node.isCurrent = true
          curNodeData.value = node
          curNode.value = node.data
          inputVal.value = curNode.value.name
          nextTick(() => {
            const el = document.querySelector('.is-current')
            curNodeDom.value = el
          })
        } else {
          inputVal.value = props.value
        }
      }
    }

    const handleInputBlur = () => {
      popoverVisible.value = false
    }

    watch(popoverVisible, (nv) => {
      if (nv && curNodeDom.value) {
        selectDomScroll(curNodeDom.value)
      }
    })

    onMounted(() => {
      initData()
    })

    onUnmounted(() => {
      curNodeDom.value = null
    })

    return {
      inputVal,
      defaultExpandedKey,
      selectTree,
      curNode,
      popoverVisible,
      handleNodeClick,
      handleInputBlur
    }
  }
})
</script>
  <style lang="scss">
  @import "STYLES/mixins";

  .tree-popover {
    padding: 5px;

      .tree-options {
        max-height: 260px !important;
        overflow: hidden;
        overflow-y: auto;

        &::-webkit-scrollbar {
          width: 7px;
          cursor:pointer;
        }

        &::-webkit-scrollbar-track {
          background: transparent;
        }

        &::-webkit-scrollbar-thumb {
          background: rgba(210, 213, 217);
          display: none;
          border-radius:4px;
          transition: display .2s linear;

          &:hover {
            background: rgba(194, 195, 198);
          }
        }

        &:hover {
          &::-webkit-scrollbar-thumb {
            display: block;
          }
          scrollbar-width:auto;
        }

        scrollbar-width: none;
      }

      .el-tree-node__label {
        font-weight: normal;
        font-size: 13px;
      }

      .el-tree  .is-current .el-tree-node__label {
        color: #409EFF;
        font-weight: 700;
      }

      .el-tree  .is-current .el-tree-node__children .el-tree-node__label {
        color: #606266;
        font-weight: 400;
      }

      .selected {
        color: #409EFF;
        font-weight: 700;
      }
  }

  .tree-select-input {

    .el-input__inner {
      cursor: pointer;
      @include text-truncate();
    }

    &-suffix {
      font-size: 14px;
      transition: all 0.3s;
    }
    .el-input__suffix {
      right: 10px;
    }

    .active {
      transform: rotate(180deg);
    }
  }
  </style>

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值