当前位置:   article > 正文

如何对ElementUI、ElementPlus中的Tree树组件进行美化,如增加辅助线、替换展开收起图标、点击节点后文字高亮等效果?本文给你答案!_element ui 树形控件样式美化

element ui 树形控件样式美化

ElementUI、ElementPlus树组件功能很不错,但是官方的树形组件没有显示线条,感觉稍微不够大气。于是网上查了一些资料,找了很多也感觉也不够完美,最后找到一个还不错的实现方案,并且再美化改进一下,分享给大家。

一、基于Vue2+ElementUI的例子

(1)示例代码

  1. <template>
  2. <div class="tree-container">
  3. <div style="padding: 20px;">
  4. <el-checkbox v-model="isSelectAll" @change="handleCheckedAllTreeNodeChange">全选/全不选</el-checkbox>
  5. <el-button size="mini" style="margin-left: 20px;" @click="handleGetCheckedNodesAndKeys(true)">获取已勾选节点</el-button>
  6. </div>
  7. <el-tree
  8. ref="treeRef"
  9. show-checkbox
  10. icon-class="el-icon-arrow-right"
  11. :node-key="'id'"
  12. :data="treeList"
  13. :props="defaultProps"
  14. :default-expand-all="true"
  15. :expand-on-click-node="false"
  16. :default-checked-keys="[5]"
  17. >
  18. <span slot-scope="{ data }">
  19. <template v-if="data.children">
  20. <div v-if="data.children.length > 0">
  21. <!-- <i class="el-icon-folder" :style="'font-size: 14px; padding: 0 5px 0 5px'"/> -->
  22. <i :style="'font-size: 13px; padding: 0 5px 0 5px'"><svg viewBox="64 64 896 896" data-icon="apartment" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M908 640H804V488c0-4.4-3.6-8-8-8H548v-96h108c8.8 0 16-7.2 16-16V80c0-8.8-7.2-16-16-16H368c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h108v96H228c-4.4 0-8 3.6-8 8v152H116c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V656c0-8.8-7.2-16-16-16H292v-88h440v88H620c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V656c0-8.8-7.2-16-16-16zm-564 76v168H176V716h168zm84-408V140h168v168H428zm420 576H680V716h168v168z"></path></svg></i>
  23. <span>{{ data.id + ' - ' + data.label }}</span>
  24. </div>
  25. <div v-else>
  26. <i class="leaf-node-line"></i>
  27. <!-- <i class="el-icon-folder" :style="'padding: 0 5px 0 5px'"/> -->
  28. <i :style="'font-size: 13px; padding: 0 5px 0 5px'"><svg viewBox="64 64 896 896" data-icon="apartment" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M908 640H804V488c0-4.4-3.6-8-8-8H548v-96h108c8.8 0 16-7.2 16-16V80c0-8.8-7.2-16-16-16H368c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h108v96H228c-4.4 0-8 3.6-8 8v152H116c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V656c0-8.8-7.2-16-16-16H292v-88h440v88H620c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V656c0-8.8-7.2-16-16-16zm-564 76v168H176V716h168zm84-408V140h168v168H428zm420 576H680V716h168v168z"></path></svg></i>
  29. <span>{{ data.id + ' - ' + data.label }}</span>
  30. </div>
  31. </template>
  32. <template v-else>
  33. <div style="margin-left: 0px;">
  34. <i class="leaf-node-line"></i>
  35. <!-- <i class="el-icon-document" :style="'padding: 0 5px 0 5px'"></i> -->
  36. <i :style="'font-size: 13px; padding: 0 5px 0 5px'"><svg viewBox="64 64 896 896" data-icon="deployment-unit" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M888.3 693.2c-42.5-24.6-94.3-18-129.2 12.8l-53-30.7V523.6c0-15.7-8.4-30.3-22-38.1l-136-78.3v-67.1c44.2-15 76-56.8 76-106.1 0-61.9-50.1-112-112-112s-112 50.1-112 112c0 49.3 31.8 91.1 76 106.1v67.1l-136 78.3c-13.6 7.8-22 22.4-22 38.1v151.6l-53 30.7c-34.9-30.8-86.8-37.4-129.2-12.8-53.5 31-71.7 99.4-41 152.9 30.8 53.5 98.9 71.9 152.2 41 42.5-24.6 62.7-73 53.6-118.8l48.7-28.3 140.6 81c6.8 3.9 14.4 5.9 22 5.9s15.2-2 22-5.9L674.5 740l48.7 28.3c-9.1 45.7 11.2 94.2 53.6 118.8 53.3 30.9 121.5 12.6 152.2-41 30.8-53.6 12.6-122-40.7-152.9zm-673 138.4a47.6 47.6 0 0 1-65.2-17.6c-13.2-22.9-5.4-52.3 17.5-65.5a47.6 47.6 0 0 1 65.2 17.6c13.2 22.9 5.4 52.3-17.5 65.5zM522 463.8zM464 234a48.01 48.01 0 0 1 96 0 48.01 48.01 0 0 1-96 0zm170 446.2l-122 70.3-122-70.3V539.8l122-70.3 122 70.3v140.4zm239.9 133.9c-13.2 22.9-42.4 30.8-65.2 17.6-22.8-13.2-30.7-42.6-17.5-65.5s42.4-30.8 65.2-17.6c22.9 13.2 30.7 42.5 17.5 65.5z"></path></svg></i>
  37. <span>{{ data.id + ' - ' + data.label }}</span>
  38. </div>
  39. </template>
  40. </span>
  41. </el-tree>
  42. </div>
  43. </template>
  44. <script>
  45. export default {
  46. data() {
  47. return {
  48. // 树列表
  49. treeList: [
  50. {
  51. id: 1,
  52. label: "香烟 WiFi 啤酒",
  53. children: [
  54. {
  55. id: 3,
  56. label: '香烟',
  57. children: [
  58. {
  59. id: 4,
  60. label: '煊赫门',
  61. },
  62. {
  63. id: 5,
  64. label: 'ESSE双爆珠',
  65. disabled: true,
  66. },
  67. ],
  68. },
  69. {
  70. id: 2,
  71. label: '后端开发技术',
  72. disabled: true,
  73. children: [
  74. {
  75. id: 6,
  76. label: 'Java编程技术',
  77. children: [],
  78. },
  79. {
  80. id: 7,
  81. label: '数据库',
  82. disabled: true,
  83. children: [
  84. {
  85. id: 8,
  86. label: '关系型数据库',
  87. children: [
  88. {
  89. id: 9,
  90. label: 'MySQL',
  91. },
  92. {
  93. id: 10,
  94. label: 'Oracle',
  95. disabled: true,
  96. },
  97. ],
  98. },
  99. {
  100. id: 11,
  101. label: '非关系型数据库',
  102. children: [
  103. {
  104. id: 12,
  105. label: 'Redis',
  106. },
  107. {
  108. id: 13,
  109. label: 'Elasticsearch',
  110. disabled: true,
  111. },
  112. ],
  113. }
  114. ],
  115. },
  116. ],
  117. },
  118. ],
  119. },
  120. ],
  121. // 对象关系映射
  122. defaultProps: {
  123. label: 'label',
  124. children: 'children',
  125. },
  126. // 是否全选所有节点标志
  127. isSelectAll: false,
  128. };
  129. },
  130. methods: {
  131. /**
  132. * 是否全选所有节点
  133. */
  134. handleCheckedAllTreeNodeChange() {
  135. if (this.isSelectAll) {
  136. // 深度遍历将子节点全选中
  137. for (let i = 0; i < this.treeList.length; i++) {
  138. this.$refs.treeRef.setChecked(this.treeList[i], true, true);
  139. }
  140. } else {
  141. // 全部不选中
  142. this.$refs.treeRef.setCheckedNodes([]);
  143. }
  144. const leafOnly = true;
  145. this.handleGetCheckedNodesAndKeys(leafOnly);
  146. },
  147. /**
  148. * 获取当前已被选中的节点集合
  149. */
  150. handleGetCheckedNodesAndKeys(leafOnly) {
  151. console.log('getCheckedNodes =>', this.$refs.treeRef.getCheckedNodes(leafOnly));
  152. console.log('getCheckedKeys =>', this.$refs.treeRef.getCheckedKeys(leafOnly));
  153. }
  154. },
  155. }
  156. </script>
  157. <style lang="less" scoped>
  158. // 设置树形组件节点的定位和左内边距
  159. .tree-container /deep/ .el-tree-node {
  160. position: relative;
  161. padding-left: 13px;
  162. }
  163. // 设置树形组件节点的 before 伪类的样式
  164. .tree-container /deep/ .el-tree-node:before {
  165. width: 1px;
  166. height: 100%;
  167. content: '';
  168. position: absolute;
  169. top: -38px;
  170. bottom: 0;
  171. left: 0;
  172. right: auto;
  173. border-width: 1px;
  174. border-left: 1px solid #b8b9bb;
  175. }
  176. // 设置树形组件节点的 after 伪类的样式
  177. .tree-container /deep/ .el-tree-node:after {
  178. width: 13px;
  179. height: 13px;
  180. content: '';
  181. position: absolute;
  182. left: 0;
  183. right: auto;
  184. top: 12px;
  185. bottom: auto;
  186. border-width: 1px;
  187. border-top: 1px solid #b8b9bb;
  188. }
  189. // 设置树形组件首节点的左边框不显示
  190. .tree-container /deep/ .el-tree > .el-tree-node:before {
  191. border-left: none;
  192. }
  193. // 设置树形组件首节点的顶部边框不显示
  194. .tree-container /deep/ .el-tree > .el-tree-node:after {
  195. border-top: none;
  196. }
  197. // 设置树形组件末节点的 before 伪类的高度
  198. .tree-container /deep/ .el-tree .el-tree-node:last-child:before {
  199. height: 50px;
  200. }
  201. // 设置树形组件节点字体大小、以及取消左内边距
  202. .tree-container /deep/ .el-tree .el-tree-node__content {
  203. color: #000;
  204. font-size: 14px;
  205. padding-left: 0 !important;
  206. }
  207. // 设置树形组件孩子节点左内边距
  208. .tree-container /deep/ .el-tree .el-tree-node__children {
  209. padding-left: 11.5px;
  210. }
  211. // 设置树形组件复选框左右外边距
  212. .tree-container /deep/ .el-tree .el-tree-node__content > label.el-checkbox {
  213. margin: 0 5px 0 5px !important;
  214. }
  215. // 设置树形组件展开图标定位、图层、内边距
  216. .tree-container /deep/ .el-tree .el-tree-node__expand-icon {
  217. position: relative;
  218. z-index: 99;
  219. }
  220. // 设置树形组件叶子节点的默认图标不显示
  221. .tree-container /deep/ .el-tree .el-tree-node__expand-icon.is-leaf {
  222. display: none;
  223. }
  224. // 设置树形组件叶子节点的横线
  225. .tree-container /deep/ .el-tree .leaf-node-line {
  226. width: 23px;
  227. height: 13px;
  228. content: '';
  229. position: absolute;
  230. left: 13px;
  231. right: auto;
  232. top: 12px;
  233. bottom: auto;
  234. border-width: 1px;
  235. border-top: 1px solid #b8b9bb;
  236. }
  237. // 设置树形组件有叶子节点的左外边距
  238. .tree-container /deep/ .el-tree .el-tree-node__content:has(.is-leaf){
  239. // color: aqua;
  240. margin-left: 24px !important;
  241. }
  242. </style>

