index.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // 参考https://www.npmjs.com/package/element-tree-line (主要是替换需要通过函数传参的方式去注册组件,并添加更好的类型支持,并移除this.$scopedSlots,在3.x中,将所有this.$scopedSlots替换为this.$slots)
  2. import { isFunction } from "@pureadmin/utils";
  3. import { h, defineComponent } from "vue";
  4. import type { PropType } from "vue";
  5. import "./index.scss";
  6. import type {
  7. TreeNode,
  8. TreeData,
  9. TreeNodeData
  10. } from "element-plus/es/components/tree-v2/src/types";
  11. /** 树形连接线组件 */
  12. export default defineComponent({
  13. name: "ReTreeLine",
  14. props: {
  15. node: {
  16. type: Object as PropType<TreeNode>,
  17. required: true
  18. },
  19. data: {
  20. type: Array as PropType<TreeNodeData>,
  21. default: () => {}
  22. },
  23. treeData: {
  24. type: Array as PropType<TreeData>,
  25. default: () => []
  26. },
  27. indent: {
  28. type: Number,
  29. default: 16
  30. },
  31. showLabelLine: {
  32. type: Boolean,
  33. default: true
  34. }
  35. },
  36. setup(_, context) {
  37. const { slots } = context;
  38. const getScopedSlot = slotName => {
  39. if (!slotName) {
  40. return null;
  41. }
  42. const slotNameSplits = slotName.split("||");
  43. let slot = null;
  44. for (let index = 0; index < slotNameSplits.length; index++) {
  45. const name = slotNameSplits[index];
  46. slot = (slots || {})[name];
  47. }
  48. return slot;
  49. };
  50. const getSlotValue = (slot, scopedData, defaultNode = null) => {
  51. if (isFunction(slot)) {
  52. return slot(scopedData) || defaultNode;
  53. }
  54. return slot || defaultNode;
  55. };
  56. return {
  57. getScopedSlot,
  58. getSlotValue
  59. };
  60. },
  61. render() {
  62. // 自定义整行节点label区域
  63. const scopeSlotDefault = this.getScopedSlot("default");
  64. // 显示横线时自定义节点label区域
  65. const labelSlot = this.getScopedSlot("node-label");
  66. // 显示横线时追加在横线右边的内容
  67. const afterLabelSlot = this.getScopedSlot("after-node-label");
  68. const labelNodes = scopeSlotDefault
  69. ? this.getSlotValue(scopeSlotDefault, {
  70. node: this.node,
  71. data: this.data
  72. })
  73. : [
  74. labelSlot
  75. ? this.getSlotValue(labelSlot, {
  76. node: this.node,
  77. data: this.data
  78. })
  79. : h("span", { class: "element-tree-node-label" }, this.node.label),
  80. this.showLabelLine
  81. ? h("span", {
  82. class: "element-tree-node-label-line"
  83. })
  84. : null,
  85. this.getSlotValue(afterLabelSlot, {
  86. node: this.node,
  87. data: this.data
  88. })
  89. ];
  90. // 取得每一层的当前节点是不是在当前层级列表的最后一个
  91. const lastnodeArr = [];
  92. let currentNode = this.node;
  93. while (currentNode) {
  94. let parentNode = currentNode.parent;
  95. // 兼容element-plus的 el-tree-v2 (Virtualized Tree 虚拟树)
  96. if (currentNode.level === 1 && !currentNode.parent) {
  97. // el-tree-v2的第一层node是没有parent的,必需 treeData 创建一个parent
  98. if (!this.treeData || !Array.isArray(this.treeData)) {
  99. throw Error(
  100. "if you using el-tree-v2 (Virtualized Tree) of element-plus,element-tree-line required data."
  101. );
  102. }
  103. parentNode = {
  104. children: Array.isArray(this.treeData)
  105. ? this.treeData.map(item => {
  106. return { ...item, key: item.id };
  107. })
  108. : [],
  109. level: 0,
  110. key: "node-0",
  111. parent: null
  112. };
  113. }
  114. if (parentNode) {
  115. // element-plus的 el-tree-v2 使用的是children和key, 其他使用的是 childNodes和id
  116. const index = (parentNode.children || parentNode.childNodes).findIndex(
  117. item => (item.key || item.id) === (currentNode.key || currentNode.id)
  118. );
  119. lastnodeArr.unshift(
  120. index === (parentNode.children || parentNode.childNodes).length - 1
  121. );
  122. }
  123. currentNode = parentNode;
  124. }
  125. const lineNodes = [];
  126. for (let i = 0; i < this.node.level; i++) {
  127. lineNodes.push(
  128. h("span", {
  129. class: {
  130. "element-tree-node-line-ver": true,
  131. "last-node-line": lastnodeArr[i] && this.node.level - 1 !== i,
  132. "last-node-isLeaf-line": lastnodeArr[i] && this.node.level - 1 === i
  133. },
  134. style: { left: this.indent * i + "px" }
  135. })
  136. );
  137. }
  138. return h(
  139. "span",
  140. {
  141. class: "element-tree-node-label-wrapper"
  142. },
  143. [labelNodes].concat(lineNodes).concat([
  144. h("span", {
  145. class: "element-tree-node-line-hor",
  146. style: {
  147. width: (this.node.isLeaf ? 24 : 8) + "px",
  148. left: (this.node.level - 1) * this.indent + "px"
  149. }
  150. })
  151. ])
  152. );
  153. }
  154. });