赞
踩
前言
在一些消费的app,eg京东,淘宝。我们经常在pc端上面会出现点击电视 ,就会有小米、海尔、等各种品牌的电视。这也就是咱们说的树形组件 --也就是一级标签,二级标签,三级标签。(以上是小编举的例子,如有不对请多指教包容)
这个篇文章主要的效果图
既然说了是Django+vue3+el-tree就了一列大纲他的步骤分那些
那小编带着来第一步
1.菜单表:id,name(菜单名),pid(父类),status(1显示0不显示)
- class Menu(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=38, verbose_name='菜单名称')
- pid = models.IntegerField(default=0, verbose_name='父id')
- status = models.IntegerField(default=1, verbose_name='显示1不显示0')
-
- def __str__(self):
- return self.name
-
- class Meta:
- db_table = 'meum_menu'
增加菜单
查看菜单
修改菜单
删除菜单
添加菜单接口
参数名称 | 参数类型 | 参数描述 | 实例 |
name | string | 菜单名称 | 冰箱 |
Pid | int | 父类id,默认0 | 0 |
status | int | 状态0不显示1显示 | 1 |
返回值:
成功返回:{“code”:”200”,’msg’:”添加成功”}
失败返回:{“code”:”500”,”msg”:”添加失败”}
- class AddMenuViews(APIView):
- """添加菜单
- 获取前端传来的数据,
- 判断 菜单名字是空的?
- 判断 改子类的父类是否存在 id=pid
- """
-
- def post(self, request):
- name = request.data.get('name')
- pid = request.data.get('pid', 0)
- print('pid>>>>>>>>>', pid)
- status = request.data.get('status', 1)
- if not all([name]):
- return Response({'msg': '菜单名称为空', 'code': 500})
- if pid:
- pid_count = Menu.objects.filter(id=pid).count()
- if pid_count == 0:
- return Response({'msg': '该类别的父类id不存在', 'code': 500})
- menu = Menu.objects.create(name=name, pid=pid, status=status)
- menu.save()
- 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的来排序----小编刚接触的时候也比较迷糊慢慢就好了
- class GetMenuViews(APIView):
- def get(self, request):
- # 获取显示的菜单
- menu = Menu.objects.filter(status=1)
- # 将获取的数据转换为json数据 对象名=序列化器(内容)
- #多个数据是many=True
- ser = MenuSer(menu, many=True)
- #.data 是返回数据
- print('ser.data', ser.data)
- result = xtree(ser.data)
- return Response({'code': 200, 'data': result})
序列化---在django中序列化是需要自己写的
小编在这用的是模型类序列化器
- from rest_framework import serializers
- from meum.models import Menu
-
-
- class MenuSer(serializers.ModelSerializer):
- class Meta:
- model = Menu # 指明需要序列化的模型类
- fields = '__all__' # 指明模型类的全部字段参数序列化和反序列化
其实那可能在我看来最重要的不是这些 而是在展示的时候的思路 怎样把一级标签相连的二级标签 展示出来 用了什么思路 那就具体问题具体分析
在代码具体解析
tree = {} for i in data: # 数的第一条数据就 变成data中i循环的第一条数据 tree[i['id']] = i
然后在重新循环data数据
查看pid 是不是0 如果是0则是一级标签
如果不是就判断下 ----子类的父类下面是否有了子类
如果没有就在子类下面添加一个空列表
如果有 就在父类下面添加子类这条数据
- def xtree(data):
- # 如果数据长度小于0,则返回数据
- if len(data) <= 0:
- return data
- # 如果长度>0 则进行下面的
- # 对数据重新布局
- tree = {}
- for i in data:
- # 数的第一条数据就 变成data中i循环的第一条数据
- # print('i', i)
- # print('i["id"]', i['id'])
- tree[i['id']] = i
- # print('tree[i["id"]]', tree[i['id']])
- dlist = []
- for j in data:
- # 查看pid是否是0,0为一级标签
- # j['pid'] 就是data 数据中 每循环一条的数据中的pid
- print('j',j)
- pid = j['pid']
- if pid == 0:
- dlist.append(j)
- else:
- # 判断此子类的父类下面是否已经有了子类
- # 如果没有得到这pid 就跳出本次循环进入下一次循环
- if not tree.get(pid,None):
- continue
- # 如果children不在tree[pid]里面
- # 则就添加一个空列表
- # 否则就是在添加children一条数据
- if 'children' not in tree[pid]:
- tree[pid]['children'] = []
- tree[pid]['children'].append(j)
- return dlist
修改菜单接口
参数 | 类型 | 参数描述 | 实例 |
Id | int | 菜单id | 1 |
name | string | 菜单名字 | 冰箱 |
返回值:
成功返回:{“code”:”200”,’msg’:”修改成功”}
失败返回:{“code”:”500”,”msg”:”修改失败”}
- class UpdateMenuViews(APIView):
- """修改菜单
- 判断 是否有空
- 判断 是否有这条数据
- 判断 这修改的名字是否重复
- 获取到这个对象get
- 进行修改
- """
-
- def post(self, request):
- id = request.data.get('id')
- name = request.data.get('name')
- if not all([name, id]):
- return Response({'msg': '菜单信息不能为空', 'code': 500})
- menu = Menu.objects.filter(id=id).first()
- if not menu:
- return Response({'msg': '没有该菜单,请输入正确的菜单id', 'code': 500})
- name_num = Menu.objects.filter(name=name).count()
- if name_num != 0:
- return Response({'msg': '该菜单名字已经重复,请重新输入', 'code': 500})
- menu.name = name
- menu.save()
- 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”:”删除失败”}
-
- class DeleteMenuViews(APIView):
- """删除
- 通过id进行删除
- 判断改id是否存在
- 存在了然后把状态修改为不显示
- """
-
- def put(self, request, id):
- try:
- menu = Menu.objects.filter(id=id).first()
- if not menu:
- return Response({'msg': "没有该菜单id,请输入正确的id", 'code': 500})
- # 相当删除 ---把修改的数据状态改了
- menu = Menu.objects.filter(id=id).update(status=0)
- # menu.save()
- # ser = MenuSer(menu, many=True)
- return Response({'msg': "删除成功"})
- except:
- error = traceback.format_exc()
- print('error', error)
- return Response({'code': 500, 'error': error})
其实简单的肯定在学的时候都接触过 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
- from django.contrib import admin
- from meum.models import Menu
-
-
- # Register your models here.
- @admin.register(Menu)
- class MenuAdmin(admin.ModelAdmin):
- """账号后台管理"""
- list_display = ('id', 'name', 'pid', 'status') # 展示字段
- list_filter = ('name', 'pid', 'status')#过滤字段
- list_per_page = 20 # 分页
- search_fields = ('name', 'pid', 'status')#搜索字段
如果刚安装的小伙伴 肯定是没有elementui组件 下载的命令来了npm install element-plus --save
在main.js里就要加上下面代码
- import ElementPlus from 'element-plus'
- import 'element-plus/theme-chalk/index.css'
- const app = createApp(App);
- app.use(ElementPlus)
如果没有下载vant 下载的命令来了npm i vant -S
在main.js里就要加上下面代码
- import Vant from 'vant';
- import 'vant/lib/index.css';
- app.use(Vant)
肯定小伙伴们第一时间想到的是官方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)
-->
- <template>
-
- <div>
- <el-tree
- :data="data"
- :props="defaultProps"
- @node-click="handleNodeClick"
- ></el-tree>
- </div>
-
- </template>
-
-
-
- <script>
- export default {
- data() {
- return {
- username: "",
- password: "",
- data: [
- {
- label: "一级 1",
- children: [
- {
- label: "二级 1-1",
- children: [
- {
- label: "三级 1-1-1",
- },
- ],
- },
- ],
- },
- {
- label: "一级 2",
- children: [
- {
- label: "二级 2-1",
- children: [
- {
- label: "三级 2-1-1",
- },
- ],
- },
- {
- label: "二级 2-2",
- children: [
- {
- label: "三级 2-2-1",
- },
- ],
- },
- ],
- },
- {
- label: "一级 3",
- children: [
- {
- label: "二级 3-1",
- children: [
- {
- label: "三级 3-1-1",
- },
- ],
- },
- {
- label: "二级 3-2",
- children: [
- {
- label: "三级 3-2-1",
- },
- ],
- },
- ],
- },
- ],
- defaultProps: {
- children: "children",
- label: "label",
- },
- };
- },
- methods: {},
- mounted() {},
- };
- </script>
-
- <style>
- </style>
这个功能能肯定 和后端挂钩啊
具体的解释都在代码块中
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的官方文档
- <template>
- <div>
- <van-row style="text-align: left; background-color: #ffff">
- <van-col span="3" style="background-color: #545c64; height: 100rem">
- </van-col>
-
- <van-col span="21">
- 添加分类
- <el-tree
- :data="data"
- node-key="id"
- default-expand-all
- :props="defaultProps"
- @node-click="handleNodeClick"
- >
- <!-- 想当于for循环 -->
- <template #default="{ node, data }">
- <span class="custom-tree-node">
- <span>{{ node.label }}</span>
- <span>
- <!-- 添加点击浮框 -->
- <el-button text @click="add_id(data)">添加</el-button>
- <el-dialog
- v-model="dialogVisible"
- title="Tips"
- width="30%"
- :before-close="handleClose"
- >
- 菜单名:<input type="text" v-model="cname" />
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="append()">提交</el-button>
- <el-button type="primary" @click="dialogVisible = false"
- >退出</el-button
- >
- </span>
- </template>
- </el-dialog>
-
- <!-- 修改 -->
- <el-button text @click="put_update(data)">修改</el-button>
- <el-dialog
- v-model="MenuUpdate"
- title="Tips"
- width="30%"
- :before-close="handleClose"
- >
- 菜单名:<input type="text" v-model="cname" />
- <template #footer>
- <span class="dialog-footer">
- <el-popconfirm
- confirm-button-text="Yes"
- cancel-button-text="No"
- :icon="InfoFilled"
- icon-color="#626AEF"
- title="确认修改"
- @confirm="updatecates()"
- @cancel="cancelEvent"
- >
- <template #reference>
- <el-button>提交</el-button>
- </template>
- </el-popconfirm>
- <el-button type="primary" @click="MenuUpdate = false"
- >退出</el-button
- >
- </span>
- </template>
- </el-dialog>
-
-
- <!-- 删除 -->
- <el-button text @click="remove(data)">删除</el-button>
-
- </span>
- </span>
- </template>
- </el-tree>
- </van-col>
- </van-row>
- </div>
- <div></div>
- </template>
-
- <script>
- import Axios from "axios";
- export default {
- data() {
- return {
- name: "",
- menulist: [],
- dialogVisible: false,
- MenuUpdate: false,
- data: [],
- defaultProps: {
- children: "children",
- label: "name",
- },
- pid: 0,
- cname: "",
- cateid: 0,
-
- };
- },
- components: {
-
- },
- methods: {
- handleNodeClick(data) {
- console.log("handleNodeClick", data.name);
- },
- getcatelist() {
- Axios.get("/meum/get_menu/")
- .then((resp) => {
- console.log(resp.data);
- if (resp.data.code == 200) {
- console.log(resp.data);
- this.data = resp.data.data;
- }
- })
- .catch((err) => {
- alert("获取失败");
- console.log(err);
- });
- },
- // 获取点击添加 某一条数据中的id 变为添加的新数据的父id
- add_id(data) {
- console.log("data添加",data);
- this.dialogVisible= true;
- this.pid = data.id;
- },
- // 获取点击修改时 某一条数据中的id 变为修改这条数据的id 修改数据id=原id 进行了传值
- put_update(data) {
- console.log("11111修改", data);
- this.id = data.id;
- this.cname=data.name
- this.MenuUpdate = true;
- },
-
- // 增加菜单-----添加是为了获取点击添加这条数据的id 变为新数据的pid父id ----然后再添加他的新名字就可以了
- append() {
- Axios.post("/meum/add_menu/", { name: this.cname, pid: this.pid }).then(
- (resp) => {
- if (resp.data.code == 200) {
- this.show = false;
- this.cname = "";
- this.cateid = 0;
- // tis.getcatelist() 相当于自动刷新
- this.getcatelist();
- this.dialogVisible = false;
- } else {
- alert("添加数据失败" + resp.data.message);
- }
- }
- );
- },
-
- // 修改菜单-----修改是修改的这个id的数据名字 要传入之前的id和之前数据的名字
- updatecates() {
- // console.log("data.id修改>>>>", data.id);
- Axios.post("/meum/update_menu/", { name: this.cname, id: this.id }).then(
- (resp) => {
- if (resp.data.code == 200) {
- this.getcatelist();
- this.MenuUpdate = false;
- } else {
- alert("修改数据失败" + resp.data.msg);
- }
- }
- );
- },
- remove(data) {
- // 删除 点击删除获取到删除这条数据的data 然后从data里面获取到这条数据的id 变成传入数据的id
- var id = data.id;
- Axios.put("/meum/delete_menu/" + id + "/").then((res) => {
- console.log("id>>>", id);
- if ((res.data.code = 200)) {
- this.getcatelist();
- }
- });
- },
- },
- mounted() {
- this.getcatelist();
- // // this.test2()
- // // 从localStorage读取
- var resourcelist = localStorage.getItem("resourcelist");
- this.menulist = JSON.parse(resourcelist);
- this.menulist = ["menu/", "/add_menu"];
- },
- };
- </script>
-
- <style>
- </style>
文章到这也就结束,也希望可以帮助到大家,有问题请多多指教
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。