【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.mzph.cn/diannao/22736.shtml

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

相关文章

车来了冲刺上市:业绩波动明显,依赖广告业务,滴滴、阿里入股

近日&#xff0c;MetaLight Inc.&#xff08;下称“元光科技”或“车来了”&#xff09;向港交所递交招股说明书&#xff0c;中金公司为其独家保荐人。 据招股书介绍&#xff0c;元光科技专注于利用时序数据&#xff08;按时间顺序排列的数据点&#xff09;来发现及预测分析对…

docker 存储 网络 命令

文章目录 1 docker存储1.1 目录挂载2.1卷映射2.1.1卷映射和目录挂载的区别2.1.2卷映射的使用 2 docker网络2.1查看docker的默认网络2.2查看容器的IP2.3容器互通2.4自定义网络2.4.1 创建自定义网络2.4.2创建容器的时候加入到自定义的网络2.4.3使用域名进行容器之间的访问2.4.4re…

盛凌电子IPO终止:实控人蒋志坚曾和前妻黄新打官司,儿子已离职

保荐人撤销&#xff0c;致使公司IPO终止。 近日&#xff0c;深圳证券交易所披露的信息显示&#xff0c;深圳盛凌电子股份有限公司&#xff08;下称“盛凌电子”&#xff09;的保荐人申万宏源证券撤回上市申请文件。因此&#xff0c;深圳证券交易所决定终止对该公司首次公开发行…

Qt报错:libvlc开发的程序,出现Direct3D output全屏窗口

问题描述&#xff1a; 在qt中开发重播模块时&#xff0c;第一次在窗口正常播放&#xff0c;点击重播按钮后会弹出新的Direct3D output窗口播放视频 分析&#xff1a; 因为libvlc_media_player_set_hwnd 这个函数 设置了不存在的窗口句柄&#xff0c;导致vlc视频播放窗口没有嵌…

kubesz(一键安装k8s)

引言 Kubernetes&#xff08;K8s&#xff09;是一个开源的容器编排系统&#xff0c;用于自动化部署、扩展和管理容器化应用程序。kubeasz 是一个用于快速搭建 Kubernetes 高可用集群的项目&#xff0c;它基于 Ansible&#xff0c;通过提供一套简单、易用的配置&#xff0c;使得…

用易查分制作活动抽奖系统,支持随机分配中奖结果!

学校或企业在开展抽奖活动时&#xff0c;如何确保公平公正&#xff0c;随机挑选中奖人员呢&#xff1f; 易查分的预置数据分配功能就可以实现&#xff0c;并且支持提交信息后随机分配中奖结果&#xff0c;不受任何人为因素的影响。下面就来教大家如何制作吧。 &#x1f4cc;使用…

ArcGIS for Vue3

二维&#xff1a; 1、创建vue项目 npm create vitelatest 2、安装ArcGIS JS API依赖包 npm install arcgis/core 3、引入ArcGIS API for JavaScript模块 <script setup> import "arcgis/core/assets/esri/themes/light/main.css"; import Map from arcgis…

HTML5+CSS3+JS小实例:网格图库

实例:网格图库 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0&…

EXCEL从图片链接获取图片

step1: 选中图片地址列 step2:开发工具→Visual Basic 文件→导入 导入我制作的脚本&#xff08;代码见文章末尾&#xff09; 点击excel的小图标回到表格界面。 点击【宏】 选中刚才导入的脚本&#xff0c;点执行&#xff0c;等待完成。 代码本体&#xff1a; Sub InsertPict…

端午档新片速递《谈判专家》领衔,每日影视作品推荐❗❗❗多部佳作待映

每日影视作品推荐一、新片速递《谈判专家》上映时间&#xff1a;2024年端午档预售情况&#xff1a;已开启预售&#xff0c;并有望成为该档期的票房冠军备注&#xff1a;据猫眼专业版数据&#xff0c;该片备受期待 《我才不要和你做朋友呢》上映时间&#xff1a;2024年端午档期预…

9. MySQL事务、字符集

