<template>
<el-popover
ref="popover"
style="display: block; height: 32px; line-height: 32px; padding: 0px"
trigger="click"
:disabled="disabled"
:width="treeWidth"
@show="onShowPopover"
@hide="onHidePopover"
:append-to-body="AppendToBody"
>
<el-scrollbar :id="id" :style="{ height: treeHeight }">
<el-tree
ref="tree"
:indent="0"
highlight-current
:style="`width: ${treeWidth}`"
:data="data"
:props="props"
:expand-on-click-node="false"
:filter-node-method="filterNode"
:default-expand-all="expandAll"
:default-expanded-keys="[value || '']"
:node-key="props.value"
:show-checkbox="multiple"
:default-checked-keys="defaultKeys"
@node-click="onClickNode"
@check="onCheckNode"
>
<span class="custom-tree-node" slot-scope="{ data }">
<slot :data="data" v-if="!!isSlot"></slot>
<span v-else>
<i
v-if="showIcon"
:class="
data[props.icon]
? data[props.icon]
: data[props.children] && data[props.children].length
? 'iconfont iconsucai'
: 'iconfont iconshujiedian'
"
></i>
{{ data[props.label] }}
</span>
</span>
</el-tree>
</el-scrollbar>
<el-input
slot="reference"
ref="input"
v-model="labelModel"
:clearable="clearable"
:disabled="disabled"
:style="`width: ${width}px`"
:class="{ rotate: showStatus }"
suffix-icon="el-icon-arrow-down"
:placeholder="placeholder"
:readonly="readonly"
@input="handleLabelChange"
></el-input>
</el-popover>
</template>
<script>
export default {
props: {
// 接收绑定参数
disabled: {
type: Boolean,
default: false, //禁用
},
expandAll: {
type: Boolean,
default: false, //展开所有
},
value: {
type: String,
default: "", //值
},
// 输入框宽度
width: String,
popoverWidth: String,
treeHeight: {
type: String,
default: "300px", //值
},
// 选项数据
options: {
type: Array,
required: true,
},
banField: {
//字段判断 哪些节点禁止被单击
type: Object,
default: () => ({
name: "",
value: "",
}),
},
banParent: {
//只容许选择叶子节点
type: Boolean,
default: false,
},
banRoot: {
//根节点不允许选择 parentId=0
type: Boolean,
default: false,
},
banData: {
//节点不允许选择 data
type: Boolean,
default: false,
},
// 输入框占位符
placeholder: {
type: String,
required: false,
default: "请选择",
},
//可以为空
clearable: {
type: Boolean,
default: true,
},
//只读
readonly: {
type: Boolean,
default: false,
},
// 树节点配置选项
props: {
type: Object,
required: false,
default: () => ({
parent: "parentId",
value: "id",
label: "text",
children: "children",
icon: "icon",
}),
},
multiple: {
//多选
type: Boolean,
default: false,
},
AppendToBody: {
//弹窗添加到body
type: Boolean,
default: false,
},
showIcon: {
//显示icon
type: Boolean,
default: true,
},
isSlot: {
//是否插槽
type: Boolean,
default: false,
},
// 默认选中的节点
defaultKeys: {
type: Array,
default: () => [],
},
},
// 设置绑定参数
model: {
prop: "value",
event: "selected",
},
computed: {
// 是否为树状结构数据
dataType() {
// const jsonStr = JSON.stringify(this.options);
// return jsonStr.indexOf(this.props.children) !== -1;
let isFlag = false;
for (let i = 0; i < this.options.length; i++) {
isFlag = !!this.options[i][this.props.children];
}
return isFlag;
},
// 若非树状结构,则转化为树状结构数据
data() {
return this.dataType ? this.options : this.switchTree();
},
},
watch: {
data(val, oldValue) {
if (JSON.stringify(val) != JSON.stringify(oldValue)) {
this.labelModel = this.queryTree(val, this.value) || this.value;
}
},
value(val) {
this.labelModel = this.queryTree(this.data, val) || this.value;
},
},
data() {
return {
// 树状菜单显示状态
showStatus: false,
// 菜单宽度
treeWidth: "auto",
// 输入框显示值
labelModel: "",
// 实际请求传值
valueModel: "0",
//单击的node
nodeModel: null,
id: "select-tree-" + new Date().getTime(),
checkNodes: [],
};
},
created() {
// 检测输入框原有值并显示对应 label
if (this.value) {
this.labelModel = this.queryTree(this.data, this.value) || this.value;
}
// 获取输入框宽度同步至树状菜单宽度
this.$nextTick(() => {
// this.treeWidth = `100px`;
this.treeWidth = `${
this.popoverWidth ||
this.width ||
(this.$refs.input
? this.$refs.input.$refs.input.clientWidth - 8
: this.width)
}px`;
});
},
methods: {
onCheckNode(node) {
const nodes = this.$refs.tree.getCheckedNodes();
this.$emit("checked", nodes);
},
// 单击节点
onClickNode(node) {
let banField = this.banField;
if (banField.name) {
let data = node.data ? JSON.parse(node.data) : node;
if (data[banField.name] == banField.value) {
return;
}
}
if (this.banParent) {
let children = node[this.props.children] || [];
if (children.length > 0) {
return;
}
}
if (this.banRoot) {
if (node[this.props.parent] == "0") {
return;
}
}
if (this.banData) {
if (!node.data) {
return;
}
}
this.labelModel = node[this.props.label];
this.valueModel = node[this.props.value];
this.nodeModel = node;
this.onCloseTree();
},
// 偏平数组转化为树状层级结构
switchTree() {
return this.cleanChildren(this.buildTree(this.options, "0"));
},
// 隐藏树状菜单
onCloseTree() {
this.$refs.popover.showPopper = false;
},
// 显示时触发
onShowPopover() {
this.showStatus = true;
this.$nextTick(() => {
this.$refs.tree.filter(false);
if (this.value) {
this.$refs.tree.setCurrentKey(this.value);
}
setTimeout(() => {
let scrollbar_wrap = $("#" + this.id).find(".el-scrollbar__wrap");
scrollbar_wrap.css("overflow", "auto");
}, 100);
});
},
// 隐藏时触发
onHidePopover() {
this.showStatus = false;
if (this.nodeModel && !this.multiple) {
//获取node
let node = this.$refs.tree.getNode(this.valueModel);
this.$emit(
"selected",
this.valueModel,
this.nodeModel || {},
node || {}
);
this.$emit("checked", node.data);
}
},
// 树节点过滤方法
filterNode(query, data) {
if (!query) return true;
return (
data[this.props.label] && 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] || this.value;
}
}
return "";
},
// 将一维的扁平数组转换为多层级对象
buildTree(data, id = "0") {
const fa = (parentId) => {
const temp = [];
for (let i = 0; i < data.length; i++) {
const n = data[i];
if (n[this.props.parent] === parentId) {
n[this.props.children] = fa(n.rowGuid);
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);
},
handleLabelChange(val) {
if (!val) {
this.valueModel = "";
this.nodeModel = null;
let node = this.$refs.tree.getNode(this.valueModel);
this.$emit("selected", this.valueModel, this.nodeModel || {}, node);
//停止后续事件冒泡 弹出
window.event.stopPropagation();
}
this.$refs.tree.filter(val);
},
},
};
</script>
<style lang="scss" scoped>
.el-input.el-input--suffix {
cursor: pointer;
overflow: hidden;
}
.el-input.el-input--suffix.rotate .el-input__suffix {
transform: rotate(180deg);
}
.custom-tree-node {
font-weight: 500;
}
</style>
element 封装 select-tree
最新推荐文章于 2024-05-17 20:06:44 发布