您现在的位置是:首页 > 技术教程 正文

Element-UI控件Tree实现数据树形结构

admin 阅读: 2024-03-31
后台-插件-广告管理-内容页头部广告(手机)

        在前端开发中,有时会遇到所有菜单数据在同一级的情况,后端未对数据进行分级处理;但前端渲染需要是树状结构的数据,如何实现数据的树状化?将数组中通过父节点的ID与子节点的parentId关联,通过递归函数来实现。

        前端框架这里使用element-ui的tree控件来实现,对其不了解可以去官网查看文档。

地址:Element - The world's most popular Vue UI framework

一、创建页面

        这里就不讲vue项目的搭建了,基础不好的,可以去官网查看文档。

        首先在src/pages目录中,创建element-trees文件夹,再创建index.vue,代码如下:

  1. <script>
  2. export default {
  3. data(){
  4. return {}
  5. },
  6. created() {},
  7. methods: {}
  8. }
  9. script>
  10. <style lang="scss">
  11. style>

        在类选择器tree-container容器中,添加相关按钮和Tree控件,以及给容器添加些样式进行修饰一下,代码如下:

  1. <script>
  2. export default {
  3. data(){
  4. return {
  5. props: {
  6. label: 'name',
  7. children: 'children'
  8. },
  9. treeData: []
  10. }
  11. },
  12. created() {},
  13. methods: {
  14. //添加父节点数据
  15. appendNode(){},
  16. //添加子项数据
  17. append(data){},
  18. //编辑数据
  19. editor(data){},
  20. //移出项目
  21. remove(node, data){},
  22. //删除选中的节点
  23. removeSelected(){}
  24. }
  25. }
  26. script>
  27. <style lang="scss">
  28. .tree-container{ width: 360px; font-size: 12px;
  29. .top-box{ text-align: right; padding-bottom: 20px; }
  30. }
  31. .custom-tree-node {
  32. flex: 1;
  33. display: flex;
  34. align-items: center;
  35. justify-content: space-between;
  36. font-size: 14px;
  37. padding-right: 8px;
  38. }
  39. style>

此时页面效果如下:

