【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;来发现及预测分析对…

用队列实现栈-力扣

本题是使用队列来实现栈&#xff0c;在栈实现队列时&#xff0c;我们使用了输入栈和输出栈来调整输出顺序&#xff0c;但时队列不同&#xff0c;队列元素先入先出&#xff0c;即使使用两个队列&#xff0c;也没法调整到先入后出。因此做法是依次将队列元素出队&#xff0c;然后…

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&…

C++中的Template常见用法

目录 摘要 函数模板 基本用法 重载与特化 类模板 基本用法 模板特化&#xff08;Specialization&#xff09;与偏特化&#xff08;Partial Specialization&#xff09; 别名模板&#xff08;Alias Templates&#xff09; 基本用法 变量模板&#xff08;Variable Temp…

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 查看字符集查看…

通过SDKMan来安装各种版本的JDK

准备工作&#xff1a; 1. 打开命令行工具&#xff1a;首先&#xff0c;你需要打开电脑上的命令行工具。在Windows上是“命令提示符”或“PowerShell”&#xff0c;Mac或Linux上是“终端”。 安装SDKMan&#xff1a; 1. 安装SDKMan&#xff1a;在命令行中输入以下命令并按…

亚马逊 AWS 视频转码功能、AWS Elemental MediaConvert 中创建和管理转码作业

上传的视频需要转码成不同的编码, 可以直接在 AWS Elemental MediaConvert 中创建和管理转码作业 AWS Elemental MediaConvert 中创建和管理转码作业 /*** 视频转码* return bool* author wzb* data 2024/5/30*/function videoTranscode(&$data){$fileId $data[id] ?? …

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单机版…

【C++PCL】点云处理KD-ICP配准

作者:迅卓科技 简介:本人从事过多项点云项目,并且负责的项目均已得到好评! 公众号:迅卓科技,一个可以让您可以学习点云的好地方 重点:每个模块都有参数如何调试的讲解,即调试某个参数对结果的影响是什么,大家有问题可以评论哈,如果文章有错误的地方,欢迎来指出错误的…

多线程调用同一个不包含可变状态,并且是线程安全的方法时,可同时执行,不必等待排队

多线程调用同一个不包含可变状态&#xff0c;并且是线程安全的方法时&#xff0c;可同时执行&#xff0c;不必等待排队 前言同时执行方法的条件示例并发执行的优势实验验证总结 前言 如果方法不包含可变状态&#xff0c;并且是线程安全的&#xff0c;那么在高并发环境下&#…