三级联动

写三级联动可能是一张表也可能是三张
下面我就分两种情况,做出个人不同的思路操作



情况一:联动表为三张

假如是三张表,那么它们一定是通过某一个列名所关联的。
那么我们可以采用spring data jpa。了解学习SpringDataJpa (下面是我做的一个省市县的三级联动demo)

mysql数据库设计:

一共三张表,province,city,town ,通过code进行联动

mysql 三级联动 省市县 源数据 表 三级联动数据库设计_html


mysql 三级联动 省市县 源数据 表 三级联动数据库设计_ci_02


mysql 三级联动 省市县 源数据 表 三级联动数据库设计_html_03

后端:

重点在于dao层,所以其他层就没有贴出来了,service,controller注入调用即可。
当然联动可以在实体类用@OneToMany来一次性返回一个json数据给前端,个人喜欢把jpa用于简单的crud

dao层:
在dao层创建三个接口 ProvinceRepository,CityRepository,TownRepository 继承JpaRepository<(对应的实体),String> 因为code,provincecode,citycode字段都是String类型,所以后面放String

public interface ProvinceRepository extends JpaRepository<Province, String> {
}
public interface CityRepository extends JpaRepository<City, String> {
    //自定义需要的方法,根据省级code来得到所关联的市集合
	List<City> findByProvincecode(String provinceCode);
}
public interface TownRepository extends JpaRepository<Town, String> {
  //自定义需要的方法,根据市级code来得到所关联的城县集合
	List<Town> findByCitycode(String cityCode);
}



前端:

ajax:
本人不专研前端,所以代码不是很精湛,但是亲测可用

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .input-lg {
            height: 25px;
        }
    </style>
</head>
<body>
<span>三级联动</span>  
<select class="form-control input-lg province">
    <option value="0">--请选择--</option>
</select>
<select class="form-control input-lg city">
    <option value="0">--请选择--</option>
</select>
<select class="form-control input-lg town">
    <option value="0">--请选择--</option>
</select>
</body>
<script src="/js/jquery-1.8.3.js"></script>
<script src="/js/bootstrap.js"></script>
<script>
    $(function () {
        init();

        //初始化数据
        // 一级分类
        function init() {
            $.ajax({
                url: "/getProvince", //获取省集合方法的请求
                dataType: "json",
                success: function (data) {
                    var html = "";
                    for (var i = 0; i < data.length; i++) {
                        html = "<option value='" + data[i].code + "'>" + data[i].name + "</option>"
                        $(".province").append(html);
                    }
                    change();
                },
                error: function () {
                    alert("一级分类错误");
                }
            })
        }

        //二级分类
        function change() {
            $(".province").change(function () {
                $(".town").empty().append("<option value='0'>--请选择--</option>");
                $(".city").empty().append("<option value='0'>--请选择--</option>");
                var provinceCode = $(".province option:selected").val();
                $.ajax({
                    url: "/getCity", //根据provinceCode获取市集合方法的请求
                    dataType: "json",
                    data: {"provinceCode": provinceCode},
                    success: function (data) {
                        var city = data;
                        var html = "";
                        for (var i = 0; i < city.length; i++) {
                            html = "<option value='" + city[i].code + "'>" + city[i].name + "</option>"
                            $(".city").append(html);
                        }
                        change2();
                    },
                    error: function () {
                        alert("二级分类错误");
                    }

                })
            })

        }


        //三级分类
        function change2() {
            $(".city").change(function () {
                $(".town").empty().append("<option value='0'>--请选择--</option>");
                var cityCode = $(".city option:selected").val();
                $.ajax({
                    url: "/getTown",//根据cityCode获取城县集合方法的请求
                    dataType: "json",
                    data: {"cityCode": cityCode},
                    success: function (data) {
                        var city = data;
                        var html = "";
                        for (var i = 0; i < city.length; i++) {
                            html = "<option value='" + city[i].code + "'>" + city[i].name + "</option>"
                            $(".town").append(html);
                        }

                    },
                    error: function () {
                        alert("三级分类错误");
                    }

                })
            })

        }


    })


</script>
</html>

