当前位置:   article > 正文

django+vue3+el-tree树形组件菜单_vue3 el-tree

vue3 el-tree

前言

在一些消费的app,eg京东,淘宝。我们经常在pc端上面会出现点击电视 ,就会有小米、海尔、等各种品牌的电视。这也就是咱们说的树形组件 --也就是一级标签,二级标签,三级标签。(以上是小编举的例子,如有不对请多指教包容)


这个篇文章主要的效果图

既然说了是Django+vue3+el-tree就了一列大纲他的步骤分那些

1.分析django 中所需要的表

2.django 中主要实现的业务逻辑

2.1django 重写后台管理

3.vue3中使用elementUI配置

4.vue3使用el-tree的布局

5.el-tree上附加的功能


那小编带着来第一步

1.分析django 中所需要的表

1.菜单表:id,name(菜单名),pid(父类),status(1显示0不显示)

  1. class Menu(models.Model):
  2. id = models.AutoField(primary_key=True)
  3. name = models.CharField(max_length=38, verbose_name='菜单名称')
  4. pid = models.IntegerField(default=0, verbose_name='父id')
  5. status = models.IntegerField(default=1, verbose_name='显示1不显示0')
  6. def __str__(self):
  7. return self.name
  8. class Meta:
  9. db_table = 'meum_menu'

2.django 中主要实现的业务逻辑

增加菜单

查看菜单

修改菜单

删除菜单


添加菜单接口

  1. 请求:post
  2. Api:http://127.0.0.1:8000/meum/add_menu/
  3. 描述:添加菜单名,父类,状态(显示不显示)
  4. 添加条件         获取前端传来的数据,
                          判断 菜单名字是空的?
                          判断 改子类的父类是否存在 id=pid  

参数名称

参数类型

参数描述

实例

name

string

菜单名称

冰箱

Pid

int

父类id,默认0

0

status

int

状态0不显示1显示

1

返回值:

成功返回:{“code”:”200”,’msg’:”添加成功”}

失败返回:{“code”:”500”,”msg”:”添加失败”}

  1. class AddMenuViews(APIView):
  2. """添加菜单
  3. 获取前端传来的数据,
  4. 判断 菜单名字是空的?
  5. 判断 改子类的父类是否存在 id=pid
  6. """
  7. def post(self, request):
  8. name = request.data.get('name')
  9. pid = request.data.get('pid', 0)
  10. print('pid>>>>>>>>>', pid)
  11. status = request.data.get('status', 1)
  12. if not all([name]):
  13. return Response({'msg': '菜单名称为空', 'code': 500})
  14. if pid:
  15. pid_count = Menu.objects.filter(id=pid).count()
  16. if pid_count == 0:
  17. return Response({'msg': '该类别的父类id不存在', 'code': 500})
  18. menu = Menu.objects.create(name=name, pid=pid, status=status)
  19. menu.save()
  20. return Response({'msg': '添加成功', 'code': 200})

查看菜单接口

1.请求:get

2.Api:http://127.0.0.1:8000/meum/get_menu/

3.描述:获取菜单中status=1的数据,进行排列根据pid进行分级

过滤条件

Status=1

Pid布局

查看pid 是否是0,0 为一级标签,添加并返回

判断此子类的父类下面是否已经有了子类,如果没有就初始化

返回值:

成功返回:{“code”:”200”,’msg’:”查看成功”}/{“code”:200,”data”:result}

                                                                     result承接的是序列化之后返回的数据

失败返回:{“code”:”500”,”msg”:”查看失败”}

其实难点还是在获取数据 因为他需要按照pid的来排序----小编刚接触的时候也比较迷糊慢慢就好了

  1. class GetMenuViews(APIView):
  2. def get(self, request):
  3. # 获取显示的菜单
  4. menu = Menu.objects.filter(status=1)
  5. # 将获取的数据转换为json数据 对象名=序列化器(内容)
  6. #多个数据是many=True
  7. ser = MenuSer(menu, many=True)
  8. #.data 是返回数据
  9. print('ser.data', ser.data)
  10. result = xtree(ser.data)
  11. return Response({'code': 200, 'data': result})

