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

news2024/11/27 10:31:21

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
}

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

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

相关文章

蓝桥杯备赛(C/C++组)

README&#xff1a; 本笔记是自己的备考笔记&#xff0c;按照官网提纲进行复习&#xff01;适合有基础&#xff0c;复习用。 一、总考点 试题考查选手解决实际问题的能力&#xff0c;对于结果填空题&#xff0c;选手可以使用手算、软件、编程等方法解决&#xff0c;对于编程大…

Laravel 11入门:使用ServBay打造高效开发环境

Laravel 11发布&#xff0c;改进了不少功能。 它引入了更加流畅的应用结构、每秒限速、健康路由等特性。 此外&#xff0c;Laravel还推出了第一方可扩展的WebSocket服务器Laravel Reverb&#xff0c;为你的应用提供强大的实时功能。 在今天的指南中&#xff0c;我将设置一个…

OSPF中配置VLAN通信(单臂路由)

OSPF中配置VLAN通信&#xff08;单臂路由&#xff09; 单臂路由&#xff08;One-Arm Routing&#xff09;是一种网络路由配置方式&#xff0c;常用于解决网络中的特定问题。在传统的网络架构中&#xff0c;路由器通常需要连接到多个子网或网络段&#xff0c;每个子网都需要一个…

项目管理工具——使用甘特图制定项目计划的详细步骤

甘特图是一种直观的项目管理工具&#xff0c;它有助于我们清晰地展示任务安排、时间管理和项目的进度。以下是使用甘特图制定项目计划的详细步骤&#xff1a; 1、创建项目&#xff1a;首先&#xff0c;在进度猫中创建新的项目&#xff0c;并设置项目的时间、工作日等参数。根据…

test4132

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

14届蓝桥杯 C/C++ B组 T6 岛屿个数 (BFS,FloodFill,填色)

首先拿到这道题不要想着去直接判断环里面的岛屿&#xff0c;这样太困难了&#xff0c;我们可以使用之前做过的题的经验&#xff0c;在输入加入一圈海水&#xff0c;然后从(0,0)点开始BFS&#xff0c;这里进行八向搜索&#xff0c;搜到的0全部都染色成2&#xff0c;假如2能够蔓延…

GEE数据集——巴基斯坦国家级土壤侵蚀数据集(2005 年和 2015 年)

简介 巴基斯坦国家级土壤侵蚀数据集&#xff08;2005 年和 2015 年&#xff09; 该数据集采用修订的通用土壤流失方程 (RUSLE)&#xff0c;并考虑了六个关键影响因素&#xff1a;降雨侵蚀率 (R)、土壤可侵蚀性 (K)、坡长 (L)、坡陡 (S)、覆盖管理 (C) 和保护措施 (P)&#xff…

机器人瓶胚检测工作站(H3U脉冲轴控制)

1、变量定义 2、程序监控1 2、 程序监控2 3、程序监控3 机器人输送料和机构的动作安全尤为重要&#xff0c;下面我们讨论下安全联锁控制逻辑

C++设计模式|0.前言

1.什么是设计模式&#xff1f; 简答来说&#xff0c;设计模式就是一套好用的代码经验总结&#xff0c;也就是怎么写好代码的方法论。使用设计模式是为了可重用代码、让代码更容易被他人理解、提高代码的可靠性。 2.设计模式的分类 设计模式可以分为三类&#xff1a;创建型、…

iOS开发如何更改xcode中的Apple ID

在Xcode中更改Apple ID是一项常见的任务&#xff0c;尤其是当你需要切换到另一个开发者账号或者团队时。下面是一个简单的步骤指南&#xff0c;帮助你更改Xcode中的Apple ID&#xff1a; 步骤一&#xff1a;退出当前的Apple ID 1.打开Xcode应用程序。 2.在菜单栏中&#xff0c;…

Vivado Design Suite中的Routing消息与Intermediate Route结果

在Vivado Design Suite中&#xff0c;优化后的Routing消息和Intermediate Route 结果保存在工程文件中的runs\impl_1中的runme.log文件。 一、Routing消息 当路由器由于拥塞或修复过多的保持时间违规而难以达到时序目标时&#xff0c;Routing消息会提供有用的消息。路由器在努力…

Training - PyTorch Lightning 的 Horovod 策略实践 (all_gather)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/137686312 在 PyTorch Lightning 中使用 Horovod 策略&#xff0c;可以在多个 GPU 上并行训练模型。Horovod 是分布式训练框架&#xff…

手写ArrrayList

需求 自定义的MyArrayList import java.util.Arrays; import java.util.Objects;public class MyArrayList<E> {private Object[] elementData ; // 存储元素的数组private int size; // 记录 的元素个数private static final int DEFAULT_CAPACITY 10; // 默认容量// …

前端重置表单的多个Demo

目录 前言1. 纯重置2. reset重置3. resetFields重置4. 彩蛋 前言 由于从Java转全栈&#xff0c;对于前端的相关知识目前 以点科普面&#xff0c;此处的总结 重置前端表单内容&#xff0c;防止影响后续操作 其基本知识只需要通过点击按钮触发重置表单 1. 纯重置 可以通过按钮…

Golang | Leetcode Golang题解之第24题两两交换链表中的节点

题目&#xff1a; 题解&#xff1a; func swapPairs(head *ListNode) *ListNode {dummyHead : &ListNode{0, head}temp : dummyHeadfor temp.Next ! nil && temp.Next.Next ! nil {node1 : temp.Nextnode2 : temp.Next.Nexttemp.Next node2node1.Next node2.Nex…

一次http访问超时服务器端调试

问题&#xff1a;http访问服务器时没有返回&#xff0c;没有超时&#xff0c;一直在阻塞 处理过程&#xff1a;telnet端口能连上&#xff0c;服务端程序也不存在处理时间过长的情况。 说明tcp连接没问题。推测是客户端连接后再发起请求&#xff0c;服务端阻塞了。因为很多客户…

项目实训2024.04.12日志:Self-QA生成问答对

1. Self-QA技术 1.1. 为什么要用Self-QA技术 关于为什么要搜集问答对&#xff0c;我在创新实训2024.04.07日志&#xff1a;提取QA对这篇文章中提到过&#xff1a;训练大模型需要从业务侧积累的问题、资料、文档中提取出一些指令-问答对作为输入的语料。 之前我们对于问答对的…

Django中间件路由映射自动加/斜杠问题原因及分析

输入 http://127.0.0.1:8000/main/index/ 输入 http://127.0.0.1:8000/main/index 路由定义情况 urlpatterns [path("index/", views.index) ]可以发现我在输入URL的index路由时&#xff0c;如果没有和Django定义的路由匹配规则一样的话&#xff0c;浏览器自…

Python——详细解析目标检测xml格式标注转换为txt格式

本文简述了目标检测xml格式标注的内容&#xff0c;以及yolo系列模型所需的txt格式标注的内容。并提供了一个简单的&#xff0c;可以将xml格式标注文件转换为txt格式标注文件的python脚本。 1. xml格式文件内容 <size>标签下为图片信息&#xff0c;包括 <width> …

【SVN】clean up报错:Cleanup failed to process the following paths 解决方法

报错来源&#xff1a;代码更新有一个文件既不能接受自己的也不能接受别人的&#xff0c;只能取消&#xff0c;再提交提醒clean up&#xff0c;随后报标题错误。 解决方法&#xff1a;参考https://www.cnblogs.com/pinpin/p/11395438.html 注&#xff1a;如果clean up的时候有…