فهرست منبع

Tree: checkbox can be disabled

Dreamacro 8 سال پیش
والد
کامیت
476f76875c

+ 87 - 0
examples/docs/en-US/tree.md

@@ -93,6 +93,35 @@
     }]
   }];
 
+  const data3 = [{
+    id: 1,
+    label: 'Level one 1',
+    children: [{
+      id: 3,
+      label: 'Level two 2-1',
+      children: [{
+        id: 4,
+        label: 'Level three 3-1-1'
+      }, {
+        id: 5,
+        label: 'Level three 3-1-2',
+        disabled: true
+      }]
+    }, {
+      id: 2,
+      label: 'Level two 2-2',
+      disabled: true,
+      children: [{
+        id: 6,
+        label: 'Level three 3-2-1'
+      }, {
+        id: 7,
+        label: 'Level three 3-2-2',
+        disabled: true
+      }]
+    }]
+  }];
+
   let id = 1000;
 
   const regions = [{
@@ -211,6 +240,7 @@
       return {
         data,
         data2,
+        data3,
         regions,
         defaultProps,
         props,
@@ -363,6 +393,63 @@ Used for node selection. In the following example, data for each layer is acquir
 ```
 :::
 
+### Can disable checkbox
+
+The checkbox of a node can be set as disabled. In the example, 'disabled' property is declared in defaultProps, and some nodes are set as 'disabled:true'. The corresponding checkbox is disabled and can't be clicked.
+
+::: demo
+```html
+<el-tree
+  :data="data3"
+  :props="defaultProps"
+  show-checkbox
+  @check-change="handleCheckChange">
+</el-tree>
+
+<script>
+  export default {
+    data() {
+      return {
+        data3: [{
+          id: 1,
+          label: 'Level one 1',
+          children: [{
+            id: 3,
+            label: 'Level two 2-1',
+            children: [{
+              id: 4,
+              label: 'Level three 3-1-1'
+            }, {
+              id: 5,
+              label: 'Level three 3-1-2',
+              disabled: true
+            }]
+          }, {
+            id: 2,
+            label: 'Level two 2-2',
+            disabled: true,
+            children: [{
+              id: 6,
+              label: 'Level three 3-2-1'
+            }, {
+              id: 7,
+              label: 'Level three 3-2-2',
+              disabled: true
+            }]
+          }]
+        }],
+        defaultProps: {
+            children: 'children',
+            label: 'label',
+            disabled: 'disabled',
+        },
+      };
+    }
+  };
+</script>
+```
+:::
+
 ### Default expanded and default checked
 Tree nodes can be initially expanded or checked
 

+ 86 - 0
examples/docs/zh-CN/tree.md

@@ -93,6 +93,35 @@
     }]
   }];
 
+  const data3 = [{
+    id: 1,
+    label: '一级 2',
+    children: [{
+      id: 3,
+      label: '二级 2-1',
+      children: [{
+        id: 4,
+        label: '三级 3-1-1'
+      }, {
+        id: 5,
+        label: '三级 3-1-2',
+        disabled: true
+      }]
+    }, {
+      id: 2,
+      label: '二级 2-2',
+      disabled: true,
+      children: [{
+        id: 6,
+        label: '三级 3-2-1'
+      }, {
+        id: 7,
+        label: '三级 3-2-2',
+        disabled: true
+      }]
+    }]
+  }];
+
   let id = 1000;
 
   const regions = [{
@@ -211,6 +240,7 @@
       return {
         data,
         data2,
+        data3,
         regions,
         defaultProps,
         props,
@@ -427,6 +457,62 @@
 ```
 :::
 
+### 禁用状态
+可将 Tree 的某些节点设置为禁用状态
+
+::: demo 通过`disabled`设置禁用状态。
+```html
+<el-tree
+  :data="data3"
+  show-checkbox
+  node-key="id"
+  :default-expanded-keys="[2, 3]"
+  :default-checked-keys="[5]">
+</el-tree>
+
+<script>
+  export default {
+    data() {
+      return {
+        data3: [{
+          id: 1,
+          label: '一级 2',
+          children: [{
+            id: 3,
+            label: '二级 2-1',
+            children: [{
+              id: 4,
+              label: '三级 3-1-1'
+            }, {
+              id: 5,
+              label: '三级 3-1-2',
+              disabled: true
+            }]
+          }, {
+            id: 2,
+            label: '二级 2-2',
+            disabled: true,
+            children: [{
+              id: 6,
+              label: '三级 3-2-1'
+            }, {
+              id: 7,
+              label: '三级 3-2-2',
+              disabled: true
+            }]
+          }]
+        }],
+        defaultProps: {
+          children: 'children',
+          label: 'label'
+        }
+      };
+    }
+  };
+</script>
+```
+:::
+
 ### 树节点的选择
 
 ::: demo 本例展示如何获取和设置选中节点。获取和设置各有两种方式:通过 node 或通过 key。如果需要通过 key 来获取或设置,则必须设置`node-key`。

+ 49 - 14
packages/tree/src/model/node.js

@@ -1,28 +1,45 @@
 import objectAssign from 'element-ui/src/utils/merge';
 import { markNodeData, NODE_KEY } from './util';
 
-const reInitChecked = function(node) {
-  const siblings = node.childNodes;
-
+export const getChildState = node => {
   let all = true;
   let none = true;
+  let allWithoutDisable = true;
 
-  for (let i = 0, j = siblings.length; i < j; i++) {
-    const sibling = siblings[i];
-    if (sibling.checked !== true || sibling.indeterminate) {
+  for (let n of node) {
+    if (n.checked !== true || n.indeterminate) {
       all = false;
+      if (!n.disabled) {
+        allWithoutDisable = false;
+      }
     }
-    if (sibling.checked !== false || sibling.indeterminate) {
+    if (n.checked !== false || n.indeterminate) {
       none = false;
     }
   }
 
+  return { all, none, allWithoutDisable, half: !all && !none };
+};
+
+const reInitChecked = function(node) {
+  const {all, none, half} = getChildState(node.childNodes);
+
   if (all) {
-    node.setChecked(true);
-  } else if (!all && !none) {
-    node.setChecked('half');
+    node.checked = true;
+    node.indeterminate = false;
+  } else if (half) {
+    node.checked = false;
+    node.indeterminate = true;
   } else if (none) {
-    node.setChecked(false);
+    node.checked = false;
+    node.indeterminate = false;
+  }
+
+  const parent = node.parent;
+  if (!parent || parent.level === 0) return;
+
+  if (!node.store.checkStrictly) {
+    reInitChecked(parent);
   }
 };
 
@@ -145,6 +162,10 @@ export default class Node {
     return null;
   }
 
+  get disabled() {
+    return getPropertyFromData(this, 'disabled');
+  }
+
   insertChild(child, index) {
     if (!child) throw new Error('insertChild error: child is required.');
 
@@ -260,16 +281,30 @@ export default class Node {
     this.isLeaf = false;
   }
 
-  setChecked(value, deep) {
+  setChecked(value, deep, recursion, passValue) {
     this.indeterminate = value === 'half';
     this.checked = value === true;
+    let { allWithoutDisable } = getChildState(this.childNodes);
+
+    if (this.childNodes.length && allWithoutDisable) {
+      this.checked = false;
+      value = false;
+    }
 
     const handleDescendants = () => {
       if (deep) {
         const childNodes = this.childNodes;
         for (let i = 0, j = childNodes.length; i < j; i++) {
           const child = childNodes[i];
-          child.setChecked(value !== false, deep);
+          passValue = passValue || value !== false;
+          const isCheck = child.disabled ? child.checked : passValue;
+          child.setChecked(isCheck, deep, true, passValue);
+        }
+        const { half, all } = getChildState(childNodes);
+        console.log(this.data.label, all);
+        if (!all) {
+          this.checked = all;
+          this.indeterminate = half;
         }
       }
     };
@@ -288,7 +323,7 @@ export default class Node {
     const parent = this.parent;
     if (!parent || parent.level === 0) return;
 
-    if (!this.store.checkStrictly) {
+    if (!this.store.checkStrictly && !recursion) {
       reInitChecked(parent);
     }
   }

+ 34 - 48
packages/tree/src/model/tree-store.js

@@ -1,4 +1,4 @@
-import Node from './node';
+import Node, { getChildState } from './node';
 import { getNodeKey } from './util';
 
 export default class TreeStore {
@@ -188,61 +188,47 @@ export default class TreeStore {
   }
 
   _setCheckedKeys(key, leafOnly = false, checkedKeys) {
-    const allNodes = this._getAllNodes();
-    allNodes.sort((a, b) => b.level - a.level);
+    let allNodes = this._getAllNodes().sort((a, b) => a.level - b.level);
 
     const keys = Object.keys(checkedKeys);
-    allNodes.forEach((node) => {
-      let checked = keys.indexOf(node.data[key] + '') > -1;
+    for (let node of allNodes) {
+      let checked = keys.indexOf(node.data[key].toString()) > -1;
+      if (!checked) {
+        node.setChecked(false, false);
+        continue;
+      }
 
-      if (!node.isLeaf) {
-        if (!this.checkStrictly) {
-          const childNodes = node.childNodes;
+      if (node.isLeaf || this.checkStrictly) {
+        node.setChecked(checked, false);
+        continue;
+      }
 
-          let all = true;
-          let none = true;
+      const { all, none, half } = getChildState(node.childNodes);
 
-          for (let i = 0, j = childNodes.length; i < j; i++) {
-            const child = childNodes[i];
-            if (child.checked !== true || child.indeterminate) {
-              all = false;
-            }
-            if (child.checked !== false || child.indeterminate) {
-              none = false;
-            }
-          }
-
-          if (all) {
-            node.setChecked(true, !this.checkStrictly);
-          } else if (!all && !none) {
-            checked = checked ? true : 'half';
-            node.setChecked(checked, !this.checkStrictly && checked === true);
-          } else if (none) {
-            node.setChecked(checked, !this.checkStrictly);
-          }
-        } else {
-          node.setChecked(checked, false);
-        }
-
-        if (leafOnly) {
-          node.setChecked(false, false);
-          const traverse = function(node) {
-            const childNodes = node.childNodes;
+      if (all) {
+        node.setChecked(true, !this.checkStrictly);
+      } else if (half) {
+        checked = checked ? true : 'half';
+        node.setChecked(checked, !this.checkStrictly && checked === true);
+      } else if (none) {
+        node.setChecked(checked, !this.checkStrictly);
+      }
 
-            childNodes.forEach((child) => {
-              if (!child.isLeaf) {
-                child.setChecked(false, false);
-              }
-              traverse(child);
-            });
-          };
+      if (leafOnly) {
+        node.setChecked(false, false);
+        const traverse = function(node) {
+          const childNodes = node.childNodes;
 
-          traverse(node);
-        }
-      } else {
-        node.setChecked(checked, false);
+          childNodes.forEach((child) => {
+            if (!child.isLeaf) {
+              child.setChecked(false, false);
+            }
+            traverse(child);
+          });
+        };
+        traverse(node);
       }
-    });
+    }
   }
 
   setCheckedNodes(array, leafOnly = false) {

+ 3 - 11
packages/tree/src/tree-node.vue

@@ -18,8 +18,8 @@
         v-if="showCheckbox"
         v-model="node.checked"
         :indeterminate="node.indeterminate"
-        @change="handleCheckChange"
-        @click.native.stop="handleUserClick">
+        :disabled="!!node.disabled"
+        @change="handleCheckChange">
       </el-checkbox>
       <span
         v-if="node.loading"
@@ -155,16 +155,8 @@
         }
       },
 
-      handleUserClick() {
-        if (this.node.indeterminate) {
-          this.node.setChecked(this.node.checked, !this.tree.checkStrictly);
-        }
-      },
-
       handleCheckChange(ev) {
-        if (!this.node.indeterminate) {
-          this.node.setChecked(ev.target.checked, !this.tree.checkStrictly);
-        }
+        this.node.setChecked(ev.target.checked, !this.tree.checkStrictly);
       },
 
       handleChildNodeExpand(nodeData, node, instance) {

+ 2 - 1
packages/tree/src/tree.vue

@@ -69,7 +69,8 @@
           return {
             children: 'children',
             label: 'label',
-            icon: 'icon'
+            icon: 'icon',
+            disabled: 'disabled'
           };
         }
       },

+ 65 - 0
test/unit/specs/tree.spec.js

@@ -61,6 +61,61 @@ describe('Tree', () => {
     }, options), true);
   };
 
+  const getDisableTreeVm = (props, options) => {
+    return createVue(Object.assign({
+      template: `
+        <el-tree ref="tree" :data="data" ${ props }></el-tree>
+        `,
+
+      data() {
+        return {
+          defaultExpandedKeys: [],
+          defaultCheckedKeys: [],
+          clickedNode: null,
+          count: 1,
+          data: [{
+            id: 1,
+            label: '一级 1',
+            children: [{
+              id: 11,
+              label: '二级 1-1',
+              children: [{
+                id: 111,
+                label: '三级 1-1',
+                disabled: true
+              }]
+            }]
+          }, {
+            id: 2,
+            label: '一级 2',
+            children: [{
+              id: 21,
+              label: '二级 2-1'
+            }, {
+              id: 22,
+              label: '二级 2-2'
+            }]
+          }, {
+            id: 3,
+            label: '一级 3',
+            children: [{
+              id: 31,
+              label: '二级 3-1'
+            }, {
+              id: 32,
+              label: '二级 3-2'
+            }]
+          }],
+          defaultProps: {
+            children: 'children',
+            label: 'label',
+            disabled: 'disabled'
+          }
+        };
+      }
+    }, options), true);
+  };
+
   const ALL_NODE_COUNT = 9;
 
   it('create', () => {
@@ -344,6 +399,16 @@ describe('Tree', () => {
     }, 0);
   });
 
+  it('set disabled checkbox', done => {
+    vm = getDisableTreeVm(':props="defaultProps" show-checkbox node-key="id"');
+    const node = document.querySelectorAll('.el-tree-node__content')[2];
+    const nodeCheckbox = node.querySelector('.el-checkbox input');
+    vm.$nextTick(() => {
+      expect(nodeCheckbox.disabled).to.equal(true);
+      done();
+    });
+  });
+
   it('check strictly', (done) => {
     vm = getTreeVm(':props="defaultProps" show-checkbox check-strictly');
     const tree = vm.$children[0];