重新构建一遍okr-tree
This commit is contained in:
		
							
								
								
									
										386
									
								
								components/AiOkrTree/OkrTreeNode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										386
									
								
								components/AiOkrTree/OkrTreeNode.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,386 @@
 | 
			
		||||
<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>
 | 
			
		||||
		Reference in New Issue
	
	Block a user