ly-tree-node.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <template>
  2. <view class="ly-tree-node" @tap.stop="handleClick" v-show="node.visible" :class="{ 'is-expanded': expanded,
  3. 'is-current': node.isCurrent,
  4. 'is-hidden': !node.visible,
  5. 'is-checked': !node.disabled && node.checked }"
  6. role="treeitem" name="LyTreeNode" ref="node">
  7. <view class="ly-tree-node__content" :style="{ 'padding-left': (node.level - 1) * indent + 'px' }">
  8. <text @tap.stop="handleExpandIconClick" :class="[{ 'is-leaf': node.isLeaf, expanded: !node.isLeaf && node.expanded }, 'ly-tree-node__expand-icon', iconClass ? iconClass : 'ly-iconfont ly-icon-caret-right']"></text>
  9. <text v-if="checkboxVisible" class="ly-checkbox" @tap.stop="handleCheckChange(!node.checked)">
  10. <text class="ly-checkbox__input" :class="[{'is-indeterminate': node.indeterminate, 'is-checked': node.checked, 'is-disabled': !!node.disabled}]">
  11. <text class="ly-checkbox__inner"></text>
  12. </text>
  13. </text>
  14. <text v-if="radioVisible" class="ly-radio" @click.stop="handleRadioChange(!node.checked)">
  15. <text class="ly-radio__input" :class="[{'is-checked': node.checked, 'is-disabled': !!node.disabled}]">
  16. <text class="ly-radio__inner"></text>
  17. </text>
  18. </text>
  19. <text v-if="node.loading" class="ly-tree-node__loading-icon ly-iconfont ly-icon-loading"></text>
  20. <template v-if="node.icon && node.icon.length > 0">
  21. <image v-if="node.icon.indexOf('/') !== -1" class="ly-tree-node__icon" :src="node.icon" mode="widthFix"></image>
  22. <text v-else class="ly-tree-node__icon" :class="node.icon"></text>
  23. </template>
  24. <text class="ly-tree-node__label">{{node.label}}</text>
  25. </view>
  26. <view v-if="!renderAfterExpand || childNodeRendered" v-show="expanded" class="ly-tree-node__children" role="group">
  27. <ly-tree-node v-for="cNodeId in node.childNodesId"
  28. :nodeId="cNodeId"
  29. :render-after-expand="renderAfterExpand"
  30. :show-checkbox="showCheckbox"
  31. :show-radio="showRadio"
  32. :check-only-leaf="checkOnlyLeaf"
  33. :key="getNodeKey(cNodeId)"
  34. :indent="indent"
  35. :icon-class="iconClass">
  36. </ly-tree-node>
  37. </view>
  38. </view>
  39. </template>
  40. <script>
  41. import {getNodeKey} from './tool/util.js';
  42. export default {
  43. name: 'LyTreeNode',
  44. componentName: 'LyTreeNode',
  45. props: {
  46. nodeId: [Number, String],
  47. renderAfterExpand: {
  48. type: Boolean,
  49. default: true
  50. },
  51. checkOnlyLeaf: {
  52. type: Boolean,
  53. default: false
  54. },
  55. showCheckbox: {
  56. type: Boolean,
  57. default: false
  58. },
  59. showRadio: {
  60. type: Boolean,
  61. default: false
  62. },
  63. indent: Number,
  64. iconClass: String
  65. },
  66. data() {
  67. return {
  68. node: null,
  69. expanded: false,
  70. childNodeRendered: false,
  71. oldChecked: null,
  72. oldIndeterminate: null
  73. };
  74. },
  75. inject: ['tree'],
  76. computed: {
  77. checkboxVisible() {
  78. if (this.checkOnlyLeaf) {
  79. return this.showCheckbox && this.node.isLeaf;
  80. }
  81. return this.showCheckbox;
  82. },
  83. radioVisible() {
  84. if (this.checkOnlyLeaf) {
  85. return this.showRadio && this.node.isLeaf;
  86. }
  87. return this.showRadio;
  88. }
  89. },
  90. watch: {
  91. 'node.indeterminate'(val) {
  92. this.handleSelectChange(this.node.checked, val);
  93. },
  94. 'node.checked'(val) {
  95. this.handleSelectChange(val, this.node.indeterminate);
  96. },
  97. 'node.expanded'(val) {
  98. this.$nextTick(() => this.expanded = val);
  99. if (val) {
  100. this.childNodeRendered = true;
  101. }
  102. }
  103. },
  104. methods: {
  105. getNodeKey(nodeId) {
  106. let node = this.tree.store.root.getChildNodes([nodeId])[0];
  107. return getNodeKey(this.tree.nodeKey, node.data);
  108. },
  109. handleSelectChange(checked, indeterminate) {
  110. if (this.oldChecked !== checked && this.oldIndeterminate !== indeterminate) {
  111. if (this.checkOnlyLeaf && !this.node.isLeaf) return;
  112. const allNodes = this.tree.store._getAllNodes();
  113. this.tree.$emit('check-change', {
  114. data: this.node.data,
  115. checked,
  116. indeterminate,
  117. checkedall: allNodes.every(item => item.checked)
  118. });
  119. }
  120. if (!this.expanded && this.tree.expandOnCheckNode && checked) {
  121. this.handleExpandIconClick();
  122. }
  123. this.oldChecked = checked;
  124. this.indeterminate = indeterminate;
  125. },
  126. handleClick() {
  127. this.tree.store.setCurrentNode(this.node);
  128. this.tree.$emit('current-change', {
  129. data: this.tree.store.currentNode ? this.tree.store.currentNode.data : null,
  130. currentNode: this.tree.store.currentNode
  131. });
  132. this.tree.currentNode = this.node;
  133. if (this.tree.expandOnClickNode) {
  134. this.handleExpandIconClick();
  135. }
  136. if (this.tree.checkOnClickNode && !this.node.disabled) {
  137. if (this.checkOnlyLeaf) {
  138. this.showCheckbox && this.node.isLeaf && this.handleCheckChange(!this.node.checked);
  139. this.showRadio && this.node.isLeaf && this.handleRadioChange(!this.node.checked);
  140. } else {
  141. this.showCheckbox && this.handleCheckChange(!this.node.checked);
  142. this.showRadio && this.handleRadioChange(!this.node.checked);
  143. }
  144. }
  145. this.tree.$emit('node-click', this.node);
  146. },
  147. handleExpandIconClick() {
  148. if (this.node.isLeaf) return;
  149. if (this.expanded) {
  150. this.tree.$emit('node-collapse', this.node);
  151. this.node.collapse();
  152. } else {
  153. this.node.expand();
  154. this.tree.$emit('node-expand', this.node);
  155. if (this.tree.accordion) {
  156. uni.$emit(`${this.tree.elId}-tree-node-expand`, this.node);
  157. }
  158. }
  159. },
  160. handleCheckChange(checked) {
  161. if (this.node.disabled) return;
  162. this.node.setChecked(checked, !(this.tree.checkStrictly || this.checkOnlyLeaf));
  163. this.$nextTick(() => {
  164. this.tree.$emit('check', {
  165. data: this.node.data,
  166. checkedNodes: this.tree.store.getCheckedNodes(),
  167. checkedKeys: this.tree.store.getCheckedKeys(),
  168. halfCheckedNodes: this.tree.store.getHalfCheckedNodes(),
  169. halfCheckedKeys: this.tree.store.getHalfCheckedKeys()
  170. });
  171. });
  172. },
  173. handleRadioChange(checked) {
  174. if (this.node.disabled) return;
  175. this.node.setRadioChecked(checked);
  176. this.tree.$emit('radio-change', {
  177. data: this.node.data,
  178. checked
  179. });
  180. }
  181. },
  182. created() {
  183. if (!this.tree) {
  184. throw new Error('Can not find node\'s tree.');
  185. }
  186. this.node = this.tree.store.nodesMap[this.nodeId];
  187. if (this.node.expanded) {
  188. this.expanded = true;
  189. this.childNodeRendered = true;
  190. }
  191. const props = this.tree.props || {};
  192. const childrenKey = props['children'] || 'children';
  193. this.$watch(`node.data.${childrenKey}`, () => {
  194. this.node.updateChildren();
  195. });
  196. if (this.tree.accordion) {
  197. uni.$on(`${this.tree.elId}-tree-node-expand`, node => {
  198. if (this.node.id !== node.id && this.node.level === node.level) {
  199. this.node.collapse();
  200. }
  201. });
  202. }
  203. },
  204. beforeDestroy() {
  205. this.$parent = null;
  206. }
  207. };
  208. </script>