<!-- 树状选择器 --> <template> <el-popover ref="popover" placement="bottom-start" trigger="click" class="select-tree scrollbar" @show="onShowPopover" @hide="onHidePopover" > <el-tree ref="tree" :style="`min-width: ${treeWidth}`" :data="data" :props="props" :expand-on-click-node="false" :filter-node-method="filterNode" :default-expand-all="false" class="innerbox" highlight-current accordion @node-click="onClickNode" /> <el-input slot="reference" ref="input" v-model="labelModel" :style="`width: ${width}px`" :size="size" :class="{ 'rotate': showStatus }" :placeholder="placeholder" :disabled="disabled" clearable suffix-icon="el-icon-arrow-down" /> </el-popover> </template> <script> export default { name: 'SelectTree', // 设置绑定参数 model: { prop: 'value', event: 'selected' }, props: { // 接收绑定参数 value: { type: String, default: '' }, // 输入框宽度 width: { type: String, default: '' }, size: { type: String, default: '' }, // 输入框宽度 disabled: { type: Boolean, default: false }, // 选项数据 options: { type: Array, required: true }, // 输入框占位符 placeholder: { type: String, required: false, default: '请选择' }, // 树节点配置选项 props: { type: Object, required: false, default: () => ({ parent: 'parentId', value: 'rowGuid', label: 'areaName', children: 'children' }) } }, data() { return { // 树状菜单显示状态 showStatus: false, // 菜单宽度 treeWidth: 'auto', // 输入框显示值 labelModel: '', // 实际请求传值 valueModel: '' } }, computed: { // 是否为树状结构数据 dataType() { const jsonStr = JSON.stringify(this.options) return jsonStr.indexOf(this.props.children) !== -1 }, // 若非树状结构,则转化为树状结构数据 data() { // return this.dataType ? this.options : this.switchTree() return this.options } }, watch: { labelModel(val) { console.log('labelModel change:' + val) if (!val) { if (val === '') { this.valueModel = '' this.onCloseTree() this.$emit('selected', this.valueModel) } this.valueModel = '' } this.$refs.tree.filter(val) }, value(val) { this.labelModel = this.queryTree(this.data, val) } }, created() { // 检测输入框原有值并显示对应 label if (this.value) { this.labelModel = this.queryTree(this.data, this.value) } // 获取输入框宽度同步至树状菜单宽度 this.$nextTick(() => { this.treeWidth = `${(this.width || this.$refs.input.$refs.input.clientWidth) - 24}px` }) }, methods: { // 单击节点 onClickNode(node) { this.labelModel = node[this.props.label] this.valueModel = node[this.props.value] this.onCloseTree() }, // 偏平数组转化为树状层级结构 switchTree() { return this.cleanChildren(this.buildTree(this.options, '0')) }, // 隐藏树状菜单 onCloseTree() { this.$refs.popover.showPopper = false }, // 显示时触发 onShowPopover() { this.showStatus = true this.$refs.tree.filter(false) }, // 隐藏时触发 onHidePopover() { this.showStatus = false console.log('onHidePopover:' + this.valueModel) // if (this.valueModel !== '') { // this.$emit('selected', this.valueModel) // } this.$emit('selected', this.valueModel) }, // 树节点过滤方法 filterNode(query, data) { if (!query) return true return data[this.props.label].indexOf(query) !== -1 }, // 搜索树状数据中的 ID queryTree(tree, id) { let stark = [] stark = stark.concat(tree) while (stark.length) { const temp = stark.shift() if (temp[this.props.children]) { stark = stark.concat(temp[this.props.children]) } if (temp[this.props.value] === id) { return temp[this.props.label] } } return '' }, // 将一维的扁平数组转换为多层级对象 buildTree(data, id = '0') { const fa = (parentId) => { console.log('parentId:' + parentId) const temp = [] for (let i = 0; i < data.length; i++) { const n = data[i] if (n[this.props.parent] === parentId) { n.children = fa(n.code) temp.push(n) } } return temp } return fa(id) }, // 清除空 children项 cleanChildren(data) { const fa = (list) => { list.map((e) => { if (e.children.length) { fa(e.children) } else { delete e.children } return e }) return list } return fa(data) } } } </script> <style lang="scss" scoped> .scrollbar { overflow-y: scroll; &::-webkit-scrollbar { z-index: 11; width: 6px; } &::-webkit-scrollbar-track, &::-webkit-scrollbar-corner { background: #fff; } &::-webkit-scrollbar-thumb { // border-radius: 5px; width: 6px; background-color: #eee; &:hover { background-color: #ccc; } } &::-webkit-scrollbar-track-piece { background: #fff; width: 6px; } } .select-tree .el-tree { max-height: 350px; } .select-tree .el-input.el-input--suffix { cursor: pointer; } .select-tree .el-input.el-input--suffix.rotate .el-input__suffix { transform: rotate(180deg); } .innerbox::-webkit-scrollbar {/*滚动条整体样式*/ width: 4px; /*高宽分别对应横竖滚动条的尺寸*/ height: 4px; } .innerbox::-webkit-scrollbar-thumb {/*滚动条里面小方块*/ border-radius: 5px; -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2); background: rgba(0,0,0,0.2); } .innerbox::-webkit-scrollbar-track { /*滚动条里面轨道*/ -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); border-radius: 0; background: rgba(0, 0, 0, 0.1); } </style> <style lang="scss"> .el-popover{ max-height: 400px; overflow: auto; } </style>