SpringBoot+Mybatis实现三级分类联动

一、后台管理系统表格分页形式的分类显示

先来看实现的效果。
在这里插入图片描述这是所有商品的一级分类,选中一个一级分类后(如图中的“家电 数码 手机”)点击“下级分类管理”,即跳转至该分类的二级分类,如下图。
在这里插入图片描述再选择二级分类中的“家电”后,点击“下级分类管理”,即跳转至该二级分类下的三级分类,如下图。
在这里插入图片描述
所有的分类都是记录在同一张表内,表内主要字段如下。

属性名 说明
category_id 自增id,用于记录每个分类的编号
category_level 表示该分类属于几级分类
parent_id 表示该分类的上级分类的id,如果是一级分类,则为0
category_name 分类名称
category_rank 分类排序值,值越高越靠前显示

另外在前后端传值的时候有三个关键的参数:

  • categoryLevel, 用来表格该分类的等级
  • parentId,用来表格该分类的上级分类的id,一级分类则为0
  • backParentId,用来表示三级分类的parentId所对应的二级分类的一级分类

现在开始上代码。
初试列表显示的是所有一级分类,因此这里三个参数分别为:categoryLevel=1,parentId=0,backParentId=0;url为:
admin/categories?parentId=0&categoryLevel=1&backParentId=0
二级列表和三级列表的参数由前端获取到后传给后端。

NewBeeMallGoodsCategoryController.java	

 @Resource
    private NewBeeMallCategoryService newBeeMallCategoryService;
    
 /**
     * 列表显示
     */
    @RequestMapping(value = "/categories/list" ,method = RequestMethod.GET)
    @ResponseBody
    public Result list(@RequestParam Map<String,Object> params){
   
   
        if (StringUtils.isEmpty(params.get("page")) || StringUtils.isEmpty(params.get("limit"))){
   
   
            return ResultGenerator.genFailResult("参数异常");
        }
        PageQueryUtil pageQueryUtil = new PageQueryUtil(params);
        return  ResultGenerator.genSuccessResult(newBeeMallCategoryService.getCategoriesPage(pageQueryUtil));
    }

(categoryLevel=1,parentId=0封装在params中)

NewBeeMallCategoryService.java
    /**
     * 管理后台分页显示
     * @param pageQueryUtil
     * @return
     */
   PageResult getCategoriesPage(PageQueryUtil pageQueryUtil);
NewBeeMallCategoryServiceImpl.java

    @Autowired
    private GoodsCategoryMapper goodsCategoryMapper;
    
    @Override
    public PageResult getCategoriesPage(PageQueryUtil pageQueryUtil) {
   
   
        List<GoodsCategory> goodsCategories = goodsCategoryMapper.findGoodsCategoryList(pageQueryUtil);
        int total = goodsCategoryMapper.getTotalGoodsCategories(pageQueryUtil);
        PageResult pageResult = new PageResult(goodsCategories,total,pageQueryUtil.getLimit(),pageQueryUtil.getPage());
        return pageResult;
    }
GoodsCategoryMapper.java
    /**
     * 后台获取分类列表
     * @param pageQueryUtil
     * @return
     */
    List<GoodsCategory> findGoodsCategoryList(PageQueryUtil pageQueryUtil);

    /**
     * 后台获取分类总数
     * @param pageQueryUtil
     * @return
     */
    int getTotalGoodsCategories(PageQueryUtil pageQueryUtil);

GoodsCategoryMapper.xml

    <sql id="Base_Column_List">
    category_id, category_level, parent_id, category_name, category_rank, is_deleted,
    create_time, create_user, update_time, update_user
    </sql>

   <select id="findGoodsCategoryList" parameterType="map" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"></include>
        from tb_newbee_mall_goods_category
        <where>
            <if test="categoryLevel!=null and categoryLevel!=''">
                and category_level = #{categoryLevel}
            </if>
            <if test="parentId!=null and parentId!=''">
                and parent_id = #{parentId}
            </if>
            and is_deleted = 0
        </where>
        order by category_rank desc
        <if test="start!=null and limit!=null">
            limit #{start},#{limit}
        </if>
    </select>

    <select id="getTotalGoodsCategories" parameterType="Map" resultType="int">
        select count(*) from tb_newbee_mall_goods_category
        <where>
            <if test="categoryLevel!=null and categoryLevel!=''">
                and category_level = #{categoryLevel}
            </if>
            <if test="parentId!=null and parentId!=''">
                and parent_id = #{parentId}
            </if>
            and is_deleted = 0
        </where>
    </select>

二、添加商品页面的下拉菜单式的三级联动

实际效果如图所示。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述第一个下拉菜单默认显示排序值最高的一级分类。第二个下拉菜单显示的是该一级分类下的第一个二级分类。第三个下拉菜单显示的是该二级分类下的第一个三级分类。若没有内容则显示空白。这里有三种情况,分别是:

(1)打开页面后的默认显示

一打开这个页面默认显示的是第一个一级分类,和其所属的第一个二级分类以及该二级分类下的第一个三级分类,即:“家电 数码 手机” -->“家电” --> “生活电器”。

