SpringBoot3 + Vue3 + Uniapp + uView + Elenment 实现动态二级分类以及二级分类的管理

SpringBoot3 + Vue3 + Uniapp + uView + Elenment 实现动态二级分类以及二级分类的管理

  • 1. 效果展示
    • 1.1 前端显示效果
    • 1.2 后台管理一级分类
    • 1.3 后台管理二级分类
  • 2. 后端代码
    • 2.1 GoodsCategoryController.java
    • 2.2.1 GoodsCategoryMapper.java
    • 2.2.2 GoodsCategorySonMapper.java
    • 2.3.1 GoodsCategory.java
    • 2.3.2 GoodsCategorySon.java
    • 2.4 PageParm.java
    • 2.5.1 GoodsCategoryService .java
    • 2.5.2 GoodsCategorySonService.java
    • 2.6.1 GoodsCategoryServiceImpl .java
    • 2.6.2 GoodsCategoryServiceSonImpl.java
  • 3. uniapp 代码
    • 3.1 GoodsCategory.vue
    • 3.2 getSelectList.js
    • 3.3 http.js
  • 4. 后台管理页面代码
    • 4.1 GoodsType.vue
    • 4.2 GoodsType.ts
    • 4.3 goods\index.ts
    • 4.4 PaginationQueryModel.ts

1. 效果展示

1.1 前端显示效果


在这里插入图片描述

1.2 后台管理一级分类


在这里插入图片描述

1.3 后台管理二级分类


点击一级分类可以进入二级分类管理

在这里插入图片描述

2. 后端代码

2.1 GoodsCategoryController.java


package com.zhx.app.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zhx.app.model.goods.GoodsCategory;
import com.zhx.app.model.PagePram;
import com.zhx.app.model.goods.GoodsCategorySon;
import com.zhx.app.service.GoodsCategoryService;
import com.zhx.app.service.GoodsCategorySonService;
import com.zhx.app.utils.ResultUtils;
import com.zhx.app.utils.ResultVo;
import io.micrometer.common.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;


/**
 * @ClassName : GoodsCategoryController
 * @Description : 商品分类
 * @Author : zhx
 * @Date: 2024-03-31 10:50
 */
@RestController
@RequestMapping("/api/goodsCategory")
public class GoodsCategoryController {
    @Autowired
    private GoodsCategoryService goodsCategoryService;
    @Autowired
    private GoodsCategorySonService goodsCategorySonService;

    /**
     * 获取商品分类列表
     *
     * @param pagePram
     * @return
     */
    @GetMapping("/getList")
    public ResultVo getList(PagePram pagePram) {
        // 构造分页查询条件
        QueryWrapper<GoodsCategory> query = new QueryWrapper<>();
        query.lambda().like(StringUtils.isNotBlank(pagePram.getSearchName()), GoodsCategory::getCategoryName, pagePram.getSearchName()).orderByDesc(GoodsCategory::getOrderNum);
        // 构建分页对象
        IPage<GoodsCategory> page = new Page<>(pagePram.getCurrentPage(), pagePram.getPageSize());
        // 查询
        IPage<GoodsCategory> list = goodsCategoryService.page(page, query);
        return ResultUtils.success("查询成功!", list);
    }

    /**
     * 查询二级分类数据
     * @param categoryFatherId
     * @param pagePram
     * @return
     */
    @GetMapping("/getInfo/{categoryFatherId}")
    public ResultVo getListInfo(@PathVariable String categoryFatherId, PagePram pagePram) {
        // 构造分页查询条件
        QueryWrapper<GoodsCategorySon> query = new QueryWrapper<>();
        query.lambda().like(StringUtils.isNotBlank(categoryFatherId), GoodsCategorySon::getCategoryFatherId,categoryFatherId).orderByDesc(GoodsCategorySon::getOrderNum);
        // 构建分页对象
        IPage<GoodsCategorySon> page = new Page<>(pagePram.getCurrentPage(), pagePram.getPageSize());
        // 查询
        IPage<GoodsCategorySon> list = goodsCategorySonService.page(page, query);
        return ResultUtils.success("查询成功!", list);
    }
    /**
     * 新增商品分类
     *
     * @param goodsCategory
     * @return
     */
    @PostMapping
    public ResultVo add(@RequestBody GoodsCategory goodsCategory) {
        if (goodsCategoryService.save(goodsCategory)) {
            return ResultUtils.success("添加成功!");
        } else {
            return ResultUtils.error("添加失败!");
        }
    }

    /**
     * 新增商品分类
     *
     * @param goodsCategorySon
     * @return
     */
    @PostMapping("/son")
    public ResultVo addSon(@RequestBody GoodsCategorySon goodsCategorySon) {
        if (goodsCategorySonService.save(goodsCategorySon)) {
            return ResultUtils.success("添加成功!");
        } else {
            return ResultUtils.error("添加失败!");
        }
    }