序列化---在django中序列化是需要自己写的

小编在这用的是模型类序列化器

  1. from rest_framework import serializers
  2. from meum.models import Menu
  3. class MenuSer(serializers.ModelSerializer):
  4. class Meta:
  5. model = Menu # 指明需要序列化的模型类
  6. fields = '__all__' # 指明模型类的全部字段参数序列化和反序列化

其实那可能在我看来最重要的不是这些  而是在展示的时候的思路  怎样把一级标签相连的二级标签 展示出来 用了什么思路  那就具体问题具体分析

在代码具体解析

tree = {}
for i in data:
    # 数的第一条数据就 变成data中i循环的第一条数据
    tree[i['id']] = i

然后在重新循环data数据

查看pid 是不是0 如果是0则是一级标签

如果不是就判断下  ----子类的父类下面是否有了子类

如果没有就在子类下面添加一个空列表

如果有 就在父类下面添加子类这条数据

  1. def xtree(data):
  2. # 如果数据长度小于0,则返回数据
  3. if len(data) <= 0:
  4. return data
  5. # 如果长度>0 则进行下面的
  6. # 对数据重新布局
  7. tree = {}
  8. for i in data:
  9. # 数的第一条数据就 变成data中i循环的第一条数据
  10. # print('i', i)
  11. # print('i["id"]', i['id'])
  12. tree[i['id']] = i
  13. # print('tree[i["id"]]', tree[i['id']])
  14. dlist = []
  15. for j in data:
  16. # 查看pid是否是0,0为一级标签
  17. # j['pid'] 就是data 数据中 每循环一条的数据中的pid
  18. print('j',j)
  19. pid = j['pid']
  20. if pid == 0:
  21. dlist.append(j)
  22. else:
  23. # 判断此子类的父类下面是否已经有了子类
  24. # 如果没有得到这pid 就跳出本次循环进入下一次循环
  25. if not tree.get(pid,None):
  26. continue
  27. # 如果children不在tree[pid]里面
  28. # 则就添加一个空列表
  29. # 否则就是在添加children一条数据
  30. if 'children' not in tree[pid]:
  31. tree[pid]['children'] = []
  32. tree[pid]['children'].append(j)
  33. return dlist

修改菜单接口

  1. 请求:post
  2. Api:http://127.0.0.1:8000/meum/update_menu/
  3. 描述修改:根据id  菜单名称
  4. 修改条件  根据id进行查找这条要修改的数据是否存在,
  5.           根据name查找数据库中是否有重复名字
  6.           然后进行修改menu.objects.filter(id=id).update(name=name)

参数

类型

参数描述

实例

Id

int

菜单id

1

name

string

菜单名字

冰箱

返回值:

成功返回:{“code”:”200”,’msg’:”修改成功”}

失败返回:{“code”:”500”,”msg”:”修改失败”}

  1. class UpdateMenuViews(APIView):
  2. """修改菜单
  3. 判断 是否有空
  4. 判断 是否有这条数据
  5. 判断 这修改的名字是否重复
  6. 获取到这个对象get
  7. 进行修改
  8. """
  9. def post(self, request):
  10. id = request.data.get('id')
  11. name = request.data.get('name')
  12. if not all([name, id]):
  13. return Response({'msg': '菜单信息不能为空', 'code': 500})
  14. menu = Menu.objects.filter(id=id).first()
  15. if not menu:
  16. return Response({'msg': '没有该菜单,请输入正确的菜单id', 'code': 500})
  17. name_num = Menu.objects.filter(name=name).count()
  18. if name_num != 0:
  19. return Response({'msg': '该菜单名字已经重复,请重新输入', 'code': 500})
  20. menu.name = name
  21. menu.save()
  22. return Response({'msg': '该菜单修改成功', 'code': 200})

