项目使用antd,下拉框太难用了,封装一个,其他ui框架改模板就行就用了一个ant的input
<template>
<div class="tree_select_content">
<a-input v-model="showValue" placeholder="请选择" @input="handleInput" />
<div class="pop" :style="{display:(showPop?'block':'none')}">
<ul id="treeSelect" class="ztree"></ul>
</div>
</div>
</template>
本来想格式化任何树的数据来着,发现后端给的格式不太支持,format方法可以直接弃用(keyoption是配合格式化数据的),nodes格式按注释来或者不嵌套children的使用pid那种单数组的应该也行,搜索那块的逻辑得改
展示、搜索、选中这三个变量感觉没必要,,没咋优化也,有可能有用呢,,
<script>
export default {
props: {
/** ztree 原始数据 固定格式 */
nodes: {
type: Array,
default() {
return [
// {
// id: 1, pId: 0, name: '111', code: '父code1', nocheck: true, children:
// [
// { id: '1-1', pId: 1, name: '222', code: '子code1' }
// ]
// },
// {
// id: 2, pId: 0, name: '333', code: '父code2', nocheck: true, children:
// [
// { id: '2-1', pId: 2, name: '444', code: '子code2' },
// { id: '2-2', pId: 2, name: '6', code: '子code3' }
// ]
// },
// {
// id: 3, pId: 0, name: '666', code: '父code3', nocheck: true, children:
// [
// { id: '3-1', pId: 3, name: '777', code: '子code4' },
// { id: '3-2', pId: 3, name: '888', code: '子code5' },
// { id: '3-3', pId: 3, name: '889', code: '子code5' },
// { id: '3-4', pId: 3, name: '880', code: '子code5' },
// { id: '3-5', pId: 3, name: '881', code: '子code5' },
// { id: '3-6', pId: 3, name: '882', code: '子code5' },
// { id: '3-7', pId: 3, name: '883', code: '子code5' },
// { id: '3-8', pId: 3, name: '884', code: '子code5' },
// { id: '3-9', pId: 3, name: '885', code: '子code5' },
// { id: '3-20', pId: 3, name: '886', code: '子code5' }
// ]
// },
// {
// id: '3-122', pId: 0, name: '777', code: 'code4'
// }
]
}
},
keyOption: {
type: Object,
default() {
return {
// fatherKey: 'id', // 父 唯一标识的 key
// fatherCode: 'id', // 父 code
// fatherLabel: 'name', // 父 lable
// childKey: 'member', // 子所在obj的 key
// childrenKey: 'personCode', // 子 唯一标识的 key
// childrenCode: 'personCode', // 子 code
// childrenLabel: 'personName' // 子 lable
}
}
}
},
data() {
return {
/** 展示pop */
showPop: false,
/** 展示 */
showValue: undefined,
/** 选中 */
checkedValue: undefined,
/** 搜索 */
searchValue: undefined,
/** ztree设置 */
setting: {
data: {
simpleData: {
enable: true,
idKey: 'id',
pIdKey: 'pId',
rootPId: 0,
},
},
view: {
showLine: false,
showIcon: false,
dblClickExpand: false,
nameIsHTML: true,
selectedMulti: false,
showTitle: false,
},
check: {
enable: true,
chkStyle: 'radio',
radioType: 'all'
},
callback: {
onClick: (event, treeId, treeNode) => this.click(event, treeId, treeNode),
onCheck: (event, treeId, treeNode) => this.check(event, treeId, treeNode)
},
},
formatNodes: []
}
},
computed: {
myNodes() {
if (this.searchValue) {
return this.filterNodes(this.formatNodes)
} else {
return this.formatNodes
}
},
},
mounted() {
this.$nextTick(() => {
this.formatNodes = this.format(this.nodes, this.keyOption)
this.init()
document.addEventListener('click', this.handleListenerAreaClick)
})
},
beforeDestroy() {
document.removeEventListener('click', this.handleListenerAreaClick)
},
methods: {
/** 格式化原始数据 仅适用于两层结构 */
format(arr, options) {
const n = []
for (let i = 0; i < arr.length; i++) {
// 第一层
const obj = {
id: arr[i][options.fatherKey],
pId: '0',
name: arr[i][options.fatherLabel],
code: arr[i][options.fatherCode],
nocheck: true,
children: []
}
// 第二层
const childArr = arr[i][options.childKey]
if (childArr && childArr.length > 0) {
for (let j = 0; j < childArr.length; j++) {
const childObj = {
id: childArr[j][options.childrenKey],
pId: arr[i][options.fatherKey],
name: childArr[j][options.childrenLabel],
code: childArr[j][options.childrenCode]
}
obj.children.push(childObj)
}
}
n.push(obj)
}
return n
},
/** 初始化 */
init() {
$.fn.zTree.destroy('#treeSelect') // 销毁
$.fn.zTree.init($('#treeSelect'), this.setting, this.myNodes)
var zTree = $.fn.zTree.getZTreeObj('treeSelect')
zTree.expandAll(true)
},
/** 监听区域内外点击 */
handleListenerAreaClick(e) {
if (!this.$el || !this.$el.compareDocumentPosition) {
return false
}
const cp = this.$el.compareDocumentPosition(e.target)
if ([16, 20].indexOf(cp) === -1 && this.$el != e.target) {
this.showPop = false
if (this.showValue === '') {
this.checkedValue = undefined
}
this.showValue = this.checkedValue
if (this.searchValue) {
this.searchValue = undefined
this.init()
}
} else {
this.showPop = true
this.showValue = this.checkedValue
}
},
/** ztree节点点击事件 */
click(e, treeId, treeNode) {
var zTree = $.fn.zTree.getZTreeObj('treeSelect')
if (treeNode.children && treeNode.children.length > 0) { // 选中父节点展开
zTree.expandNode(treeNode)
} else { // 选中子节点进行操作
// zTree.checkNode(treeNode, false, true) // 取消勾选
zTree.checkNode(treeNode, true, true) // 勾选
this.showValue = treeNode.name
this.checkedValue = treeNode.name
this.searchValue = undefined
setTimeout(() => {
this.showPop = false
}, 100)
this.$emit('treeClick', treeNode)
}
},
/** ztree单选点击事件 */
check(e, treeId, treeNode) {
this.showValue = treeNode.name
this.checkedValue = treeNode.name
this.searchValue = undefined
setTimeout(() => {
this.showPop = false
}, 100)
this.$emit('treeClick', treeNode)
},
/** 输入 */
handleInput() {
this.searchValue = this.showValue
this.init()
},
/**
* 获取搜索的node
* node: array
*/
filterNodes(nodes) {
const filterTree = []
for (let i = 0; i < nodes.length; i++) {
// 本层
const has = nodes[i].name.includes(this.searchValue)
if (has) {
filterTree.push(nodes[i])
continue
}
// 子层
const childNodes = nodes[i].children
if (childNodes && childNodes.length > 0) {
filterTree.push(...this.filterNodes(childNodes))
}
}
return filterTree
}
}
}
</script>
<style lang="less" scoped>
.tree_select_content {
width: 100%;
position: relative;
display: inline-block;
.ztree /deep/li span {
font-family: 'Microsoft YaHei', '宋体', Arial, sans-serif;
font-size: 12px;
}
.ztree /deep/li span.button.switch {
display: none;
}
// .ztree /deep/li span.button.chk.radio_false_full {
// position: absolute;
// right: 0;
// }
// .ztree /deep/li span.button.chk.radio_false_full_focus {
// position: absolute;
// right: 0;
// }
// .ztree /deep/li span.button.chk.radio_true_full {
// position: absolute;
// right: 0;
// }
// .ztree /deep/li span.button.chk.radio_true_full_focus {
// position: absolute;
// right: 0;
// }
.ztree /deep/li a {
width: 100%;
}
.ztree /deep/li .node_name {
display: inline-block;
width: 100%;
}
.pop {
width: 100%;
max-height: 200px;
overflow-y: scroll;
color: rgba(0, 0, 0, 0.65);
-webkit-font-feature-settings: 'tnum';
font-feature-settings: 'tnum';
position: absolute;
z-index: 1050;
-webkit-box-sizing: border-box;
box-sizing: border-box;
background-color: #f9f9f9;
padding: 12px 16px 12px 6px;
margin-top: 5px;
border-radius: 4px;
-webkit-box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
}
}
</style>
菜鸡一枚,望大佬们有时间的话能指导指导