文章目录 【 1. 事务 Transaction 】1.1 事务的基本原理1.2 MySQL 执行事务的语法和流程1.2.1 开始事务1.2.2 提交事务1.2.3 回滚&#xff08;撤销&#xff09;事务实例1&#xff1a;一致性实例2&#xff1a;原子性 【 2. 字符集 和 校对规则 】2.1 基本原理2.2 查看字符集查看…

Python用于存储和组织大型数据集的文件格式库之h5py使用详解

概要 在科学计算和数据分析中,大规模数据集的存储和管理是一个重要的问题。HDF5(Hierarchical Data Format version 5)是一种用于存储和组织大型数据集的文件格式。Python 的 h5py 库是一个用于与 HDF5 文件交互的接口,它结合了 HDF5 的强大功能和 Python 的易用性,使得处…

微信小游戏性能优化解决方案全新发布

小游戏凭借其简单易上手、玩法多样、互动性强的特点&#xff0c;迅速在市场中崭露头角。MMO、ARPG、卡牌等游戏类型也纷纷入局。玩家对启动时间长、发热、加载缓慢、闪退等问题也越来越敏感。 为了突破这些性能瓶颈&#xff0c;UWA全新发布了针对微信小游戏的性能优化解决方案…

clickhouse学习笔记(一)入门与安装

目录 一 、入门 简介 核心特性包括 1.1 列式存储 1.2 原生压缩 1.3 向量化执行引擎 1.4 DBMS 功能 1.5 分布式处理 1.6 高吞吐写入能力 1.7 实时分析 1.8 SQL支持 1.9 高度可扩展 1.10 数据分区与线程级并行 1.11 应用场景 1.12 不适用场景 二、ClickHouse单机版…

深度学习中torch.max函数的作用

文章目录 解释代码举例 解释 torch.max 是 PyTorch 中的一个函数&#xff0c;用于在张量中沿指定维度计算最大值。它有两种用法&#xff1a; ① 如果只提供一个输入张量&#xff0c;则返回该张量中的最大值和对应的索引。     ② 如果提供两个输入张量&#xff0c;则返回两…

最短路径——迪杰斯特拉与弗洛伊德算法

一.迪杰斯特拉算法 首先对于最短路径来说&#xff1a;从vi-vj的最短路径&#xff0c;不用非要经过所有的顶点&#xff0c;只需要找到路径最短的路径即可&#xff1b; 那么迪杰斯特拉的算法&#xff1a;其实也就与最小生成树的思想类似&#xff0c;找到较小的&#xff0c;然后…

本地文件复制到虚拟机VMWare报错 Thre was an error getting infomation about以及关于如何搭建linux虚拟机

解决方式 直接远程ssh连接&#xff0c;用ftp上传即可 关于如何搭建linux虚拟机系统 https://juejin.cn/post/7250009145915719740?searchId2024060409134616191B1350EC8E073921 需要寄快递的朋友&#xff0c;这个小程序发快递只要五块钱哦~

短剧APP开发,推动短剧市场的全新发展

近几年&#xff0c;短剧火爆出圈&#xff0c;迎来了爆发式增长态势&#xff0c;市场规模一跃达到了百亿元&#xff01;短剧节奏快、剧情爽、情节猎奇&#xff0c;极大地满足了用户的追剧需求&#xff0c;深受大众的喜爱。 短剧巨大的市场发展前景也衍生出了各种新的短剧发展赛…

【第三节】C/C++数据结构之栈与队列

目录 一、数据结构-栈 1.1 栈的定义 1.2 栈的 ADT (Abstract Data Type) 1.3 栈的顺序存储结构及实现 二、数据结构-队列 2.1 队列的定义 2.2 队列的 ADT 2.3 队列的顺序存储结构与实现 2.4 优先队列 2.5 各种队列异同点 一、数据结构-栈 1.1 栈的定义 栈(Stack)可…

租赁小程序基于ThinkPHP+FastAdmin+UniApp

本文来自&#xff1a;XYlease租赁小程序基于ThinkPHPFastAdminUniApp - 源码1688 应用介绍 基于ThinkPHPFastAdminUniApp开发的租赁商城小程序&#xff0c;提供用户物品租赁服务的应用程序&#xff0c;方便客户搭建各种类型的租赁场景服务。通过小程序端多角色进行平台管理&…