删除菜单接口

1.请求put

2.apI:http://127.0.0.1:8000/meum/delete_menu/

3.描述删除:通过传参id的形式进行删除--把状态改为status=0

4.删除的条件:根据id进行查找这条要修改的数据是否存在,

                       然后进行修改menu.objects.filter(id=id).update(status=0)

参数

类型

参数描述

修改为

status

int

状态

0(不显示)

返回值:

成功返回:{“code”:”200”,’msg’:”删除成功”}

失败返回:{“code”:”500”,”msg”:”删除失败”}

  1. class DeleteMenuViews(APIView):
  2. """删除
  3. 通过id进行删除
  4. 判断改id是否存在
  5. 存在了然后把状态修改为不显示
  6. """
  7. def put(self, request, id):
  8. try:
  9. menu = Menu.objects.filter(id=id).first()
  10. if not menu:
  11. return Response({'msg': "没有该菜单id,请输入正确的id", 'code': 500})
  12. # 相当删除 ---把修改的数据状态改了
  13. menu = Menu.objects.filter(id=id).update(status=0)
  14. # menu.save()
  15. # ser = MenuSer(menu, many=True)
  16. return Response({'msg': "删除成功"})
  17. except:
  18. error = traceback.format_exc()
  19. print('error', error)
  20. return Response({'code': 500, 'error': error})

2.1django 重写后台管理

其实简单的肯定在学的时候都接触过  admin.site.register(models)

from django.contrib import admin

# Register your models here.
# 在应用app中的admin.py中导入注册要管理的模型models类
from .models import Book
# 调用admin.site.register方法进行注册
admin.site.register(Book)

还有一种叫做模型类管理器

我也不具体说了 ----有需要点击这个链接http://t.csdn.cn/pYrsf

  1. from django.contrib import admin
  2. from meum.models import Menu
  3. # Register your models here.
  4. @admin.register(Menu)
  5. class MenuAdmin(admin.ModelAdmin):
  6. """账号后台管理"""
  7. list_display = ('id', 'name', 'pid', 'status') # 展示字段
  8. list_filter = ('name', 'pid', 'status')#过滤字段
  9. list_per_page = 20 # 分页
  10. search_fields = ('name', 'pid', 'status')#搜索字段

3.vue3中使用elementUI配置

如果刚安装的小伙伴 肯定是没有elementui组件 下载的命令来了npm install element-plus --save

在main.js里就要加上下面代码

  1. import ElementPlus from 'element-plus'
  2. import 'element-plus/theme-chalk/index.css'
  3. const app = createApp(App);
  4. app.use(ElementPlus)

如果没有下载vant   下载的命令来了npm i vant -S

在main.js里就要加上下面代码

  1. import Vant from 'vant';
  2. import 'vant/lib/index.css';
  3. app.use(Vant)

4.vue3使用el-tree的布局

肯定小伙伴们第一时间想到的是官方Tree 树形控件 | Element Plus

那咱就说说简单的tree

<!--
 :data (绑定的数据)
 show-checkbox (节点是否可选择)
 node-key='id' (每个树节点来作为唯一标识的属性)
 :props='defaultProps'(指定label children等属性)
 check-on-click-node (点击节点时就选中复选框 默认值是false 只有点击框   框的时候才选中)
 @node-click (节点点击时的回调)
 :check-strictly="true"(父子不关联 默认为false)
 ref (绑定dom)
