feat: add tree line

This commit is contained in:
xiaoxian521
2022-03-07 20:21:15 +08:00
parent 8e7a79cf94
commit 74a6b3ffdc
10 changed files with 408 additions and 146 deletions

View File

@@ -0,0 +1,49 @@
$--element-tree-line-color: #dcdfe6 !default;
$--element-tree-line-style: dashed !default;
$--element-tree-line-width: 1px !default;
/* 添加 el-tree-node__conten 默认没有的 position */
.el-tree .el-tree-node__content {
position: relative;
}
.element-tree-node-label-wrapper {
flex: 1;
display: flex;
align-items: center;
}
.element-tree-node-label {
font-size: 12px;
}
.element-tree-node-line-ver {
display: block;
position: absolute;
top: 0;
left: 0;
height: 100%;
border-left: $--element-tree-line-width $--element-tree-line-style
$--element-tree-line-color;
&.last-node-line {
border-left: $--element-tree-line-width $--element-tree-line-style
transparent;
}
&.last-node-isLeaf-line {
height: 50%;
}
}
.element-tree-node-line-hor {
display: block;
position: absolute;
top: 50%;
left: 0;
height: 0;
border-bottom: $--element-tree-line-width $--element-tree-line-style
$--element-tree-line-color;
}
.element-tree-node-label-line {
flex: 1;
border-top: $--element-tree-line-width $--element-tree-line-style
$--element-tree-line-color;
align-self: center;
margin: 0 10px;
}

View File

@@ -0,0 +1,155 @@
// 参考https://www.npmjs.com/package/element-tree-line (主要是替换需要通过函数传参的方式去注册组件并添加更好的类型支持并移除this.$scopedSlots在3.x中,将所有this.$scopedSlots替换为this.$slots)
import { isFunction } from "/@/utils/is";
import { h, defineComponent } from "vue";
import type { PropType } from "vue";
import "./index.scss";
import type {
TreeNode,
TreeData,
TreeNodeData
} from "element-plus/es/components/tree-v2/src/types";
export default defineComponent({
name: "el-tree-line",
props: {
node: {
type: Object as PropType<TreeNode>,
required: true
},
data: {
type: Array as PropType<TreeNodeData>,
default: () => {}
},
treeData: {
type: Array as PropType<TreeData>,
default: () => []
},
indent: {
type: Number,
default: 16
},
showLabelLine: {
type: Boolean,
default: true
}
},
setup(_, context) {
const { slots } = context;
const getScopedSlot = slotName => {
if (!slotName) {
return null;
}
const slotNameSplits = slotName.split("||");
let slot = null;
for (let index = 0; index < slotNameSplits.length; index++) {
const name = slotNameSplits[index];
slot = (slots || {})[name];
}
return slot;
};
const getSlotValue = (slot, scopedData, defaultNode = null) => {
if (isFunction(slot)) {
return slot(scopedData) || defaultNode;
}
return slot || defaultNode;
};
return {
getScopedSlot,
getSlotValue
};
},
render() {
// 自定义整行节点label区域
const scopeSlotDefault = this.getScopedSlot("default");
// 显示横线时自定义节点label区域
const labelSlot = this.getScopedSlot("node-label");
// 显示横线时追加在横线右边的内容
const afterLabelSlot = this.getScopedSlot("after-node-label");
const labelNodes = scopeSlotDefault
? this.getSlotValue(scopeSlotDefault, {
node: this.node,
data: this.data
})
: [
labelSlot
? this.getSlotValue(labelSlot, {
node: this.node,
data: this.data
})
: h("span", { class: "element-tree-node-label" }, this.node.label),
this.showLabelLine
? h("span", {
class: "element-tree-node-label-line"
})
: null,
this.getSlotValue(afterLabelSlot, {
node: this.node,
data: this.data
})
];
// 取得每一层的当前节点是不是在当前层级列表的最后一个
const lastnodeArr = [];
let currentNode = this.node;
while (currentNode) {
let parentNode = currentNode.parent;
// 兼容element-plus的 el-tree-v2 (Virtualized Tree 虚拟树)
if (currentNode.level === 1 && !currentNode.parent) {
// el-tree-v2的第一层node是没有parent的必需 treeData 创建一个parent
if (!this.treeData || !Array.isArray(this.treeData)) {
throw Error(
"if you using el-tree-v2 (Virtualized Tree) of element-plus,element-tree-line required data."
);
}
parentNode = {
children: Array.isArray(this.treeData)
? this.treeData.map(item => {
return { ...item, key: item.id };
})
: [],
level: 0,
key: "node-0",
parent: null
};
}
if (parentNode) {
// element-plus的 el-tree-v2 使用的是children和key 其他使用的是 childNodes和id
const index = (parentNode.children || parentNode.childNodes).findIndex(
item => (item.key || item.id) === (currentNode.key || currentNode.id)
);
lastnodeArr.unshift(
index === (parentNode.children || parentNode.childNodes).length - 1
);
}
currentNode = parentNode;
}
const lineNodes = [];
for (let i = 0; i < this.node.level; i++) {
lineNodes.push(
h("span", {
class: {
"element-tree-node-line-ver": true,
"last-node-line": lastnodeArr[i] && this.node.level - 1 !== i,
"last-node-isLeaf-line": lastnodeArr[i] && this.node.level - 1 === i
},
style: { left: this.indent * i + "px" }
})
);
}
return h(
"span",
{
class: "element-tree-node-label-wrapper"
},
[labelNodes].concat(lineNodes).concat([
h("span", {
class: "element-tree-node-line-hor",
style: {
width: (this.node.isLeaf ? 24 : 8) + "px",
left: (this.node.level - 1) * this.indent + "px"
}
})
])
);
}
});