SpringBoot3 + Vue3 + Element-Plus + TS 实现动态二级菜单级联选择器

devtools/2024/11/14 2:30:05/

SpringBoot3 + Vue3 + Element-Plus + TS 实现动态二级菜单选择器

  • 1、效果展示
    • 1.1 点击效果
    • 1.2 选择效果
    • 1.3 返回值
    • 1.4 模拟后端返回数据
  • 2、前端代码
    • 2.1 UnusedList.vue
    • 2.2 goodsType.ts
    • 2.3 http.ts
  • 3、后端代码
    • 3.1 GoodsCategoryController.java
    • 3.2 GoodsCategoryService.java
    • 3.3 GoodsCategoryServiceImpl.java

1、效果展示

1.1 点击效果

在这里插入图片描述

1.2 选择效果

在这里插入图片描述

1.3 返回值

返回值为二级分类的 id

{categoryId: "21"
}

1.4 模拟后端返回数据

const categories = 
[[{ "id": 9, "name": "吃的" },{ "id": 5, "name": "食品" },{ "id": 4, "name": "数码" },{ "id": 1, "name": "服饰" }],[{ "id": 18, "name": "相机", "categoryFatherId": 4 },{ "id": 17, "name": "电脑", "categoryFatherId": 4 },{ "id": 14, "name": "裤子", "categoryFatherId": 1 },{ "id": 19, "name": "零食", "categoryFatherId": 5 },{ "id": 16, "name": "手机", "categoryFatherId": 4 },{ "id": 20, "name": "牛奶", "categoryFatherId": 5 },{ "id": 21, "name": "辣条", "categoryFatherId": 5 },{ "id": 1, "name": "衣服", "categoryFatherId": 1 },{ "id": 15, "name": "裙子", "categoryFatherId": 1 },{ "id": 23, "name": "可乐", "categoryFatherId": 9 }]];

2、前端代码

vue_40">2.1 UnusedList.vue