    /**
     * 删除商品分类
     *
     * @param goodsCategoryId
     * @return
     */
    @DeleteMapping("/{categoryId}")
    public ResultVo delete(@PathVariable("categoryId") Long goodsCategoryId) {
        if (goodsCategoryService.removeById(goodsCategoryId)) {
            return ResultUtils.success("删除成功!");
        } else {
            return ResultUtils.error("删除失败!");
        }
    }
    /**
     * 删除商品分类
     *
     * @param goodsCategoryId
     * @return
     */
    @DeleteMapping("/son/{categoryId}")
    public ResultVo deleteSon(@PathVariable("categoryId") Long goodsCategoryId) {
        System.out.println(goodsCategoryId);
        if (goodsCategorySonService.removeById(goodsCategoryId)) {
            return ResultUtils.success("删除成功!");
        } else {
            return ResultUtils.error("删除失败!");
        }
    }

    /**
     * 修改商品分类
     *
     * @param goodsCategorySon
     * @return
     */
    @PutMapping("/son")
    public ResultVo edit(@RequestBody GoodsCategorySon goodsCategorySon) {
        if (goodsCategorySonService.updateById(goodsCategorySon)) {
            return ResultUtils.success("修改成功!");
        } else {
            return ResultUtils.error("修改失败!");
        }
    }

    /**
     * 修改子商品分类
     *
     * @param goodsCategory
     * @return
     */
    @PutMapping
    public ResultVo edit(@RequestBody GoodsCategory goodsCategory) {
        if (goodsCategoryService.updateById(goodsCategory)) {
            return ResultUtils.success("修改成功!");
        } else {
            return ResultUtils.error("修改失败!");
        }
    }

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

    /**
     * 通过二级分类id查询整个分类详情
     * @param id
     * @return
     */
    @GetMapping("/{categoryId}")
    public ResultVo getCategoryListById(@PathVariable("categoryId") String id) {
        List<String> categoryList = goodsCategorySonService.getCategoryListById(id);
        return ResultUtils.success("查询成功!", categoryList);
    }
}

2.2.1 GoodsCategoryMapper.java


package com.zhx.app.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zhx.app.model.goods.GoodsCategory;

/**
 * @ClassName : GoodsCategoryMapper
 * @Description :
 * @Author : zhx
 * @Date: 2024-03-31 10:47
 */
public interface GoodsCategoryMapper extends BaseMapper<GoodsCategory> {
}

2.2.2 GoodsCategorySonMapper.java


package com.zhx.app.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zhx.app.model.goods.GoodsCategory;
import com.zhx.app.model.goods.GoodsCategorySon;

/**
 * @ClassName : GoodsCategorySonMapper
 * @Description :
 * @Author : zhx
 * @Date: 2024-03-31 10:47
 */
public interface GoodsCategorySonMapper extends BaseMapper<GoodsCategorySon> {
}

2.3.1 GoodsCategory.java

package com.zhx.app.model.goods;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @ClassName : GoodsCategory
 * @Description : 商品一级分类
 * @Author : zhx
 * @Date: 2024-03-31 10:44
 */
@Data
@TableName("goods_category")
public class GoodsCategory {
    @TableId(type = IdType.AUTO)
    private Long categoryId;

    private String categoryName;
    private Integer orderNum;
}

2.3.2 GoodsCategorySon.java


package com.zhx.app.model.goods;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @ClassName : GoodsCategorySon
 * @Description : 商品二级分类
 * @Author : zhx
 * @Date: 2024-03-31 10:44
 */
@Data
@TableName("goods_category_son")
public class GoodsCategorySon {
    @TableId(type = IdType.AUTO)
    private Long categoryId;

    private String categoryName;
    private Integer orderNum;
    private Long categoryFatherId;
}

2.4 PageParm.java


package com.zhx.app.model;

import lombok.Data;

/**
 * @ClassName : PageParm
 * @Description : 分页
 * @Author : zhx
 * @Date: 2024-03-30 11:00
 */
@Data
public class PagePram {
    private Long currentPage;
    private Long pageSize;
    private String searchName;
}

2.5.1 GoodsCategoryService .java


package com.zhx.app.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.zhx.app.model.goods.GoodsCategory;

/**
 * @ClassName : GoodsCategoryService
 * @Description :
 * @Author : zhx
 * @Date: 2024-03-31 10:48
 */

public interface GoodsCategoryService extends IService<GoodsCategory> {
}

2.5.2 GoodsCategorySonService.java


package com.zhx.app.service;

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();

    List<String> getCategoryListById(String id);
}

2.6.1 GoodsCategoryServiceImpl .java


package com.zhx.app.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhx.app.mapper.GoodsCategoryMapper;
import com.zhx.app.model.goods.GoodsCategory;
import com.zhx.app.model.goods.GoodsCategorySon;
import com.zhx.app.service.GoodsCategoryService;
import org.springframework.stereotype.Service;

/**
 * @ClassName : GoodsCategoryServiceImpl
 * @Description :
 * @Author : zhx
 * @Date: 2024-03-31 10:49
 */