二、模拟数据

        在element-trees文件夹中添加data.js文件,用来存储模拟数据。代码如下:

  1. /**
  2. * 模拟数据
  3. */
  4. const dataList = [
  5. {
  6. "name": "网络安全渗透工程师体系大纲",
  7. "pid": 0,
  8. "createtime": "2023-03-29",
  9. "updatetime": "2023-03-29",
  10. "id": 1
  11. },
  12. {
  13. "name": "WEB通信、前后端原理",
  14. "pid": 1,
  15. "createtime": "2023-03-29",
  16. "updatetime": "2023-03-29",
  17. "id": 2
  18. },
  19. {
  20. "name": "信息收集",
  21. "pid": 1,
  22. "createtime": "2023-03-29",
  23. "updatetime": "2023-03-29",
  24. "id": 3
  25. },
  26. {
  27. "name": "注入全方位利用+数据库注入",
  28. "pid": 1,
  29. "createtime": "2023-03-29",
  30. "updatetime": "2023-03-29",
  31. "id": 4
  32. },
  33. {
  34. "name": "前端渗透、文件上传解析漏洞",
  35. "pid": 1,
  36. "createtime": "2023-03-29",
  37. "updatetime": "2023-03-29",
  38. "id": 5
  39. },
  40. {
  41. "name": "漏洞利用",
  42. "pid": 1,
  43. "createtime": "2023-03-29",
  44. "updatetime": "2023-03-29",
  45. "id": 6
  46. },
  47. {
  48. "name": "漏洞挖掘",
  49. "pid": 1,
  50. "createtime": "2023-03-29",
  51. "updatetime": "2023-03-29",
  52. "id": 7
  53. },
  54. {
  55. "name": "Web服务器通信原理",
  56. "pid": 2,
  57. "createtime": "2023-03-29",
  58. "updatetime": "2023-03-29",
  59. "id": 8
  60. },
  61. {
  62. "name": "后端基础SQL",
  63. "pid": 2,
  64. "createtime": "2023-03-29",
  65. "updatetime": "2023-03-29",
  66. "id": 9
  67. },
  68. {
  69. "name": "数据库简介及SQL语法",
  70. "pid": 2,
  71. "createtime": "2023-03-29",
  72. "updatetime": "2023-03-29",
  73. "id": 10
  74. },
  75. {
  76. "name": "后端基础SQL高级查询与子查询",
  77. "pid": 2,
  78. "createtime": "2023-03-29",
  79. "updatetime": "2023-03-29",
  80. "id": 11
  81. },
  82. {
  83. "name": "后端基础PHP简介及基本函数上",
  84. "pid": 2,
  85. "createtime": "2023-03-29",
  86. "updatetime": "2023-03-29",
  87. "id": 12
  88. },
  89. {
  90. "name": "后端基础PHP—表单验证",
  91. "pid": 2,
  92. "createtime": "2023-03-29",
  93. "updatetime": "2023-03-29",
  94. "id": 13
  95. },
  96. {
  97. "name": "正则表达式",
  98. "pid": 2,
  99. "createtime": "2023-03-29",
  100. "updatetime": "2023-03-29",
  101. "id": 14
  102. },
  103. {
  104. "name": "信息搜集的意义 — 渗透测试的灵魂",
  105. "pid": 3,
  106. "createtime": "2023-03-29",
  107. "updatetime": "2023-03-29",
  108. "id": 15
  109. },
  110. {
  111. "name": "信息收集(一)",
  112. "pid": 3,
  113. "createtime": "2023-03-29",
  114. "updatetime": "2023-03-29",
  115. "id": 16
  116. },
  117. {
  118. "name": "网络架构-信息收集",
  119. "pid": 3,
  120. "createtime": "2023-03-29",
  121. "updatetime": "2023-03-29",
  122. "id": 17
  123. },
  124. {
  125. "name": "前端-信息收集",
  126. "pid": 3,
  127. "createtime": "2023-03-29",
  128. "updatetime": "2023-03-29",
  129. "id": 18
  130. },
  131. {
  132. "name": "系统-信息收集",
  133. "pid": 3,
  134. "createtime": "2023-03-29",
  135. "updatetime": "2023-03-29",
  136. "id": 19
  137. },
  138. {
  139. "name": "SQL注入的原理",
  140. "pid": 4,
  141. "createtime": "2023-03-29",
  142. "updatetime": "2023-03-29",
  143. "id": 20
  144. },
  145. {
  146. "name": "渗透测试常用工具讲解",
  147. "pid": 4,
  148. "createtime": "2023-03-29",
  149. "updatetime": "2023-03-29",
  150. "id": 21
  151. },
  152. {
  153. "name": "POST注入/HEAD注入",
  154. "pid": 4,
  155. "createtime": "2023-03-29",
  156. "updatetime": "2023-03-29",
  157. "id": 22
  158. },
  159. {
  160. "name": "盲注",
  161. "pid": 4,
  162. "createtime": "2023-03-29",
  163. "updatetime": "2023-03-29",
  164. "id": 23
  165. },
  166. {
  167. "name": "XSS的原理分析与解剖",
  168. "pid": 5,
  169. "createtime": "2023-03-29",
  170. "updatetime": "2023-03-29",
  171. "id": 24
  172. },
  173. {
  174. "name": "存储型XSS",
  175. "pid": 5,
  176. "createtime": "2023-03-29",
  177. "updatetime": "2023-03-29",
  178. "id": 25
  179. },
  180. {
  181. "name": " Dom Based XSS",
  182. "pid": 5,
  183. "createtime": "2023-03-29",
  184. "updatetime": "2023-03-29",
  185. "id": 26
  186. },
  187. {
  188. "name": "XXE实体注入",
  189. "pid": 6,
  190. "createtime": "2023-03-29",
  191. "updatetime": "2023-03-29",
  192. "id": 27
  193. },
  194. {
  195. "name": "SSRF-服务器端请求伪造",
  196. "pid": 6,
  197. "createtime": "2023-03-29",
  198. "updatetime": "2023-03-29",
  199. "id": 28
  200. },
  201. {
  202. "name": "验证码绕过、密码找回漏洞",
  203. "pid": 7,
  204. "createtime": "2023-03-29",
  205. "updatetime": "2023-03-29",
  206. "id": 29
  207. },
  208. {
  209. "name": "平行越权、垂直越权",
  210. "pid": 7,
  211. "createtime": "2023-03-29",
  212. "updatetime": "2023-03-29",
  213. "id": 30
  214. },
  215. {
  216. "name": "支付漏洞",
  217. "pid": 7,
  218. "createtime": "2023-03-29",
  219. "updatetime": "2023-03-29",
  220. "id": 31
  221. },
  222. {
  223. "name": "Sql注入",
  224. "pid": 7,
  225. "createtime": "2023-03-29",
  226. "updatetime": "2023-03-29",
  227. "id": 32
  228. },
  229. {
  230. "name": "变量覆盖漏洞",
  231. "pid": 6,
  232. "createtime": "2023-03-29",
  233. "updatetime": "2023-03-29",
  234. "id": 33
  235. },
  236. {
  237. "name": "本地包含与远程包含",
  238. "pid": 6,
  239. "createtime": "2023-03-29",
  240. "updatetime": "2023-03-29",
  241. "id": 34
  242. },
  243. {
  244. "name": "unserialize反序列化漏洞",
  245. "pid": 6,
  246. "createtime": "2023-03-29",
  247. "updatetime": "2023-03-29",
  248. "id": 35
  249. },
  250. {
  251. "name": "CSRF",
  252. "pid": 5,
  253. "createtime": "2023-03-29",
  254. "updatetime": "2023-03-29",
  255. "id": 36
  256. },
  257. {
  258. "name": "文件上传解析漏洞",
  259. "pid": 5,
  260. "createtime": "2023-03-29",
  261. "updatetime": "2023-03-29",
  262. "id": 37
  263. },
  264. {
  265. "name": "宽字节注入",
  266. "pid": 4,
  267. "createtime": "2023-03-29",
  268. "updatetime": "2023-03-29",
  269. "id": 38
  270. },
  271. {
  272. "name": "Accsess注入 — Cookie注入",
  273. "pid": 4,
  274. "createtime": "2023-03-29",
  275. "updatetime": "2023-03-29",
  276. "id": 39
  277. },
  278. {
  279. "name": "Accsess注入 — 偏移注入",
  280. "pid": 4,
  281. "createtime": "2023-03-29",
  282. "updatetime": "2023-03-29",
  283. "id": 40
  284. },
  285. {
  286. "name": "MySQL注入 — Dns 注入",
  287. "pid": 4,
  288. "createtime": "2023-03-29",
  289. "updatetime": "2023-03-29",
  290. "id": 41
  291. },
  292. {
  293. "name": "MSSQL注入 — 反弹注入",
  294. "pid": 4,
  295. "createtime": "2023-03-29",
  296. "updatetime": "2023-03-29",
  297. "id": 42
  298. },
  299. {
  300. "name": "Oracle注入 — 报错注入",
  301. "pid": 4,
  302. "createtime": "2023-03-29",
  303. "updatetime": "2023-03-29",
  304. "id": 43
  305. }
  306. ];
  307. export default dataList;

        文件创建好后,我们将其引入到index.vue文件中,代码如下:

        如上代码,通过loadNode()函数执行后,模拟数据则已与Tree控件进行了双方绑定。但是页面中显示的数据,全都显示在同级位置。数据的归类分级,我们在后面再讲解。页面效果如下:

三、递归函数

        此时离成功紧一步之遥了,我们先来分析下data.js中的数据结构。如下图:

        如何将数据归类分级呢,可以通过循环判断,每级的id与数据中的pid进行关联;比如第一条数据id为1,将数组进行遍历查询判断pid有为1的数据,则表示其有子项数据,将其子项数据添加到children数组中。pid为0的为一级数据,所以当定义递归函数时,传入的pid默认为0。

        这里层次有可能是1层,2层,3层...... n层,要一层一层循环判断,就太麻烦了;所以这里我们要使用递归方法,来实现n层级的数据处理。

        在src下创建utils/utils.js文件,在utils.js中定义递归函数,代码如下:

  1. /**
  2. * 递归函数
  3. * @param arr 数组
  4. * @param pid 父ID,不传默认为0
  5. */
  6. export const DGFilterTrees = (arr, pid = 0) => {
  7. let newArr = [];
  8. //循环数组
  9. arr.forEach((item, i) => {
  10. //判断pid与遍历元素中pid相同的数据,相等的为同级数据,追加到该级数组中
  11. if(item.pid == pid){
  12. newArr.push(item);
  13. /**
  14. * 判断该item元素是否有子项
  15. * 当数组中有元素的pid与item.id相等,some函数会返回true,表示该元素有子项数据
  16. */
  17. if(arr.some(ele => ele.pid == item.id)){
  18. //此时,通过重新调用本函数进行递归处理
  19. item['children'] = DGFilterTrees(arr, item.id);
  20. }
  21. }
  22. });
  23. return newArr;
  24. }

