Vue3:分类管理综合案例实现

news2024/12/25 23:47:01

综合案例

实现分类管理功能

请添加图片描述

路由

在main.js中引入router

访问根路径’/'后跳转到布局容器

加载布局容器后重定向到’/nav/manage’

加载我们需要的组件

这样可以在布局容器中切换功能模块时,只对需要修改的组件进行重新加载

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      component: () => import('@/views/layout/LayoutContainer.vue'),
      redirect: '/nav/manage',
      children: [
        {
          path: '/nav/manage',
          component: () => import('@/views/nav/NavigationManage.vue')
        }
      ]
    }
  ]
})

封装组件

数据列表组件
<template>
    <div> <el-table v-loading="loading" :data="dataList" style="width: 100%">
            <el-table-column prop="id" label="序号"></el-table-column>
            <el-table-column prop="name" label="分类名称"></el-table-column>
            <el-table-column prop="pictureImg" label="分类图片">
                <template #default="scope">
                    <div>
                        <el-image style="width: auto; height: 40px; border: none; cursor: pointer"
                            :src="scope.row.pictureImg" :preview-src-list="[scope.row.pictureImg]"
                            :preview-teleported=true :hide-on-click-modal=true></el-image>
                    </div>
                </template>
            </el-table-column>

            <el-table-column prop="sort" label="分类排序"></el-table-column>
            <el-table-column label="状态">
                <template #default="scope">
                    <div style="display: flex; align-items: center">
                        <span style="margin-left: 10px">
                            {{ scope.row.state == '0' ? '启用' : '禁用' }}
                        </span>
                    </div>
                </template>
            </el-table-column>
            <el-table-column prop="creator" label="创建人"></el-table-column>
            <el-table-column label="操作">
                <!-- row 就是 channelList 的一项, $index 下标 -->
                <template #default="{ row, $index }">
                    <el-button :icon="Edit" circle plain type="primary" @click="onEditChannel(row, $index)"></el-button>
                    <el-button :icon="Delete" circle plain type="danger" @click="onDelChannel(row, $index)"></el-button>
                </template>
            </el-table-column>

            <template #empty>
                <el-empty description="没有数据"></el-empty>
            </template>
        </el-table></div>
</template>

<script setup>
import { Edit, Delete } from '@element-plus/icons-vue';
    defineProps({
        dataList:Object,
        loading:Boolean
    })

    const Emits = defineEmits(['onEditChannel','onDelChannel']);

    const onEditChannel = (row) => {
        Emits("onEditChannel",row)
    }

    const onDelChannel = (row) => {
        Emits("onDelChannel",row)
    }
</script>

<style lang='stylus' scoped>

</style>
查询组件
<template>
    <div><!-- 表单区域 -->
        <el-form inline>
            <el-form-item label="分类:">
                <el-input v-model="searchParams.name" placeholder="请输入分类名字称" clearable />
            </el-form-item>
            <el-form-item label="状态:">
                <el-select v-model="searchParams.state" placeholder="请选择状态" style="width: 120px" clearable>
                    <el-option label="启用" value="0"></el-option>
                    <el-option label="禁用" value="1"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item>
                <el-button type="primary">搜索</el-button>
                <el-button>重置</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>

<script setup>
    defineProps({
        searchParams: Object
    })
</script>

<style lang='stylus' scoped>

</style>
外层父组件(分页组件)

在引入时给子组件传递数据和方法,实现组件间通信

<script setup>
import { ref , onMounted} from 'vue'
import NavigationEdit from './components/NavigationEdit.vue'
import NavigationSearch from './components/NavigationSearch.vue'
import NavigationDataList from './components/NavigationDataList.vue'
import {getCategoryList} from '@/api/navigation'

//测试数据
const testData = []
const loading = ref(false); //加载中,默认false
const dataList = ref(testData); //数据列表
const searchParams = ref({ //搜索参数
  name: '',
  state: ''
})
const pageData = ref({ //分页参数
  currentPage: 1,
  pageSize: 5,
  total: 0
});