@Service
public class GoodsCategoryServiceImpl extends ServiceImpl<GoodsCategoryMapper, GoodsCategory> implements GoodsCategoryService{
}

2.6.2 GoodsCategoryServiceSonImpl.java


package com.zhx.app.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhx.app.mapper.GoodsCategoryMapper;
import com.zhx.app.mapper.GoodsCategorySonMapper;
import com.zhx.app.model.goods.GoodsCategory;
import com.zhx.app.model.goods.GoodsCategorySon;
import com.zhx.app.service.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 : GoodsCategoryServiceSonImpl
 * @Description :
 * @Author : zhx
 * @Date: 2024-03-31 10:49
 */
@Service
public class GoodsCategoryServiceSonImpl extends ServiceImpl<GoodsCategorySonMapper, GoodsCategorySon> implements GoodsCategorySonService{
    @Autowired
    private GoodsCategoryMapper goodsCategoryMapper;
    @Autowired
    private GoodsCategorySonMapper goodsCategorySonMapper;

    /**
     * 格式化返回一级分类和二级分类列表
     * @return
     */
    @Override
    public List<Object> getSelectLists() {
        @Data
        class SelectType {
            private Long id;
            private String name;
        }
        @Data
        class 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;
    }

    /**
     * 根据二级分类的id查询一级分类和二级分类名称
     * @param id
     * @return
     */
    @Override
    public List<String> getCategoryListById(String id) {
        // 创建容器存放 一级分类和二级分类名称
        List<String> nameList = new ArrayList<>();
        // 查询分类列表
        // 构造查询
        QueryWrapper<GoodsCategorySon> query = new QueryWrapper<>();
        query.lambda().like(StringUtils.isNotBlank(id),GoodsCategorySon::getCategoryId, id);
        GoodsCategorySon son = goodsCategorySonMapper.selectOne(query);
        // 二级分类添加到容器
        nameList.add(son.getCategoryName());
        // 通过二级分类的 父id字段 获取一级分类名
        Long categoryFatherId = son.getCategoryFatherId();
        QueryWrapper<GoodsCategory> queryFather = new QueryWrapper<>();
        queryFather.lambda().like(StringUtils.isNotBlank(String.valueOf(categoryFatherId)),GoodsCategory::getCategoryId, categoryFatherId);
        GoodsCategory father = goodsCategoryMapper.selectOne(queryFather);
        nameList.add(father.getCategoryName());
        Collections.reverse(nameList);
        return nameList;
    }
}

3. uniapp 代码

3.1 GoodsCategory.vue


			<!-- 商品分类 -->
			<view class="foot line-border" @tap="showShopType=true;">
				<view>商品分类</view>
				<view class="foot-right">
					<u-tag v-if="productData.type==''" @click="showShopType=true"
						:text="productData.type==''?'请选择商品分类':items" :type="productData.newColor" shape="circle"
						size="mini" style="margin-left: 10rpx;"></u-tag>
					<template v-for="(items, index) in productData.type" :key="index">
						<u-tag @click="showShopType=true" :text="items" :type="productData.type!=''?'success':''"
							shape="circle" size="mini" style="margin-left: 10rpx;"></u-tag>
					</template>

					<u-icon name="arrow-right"></u-icon>
				</view>
			</view>
			<!-- 商品分类弹出层 -->
			<view>
				<u-picker :show="showShopType" ref="uPicker" :columns="typeList.columns" @confirm="confirm"
					@change="changeHandler" confirmColor="green" immediateChange @cancel="showShopType=false"
					keyName="name" :defaultIndex="[Math.trunc(typeList.columns[0].length / 2),1]"></u-picker>
			</view>


