三级联动
写三级联动可能是一张表也可能是三张
下面我就分两种情况,做出个人不同的思路操作
情况一:联动表为三张
假如是三张表,那么它们一定是通过某一个列名所关联的。
那么我们可以采用spring data jpa。了解学习SpringDataJpa (下面是我做的一个省市县的三级联动demo)
mysql数据库设计:
一共三张表,province,city,town ,通过code进行联动
后端:
重点在于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
页面展示:
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层和实体类的特殊处理)
数据库表设计:
那么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必须要前端的格式一致,否则会导致有数据但是页面渲染不上。