概述
文章目录
- 历史记录
- 一、运行环境
- 二、根据商品类查询
- 三、使用JQuery插件实现图片上传
- 四、JS实现省市区三级联动
- 4.1 基本实现
- 4.2 优化:修改时先显示用户的地区信息
- 五、Bug & DeBug
历史记录
-
Web项目实战 | 购物系统v2.0 | 开发记录(一)需求分析 | 技术选型 | 系统设计 | 数据表设计 | SpringBoot、SSM、Thymeleaf、Bootstrap…
-
Web项目实战 | 购物系统v2.0 | 开发记录(二)搭建SpringBoot+SSM框架环境 | 配置Druid+MyBatis | 基于Bootstrap实现登陆页面| 图片验证码接口
-
Web项目实战 | 购物系统v2.0 | 开发记录(三)分页显示 | 根据商品名称进行模糊查询
一、运行环境
- windows10
- IDEA 2021.1 专业版
- JDK8
- SpringBoot2
- Druid 1.2.5
- Bootstrap 4.6.0
- MySQL 8
- Navicat 11
二、根据商品类查询
在实现此功能时有几个难点,如:1)搜索的同时要支持分页显示,保证下一页仍是当前类型;2)支持显示当前选择的类型。
解决思路:
对所有到商品页面的请求添加一个键值对,键为 comType,值为商品类型的ID,若是所有商品就设置为-1,这样在前端页面就可以用th:style
标签直接使用三目运算符进行判断,因显示分类标签时是通过th:each
循环显示的,所以可以得知当前的商品类型的ID。
除了分类按钮的显示外,还有分页的功能,之前分页请求的地址和固定类型后的请求地址不同,所以需要使用th:href
标签借助三目运算符进行判断,若当前comType是-1,则跟原先的请求地址一样/index/页数
,若是其他的值,那么请求的地址需要变化/index/type/页数
。
至此,解决了分类显示、分页的问题,最后还有搜索商品的问题:之前的搜索是默认为所有商品的,现在需要加上一个类型的选择,这样用户可以搜索固定的类别,在前端样式上需稍作修改。
前端代码-搜索部分:
<nav class="nav input-group input-group-sm mt-3 container" th:fragment="search">
<div class="input-group-prepend">
<button id="btn-ctype" class="btn btn-outline-dark dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false"
th:text="${comType.comTypeName}" th:data-id="${comType.id}">
所有商品
</button>
<div class="dropdown-menu">
<a class="dropdown-item">所有商品</a>
<a class="dropdown-item"
th:each="ct: ${session.ctypeInfo}"
th:text="${ct.comTypeName}"
th:onclick="chose([[${ct.id}]], [[${ct.comTypeName}]])"
th:data-id="${ct.id}"
/>
</div>
</div>
<input id="input-search" th:value="${searchText}" type="text" class="form-control" placeholder="请输入查询的商品,商品名称/种类/...">
<div class="input-group-append">
<button class="input-group-text btn btn-info" id="btn-search">
<i class="bi bi-search"></i>
</button>
</div>
</nav>
前端代码-商品分页部分
<!-- 分页部分 -->
<h1 th:text="'第' + ${pageInfo.pageNum} + '页 共' + ${pageInfo.pages} + '页'" class="u-text-sm text-center"></h1>
<nav aria-label="Page navigation" class="u-text-sm" th:if="${comInfo.size > 0}">
<ul class="pagination justify-content-center">
<!--首页-->
<li class="page-item">
<a class="page-link u-a-com"
th:data-type="${comType.id}"
th:data-searchtext="${searchText}"
th:data-pagenow="${pageInfo.navigateFirstPage}">
首页
</a>
</li>
<!--上一页-->
<li class="page-item">
<a class="page-link u-a-com"
th:data-type="${comType.id}"
th:data-searchtext="${searchText}"
th:data-pagenow="${pageInfo.prePage}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<!--某一页-->
<li class="page-item" th:each="pageNow : ${pageInfo.navigatepageNums}">
<a th:data-type="${comType.id}"
th:data-searchtext="${searchText}"
th:data-pagenow="${pageNow}" th:text="${pageNow}"
th:class="'page-link u-a-com' + ${pageNow == pageInfo.pageNum ? ' bg-info text-light' : ''}">
</a>
</li>
<!--下一页-->
<li class="page-item">
<a class="page-link u-a-com"
th:data-type="${comType.id}"
th:data-searchtext="${searchText}"
th:data-pagenow="${pageInfo.nextPage}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
<!--最后一页-->
<li class="page-item">
<a class="page-link u-a-com"
th:data-type="${comType.id}"
th:data-searchtext="${searchText}"
th:data-pagenow="${pageInfo.navigateLastPage}">
末页
</a>
</li>
</ul>
</nav>
分页部分JS代码:
$('.u-a-com').click(function (){
let type = $(this).data('type')
let searchText = $(this).data('searchtext')
let pageNow = $(this).data('pagenow')
if(searchText != null)
$(this).attr('href', '/index.html?type=' + type + '&searchText=' + searchText + '&pageNow=' + pageNow)
else
$(this).attr('href', '/index.html?type=' + type + '&pageNow=' + pageNow)
})
控制层代码:
package com.uni.controller;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.uni.dao.CommodityMapper;
import com.uni.dao.CommodityTypeMapper;
import com.uni.pojo.Commodity;
import com.uni.pojo.CommodityExample;
import com.uni.pojo.CommodityType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller
public class CommodityAction {
@Autowired
CommodityMapper commodityMapper;
@Autowired
CommodityTypeMapper commodityTypeMapper;
@GetMapping("/index.html")
public String findAllByPage(ModelMap modelMap,
@RequestParam(value="type", defaultValue = "0") Integer type,
@RequestParam(value="searchText", defaultValue = "") String searchText,
@RequestParam(value = "pageNow" , defaultValue = "1") Integer pageNow,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize){
PageHelper.startPage(pageNow, pageSize);
CommodityExample example = new CommodityExample();
CommodityExample.Criteria criteria = example.createCriteria();
if(type != 0)
criteria.andComTypeEqualTo(type);
criteria.andComNameLike("%" + searchText + "%");
List<Commodity> comInfo = commodityMapper.selectByExample(example);
PageInfo<Commodity> pageInfo = new PageInfo<>(comInfo);
// 首页设置上一个页面为1,防止上一页到末页
if(pageNow == 1) pageInfo.setPrePage(1);
// 末页设置最后一个页面,防止下一页到首页
if(pageInfo.getNextPage() == 0) pageInfo.setNextPage(Math.min(pageInfo.getPrePage() + 1, pageInfo.getPageNum()));
// 查询商品类型
comInfo.forEach(com -> {
com.setCommodityType(commodityTypeMapper.selectByPrimaryKey(com.getComType()));
});
modelMap.addAttribute("pageNow", pageNow);
modelMap.addAttribute("pageSize", pageSize);
modelMap.addAttribute("comInfo", comInfo);
modelMap.addAttribute("pageInfo", pageInfo);
modelMap.addAttribute("searchText", searchText);
modelMap.addAttribute("comType", type == 0 ?
new CommodityType(0, "所有商品"): commodityTypeMapper.selectByPrimaryKey(type));
return "index";
}
}
三、使用JQuery插件实现图片上传
插件地址:点击访问
下载好插件后将JS、CSS插件复制到项目里,然后参考给出的index.html样例对自己的项目进行修改,最后的效果:
四、JS实现省市区三级联动
4.1 基本实现
全国省市地区信息的 MySql 数据代码 :点击查看
SQL脚本执行后的结果:
为了方便JDBC操作,笔者依旧采用之前MyBatis逆向工程的方式【点击查看】,不用手写Mapper接口和映射文件。MyBatis逆向工程的配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- context 运行上下文 -->
<!-- id 必唯一 -->
<!-- defaultModelType:可选;flat:所有内容(主键,blob)等全部生成在一个对象中 -->
<!-- targetRuntime:可选 ;建议:MyBatis3Simple/MyBatis3 -->
<context id="MySqlTables" defaultModelType="flat"
targetRuntime="MyBatis3">
<!--beginningDelimiter和endingDelimiter:指明数据库的用于标记数据库对象名的符号 -->
<!--如ORACLE就是双引号;MYSQL默认是`反引号; -->
<property name="beginningDelimiter" value="`" />
<property name="endingDelimiter" value="`" />
<!-- 指定生成的java文件的编码,没有直接生成到项目时中文可能会乱码 -->
<property name="javaFileEncoding" value="UTF-8" />
<!-- 格式化java代码 -->
<property name="javaFormatter"
value="org.mybatis.generator.api.dom.DefaultJavaFormatter" />
<!-- 格式化XML代码 -->
<property name="xmlFormatter"
value="org.mybatis.generator.api.dom.DefaultXmlFormatter" />
<!--自动实现Serializable可序列化接口 -->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
<!--去除生成的注释;建议初次不去注释 -->
<commentGenerator>
<!-- 去除自动生成时的时间戳 -->
<property name="suppressDate" value="true" />
<!-- 去除自动生成的注释 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库基本信息 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/simple_shop_system_2?characterEncoding=UTF-8"
userId="root"
password="数据库密码">
</jdbcConnection>
<!-- java类型处理器 用于处理DB中的类型到Java中的类型,默认使用JavaTypeResolverDefaultImpl; -->
<!-- 注意一点,默认会先尝试使用Integer,Long,Short等来对应DECIMAL和 NUMERIC数据类型; -->
<javaTypeResolver
type="org.mybatis.generator.internal.types.JavaTypeResolverDefaultImpl">
<!-- true:使用BigDecimal对应DECIMAL和 NUMERIC数据类型 -->
<!--false:默认, -->
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--Java model(POJO)生成的位置 -->
<!--targetPackage:包路径 -->
<!--targetProject:项目路径 -->
<javaModelGenerator targetPackage="com.uni.pojo"
targetProject="src/main/java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="true" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--*Mapper.xml生成的位置 -->
<sqlMapGenerator targetPackage="mybatis.mapper"
targetProject="src/main/resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- 对于mybatis来说,即生成Mapper接口,注意,如果没有配置该元素,那么默认不会生成Mapper接口 -->
<!--type:选择怎么生成mapper接口(在MyBatis3/MyBatis3Simple下) -->
<!--1,ANNOTATEDMAPPER:会生成使用Mapper接口+Annotation的方式创建(SQL生成在annotation中),不会生成对应的XML -->
<!--2,MIXEDMAPPER:使用混合配置,会生成Mapper接口,并适当添加合适的Annotation,但是XML会生成在XML中 -->
<!--3,XMLMAPPER:会生成Mapper接口,接口完全依赖XML -->
<!--注意,如果context是MyBatis3Simple:只支持ANNOTATEDMAPPER和XMLMAPPER -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.uni.dao" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!--表的名称;对应类的名称; -->
<table tableName="province" domainObjectName="Province">
<property name="useActualColumnNames" value="true"/>
</table>
<table tableName="city" domainObjectName="City">
<property name="useActualColumnNames" value="true"/>
</table>
<table tableName="area" domainObjectName="Area">
<property name="useActualColumnNames" value="true"/>
</table>
</context>
</generatorConfiguration>
跟之前一样,使用Maven插件生成完毕后,在DAO层的Mapper接口类上方添加@Component
注解,注入到Spring IoC容器中,同时在相应的pojo类都添加toString()
方法,笔者建议用lombok包,使用@Data
注解就能实现,特别方便。
PS:SpringBoot项目记得设置支持扫描通过注解标记的Mapper接口类的包,方法有多种,笔者是在SpringBoot启动类上方使用@MapperScan(value = "DAO层所在包名")
的注解。
测试代码:
@Autowired
ProvinceMapper provinceMapper;
@Autowired
CityMapper cityMapper;
@Test
public void TestProvinceCityArea(){
/* 设置省份的查询条件 */
ProvinceExample provinceExample = new ProvinceExample(){{
createCriteria().andNameLike("%浙江%");
}};
/* 根据条件查询省份 -> 浙江 */
List<Province> provinces = provinceMapper.selectByExample(provinceExample);
Province ZheJiang = provinces.get(0);
/* 设置的城市查询条件 */
CityExample cityExample = new CityExample(){{
createCriteria().andProvincecodeEqualTo(ZheJiang.getCode());
}};
// 输出浙江省的信息
System.out.println(ZheJiang);
// 根据省份ID -> 浙江省ID 查找城市
cityMapper.selectByExample(cityExample).forEach(city -> System.out.println(city));
}
运行结果:
控制层,负责返回数据库的数据:
package com.uni.controller;
import com.alibaba.fastjson.JSON;
import com.uni.dao.AreaMapper;
import com.uni.dao.CityMapper;
import com.uni.dao.ProvinceMapper;
import com.uni.pojo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class AddressAction {
@Autowired
ProvinceMapper provinceMapper;
@Autowired
CityMapper cityMapper;
@Autowired
AreaMapper areaMapper;
@GetMapping("/getProvinces")
public String getProvinces(){
return JSON.toJSONString(provinceMapper.selectByExample(new ProvinceExample()));
}
@GetMapping("/getCities")
public String getCities(String provCode){
CityExample cityExample = new CityExample() {{
createCriteria().andProvincecodeEqualTo(provCode);
}};
List<City> cities = cityMapper.selectByExample(cityExample);
return JSON.toJSONString(cities);
}
@GetMapping("/getAreas")
public String getAreas(String cityCode){
AreaExample areaExample = new AreaExample(){{
createCriteria().andCitycodeEqualTo(cityCode);
}};
List<Area> areas = areaMapper.selectByExample(areaExample);
return JSON.toJSONString(areas);
}
}
实现省市区三级联动的JS代码:
/* 省市三级联动 */
// 获取用户的信息
var userProvince = $('select[name="userProvince"]').data('addr')
var userCity = $('select[name="userCity"]').data('addr')
var userArea = $('select[name="userArea"]').data('addr')
var userAddress = $('input[name="userAddress"]').data('addr')
$(function() {
// 初始化省市区
initAddress();
// 更改省份后的操作
$("select[name='userProvince']").change(function() {
var provCode = $("select[name='userProvince']").val();
getCity(provCode);
});
// 更改城市后的操作
$("select[name='userCity']").change(function() {
var cityCode = $("select[name='userCity']").val();
getArea(cityCode);
});
});
function initAddress() {
$('input[name="userAddress"]').val(userAddress)
var firstProvCode ='0';
// ajax请求所有省份
$.get({
url: "/getProvinces",
dataType: 'JSON',
error: (err) => {
alert('获取省份失败.')
console.error(err.responseText)
},
success: (data) => {
$.each(data, function(i, d) {
if(d.name === userProvince) firstProvCode = d.code
$("select[name='userProvince']").append(
"<option value='" + d.code + (d.name===userProvince ? "' selected>" : "'>") + d.n
+ "</option>");
});
// 获取第一个省份的code
if(firstProvCode != '0')
$("select[name='userProvince']").css('value', userProvince)
else
firstProvCode = data[0].code;
// 根据第一个省份code获取对应城市列表
getCity(firstProvCode);
}
});
}
//获取对应城市列表(里面包括获取第一个城市的区县列表)
function getCity(provCode) {
var firstCityCode = '0';
// ajax请求所有市级单位
$.get({
url: "/getCities",
data: {provCode:provCode},
dataType: 'JSON',
error: (err) => {
alert('获取市区失败.')
console.error(err.responseText)
},
success: (data) => {
// 先清空城市下拉框
$("select[name='userCity']").empty();
$.each(data, function(i, d) {
if(d.name === userCity) firstCityCode = d.code
$("select[name='userCity']").append(
"<option value='" + d.code + (d.name===userCity ? "' selected>" : "'>") + d.name
+ "</option>");
});
// 获取第一个城市的code
if(firstCityCode != '0') {
$("select[name='userCity']").attr('value', userCity)
}else firstCityCode = data[0].code;
// 根据第一个城市code获取对应区县列表
getArea(firstCityCode);
}
})
}
function getArea(cityCode) {
// ajax请求所有区县单位
$.get({
url: '/getAreas',
data: {cityCode:cityCode},
dataType: 'JSON',
error: (err) =>{
alert('获取地区信息失败.')
console.error(err.responseText)
},
success: (data) => {
// 先清空区县下拉框
$("select[name='userArea']").empty();
$.each(data, function (i, d) {
$("select[name='userArea']").append(
"<option value='" + d.code + (d.name===userArea ? "' selected>" : "'>") + d.name
+ "</option>");
});
}
})
}
前端HTML代码:
<li>
<label>地址:</label>
<select name="userProvince" th:data-addr="${userProvince}" style="font-size: 10px"></select>
<select name="userCity" th:data-addr="${userCity}" style="font-size: 10px"></select>
<select name="userArea" th:data-addr="${userArea}" style="font-size: 10px"></select>
</li>
<li>
<label>详细地址:</label>
<input name="userAddress" th:data-addr="${userAddress}" type="text" placeholder="详细地址">
</li>
4.2 优化:修改时先显示用户的地区信息
在之前SQL表的基础上,User用户信息表里address字段表示当前用户的地址,而真正的地址有四个部分,分别为省、市/区 、县/镇、详细地址。
这里笔者采用四合一方式,使用分隔符 -
将四个信息连接起来成一个字符串序列
控制层代码:
package com.uni.controller;
import com.uni.pojo.User;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
@Log4j2
@Controller
public class UserAction {
@GetMapping("/user.html")
public String toUserCenter(){
return "redirect:/user/info.html";
}
@GetMapping("/user/info.html")
public String toInfo(HttpSession session, ModelMap modelMap){
// 获取当前用户的地址信息
User user = (User) session.getAttribute("userInfo");
String[] addr = user.getAddress().split("-");
// 依次为 省份-城市-地区-详细地址
if(addr!=null && addr.length == 4){
modelMap.addAttribute("userProvince", addr[0]);
modelMap.addAttribute("userCity", addr[1]);
modelMap.addAttribute("userArea", addr[2]);
modelMap.addAttribute("userAddress", addr[3]);
}
// 标记当前页面的类型
modelMap.addAttribute("type", "info");
return "user";
}
}
五、Bug & DeBug
在使用 Bootstrap4.6版本的下拉菜单时报错
bootstrap.js:1667 Uncaught TypeError: Bootstrap's dropdowns require Popper (https://popper.js.org)
at Dropdown.show (bootstrap.js:1667:17)
at Dropdown.toggle (bootstrap.js:1635:12)
at HTMLButtonElement.<anonymous> (bootstrap.js:1863:23)
at Function.each (jquery.js:354:19)
at jQuery.fn.init.each (jquery.js:189:17)
at jQuery.fn.init._jQueryInterface [as dropdown] (bootstrap.js:1848:19)
at HTMLButtonElement.<anonymous> (bootstrap.js:2030:31)
at HTMLDocument.dispatch (jquery.js:5183:27)
at HTMLDocument.elemData.handle (jquery.js:4991:28)
解决方式一:在导入bootstrap.js前导入popper.js 脚本,这是下拉菜单用到的拓展插件,插件官网:点击查看
pom.xml
<!-- https://mvnrepository.com/artifact/org.webjars.npm/popper.js -->
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>popper.js</artifactId>
<version>1.16.1</version>
</dependency>
Thymeleaf页面导入脚本:
<script type="text/javascript" th:src="@{/webjars/jquery/3.3.1/jquery.js}"></script>
<script type="text/javascript" th:src="@{/webjars/popper.js/1.16.1/dist/umd/popper.js}"></script>
<script type="text/javascript" th:src="@{/webjars/bootstrap/4.6.0/js/bootstrap.js}"></script>
解决方式二:使用Bootstrap4框架提供的集成后的JS脚本 bootstrap.bundle.js
<script type="text/javascript" th:src="@{/webjars/bootstrap/4.6.0/js/bootstrap.bundle.js}"></script>
最后
以上就是霸气薯片为你收集整理的Web项目实战 | 购物系统v2.0 | 开发记录(四)单个页面单个请求解决根据商品类型进行分页查询 | 使用省市区三级联动 | 使用JQuery 插件实现图片上传历史记录一、运行环境二、根据商品类查询三、使用JQuery插件实现图片上传四、JS实现省市区三级联动五、Bug & DeBug的全部内容,希望文章能够帮你解决Web项目实战 | 购物系统v2.0 | 开发记录(四)单个页面单个请求解决根据商品类型进行分页查询 | 使用省市区三级联动 | 使用JQuery 插件实现图片上传历史记录一、运行环境二、根据商品类查询三、使用JQuery插件实现图片上传四、JS实现省市区三级联动五、Bug & DeBug所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复