<script setup>
	import {
		reactive,
		ref
	} from 'vue';
	import UpLoadFile from "@/utils/uploadFlie/UpLoadFile.vue"
	import MapOpen from "@/utils/mapOpen/MapOpen.vue"
	import {
		getSelectList
	} from '../../api/shopcategory';
	const urlLists = ref([]);
	const urlList = (data) => {
		urlLists.value = data;
	}
	const showShopType = ref(false);
	
	const realyType = ref("");
	// 发布商品
	const publishProducts = () => {
		// 构造商品数据
		let data = {
			profile: productData.value.profile,
			url: '',
			address: "商品位置",
			money: productData.value.money,
			type: realyType.value,
			oldMoney: productData.value.oldMoney,
			new: productData.value.new,
			trading: productData.value.trading,
			contact: productData.value.contact,
			contactType: productData.value.contactType
		}
		urlLists.value.forEach(x => data.url += x + ",")
		data.url = data.url.slice(0, -1)

		console.log(data);
		// 调用接口写入商品数据
	}

	// 商品数据
	const productData = ref({
		tips: "清晰的描述品牌型号和宝贝细节,才能更快的卖出~",
		profile: "", // 商品介绍
		address: "", // 商品位置
		money: "", // 商品价格
		oldMoney: "", // 商品原价
		type: "",
		new: "请选择新旧程度", // 新旧程度
		newColor: "", //新旧颜色
		trading: "请选择交易方式", // 交易方式
		contact: "", // 联系方式
		contactType: "" // 联系方式
	})
	// 商品分类
	const typeList = reactive({
		columns: [
			['服饰', '数码']
		],
		columnData: []
	})
	// 获取数据
	import {
		onShow
	} from '@dcloudio/uni-app'

	onShow(() => {
		getList();
	})
		const getList = () => {
		let res = getSelectList();
		res.then((result) => {
			// 设置以及分类默认选中
			let num = Math.trunc(result.data[0].length/2);
			// 设置一级分类
			typeList.columns = [result.data[0]];
			// 构造二级分类
			// 使用一个 Map 对象来按照 categoryFatherId 分组
			const groupedData = new Map();

			result.data[1].forEach(item => {
				const {
					categoryFatherId
				} = item;
				if (!groupedData.has(categoryFatherId)) {
					// 如果 Map 中不存在该 categoryFatherId 对应的键,则创建一个新数组
					groupedData.set(categoryFatherId, [item]);
				} else {
					// 如果 Map 中已存在该 categoryFatherId 对应的键,则将对象添加到对应的数组中
					groupedData.get(categoryFatherId).push(item);
				}
			});

			// 将 Map 中的值(数组)转换为最终的结果数组
			const resultArray = Array.from(groupedData.values());
			typeList.columnData = resultArray;
			// 定义比较函数,按照子数组中第一个对象的 categoryFatherId 进行排序
			const compareByCategoryFatherId = (a, b) => {
				const categoryA = a[0].categoryFatherId;
				const categoryB = b[0].categoryFatherId;
				if (categoryA < categoryB) {
					return 1;
				}
				if (categoryA > categoryB) {
					return -1;
				}
				return 0;
			};
			// 对二级分类进行排序 使得和一级分类的数据一一对应(一个一级分类对应一个二级分类数组)
			typeList.columnData.sort(compareByCategoryFatherId);
			// 添加一个二级分类列到一级分类使得一开始就显示一级分类和二级分类 否则刚进去不会显示二级分类
			typeList.columns.push(typeList.columnData[num])
		});
	}

	const changeHandler = (e) => {
		const {
			columnIndex,
			value,
			values, // values为当前变化列的数组内容
			index,
			picker = this.$refs.uPicker
		} = e;
		//当第一列值发生变化时,变化第二列(后一列)对应的选项
		if (columnIndex === 0) {
			// picker为选择器this实例,变化第二列对应的选项
			picker.setColumnValues(1, typeList.columnData[index])
		}
	}
	// 回调参数为包含columnIndex、value、values
	const confirm = (e) => {
		let type = [];
		e.value.forEach(x => type.push(x.name));
		productData.value.type = type;
		showShopType.value = false
		realyType.value = e.value[1].id;
	}
</script>

3.2 getSelectList.js


import http from "../../utils/httpRequest/http";

// 查询所有分类
export const getSelectList = () =>{
	return http.get(`/api/goodsCategory/getSelectList`);
} 

3.3 http.js


const baseUrl = 'http://localhost:9999';
const http = (options = {}) => {
	return new Promise((resolve, reject) => {
		uni.request({
			url: baseUrl + options.url || '',
			method: options.type || 'GET',
			data: options.data || {},
			header: options.header || {}
		}).then((response) => {
			// console.log(response);
			if (response.data && response.data.code == 200) {
				resolve(response.data);
			} else {
				uni.showToast({
					icon: 'none',
					title: response.data.msg,
					duration: 2000
				});
			}
		}).catch(error => {
			reject(error);
		})
	});
}
/**
 * get 请求封装
 */
const get = (url, data, options = {}) => {
	options.type = 'get';
	options.data = data;
	options.url = url;
	return http(options);
}

/**
 * post 请求封装
 */
const post = (url, data, options = {}) => {
	options.type = 'post';
	options.data = data;
	options.url = url;
	return http(options);
}

/**
 * put 请求封装
 */
const put = (url, data, options = {}) => {
	options.type = 'put';
	options.data = data;
	options.url = url;
	return http(options);
}

/**
 * upLoad 上传
 * 
 */
const upLoad = (parm) => {
	return new Promise((resolve, reject) => {
		uni.uploadFile({
			url: baseUrl + parm.url,
			filePath: parm.filePath,
			name: 'file',
			formData: {
				openid: uni.getStorageSync("openid")
			},
			header: {
				// Authorization: uni.getStorageSync("token")
			},
			success: (res) => {
				resolve(res.data);
			},
			fail: (error) => {
				reject(error);
			}
		})
	})
}

export default {
	get,
	post,
	put,
	upLoad,
	baseUrl
}

4. 后台管理页面代码

4.1 GoodsType.vue


<!--
 * @Date: 2024-04-11 18:15:17
 * @LastEditors: zhong
 * @LastEditTime: 2024-04-11 17:25:33
 * @FilePath: \app-admin\src\views\goods\GoodsType.vue