(2)效果如下~

二、基于Vue3+ElementPlus的例子

1.普通示例

(1)示例代码

  1. <template>
  2. <div class="element-plus-tree">
  3. <el-tree
  4. :data="treeData"
  5. :props="defaultProps"
  6. :show-checkbox="false"
  7. :default-expand-all="true"
  8. :highlight-current="true"
  9. :expand-on-click-node="false"
  10. :indent="22"
  11. >
  12. <template #default="{ node, data }">
  13. <span v-if="!node.isLeaf" style="display: flex; align-items: center;">
  14. <el-icon v-if="node.expanded" style="margin: 0 6px 0 2px;" size="16"><FolderOpened /></el-icon>
  15. <el-icon v-else style="margin: 0 6px 0 2px;" size="16"><Folder /></el-icon>
  16. <small @click="handleNodeClick(node, data)">{{ node.label }}</small>
  17. </span>
  18. <span v-else style="display: flex; align-items: center;">
  19. <el-icon style="margin: 0 6px 0 2px;" size="16"><Document /></el-icon>
  20. <small @click="handleNodeClick(node, data)">{{ node.label }}</small>
  21. </span>
  22. </template>
  23. </el-tree>
  24. </div>
  25. </template>
  26. <script setup>
  27. import { onMounted, ref, getCurrentInstance } from 'vue'
  28. // 代理对象
  29. const { proxy } = getCurrentInstance()
  30. // 树型数据
  31. const treeData = ref(
  32. [
  33. {
  34. label: '香烟 WiFi 啤酒',
  35. expanded: true,
  36. children: [
  37. {
  38. label: '香烟',
  39. children: [
  40. {
  41. label: '煊赫门'
  42. },
  43. {
  44. label: 'ESSE双爆珠'
  45. }
  46. ],
  47. },
  48. {
  49. label: '后端开发技术',
  50. children: [
  51. {
  52. label: 'Java编程技术'
  53. },
  54. {
  55. label: 'Python编程技术'
  56. }
  57. ],
  58. },
  59. {
  60. label: '数据库',
  61. children: [
  62. {
  63. label: '关系型数据库',
  64. children: [
  65. {
  66. label: 'MySQL'
  67. },
  68. {
  69. label: 'Oracle'
  70. }
  71. ],
  72. },
  73. {
  74. label: '非关系型数据库',
  75. children: [
  76. {
  77. label: 'Redis'
  78. },
  79. {
  80. label: 'Elasticsearch'
  81. }
  82. ],
  83. }
  84. ],
  85. },
  86. {
  87. label: 'AI人工智能'
  88. },
  89. ],
  90. },
  91. {
  92. label: '火腿 iPad 泡面'
  93. },
  94. ]
  95. )
  96. // 树节点属性映射关系
  97. const defaultProps = {
  98. children: 'children',
  99. label: 'label',
  100. }
  101. /**
  102. * 树组件点击事件句柄方法
  103. */
  104. const handleNodeClick = (node, data) => {
  105. console.log(
  106. '%c 树组件点击事件句柄方法 %c handleNodeClick',
  107. 'padding: 1px; background-color: #35495e; color: #fff',
  108. 'padding: 1px; background-color: #5e7ce0; color: #fff',
  109. )
  110. console.log('%c ∟ %c node %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', node)
  111. console.log('%c ∟ %c data %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', data)
  112. }
  113. onMounted(() => {
  114. // ...
  115. })
  116. </script>
  117. <style lang="less" scoped>
  118. .element-plus-tree {
  119. padding: 100px;
  120. :deep(.el-tree) {
  121. .el-tree-node {
  122. /* ^ 所有节点 */
  123. i.el-tree-node__expand-icon {
  124. padding: 6px;
  125. &::before {
  126. font-family: element-ui-icons;
  127. font-style: normal;
  128. content: "\e6d9";
  129. color: #000000;
  130. border: 1px solid #606266;
  131. border-radius: 2px;
  132. }
  133. svg {
  134. display: none; // 隐藏所有节点的 svg 图标
  135. }
  136. }
  137. /* / 所有节点 */
  138. /* ^ 已展开的父节点 */
  139. i.el-tree-node__expand-icon.expanded {
  140. transform: rotate(0deg); // 取消旋转
  141. -webkit-transform: rotate(0deg); // 取消旋转
  142. &::before {
  143. font-family: element-ui-icons;
  144. font-style: normal;
  145. content: "\e6d8";
  146. color: #000000;
  147. border: 1px solid #606266;
  148. border-radius: 2px;
  149. }
  150. }
  151. /* / 已展开的父节点 */
  152. /* ^ 叶子节点 */
  153. i.el-tree-node__expand-icon.is-leaf {
  154. &::before {
  155. display: none;
  156. }
  157. }
  158. /* / 叶子节点 */
  159. /* ^ 复选框 */
  160. .el-checkbox {
  161. margin: 0 7px 0 2px;
  162. .el-checkbox__inner {
  163. width: 14px;
  164. height: 14px;
  165. border-radius: 2px;
  166. border: 1px solid #bbb;
  167. }
  168. .el-checkbox__input.is-checked .el-checkbox__inner,
  169. .el-checkbox__input.is-indeterminate .el-checkbox__inner {
  170. border: 1px solid #5e7ce0;
  171. }
  172. }
  173. /* / 复选框 */
  174. .el-tree-node__content {
  175. small {
  176. font-size: 13px;
  177. }
  178. }
  179. }
  180. }
  181. }
  182. </style>