NewBeeMallGoodsController.java

    @Resource
    private NewBeeMallCategoryService newBeeMallCategoryService;

    @GetMapping("/goods/edit")
    public String edit(HttpServletRequest request){
   
   
        request.setAttribute("path", "edit");
        //查询所有的一级分类
        List<GoodsCategory> firstLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(0L), NewBeeMallCategoryLevelEnum.LEVEL_ONE.getLevel());
        if (!CollectionUtils.isEmpty(firstLevelCategories)){
   
   
            //查询一级分类列表中第一个实体的所有二级分类
            List<GoodsCategory> secondLevelCategories = newBeeMallCategoryService.selectByLevelAndParentIdsAndNumber(Collections.singletonList(firstLevelCategories.get(0).getCategoryId()), NewBeeMallCategoryLevelEnum.LEVEL_TWO.getLevel());
            if (!CollectionUtils.isEmpty(secondLevelCategories)){
   
   
                
### 实现 Spring Boot 中的省市区三级联动 #### 项目初始化与配置 为了实现省市区三级联动,首先需要创建一个新的 Spring Boot 项目并添加必要的依赖项。这包括 `web`, `jpa`, `lombok`, `mysql` 和 `jdbc` 启动器[^1]。 ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> </dependencies> ``` #### 数据库表设计 假设已经有一个包含省份、城市和区县的数据表结构。这些数据可以通过 SQL 脚本预先填充到 MySQL 数据库中。 #### Mapper 接口定义 对于每一个级别的区域(省/市/区),都需要编写相应的 MyBatis 或 JPA 的 Repository 来操作数据库。这里以 MyBatis 为例: ```java public interface TAddressProvinceMapper { @Select("SELECT * FROM t_address_province") List<TAddressProvince> getAllProvinces(); } public interface TAddressCityMapper { @Select("SELECT * FROM t_address_city WHERE province_code = #{provinceCode}") List<TAddressCity> getCitiesByProvinceCode(@Param("provinceCode") String provinceCode); } public interface TAddressTownMapper { @Select("SELECT * FROM t_address_town WHERE city_code = #{cityCode}") List<TAddressTown> getTownsByCityCode(@Param("cityCode") String cityCode); } ``` #### 修改启动类扫描包路径 为了让 Spring 自动发现所有的 Mapper 接口,可以在应用入口处通过 `@MapperScan` 注解指定 mapper 文件所在的包名[^2]。 ```java @SpringBootApplication @MapperScan(basePackages = "com.example.mapper") public class ProvinceApplication { public static void main(String[] args) { SpringApplication.run(ProvinceApplication.class, args); } } ``` #### 控制器层处理请求 控制器负责接收来自前端的选择变化事件,并返回对应下一级别的选项列表给前端展示。 ```java @RestController @RequestMapping("/api/address") public class AddressController { private final TAddressProvinceMapper provinceMapper; private final TAddressCityMapper cityMapper; private final TAddressTownMapper townMapper; @Autowired public AddressController(TAddressProvinceMapper provinceMapper, TAddressCityMapper cityMapper, TAddressTownMapper townMapper) { this.provinceMapper = provinceMapper; this.cityMapper = cityMapper; this.townMapper = townMapper; } @GetMapping("/provinces") public ResponseEntity<List<TAddressProvince>> listProvinces() { return new ResponseEntity<>(provinceMapper.getAllProvinces(), HttpStatus.OK); } @GetMapping("/cities/{provinceCode}") public ResponseEntity<List<TAddressCity>> listCities(@PathVariable String provinceCode) { return new ResponseEntity<>(cityMapper.getCitiesByProvinceCode(provinceCode), HttpStatus.OK); } @GetMapping("/towns/{cityCode}") public ResponseEntity<List<TAddressTown>> listTowns(@PathVariable String cityCode) { return new ResponseEntity<>(townMapper.getTownsByCityCode(cityCode), HttpStatus.OK); } } ``` #### 前端 Vue.js 集成 在前端部分可以采用 Vue.js 框架来构建交互界面。当用户选择不同的级别时触发 AJAX 请求获取新的子集数据更新视图[^4]。 ```html <div id="app"> <select v-model="selectedProvince" @change="fetchCities()"> <option value="">请选择省份...</option> <option v-for="item in provinces" :value="item.code">{{ item.name }}</option> </select> <select v-if="selectedProvince" v-model="selectedCity" @change="fetchDistricts()"> <option value="">请选择城市...</option> <option v-for="item in cities" :value="item.code">{{ item.name }}</option> </select> <select v-if="selectedCity" v-model="selectedDistrict"> <option value="">请选择区县...</option> <option v-for="item in districts" :value="item.code">{{ item.name }}</option> </select> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script> <script> new Vue({ el: '#app', data: () => ({ selectedProvince: '', selectedCity: '', selectedDistrict: '', provinces: [], cities: [], districts: [] }), methods: { fetchProvinces() { axios.get('/api/address/provinces') .then(response => (this.provinces = response.data)); }, fetchCities() { if (!this.selectedProvince) return; const url = `/api/address/cities/${this.selectedProvince}`; axios.get(url).then(response => (this.cities = response.data)); }, fetchDistricts() { if (!this.selectedCity) return; const url = `/api/address/towns/${this.selectedCity}`; axios.get(url).then(response => (this.districts = response.data)); } }, created() { this.fetchProvinces(); } }); </script> ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值