-->
<template>
    <el-main>
        <!-- 搜索栏 -->
        <el-form :model="searchParm" :inline="true" size="default">
            <el-form-item>
                <el-input v-model="searchParm.searchName" placeholder="请输入分类名称"></el-input>
            </el-form-item>
            <el-form-item>
                <el-button icon="search" @click="searchBtn()">搜索</el-button>
                <el-button icon="closeBold" type="danger" @click="resetBtn()">重置</el-button>
                <el-button icon="plus" type="primary" @click="addBtn()">新增</el-button>
                <el-button v-if="isShowBack" icon="RefreshRight" type="success" @click="back()"
                    style="justify-content: flex-end;">返回</el-button>
            </el-form-item>
        </el-form>

        <!-- 表格 -->
        <el-table :height="tableHeight" :data="tableList" border stripe>
            <el-table-column label="序号" align="center">
                <template #default="scope">
                    <div @click="lookInfo(scope.row.categoryId)"> <el-tag>{{ scope.$index + 1 }}</el-tag></div>
                </template>
            </el-table-column>

            <el-table-column label="分类" align="center">
                <template #default="scope">
                    <div @click="lookInfo(scope.row.categoryId)"> <el-tag>{{ scope.row.categoryName }}</el-tag></div>
                </template>
            </el-table-column>
            <!-- 操作 -->
            <el-table-column prop="status" label="操作" align="center" width="220">
                <template #default="scope">
                    <el-button type="primary" icon="edit" size="default" @click="editBtn(scope.row)">编辑</el-button>
                    <el-button type="danger" icon="delete" size="default" @click="deleteBtn(scope.row)">删除</el-button>
                </template>
            </el-table-column>
        </el-table>
        <div class="tips">Tips: 当前为一级分类,点击查看二级分类。</div>
        <!-- 分页 -->
        <div class="page-helper">
            <el-pagination @size-change="sizeChange" @current-change="currentChange"
                :current-page.sync="searchParm.currentPage" :page-sizes="[10, 20, 40, 80, 100]"
                :page-size="searchParm.pageSize" layout="total, sizes, prev, pager, next, jumper"
                :total="searchParm.total" background>
            </el-pagination>
        </div>

        <!-- 新增 -->
        <SystemDialog :title="dialog.title" :height="dialog.height" :width="dialog.width" :visible="dialog.visible"
            @on-close="onClose" @on-confirm="commit">
            <template v-slot:content>
                <!-- 新增内容表单 -->
                <el-form :model="addGoodsTypePram" ref="addRef" :rules="rules" label-width="80px" :inline="false"
                    size="default">
                    <el-form-item prop="categoryName" label="名称:">
                        <el-input v-model="addGoodsTypePram.categoryName"></el-input>
                    </el-form-item>
                    <el-form-item prop="orderNum" label="优先级:">
                        <el-input type="number" v-model="addGoodsTypePram.orderNum"></el-input>
                    </el-form-item>
                </el-form>

            </template>
        </SystemDialog>
    </el-main>

</template>

<script setup lang="ts">
import { nextTick, onMounted, reactive, ref } from 'vue';
import useDialog from '@/hooks/useDialog';
import { Title } from '@/type/BaseEnum';
import { ElMessage, FormInstance } from 'element-plus';
import { getGoodsTypeListApi, addGoodsTypeApi, editGoodsTypeApi, deleteGoodsTypeApi, getGoodsTypeSonListApi, deleteGoodsTypeSonApi, addGoodsTypeSonApi, editGoodsTypeSonApi } from '@/api/goods'
import SystemDialog from '@/components/SystemDialog/SystemDialog.vue';
import { GoodsType } from '@/api/goods/GoodsType';
import useInstance from '@/hooks/useInstance';
// 是否显示返回按钮
const isShowBack = ref(false);
// 返回一级分类
const back = () => {
    isShowBack.value = false;
    getList();
}
//获取当前点击对象的id
const id = ref<string>("0");
// 获取全局属性
const { golbal } = useInstance();
// 获取弹框属性
const { dialog, onClose } = useDialog();
// 表单 ref 属性
const addRef = ref<FormInstance>();
// 搜索绑定的对象 列表查询参数
const searchParm = reactive({
    currentPage: 1,
    pageSize: 10,
    searchName: "",
    total: 0
})
// 标识我们是提交操作还是修改操作
const tags = ref();
// 新增按钮
const addBtn = () => {
    tags.value = '0';
    // 设置弹框标题
    dialog.title = Title.ADD
    dialog.height = 120;
    dialog.visible = true;

    // 清空表单
    addRef.value?.resetFields()
}
// 点击跳转分类详情
const lookInfo = async (id_: string) => {
    if (!isShowBack.value) {
        id.value = id_;

        let res = await getGoodsTypeSonListApi(searchParm, id_);
        if (res && res.code == 200) {
            // res.data.records.forEach((x: { status: number; }) => x.status == 0 ? "true" : "false")
            tableList.value = res.data.records;
            searchParm.total = res.data.total;
            isShowBack.value = true;
        }
    }

}

