需求:实现如下效果
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>