<template><el-form-item prop="categoryId" label="商品分类:"><el-cascader :options="cascaderOptions" @change="handleCascaderChange" style="width: 600px"><template #default="{ node, data }"><span>{{ data.label }}</span><span v-if="!node.isLeaf"> ({{ data.children.length }}) </span></template></el-cascader></el-form-item>
</template><script setup lang="ts">
import { Ref, computed, onMounted, reactive, ref } from 'vue';
import { getSelectListApi } from '@/api/goods/goodsType.ts'
// 新增表单内容
const addGoodParm = reactive({categoryId: "",
})// 定义 Ref 类型的数组
const categories = ref<any[]>([]);
// 获取所有分类
const getAllShopType = async () => {let res = await getSelectListApi();categories.value = res.data;
}// 动态计算二级分类
const cascaderOptions = computed(() => {if (!categories.value || categories.value.length !== 2) {return [];}const [mainCategories, subCategories] = categories.value;// 根据一级分类和二级分类动态生成 options 数据return mainCategories.map((mainCategory: { id: { toString: () => any; }; name: any; }) => {const children = subCategories.filter((subCategory: { categoryFatherId: { toString: () => any; }; }) => subCategory.categoryFatherId === mainCategory.id).map((subCategory: { id: { toString: () => any; }; name: any; }) => ({value: subCategory.id.toString(),label: subCategory.name,}));return {value: mainCategory.id.toString(),label: mainCategory.name,children,};});
});// 处理 el-cascader 的 change 事件
// 设置商品分类id
const handleCascaderChange = (value: string[]) => {addGoodParm.categoryId = value[1];console.log( addGoodParm);</script>onMounted(() => {getAllShopType();
})

2.2 goodsType.ts

import http from "@/http/http.ts";
// 查询所有分类结构化的返回
export const getSelectListApi = () => {return http.get(`/api/goodsCategory/getSelectList`);
}

2.3 http.ts

/** @Date: 2024-03-30 12:37:05* @LastEditors: zhong* @LastEditTime: 2024-04-16 20:27:33* @FilePath: \app-admin\src\http\http.ts*/
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from "axios";
import { ElMessage } from "element-plus";// axios 请求配置
const config = {// baseURL:'http://localhost:8080',baseURL: '/api',timeout: 1000
}
// 定义返回值类型
export interface Result<T = any> {code: number;msg: string;data: T;
}class Http {// axios 实例private instance: AxiosInstance;// 构造函数初始化constructor(config: AxiosRequestConfig) {this.instance = axios.create(config);//定义拦截器this.interceptors();}private interceptors() {// axios 发送请求之前的处理this.instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {// 在请求头部携带token// let token = sessionStorage.getItem('token');let token = '';if (token) {config.headers!['token'] = token;// 把 token 放到 headers 里面// (config.headers as AxiosRequestHeaders).token = token;}// console.log(config);return config;}, (error: any) => {error.data = {};error.data.msg = '服务器异常,请联系管理员!'return error;})// axios 请求返回之后的处理// 请求返回处理this.instance.interceptors.response.use((res: AxiosResponse) => {// console.log(res.data);if (res.data.code != 200) {ElMessage.error(res.data.msg || '服务器出错啦');return Promise.reject(res.data.msg || '服务器出错啦');} else {return res.data;}}, (error) => {console.log('进入错误!');error.data = {};if (error && error.response) {switch (error.response.status) {case 400:error.data.msg = "错误请求";ElMessage.error(error.data.msg);break;case 401:error.data.msg = "未授权,请登录";ElMessage.error(error.data.msg);break;case 403:error.data.msg = "拒绝访问";ElMessage.error(error.data.msg);break;case 404:error.data.msg = "请求错误,未找到该资源";ElMessage.error(error.data.msg);break;case 405:error.data.msg = "请求方法未允许";ElMessage.error(error.data.msg);break;case 408:error.data.msg = "请求超时";ElMessage.error(error.data.msg);break;case 500:error.data.msg = "服务器端出错";ElMessage.error(error.data.msg);break;case 501:error.data.msg = "网络未实现";ElMessage.error(error.data.msg);break;case 502:error.data.msg = "网络错误";ElMessage.error(error.data.msg);break;case 503:error.data.msg = "服务不可用";ElMessage.error(error.data.msg);break;case 504:error.data.msg = "网络超时";ElMessage.error(error.data.msg);break;case 505:error.data.msg = "http版本不支持该请求";ElMessage.error(error.data.msg);break;default:error.data.msg = `连接错误${error.response.status}`;ElMessage.error(error.data.msg);}} else {error.data.msg = "连接到服务器失败";ElMessage.error(error.data.msg)}return Promise.reject(error);})}// GET方法get<T = Result>(url: string, params?: object): Promise<T> {return this.instance.get(url, { params });}// POST方法post<T = Result>(url: string, data?: object): Promise<T> {return this.instance.post(url, data);}// PUT方法put<T = Result>(url: string, data?: object): Promise<T> {return this.instance.put(url, data );}// DELETE方法delete<T = Result>(url: string): Promise<T> {return this.instance.delete(url);}
}export default new Http(config);

3、后端代码

3.1 GoodsCategoryController.java

@RestController
@RequestMapping("/api/goodsCategory")
public class GoodsCategoryController {@Autowiredprivate GoodsCategoryService goodsCategoryService;@Autowiredprivate GoodsCategorySonService goodsCategorySonService;/*** 获取查询列用于前端 u-picker 组件渲染值* @return*/@GetMapping("/getSelectList")public ResultVo getSelectList() {List<Object> categoryList = goodsCategorySonService.getSelectLists();return ResultUtils.success("查询成功!", categoryList);}
}

3.2 GoodsCategoryService.java

package com.zhx.app.service.goods;import com.baomidou.mybatisplus.extension.service.IService;
import com.zhx.app.model.goods.GoodsCategorySon;
import lombok.Data;import java.util.ArrayList;
import java.util.List;/*** @ClassName : GoodsCategoryService* @Description :* @Author : zhx* @Date: 2024-03-31 10:48*/public interface GoodsCategorySonService extends IService<GoodsCategorySon> {List<Object> getSelectLists();
}

3.3 GoodsCategoryServiceImpl.java

package com.zhx.app.service.impl.goods;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhx.app.mapper.goods.GoodsCategoryMapper;
import com.zhx.app.mapper.goods.GoodsCategorySonMapper;
import com.zhx.app.model.goods.GoodsCategory;
import com.zhx.app.model.goods.GoodsCategorySon;
import com.zhx.app.service.goods.GoodsCategorySonService;
import io.micrometer.common.util.StringUtils;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;/*** @ClassName : GoodsCategoryServiceImpl* @Description :* @Author : zhx* @Date: 2024-03-31 10:49*/
@Service
public class GoodsCategorySonServiceImpl extends ServiceImpl<GoodsCategorySonMapper, GoodsCategorySon> implements GoodsCategorySonService{@Autowiredprivate GoodsCategoryMapper goodsCategoryMapper;@Autowiredprivate GoodsCategorySonMapper goodsCategorySonMapper;/*** 格式化返回一级分类和二级分类列表* @return*/@Overridepublic List<Object> getSelectLists() {@Dataclass SelectType {private Long id;private String name;}@Dataclass SelectTypeSon {private Long id;private String name;private Long categoryFatherId;}// 查询分类列表// 构造查询QueryWrapper<GoodsCategory> query = new QueryWrapper<>();// 查询条件query.lambda().orderByDesc(GoodsCategory::getCategoryId);// 获取查询结果List<GoodsCategory> list = goodsCategoryMapper.selectList(query);// 构造查询QueryWrapper<GoodsCategorySon> querySon = new QueryWrapper<>();// 查询条件querySon.lambda().orderByDesc(GoodsCategorySon::getOrderNum);// 获取查询结果List<GoodsCategorySon> listSon = goodsCategorySonMapper.selectList(querySon);// 存储需要的类型ArrayList<SelectType> selectList = new ArrayList<>();ArrayList<SelectTypeSon> selectListSon = new ArrayList<>();List<Object> category = new ArrayList<>();// 构造需要的类型Optional.ofNullable(list).orElse(new ArrayList<>()).stream().forEach(x -> {SelectType type = new SelectType();type.setId(x.getCategoryId());type.setName(x.getCategoryName());selectList.add(type);});Optional.ofNullable(listSon).orElse(new ArrayList<>()).stream().forEach(x -> {SelectTypeSon type = new SelectTypeSon();type.setId(x.getCategoryId());type.setName(x.getCategoryName());type.setCategoryFatherId(x.getCategoryFatherId());selectListSon.add(type);});category.add(selectList);category.add(selectListSon);return category;}
}

http://www.ppmy.cn/devtools/8118.html

相关文章

面试题汇总

文章目录 Leetcode 147. 对链表进行插入排序题目描述C语言题解和思路解题思路 Leetcode 309. 买卖股票的最佳时机含冷冻期题目描述C语言题解和思路解题思路 Leetcode 187. 重复的DNA序列题目描述C语言题解和思路解题思路 Leetcode 2517. 礼盒的最大甜蜜度题目描述C语言题解和思…

echarts部分属性使用

标题部分 (title): 控制图表的标题显示&#xff0c;包括主标题和副标题。你可以设置标题的文字内容、样式、位置等属性。 图例部分 (legend): 图例是用来标识每个系列的名称的&#xff0c;可以让用户通过点击图例来控制显示/隐藏对应的数据系列。 提示框部分 (tooltip): 当鼠…

Git使用说明

Git使用说明 git branch 用于管理分支&#xff0c;包括查看、创建、删除、重命名分支等操作。 git branch: 列出本地仓库中的所有分支&#xff0c;并标识当前所在的分支。git branch <branch-name>: 创建一个新分支&#xff0c;名称为 <branch-name>。git branc…

论文解读:(CoCoOP)Conditional Prompt Learning for Vision-Language Models

文章汇总 存在的问题 CoOp的一个关键问题:学习到的上下文不能推广到同一数据集中更广泛的未见类&#xff0c;这表明CoOp过拟合了训练期间观察到的基本类。 动机 为了解决弱泛化问题&#xff0c;我们引入了一个新的概念:条件提示学习。关键思想是使提示取决于每个输入实例(图…

数据结构––kmp算法(串)

kmp算法作为串的一个重要内容&#xff0c;必然有一定的难度&#xff0c;而在看到各类教辅书里的概念与解释后&#xff0c;其晦涩难懂的内容直接劝退一部分人&#xff0c;现在&#xff0c;让我们来看看吧 KMP解决的问题类型 KMP算法的作用就是在一个已知的字符串中查找子串的位…

Mysql学习2

目录 一.数据库&#xff1a; 1.创建数据库&#xff1a; 2.查看数据库&#xff1a; 3.备份恢复数据库&#xff1a; 二.表 1.创建表指令&#xff1a; 2.MySQL常用数据类型&#xff1a; 3.删除与修改表&#xff08;重点&#xff09;&#xff1a; 4.数据库CRUD语句&#xf…

书生·浦语大模型全链路开源体系-第4课

书生浦语大模型全链路开源体系-第4课 书生浦语大模型全链路开源体系-第4课相关资源XTuner 微调 LLMXTuner 微调小助手认知环境安装前期准备启动微调模型格式转换模型合并微调结果验证 将认知助手上传至OpenXLab将认知助手应用部署到OpenXLab使用XTuner微调多模态LLM前期准备启动…

【k8s】(五)kubernetes1.29.4离线部署之-初始化第一个控制平面

备注&#xff1a; 完整版请参阅 【k8s】Kubernetes 1.29.4离线安装部署&#xff08;总&#xff09; 执行命令初始化第一个控制平面节点 在上节的安装过程中&#xff0c;实际以及包含了初始化第一个控制平面的脚本&#xff0c;由于其重要性&#xff0c;这里单独提出来详细说明。…