981059f5ea59ed5b3f24edcca1f9283322ae457059450d64ed4162cdc416fe5a1070581f8336c5bdc768a6cd0f870a8967d173cc7453dd9b073f52845136fe 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import { isEqual, capitalize } from 'element-ui/src/utils/util';
  2. import { isDef } from 'element-ui/src/utils/shared';
  3. let uid = 0;
  4. export default class Node {
  5. constructor(data, config, parentNode) {
  6. this.data = data;
  7. this.config = config;
  8. this.parent = parentNode || null;
  9. this.level = !this.parent ? 1 : this.parent.level + 1;
  10. this.uid = uid++;
  11. this.initState();
  12. this.initChildren();
  13. }
  14. initState() {
  15. const { value: valueKey, label: labelKey } = this.config;
  16. this.value = this.data[valueKey];
  17. this.label = this.data[labelKey];
  18. this.pathNodes = this.calculatePathNodes();
  19. this.path = this.pathNodes.map(node => node.value);
  20. this.pathLabels = this.pathNodes.map(node => node.label);
  21. // lazy load
  22. this.loading = false;
  23. this.loaded = false;
  24. }
  25. initChildren() {
  26. const { config } = this;
  27. const childrenKey = config.children;
  28. const childrenData = this.data[childrenKey];
  29. this.hasChildren = Array.isArray(childrenData);
  30. this.children = (childrenData || []).map(child => new Node(child, config, this));
  31. }
  32. get isDisabled() {
  33. const { data, parent, config } = this;
  34. const disabledKey = config.disabled;
  35. const { checkStrictly } = config;
  36. return data[disabledKey] ||
  37. !checkStrictly && parent && parent.isDisabled;
  38. }
  39. get isLeaf() {
  40. const { data, loaded, hasChildren, children } = this;
  41. const { lazy, leaf: leafKey } = this.config;
  42. if (lazy) {
  43. const isLeaf = isDef(data[leafKey])
  44. ? data[leafKey]
  45. : (loaded ? !children.length : false);
  46. this.hasChildren = !isLeaf;
  47. return isLeaf;
  48. }
  49. return !hasChildren;
  50. }
  51. calculatePathNodes() {
  52. const nodes = [this];
  53. let parent = this.parent;
  54. while (parent) {
  55. nodes.unshift(parent);
  56. parent = parent.parent;
  57. }
  58. return nodes;
  59. }
  60. getPath() {
  61. return this.path;
  62. }
  63. getValue() {
  64. return this.value;
  65. }
  66. getValueByOption() {
  67. return this.config.emitPath
  68. ? this.getPath()
  69. : this.getValue();
  70. }
  71. getText(allLevels, separator) {
  72. return allLevels ? this.pathLabels.join(separator) : this.label;
  73. }
  74. isSameNode(checkedValue) {
  75. const value = this.getValueByOption();
  76. return this.config.multiple && Array.isArray(checkedValue)
  77. ? checkedValue.some(val => isEqual(val, value))
  78. : isEqual(checkedValue, value);
  79. }
  80. broadcast(event, ...args) {
  81. const handlerName = `onParent${capitalize(event)}`;
  82. this.children.forEach(child => {
  83. if (child) {
  84. // bottom up
  85. child.broadcast(event, ...args);
  86. child[handlerName] && child[handlerName](...args);
  87. }
  88. });
  89. }
  90. emit(event, ...args) {
  91. const { parent } = this;
  92. const handlerName = `onChild${capitalize(event)}`;
  93. if (parent) {
  94. parent[handlerName] && parent[handlerName](...args);
  95. parent.emit(event, ...args);
  96. }
  97. }
  98. onParentCheck(checked) {
  99. if (!this.isDisabled) {
  100. this.setCheckState(checked);
  101. }
  102. }
  103. onChildCheck() {
  104. const { children } = this;
  105. const validChildren = children.filter(child => !child.isDisabled);
  106. const checked = validChildren.length
  107. ? validChildren.every(child => child.checked)
  108. : false;
  109. this.setCheckState(checked);
  110. }
  111. setCheckState(checked) {
  112. const totalNum = this.children.length;
  113. const checkedNum = this.children.reduce((c, p) => {
  114. const num = p.checked ? 1 : (p.indeterminate ? 0.5 : 0);
  115. return c + num;
  116. }, 0);
  117. this.checked = checked;
  118. this.indeterminate = checkedNum !== totalNum && checkedNum > 0;
  119. }
  120. syncCheckState(checkedValue) {
  121. const value = this.getValueByOption();
  122. const checked = this.isSameNode(checkedValue, value);
  123. this.doCheck(checked);
  124. }
  125. doCheck(checked) {
  126. if (this.checked !== checked) {
  127. if (this.config.checkStrictly) {
  128. this.checked = checked;
  129. } else {
  130. // bottom up to unify the calculation of the indeterminate state
  131. this.broadcast('check', checked);
  132. this.setCheckState(checked);
  133. this.emit('check');
  134. }
  135. }
  136. }
  137. }