const param = {
  name: '',
  state: '',
  page: 2,
  pageSize: 5
}

const getDataList = async() => {
  loading.value = true;
  let result = await getCategoryList(param);    
  dataList.value = result.data.result.records;
  pageData.value.total = result.data.result.total;
  console.log(result);
  loading.value = false;
}


onMounted(()=>{
  getDataList();
})

//对话框对象
const dialog = ref({});

const onAddChannel = () => {
  dialog.value.open({})
}

const onSuccess = () => {
  console.log("success...");
}

const onEditChannel = (row) => { // 点击编辑执行的方法
  dialog.value.open(row)
}
const onDelChannel = async (row) => { // 点击删除执行的方法
  await ElMessageBox.confirm('你确认要删除该分类么', '温馨提示', {
    type: 'warning',
    confirmButtonText: '确认',
    cancelButtonText: '取消'
  })
  //TODO 待实现
}
</script> 

<template>
  <div>
    <!-- 引入自定义组件 -->
  <NavigationEdit ref="dialog" @success="onSuccess"></NavigationEdit>
  <el-card>
    <template #header>
      <span>分类管理</span>
      <el-button style="float: right" @click="onAddChannel">添加分类</el-button>
    </template>

    <NavigationSearch
      :searchParams = "searchParams"
    />
   
    <NavigationDataList
      :dataList = "dataList"
      :loading = "loading"
      @onEditChannel = "onEditChannel"
      @onDelChannel = "onDelChannel"
    />

    <!-- 分页插件 -->
    <div class="pagination-block">
      <el-pagination
        background
        v-model:current-page="pageData.currentPage"
        v-model:page-size="pageData.pageSize"
        :page-sizes="[3, 5, 10, 15, 20]"
        layout="->,total, sizes, prev, pager, next"
        v-model:total="pageData.total"
      />
    </div>
  </el-card>
  </div>
</template>

<style scoped>
.pagination-block {
  margin-top: 10px;
}
</style>

功能实现

向后端发送请求

工具类

返回一个对应链接和超时参数的axios对象

import axios from 'axios'
// import { useUserStore } from '@/stores'
// import { ElMessage } from 'element-plus'
// import router from '@/router'
const baseURL = '/api'

const instance = axios.create({
  // TODO 1. 基础地址,超时时间
  baseURL: baseURL,
  timeout: 10000
})

export default instance
export { baseURL }

数据列表请求

通过工具类的axios对象向后端对应资源链接发送get请求和传递参数,get方法的第二个参数传递一个config{}对象,params是其中的一个属性

import request from '@/utils/request'

export const getCategoryList = (params) =>request.get('/home/category/head',{params:params});
在组件中定义方法对这个请求方法进行调用
const getDataList = async() => {
  loading.value = true;
  let result = await getCategoryList(param);    
  dataList.value = result.data.result.records;
  pageData.value.total = result.data.result.total;
  console.log(result);
  loading.value = false;
}
通过生命周期函数在组件对应生命周期进行挂载
onMounted(()=>{
  getDataList();
})

分页查询

在el-pagination组件中实现了属性和pageData的双向绑定

<!-- 分页插件 -->
      <div class="pagination-block">
        <el-pagination background v-model:current-page="pageData.currentPage" v-model:page-size="pageData.pageSize"
          :page-sizes="[3, 5, 10, 15, 20]" layout="->,total, sizes, prev, pager, next" v-model:total="pageData.total"
          @size-change="getDataList" @current-change="getDataList" />
      </div>

所以我们要从pageData中取数据

普通对象无法被二次修改,所以要使用一个响应式对象进行数据同步

const param = computed(() => {
  return {
    name: '',
    state: '',
    page: pageData.value.currentPage,
    pageSize: pageData.value.pageSize
  }
  }
);

最后因为在el-pagination组件中实现了

@size-change=“getDataList” @current-change=“getDataList”

事件和方法的绑定