(2)效果如下~

2.高级示例

(1)示例代码

  1. <template>
  2. <div class="element-plus-tree">
  3. <el-tree
  4. :data="treeData"
  5. :props="defaultProps"
  6. :show-checkbox="true"
  7. :default-expand-all="true"
  8. :highlight-current="true"
  9. :expand-on-click-node="false"
  10. :indent="22"
  11. >
  12. <template #default="{ node, data }">
  13. <span v-if="!node.isLeaf" style="display: flex; align-items: center;">
  14. <el-icon v-if="node.expanded" style="margin: 0 6px 0 0px;" size="16"><FolderOpened /></el-icon>
  15. <el-icon v-else style="margin: 0 6px 0 0px;" size="16"><Folder /></el-icon>
  16. <small @click="handleNodeClick(node, data)">{{ node.label }}</small>
  17. </span>
  18. <span v-else style="display: flex; align-items: center;">
  19. <el-icon style="margin: 0 6px 0 0px;" size="16"><Document /></el-icon>
  20. <small @click="handleNodeClick(node, data)">{{ node.label }}</small>
  21. </span>
  22. </template>
  23. </el-tree>
  24. </div>
  25. </template>
  26. <script setup>
  27. import { onMounted, ref, getCurrentInstance } from 'vue'
  28. // 代理对象
  29. const { proxy } = getCurrentInstance()
  30. // 树型数据
  31. const treeData = ref(
  32. [
  33. {
  34. label: '香烟 WiFi 啤酒',
  35. expanded: true,
  36. children: [
  37. {
  38. label: '香烟',
  39. children: [
  40. {
  41. label: '煊赫门'
  42. },
  43. {
  44. label: 'ESSE双爆珠'
  45. }
  46. ],
  47. },
  48. {
  49. label: '后端开发技术',
  50. children: [
  51. {
  52. label: 'Java编程技术'
  53. },
  54. {
  55. label: 'Python编程技术'
  56. }
  57. ],
  58. },
  59. {
  60. label: '数据库',
  61. children: [
  62. {
  63. label: '关系型数据库',
  64. children: [
  65. {
  66. label: 'MySQL'
  67. },
  68. {
  69. label: 'Oracle'
  70. }
  71. ],
  72. },
  73. {
  74. label: '非关系型数据库',
  75. children: [
  76. {
  77. label: 'Redis'
  78. },
  79. {
  80. label: 'Elasticsearch'
  81. }
  82. ],
  83. }
  84. ],
  85. },
  86. {
  87. label: 'AI人工智能'
  88. },
  89. ],
  90. },
  91. {
  92. label: '火腿 iPad 泡面'
  93. },
  94. ]
  95. )
  96. // 树节点属性映射关系
  97. const defaultProps = {
  98. children: 'children',
  99. label: 'label',
  100. }
  101. /**
  102. * 树组件点击事件句柄方法
  103. */
  104. const handleNodeClick = (node, data) => {
  105. console.log(
  106. '%c 树组件点击事件句柄方法 %c handleNodeClick',
  107. 'padding: 1px; background-color: #35495e; color: #fff',
  108. 'padding: 1px; background-color: #5e7ce0; color: #fff',
  109. )
  110. console.log('%c ∟ %c node %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', node)
  111. console.log('%c ∟ %c data %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', data)
  112. }
  113. onMounted(() => {
  114. // ...
  115. })
  116. </script>
  117. <style lang="less" scoped>
  118. .element-plus-tree {
  119. padding: 100px;
  120. :deep(.el-tree) {
  121. /* ---- ---- ---- ---- ^(节点对齐)---- ---- ---- ---- */
  122. .el-tree-node {
  123. /* ^ 所有节点 */
  124. i.el-tree-node__expand-icon {
  125. padding: 6px;
  126. &::before {
  127. font-family: element-ui-icons;
  128. font-style: normal;
  129. content: "\e6d9";
  130. color: #000000;
  131. border: 1px solid #606266;
  132. border-radius: 2px;
  133. }
  134. svg {
  135. display: none; // 隐藏所有节点的 svg 图标
  136. }
  137. }
  138. /* / 所有节点 */
  139. /* ^ 已展开的父节点 */
  140. i.el-tree-node__expand-icon.expanded {
  141. transform: rotate(0deg); // 取消旋转
  142. -webkit-transform: rotate(0deg); // 取消旋转
  143. &::before {
  144. font-family: element-ui-icons;
  145. font-style: normal;
  146. content: "\e6d8";
  147. color: #000000;
  148. border: 1px solid #606266;
  149. border-radius: 2px;
  150. }
  151. }
  152. /* / 已展开的父节点 */
  153. /* ^ 叶子节点 */
  154. i.el-tree-node__expand-icon.is-leaf {
  155. &::before {
  156. display: none;
  157. }
  158. }
  159. /* / 叶子节点 */
  160. /* ^ 复选框 */
  161. .el-checkbox {
  162. margin: 0 7px 0 2px;
  163. .el-checkbox__inner {
  164. width: 14px;
  165. height: 14px;
  166. border-radius: 2px;
  167. border: 1px solid #bbb;
  168. }
  169. .el-checkbox__input.is-checked .el-checkbox__inner,
  170. .el-checkbox__input.is-indeterminate .el-checkbox__inner {
  171. border: 1px solid #5e7ce0;
  172. }
  173. }
  174. /* / 复选框 */
  175. .el-tree-node__content {
  176. small {
  177. font-size: 13px;
  178. }
  179. }
  180. }
  181. /* ---- ---- ---- ---- /(节点对齐)---- ---- ---- ---- */
  182. /* ---- ---- ---- ---- ^(文字高亮)---- ---- ---- ---- */
  183. .el-tree-node.is-current {
  184. .el-tree-node__content {
  185. small {
  186. color: #5e7ce0;
  187. }
  188. }
  189. .el-tree-node__children {
  190. small {
  191. color: unset;
  192. }
  193. }
  194. }
  195. /* ---- ---- ---- ---- /(文字高亮)---- ---- ---- ---- */
  196. /* ---- ---- ---- ---- ^(新增辅助线)---- ---- ---- ---- */
  197. /* ^ 树节点 */
  198. .el-tree-node {
  199. position: relative;
  200. width: auto;
  201. // width: max-content; // 显示文字宽度
  202. padding-left: 13px;
  203. &::before {
  204. width: 1px;
  205. height: 100%;
  206. content: '';
  207. position: absolute;
  208. top: -38px;
  209. bottom: 0;
  210. left: 0;
  211. right: auto;
  212. border-width: 1px;
  213. border-left: 1px solid #b8b9bb;
  214. }
  215. &::after {
  216. width: 13px;
  217. height: 13px;
  218. content: '';
  219. position: absolute;
  220. z-index: 0;
  221. left: 0;
  222. right: auto;
  223. top: 12px;
  224. bottom: auto;
  225. border-width: 1px;
  226. border-top: 1px solid #b8b9bb;
  227. }
  228. .el-tree-node__content {
  229. position: relative;
  230. z-index: 1;
  231. color: #000;
  232. padding-left: 0 !important;
  233. /* ^ 复选框 */
  234. .el-checkbox {
  235. margin: 0 10px 0 5.5px;
  236. }
  237. /* / 复选框 */
  238. }
  239. .el-tree-node__children {
  240. padding-left: 12px;
  241. }
  242. &:last-child::before {
  243. height: 50px;
  244. }
  245. }
  246. /* / 树节点 */
  247. /* ^ 第一层节点 */
  248. > .el-tree-node {
  249. padding-left: 0;
  250. &::before {
  251. border-left: none;
  252. }
  253. &::after {
  254. border-top: none;
  255. }
  256. }
  257. /* / 第一层节点 */
  258. /* ^ 叶子节点 */
  259. i.el-tree-node__expand-icon.is-leaf {
  260. display: none;
  261. }
  262. /* / 叶子节点 */
  263. /* ^ 设置子节点左外边距 */
  264. .el-tree-node__content:has(.is-leaf) {
  265. // color: #00ffff;
  266. margin-left: 25px !important;
  267. }
  268. /* / 设置子节点左外边距 */
  269. /* ---- ---- ---- ---- /(新增辅助线)---- ---- ---- ---- */
  270. }
  271. }
  272. </style>

