ly-tree.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. <template>
  2. <view>
  3. <view v-if="showLoading"
  4. class="ly-tree_loading">
  5. <image class="ly-tree_loading-icon"
  6. src="data:image/gif;base64,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">
  7. </image>
  8. <text class="ly-tree_loading-text">正在加载</text>
  9. </view>
  10. <template v-else>
  11. <text v-if="isEmpty" class="ly-tree_empty">{{emptyText}}</text>
  12. <view :key="updateKey" class="ly-tree" role="tree" name="LyTreeExpand">
  13. <ly-tree-node v-for="nodeId in childNodesId"
  14. :nodeId="nodeId"
  15. :render-after-expand="renderAfterExpand"
  16. :show-checkbox="showCheckbox"
  17. :show-radio="showRadio"
  18. :check-only-leaf="checkOnlyLeaf"
  19. :key="getNodeKey(nodeId)"
  20. :indent="indent"
  21. :icon-class="iconClass" />
  22. </view>
  23. </template>
  24. </view>
  25. </template>
  26. <script>
  27. import TreeStore from './model/tree-store.js';
  28. import {getNodeKey} from './tool/util.js';
  29. import LyTreeNode from './ly-tree-node.vue';
  30. export default {
  31. name: 'LyTree',
  32. componentName: 'LyTree',
  33. components: {
  34. LyTreeNode
  35. },
  36. data() {
  37. return {
  38. updateKey: new Date().getTime(), // 数据更新的时候,重新渲染树
  39. elId: `ly_${Math.ceil(Math.random() * 10e5).toString(36)}`,
  40. store: {
  41. ready: false
  42. },
  43. currentNode: null,
  44. childNodesId: []
  45. };
  46. },
  47. provide() {
  48. return {
  49. tree: this
  50. }
  51. },
  52. props: {
  53. // 展示数据
  54. treeData: Array,
  55. // 自主控制loading加载,避免数据还没获取到的空档出现“暂无数据”字样
  56. ready: {
  57. type: Boolean,
  58. default: true
  59. },
  60. // 内容为空的时候展示的文本
  61. emptyText: {
  62. type: String,
  63. default: '暂无数据'
  64. },
  65. // 是否在第一次展开某个树节点后才渲染其子节点
  66. renderAfterExpand: {
  67. type: Boolean,
  68. default: true
  69. },
  70. // 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
  71. nodeKey: String,
  72. // 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false
  73. checkStrictly: Boolean,
  74. // 是否默认展开所有节点
  75. defaultExpandAll: Boolean,
  76. // 切换全部展开、全部折叠
  77. toggleExpendAll: Boolean,
  78. // 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点
  79. expandOnClickNode: {
  80. type: Boolean,
  81. default: true
  82. },
  83. // 选中的时候展开节点
  84. expandOnCheckNode: {
  85. type: Boolean,
  86. default: true
  87. },
  88. // 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点
  89. checkOnClickNode: Boolean,
  90. checkDescendants: {
  91. type: Boolean,
  92. default: false
  93. },
  94. // 展开子节点的时候是否自动展开父节点
  95. autoExpandParent: {
  96. type: Boolean,
  97. default: true
  98. },
  99. // 默认勾选的节点的 key 的数组
  100. defaultCheckedKeys: Array,
  101. // 默认展开的节点的 key 的数组
  102. defaultExpandedKeys: Array,
  103. // 是否展开当前节点的父节点
  104. expandCurrentNodeParent: Boolean,
  105. // 当前选中的节点
  106. currentNodeKey: [String, Number],
  107. // 是否最后一层叶子节点才显示单选/多选框
  108. checkOnlyLeaf: {
  109. type: Boolean,
  110. default: false
  111. },
  112. // 节点是否可被选择
  113. showCheckbox: {
  114. type: Boolean,
  115. default: false
  116. },
  117. // 节点单选
  118. showRadio: {
  119. type: Boolean,
  120. default: false
  121. },
  122. // 配置选项
  123. props: {
  124. type: [Object, Function],
  125. default () {
  126. return {
  127. children: 'children', // 指定子树为节点对象的某个属性值
  128. label: 'label', // 指定节点标签为节点对象的某个属性值
  129. disabled: 'disabled' // 指定节点选择框是否禁用为节点对象的某个属性值
  130. };
  131. }
  132. },
  133. // 是否懒加载子节点,需与 load 方法结合使用
  134. lazy: {
  135. type: Boolean,
  136. default: false
  137. },
  138. // 是否高亮当前选中节点,默认值是 false
  139. highlightCurrent: Boolean,
  140. // 加载子树数据的方法,仅当 lazy 属性为true 时生效
  141. load: Function,
  142. // 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏
  143. filterNodeMethod: Function,
  144. // 搜索时是否展示匹配项的所有子节点
  145. childVisibleForFilterNode: {
  146. type: Boolean,
  147. default: false
  148. },
  149. // 是否每次只打开一个同级树节点展开
  150. accordion: Boolean,
  151. // 相邻级节点间的水平缩进,单位为像素
  152. indent: {
  153. type: Number,
  154. default: 18
  155. },
  156. // 自定义树节点的展开图标
  157. iconClass: String,
  158. // 是否显示节点图标,如果配置为true,需要配置props中对应的图标属性名称
  159. showNodeIcon: {
  160. type: Boolean,
  161. default: false
  162. },
  163. // 当节点图标显示出错时,显示的默认图标
  164. defaultNodeIcon: {
  165. type: String,
  166. default: 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/doc/github.svg'
  167. },
  168. // 如果数据量较大,建议不要在node节点中添加parent属性,会造成性能损耗
  169. isInjectParentInNode: {
  170. type: Boolean,
  171. default: false
  172. }
  173. },
  174. computed: {
  175. isEmpty() {
  176. if (this.store.root) {
  177. const childNodes = this.store.root.getChildNodes(this.childNodesId);
  178. return !childNodes || childNodes.length === 0 || childNodes.every(({visible}) => !visible);
  179. }
  180. return true;
  181. },
  182. showLoading() {
  183. return !(this.store.ready && this.ready);
  184. }
  185. },
  186. watch: {
  187. toggleExpendAll(newVal) {
  188. this.store.toggleExpendAll(newVal);
  189. },
  190. defaultCheckedKeys(newVal) {
  191. this.store.setDefaultCheckedKey(newVal);
  192. },
  193. defaultExpandedKeys(newVal) {
  194. this.store.defaultExpandedKeys = newVal;
  195. this.store.setDefaultExpandedKeys(newVal);
  196. },
  197. checkStrictly(newVal) {
  198. this.store.checkStrictly = newVal || this.checkOnlyLeaf;
  199. },
  200. 'store.root.childNodesId'(newVal) {
  201. this.childNodesId = newVal;
  202. },
  203. childNodesId(){
  204. this.$nextTick(() => {
  205. this.$emit('ly-tree-render-completed');
  206. });
  207. },
  208. treeData: {
  209. handler(newVal) {
  210. this.updateKey = new Date().getTime();
  211. this.store.setData(newVal);
  212. },
  213. deep: true
  214. }
  215. },
  216. methods: {
  217. /*
  218. * @description 对树节点进行筛选操作
  219. * @method filter
  220. * @param {all} value 在 filter-node-method 中作为第一个参数
  221. * @param {Object} data 搜索指定节点的节点数据,不传代表搜索所有节点,假如要搜索A节点下面的数据,那么nodeData代表treeData中A节点的数据
  222. */
  223. filter(value, data) {
  224. if (!this.filterNodeMethod) throw new Error('[Tree] filterNodeMethod is required when filter');
  225. this.store.filter(value, data);
  226. },
  227. /*
  228. * @description 获取节点的唯一标识符
  229. * @method getNodeKey
  230. * @param {String, Number} nodeId
  231. * @return {String, Number} 匹配到的数据中的某一项数据
  232. */
  233. getNodeKey(nodeId) {
  234. let node = this.store.root.getChildNodes([nodeId])[0];
  235. return getNodeKey(this.nodeKey, node.data);
  236. },
  237. /*
  238. * @description 获取节点路径
  239. * @method getNodePath
  240. * @param {Object} data 节点数据
  241. * @return {Array} 路径数组
  242. */
  243. getNodePath(data) {
  244. return this.store.getNodePath(data);
  245. },
  246. /*
  247. * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点所组成的数组
  248. * @method getCheckedNodes
  249. * @param {Boolean} leafOnly 是否只是叶子节点,默认false
  250. * @param {Boolean} includeHalfChecked 是否包含半选节点,默认false
  251. * @return {Array} 目前被选中的节点所组成的数组
  252. */
  253. getCheckedNodes(leafOnly, includeHalfChecked) {
  254. return this.store.getCheckedNodes(leafOnly, includeHalfChecked);
  255. },
  256. /*
  257. * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点的 key 所组成的数组
  258. * @method getCheckedKeys
  259. * @param {Boolean} leafOnly 是否只是叶子节点,默认false,若为 true 则仅返回被选中的叶子节点的 keys
  260. * @param {Boolean} includeHalfChecked 是否返回indeterminate为true的节点,默认false
  261. * @return {Array} 目前被选中的节点所组成的数组
  262. */
  263. getCheckedKeys(leafOnly, includeHalfChecked) {
  264. return this.store.getCheckedKeys(leafOnly, includeHalfChecked);
  265. },
  266. /*
  267. * @description 获取当前被选中节点的 data,若没有节点被选中则返回 null
  268. * @method getCurrentNode
  269. * @return {Object} 当前被选中节点的 data,若没有节点被选中则返回 null
  270. */
  271. getCurrentNode() {
  272. const currentNode = this.store.getCurrentNode();
  273. return currentNode ? currentNode.data : null;
  274. },
  275. /*
  276. * @description 获取当前被选中节点的 key,若没有节点被选中则返回 null
  277. * @method getCurrentKey
  278. * @return {all} 当前被选中节点的 key, 若没有节点被选中则返回 null
  279. */
  280. getCurrentKey() {
  281. const currentNode = this.getCurrentNode();
  282. return currentNode ? currentNode[this.nodeKey] : null;
  283. },
  284. /*
  285. * @description 设置全选/取消全选
  286. * @method setCheckAll
  287. * @param {Boolean} isCheckAll 选中状态,默认为true
  288. */
  289. setCheckAll(isCheckAll = true) {
  290. if (this.showRadio) throw new Error('You set the "show-radio" property, so you cannot select all nodes');
  291. if (!this.showCheckbox) console.warn('You have not set the property "show-checkbox". Please check your settings');
  292. this.store.setCheckAll(isCheckAll);
  293. },
  294. /*
  295. * @description 设置目前勾选的节点
  296. * @method setCheckedNodes
  297. * @param {Array} nodes 接收勾选节点数据的数组
  298. * @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false
  299. */
  300. setCheckedNodes(nodes, leafOnly) {
  301. this.store.setCheckedNodes(nodes, leafOnly);
  302. },
  303. /*
  304. * @description 通过 keys 设置目前勾选的节点
  305. * @method setCheckedKeys
  306. * @param {Array} keys 勾选节点的 key 的数组
  307. * @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false
  308. */
  309. setCheckedKeys(keys, leafOnly) {
  310. if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCheckedKeys');
  311. this.store.setCheckedKeys(keys, leafOnly);
  312. },
  313. /*
  314. * @description 通过 key / data 设置某个节点的勾选状态
  315. * @method setChecked
  316. * @param {all} data 勾选节点的 key 或者 data
  317. * @param {Boolean} checked 节点是否选中
  318. * @param {Boolean} deep 是否设置子节点 ,默认为 false
  319. */
  320. setChecked(data, checked, deep) {
  321. this.store.setChecked(data, checked, deep);
  322. },
  323. /*
  324. * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点所组成的数组
  325. * @method getHalfCheckedNodes
  326. * @return {Array} 目前半选中的节点所组成的数组
  327. */
  328. getHalfCheckedNodes() {
  329. return this.store.getHalfCheckedNodes();
  330. },
  331. /*
  332. * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点的 key 所组成的数组
  333. * @method getHalfCheckedKeys
  334. * @return {Array} 目前半选中的节点的 key 所组成的数组
  335. */
  336. getHalfCheckedKeys() {
  337. return this.store.getHalfCheckedKeys();
  338. },
  339. /*
  340. * @description 通过 node 设置某个节点的当前选中状态
  341. * @method setCurrentNode
  342. * @param {Object} node 待被选节点的 node
  343. */
  344. setCurrentNode(node) {
  345. if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentNode');
  346. this.store.setUserCurrentNode(node);
  347. },
  348. /*
  349. * @description 通过 key 设置某个节点的当前选中状态
  350. * @method setCurrentKey
  351. * @param {all} key 待被选节点的 key,若为 null 则取消当前高亮的节点
  352. */
  353. setCurrentKey(key) {
  354. if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentKey');
  355. this.store.setCurrentNodeKey(key);
  356. },
  357. /*
  358. * @description 根据 data 或者 key 拿到 Tree 组件中的 node
  359. * @method getNode
  360. * @param {all} data 要获得 node 的 key 或者 data
  361. */
  362. getNode(data) {
  363. return this.store.getNode(data);
  364. },
  365. /*
  366. * @description 删除 Tree 中的一个节点
  367. * @method remove
  368. * @param {all} data 要删除的节点的 data 或者 node
  369. */
  370. remove(data) {
  371. this.store.remove(data);
  372. },
  373. /*
  374. * @description 为 Tree 中的一个节点追加一个子节点
  375. * @method append
  376. * @param {Object} data 要追加的子节点的 data
  377. * @param {Object} parentNode 子节点的 parent 的 data、key 或者 node
  378. */
  379. append(data, parentNode) {
  380. this.store.append(data, parentNode);
  381. },
  382. /*
  383. * @description 为 Tree 的一个节点的前面增加一个节点
  384. * @method insertBefore
  385. * @param {Object} data 要增加的节点的 data
  386. * @param {all} refNode 要增加的节点的后一个节点的 data、key 或者 node
  387. */
  388. insertBefore(data, refNode) {
  389. this.store.insertBefore(data, refNode);
  390. },
  391. /*
  392. * @description 为 Tree 的一个节点的后面增加一个节点
  393. * @method insertAfter
  394. * @param {Object} data 要增加的节点的 data
  395. * @param {all} refNode 要增加的节点的前一个节点的 data、key 或者 node
  396. */
  397. insertAfter(data, refNode) {
  398. this.store.insertAfter(data, refNode);
  399. },
  400. /*
  401. * @description 通过 keys 设置节点子元素
  402. * @method updateKeyChildren
  403. * @param {String, Number} key 节点 key
  404. * @param {Object} data 节点数据的数组
  405. */
  406. updateKeyChildren(key, data) {
  407. if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in updateKeyChild');
  408. this.store.updateChildren(key, data);
  409. }
  410. },
  411. created() {
  412. this.isTree = true;
  413. let props = this.props;
  414. if (typeof this.props === 'function') props = this.props();
  415. if (typeof props !== 'object') throw new Error('props must be of object type.');
  416. this.store = new TreeStore({
  417. key: this.nodeKey,
  418. data: this.treeData,
  419. lazy: this.lazy,
  420. props: props,
  421. load: this.load,
  422. showCheckbox: this.showCheckbox,
  423. showRadio: this.showRadio,
  424. currentNodeKey: this.currentNodeKey,
  425. checkStrictly: this.checkStrictly || this.checkOnlyLeaf,
  426. checkDescendants: this.checkDescendants,
  427. expandOnCheckNode: this.expandOnCheckNode,
  428. defaultCheckedKeys: this.defaultCheckedKeys,
  429. defaultExpandedKeys: this.defaultExpandedKeys,
  430. expandCurrentNodeParent: this.expandCurrentNodeParent,
  431. autoExpandParent: this.autoExpandParent,
  432. defaultExpandAll: this.defaultExpandAll,
  433. filterNodeMethod: this.filterNodeMethod,
  434. childVisibleForFilterNode: this.childVisibleForFilterNode,
  435. showNodeIcon: this.showNodeIcon,
  436. isInjectParentInNode: this.isInjectParentInNode
  437. });
  438. this.childNodesId = this.store.root.childNodesId;
  439. },
  440. beforeDestroy() {
  441. if (this.accordion) {
  442. uni.$off(`${this.elId}-tree-node-expand`)
  443. }
  444. }
  445. };
  446. </script>
  447. <style>
  448. .ly-tree {
  449. position: relative;
  450. background-color: #FFF;
  451. padding: 30rpx;
  452. font-size: 30rpx;
  453. line-height: 2em;
  454. }
  455. .ly-tree_loading {
  456. /* #ifndef APP-NVUE */
  457. display: flex;
  458. /* #endif */
  459. text-align: center;
  460. margin-top: 100rpx;
  461. align-items: center;
  462. }
  463. .ly-tree_loading-icon {
  464. width: 50rpx;
  465. height: 50rpx;
  466. }
  467. .ly-tree_loading-text {
  468. font-size: 30rpx;
  469. color: #606266;
  470. }
  471. .ly-tree_empty {
  472. text-align: center;
  473. margin-top: 100rpx;
  474. font-size: 30rpx;
  475. color: #606266;
  476. }
  477. </style>