vue.js:
这里我使用的是vuejs框架。搭建vue-cli脚手架 可以用指令生成脚手架文件后拖入vscode,也可以用webstorm生成脚手架。
需要有node.js,webpack,vue.js否则无法创建脚手架。

注意:实现前后端分离的时候需要解决跨域问题,在config包下找到index.js 设置proxyTable

mysql 三级联动 省市县 源数据 表 三级联动数据库设计_html_04


页面展示:

mysql 三级联动 省市县 源数据 表 三级联动数据库设计_html_05


vue代码:

<template>
  <div>
    <el-select v-model="province" placeholder="请选择">
      <el-option
        v-for="item in provinceOptions"
        :key="item.code"
        :label="item.name"
        :value="item.code"
      ></el-option>
    </el-select>
    <el-select v-model="city" placeholder="请选择">
      <el-option v-for="item in cityOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
    </el-select>
    <el-select v-model="town" placeholder="请选择">
      <el-option v-for="item in townOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
    </el-select>
  </div>
</template>

<script>
const axios = require("axios");
export default {
  data() {
    return {
      province: "",
      city: "",
      town: "",
      provinceOptions: [],
      cityOptions: [],
      townOptions: []
    };
  },
  methods: {
    init() {
      axios
        .get("/apis/getProvince")
        .then(res => (this.provinceOptions = res.data));
    },
    getCity() {
      var province = this;
      axios
        .get("/apis/getCity", {
          params: {
            provinceCode: province.province
          }
        })
        .then(res => (province.cityOptions = res.data));
    },
    getTown() {
      var city = this;
      axios
        .get("/apis/getTown", {
          params: {
            cityCode: city.city
          }
        })
        .then(res => (city.townOptions = res.data));
    }
  },
  created() {
    this.init();
  },
  watch: {
    province: {
      handler() {
        this.cityOptions = [];
        this.townOptions = [];
        this.city = "";
        this.town = "";
        this.getCity();
      }
    },
    city: {
      handler() {
        this.townOptions = [];
        this.town = "";
        this.getTown();
      }
    }
  }
};
</script>

<style scoped>
</style>

情况二:联动表为一张

如果是一张表来三级联动的话,那么个人建议使用mybatis来操作复杂的sql语句,在此就不是很适用jpa了(主要难点在sql的编写和mapper.xml,所以我只列出dao层和实体类的特殊处理)

数据库表设计:

mysql 三级联动 省市县 源数据 表 三级联动数据库设计_html_06


那么sql需要这样写来实现三级联动

SELECT
  a.id one_id,
  a.pname one_pname,
  b.id two_id,
  b.pname two_pname,
  c.id three_id,
  c.pname three_pname
FROM
  teachplan a
  LEFT JOIN teachplan b
    ON b.parentid = a.id
  LEFT JOIN teachplan c
    ON c.parentid = b.id

mapper:

<resultMap id="teachplanMap" type="xxxTeachplanNode ">
        <id column="one_id" property="id"></id>
        <result column="one_pname" property="pname"></result>
        <collection property="children" ofType="同上">
            <id column="two_id" property="id"></id>
            <result column="two_pname" property="pname"></result>
            <collection property="children" ofType="同上">
                <id column="three_id" property="id"></id>
                <result column="three_pname" property="pname"></result>
            </collection>
        </collection>
    </resultMap>
        <select id="selectList" resultMap="teachplanMap">
  SELECT
  a.id one_id,
  a.pname one_pname,
  b.id two_id,
  b.pname two_pname,
  c.id three_id,
  c.pname three_pname
FROM
  teachplan a
  LEFT JOIN teachplan b
    ON b.parentid = a.id
  LEFT JOIN teachplan c
    ON c.parentid = b.id
    </select>

但是要注意,因为只要一张表,那么实体类也只有一个,但是需要得到的json是层级嵌套的
所以需要创建一个新的类

@Data
@ToString
public class TeachplanNode extends Teachplan {
    List<TeachplanNode> children;
}

其他service,controller和前端调用都是和情况一一样的了



小结:

实现后台的三级联动主要在于找到表字段之间的对应关系,sql语句的编写是个突破可,其次,得到的json必须要前端的格式一致,否则会导致有数据但是页面渲染不上。