(2)效果如下~

三、基于Vue3+DevUI的附赠例子

1.导入相关依赖

npm i vue-devui -D

2.官方文档

https://vue-devui.github.io/

3.普通示例

(1)示例代码

  1. <template>
  2. <div class="vue-devui-tree">
  3. <div>
  4. <span>是否展开所有节点&nbsp;:&nbsp;</span>
  5. <el-switch
  6. v-model="isExpandAllNodes"
  7. size="small"
  8. @change="handleIsExpandAllNodesChange"
  9. />
  10. </div>
  11. <DevTree ref="treeRef" :data="data">
  12. <template #icon="{ nodeData, toggleNode }">
  13. <span
  14. v-if="!nodeData.isLeaf"
  15. class="devui-tree__node-folder"
  16. @click="
  17. (event) => {
  18. event.stopPropagation();
  19. toggleNode(nodeData);
  20. }
  21. "
  22. >
  23. <!-- 未展开图标 -->
  24. <svg v-if="!nodeData.expanded" style="margin-right: 8px" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
  25. <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
  26. <rect x="0.5" y="0.5" width="15" height="15" rx="2" stroke="#666666"></rect>
  27. <path fill="#666666" d="M8.75,4 L8.75,7.25 L12,7.25 L12,8.75 L8.749,8.75 L8.75,12 L7.25,12 L7.249,8.75 L4,8.75 L4,7.25 L7.25,7.25 L7.25,4 L8.75,4 Z"></path>
  28. </g>
  29. </svg>
  30. <!-- 已展开图标 -->
  31. <svg v-else style="margin-right: 8px" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
  32. <g stroke-width="1" fill="none" fill-rule="evenodd">
  33. <rect x="0.5" y="0.5" width="15" height="15" rx="2" stroke="#666666"></rect>
  34. <rect x="4" y="7" width="8" height="2" fill="#666666"></rect>
  35. </g>
  36. </svg>
  37. </span>
  38. <span
  39. v-else
  40. class="devui-tree__node-leaf"
  41. >
  42. </span>
  43. </template>
  44. <template #content="{ nodeData }">
  45. <svg style="margin-right: 7px" viewBox="0 0 16 16" width="16" height="16">
  46. <path
  47. :d="`${
  48. nodeData.isLeaf
  49. ? 'M13,6 L9,6 L9,5 L9,2 L3,2 L3,14 L13,14 L13,6 Z M12.5857864,5 L10,2.41421356 L10,5 L12.5857864,5 Z M2,1 L10,1 L14,5 L14,15 L2,15 L2,1 Z'
  50. : nodeData.expanded
  51. ? 'M16,6 L14,14 L2,14 L0,6 L16,6 Z M14.7192236,7 L1.28077641,7 L2.78077641,13 L13.2192236,13 L14.7192236,7 Z M6,1 L8,3 L15,3 L15,5 L14,5 L14,4 L7.58578644,4 L5.58578644,2 L2,2 L2,5 L1,5 L1,1 L6,1 Z'
  52. : 'M14,6 L14,5 L7.58578644,5 L5.58578644,3 L2,3 L2,6 L14,6 Z M14,7 L2,7 L2,13 L14,13 L14,7 Z M1,2 L6,2 L8,4 L15,4 L15,14 L1,14 L1,2 Z'
  53. }`"
  54. stroke-width="1"
  55. fill="#8a8e99"
  56. ></path>
  57. </svg>
  58. <small>{{ nodeData.label }}</small>
  59. </template>
  60. </DevTree>
  61. </div>
  62. </template>
  63. <script setup>
  64. import { onMounted, ref, getCurrentInstance, toRaw } from 'vue'
  65. import { Tree as DevTree } from "vue-devui"
  66. import 'vue-devui/style.css'
  67. // 代理对象
  68. const currentInstance = getCurrentInstance()
  69. // 树型DOM引用
  70. const treeRef = ref()
  71. // 是否展开所有节点
  72. const isExpandAllNodes = ref(false)
  73. // 树型数据
  74. const data = ref(
  75. [
  76. {
  77. label: '香烟 WiFi 啤酒',
  78. expanded: true,
  79. children: [
  80. {
  81. label: '香烟',
  82. children: [
  83. {
  84. label: '煊赫门'
  85. },
  86. {
  87. label: 'ESSE双爆珠'
  88. }
  89. ],
  90. },
  91. {
  92. label: '后端开发技术',
  93. children: [
  94. {
  95. label: 'Java编程技术'
  96. },
  97. {
  98. label: 'Python编程技术'
  99. }
  100. ],
  101. },
  102. {
  103. label: '数据库',
  104. children: [
  105. {
  106. label: '关系型数据库',
  107. children: [
  108. {
  109. label: 'MySQL'
  110. },
  111. {
  112. label: 'Oracle'
  113. }
  114. ],
  115. },
  116. {
  117. label: '非关系型数据库',
  118. children: [
  119. {
  120. label: 'Redis'
  121. },
  122. {
  123. label: 'Elasticsearch'
  124. }
  125. ],
  126. }
  127. ],
  128. },
  129. {
  130. label: 'AI人工智能'
  131. },
  132. ],
  133. },
  134. {
  135. label: '火腿 iPad 泡面'
  136. },
  137. ]
  138. )
  139. /**
  140. * 是否展开所有节点句柄方法
  141. */
  142. const handleIsExpandAllNodesChange = (val) => {
  143. if (val) {
  144. handleExpandAllNodes()
  145. } else {
  146. handleCollapseAllNodes()
  147. }
  148. }
  149. /**
  150. * 将树节点全部展开句柄方法
  151. */
  152. const handleExpandAllNodes = () => {
  153. treeRef.value.treeFactory.expandAllNodes()
  154. }
  155. /**
  156. * 将树节点全部收起句柄方法
  157. */
  158. const handleCollapseAllNodes = () => {
  159. const treeFactory = treeRef.value.treeFactory
  160. const treeData = treeFactory.treeData._rawValue
  161. recursionCollapseNode(treeData)
  162. }
  163. /**
  164. * 递归收起节点句柄方法
  165. */
  166. const recursionCollapseNode = (treeData) => {
  167. for (let node of treeData) {
  168. if (node.expanded) {
  169. treeRef.value.treeFactory.collapseNode(node)
  170. }
  171. if (node.children && node.children.length > 0) {
  172. recursionCollapseNode(node.children)
  173. }
  174. }
  175. }
  176. onMounted(
  177. async () => {
  178. // Todo
  179. }
  180. )
  181. </script>
  182. <style lang="less" scoped>
  183. .vue-devui-tree {
  184. padding: 100px;
  185. :deep(.devui-tree) {
  186. .devui-tree__node-leaf {
  187. // display: none;
  188. position: relative;
  189. width: 16px;
  190. height: 16px;
  191. margin-right: 6px;
  192. // background-color: #ccc;
  193. // border: 1px solid #ccc;
  194. // border-radius: 2px;
  195. }
  196. .devui-tree__node-vline {
  197. margin-left: 4.5px;
  198. background-color: #cccccc !important;
  199. }
  200. .devui-tree__node-hline {
  201. margin-left: 4.5px;
  202. background-color: #cccccc !important;
  203. }
  204. .devui-tree__node {
  205. .devui-tree__node-content {
  206. &.active {
  207. // background-color: unset;
  208. small {
  209. color: #5e7ce0;
  210. }
  211. }
  212. &:hover {
  213. // background-color: unset;
  214. }
  215. small {
  216. font-size: 13px;
  217. transition: all ease 0.3s;
  218. &:hover {
  219. color: #5e7ce0;
  220. }
  221. }
  222. }
  223. }
  224. }
  225. }
  226. </style>

(2)效果如下~

4.高级示例

(1)示例代码

  1. <template>
  2. <div class="vue-devui-tree">
  3. <DevTree ref="treeRef" :data="data" check>
  4. <template #icon="{ nodeData, toggleNode }">
  5. <span
  6. v-if="!nodeData.isLeaf"
  7. class="devui-tree__node-folder"
  8. @click="
  9. (event) => {
  10. event.stopPropagation();
  11. toggleNode(nodeData);
  12. }
  13. "
  14. >
  15. <!-- 未展开图标 -->
  16. <svg v-if="!nodeData.expanded" style="margin-right: 7px" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
  17. <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
  18. <rect x="0.5" y="0.5" width="15" height="15" rx="2" stroke="#666666"></rect>
  19. <path fill="#666666" d="M8.75,4 L8.75,7.25 L12,7.25 L12,8.75 L8.749,8.75 L8.75,12 L7.25,12 L7.249,8.75 L4,8.75 L4,7.25 L7.25,7.25 L7.25,4 L8.75,4 Z"></path>
  20. </g>
  21. </svg>
  22. <!-- 已展开图标 -->
  23. <svg v-else style="margin-right: 7px" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
  24. <g stroke-width="1" fill="none" fill-rule="evenodd">
  25. <rect x="0.5" y="0.5" width="15" height="15" rx="2" stroke="#666666"></rect>
  26. <rect x="4" y="7" width="8" height="2" fill="#666666"></rect>
  27. </g>
  28. </svg>
  29. </span>
  30. <span
  31. v-else
  32. class="devui-tree__node-leaf"
  33. >
  34. </span>
  35. </template>
  36. <template #content="{ nodeData }">
  37. <svg style="margin: 0 7px" viewBox="0 0 16 16" width="16" height="16">
  38. <path
  39. :d="`${
  40. nodeData.isLeaf
  41. ? 'M13,6 L9,6 L9,5 L9,2 L3,2 L3,14 L13,14 L13,6 Z M12.5857864,5 L10,2.41421356 L10,5 L12.5857864,5 Z M2,1 L10,1 L14,5 L14,15 L2,15 L2,1 Z'
  42. : nodeData.expanded
  43. ? 'M16,6 L14,14 L2,14 L0,6 L16,6 Z M14.7192236,7 L1.28077641,7 L2.78077641,13 L13.2192236,13 L14.7192236,7 Z M6,1 L8,3 L15,3 L15,5 L14,5 L14,4 L7.58578644,4 L5.58578644,2 L2,2 L2,5 L1,5 L1,1 L6,1 Z'
  44. : 'M14,6 L14,5 L7.58578644,5 L5.58578644,3 L2,3 L2,6 L14,6 Z M14,7 L2,7 L2,13 L14,13 L14,7 Z M1,2 L6,2 L8,4 L15,4 L15,14 L1,14 L1,2 Z'
  45. }`"
  46. stroke-width="1"
  47. fill="#8a8e99"
  48. ></path>
  49. </svg>
  50. <small>{{ nodeData.label }}</small>
  51. </template>
  52. </DevTree>
  53. </div>
  54. </template>
  55. <script setup lang="ts">
  56. import { onMounted, ref, getCurrentInstance, toRaw, useCssModule, provide } from 'vue'
  57. import { Tree as DevTree } from "vue-devui"
  58. import 'vue-devui/style.css'
  59. // 代理对象
  60. const proxy = getCurrentInstance()
  61. // 树型DOM引用
  62. const treeRef = ref(null);
  63. // 树型数据
  64. const data = ref(
  65. [
  66. {
  67. label: '香烟 WiFi 啤酒',
  68. expanded: true,
  69. children: [
  70. {
  71. label: '香烟',
  72. children: [
  73. {
  74. label: '煊赫门'
  75. },
  76. {
  77. label: 'ESSE双爆珠'
  78. }
  79. ],
  80. },
  81. {
  82. label: '后端开发技术',
  83. children: [
  84. {
  85. label: 'Java编程技术'
  86. },
  87. {
  88. label: 'Python编程技术'
  89. }
  90. ],
  91. },
  92. {
  93. label: '数据库',
  94. children: [
  95. {
  96. label: '关系型数据库',
  97. children: [
  98. {
  99. label: 'MySQL'
  100. },
  101. {
  102. label: 'Oracle'
  103. }
  104. ],
  105. },
  106. {
  107. label: '非关系型数据库',
  108. children: [
  109. {
  110. label: 'Redis'
  111. },
  112. {
  113. label: 'Elasticsearch'
  114. }
  115. ],
  116. }
  117. ],
  118. },
  119. {
  120. label: 'AI人工智能'
  121. },
  122. ],
  123. },
  124. {
  125. label: '火腿 iPad 泡面'
  126. },
  127. ]
  128. )
  129. onMounted(() => {
  130. // 展开全部节点
  131. if (treeRef && treeRef.value) {
  132. const treeFactory: any = treeRef.value['treeFactory']
  133. treeFactory.expandAllNodes()
  134. }
  135. })
  136. </script>
  137. <style lang="less" scoped>
  138. .vue-devui-tree {
  139. padding: 100px;
  140. :deep(.devui-tree) {
  141. .devui-tree__node-leaf {
  142. // display: none;
  143. position: relative;
  144. width: 16px;
  145. height: 16px;
  146. margin-right: 6px;
  147. // background-color: #ccc;
  148. // border: 1px solid #ccc;
  149. // border-radius: 2px;
  150. }
  151. .devui-tree__node-vline {
  152. margin-left: 4.5px;
  153. background-color: #cccccc !important;
  154. }
  155. .devui-tree__node-hline {
  156. margin-left: 4.5px;
  157. background-color: #cccccc !important;
  158. }
  159. .devui-tree__node {
  160. .devui-tree__node-content {
  161. &.active {
  162. // background-color: unset;
  163. small {
  164. color: #5e7ce0;
  165. }
  166. }
  167. // &:hover {
  168. // background-color: unset;
  169. // }
  170. small {
  171. font-size: 13px;
  172. transition: all ease 0.3s;
  173. &:hover {
  174. color: #5e7ce0;
  175. }
  176. }
  177. }
  178. }
  179. }
  180. }
  181. </style>

(2)效果如下~

四、参考资料

vue树形控件tree的使用方法_vue.js_脚本之家

Vue DevUI

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/283678
推荐阅读
相关标签
  

闽ICP备14008679号