// 搜索
const searchBtn = () => {
    getList();
}
// 重置
const resetBtn = () => {
    searchParm.searchName = '';
    getList();
}
// 表格数据
const tableList = ref([]);
// 表格高度
const tableHeight = ref(0);
// 列表查询
const getList = async () => {
    let res = await getGoodsTypeListApi(searchParm);
    if (res && res.code == 200) {
        // res.data.records.forEach((x: { status: number; }) => x.status == 0 ? "true" : "false")
        tableList.value = res.data.records;
        searchParm.total = res.data.total;
    }
}
// 新增表单内容
const addGoodsTypePram = reactive({
    categoryId: "",
    categoryName: "",
    orderNum: ""
})
// 新增表单内容
const addGoodsTypeSonPram = reactive({
    categoryId: "",
    categoryName: "",
    orderNum: "",
    categoryFatherId: ""
})


// 表单验证规则
const rules = {
    categoryName: [
        { required: true, message: '请填写商品分类', trigger: 'blur' },
        { min: 0, max: 12, message: 'Length should be 0 to 12', trigger: 'blur' },
    ],
    orderNum: [{ required: true, message: '请输入分类序号', trigger: 'blur' }],
}
// 提交表单
const commit = () => {
    addRef.value?.validate(async (valid) => {
        let res = null;
        if (valid) {
            addGoodsTypeSonPram.categoryId = addGoodsTypePram.categoryId;
            addGoodsTypeSonPram.categoryName = addGoodsTypePram.categoryName;
            addGoodsTypeSonPram.orderNum = addGoodsTypePram.orderNum;
            addGoodsTypeSonPram.categoryFatherId = id.value;
            if (tags.value == '0') {
                // 提交数据
                if (isShowBack.value) {
                    res = await addGoodsTypeSonApi(addGoodsTypeSonPram);
                } else {
                    res = await addGoodsTypeApi(addGoodsTypePram);
                }
            } else {
                // 提交数据
                if (!isShowBack.value) {
                    res = await editGoodsTypeApi(addGoodsTypePram);
                }
                else {
                    res = await editGoodsTypeSonApi(addGoodsTypeSonPram);
                }
            }
            if (res && res.code == 200) {
                // 重新拉取数据用户
                if (isShowBack.value) {
                    isShowBack.value = false;
                    lookInfo(id.value);
                    isShowBack.value = true;
                } else {
                    getList();
                }
                // 信息提示
                ElMessage.success(res.msg);
                // 提交成功 关闭弹框
                dialog.visible = false;
            }
        }
    });
}
// 编辑
const editBtn = (row: GoodsType) => {
    tags.value = '1';
    console.log(row);
    // 设置弹框标题
    dialog.title = Title.EDIT
    dialog.height = 320;
    dialog.visible = true;

    // 设置数据回显
    nextTick(() => {
        Object.assign(addGoodsTypePram, row);
    })

}
// 删除
const deleteBtn = async (row: GoodsType) => {
    console.log(row);
    const confirm = await golbal.$myConfirm("确定删除该数据吗?")
    if (confirm) {
        let res;
        if (!isShowBack.value) {
            res = await deleteGoodsTypeApi(row.categoryId);
        } else {
            res = await deleteGoodsTypeSonApi(row.categoryId);
            isShowBack.value = false;
        }
        if (res && res.code == 200) {
            // 重新拉取数据用户
            getList();
            // 信息提示
            ElMessage.success(res.msg);
        }
        // 设置数据回显
        nextTick(() => {
            Object.assign(addGoodsTypePram, row);
        })
    }
}
// 页容量改变时触发
const sizeChange = (size: number) => {
    searchParm.pageSize = size;
    getList();
}
// 页数改变时触发
const currentChange = (page: number) => {
    searchParm.currentPage = page;
    getList();
}
onMounted(() => {
    tableHeight.value = window.innerHeight - 200;
    getList();
})
</script>

<style lang="scss" scoped>
.tips {
    float: left;
    color: red;
    z-index: 99999;
}

.page-helper {
    margin-top: 20px;
    display: flex;
    justify-content: flex-end;
}
</style>

4.2 GoodsType.ts


/*
 * @Date: 2024-03-31 13:09:13
 * @LastEditors: zhong
 * @LastEditTime: 2024-03-31 13:10:56
 * @FilePath: \app-admin\src\api\goods\GoodsType.ts
 */
// 定义商品分类数据
export type GoodsType = {
    categoryId: string,
    categoryName: string,
    orderNum: string,
}
// 二级商品分类数据
export type GoodsTypeSon = {
    categoryId: string,
    categoryName: string,
    orderNum: string,
    categoryFatherId: string
}

4.3 goods\index.ts


/*
 * @Date: 2024-03-31 13:02:30
 * @LastEditors: zhong
 * @LastEditTime: 2024-04-11 17:23:15
 * @FilePath: \app-admin\src\api\goods\index.ts
 */