所以调用

const getDataList = async () => {
  loading.value = true;
  let result = await getCategoryList(param.value);
  dataList.value = result.data.result.records;
  pageData.value.total = result.data.result.total;
  console.log(result);
  loading.value = false;
}

方法进行数据更新

搜索功能

分为搜索事件,重置事件,在输入框中删除事件,和在多选框中删除事件

搜索事件
const search = () => {//拿到父组件的搜索方法
        emits('search');
    }
<el-button type="primary" @click="search">搜索</el-button>
//绑定搜索方法
const param = computed(() => {
  return {
    name: searchParams.value.name,
    state: searchParams.value.state,
    page: pageData.value.currentPage,
    pageSize: pageData.value.pageSize
  }
}
);
//在父组件中绑定搜索的参数
const search = () => {
  pageData.value.currentPage = 1;
  getDataList();
}
//父组件中定义搜索方法
重置事件
 const reset = ()  => {//拿到父组件的重置方法
        emits('reset')
    }
<el-button @click="reset">重置</el-button>
//绑定重置按钮
const reset = () => {
  searchParams.value.name = '';
  searchParams.value.state = '';
  pageData.value.currentPage = 1;
  pageData.value.pageSize = 5;
  search();
}
//父组件中定义重置方法
搜索/下拉框重置
<el-input v-model="searchParams.name" placeholder="请输入分类名字称" clearable  @clear="search"/>
//input组件自带clear事件绑定search方法

图片上传

<el-form-item label="分类图片" prop="pictureImg">
        <el-upload
          class="avatar-uploader"
          action="/api/upload"//设置上传的资源链接
          name="pictureImg"//设置参数名字
          :show-file-list="false"
          :on-success="handleAvatarSuccess"
          :before-upload="beforeAvatarUpload"
        >

新增分类/修改分类

export const  navEditCategoryService = (params) => request.put('/home/category/edit',{params});
//发起修改请求

export const  navAddCategoryService = (params) => request.post('/home/category/add',params);
//发起新增请求

对请求方法进行调用

const onSubmit = async () => {
  await formRef.value.validate()
  const isEdit = formModel.value.id
  if (isEdit) {
    await navEditCategoryService(formModel.value)//修改数据
    ElMessage.success('编辑成功')
  } else {
    await navAddCategoryService(formModel.value)//新增数据
    ElMessage.success('添加成功')
  }
  dialogVisible.value = false
  emit('success')
}

查询一级分类(在分类dialog中显示)

export const getCategoryParenList = () => request.get('/home/category/parent')
//发起查询请求
onMounted(async() =>{
  let result = await getCategoryParenList();
  parentCategory.value = result.data.result;
})
//通过钩子函数在组件加载完成后自动执行

删除分类(前端加后端)

export const deleteCategoryService = (id) => request.delete(`/home/category/del/${id}`)
//发起删除请求

const onDelChannel = async (row) => { // 点击删除执行的方法
  await ElMessageBox.confirm('你确认要删除该分类么', '温馨提示', {
    type: 'warning',
    confirmButtonText: '确认',
    cancelButtonText: '取消'
  })
  //TODO 待实现
  console.log(row);
  deleteCategoryService(row.id);
  search();
}
//父组件中的删除方法

<el-button :icon="Delete" circle plain type="danger" @click="onDelChannel(row, $index)"></el-button>
//绑定删除方法,将对应行的id传入
@DeleteMapping("/category/del/{id}")
    public R deleteCategory(@PathVariable String id){
        frontService.deleteCategory(id);
        return R.ok();
    }
  	//控制层接收
  
@Override
    public void deleteCategory(String id) {
        frontMapper.delectCategory(id);
    }  
    //业务层处理

@Delete("delete from classification_front where id = ${id}")
    void delectCategory(String id);
    //持久层和数据库交互

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

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

相关文章

深度学习500问——Chapter08:目标检测(9)