-->
 

  1. <template>
  2. <div>
  3. <el-tree
  4. :data="data"
  5. :props="defaultProps"
  6. @node-click="handleNodeClick"
  7. ></el-tree>
  8. </div>
  9. </template>
  10. <script>
  11. export default {
  12. data() {
  13. return {
  14. username: "",
  15. password: "",
  16. data: [
  17. {
  18. label: "一级 1",
  19. children: [
  20. {
  21. label: "二级 1-1",
  22. children: [
  23. {
  24. label: "三级 1-1-1",
  25. },
  26. ],
  27. },
  28. ],
  29. },
  30. {
  31. label: "一级 2",
  32. children: [
  33. {
  34. label: "二级 2-1",
  35. children: [
  36. {
  37. label: "三级 2-1-1",
  38. },
  39. ],
  40. },
  41. {
  42. label: "二级 2-2",
  43. children: [
  44. {
  45. label: "三级 2-2-1",
  46. },
  47. ],
  48. },
  49. ],
  50. },
  51. {
  52. label: "一级 3",
  53. children: [
  54. {
  55. label: "二级 3-1",
  56. children: [
  57. {
  58. label: "三级 3-1-1",
  59. },
  60. ],
  61. },
  62. {
  63. label: "二级 3-2",
  64. children: [
  65. {
  66. label: "三级 3-2-1",
  67. },
  68. ],
  69. },
  70. ],
  71. },
  72. ],
  73. defaultProps: {
  74. children: "children",
  75. label: "label",
  76. },
  77. };
  78. },
  79. methods: {},
  80. mounted() {},
  81. };
  82. </script>
  83. <style>
  84. </style>

5.el-tree上附加的功能

这个功能能肯定 和后端挂钩啊

具体的解释都在代码块中

