重新构建一遍okr-tree
This commit is contained in:
		@@ -8,7 +8,7 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import AiOkrTree from "./AiOkrTree";
 | 
			
		||||
import AiOkrTree from "./AiOkrTree/AiOkrTree";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'AiDvPartyOrg',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,40 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <section class="AiOkrTree">
 | 
			
		||||
    <component v-if="okrTreeLoaded" v-bind="$attrs" :is="ot" :data="data"/>
 | 
			
		||||
  </section>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import Vue from "vue"
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "AiOkrTree",
 | 
			
		||||
  props: {
 | 
			
		||||
    data: {default: () => []}
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    okrTreeLoaded: v => !!v.ot
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      ot: null
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  created() {
 | 
			
		||||
    this.$injectCss("https://cdn.cunwuyun.cn/vot/vue-okr-tree.css")
 | 
			
		||||
    this.$injectLib("https://cdn.cunwuyun.cn/vot/vue-okr-tree.umd.min.js", () => {
 | 
			
		||||
      const {VueOkrTree} = (window?.["vue-okr-tree"] || {})
 | 
			
		||||
      this.ot = Vue.extend({
 | 
			
		||||
        ...VueOkrTree, data() {
 | 
			
		||||
          return {...this.$attrs}
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.AiOkrTree {
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										732
									
								
								components/AiOkrTree/AiOkrTree.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										732
									
								
								components/AiOkrTree/AiOkrTree.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,732 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="org-chart-container">
 | 
			
		||||
    <div
 | 
			
		||||
        ref="orgChartRoot"
 | 
			
		||||
        class="org-chart-node-children"
 | 
			
		||||
        :class="{
 | 
			
		||||
        vertical: direction === 'vertical',
 | 
			
		||||
        horizontal: direction === 'horizontal',
 | 
			
		||||
        'show-collapsable': showCollapsable,
 | 
			
		||||
        'one-branch': data.length === 1
 | 
			
		||||
      }"
 | 
			
		||||
    >
 | 
			
		||||
      <OkrTreeNode
 | 
			
		||||
          v-for="child in root.childNodes"
 | 
			
		||||
          :node="child"
 | 
			
		||||
          :root="root"
 | 
			
		||||
          orkstyle
 | 
			
		||||
          :show-collapsable="showCollapsable"
 | 
			
		||||
          :label-width="labelWidth"
 | 
			
		||||
          :label-height="labelHeight"
 | 
			
		||||
          :renderContent="renderContent"
 | 
			
		||||
          :nodeBtnContent="nodeBtnContent"
 | 
			
		||||
          :selected-key="selectedKey"
 | 
			
		||||
          :default-expand-all="defaultExpandAll"
 | 
			
		||||
          :node-key="nodeKey"
 | 
			
		||||
          :show-node-num="showNodeNum"
 | 
			
		||||
          :key="getNodeKey(child)"
 | 
			
		||||
          :props="props"
 | 
			
		||||
      ></OkrTreeNode>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
<script>
 | 
			
		||||
import Vue from "vue";
 | 
			
		||||
import OkrTreeNode from "./OkrTreeNode.vue";
 | 
			
		||||
import TreeStore from "./model/tree-store.js";
 | 
			
		||||
import {getNodeKey} from "./model/util";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "AiOkrTree",
 | 
			
		||||
  components: {
 | 
			
		||||
    OkrTreeNode
 | 
			
		||||
  },
 | 
			
		||||
  provide() {
 | 
			
		||||
    return {
 | 
			
		||||
      okrEventBus: this.okrEventBus
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  props: {
 | 
			
		||||
    data: {
 | 
			
		||||
      // 源数据
 | 
			
		||||
      required: true
 | 
			
		||||
    },
 | 
			
		||||
    leftData: {
 | 
			
		||||
      // 源数据
 | 
			
		||||
      type: Array
 | 
			
		||||
    },
 | 
			
		||||
    // 方向
 | 
			
		||||
    direction: {
 | 
			
		||||
      type: String,
 | 
			
		||||
      default: "vertical"
 | 
			
		||||
    },
 | 
			
		||||
    // 子节点是否可折叠
 | 
			
		||||
    showCollapsable: {
 | 
			
		||||
      type: Boolean,
 | 
			
		||||
      default: false
 | 
			
		||||
    },
 | 
			
		||||
    // 飞书 OKR 模式
 | 
			
		||||
    onlyBothTree: {
 | 
			
		||||
      type: Boolean,
 | 
			
		||||
      default: false
 | 
			
		||||
    },
 | 
			
		||||
    orkstyle: {
 | 
			
		||||
      type: Boolean,
 | 
			
		||||
      default: false
 | 
			
		||||
    },
 | 
			
		||||
    // 树节点的内容区的渲染 Function
 | 
			
		||||
    renderContent: Function,
 | 
			
		||||
    // 展开节点的内容渲染 Function
 | 
			
		||||
    nodeBtnContent: Function,
 | 
			
		||||
    // 显示节点数
 | 
			
		||||
    showNodeNum: Boolean,
 | 
			
		||||
    // 树节点区域的宽度
 | 
			
		||||
    labelWidth: [String, Number],
 | 
			
		||||
    // 树节点区域的高度
 | 
			
		||||
    labelHeight: [String, Number],
 | 
			
		||||
    // 树节点的样式
 | 
			
		||||
    labelClassName: [Function, String],
 | 
			
		||||
    // 当前选中节点样式
 | 
			
		||||
    currentLableClassName: [Function, String],
 | 
			
		||||
    // 用来控制选择节点的字段名
 | 
			
		||||
    selectedKey: String,
 | 
			
		||||
    // 是否默认展开所有节点
 | 
			
		||||
    defaultExpandAll: {
 | 
			
		||||
      type: Boolean,
 | 
			
		||||
      default: false
 | 
			
		||||
    },
 | 
			
		||||
    // 当前选中的节点
 | 
			
		||||
    currentNodeKey: [String, Number],
 | 
			
		||||
    // 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
 | 
			
		||||
    nodeKey: String,
 | 
			
		||||
    defaultExpandedKeys: {
 | 
			
		||||
      type: Array
 | 
			
		||||
    },
 | 
			
		||||
    filterNodeMethod: Function,
 | 
			
		||||
    props: {
 | 
			
		||||
      default() {
 | 
			
		||||
        return {
 | 
			
		||||
          leftChildren: "leftChildren",
 | 
			
		||||
          children: "children",
 | 
			
		||||
          label: "label",
 | 
			
		||||
          disabled: "disabled"
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    // 动画
 | 
			
		||||
    animate: {
 | 
			
		||||
      type: Boolean,
 | 
			
		||||
      default: false
 | 
			
		||||
    },
 | 
			
		||||
    animateName: {
 | 
			
		||||
      type: String,
 | 
			
		||||
      default: "okr-zoom-in-center"
 | 
			
		||||
    },
 | 
			
		||||
    animateDuration: {
 | 
			
		||||
      type: Number,
 | 
			
		||||
      default: 200
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ondeClass() {
 | 
			
		||||
      return {
 | 
			
		||||
        findNode: null
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      okrEventBus: new Vue(),
 | 
			
		||||
      store: null,
 | 
			
		||||
      root: null
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  created() {
 | 
			
		||||
    this.isTree = true;
 | 
			
		||||
    this.store = new TreeStore({
 | 
			
		||||
      key: this.nodeKey,
 | 
			
		||||
      data: Object.freeze(this.data),
 | 
			
		||||
      leftData: this.leftData,
 | 
			
		||||
      props: this.props,
 | 
			
		||||
      defaultExpandedKeys: this.defaultExpandedKeys,
 | 
			
		||||
      showCollapsable: this.showCollapsable,
 | 
			
		||||
      currentNodeKey: this.currentNodeKey,
 | 
			
		||||
      defaultExpandAll: this.defaultExpandAll,
 | 
			
		||||
      filterNodeMethod: this.filterNodeMethod,
 | 
			
		||||
      labelClassName: this.labelClassName,
 | 
			
		||||
      currentLableClassName: this.currentLableClassName,
 | 
			
		||||
      onlyBothTree: this.onlyBothTree,
 | 
			
		||||
      direction: this.direction,
 | 
			
		||||
      animate: this.animate,
 | 
			
		||||
      animateName: this.animateName
 | 
			
		||||
    });
 | 
			
		||||
    this.root = this.store.root;
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    data(newVal) {
 | 
			
		||||
      this.store.setData(newVal);
 | 
			
		||||
    },
 | 
			
		||||
    defaultExpandedKeys(newVal) {
 | 
			
		||||
      this.store.defaultExpandedKeys = newVal;
 | 
			
		||||
      this.store.setDefaultExpandedKeys(newVal);
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    filter(value) {
 | 
			
		||||
      if (!this.filterNodeMethod)
 | 
			
		||||
        throw new Error("[Tree] filterNodeMethod is required when filter");
 | 
			
		||||
      this.store.filter(value);
 | 
			
		||||
      if (this.onlyBothTree) {
 | 
			
		||||
        this.store.filter(value, "leftChildNodes");
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    getNodeKey(node) {
 | 
			
		||||
      return getNodeKey(this.nodeKey, node.data);
 | 
			
		||||
    },
 | 
			
		||||
    // 通过 node 设置某个节点的当前选中状态
 | 
			
		||||
    setCurrentNode(node) {
 | 
			
		||||
      if (!this.nodeKey)
 | 
			
		||||
        throw new Error("[Tree] nodeKey is required in setCurrentNode");
 | 
			
		||||
      this.store.setUserCurrentNode(node);
 | 
			
		||||
    },
 | 
			
		||||
    // 根据 data 或者 key 拿到 Tree 组件中的 node
 | 
			
		||||
    getNode(data) {
 | 
			
		||||
      return this.store.getNode(data);
 | 
			
		||||
    },
 | 
			
		||||
    // 通过 key 设置某个节点的当前选中状态
 | 
			
		||||
    setCurrentKey(key) {
 | 
			
		||||
      if (!this.nodeKey)
 | 
			
		||||
        throw new Error("[Tree] nodeKey is required in setCurrentKey");
 | 
			
		||||
      this.store.setCurrentNodeKey(key);
 | 
			
		||||
    },
 | 
			
		||||
    remove(data) {
 | 
			
		||||
      this.store.remove(data);
 | 
			
		||||
    },
 | 
			
		||||
    // 获取当前被选中节点的 data
 | 
			
		||||
    getCurrentNode() {
 | 
			
		||||
      const currentNode = this.store.getCurrentNode();
 | 
			
		||||
      return currentNode ? currentNode.data : null;
 | 
			
		||||
    },
 | 
			
		||||
    getCurrentKey() {
 | 
			
		||||
      if (!this.nodeKey)
 | 
			
		||||
        throw new Error("[Tree] nodeKey is required in getCurrentKey");
 | 
			
		||||
      const currentNode = this.getCurrentNode();
 | 
			
		||||
      return currentNode ? currentNode[this.nodeKey] : null;
 | 
			
		||||
    },
 | 
			
		||||
    append(data, parentNode) {
 | 
			
		||||
      this.store.append(data, parentNode);
 | 
			
		||||
    },
 | 
			
		||||
    insertBefore(data, refNode) {
 | 
			
		||||
      this.store.insertBefore(data, refNode);
 | 
			
		||||
    },
 | 
			
		||||
    insertAfter(data, refNode) {
 | 
			
		||||
      this.store.insertAfter(data, refNode);
 | 
			
		||||
    },
 | 
			
		||||
    updateKeyChildren(key, data) {
 | 
			
		||||
      if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in updateKeyChild');
 | 
			
		||||
      this.store.updateChildren(key, data);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
@import "./model/transition.css";
 | 
			
		||||
 | 
			
		||||
* {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.org-chart-container {
 | 
			
		||||
  display: block;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node-children {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  padding-top: 20px;
 | 
			
		||||
  transition: all 0.5s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node {
 | 
			
		||||
  float: left;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  list-style-type: none;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  padding: 20px 5px 0 5px;
 | 
			
		||||
  transition: all 0.5s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*使用 ::before 和 ::after 绘制连接器*/
 | 
			
		||||
.vertical .org-chart-node::before,
 | 
			
		||||
.vertical .org-chart-node::after {
 | 
			
		||||
  content: "";
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  right: 50%;
 | 
			
		||||
  width: 50%;
 | 
			
		||||
  border-top: 1px solid #ccc;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node::after {
 | 
			
		||||
  right: auto;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  border-left: 1px solid #ccc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*我们需要从没有任何兄弟元素的元素中删除左右连接器*/
 | 
			
		||||
.vertical.one-branch > .org-chart-node::after,
 | 
			
		||||
.vertical.one-branch > .org-chart-node::before {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*从单个子节点的顶部移除空格*/
 | 
			
		||||
.vertical.one-branch > .org-chart-node {
 | 
			
		||||
  padding-top: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*从第一个子节点移除左连接器,从最后一个子节点移除右连接器*/
 | 
			
		||||
.vertical .org-chart-node:first-child::before,
 | 
			
		||||
.vertical .org-chart-node:last-child::after {
 | 
			
		||||
  border: 0 none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*将垂直连接器添加回最后的节点*/
 | 
			
		||||
.vertical .org-chart-node:last-child::before {
 | 
			
		||||
  border-right: 1px solid #ccc;
 | 
			
		||||
  border-radius: 0 5px 0 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node:only-child:before {
 | 
			
		||||
  border-radius: 0 0px 0 0;
 | 
			
		||||
  margin-right: -1px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node:first-child::after {
 | 
			
		||||
  border-radius: 5px 0 0 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node.is-leaf {
 | 
			
		||||
  padding-top: 20px;
 | 
			
		||||
  padding-bottom: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node.is-leaf::before {
 | 
			
		||||
  content: "";
 | 
			
		||||
  display: block;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node.is-leaf .org-chart-node-label::after {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*从父节点添加向下的连接器了*/
 | 
			
		||||
.vertical .org-chart-node-children::before {
 | 
			
		||||
  content: "";
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  border-left: 1px solid #ccc;
 | 
			
		||||
  width: 0;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node-label {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node-label .org-chart-node-label-inner {
 | 
			
		||||
  box-shadow: 0 1px 10px rgba(31, 35, 41, 0.08);
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
  margin: 0px;
 | 
			
		||||
  font-size: 16px;
 | 
			
		||||
  word-break: break-all;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node-label .org-chart-node-label-inner:hover {
 | 
			
		||||
  box-shadow: 0 1px 14px rgba(31, 35, 41, 0.12);
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node-label .org-chart-node-btn {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 100%;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  width: 20px;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
  z-index: 10;
 | 
			
		||||
  margin-left: -11px;
 | 
			
		||||
  margin-top: 9px;
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  border: 1px solid #ccc;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.15);
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  transition: all 0.35s ease;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node-label .org-chart-node-btn:hover {
 | 
			
		||||
  transform: scale(1.15);
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node-label .org-chart-node-btn::before,
 | 
			
		||||
.vertical .org-chart-node-label .org-chart-node-btn::after {
 | 
			
		||||
  content: "";
 | 
			
		||||
  position: absolute;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node-label .org-chart-node-btn::before {
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  left: 4px;
 | 
			
		||||
  right: 4px;
 | 
			
		||||
  height: 0;
 | 
			
		||||
  border-top: 1px solid #ccc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node-label .org-chart-node-btn::after {
 | 
			
		||||
  top: 4px;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  bottom: 4px;
 | 
			
		||||
  width: 0;
 | 
			
		||||
  border-left: 1px solid #ccc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node-label .expanded.org-chart-node-btn::after {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node.collapsed .org-chart-node-label {
 | 
			
		||||
  position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .org-chart-node.collapsed .org-chart-node-label::after {
 | 
			
		||||
  content: "";
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 100%;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  width: 50%;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
  border-right: 1px solid #ddd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-children,
 | 
			
		||||
.horizontal .org-chart-node-left-children {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  padding-left: 20px;
 | 
			
		||||
  transition: all 0.5s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-left-children {
 | 
			
		||||
  padding-right: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node:not(.is-left-child-node) {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  padding: 0px 5px 0 20px;
 | 
			
		||||
  transition: all 0.5s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .is-left-child-node {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  justify-content: flex-end;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .is-left-child-node {
 | 
			
		||||
  padding: 0px 20px 0 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*使用 ::before 和 ::after 绘制连接器*/
 | 
			
		||||
.horizontal .org-chart-node:not(.is-left-child-node):before,
 | 
			
		||||
.horizontal .org-chart-node:not(.is-left-child-node)::after {
 | 
			
		||||
  content: "";
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  border-left: 1px solid #ccc;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  width: 20px;
 | 
			
		||||
  height: 50%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .is-left-child-node:before,
 | 
			
		||||
.horizontal .is-left-child-node::after {
 | 
			
		||||
  content: "";
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  border-right: 1px solid #ccc;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  width: 20px;
 | 
			
		||||
  height: 50%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node:not(.is-left-child-node):after {
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  border-top: 1px solid #ccc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .is-left-child-node:after {
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  border-top: 1px solid #ccc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*我们需要从没有任何兄弟元素的元素中删除左右连接器*/
 | 
			
		||||
.horizontal.one-branch > .org-chart-node::after,
 | 
			
		||||
.horizontal.one-branch > .org-chart-node::before {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*从单个子节点的顶部移除空格*/
 | 
			
		||||
.horizontal.one-branch > .org-chart-node {
 | 
			
		||||
  padding-left: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*从第一个子节点移除左连接器,从最后一个子节点移除右连接器*/
 | 
			
		||||
.horizontal .org-chart-node:first-child::before,
 | 
			
		||||
.horizontal .org-chart-node:last-child::after {
 | 
			
		||||
  border: 0 none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*将垂直连接器添加回最后的节点*/
 | 
			
		||||
.horizontal .org-chart-node:not(.is-left-child-node):not(.is-not-child):last-child::before {
 | 
			
		||||
  border-bottom: 1px solid #ccc;
 | 
			
		||||
  border-radius: 0 0px 0 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .is-left-child-node:last-child::before {
 | 
			
		||||
  border-bottom: 1px solid #ccc;
 | 
			
		||||
  border-radius: 0 0px 5px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node:only-child::before {
 | 
			
		||||
  border-radius: 0 0px 0 0px !important;
 | 
			
		||||
  border-color: #ccc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node:not(.is-left-child-node):first-child::after {
 | 
			
		||||
  border-radius: 5px 0px 0 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .is-left-child-node:first-child::after {
 | 
			
		||||
  border-radius: 0 5px 0px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node.is-leaf {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  padding-left: 20px;
 | 
			
		||||
  padding-right: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node.is-leaf::before {
 | 
			
		||||
  content: "";
 | 
			
		||||
  display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node.is-leaf .org-chart-node-label::after,
 | 
			
		||||
.horizontal .is-left-child-node.is-leaf .org-chart-node-label::before {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .is-left-child-node:last-child::after {
 | 
			
		||||
  /* border-bottom: 1px solid green; */
 | 
			
		||||
  border-radius: 0 0px 5px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .is-left-child-node:only-child::after {
 | 
			
		||||
  border-radius: 0 0px 0 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .is-left-child-node.is-leaf {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  padding-left: 20px;
 | 
			
		||||
  padding-right: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .is-left-child-node.is-leaf::before {
 | 
			
		||||
  content: "";
 | 
			
		||||
  display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .is-left-child-node .org-chart-node-label::after {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*从父节点添加向下的连接器了*/
 | 
			
		||||
.horizontal .org-chart-node-children::before,
 | 
			
		||||
.horizontal .org-chart-node-left-children::before {
 | 
			
		||||
  content: "";
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  border-top: 1px solid #ccc;
 | 
			
		||||
  width: 12px;
 | 
			
		||||
  height: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-children::before {
 | 
			
		||||
  width: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-left-children::before {
 | 
			
		||||
  left: calc(100% - 11px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal > .only-both-tree-node > .org-chart-node-left-children::before {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-label {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-label-inner {
 | 
			
		||||
  box-shadow: 0 1px 10px rgba(31, 35, 41, 0.08);
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
  margin: 10px 0;
 | 
			
		||||
  font-size: 16px;
 | 
			
		||||
  word-break: break-all;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-label-inner:hover {
 | 
			
		||||
  box-shadow: 0 1px 14px rgba(31, 35, 41, 0.12);
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-btn {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  left: 100%;
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  width: 20px;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
  z-index: 10;
 | 
			
		||||
  margin-top: -11px;
 | 
			
		||||
  margin-left: 9px;
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  border: 1px solid #ccc;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.15);
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  transition: all 0.35s ease;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-btn:hover,
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-left-btn:hover {
 | 
			
		||||
  transform: scale(1.15);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-btn::before,
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-btn::after,
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-left-btn::before,
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-left-btn::after {
 | 
			
		||||
  content: "";
 | 
			
		||||
  position: absolute;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-btn::before,
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-left-btn::before {
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  left: 4px;
 | 
			
		||||
  right: 3px;
 | 
			
		||||
  border-top: 1px solid #ccc;
 | 
			
		||||
  height: 0;
 | 
			
		||||
  transform: translateY(-50%);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-btn::after,
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-left-btn::after {
 | 
			
		||||
  top: 4px;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  bottom: 4px;
 | 
			
		||||
  width: 0;
 | 
			
		||||
  border-left: 1px solid #ccc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-label .expanded.org-chart-node-btn::after,
 | 
			
		||||
.horizontal .org-chart-node-label .expanded.org-chart-node-left-btn::after {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node-label .org-chart-node-left-btn {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  right: 100%;
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  width: 20px;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
  z-index: 10;
 | 
			
		||||
  margin-top: -11px;
 | 
			
		||||
  margin-right: 9px;
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  border: 1px solid #ccc;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.15);
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  transition: all 0.35s ease;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node.collapsed .org-chart-node-label,
 | 
			
		||||
.horizontal .is-left-child-node.collapsed .org-chart-node-label {
 | 
			
		||||
  position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node.collapsed .org-chart-node-label::after,
 | 
			
		||||
.horizontal .is-left-child-node.collapsed .org-chart-node-label::before {
 | 
			
		||||
  content: "";
 | 
			
		||||
  border-bottom: 1px solid #ccc;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 100%;
 | 
			
		||||
  height: 50%;
 | 
			
		||||
  width: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .org-chart-node .is-root-label.is-not-right-child::after,
 | 
			
		||||
.horizontal .org-chart-node .is-root-label.is-not-left-child::before {
 | 
			
		||||
  display: none !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* .horizontal .org-chart-node.collapsed .is-root-label.is-right-child::after,
 | 
			
		||||
.horizontal .org-chart-node.collapsed .is-root-label.is-left-child::before {
 | 
			
		||||
  display: block;
 | 
			
		||||
} */
 | 
			
		||||
 | 
			
		||||
.horizontal .is-left-child-node.collapsed .org-chart-node-label::before {
 | 
			
		||||
  left: -10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .only-both-tree-node > .org-chart-node-label::before {
 | 
			
		||||
  content: "";
 | 
			
		||||
  border-bottom: 1px solid #ccc;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  right: 100%;
 | 
			
		||||
  height: 50%;
 | 
			
		||||
  width: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.org-chart-node-children .org-chart-node-btn-text {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  background: #fff;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  font-size: 12px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  color: #909090;
 | 
			
		||||
  z-index: 2;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										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>
 | 
			
		||||
							
								
								
									
										14
									
								
								components/AiOkrTree/model/merge.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								components/AiOkrTree/model/merge.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
export default function(target) {
 | 
			
		||||
  for (let i = 1, j = arguments.length; i < j; i++) {
 | 
			
		||||
    let source = arguments[i] || {};
 | 
			
		||||
    for (let prop in source) {
 | 
			
		||||
      if (source.hasOwnProperty(prop)) {
 | 
			
		||||
        let value = source[prop];
 | 
			
		||||
        if (value !== undefined) {
 | 
			
		||||
          target[prop] = value;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return target;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										254
									
								
								components/AiOkrTree/model/node.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								components/AiOkrTree/model/node.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,254 @@
 | 
			
		||||
import { markNodeData, NODE_KEY } from './util';
 | 
			
		||||
import objectAssign from './merge';
 | 
			
		||||
 | 
			
		||||
const getPropertyFromData = function (node, prop) {
 | 
			
		||||
  const props = node.store.props;
 | 
			
		||||
  const data = node.data || {};
 | 
			
		||||
  const config = props[prop];
 | 
			
		||||
 | 
			
		||||
  if (typeof config === 'function') {
 | 
			
		||||
    return config(data, node);
 | 
			
		||||
  } else if (typeof config === 'string') {
 | 
			
		||||
    return data[config];
 | 
			
		||||
  } else if (typeof config === 'undefined') {
 | 
			
		||||
    const dataProp = data[prop];
 | 
			
		||||
    return dataProp === undefined ? '' : dataProp;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let nodeIdSeed = 0;
 | 
			
		||||
 | 
			
		||||
export default class Node {
 | 
			
		||||
  constructor(options, isLeftChild = false) {
 | 
			
		||||
    this.isLeftChild = isLeftChild;
 | 
			
		||||
    this.id = nodeIdSeed++;
 | 
			
		||||
    this.data = null;
 | 
			
		||||
    this.expanded = false;
 | 
			
		||||
    this.leftExpanded = false;
 | 
			
		||||
    this.isCurrent = false;
 | 
			
		||||
    this.visible = true;
 | 
			
		||||
    this.parent = null;
 | 
			
		||||
    for (let name in options) {
 | 
			
		||||
      if (options.hasOwnProperty(name)) {
 | 
			
		||||
        this[name] = options[name];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    this.level = 0;
 | 
			
		||||
    this.childNodes = [];
 | 
			
		||||
    this.leftChildNodes = [];
 | 
			
		||||
 | 
			
		||||
    if (this.parent) {
 | 
			
		||||
      this.level = this.parent.level + 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const store = this.store;
 | 
			
		||||
    if (!store) {
 | 
			
		||||
      throw new Error('[Node]store is required!');
 | 
			
		||||
    }
 | 
			
		||||
    store.registerNode(this);
 | 
			
		||||
    if (this.data) {
 | 
			
		||||
      this.setData(this.data, isLeftChild);
 | 
			
		||||
      if (store.defaultExpandAll || !store.showCollapsable) {
 | 
			
		||||
        this.expanded = true;
 | 
			
		||||
        this.leftExpanded = true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!Array.isArray(this.data)) {
 | 
			
		||||
      markNodeData(this, this.data);
 | 
			
		||||
    }
 | 
			
		||||
    if (!this.data) return;
 | 
			
		||||
    const defaultExpandedKeys = store.defaultExpandedKeys;
 | 
			
		||||
    const key = store.key;
 | 
			
		||||
    if (
 | 
			
		||||
      key &&
 | 
			
		||||
      defaultExpandedKeys &&
 | 
			
		||||
      defaultExpandedKeys.indexOf(this.key) !== -1
 | 
			
		||||
    ) {
 | 
			
		||||
      this.expand(null, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
      key &&
 | 
			
		||||
      store.currentNodeKey !== undefined &&
 | 
			
		||||
      this.key === store.currentNodeKey
 | 
			
		||||
    ) {
 | 
			
		||||
      store.currentNode = this;
 | 
			
		||||
      store.currentNode.isCurrent = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.updateLeafState();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setData(data, isLeftChild) {
 | 
			
		||||
    if (!Array.isArray(data)) {
 | 
			
		||||
      markNodeData(this, data);
 | 
			
		||||
    }
 | 
			
		||||
    const store = this.store;
 | 
			
		||||
    this.data = data;
 | 
			
		||||
    this.childNodes = [];
 | 
			
		||||
    let children;
 | 
			
		||||
    if (this.level === 0 && this.data instanceof Array) {
 | 
			
		||||
      children = this.data;
 | 
			
		||||
    } else {
 | 
			
		||||
      children = getPropertyFromData(this, 'children') || [];
 | 
			
		||||
    }
 | 
			
		||||
    for (let i = 0, j = children.length; i < j; i++) {
 | 
			
		||||
      this.insertChild({ data: children[i] }, null, null, isLeftChild);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  get key() {
 | 
			
		||||
    const nodeKey = this.store.key;
 | 
			
		||||
    if (this.data) return this.data[nodeKey];
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
  get label() {
 | 
			
		||||
    return getPropertyFromData(this, 'label');
 | 
			
		||||
  }
 | 
			
		||||
  // 是否是 OKR 飞书模式
 | 
			
		||||
  hasLeftChild() {
 | 
			
		||||
    const store = this.store;
 | 
			
		||||
    return store.onlyBothTree && store.direction === 'horizontal';
 | 
			
		||||
  }
 | 
			
		||||
  insertChild(child, index, batch, isLeftChild) {
 | 
			
		||||
    if (!child) throw new Error('insertChild error: child is required.');
 | 
			
		||||
    if (!(child instanceof Node)) {
 | 
			
		||||
      if (!batch) {
 | 
			
		||||
        const children = this.getChildren(true);
 | 
			
		||||
        if (children.indexOf(child.data) === -1) {
 | 
			
		||||
          if (index === undefined || index === null || index < 0) {
 | 
			
		||||
            children.push(child.data);
 | 
			
		||||
          } else {
 | 
			
		||||
            children.splice(index, 0, child.data);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      objectAssign(child, {
 | 
			
		||||
        parent: this,
 | 
			
		||||
        store: this.store,
 | 
			
		||||
      });
 | 
			
		||||
      child = new Node(child, isLeftChild);
 | 
			
		||||
    }
 | 
			
		||||
    child.level = this.level + 1;
 | 
			
		||||
    if (index === undefined || index === null || index < 0) {
 | 
			
		||||
      this.childNodes.push(child);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.childNodes.splice(index, 0, child);
 | 
			
		||||
    }
 | 
			
		||||
    this.updateLeafState();
 | 
			
		||||
  }
 | 
			
		||||
  getChildren(forceInit = false) {
 | 
			
		||||
    // this is data
 | 
			
		||||
    if (this.level === 0) return this.data;
 | 
			
		||||
    const data = this.data;
 | 
			
		||||
    if (!data) return null;
 | 
			
		||||
 | 
			
		||||
    const props = this.store.props;
 | 
			
		||||
    let children = 'children';
 | 
			
		||||
    if (props) {
 | 
			
		||||
      children = props.children || 'children';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (data[children] === undefined) {
 | 
			
		||||
      data[children] = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (forceInit && !data[children]) {
 | 
			
		||||
      data[children] = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return data[children];
 | 
			
		||||
  }
 | 
			
		||||
  updateLeafState() {
 | 
			
		||||
    if (
 | 
			
		||||
      this.store.lazy === true &&
 | 
			
		||||
      this.loaded !== true &&
 | 
			
		||||
      typeof this.isLeafByUser !== 'undefined'
 | 
			
		||||
    ) {
 | 
			
		||||
      this.isLeaf = this.isLeafByUser;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const childNodes = this.childNodes;
 | 
			
		||||
    if (
 | 
			
		||||
      !this.store.lazy ||
 | 
			
		||||
      (this.store.lazy === true && this.loaded === true)
 | 
			
		||||
    ) {
 | 
			
		||||
      this.isLeaf = !childNodes || childNodes.length === 0;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this.isLeaf = false;
 | 
			
		||||
  }
 | 
			
		||||
  updateLeftLeafState() {
 | 
			
		||||
    if (
 | 
			
		||||
      this.store.lazy === true &&
 | 
			
		||||
      this.loaded !== true &&
 | 
			
		||||
      typeof this.isLeafByUser !== 'undefined'
 | 
			
		||||
    ) {
 | 
			
		||||
      this.isLeaf = this.isLeafByUser;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const childNodes = this.leftChildNodes;
 | 
			
		||||
    if (
 | 
			
		||||
      !this.store.lazy ||
 | 
			
		||||
      (this.store.lazy === true && this.loaded === true)
 | 
			
		||||
    ) {
 | 
			
		||||
      this.isLeaf = !childNodes || childNodes.length === 0;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this.isLeaf = false;
 | 
			
		||||
  }
 | 
			
		||||
  // 节点的收起
 | 
			
		||||
  collapse() {
 | 
			
		||||
    this.expanded = false;
 | 
			
		||||
  }
 | 
			
		||||
  // 节点的展开
 | 
			
		||||
  expand(callback, expandParent) {
 | 
			
		||||
    const done = () => {
 | 
			
		||||
      if (expandParent) {
 | 
			
		||||
        let parent = this.parent;
 | 
			
		||||
        while (parent.level > 0) {
 | 
			
		||||
          parent.isLeftChild
 | 
			
		||||
            ? (parent.leftExpanded = true)
 | 
			
		||||
            : (parent.expanded = true);
 | 
			
		||||
          parent = parent.parent;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this.isLeftChild ? (this.leftExpanded = true) : (this.expanded = true);
 | 
			
		||||
      if (callback) callback();
 | 
			
		||||
    };
 | 
			
		||||
    done();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  removeChild(child) {
 | 
			
		||||
    const children = this.getChildren() || [];
 | 
			
		||||
    const dataIndex = children.indexOf(child.data);
 | 
			
		||||
    if (dataIndex > -1) {
 | 
			
		||||
      children.splice(dataIndex, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const index = this.childNodes.indexOf(child);
 | 
			
		||||
 | 
			
		||||
    if (index > -1) {
 | 
			
		||||
      this.store && this.store.deregisterNode(child);
 | 
			
		||||
      child.parent = null;
 | 
			
		||||
      this.childNodes.splice(index, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.updateLeafState();
 | 
			
		||||
  }
 | 
			
		||||
  insertBefore(child, ref) {
 | 
			
		||||
    let index;
 | 
			
		||||
    if (ref) {
 | 
			
		||||
      index = this.childNodes.indexOf(ref);
 | 
			
		||||
    }
 | 
			
		||||
    this.insertChild(child, index);
 | 
			
		||||
  }
 | 
			
		||||
  insertAfter(child, ref) {
 | 
			
		||||
    let index;
 | 
			
		||||
    if (ref) {
 | 
			
		||||
      index = this.childNodes.indexOf(ref);
 | 
			
		||||
      if (index !== -1) index += 1;
 | 
			
		||||
    }
 | 
			
		||||
    this.insertChild(child, index);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								components/AiOkrTree/model/transition.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								components/AiOkrTree/model/transition.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
.okr-fade-in-enter,.okr-fade-in-leave-active,.okr-fade-in-linear-enter,.okr-fade-in-linear-leave,.okr-fade-in-linear-leave-active,.fade-in-linear-enter,.fade-in-linear-leave,.fade-in-linear-leave-active{opacity:0}.fade-in-linear-enter-active,.fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.okr-fade-in-linear-enter-active,.okr-fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.okr-fade-in-enter-active,.okr-fade-in-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.okr-zoom-in-center-enter-active,.okr-zoom-in-center-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.okr-zoom-in-center-enter,.okr-zoom-in-center-leave-active{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}.okr-zoom-in-top-enter-active,.okr-zoom-in-top-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:center top;transform-origin:center top}.okr-zoom-in-top-enter,.okr-zoom-in-top-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.okr-zoom-in-bottom-enter-active,.okr-zoom-in-bottom-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}.okr-zoom-in-bottom-enter,.okr-zoom-in-bottom-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.okr-zoom-in-left-enter-active,.okr-zoom-in-left-leave-active{opacity:1;-webkit-transform:scale(1,1);transform:scale(1,1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:top left;transform-origin:top left}.okr-zoom-in-left-enter,.okr-zoom-in-left-leave-active{opacity:0;-webkit-transform:scale(.45,.45);transform:scale(.45,.45)}.collapse-transition{-webkit-transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out;transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out}.horizontal-collapse-transition{-webkit-transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out;transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out}.okr-list-enter-active,.okr-list-leave-active{-webkit-transition:all 1s;transition:all 1s}.okr-list-enter,.okr-list-leave-active{opacity:0;-webkit-transform:translateY(-30px);transform:translateY(-30px)}.okr-opacity-transition{-webkit-transition:opacity .3s cubic-bezier(.55,0,.1,1);transition:opacity .3s cubic-bezier(.55,0,.1,1)}
 | 
			
		||||
							
								
								
									
										176
									
								
								components/AiOkrTree/model/tree-store.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								components/AiOkrTree/model/tree-store.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
			
		||||
import Node from "./node";
 | 
			
		||||
import { getNodeKey } from "./util";
 | 
			
		||||
export default class TreeStore {
 | 
			
		||||
  constructor(options) {
 | 
			
		||||
    this.currentNode = null;
 | 
			
		||||
    this.currentNodeKey = null;
 | 
			
		||||
 | 
			
		||||
    for (let option in options) {
 | 
			
		||||
      if (options.hasOwnProperty(option)) {
 | 
			
		||||
        this[option] = options[option];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    this.nodesMap = {};
 | 
			
		||||
    this.root = new Node(
 | 
			
		||||
      {
 | 
			
		||||
        data: this.data,
 | 
			
		||||
        store: this
 | 
			
		||||
      },
 | 
			
		||||
      false
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (this.root.store.onlyBothTree) {
 | 
			
		||||
      if (!this.leftData)
 | 
			
		||||
        throw new Error("[Tree] leftData is required in onlyBothTree");
 | 
			
		||||
      if (this.leftData) {
 | 
			
		||||
        this.isLeftChilds = new Node(
 | 
			
		||||
          {
 | 
			
		||||
            data: this.leftData,
 | 
			
		||||
            store: this
 | 
			
		||||
          },
 | 
			
		||||
          true
 | 
			
		||||
        );
 | 
			
		||||
        if (this.isLeftChilds) {
 | 
			
		||||
          this.root.childNodes[0].leftChildNodes = this.isLeftChilds.childNodes[0].childNodes;
 | 
			
		||||
          this.root.childNodes[0].leftExpanded = this.isLeftChilds.childNodes[0].leftExpanded;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  filter(value, childName = "childNodes") {
 | 
			
		||||
    this.filterRight(value, childName);
 | 
			
		||||
  }
 | 
			
		||||
  // 过滤默认节点
 | 
			
		||||
  filterRight(value, childName) {
 | 
			
		||||
    const filterNodeMethod = this.filterNodeMethod;
 | 
			
		||||
    const traverse = function(node, childName) {
 | 
			
		||||
      let childNodes;
 | 
			
		||||
      if (node.root) {
 | 
			
		||||
        childNodes = node.root.childNodes[0][childName];
 | 
			
		||||
      } else {
 | 
			
		||||
        childNodes = node.childNodes;
 | 
			
		||||
      }
 | 
			
		||||
      childNodes.forEach(child => {
 | 
			
		||||
        child.visible = filterNodeMethod.call(child, value, child.data, child);
 | 
			
		||||
        traverse(child, childName);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      if (!node.visible && childNodes.length) {
 | 
			
		||||
        let allHidden = true;
 | 
			
		||||
        allHidden = !childNodes.some(child => child.visible);
 | 
			
		||||
 | 
			
		||||
        if (node.root) {
 | 
			
		||||
          node.root.visible = allHidden === false;
 | 
			
		||||
        } else {
 | 
			
		||||
          node.visible = allHidden === false;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (!value) return;
 | 
			
		||||
 | 
			
		||||
      if (node.visible) node.expand();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    traverse(this, childName);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  registerNode(node) {
 | 
			
		||||
    const key = this.key;
 | 
			
		||||
    if (!key || !node || !node.data) return;
 | 
			
		||||
 | 
			
		||||
    const nodeKey = node.key;
 | 
			
		||||
    if (nodeKey !== undefined) this.nodesMap[node.key] = node;
 | 
			
		||||
  }
 | 
			
		||||
  deregisterNode(node) {
 | 
			
		||||
    const key = this.key;
 | 
			
		||||
    if (!key || !node || !node.data) return;
 | 
			
		||||
    node.childNodes.forEach(child => {
 | 
			
		||||
      this.deregisterNode(child);
 | 
			
		||||
    });
 | 
			
		||||
    delete this.nodesMap[node.key];
 | 
			
		||||
  }
 | 
			
		||||
  setData(newVal) {
 | 
			
		||||
    const instanceChanged = newVal !== this.root.data;
 | 
			
		||||
    if (instanceChanged) {
 | 
			
		||||
      this.root.setData(newVal);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.root.updateChildren();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  updateChildren(key, data) {
 | 
			
		||||
    const node = this.nodesMap[key];
 | 
			
		||||
    if (!node) return;
 | 
			
		||||
    const childNodes = node.childNodes;
 | 
			
		||||
    for (let i = childNodes.length - 1; i >= 0; i--) {
 | 
			
		||||
      const child = childNodes[i];
 | 
			
		||||
      this.remove(child.data);
 | 
			
		||||
    }
 | 
			
		||||
    for (let i = 0, j = data.length; i < j; i++) {
 | 
			
		||||
      const child = data[i];
 | 
			
		||||
      this.append(child, node.data);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  getNode(data) {
 | 
			
		||||
    if (data instanceof Node) return data;
 | 
			
		||||
    const key = typeof data !== "object" ? data : getNodeKey(this.key, data);
 | 
			
		||||
    return this.nodesMap[key] || null;
 | 
			
		||||
  }
 | 
			
		||||
  setDefaultExpandedKeys(keys) {
 | 
			
		||||
    keys = keys || [];
 | 
			
		||||
    this.defaultExpandedKeys = keys;
 | 
			
		||||
    keys.forEach(key => {
 | 
			
		||||
      const node = this.getNode(key);
 | 
			
		||||
      if (node) node.expand(null, true);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  setCurrentNode(currentNode) {
 | 
			
		||||
    const prevCurrentNode = this.currentNode;
 | 
			
		||||
    if (prevCurrentNode) {
 | 
			
		||||
      prevCurrentNode.isCurrent = false;
 | 
			
		||||
    }
 | 
			
		||||
    this.currentNode = currentNode;
 | 
			
		||||
    this.currentNode.isCurrent = true;
 | 
			
		||||
  }
 | 
			
		||||
  setUserCurrentNode(node) {
 | 
			
		||||
    const key = node.key;
 | 
			
		||||
    const currNode = this.nodesMap[key];
 | 
			
		||||
    this.setCurrentNode(currNode);
 | 
			
		||||
  }
 | 
			
		||||
  setCurrentNodeKey(key) {
 | 
			
		||||
    if (key === null || key === undefined) {
 | 
			
		||||
      this.currentNode && (this.currentNode.isCurrent = false);
 | 
			
		||||
      this.currentNode = null;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const node = this.getNode(key);
 | 
			
		||||
    if (node) {
 | 
			
		||||
      this.setCurrentNode(node);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  getCurrentNode() {
 | 
			
		||||
    return this.currentNode;
 | 
			
		||||
  }
 | 
			
		||||
  remove(data) {
 | 
			
		||||
    const node = this.getNode(data);
 | 
			
		||||
    if (node && node.parent) {
 | 
			
		||||
      if (node === this.currentNode) {
 | 
			
		||||
        this.currentNode = null;
 | 
			
		||||
      }
 | 
			
		||||
      node.parent.removeChild(node);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  append(data, parentData) {
 | 
			
		||||
    const parentNode = parentData ? this.getNode(parentData) : this.root;
 | 
			
		||||
 | 
			
		||||
    if (parentNode) {
 | 
			
		||||
      parentNode.insertChild({ data });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  insertBefore(data, refData) {
 | 
			
		||||
    const refNode = this.getNode(refData);
 | 
			
		||||
    refNode.parent.insertBefore({ data }, refNode);
 | 
			
		||||
  }
 | 
			
		||||
  insertAfter(data, refData) {
 | 
			
		||||
    const refNode = this.getNode(refData);
 | 
			
		||||
    refNode.parent.insertAfter({ data }, refNode);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								components/AiOkrTree/model/util.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								components/AiOkrTree/model/util.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
export const NODE_KEY = "$treeNodeId";
 | 
			
		||||
 | 
			
		||||
export const markNodeData = function(node, data) {
 | 
			
		||||
  if (!data || data[NODE_KEY]) return;
 | 
			
		||||
  Object.defineProperty(data, NODE_KEY, {
 | 
			
		||||
    value: node.id,
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    configurable: false,
 | 
			
		||||
    writable: false
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getNodeKey = function(key, data) {
 | 
			
		||||
  if (!key) return data[NODE_KEY];
 | 
			
		||||
  return data[key];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const findNearestComponent = (element, componentName) => {
 | 
			
		||||
  let target = element;
 | 
			
		||||
  while (target && target.tagName !== "BODY") {
 | 
			
		||||
    if (target.__vue__ && target.__vue__.$options.name === componentName) {
 | 
			
		||||
      return target.__vue__;
 | 
			
		||||
    }
 | 
			
		||||
    target = target.parentNode;
 | 
			
		||||
  }
 | 
			
		||||
  return null;
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user