文章目录 8.5 目标检测的技巧汇总 8.5.1 Data Augmentation 8.5.2 OHEM 8.5.3 NMS&#xff1a;Soft NMS/ Polygon NMS/ Inclined NMS/ ConvNMS/ Yes-Net NMS/ Softer NMS 8.5.4 Multi Scale Training/Testing 8.5.5 建立小物体与context的关系 8.5.6 参考relation network 8.5.…

Python数据分析——Py基础语法复习(非常详细版)

1.基础数据类型 Number数字、String字符串、List列表、Tuple元组、Set集合、dictionary字典。数字、字符串、元组不可变&#xff0c;列表、集合、字典是可变数据类型 数字类型&#xff1a;int float boo complex复数 2.变量无需声明数据类型&#xff0c;且允许同时为多个变量…

二手手机行业商家如何利用二手机店erp进行破局?

在数字化和AI发展越发先进的的今天&#xff0c;二手手机市场正迎来前所未有的变革。途渡科技精心打造的超机购ERP管理软件&#xff0c;凭借其独特的智能化、高效化特点&#xff0c;正在引领这场变革&#xff0c;为二手手机商家提供全面、深度的数字化管理解决方案。二手手机商家…

软件设计师笔记和错题

笔记截图 数据库 模式是概念模式 模式/内模式 存在概念级和内部级之间&#xff0c;实现了概念模式和内模式的互相转换 外模式/模式映像 存在外部级和概念级之间&#xff0c;实现了外模式和概念模式的互相转换。 数据的物理独立性&#xff0c; 概念模式和内模式之间的映像…

Java开发大厂面试第03讲:线程的状态有哪些?它是如何工作的?

线程&#xff08;Thread&#xff09;是并发编程的基础&#xff0c;也是程序执行的最小单元&#xff0c;它依托进程而存在。一个进程中可以包含多个线程&#xff0c;多线程可以共享一块内存空间和一组系统资源&#xff0c;因此线程之间的切换更加节省资源、更加轻量化&#xff0…

微信小程序踩坑,skyline模式下,scroll-view下面的一级元素设置margin中的auto无效,具体数据有效

开发工具版本 基础库 开启skyline渲染调试 问题描述 skyline模式下,scroll-view下面的一级元素的margin写auto的值是没有效果的(二级元素margin写auto是有效果的),关闭这个模式就正常显示 演示效果图 父元素的宽度和高度效果(宽度是750rpx,宽度占满的) 一级元素宽度和css效果…

Apifox:API 接口自动化测试完全指南

01 前言 这是一篇关于 Apifox 的接口自动化测试教程。相信你已经对 Apifox 有所了解&#xff1a;“集 API 文档、API 调试、API Mock、API 自动化测试&#xff0c;更先进的 API 设计/开发/测试工具”。 笔者是后端开发&#xff0c;因此这篇教程关注的是 API 自动化测试&#…

Jenkins 忘记登录密码怎么办

在安装Jenkins中遇到忘记登录密码该怎么呢&#xff1f;下面是一个解决办法 1. 先停止jenkins服务 我是用tomcat启动的jenkis 2. 找到config.yaml文件 find / -name config.xml命令执行后找到如下结果&#xff1a; /root/.jenkins/config.xml /root/.jenkins/users/admin_839…

正则表达式和sed

一、正则表达式 主要用来匹配字符串&#xff08;命令结果&#xff0c;文本内容&#xff09;&#xff0c; 通配符匹配文件&#xff08;而且是已存在的文件&#xff09; 基本正则表达式 扩展正则表达式 1.元字符 . 匹配任意单个字符&#xff0c;可以是一个汉字 […

机器人计算力矩控制

反馈线性化&#xff1a; 反馈线性化是一种控制系统设计方法&#xff0c;其目标是通过状态空间的坐标变换和控制变换&#xff0c;使得非线性系统的输入-状态映射或输入-输出映射反馈等价于线性系统。这样&#xff0c;就可以应用线性系统的控制理论来实现非线性系统的控制。在机…

