FolderTree.vue 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <template>
  2. <div class="tree-with-lines">
  3. <el-tree
  4. class="custom-tree"
  5. :data="props.treeData"
  6. :props="defaultProps"
  7. :expand-on-click-node="false"
  8. :default-expand-all="true"
  9. node-key="id"
  10. @node-click="handleNodeClick"
  11. :current-node-key="currentNodeKey"
  12. >
  13. <template #default="{ node, data }">
  14. <div class="custom-tree-node">
  15. <div class="node-content">
  16. <span class="node-icon">
  17. <el-icon v-if="data.type === 'folder'">
  18. <FolderOpened v-if="node.expanded" />
  19. <Folder v-else />
  20. </el-icon>
  21. <el-icon v-else><Document /></el-icon>
  22. </span>
  23. <span class="node-label">{{ node.label }}</span>
  24. </div>
  25. </div>
  26. </template>
  27. </el-tree>
  28. </div>
  29. </template>
  30. <script setup>
  31. const props = defineProps({
  32. treeData: {
  33. type: Array,
  34. },
  35. })
  36. watch(() => props.treeData, (newVal) => {
  37. if (newVal) {
  38. newTreeData.value = newVal;
  39. }
  40. });
  41. const emit = defineEmits(['send-nodeData']);
  42. const newTreeData = ref(props.treeData);
  43. const defaultProps = ref({
  44. children: 'children',
  45. label: 'label'
  46. })
  47. const handleNodeClick = (data, node) => {
  48. emit('send-nodeData', data,node);
  49. };
  50. </script>
  51. <style lang="scss"> /* 自定义树形控件样式 */
  52. .custom-tree {
  53. position: relative;
  54. /* 连接线样式 */
  55. .el-tree-node {
  56. position: relative;
  57. }
  58. .el-tree-node__children {
  59. position: relative;
  60. }
  61. /* 垂直连接线 */
  62. .el-tree-node__children::before {
  63. content: "";
  64. position: absolute;
  65. top: 0;
  66. bottom: 0;
  67. left: 15px;
  68. border-left: 1px dashed #c0c4cc;
  69. }
  70. /* 水平连接线 */
  71. .el-tree-node::before {
  72. content: "";
  73. position: absolute;
  74. top: 15px;
  75. left: 15px;
  76. width: 18px;
  77. height: 0;
  78. border-top: 1px dashed #c0c4cc;
  79. }
  80. /* 第一个节点的水平连接线调整 */
  81. .el-tree-node:first-child::before {
  82. top: 15px;
  83. }
  84. /* 最后一个节点的垂直连接线调整 */
  85. .el-tree-node:last-child::after {
  86. content: "";
  87. position: absolute;
  88. top: 15px;
  89. bottom: 0;
  90. left: 15px;
  91. width: 18px;
  92. border-left: 1px solid white;
  93. }
  94. /* 节点内容样式 */
  95. .custom-tree-node {
  96. flex: 1;
  97. display: flex;
  98. align-items: center;
  99. justify-content: space-between;
  100. font-size: 14px;
  101. padding-right: 8px;
  102. position: relative;
  103. }
  104. .node-content {
  105. display: flex;
  106. align-items: center;
  107. }
  108. .node-icon {
  109. margin-right: 6px;
  110. color: #e6a23c;
  111. }
  112. .node-label {
  113. padding: 2px 5px;
  114. border-radius: 3px;
  115. transition: background-color 0.3s;
  116. }
  117. .node-label:hover {
  118. background-color: #f0f9ff;
  119. }
  120. .node-actions {
  121. display: flex;
  122. gap: 8px;
  123. }
  124. .action-btn {
  125. padding: 2px 6px;
  126. font-size: 12px;
  127. border-radius: 3px;
  128. background-color: #f4f4f5;
  129. color: #606266;
  130. border: none;
  131. cursor: pointer;
  132. transition: all 0.3s;
  133. }
  134. .action-btn:hover {
  135. background-color: #409eff;
  136. color: white;
  137. }
  138. /* 选中的节点样式 */
  139. .el-tree-node.is-current > .el-tree-node__content .node-label {
  140. background-color: #ecf5ff;
  141. color: #409eff;
  142. }
  143. }
  144. /* 空数据提示 */
  145. .empty-tree {
  146. text-align: center;
  147. color: #909399;
  148. padding: 20px;
  149. }
  150. </style>