import http from "@/http";
import { PageQueryPram } from "../PaginationQueryModel";
import { GoodsType, GoodsTypeSon } from './GoodsType'

// 新增
export const addGoodsTypeApi = (parm: GoodsType) => {
    return http.post("/api/goodsCategory", parm);
}
// 查询所有
export const getGoodsTypeListApi = (parm: PageQueryPram) => {
    return http.get("/api/goodsCategory/getList", parm);
}
// 编辑
export const editGoodsTypeApi = (parm: GoodsType) => {
    return http.put("/api/goodsCategory", parm);
}
// 删除
export const deleteGoodsTypeApi = (categoryId: string) => {
    return http.delete(`/api/goodsCategory/${categoryId}`);
}


// 查询所有子分类
export const getGoodsTypeSonListApi = (parm: PageQueryPram, id: string) => {
    return http.get(`/api/goodsCategory/getInfo/${id}`, parm);
}
// 删除子分类
export const deleteGoodsTypeSonApi = (categoryId: string) => {
    return http.delete(`/api/goodsCategory/son/${categoryId}`);
}
// 新增子分类
export const addGoodsTypeSonApi = (parm: GoodsTypeSon) => {
    return http.post("/api/goodsCategory/son", parm);
}

// 编辑
export const editGoodsTypeSonApi = (parm: GoodsTypeSon) => {
    console.log(parm);
    
    return http.put("/api/goodsCategory/son", parm);
}

4.4 PaginationQueryModel.ts


/*
 * @Date: 2024-03-30 13:16:03
 * @LastEditors: zhong
 * @LastEditTime: 2024-03-31 13:31:27
 * @FilePath: \app-admin\src\api\PaginationQueryModel.ts
 */
// 定义用户的数据类型