企业级复杂前中台项目响应式处理方案

目录 01: 前言 02: 响应式下navigtionBar实现方案分析 数据 视图 小结 03: 抽离公用逻辑&#xff0c;封装系列动作 04: PC端navigationBar私有逻辑处理 05: 分析 navigationBar 闪烁问题 06: 处理 navigationBar 闪烁问题 07: category数据缓存&#xff0c;覆盖…

【Axure原型分享】动态伸缩组织架构图

今天和大家分享动态伸缩组织架构图图原型模板&#xff0c;我们可以通过点击加减按钮来展开或收起子内容&#xff0c;具体效果可以点击下方视频观看或者打开预览地址来体验 【原型效果】 【Axure高保真原型】动态伸缩组织架构图 【原型预览含下载地址】 https://axhub.im/ax9/…

层次式体系结构概述

1.软件体系结构 软件体系结构可定义为&#xff1a;软件体系结构为软件系统提供了结构、行为和属性的高级抽象&#xff0c;由构成系统的元素描述、这些元素的相互作用、指导元素集成的模式以及这些模式的约束组成。软件体系结构不仅指定了系统的组织结构和拓扑结构&#xff0c;并…

Spark RDD案例:统计网站每月访问量

这个项目利用Spark技术&#xff0c;通过统计网站访问记录中的日期信息&#xff0c;实现了对每月访问量的统计和排序。通过分析数据&#xff0c;我们可以了解到不同月份的网站访问情况&#xff0c;为进一步优化网站内容和推广策略提供数据支持。 使用Spark统计网站每月访问量 …

Android Iptables 客制化方法及基本使用

Android Iptables 客制化方法及基本使用 Android netd 的自定义链NetdConstants.cpp 的 execIptablesRestore 方法IptablesRestoreController 的 execute 方法使用 oem-iptables-init.sh 添加自定义的防火墙规则oem-iptables-init.sh 示例文件 基本概念Iptables 链Iptables 表 …

OpenHarmony 实战开发——使用分布式菜单创建点餐神器

随着社会的进步与发展&#xff0c;科技手段的推陈出新&#xff0c;餐饮行业也在寻求新的突破与变革&#xff0c;手机扫描二维码点餐系统已经成为餐饮行业的未来趋势&#xff0c;发展空间巨大&#xff1b;扫码点餐&#xff0c;是“互联网餐饮”潮流的产物&#xff0c;可以有效地…

k8s-Helm包管理器

这里写目录标题 什么是Helmhelm架构重要概念组件Helm 客户端Helm 库 安装heml使用halm快速部署应用添加 helm 仓库 Helm 的常用命令chart 详解目录结构Redis chart 实践创建StorageClass制备器&#xff0c;配置NFS动态制备修改 helm 源搜索 redis chart修改配置安装查看安装情况…

力扣HOT100 - 152. 乘积最大子数组

解题思路&#xff1a; 方法一&#xff1a;暴力 class Solution {public int maxProduct(int[] nums) {int max Integer.MIN_VALUE;int s 1;for (int i 0; i < nums.length; i) {s 1;for (int j i ; j < nums.length; j) {s * nums[j];max Math.max(max, s);}}ret…

服务器之间实现免密码传输文件(scp免密传输)

问题&#xff1a;需要定时将本服务器的文件传输到指定服务器上作为备份 通过scp实现不同服务器之间的文件传输 正常使用scp传输文件 传输文件命令&#xff1a;scp /data/文件 root服务器地址&#xff1a;/指定目录 传输文件夹命令&#xff1a;scp -r /data/文件 root服务…

【回溯】1255. 得分最高的单词集合

本文涉及知识点 回溯 力扣难道&#xff1a;1881 LeetCode1255. 得分最高的单词集合 你将会得到一份单词表 words&#xff0c;一个字母表 letters &#xff08;可能会有重复字母&#xff09;&#xff0c;以及每个字母对应的得分情况表 score。 请你帮忙计算玩家在单词拼写游戏…