| 
1 | 1 | <template lang="pug">  | 
2 | 2 | .he-tree.tree  | 
3 |  | -  .tree-branch(v-for="node in data" :class="[data.open ? root.openedClass : '']")  | 
4 |  | -    slot(:data="node" :root="root")  | 
 | 3 | +  .tree-branch(v-for="(node, i) in nodes" :key="metas[i].id" :class="[metas[i].folded ? root.foldedClass : root.unfoldedClass]")  | 
 | 4 | +    slot(:node="node" :meta="metas[i]" :root="root")  | 
5 | 5 |       .tree-node {{node.text}}  | 
6 |  | -    transition(:name="root.childrenTransition")  | 
7 |  | -      Tree.tree-children(v-if="node.open" :data="node.children" :root="root")  | 
 | 6 | +    transition(v-if="node.children && node.children.length > 0" :name="root.foldingTransition")  | 
 | 7 | +      Tree.tree-children(v-if="!metas[i].folded" :value="node.children" :privateProps="childPrivateProps")  | 
8 | 8 | </template>  | 
9 | 9 | 
 
  | 
10 | 10 | <script>  | 
11 | 11 | import * as hp from 'helper-js'  | 
12 | 12 | import * as th from 'tree-helper'  | 
13 | 13 | 
  | 
