<template> <el-select ref="select" :value="scope.value" class="fd-tree-select" popper-class="fd-tree-select-poper" :clearable="clearable" :disabled="disabled" :filterable="filterable" :placeholder="placeholder" :multiple="multiple" :collapse-tags="collapseTags" :valueKey="valueKey || getValueKey" :filter-method="selectFilterNode" @clear="clear"> <el-tree ref="tree" :node-key="scope.props.value" class="fd-tree" :props="scope.props" :data="scope.data" :show-checkbox="multiple" :default-expand-all="defaultExpandAll" :accordion="accordion" :indent="indent" :filter-node-method="treeFilterNode" :default-checked-keys="defaultCheckedKeys" :default-expanded-keys="defaultExpandKeys" :check-on-click-node="true" :expand-on-click-node="false" :lazy="lazy" :load="loadNode" @check="clickCheck" @node-click="clickNode"> <el-option ref="option" slot-scope="{ node, data }" :label="data[scope.props.label]" :value="data[scope.props.value]" :title="data[scope.props.label]" class="fd-tree-option"> </el-option> </el-tree> </el-select></template><script>export default { name: 'DsTreeSelect', props: { // tree数据 data: Array, // v-model绑定数据 value: [String, Array], // 是否可以清空 clearable: Boolean, // 为空的时候文字提示 placeholder: String, // 是否禁用 disabled: Boolean, // 是否支持筛选 filterable: Boolean, // 作为 value 唯一标识的键名,绑定值为对象类型时必填 valueKey: String, // 多选时是否将选中值按文字的形式展示 collapseTags: Boolean, // 是否多选 multiple: Boolean, // 是否展开树所有的节点 defaultExpandAll: Boolean, // 是否每次只打开一个同级树节点展开 accordion: Boolean, // 相邻级节点间的水平缩进,单位为像素 indent: Number, // 是否懒加载 lazy: Boolean, // 自定义配置项 props: Object }, data() { return { scope: { // tree数据 data: this.data, // v-mode绑定数据 value: this.value, // 自定义配置项 props: this.props } }; }, // 计算属性 computed: { // 默认选中的节点的 key 的数组 defaultCheckedKeys() { // 获取内部v-model绑定数据 const {value, props} = this.scope; // 最终返回的list const _dataList = []; // 判断v-model绑定值是否是数组类型 数组类型代表多选 if (Array.isArray(value)) { // 数组类型循环 for (const item of value) { // 生成新的list 字符串类型直接返回 object类型 返回props配置的value值 _dataList.push((typeof item === 'string' || typeof item === 'number') ? item : item[props.value]); } } else { // 字符串类型代表单选 _dataList.push(value); } return _dataList; }, // 默认展开的节点的 key 的数组 defaultExpandKeys() { // 判断是否是懒加载 let _dataList = []; if (this.lazy) { for (const item of this.defaultCheckedKeys) { const arr = item.split('-'); for (let i = 0; i < arr.length - 1; i++) { _dataList.push(arr.slice(0, i + 1).join('-')); } } } else { _dataList = JSON.parse(JSON.stringify(this.defaultCheckedKeys)); } return _dataList; }, // 当选中的是对象的时候需要设置value-key getValueKey() { // 获取value const {value} = this.scope; return (typeof value === 'string' || typeof value === 'number') ? value : ''; } }, // 监听值变化 watch: { // 监听组件外部输入值变化 更新到组件内部 value: { deep: true, handler(newVal) { this.scope.value = JSON.parse(JSON.stringify(newVal)); } }, // 监听自定义配置项 更新到当前组件内部 props: { immediate: true, deep: true, handler(newVal) { this.scope.props = Object.assign({}, { value: 'label', // ID字段名 label: 'label', // 显示名称 children: 'children', // 子级字段名 isLeaf: 'leaf' // 叶子节点字段名 }, newVal); } } }, // 页面的方法 methods: { // 树组件搜索方法 treeFilterNode(value, data) { // 获取配置项 const {props} = this.scope; // 没输入的时候返回全部的节点 if (!value) return true; // 对label进行过滤 return data[props.label].indexOf(value) !== -1; }, // 下拉自定义过滤方法 selectFilterNode(value) { // 调用tree的filter方法 this.$refs.tree.filter(value); }, // 多选框点击事件 clickCheck(data, node) { if (this.multiple) { // 获取全选的所有父节点list const parent = this.getSimpleCheckedNodes(this.$refs.tree.store); this.$emit('input', node.checkedKeys.filter(item => { return !parent.length || !parent.includes(item); })); } }, // 树节点点击的事件 clickNode(data, node, component) { const {props} = this.scope; // 单选的时候点击节点就选中节点 此处注意 不能选中存在子节点的节点 if (!this.multiple && (!data[props.children] || !data[props.children].length)) { this.$emit('input', JSON.parse(JSON.stringify(data[props.value]))); // 此处需要隐藏弹窗 this.$refs.select.visible = false; } }, // 懒加载获取数据方法 loadNode(node, resolve) { this.$emit('load', node, resolve); }, // 子节点选中 只返回父节点 getSimpleCheckedNodes(store) { const {props} = this.scope; const checkedNodes = []; const traverse = function(node) { const childNodes = node.root ? node.root.childNodes : node.childNodes; for (const child of childNodes) { if (child.checked && child.childNodes.length) { checkedNodes.push(child.data[props.value]); } if (child.indeterminate) { traverse(child); } } }; traverse(store); return checkedNodes; }, // 清空 clear() { if (Array.isArray(this.scope.value)) { return this.$emit('input', []); } this.$emit('input', ''); } }};</script><style lang="less"> /* tree-select 弹层样式 */ .fd-tree-select-poper{ width: 0px; &.is-multiple .fd-tree-option{ /* hover selected样式初始化 */ &.hover, &.selected{ color: inherit !important; background: inherit !important; font-weight: 400 !important; } } } /* tree结构样式 */ .fd-tree{ .fd-tree-option{ position: relative; flex: 1; pointer-events:none; padding: 0 5px; height: 26px !important; line-height: 26px !important; /* hover selected样式初始化 */ &.hover, &.selected{ background: inherit !important; } /* 去掉option勾号 */ &:after{ content: '' !important; } } }</style>