Layui是一套开源的 Web UI 组件库。layui采用自身极简的模块化规范,并遵循原生 HTML/CSS/JS 的开发模式,极易上手,开箱即用。是前后端初学者必不可少的学习神器。(ps:对于笔者这种前端页面写的很丑的人来说简直是福音啊!)
使用的方式主要有两种,一种直接下载layui的源码使用;一种是使用在线引用(即通过非下载的方式进行使用)。笔者更喜欢通过下载源码来使用。
附上Layui的网址:https://layui.org.cn/index.html
需求是这样的,自己在写一个商城项目的时候,涉及到了供应商提交的表单。表单中有个供应商地址选择运用到了select选择框。以前刚学html的时候,也写过很多次的选择框。但是选择框中的内容选项option都已经是固定的了。如下所示:
<select name="pets" id="pet-select">
<option value="">--Please choose an option--option>
<option value="dog">Dogoption>
<option value="cat">Catoption>
<option value="hamster">Hamsteroption>
<option value="parrot">Parrotoption>
<option value="spider">Spideroption>
<option value="goldfish">Goldfishoption>
select>
这次项目的需求是需要联动选择国内所有的省市县(区)三级,如果以最原始的写法,那无疑是灾难。因此我尝试换一种方式,即从数据库中读取选择框的内容,并且多个选择框之间是联动选择的(即如果选择了广东省,那么你将选不到杭州市,只能从广东省的所有地级市中选择)。
首先是需要将整个供应商需要提交的表单书写,形成一个大致的轮廓。
要从后台的数据库中读取到数据,就需要前后端交互,因此在本次项目中三级联动的实现主要依靠jQuery来实现。
笔者都将jQuery和layui的源码下载下来,之后直接附在项目中。
本文主要针对三个选择框的联动选择,因此将其他内容进行了简化。代码附上
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>供应商登记title>
<script type="text/javascript" src="../../../static/jquery-3.6.3.min.js">script>
<link rel="stylesheet" href="../../../static/layui/css/layui.css">
<script type="text/javascript" src="../../../static/layui/layui.js">script>
<script type="text/javascript" src="../../../static/js/registerVendors.js">script>
head>
<body onload="load()">
<div class="layui-fluid">
<div class="layui-row">
<h1 style="text-align: center">供应商h1>
div>
<form class="layui-form" action="/registerVendors" method="get">
<div class="layui-form-item">
<label class="layui-form-label">注册地址label>
<div class="layui-input-block">
<select id="province" name="vendorsProvince" lay-filter="changeProvince" >
select>
<select id="city" name="vendorsCity" lay-filter="changeCity" >
select>
<select id="region" name="vendorsRegion">
select>
div>
div>
form>
div>
body>
html>
在这个编码中注册地址使用三个select选择框进行表示。lay-filter
起到类似选择器的作用。
实现的效果是:当使用者选中了省,那么系统才会加载地级市,选择了地级市才会加载区。如果有人有意先跳过选择,那么将无法选择任何内容。这样保证了选择的正确性以及简便性。
这样的实现需要搭配js才能实现响应式加载的效果
layui.use('form',function () {
form = layui.form;
loadCities();
loadRegions();
})
layui.use('layer',function () {
layer=layui.layer;
})
//页面开始就加载省份
function load() {
$().ready(function () {
$.ajax({
type: 'post',
url: '/getProvince',
cache: false,
async: true,
dataType: 'json',
success(data) {
console.log(data)
let selectProvince= document.getElementById("province");
selectProvince.add(new Option("请选择对应的省份","0"))
let option=null;
for (let i = 0; i < data.length; i++) {
option = new Option(data[i].provinceName,data[i].provinceId);
selectProvince.add(option)
}
console.log("添加完毕!")
form.render('select')
form.render();
// $("#province").html(options);
},
error() {
console.log("进入了error")
layer.confirm('省份加载暂时出现了点问题!', {icon: 2, btn: ['好的,回主页吧', '去登陆页'], title: '错误信息'}, function () {
window.location.href = '/main'
},
function () {
window.location.href = '/login'
})
}
})
})
}
//选择城市
function loadCities() {
form.on('select(changeProvince)', function () {
$("#city").html('')
//alert("改变方法执行了!")
let provinceId = $("#province option:selected").val();
//let provinceId = data.val();
$.ajax({
type: 'post',
url: '/getCities',
cache: false,
async: true,
data: {"provinceId": provinceId},
// contentType:"application/json",
dataType: 'json',
success(data) {
console.log(data)
let selectCities = document.getElementById("city");
selectCities.add(new Option("请选择对应的城市", "0"));
let option = null;
for (let i = 0; i < data.length; i++) {
option = new Option(data[i].cityName, data[i].cityId);
selectCities.add(option)
}
form.render('select')
form.render();
},
error() {
console.log("进入了error")
layer.confirm('市加载暂时出现了点问题!', {icon: 2, btn: ['好的,回主页吧', '去登陆页'], title: '错误信息'}, function () {
window.location.href = '/main'
},
function () {
window.location.href = '/login'
})
}
})
})
}
//选择地区
function loadRegions () {
form.on('select(changeCity)',function () {
$("#region").html('');
let cityId=$("#city option:selected").val();
$.ajax({
type:'post',
url:'/getRegions',
cache:false,
async:true,
data: {"cityId":cityId},
dataType:'json',
success(data){
let selectRegions = document.getElementById("region");
selectRegions.add(new Option("请选择对应的区",0))
let option=null
for (let i=0;i<data.length;i++){
option=new Option(data[i].regionName,data[i].regionId);
selectRegions.add(option);
}
form.render('select')
form.render();
},
error() {
console.log("进入了error")
layer.confirm('区加载暂时出现了点问题!', {icon: 2, btn: ['好的,回主页吧', '去登陆页'], title: '错误信息'}, function () {
window.location.href = '/main'
},
function () {
window.location.href = '/login'
})
}
})
})
}
实现的方式:
首先页面一加载,通过onload=load()
进行加载每个省的具体名称。即省份的加载伴随着页面的载入进行。加载的具体省份的内容是通过jQuery的ajax方法从后端的数据库获取的。后端返回的结果是json格式的(dataType:json)。
当后台成功返回数据的时候,就会进入success函数,data即为后端返回的实际结果。再对后端返回的json结果进行具体分析,笔者返回的格式为{xxxId:xxxName}。
最后通过option对象,加入到select选择框之中,最后需要着重注意的是 form.render(‘select’),加载完成后需要进行刷新。这是个巨坑。
省份加载完毕之后,地级市的加载与省份的加载不同,我们需要获取到省份加载之后,使用者选取的省份的具体id并将其传到后端,后端根据省份的id选择出对应的所有地级市。
此项功能需要使用到form.on()
函数,该函数内的第一个参数是起到类似选择器的作用,一旦省份被选中,就会立即执行第二参数,即对应的函数进行地级市的加载,仍旧是通过jQuery的ajax方法进行异步请求。返回的格式依旧是json格式,
最后通过将设置的option对象实现select选择框。
需要注意的是 $("#city").html('')
。该项是起到初始化的作用,如果后续出现选择错了省份需要改正,则会将之前选择的内容完全清除掉,这是十分重要的。
区(县)的加载与地级市的加载类似,不过其是通过对应地级市的id来获取对应地级市下所有的区(县)名。操作与地级市加载类似。
在这次的项目中,笔者的后端采用的是SpringBoot框架。通过mvc模式来实现选择框具体内容的加载。
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Province {
@Column(name = "province_id")
private String provinceId;
@Column(name = "province_name")
private String provinceName;
}
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class City {
@Column(name = "city_id")
private String cityId;
@Column(name = "city_name")
private String cityName;
}
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Region {
@Column(name = "region_id")
private String regionId;
@Column(name = "region_name")
private String regionName;
}
三者都使用了lombok进行了setter和getter方法以及构造方法的加载。@Column
的作用是将实体类的字段与数据库表中的字段进行事先的匹配,防止后面出现不匹配报错的情况。
本次数据库设计比较简单,只设计了一张表,并随意填充了一些数据进行测试。
CREATE TABLE IF NOT EXISTS address(
province_id VARCHAR(20) NOT NULL ,
province_name VARCHAR(20) NOT NULL ,
city_id VARCHAR(20) NOT NULL ,
city_name VARCHAR(20) NOT NULL ,
region_id VARCHAR(20) NOT NULL PRIMARY KEY ,
region_name VARCHAR(20) NOT NULL
);
-- 手动添加一些数据
INSERT INTO address VALUES ('p1','北京','c1','北京','r1','房山区');
INSERT INTO address VALUES ('p1','北京','c1','北京','r2','通州区');
INSERT INTO address VALUES ('p1','北京','c1','北京','r3','丰台区');
INSERT INTO address VALUES ('p1','北京','c1','北京','r4','东城区');
INSERT INTO address VALUES ('p1','北京','c1','北京','r5','西城区');
INSERT INTO address VALUES ('p1','北京','c1','北京','r6','崇文区');
INSERT INTO address VALUES ('p1','北京','c1','北京','r7','朝阳区');
INSERT INTO address VALUES ('p1','北京','c1','北京','r8','海淀区');
INSERT INTO address VALUES ('p2','上海','c1','上海','p2r1','嘉定区');
INSERT INTO address VALUES ('p2','上海','p2c1','上海','p2r3','徐汇区');
INSERT INTO address VALUES ('p2','上海','p2c1','上海','p2r4','长宁区');
INSERT INTO address VALUES ('p2','上海','p2c1','上海','p2r5','静安区');
INSERT INTO address VALUES ('p2','上海','p2c1','上海','p2r6','普陀区');
INSERT INTO address VALUES ('p2','上海','p2c1','上海','p2r7','宝山区');
INSERT INTO address VALUES ('p2','上海','p2c1','上海','p2r8','青浦区');
INSERT INTO address VALUES ('p2','上海','p2c1','上海','p2r9','奉贤区');
INSERT INTO address VALUES ('p2','上海','p2c1','上海','p2r10','金山区');
INSERT INTO address VALUES ('P3','天津','p3c1','天津','p3r1','滨海新区');
INSERT INTO address VALUES ('P3','天津','p3c1','天津','p3r2','宁河区');
INSERT INTO address VALUES ('P3','天津','p3c1','天津','p3r3','和平区');
INSERT INTO address VALUES ('P3','天津','p3c1','天津','p3r4','河北区');
INSERT INTO address VALUES ('P3','天津','p3c1','天津','p3r5','河东区');
INSERT INTO address VALUES ('P3','天津','p3c1','天津','p3r6','河西区');
INSERT INTO address VALUES ('P3','天津','p3c1','天津','p3r7','南开区');
INSERT INTO address VALUES ('P3','天津','p3c1','天津','p3r8','武清区');
INSERT INTO address VALUES ('P3','天津','p3c1','天津','p3r9','静海区');
INSERT INTO address VALUES ('P3','天津','p3c1','天津','p3r10','蓟州区');
数据库中将 region_id设置为主键是因为在加载数据的时候,省和市都有可能重复,只有区名在本张表是唯一的。
java代码实现
@Mapper
@Repository
public interface LoadAddressData {
/**
* @return
*/
List<Province> getProvince();
/**
*
* @param provinceId 省份的id值
* @return 返回对应省份id所对应的所有城市
*/
List<City> getCities(@Param("provinceId") String provinceId);
/**
*
* @param cityId 对应城市的id
* @return 返回对应id城市的所有区
*/
List<Region>getRegions(@Param("cityId")String cityId);
}
三个方法返回的结果都是list形式的内容,list里的内容都是一个个对象
通过mybatis的映射文件实现
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mail.dao.vendorsDao.LoadAddressData">
<resultMap id="resultMapProvince" type="com.example.mail.entity.Province">
<result column="province_id" property="provinceId"/>
<result column="province_name" property="provinceName"/>
resultMap>
<select id="getProvince" resultMap="resultMapProvince" resultType="list">
SELECT DISTINCT province_id,province_name FROM address;
select>
<resultMap id="resultMapCities" type="com.example.mail.entity.City">
<result column="city_id" property="cityId"/>
<result column="city_name" property="cityName"/>
<result column="province_id" property="provinceId"/>
resultMap>
<select id="getCities" parameterType="java.lang.String" resultType="list" resultMap="resultMapCities">
SELECT DISTINCT city_id,city_name FROM address WHERE province_id=#{provinceId};
select>
<resultMap id="resultMapRegions" type="com.example.mail.entity.Region">
<result column="city_id" property="cityId"/>
<result column="region_id" property="regionId"/>
<result column="region_name" property="regionName"/>
resultMap>
<select id="getRegions" parameterType="java.lang.String" resultType="list" resultMap="resultMapRegions">
SELECT DISTINCT region_id,region_name FROM address WHERE city_id=#{cityId};
select>
mapper>
public interface LoadAddressDataService {
/**
*获取所有的省份
*/
List<Province> getProvince();
/**
*
* @param provinceId 省份的id值
* @return 返回对应省份id所对应的所有城市
*/
List<City> getCities(@Param("provinceId") String provinceId);
/**
*
* @param cityId 对应城市的id
* @return 返回对应id城市的所有区
*/
List<Region>getRegions(@Param("cityId")String cityId);
}
@Service
public class LoadAddressDataServiceImpl implements LoadAddressDataService {
@Autowired
private LoadAddressData loadAddressData;
/**
*获取所有的省份
*/
@Override
public List<Province> getProvince() {
return loadAddressData.getProvince();
}
/**
* @param provinceId 省份的id值
* @return 返回对应省份id所对应的所有城市
*/
@Override
public List<City> getCities(String provinceId) {
return loadAddressData.getCities(provinceId);
}
/**
* @param cityId 对应城市的id
* @return 返回对应id城市的所有区
*/
@Override
public List<Region> getRegions(String cityId) {
return loadAddressData.getRegions(cityId);
}
}
@Controller
public class LoadAddressDataController {
@Autowired
private LoadAddressDataService loadAddressDataService;
@RequestMapping("/getProvince")
@ResponseBody
public String getProvince(){
List<Province> provinces = loadAddressDataService.getProvince();
return JSON.toJSONString(provinces);
}
@RequestMapping("/getCities")
@ResponseBody
public String getCities( String provinceId){
System.out.println("获取到的省份id为"+provinceId);
List<City> cities = loadAddressDataService.getCities(provinceId);
return JSON.toJSONString(cities);
}
@RequestMapping("/getRegions")
@ResponseBody
public String getRegions(String cityId){
System.out.println("获取到城市id为"+cityId);
List<Region> regions = loadAddressDataService.getRegions(cityId);
return JSON.toJSONString(regions);
}
}
可以看到在控制层代码中最后返回的都是json格式的字符串,这是因为前端js文件中三个ajax请求设置的dataType
,均为json
选择省份
如果绕过选择城市,则无法继续选择区县
最后选择区县
到此over!