Files
dvcp_v2_webapp/ui/dv/AiOkrTree/OkrTreeNode.vue
2024-04-12 10:22:47 +08:00

387 lines
10 KiB
Vue

<template>
<div
class="org-chart-node"
@contextmenu="$event => this.handleContextMenu($event)"
v-if="node.visible"
:class="{
collapsed: !node.leftExpanded || !node.expanded,
'is-leaf': isLeaf,
'is-current': node.isCurrent,
'is-left-child-node': isLeftChildNode,
'is-not-child': node.level === 1 && node.childNodes.length <= 0 && leftChildNodes.length <= 0,
'only-both-tree-node': node.level === 1 && tree.store.onlyBothTree
}"
>
<transition :duration="animateDuration" :name="animateName">
<div
class="org-chart-node-left-children"
v-if="showLeftChildNode"
v-show="node.leftExpanded"
>
<OkrTreeNode
v-for="child in leftChildNodes"
:show-collapsable="showCollapsable"
:node="child"
:label-width="labelWidth"
:label-height="labelHeight"
:renderContent="renderContent"
:nodeBtnContent="nodeBtnContent"
:selected-key="selectedKey"
:node-key="nodeKey"
:key="getNodeKey(child)"
:props="props"
:show-node-num="showNodeNum"
is-left-child-node
></OkrTreeNode>
</div>
</transition>
<div class="org-chart-node-label"
:class="{
'is-root-label': node.level === 1,
'is-not-right-child': node.level === 1 && node.childNodes.length <= 0,
'is-not-left-child': node.level === 1 && leftChildNodes.length <= 0
}"
>
<div
v-if="showNodeLeftBtn && leftChildNodes.length > 0"
class="org-chart-node-left-btn"
:class="{ expanded: node.leftExpanded }"
@click="handleBtnClick('left')">
<template v-if="showNodeNum" >
<span v-if="!node.leftExpanded" class="org-chart-node-btn-text">
{{ (node.level === 1 && leftChildNodes.length > 0) ? leftChildNodes.length : node.childNodes.length }}
</span>
</template>
<node-btn-content v-else :node="node">
<slot>
</slot>
</node-btn-content>
</div>
<div
class="org-chart-node-label-inner"
@click="handleNodeClick"
:class="computeLabelClass"
:style="computeLabelStyle"
>
<node-content :node="node">
<slot>
{{ node.label }}
</slot>
</node-content>
</div>
<div
v-if="showNodeBtn && !isLeftChildNode"
class="org-chart-node-btn"
:class="{ expanded: node.expanded }"
@click="handleBtnClick('right')">
<template v-if="showNodeNum">
<span v-if="!node.expanded " class="org-chart-node-btn-text">
{{ node.childNodes.length }}
</span>
</template>
<node-btn-content v-else :node="node">
<slot>
<!-- <div class="org-chart-node-btn-text">10</div> -->
</slot>
</node-btn-content>
</div>
</div>
<transition :duration="animateDuration" :name="animateName">
<div
class="org-chart-node-children"
v-if="!isLeftChildNode && node.childNodes && node.childNodes.length > 0"
v-show="node.expanded"
>
<OkrTreeNode
v-for="child in node.childNodes"
:show-collapsable="showCollapsable"
:node="child"
:label-width="labelWidth"
:label-height="labelHeight"
:renderContent="renderContent"
:nodeBtnContent="nodeBtnContent"
:selected-key="selectedKey"
:node-key="nodeKey"
:key="getNodeKey(child)"
:show-node-num='showNodeNum'
:props="props"
></OkrTreeNode>
</div>
</transition>
</div>
</template>
<script>
import { getNodeKey } from "./model/util";
export default {
name: "OkrTreeNode",
inject: ["okrEventBus"],
props: {
props: {},
node: {
default() {
return {};
}
},
root: {
default() {
return {};
}
},
// 子节点是否可折叠
showCollapsable: {
type: Boolean,
default: false
},
// 判断是否是左子树的节点,样式需要改
isLeftChildNode: {
type: Boolean,
default: false
},
// 树节点的内容区的渲染 Function
renderContent: Function,
// 展开节点的内容渲染 Function
nodeBtnContent: Function,
// 显示节点数
showNodeNum: Boolean,
// 树节点区域的宽度
labelWidth: [String, Number],
// 树节点区域的高度
labelHeight: [String, Number],
// 用来控制选择节点的字段名
selectedKey: String,
// 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
nodeKey: String
},
components: {
NodeContent: {
props: {
node: {
required: true
}
},
render(h) {
const parent = this.$parent;
if (parent._props.renderContent) {
return parent._props.renderContent(h, this.node);
} else {
return this.$scopedSlots.default(this.node);
}
}
},
// 渲染展开节点的样式
NodeBtnContent: {
props: {
node: {
required: true
}
},
render(h) {
const parent = this.$parent;
if (parent._props.nodeBtnContent) {
return parent._props.nodeBtnContent(h, this.node);
} else {
if (this.$scopedSlots.default) {
return this.$scopedSlots.default(this.node);
}
}
}
}
},
computed: {
isLeaf () {
if (this.node.level === 1) {
if (this.leftChildNodes.length == 0 && this.node.childNodes.length == 0) {
return true
} else {
return false
}
} else {
return this.node.isLeaf
}
},
leftChildNodes() {
if (this.tree.store.onlyBothTree) {
if (this.isLeftChildNode) {
return this.node.childNodes;
} else {
return this.node.leftChildNodes;
}
}
return [];
},
animateName() {
if (this.tree.store.animate) {
return this.tree.store.animateName;
}
return "";
},
animateDuration() {
if (this.tree.store.animate) {
return this.tree.store.animateDuration;
}
return 0;
},
// 是否显示展开按钮
showNodeBtn() {
if (this.isLeftChildNode) {
return (
(this.tree.store.direction === "horizontal" &&
this.showCollapsable &&
this.leftChildNodes.length > 0) || this.level === 1
);
}
return (
this.showCollapsable &&
this.node.childNodes &&
this.node.childNodes.length > 0
)
},
showNodeLeftBtn() {
return (
(this.tree.store.direction === "horizontal" &&
this.showCollapsable &&
this.leftChildNodes.length > 0)
)
},
// 节点的宽度
computeLabelStyle() {
let { labelWidth = "auto", labelHeight = "auto" } = this;
if (typeof labelWidth === "number") {
labelWidth = `${labelWidth}px`;
}
if (typeof labelHeight === "number") {
labelHeight = `${labelHeight}px`;
}
return {
width: labelWidth,
height: labelHeight
};
},
computeLabelClass() {
let clsArr = [];
const store = this.tree.store;
if (store.labelClassName) {
if (typeof store.labelClassName === "function") {
clsArr.push(store.labelClassName(this.node));
} else {
clsArr.push(store.labelClassName);
}
}
if (store.currentLableClassName && this.node.isCurrent) {
if (typeof store.currentLableClassName === "function") {
clsArr.push(store.currentLableClassName(this.node));
} else {
clsArr.push(store.currentLableClassName);
}
}
if (this.node.isCurrent) {
clsArr.push("is-current");
}
return clsArr;
},
computNodeStyle() {
return {
display: this.node.expanded ? "" : "none"
};
},
computLeftNodeStyle() {
return {
display: this.node.leftExpanded ? "" : "none"
};
},
// 是否显示左子数
showLeftChildNode() {
return (
this.tree.onlyBothTree &&
this.tree.store.direction === "horizontal" &&
this.leftChildNodes &&
this.leftChildNodes.length > 0
);
}
},
watch: {
"node.expanded"(val) {
// this.$nextTick(() => this.expanded = val);
},
"node.leftExpanded"(val) {
// this.$nextTick(() => this.expanded = val);
}
},
data() {
return {
// node 的展开状态
expanded: false,
tree: null
};
},
created() {
const parent = this.$parent;
if (parent.isTree) {
this.tree = parent;
} else {
this.tree = parent.tree;
}
const tree = this.tree;
if (!tree) {
console.warn("Can not find node's tree.");
}
},
methods: {
getNodeKey(node) {
return getNodeKey(this.nodeKey, node.data);
},
handleNodeClick() {
const store = this.tree.store;
store.setCurrentNode(this.node);
this.tree.$emit("node-click", this.node.data, this.node, this);
},
handleBtnClick(isLeft) {
isLeft = isLeft === "left";
const store = this.tree.store;
// 表示是OKR飞书模式
if (store.onlyBothTree) {
if (isLeft) {
if (this.node.leftExpanded) {
this.node.leftExpanded = false;
this.tree.$emit("node-collapse", this.node.data, this.node, this);
} else {
this.node.leftExpanded = true;
this.tree.$emit("node-expand", this.node.data, this.node, this);
}
return;
}
}
if (this.node.expanded) {
this.node.collapse();
this.tree.$emit("node-collapse", this.node.data, this.node, this);
} else {
if (this.node.parent.childNodes && this.node.parent.childNodes.length) {
this.node.parent.childNodes.forEach(e => {
e.collapse()
})
}
this.node.expand();
this.tree.$emit("node-expand", this.node.data, this.node, this);
}
},
handleContextMenu(event) {
if (
this.tree._events["node-contextmenu"] &&
this.tree._events["node-contextmenu"].length > 0
) {
event.stopPropagation();
event.preventDefault();
}
this.tree.$emit(
"node-contextmenu",
event,
this.node.data,
this.node,
this
);
}
}
};
</script>