【wiki知识库】05.分类管理实现--前端Vue模块

  📝个人主页:哈__

期待您的关注 

目录

一、🔥今日目标

二、🌏前端部分的改造

2.1 新增一个tool.ts 

2.2 新增admin-categoty.vue

2.3 添加新的路由规则

2.4 添加the-welcome.vue

2.5 修改HomeView.vue

 三、❗注意


一、🔥今日目标

【wiki知识库】04.SpringBoot后端实现电子书的增删改查以及前端界面的展示-CSDN博客

上回带大家把电子书管理的模块做了出来,能够实现电子书的添加、修改和删除功能了,今天带着大家把分类的接口实现一下。下方我添加了一个分类管理的组件,展示我们当前的所有分类,你可以看到这个分类页面还是一个树形结构的。

除了分类管理,我们的首页也变动了一下。首页的导航栏加载的是我们已经有的分类,同时还加上了一个欢迎页面。

 

二、🌏前端部分的改造

2.1 新增一个tool.ts 

在util包下建立一个tool.ts文件,这个文件是我们的工具文件,之前的代码可能也用到过了,我忘记给大家了。

export class Tool {/*** 空校验 null或""都返回true*/public static isEmpty(obj: any) {if ((typeof obj === 'string')) {return !obj || obj.replace(/\s+/g, "") === ""} else {return (!obj || JSON.stringify(obj) === "{}" || obj.length === 0);}}/*** 非空校验*/public static isNotEmpty(obj: any) {return !this.isEmpty(obj);}/*** 对象复制* @param obj*/public static copy(obj: object) {if (Tool.isNotEmpty(obj)) {return JSON.parse(JSON.stringify(obj));}}/*** 使用递归将数组转为树形结构* 父ID属性为parent*/public static array2Tree(array: any, parentId: number) {if (Tool.isEmpty(array)) {return [];}const result = [];for (let i = 0; i < array.length; i++) {const c = array[i];// console.log(Number(c.parent), Number(parentId));if (Number(c.parent) === Number(parentId)) {result.push(c);// 递归查看当前节点对应的子节点const children = Tool.array2Tree(array, c.id);if (Tool.isNotEmpty(children)) {c.children = children;}}}return result;}/*** 随机生成[len]长度的[radix]进制数* @param len* @param radix 默认62* @returns {string}*/public static uuid(len: number, radix = 62) {const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');const uuid = [];radix = radix || chars.length;for (let i = 0; i < len; i++) {uuid[i] = chars[0 | Math.random() * radix];}return uuid.join('');}
}

2.2 新增admin-categoty.vue

在admin包下,创建admin-category.vue,这个组件用来展示我们的分类信息。这一部分我带着大家稍微过一下。


分类添加功能:

在我们点击添加或者编辑功能的时候,会把下边的代码以一个窗口的模式弹出,在这个窗口中展示了当前分类的名称,当前分类的父分类是谁以及当前分类的分类序号。在我们为一个分类进行添加或者修改的时候,我们都要涉及到这个分类到底是第一分类还是第二分类的问题,我们使用了一个level1变量来保存我们的分类结构,这个结构下边在讲。

<a-modaltitle="分类表单"v-model:visible="modalVisible":confirm-loading="modalLoading"@ok="handleModalOk"><a-form :model="category" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }"><a-form-item label="名称"><a-input v-model:value="category.name" /></a-form-item><a-form-item label="父分类"><a-selectv-model:value="category.parent"ref="select"><a-select-option :value="0">无</a-select-option><a-select-option v-for="c in level1" :key="c.id" :value="c.id" :disabled="category.id === c.id">{{c.name}}</a-select-option></a-select></a-form-item><a-form-item label="顺序"><a-input v-model:value="category.sort" /></a-form-item></a-form></a-modal>

我们的分类结构可以用一张图来表示。 我给每一个分类都排上了一个编号,第一级分类的parent编号都为0,二级分类的parent编号则要相对应其父分类的编号。


level变量的封装过程:

我们的level变量是和我们全部的分类变量有关的,我们先要获取所有的分类然后对分类进行重新组合。

const handleQuery = () => {loading.value = true;// 如果不清空现有数据,则编辑保存重新加载数据后,再点编辑,则列表显示的还是编辑前的数据level1.value = [];axios.get("/category/all").then((response) => {loading.value = false;const data = response.data;if (data.success) {categorys.value = data.content;console.log("原始数组:", categorys.value);level1.value = [];level1.value = Tool.array2Tree(categorys.value, 0);console.log("树形结构:", level1);} else {message.error(data.message);}});};

这时候打开我们的tool.ts来看一看。你会看到一共有两个参数,第一个参数呢我们传的是一个数据数组,第二个我们传来的是0。

首先我们判断一下传过来的数组是不是空的,如果是空的直接返回,否则的话执行下方逻辑。

首先遍历我们所有的分类,检查每一个分类的父分类的编号是不是我们传过来的0,这里你应该会理解为什么这样做,因为我们要把一个数据数据重新格式化成树的形式,那我们一定要先找到这棵树的一级分类,也就是父节点编号为0的分类,找到了之后呢我们把这个分类加到result当中。然后呢我们再次调用array2Tree这个方法,同时传入两个参数,第一个参数还是之前的全部分类的数组,但是第二个参数就不是0了,是我们刚才加入到result中的分类的编号,我们这次调用这个方法的目的是为了找到一级分类的子分类:二级分类。

这样的一次递归调用就可以将数据进行树化。(不懂私信我)

 /*** 使用递归将数组转为树形结构* 父ID属性为parent*/public static array2Tree(array: any, parentId: number) {if (Tool.isEmpty(array)) {return [];}const result = [];for (let i = 0; i < array.length; i++) {const c = array[i];// console.log(Number(c.parent), Number(parentId));if (Number(c.parent) === Number(parentId)) {result.push(c);// 递归查看当前节点对应的子节点const children = Tool.array2Tree(array, c.id);if (Tool.isNotEmpty(children)) {c.children = children;}}}return result;}

 全部代码如下:

<template><a-layout><a-layout-content:style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }"><p><a-form layout="inline" :model="param"><a-form-item><a-input v-model:value="param.name" placeholder="名称"></a-input></a-form-item><a-form-item><a-button type="primary" @click="handleQuery()">查询</a-button></a-form-item><a-form-item><a-button type="primary" @click="add()">新增</a-button></a-form-item></a-form></p><p><a-alertclass="tip"message="小提示:这里的分类会显示到首页的侧边菜单"type="info"closable/></p><a-tablev-if="level1.length > 0":columns="columns":row-key="record => record.id":data-source="level1":loading="loading":pagination="false":defaultExpandAllRows="true"><template #cover="{ text: cover }"><img v-if="cover" :src="cover" alt="avatar" /></template><template v-slot:action="{ text, record }"><a-space size="small"><a-button type="primary" @click="edit(record)">编辑</a-button><a-popconfirmtitle="删除后不可恢复,确认删除?"ok-text="是"cancel-text="否"@confirm="handleDelete(record.id)"><a-button type="danger">删除</a-button></a-popconfirm></a-space></template></a-table></a-layout-content></a-layout><a-modaltitle="分类表单"v-model:visible="modalVisible":confirm-loading="modalLoading"@ok="handleModalOk"><a-form :model="category" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }"><a-form-item label="名称"><a-input v-model:value="category.name" /></a-form-item><a-form-item label="父分类"><a-selectv-model:value="category.parent"ref="select"><a-select-option :value="0">无</a-select-option><a-select-option v-for="c in level1" :key="c.id" :value="c.id" :disabled="category.id === c.id">{{c.name}}</a-select-option></a-select></a-form-item><a-form-item label="顺序"><a-input v-model:value="category.sort" /></a-form-item></a-form></a-modal>
</template><script lang="ts">import { defineComponent, onMounted, ref } from 'vue';import axios from 'axios';import { message } from 'ant-design-vue';import {Tool} from "@/util/tool";export default defineComponent({name: 'AdminCategory',setup() {const param = ref();param.value = {};const categorys = ref();const loading = ref(false);const columns = [{title: '名称',dataIndex: 'name'},// {//   title: '父分类',//   key: 'parent',//   dataIndex: 'parent'// },{title: '顺序',dataIndex: 'sort'},{title: 'Action',key: 'action',slots: { customRender: 'action' }}];/*** 一级分类树,children属性就是二级分类* [{*   id: "",*   name: "",*   children: [{*     id: "",*     name: "",*   }]* }]*/const level1 = ref(); // 一级分类树,children属性就是二级分类level1.value = [];/*** 数据查询**/const handleQuery = () => {loading.value = true;// 如果不清空现有数据,则编辑保存重新加载数据后,再点编辑,则列表显示的还是编辑前的数据level1.value = [];axios.get("/category/all").then((response) => {loading.value = false;const data = response.data;if (data.success) {categorys.value = data.content;console.log("原始数组:", categorys.value);level1.value = [];level1.value = Tool.array2Tree(categorys.value, 0);console.log("树形结构:", level1);} else {message.error(data.message);}});};// -------- 表单 ---------const category = ref({});const modalVisible = ref(false);const modalLoading = ref(false);const handleModalOk = () => {modalLoading.value = true;axios.post("/category/save", category.value).then((response) => {modalLoading.value = false;const data = response.data; // data = commonRespif (data.success) {modalVisible.value = false;// 重新加载列表handleQuery();} else {message.error(data.message);}});};/*** 编辑*/const edit = (record: any) => {modalVisible.value = true;category.value = Tool.copy(record);};/*** 新增*/const add = () => {modalVisible.value = true;category.value = {};};const handleDelete = (id: number) => {axios.delete("/category/delete/" + id).then((response) => {const data = response.data; // data = commonRespif (data.success) {// 重新加载列表handleQuery();} else {message.error(data.message);}});};onMounted(() => {handleQuery();});return {param,// categorys,level1,columns,loading,handleQuery,edit,add,category,modalVisible,modalLoading,handleModalOk,handleDelete}}});
</script><style scoped>img {width: 50px;height: 50px;}
</style>

2.3 添加新的路由规则

  {path: '/admin/category',name: 'AdminCateGory',component: AdminCategory},

2.4 添加the-welcome.vue

在component文件夹下边 添加the-welcome.vue页面。

<template><span>欢迎</span></template><script lang="ts">name: 'the-welcome'
</script>
<style scoped></style>

2.5 修改HomeView.vue

这里做出了一些修改,一个是the-welcome组件的展示,还有一个就是页面侧边栏的展示,我们之前展示的是页面默认的,现在我们要展示数据库当中的分类结构。里边我们有一些代码还用不到,但是我没有注释掉。

<template><a-layout><a-layout-sider width="200" style="background: #fff"><a-menumode="inline":style="{ height: '100%', borderRight: 0 }"@click="handleClick":openKeys="openKeys"><a-menu-item key="welcome"><MailOutlined /><span>欢迎</span></a-menu-item><a-sub-menu v-for="item in level1" :key="item.id" :disabled="true"><template v-slot:title><span><user-outlined />{{item.name}}</span></template><a-menu-item v-for="child in item.children" :key="child.id"><MailOutlined /><span>{{child.name}}</span></a-menu-item></a-sub-menu><a-menu-item key="tip" :disabled="true"><span>以上菜单在分类管理配置</span></a-menu-item></a-menu></a-layout-sider><a-layout-content:style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }"><div class="welcome" v-show="isShowWelcome"><the-welcome></the-welcome></div><a-list v-show="!isShowWelcome" item-layout="vertical" size="large" :grid="{ gutter: 20, column: 3 }" :data-source="ebooks"><template #renderItem="{ item }"><a-list-item key="item.name"><template #actions><span><component v-bind:is="'FileOutlined'" style="margin-right: 8px" />{{ item.docCount }}</span><span><component v-bind:is="'UserOutlined'" style="margin-right: 8px" />{{ item.viewCount }}</span><span><component v-bind:is="'LikeOutlined'" style="margin-right: 8px" />{{ item.voteCount }}</span></template><a-list-item-meta :description="item.description"><template #title><router-link :to="'/doc?ebookId=' + item.id">{{ item.name }}</router-link></template><template #avatar><a-avatar :src="item.cover"/></template></a-list-item-meta></a-list-item></template></a-list></a-layout-content></a-layout>
</template><script lang="ts">
import { defineComponent, onMounted, ref, reactive, toRef } from 'vue';
import axios from 'axios';
import { message } from 'ant-design-vue';
import {Tool} from "@/util/tool";
import TheWelcome from '@/components/the-welcome.vue';export default defineComponent({name: 'Home',components: {TheWelcome},setup() {const ebooks = ref();// const ebooks1 = reactive({books: []});const openKeys =  ref();const level1 =  ref();let categorys: any;/*** 查询所有分类**/const handleQueryCategory = () => {axios.get("/category/all").then((response) => {const data = response.data;if (data.success) {categorys = data.content;console.log("原始数组:", categorys);// 加载完分类后,将侧边栏全部展开openKeys.value = [];for (let i = 0; i < categorys.length; i++) {openKeys.value.push(categorys[i].id)}level1.value = [];level1.value = Tool.array2Tree(categorys, 0);console.log("树形结构:", level1.value);} else {message.error(data.message);}});};const isShowWelcome = ref(true);let categoryId2 = 0;const handleQueryEbook = () => {axios.get("/ebook/list", {params: {page: 1,size: 1000,categoryId2: categoryId2}}).then((response) => {const data = response.data;ebooks.value = data.content.list;// ebooks1.books = data.content;});};const handleClick = (value: any) => {// console.log("menu click", value)if (value.key === 'welcome') {isShowWelcome.value = true;} else {categoryId2 = value.key;isShowWelcome.value = false;handleQueryEbook();}// isShowWelcome.value = value.key === 'welcome';};onMounted(() => {handleQueryCategory();// handleQueryEbook();});return {ebooks,// ebooks2: toRef(ebooks1, "books"),// listData,pagination: {onChange: (page: any) => {console.log(page);},pageSize: 3,},handleClick,level1,isShowWelcome,openKeys}}
});
</script><style scoped>.ant-avatar {width: 50px;height: 50px;line-height: 50px;border-radius: 8%;margin: 5px 0;}
</style>

 三、❗注意

在之前写的admin-ebook.vue当中还有一些代码是注释掉的,因为之前没有写分类模块,现在我们需要使用分类的功能了,我们还需要解除一些注释。大家可以看看哪些地方有关category的被注释掉了,大家可以打开,后端接口下一篇文章就会带大家写出来。

这里需要修改一些onMounted()函数中的代码,修改成下边的部分。

 onMounted(() => {handleQueryCategory();// handleQueryEbook();});

此外,这个网站的标题头部的信息可能我之前没加上去,就是这个。

 在the-header.vue中修改一下自己的代码。

在下边的style中加上样式。然后就可以展示出来了。

<style>.logo {width: 120px;height: 31px;/*background: rgba(255, 255, 255, 0.2);*//*margin: 16px 28px 16px 0;*/float: left;color: white;font-size: 18px;}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/341436.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

The authenticity of host ‘github.com (20.205.243.166)‘ can‘t be established.

目录 github初始化仓库&#xff0c;无法链接 解决无法与主机github.com(20.205.243.166)建立真实性 # 问题原因 # 生成密钥 # 物理路径 # 建立交互 # 验证 github初始化仓库&#xff0c;无法链接 在github创建一个新的仓库时&#xff0c;如果我们未初始化&#xff0c;…

面试题vue+uniapp(个人理解-面试口头答述)未编辑完整....

1.vue2和vue3的区别&#xff08;vue3与vue2的区别&#xff08;你不知道细节全在这&#xff09;_vue2和vue3区别-CSDN博客&#xff09;参考 Vue3 在组合式&#xff08;Composition &#xff09;API&#xff0c;中使用生命周期钩子时需要先引入&#xff0c;而 Vue2 在选项API&am…

操作失败——后端

控制台观察&#xff0c;页面发送的保存菜品的请求 返回的response显示&#xff1a; ---------- 我开始查看明明感觉都挺正常&#xff0c;没啥错误&#xff0c;就是查不出来。结果后面电脑关机重启后&#xff0c;隔一天看&#xff0c;就突然可以了。我觉着可能是浏览器的缓存没…

2022.9.26DAY678

课程学习&#xff1a;《数据处理技术》讲了“数据查询”的语法格式&#xff0c;语法格式也算是简单&#xff0c;就是没能跟之前的内容联系起来&#xff0c;之前的内容没有及时回顾。 高等数学&#xff1a;“ 函数的概念”&#xff0c;讲了函数的概念&#xff0c;反函数&#…

登录通用解决方案 —— 第三方登录处理

目录 01: 前言 02: 第三方平台登录解决方案流程大解析 03: QQ 开放平台流程大解析 04: QQ 登录对接流程&#xff1a;获取 QQ 用户信息 05: QQ 登录对接流程&#xff1a;跨页面信息传输 06: QQ 登录对接流程&#xff1a;认证是否已注册&#xff0c;完成 QQ …

今日科普:了解、预防、控制高血压

高血压&#xff0c;常被称为“隐形的健康威胁”&#xff0c;许多患者可能在毫无预警的情况下发病&#xff0c;且患病率逐年攀升&#xff0c;同时患者群体逐渐年轻化&#xff0c;高血压虽然难以根治&#xff0c;但并不可怕&#xff0c;真正可怕的是血压长期居高不下&#xff0c;…

MySQL中所有常见知识点汇总

存储引擎 这一张是关于整个存储引擎的汇总知识了。 MySQL体系结构 这里是MySQL的体系结构图&#xff1a; 一般将MySQL分为server层和存储引擎两个部分。 其实MySQL体系结构主要分为下面这几个部分&#xff1a; 连接器&#xff1a;负责跟客户端建立连 接、获取权限、维持和管理…

低代码专题 | 什么是低代码?低代码是什么意思?最详细解释!

什么是低代码&#xff0c;低代码是什么意思&#xff1f;低代码到底有什么用&#xff1f;企业该如何用低代码赋能&#xff1f;......因为现在太多碎片化信息了&#xff0c;所以大家对于一个概念的理解都是零散的。 故给大家开一个专题&#xff0c;将低代码给大家掰开揉碎了讲清…

DevOps全面综述:从概念到实践

一、背景与概述 1.1 DevOps的起源与发展 DevOps&#xff08;Development and Operations的缩写&#xff09;是软件工程领域中的一种文化和实践方法&#xff0c;旨在促进开发团队与运维团队之间的协作&#xff0c;从而实现更高效、更可靠的软件交付。DevOps起源于敏捷软件开发方…

php实现抖音小程序支付

开发者发起下单_小程序_抖音开放平台 第一步、抖音小程序发起支付 tt.pay_小程序_抖音开放平台 前端提交订单数据到后端接口&#xff0c;然后使用 tt.pay发起支付 请求参数 属性 类型 必填 说明 order_id string 是 担保交易服务端订单号 order_token string 是 …

RocketMq源码解析五:生产者Producer发送消息

上一章我们把生产者启动的流程和大家一起跟着源码走了一遍,现在我们来看发送消息的流程。上一章我们已经把核心接口和类关系梳理了一遍。如下图 我们今天重点看MQProducer中的send方法最终的实现。DefaultMQProducer中,send的实现最终还是调用了 defaultMQProducerIm…

寺庙小程序-H5网页开发

大家好&#xff0c;我是程序员小孟。 现在有很多的产品或者工具都开始信息话了&#xff0c;寺庙或者佛教也需要小程序吗&#xff1f; 当然了&#xff01; 前面我们还开发了很多寺庙相关的小程序。 今天要介绍的是一款寺庙系统&#xff0c;该系统可以作为小程序、H5网页、安…

【面经】亚信科技面试问题合集

下述内容经搜寻广大平台的面试经历&#xff0c;整理汇合得出&#xff0c;答案来自chatgpt&#xff0c;加黑的地方意味着出现多次。 1.自我介绍 2.介绍项目功能 3.和equals的区别。八大基本类型&#xff08;byte,char,int ,long,double,float,boolean,short) 是用于比较两个…

LeetCode-43. 字符串相乘【数学 字符串 模拟】

LeetCode-43. 字符串相乘【数学 字符串 模拟】 题目描述&#xff1a;解题思路一&#xff1a;模拟乘法&#xff0c;两个数中每一位数相乘的时候乘上他们各自的进制数&#xff0c;之后求和。循环时&#xff0c;分别记录各自的进制数背诵版&#xff1a;解题思路三&#xff1a;0 题…

九、参数处理器

debug调试&#xff0c;一个参数的调通了&#xff0c;但是两个参数的会失败 总结一下&#xff1a; 到现在已经学了有10节了&#xff0c;我对mybatis底层的执行流程算是挺了解的了&#xff0c;把流程拆解开&#xff0c;每一个小步骤都是非常多的代码实现&#xff0c;代码都能看懂…

day25-XML

1.xml 1.1概述【理解】 1.2语法规则【应用】 1.5DTD约束【理解】 1.6schema约束【理解】 1.4xml解析【应用】 概述 xml解析就是从xml中获取到数据 常见的解析思想 DOM(Document Object Model)文档对象模型:就是把文档的各个组成部分看做成对应的对象。 会把xml文件全部加载到…

STM32作业实现(六)闪存保存数据

目录 STM32作业设计 STM32作业实现(一)串口通信 STM32作业实现(二)串口控制led STM32作业实现(三)串口控制有源蜂鸣器 STM32作业实现(四)光敏传感器 STM32作业实现(五)温湿度传感器dht11 STM32作业实现(六)闪存保存数据 STM32作业实现(七)OLED显示数据 STM32作业实现(八)触摸按…

Ubuntu系统配置DDNS-GO【笔记】

DDNS-GO 是一个基于 Go 语言的动态 DNS (DDNS) 客户端&#xff0c;用于自动更新你的 IP 地址到 DNS 记录上。这对于经常变更 IP 地址的用户&#xff08;如使用动态 IP 的家庭用户或者小型服务器&#xff09;非常有用。 此文档实验环境为&#xff1a;ubuntu20.04.6。 在Ubuntu…

查看 samba 文件共享服务器地址的具体 IP

问题背景 在某个局域网中&#xff0c;已知 samba 文件共享服务器的地址如 \\samba_share在该局域网的子网中&#xff0c;由于 dns 服务器缺失&#xff0c;无法通过地址 \\samba_share 直接访问该服务器 解决方法 使用 ping 命令查看某个地址的 ip &#xff1a; ping [addre…

大模型系列:大模型tokenizer分词编码算法BPE理论简述和实践

前言 token是大模型处理和生成语言文本的基本单位&#xff0c;在之前介绍的Bert和GPT-2中&#xff0c;都是简单地将中文文本切分为单个汉字字符作为token&#xff0c;而目前LLaMA&#xff0c;ChatGLM等大模型采用的是基于分词工具sentencepiece实现的BBPE&#xff08;Byte-lev…