注:递归算法是一种直接或者间接调用自身函数或者方法的算法。

递归算法的特点:

  • 递归就是方法里调用自身。
  • 在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。
  • 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
  • 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等,所以一般不提倡用递归算法设计程序。

所以在使用递归算法时,一定要注意判断条件能正常退出,否则无法靠自身的控制终止的循环,会导致栈溢出,程序卡死等问题。

  将DGFilterTrees()递归函数引入到index.vue中,将模拟数据重组,代码如下:

        此时,所有数据都进行了归类分级显示,页面的效果图如下:

四、选中后显示按钮

        如上图,我们会发现每项后面的添加、修改、删除全显示出来,会显示拥挤并不方便操作。这里我们将节点输出后会发现,可以使用isCurrent来判断,只显示选中当前项的按钮。

         在span标签上添加v-if,判断node.isCurrent是否为true,代码如下:

  1. ref="tree"
  2. :data="treeData"
  3. :props="props"
  4. accordion
  5. show-checkbox
  6. node-key="id"
  7. default-expand-all
  8. :expand-on-click-node="false">
  9. <span class="custom-tree-node" slot-scope="{ node, data }">
  10. <span>{{ node.label }}span>
  11. <span v-if="node.isCurrent">
  12. <el-button type="text" size="mini" @click="append(data)">添加el-button>
  13. <el-button type="text" size="mini" @click="editor(data)">修改el-button>
  14. <el-button type="text" size="mini" @click="remove(node, data)">删除el-button>
  15. span>
  16. span>

        如下图,点击选择”后端基础SQL“后,会显示后面的操作按钮。

 五、删除元素

        这里我们是使用本地定义模拟数据进行操作的,所以没有真实项目中是通过接口进行数据的增删改查部分。代码如下:

  1. //移出项目
  2. remove(node, data){
  3. if(Array.isArray(data['children'])&&data.children.length>0){
  4. this.$message.error('当前数据有子项,无法删除~');
  5. return;
  6. }
  7. //查询数据位置索引
  8. let index = DataList.findIndex(item => item.id==data.id);
  9. //删除指定位置数据
  10. DataList.splice(index, 1);
  11. //重新加载数据
  12. this.loadNode();
  13. }

        效果如下图:

五、删除多选项 

        这里需要使用Tree控件的getCheckedNodes()函数,来获取被选中项;然后通过循环,删除每条被选中元素,代码如下:

  1. //删除选中的节点
  2. removeSelected(){
  3. //获取选中的数据
  4. let checks = this.$refs.tree.getCheckedNodes();
  5. //循环删除选中
  6. checks.forEach(item => {
  7. DataList.splice(DataList.findIndex(sub => sub.id == item.id), 1);
  8. });
  9. //重新加载数据
  10. this.loadNode();
  11. }

        效果如下图:

        本期就先介绍到这,添加、修改等功能也比较简单,可以通过element-ui的$prompt弹框控件来实现。

标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索
排行榜