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

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
}
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

她似晚风般温柔789

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值