index.ts 4.7 KB

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