select三级联动选择—基于layui的select

Select三级联动选择—基于Layui

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">Dog</option>
    <option value="cat">Cat</option>
    <option value="hamster">Hamster</option>
    <option value="parrot">Parrot</option>
    <option value="spider">Spider</option>
    <option value="goldfish">Goldfish</option>
</select>
<!--在select选择框中已经提前预写了option选项,可以直接选择。但是这种选择的方式有一个非常明显的弊端,如果待选项数目很多,挨个去写很浪费时间而且代码还很臃肿,不美观。-->

这次项目的需求是需要联动选择国内所有的省市县(区)三级,如果以最原始的写法,那无疑是灾难。因此我尝试换一种方式,即从数据库中读取选择框的内容,并且多个选择框之间是联动选择的(即如果选择了广东省,那么你将选不到杭州市,只能从广东省的所有地级市中选择)。

二、分析及前端代码

首先是需要将整个供应商需要提交的表单书写,形成一个大致的轮廓。

要从后台的数据库中读取到数据,就需要前后端交互,因此在本次项目中三级联动的实现主要依靠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>
    <!--resultMap的主要作用是将结果集映射到具体的数据库字段名称上去-->
    
    <select id="getProvince" resultMap="resultMapProvince" resultType="list">
        SELECT DISTINCT province_id,province_name FROM address;
    </select>
    <!--执行查询所有省份的方法-->
    
    <!--根据省份id查询所有的城市-->
    <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>
    
    
    <!--根据城市的id查询所有区(县)-->
    <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!

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_50824019/article/details/130062326