vue2中el-tree中是使用 scoped slot 会传入两个参数node和data,分别表示当前节点的 Node 对象和当前节点的数据。
vue2中的 scoped slot 位于span标签内
vue3中el-tree则是使用#default传入两个参数node和data,分别表示当前节点的 Node 对象和当前节点的数据。
vue3中的#default位于template标签内
vue3在使用elemnt ui是应该参考element plus的官方文档

  1. <template>
  2. <div>
  3. <van-row style="text-align: left; background-color: #ffff">
  4. <van-col span="3" style="background-color: #545c64; height: 100rem">
  5. </van-col>
  6. <van-col span="21">
  7. 添加分类
  8. <el-tree
  9. :data="data"
  10. node-key="id"
  11. default-expand-all
  12. :props="defaultProps"
  13. @node-click="handleNodeClick"
  14. >
  15. <!-- 想当于for循环 -->
  16. <template #default="{ node, data }">
  17. <span class="custom-tree-node">
  18. <span>{{ node.label }}</span>
  19. <span>
  20. <!-- 添加点击浮框 -->
  21. <el-button text @click="add_id(data)">添加</el-button>
  22. <el-dialog
  23. v-model="dialogVisible"
  24. title="Tips"
  25. width="30%"
  26. :before-close="handleClose"
  27. >
  28. 菜单名:<input type="text" v-model="cname" />
  29. <template #footer>
  30. <span class="dialog-footer">
  31. <el-button @click="append()">提交</el-button>
  32. <el-button type="primary" @click="dialogVisible = false"
  33. >退出</el-button
  34. >
  35. </span>
  36. </template>
  37. </el-dialog>
  38. <!-- 修改 -->
  39. <el-button text @click="put_update(data)">修改</el-button>
  40. <el-dialog
  41. v-model="MenuUpdate"
  42. title="Tips"
  43. width="30%"
  44. :before-close="handleClose"
  45. >
  46. 菜单名:<input type="text" v-model="cname" />
  47. <template #footer>
  48. <span class="dialog-footer">
  49. <el-popconfirm
  50. confirm-button-text="Yes"
  51. cancel-button-text="No"
  52. :icon="InfoFilled"
  53. icon-color="#626AEF"
  54. title="确认修改"
  55. @confirm="updatecates()"
  56. @cancel="cancelEvent"
  57. >
  58. <template #reference>
  59. <el-button>提交</el-button>
  60. </template>
  61. </el-popconfirm>
  62. <el-button type="primary" @click="MenuUpdate = false"
  63. >退出</el-button
  64. >
  65. </span>
  66. </template>
  67. </el-dialog>
  68. <!-- 删除 -->
  69. <el-button text @click="remove(data)">删除</el-button>
  70. </span>
  71. </span>
  72. </template>
  73. </el-tree>
  74. </van-col>
  75. </van-row>
  76. </div>
  77. <div></div>
  78. </template>
  79. <script>
  80. import Axios from "axios";
  81. export default {
  82. data() {
  83. return {
  84. name: "",
  85. menulist: [],
  86. dialogVisible: false,
  87. MenuUpdate: false,
  88. data: [],
  89. defaultProps: {
  90. children: "children",
  91. label: "name",
  92. },
  93. pid: 0,
  94. cname: "",
  95. cateid: 0,
  96. };
  97. },
  98. components: {
  99. },
  100. methods: {
  101. handleNodeClick(data) {
  102. console.log("handleNodeClick", data.name);
  103. },
  104. getcatelist() {
  105. Axios.get("/meum/get_menu/")
  106. .then((resp) => {
  107. console.log(resp.data);
  108. if (resp.data.code == 200) {
  109. console.log(resp.data);
  110. this.data = resp.data.data;
  111. }
  112. })
  113. .catch((err) => {
  114. alert("获取失败");
  115. console.log(err);
  116. });
  117. },
  118. // 获取点击添加 某一条数据中的id 变为添加的新数据的父id
  119. add_id(data) {
  120. console.log("data添加",data);
  121. this.dialogVisible= true;
  122. this.pid = data.id;
  123. },
  124. // 获取点击修改时 某一条数据中的id 变为修改这条数据的id 修改数据id=原id 进行了传值
  125. put_update(data) {
  126. console.log("11111修改", data);
  127. this.id = data.id;
  128. this.cname=data.name
  129. this.MenuUpdate = true;
  130. },
  131. // 增加菜单-----添加是为了获取点击添加这条数据的id 变为新数据的pid父id ----然后再添加他的新名字就可以了
  132. append() {
  133. Axios.post("/meum/add_menu/", { name: this.cname, pid: this.pid }).then(
  134. (resp) => {
  135. if (resp.data.code == 200) {
  136. this.show = false;
  137. this.cname = "";
  138. this.cateid = 0;
  139. // tis.getcatelist() 相当于自动刷新
  140. this.getcatelist();
  141. this.dialogVisible = false;
  142. } else {
  143. alert("添加数据失败" + resp.data.message);
  144. }
  145. }
  146. );
  147. },
  148. // 修改菜单-----修改是修改的这个id的数据名字 要传入之前的id和之前数据的名字
  149. updatecates() {
  150. // console.log("data.id修改>>>>", data.id);
  151. Axios.post("/meum/update_menu/", { name: this.cname, id: this.id }).then(
  152. (resp) => {
  153. if (resp.data.code == 200) {
  154. this.getcatelist();
  155. this.MenuUpdate = false;
  156. } else {
  157. alert("修改数据失败" + resp.data.msg);
  158. }
  159. }
  160. );
  161. },
  162. remove(data) {
  163. // 删除 点击删除获取到删除这条数据的data 然后从data里面获取到这条数据的id 变成传入数据的id
  164. var id = data.id;
  165. Axios.put("/meum/delete_menu/" + id + "/").then((res) => {
  166. console.log("id>>>", id);
  167. if ((res.data.code = 200)) {
  168. this.getcatelist();
  169. }
  170. });
  171. },
  172. },
  173. mounted() {
  174. this.getcatelist();
  175. // // this.test2()
  176. // // 从localStorage读取
  177. var resourcelist = localStorage.getItem("resourcelist");
  178. this.menulist = JSON.parse(resourcelist);
  179. this.menulist = ["menu/", "/add_menu"];
  180. },
  181. };
  182. </script>
  183. <style>
  184. </style>

文章到这也就结束,也希望可以帮助到大家,有问题请多多指教

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

闽ICP备14008679号