14 | 14 | export default {  | 
 | 15 | +  name: 'Tree',  | 
15 | 16 |   props: {  | 
16 |  | -    data: {},  | 
17 |  | -    root: {default: is => this},  | 
18 |  | -    openedClass: {default: 'open'},  | 
19 |  | -    childrenTransition: {},  | 
 | 17 | +    value: {},  | 
 | 18 | +    foldedClass: {default: 'folded'},  | 
 | 19 | +    unfoldedClass: {default: 'unfolded'},  | 
 | 20 | +    foldingTransition: {},  | 
 | 21 | +    foldAllAtBeginning: {type: Boolean},  | 
 | 22 | +    privateProps: {},  | 
 | 23 | +    idMode: {default: 'object'}, // object, id(node must has id)  | 
20 | 24 |   },  | 
21 | 25 |   // components: {},  | 
22 | 26 |   data() {  | 
23 | 27 |     return {  | 
24 |  | -      // rootData: null,  | 
 | 28 | +      metas: [], // metas of current level nodes  | 
25 | 29 |     }  | 
26 | 30 |   },  | 
27 |  | -  // computed: {},  | 
28 |  | -  watch: {  | 
29 |  | -    // data: {  | 
30 |  | -    //   immediate: true,  | 
31 |  | -    //   handler(data, old) {  | 
32 |  | -    //     if (data === old) {  | 
33 |  | -    //       return  | 
34 |  | -    //     }  | 
35 |  | -    //     // make rootData always use a same object  | 
36 |  | -    //     this.rootData = this.rootData || {isRoot: true, _id: `tree_${this._uid}_node_root`, children: []}  | 
37 |  | -    //     th.breadthFirstSearch(data, (node, k, parent) => {  | 
38 |  | -    //       this.compeleteNode(node, parent)  | 
39 |  | -    //     })  | 
40 |  | -    //     this.rootData.children = data  | 
41 |  | -    //   }  | 
42 |  | -    // }  | 
43 |  | -  },  | 
44 |  | -  methods: {  | 
45 |  | -    compeleteNode(node, parent) {  | 
46 |  | -      const compeletedData = {  | 
47 |  | -        open: true,  | 
48 |  | -        children: [],  | 
49 |  | -        active: false,  | 
50 |  | -        style: {},  | 
51 |  | -        class: '',  | 
52 |  | -        innerStyle: {},  | 
53 |  | -        innerClass: '',  | 
54 |  | -        innerBackStyle: {},  | 
55 |  | -        innerBackClass: {},  | 
56 |  | -      }  | 
57 |  | -      for (const key in compeletedData) {  | 
58 |  | -        if (!node.hasOwnProperty(key)) {  | 
59 |  | -          this.$set(node, key, compeletedData[key])  | 
60 |  | -        }  | 
 | 31 | +  computed: {  | 
 | 32 | +    root() { return this.privateProps && this.privateProps.root || this },  | 
 | 33 | +    nodes() { return this.value || [] },  | 
 | 34 | +    childPrivateProps() {  | 
 | 35 | +      return {  | 
 | 36 | +        root: this.root  | 
61 | 37 |       }  | 
62 |  | -      this.$set(node, 'parent', parent || this.rootData)  | 
63 |  | -      if (!node.hasOwnProperty('_id')) {  | 
64 |  | -        node._id = `tree_${this._uid}_node_${hp.strRand(this.idLength)}`  | 
65 |  | -      }  | 
66 |  | -      node._treeNodePropertiesCompleted = true  | 
67 | 38 |     },  | 
68 |  | -    // pure node self  | 
69 |  | -    pure(node, withChildren, after) {  | 
70 |  | -      const t = Object.assign({}, node)  | 
71 |  | -      delete t._id  | 
72 |  | -      delete t.parent  | 
73 |  | -      delete t.children  | 
74 |  | -      delete t.open  | 
75 |  | -      delete t.active  | 
76 |  | -      delete t.style  | 
77 |  | -      delete t.class  | 
78 |  | -      delete t.innerStyle  | 
79 |  | -      delete t.innerClass  | 
80 |  | -      delete t.innerBackStyle  | 
81 |  | -      delete t.innerBackClass  | 
82 |  | -      for (const key of Object.keys(t)) {  | 
83 |  | -        if (key[0] === '_') {  | 
84 |  | -          delete t[key]  | 
 | 39 | +  },  | 
 | 40 | +  watch: {},  | 
 | 41 | +  methods: {  | 
 | 42 | +    nodesWatcher(nodes, oldNodes) {  | 
 | 43 | +      if (oldNodes) {  | 
 | 44 | +        // removed metas  | 
 | 45 | +        if (this.root.idMode === 'id') {  | 
 | 46 | +          const t = {}  | 
 | 47 | +          nodes.forEach(node => {t[node.id]=true})  | 
 | 48 | +          oldNodes.forEach(node => {  | 
 | 49 | +            if (!t[node.id]) {  | 
 | 50 | +              // delete node and children meta  | 
 | 51 | +              th.depthFirstSearch(node, (childNode) => {  | 
 | 52 | +                delete this.root.metaMap[childNode.id]  | 
 | 53 | +              })  | 
 | 54 | +            }  | 
 | 55 | +          })  | 
 | 56 | +        } else {  | 
 | 57 | +          const t = new Map()  | 
 | 58 | +          nodes.forEach(node => t.set(node, null))  | 
 | 59 | +          oldNodes.forEach(node => {  | 
 | 60 | +            if (!t.has(node)) {  | 
 | 61 | +              // delete node and children meta  | 
 | 62 | +              th.depthFirstSearch(node, (childNode) => {  | 
 | 63 | +                this.root.metaMap.delete(childNode)  | 
 | 64 | +              })  | 
 | 65 | +            }  | 
 | 66 | +          })  | 
85 | 67 |         }  | 
86 | 68 |       }  | 
87 |  | -      if (withChildren && node.children) {  | 
88 |  | -        t.children = node.children.slice()  | 
89 |  | -        t.children.forEach((v, k) => {  | 
90 |  | -          t.children[k] = this.pure(v, withChildren)  | 
91 |  | -        })  | 
92 |  | -      }  | 
93 |  | -      if (after) {  | 
94 |  | -        return after(t, node) || t  | 
95 |  | -      }  | 
96 |  | -      return t  | 
97 |  | -    },  | 
98 |  | -    getNodeById(id) {  | 
99 |  | -      let r  | 
100 |  | -      th.breadthFirstSearch(this.rootData.children, (node) => {  | 
101 |  | -        if (node._id === id) {  | 
102 |  | -          r = node  | 
103 |  | -          return false  | 
104 |  | -        }  | 
105 |  | -      })  | 
106 |  | -      return r  | 
107 |  | -    },  | 
108 |  | -    getActivated() {  | 
109 |  | -      const r = []  | 
110 |  | -      th.breadthFirstSearch(this.rootData.children, (node) => {  | 
111 |  | -        if (node.active) {  | 
112 |  | -          r.push(node)  | 
113 |  | -        }  | 
 | 69 | +      //  | 
 | 70 | +      this.metas = nodes.map(node => (this.idMode === 'id' ? this.root.metaMap[node.id] : this.root.metaMap.get(node)) || {  | 
 | 71 | +        id: `he_tree_node_${node.id || hp.strRand()}`,  | 
 | 72 | +        folded: this.root.foldAllAtBeginning,  | 
114 | 73 |       })  | 
115 |  | -      return r  | 
116 |  | -    },  | 
117 |  | -    getOpened() {  | 
118 |  | -      const r = []  | 
119 |  | -      th.breadthFirstSearch(this.rootData.children, (node) => {  | 
120 |  | -        if (node.open) {  | 
121 |  | -          r.push(node)  | 
122 |  | -        }  | 
123 |  | -      })  | 
124 |  | -      return r  | 
125 |  | -    },  | 
126 |  | -    activeNode(node, inactiveOld) {  | 
127 |  | -      let {activated} = this  | 
128 |  | -      if (inactiveOld) {  | 
129 |  | -        this.getActivated().forEach(node2 => {  | 
130 |  | -          node2.active = false  | 
131 |  | -        })  | 
132 |  | -      }  | 
133 |  | -      node.active = true  | 
134 |  | -    },  | 
135 |  | -    toggleActive(node, inactiveOld) {  | 
136 |  | -      if (node.active) {  | 
137 |  | -        node.active = false  | 
138 |  | -      } else {  | 
139 |  | -        this.activeNode(node, inactiveOld)  | 
140 |  | -      }  | 
141 |  | -    },  | 
142 |  | -    openNode(node, closeOld) {  | 
143 |  | -      let {opened} = this  | 
144 |  | -      if (closeOld) {  | 
145 |  | -        this.getOpened().forEach(node2 => {  | 
146 |  | -          node2.open = false  | 
147 |  | -          this.$emit('nodeOpenChanged', node2)  | 
148 |  | -        })  | 
149 |  | -      }  | 
150 |  | -      node.open = true  | 
151 |  | -      this.$emit('nodeOpenChanged', node)  | 
152 |  | -    },  | 
153 |  | -    toggleOpen(node, closeOld) {  | 
154 |  | -      if (node.open) {  | 
155 |  | -        node.open = false  | 
156 |  | -        this.$emit('nodeOpenChanged', node)  | 
157 |  | -      } else {  | 
158 |  | -        this.openNode(node, closeOld)  | 
159 |  | -      }  | 
160 |  | -    },  | 
161 |  | -    getPureData(after) {  | 
162 |  | -      return this.pure(this.rootData, true, after).children  | 
163 |  | -    },  | 
164 |  | -    deleteNode(node) {  | 
165 |  | -      return hp.arrayRemove(node.parent.children, node)  | 
166 | 74 |     },  | 
167 | 75 |   },  | 
168 |  | -  // created() {},  | 
 | 76 | +  created() {  | 
 | 77 | +    if (this === this.root) {  | 
 | 78 | +      this.metaMap = this.idMode === 'id' ? {} : new Map()  | 
 | 79 | +    }  | 
 | 80 | +    this.$watch('nodes', (...args) => this.nodesWatcher(...args), {immediate: true})  | 
 | 81 | +  },  | 
169 | 82 |   // mounted() {},  | 
 | 83 | +  // beforeDestroy() {},  | 
170 | 84 | }  | 
171 | 85 | </script>  | 
 | 86 | + | 
 | 87 | +<style>  | 
 | 88 | +.he-tree .tree-children{  | 
 | 89 | +  padding-left: 20px;  | 
 | 90 | +}  | 
 | 91 | +</style>  | 
0 commit comments