所谓的无限极分类是啥?其实简单点说就是一个人类可以繁衍出多个后代,然后一个后代又可以分另外多个后代这样无限繁衍下去(可以想象神奇动物在哪里2里面莱斯特兰奇的家族族谱),就好象linux系统你可以新建一个文件夹,然后在这个文件夹里又可以建一些个文件夹,在文件夹底下还可以建一些文件夹一样,随后使用tree命令就可以查看文件夹目录层级。
那么这种层级结构也成为树结构在日常的开发需求里也是很常见的,比如美多商城系统的商品分类,课程的目录章节,以及以及论坛里的帖子回复等等,本次我们后台利用Django来写一个能够返回层级结构数据的接口,接口将该数据以json的形式返回前端,前端使用vue的组件递归来展示数据。
首先,打开django项目中的models.py,新建一个类别的模型类,这里我们以最简单的parentid的形式来建立
class Cate(models.Model): #主键 通过参数声明主键 id = models.AutoField(primary_key=True) #分类名称 name = models.CharField(max_length=500) #父级id pid = models.IntegerField() #表名 class Meta: db_table = 'cate'由于我们的项目基于drf框架,所以要增加一个序列器类,如果你的项目没有用drf,可以直接用json模块来进行序列化
from rest_framework import serializers from myapp.models import Cate class CateSer(serializers.ModelSerializer): class Meta: model = Cate fields = "__all__"由于django序列化出来的类不具备层级结构,所以我们提前写好一个用来递归的方法:
def xTree(datas): lists=[] tree={} parent_id='' for i in datas: item=i tree[item['id']]=item root=None for i in datas: obj=i if not obj['pid']: root=tree[obj['id']] lists.append(root) else: parent_id=obj['pid'] if 'childlist' not in tree[parent_id]: tree[parent_id]['childlist']=[] tree[parent_id]['childlist'].append(tree[obj['id']]) return lists此时我们写一个视图接口,用来从数据库中读取数据
class MyTree(APIView): def get(self,request): #获取订单列表 catelist = Cate.objects.filter() #序列化 catelist_ser = CateSer(catelist,many=True) mylist= xTree(catelist_ser.data) return Response(mylist)请在mysql库里存一些测试数据
测试一下接口,看看是否以json的形式返回了层级结构
ok,接口没有问题,现在我们把目光投向前端,打开vue项目,新建一个Reply.vue组件
所谓递归组件: 就是组件可以在它们自己的模板中调用自身,不过它们只能通过 name 选项来做这件事,例如给组件设置属性 name: 'Reply',然后在模板中就可以使用 Reply 调用自己进行递归调用了
<template> <div> <li > <div :class="[data.id==0 ? 'root': '', 'reply']">{{ data.name }}</div> <ul v-if="data.childlist && data.childlist.length>0"> <Reply v-for="child in data.childlist" :key="child.id" :data="child"/> </ul> </li> </div> </template> <script> export default { name: 'Reply', // 递归组件需要设置 name 属性,才能在模板中调用自己 props:['data'], }; </script> <style > .reply { padding-left: 4px; border-left: 1px solid #eee; } ul { padding-left: 20px; list-style: none; } .root { display: none; } </style>然后在其他任意组件中调用该Reply.vue组件,传入刚刚写好的接口数据
<template> <div> <Reply :data="data" /> </div> </template> <script> import {config,formatXml} from '../config.js'; import mh_test from './mh_test.vue'; import Reply from './Reply.vue'; import md5 from 'js-md5'; export default { data () { return { data:{}, online: 0 } }, components: { //'mh_test':mh_test Reply }, //钩子方法 mounted:function(){ this.get_token(); }, //绑定事件 methods:{ get_token(){ this.axios.get('http://localhost:8000/mytree/').then((result) =>{ console.log(result); var mytree = {'id':0,name:'123'}; mytree['childlist'] = result.data; this.data = mytree; console.log(this.data); }); }, } } </script> <style> .on { background: #cdcbff; } .off { background: #fefdff; } </style>最后请求页面,可以看到展示的效果:
当然了,vue组件也可以间接的调用自己实现递归,这涉及到组件之间的循环引用,这样比较麻烦,可读性也间接的降低了不少。