export type PageQueryPram = {
    currentPage: number,
    pageSize: number,
    searchName: string,
    total?: number
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Pytest精通指南(06)Fixture scope作用域详解

文章目录 前言Scope 作用域写在测试用例函数文件写在conftest.py文件作用域总结验证默认作用域验证执行顺序遵循验证类中的fixture作用域验证重名fixture作用域 前言 从前文中&#xff0c;我们已经知道固件&#xff08;fixture&#xff09;的概念、原理、作用域&#xff0c;并且…

【年度典型案例】扫码就能领补贴?通知社保在线速办?当心是钓鱼骗局!

随着我们生活的数字化程度越来越高&#xff0c;完成各种业务和服务变得前所未有的便捷。只需轻轻一点手机屏幕&#xff0c;我们办事儿变得飞快又方便。然而&#xff0c;正当我们享受这种数字化带来的便捷时&#xff0c;一些不法分子也在暗中伺机而动&#xff0c;利用各种手段制…

k8s知识

k8s是用于容器编排和管理的&#xff0c;docker或者ctr是k8s的运行时&#xff0c;k8s通过容器运行时来启动容器&#xff0c;容器启动需要镜像&#xff0c;镜像可以用docker构建&#xff0c;dockerfile就是用于自定义如何构建镜像&#xff0c;所以上面那套流水线就是先用dockerfi…

Java算法小练习——五道经典算法题

练习一&#xff1a;按照要求进行排序 定义数组并存储一些朋友对象&#xff0c;利用Arrays中sort方法进行排序 要求1&#xff1a;属性有姓名、年龄、身高。 要求2&#xff1a;按照年龄的大小进行排序&#xff0c;年龄一样&#xff0c;按身高排序&#xff0c;身高一样安姓名的字母…

策略为王股票软件源代码-----如何修改为自己软件05

上面是如何修改里面的图标和图片,,, 试用版下载: http://www.ninebulls.com/ 联系方式: support@ninebulls.com 常见问题: 1。源代码经编程后产生的目标文件执行后显示为试用版,这样是否正常?如何切换成专业版? 显示为评估版是正常的,注册后即切换成专业版。 Too…

【算法一则】做算法学数据结构 - 简化路径 - 【栈】

目录 题目栈代码题解 题目 给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &#xff08;以 ‘/’ 开头&#xff09;&#xff0c;请你将其转化为更加简洁的规范路径。 在 Unix 风格的文件系统中&#xff0c;一个点&#xff08;.&#xff09;表…

python使用ffmpeg分割视频为Hls分片文件/使用OpenSSL加密m3u8和TS文件

FFmpeg和OpenSSL是一个开源免费的软件&#xff0c;在官网上就能下载&#xff0c; FFmpage网址&#xff08;建议选择文件名full结尾的文件&#xff09;&#xff1a;Builds - CODEX FFMPEG gyan.dev OpenSSL网址&#xff08;建议选择win64的MSI文件&#xff09;&#xff1a;Win3…

vscode 中显示 pnpm : 无法加载文件 C:\Users\AppData\Roaming\npm\pnpm.ps1,因为在此系统上禁止运行脚本

vscode 中无法运行pnpm vscode中运行pnpm报错解决办法如下 vscode中运行pnpm报错 pnpm : 无法加载文件 C:\Users\AppData\Roaming\npm\pnpm.ps1&#xff0c;因为在此系统上禁止运行脚本 解决办法如下 1、用get-ExecutionPolicy命令在vscode终端查询状态 如果返回的是 Restr…

堆排序-升序和降序_TopK-N个数找找最大的前K个

一、堆排序 堆排序即利用堆的思想来进行排序&#xff0c;总共分为两个步骤:1.建堆 升序:建大堆 降序:建小堆 2.利用堆删除思想来进行排序 方法一&#xff1a;把数据拷贝进堆、把堆拷贝进数据 //弊端&#xff0c;1.需要先有一个堆 2.时间复杂度拷贝数据 void HeapSort(int* …

前端学习<四>JavaScript基础——18-数组之隐秘

之前学习的数据类型&#xff0c;只能存储一个值&#xff08;字符串也为一个值&#xff09;。如果我们想存储多个值&#xff0c;就可以使用数组。 数组简介 数组&#xff08;Array&#xff09;是属于内置对象&#xff0c;数组和普通对象的功能类似&#xff0c;都可以用来存储一…

一文了解HTTPS的加密原理

HTTPS是一种安全的网络通信协议&#xff0c;用于在互联网上提供端到端的加密通信&#xff0c;确保数据在客户端&#xff08;如Web浏览器&#xff09;与服务器之间传输时的机密性、完整性和身份验证。HTTPS的加密原理主要基于SSL/TLS协议&#xff0c;以下详细阐述其工作过程&…

C语言易错知识点(3):字符数组的修改、sscanf、sprintf

字符数组是一个很细节的语法&#xff0c;涉及很多知识点&#xff0c;这篇文章我主要分享一下如何理解字符数组&#xff0c;以及对应的sscanf、sprintf有什么用 1.字符数组的初始化以及内容修改易错点 字符数组的初始化方式有两种&#xff0c;一种是直接用字符串进行初始化&am…

蓝桥杯第2152题——红绿灯

问题描述 爱丽丝要开车去上班, 上班的路上有许多红绿灯, 这让爱丽丝很难过。为 了上班不迟到, 她给自己的车安装了氮气喷射装置。现在她想知道自己上班最 短需要多少时间。 爱丽丝的车最高速度是 米每秒, 并且经过改装后, 可以瞬间加速到小于 等于最高速的任意速度, 也可以瞵…

(Java)数据结构——图(第五节)Kruskal的实现最小生成树(MST)

前言 本博客是博主用于复习数据结构以及算法的博客&#xff0c;如果疏忽出现错误&#xff0c;还望各位指正。 Kruskal算法&#xff08;Kruskal的实现原理&#xff09; Kruskal算法的原理&#xff1a; 就是每次取最小的边&#xff0c;看看是不是与已经选择的构成回路&#x…

imu6xl点灯(C语言)

参考正点原子开发指南 根据原理图可以看出&#xff0c;我们需要设置低电平导通电路。 在原理图上找到LED0&#xff0c;对应IO为GPIO3 IO复用配置 IMX6UL每个引脚都可以复用 在用户手册第30章可以找到IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03这个寄存器&#xff0c;地址为0x020E0068&…

洛谷-P1036 [NOIP2002 普及组] 选数

P1036 [NOIP2002 普及组] 选数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include<bits/stdc.h> using namespace std; const int N30; int n,r; int g[N]; //存用户输入的数 int arr[N]; //存答案 int res0; //存种类数bool is_prime(int y){ //求素数if(y<2){…

HarmonyOS实战开发-音视频录制、如何实现音频录制和视频录制功能的应用

介绍 音视频录制应用是基于AVRecorder接口开发的实现音频录制和视频录制功能的应用&#xff0c;音视频录制的主要工作是捕获音频信号&#xff0c;接收视频信号&#xff0c;完成音视频编码并保存到文件中&#xff0c;帮助开发者轻松实现音视频录制功能&#xff0c;包括开始录制…

2024年MCN商业模式运营体系行业发展分析

【干货资料持续更新&#xff0c;以防走丢】 2024年MCN商业模式运营体系行业发展分析 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 mcn运营资料包&#xff08;完整资料包含以下内容&#xff09; 目录 MCN机构运营方案的概要&#xff1a; 一、MCN机构定位与目…

Linux中安装seata

Linux中安装seata 一、准备1、环境2、下载3、上传到服务器4、解压 二、配置1、备份配置文件2、导入sql3、修改配置前4、修改配置后5、在nacos中配置 三、使用1、启动2、关闭 一、准备 1、环境 因为要在 nacos 中配置&#xff0c;要求安装并启动 nacos 。可以参考这篇博客。 …

接口优化技巧

一、背景 针对老项目&#xff0c;去年做了许多降本增效的事情&#xff0c;其中发现最多的就是接口耗时过长的问题&#xff0c;就集中搞了一次接口性能优化。本文将给小伙伴们分享一下接口优化的通用方案 二、接口优化方案总结 1.批处理 批量思想&#xff1a;批量操作数据库&a…