-
续0905
-
使用art-template模板,来给一些重复性的代码写模板
- https://aui.github.io/art-template/
-
- 当你写完了模板之后,你需要去连接后台响应的数据和模板,然后追加到页面去显示
- 相比较之前拼接的方法去显示,在需要用到非常多相同格式的数据时,会简单很多
-
然后就是需要用到评分组件--星型组件
-
- 星星插件:https://www.jq22.com/jquery-info6430
- 要用别人的东西,我们首先要引入别人的插件
- 然后我们就需要去给需要指定星星插件的标签起个名字,方便后续指定
- data-score="",title也不要漏了
- //初始化星形图片目录,前面这一部分不要写错了
$.fn.raty.defaults.path="/xzw/resources/raty/lib/images/"; - 这个目录我们要到哪里去初始化呢,可以看官方文档
- 在初始化目录的时候,报了一个这样的错:
-
解决办法:之前报错是因为引入插件的时候写错了,js不是用link
- 当上面的错误排除之后,页面上还是不会有效果的,你需要去指定那个标签用到了星型插件,在这个案例当中是在success回调函数末尾指定
-
然后我们现在要实现,点击加载更多,显示更多数据
- 首先我们想一下实现的思路:当我们点击加载更多的时候,我们需要触发一个事件,发送一个ajax请求,将当前页传到后台,从而返回当前页的数据,那么我们就需要获取到当前页的页码,所以我们需要在页面上写一个辅助分页的代码,并将其隐藏起来,当我们触发事件之后,获取到辅助分页的默认当前页value值,发送ajax将下一页数据显示出来之后,再将辅助分页的value值+1
- 因为后台响应的数据res(info)中,有一个current属性,所以可以利用它来+1,然后因为怕数据是字符串类型的,所以给他强转一下
-
那么我们的分页效果就实现了
- 当我们使用上面的思想实现分页显示的时候,第一次和后续加载代码上ajax是完全重复的,所以可以优化,思路就是要判断哪一次是第一次,所以可以写一个方法来判断,然后后续来调用就可以了
-
//加载下一页 function loadNext(isFirst){ if(isFirst == true){ //如果是第一页,页码设置为1,不是,就不会执行 $('#nextPage').val(1); } var nextPage = $('#nextPage').val();//如果是第一页,则取出来的是1 $.ajax({ url: 'books', type: 'get', data: {"page":nextPage}, //另一种方式,序列化,就能拿到表单里面的数据,但是这样写需要注意传的数据要和User类能对上 // data:$('#frm').serialize(), //如果不需要返回数据,就不用写 dataType: 'json', success: function (info) { console.log(info); var bookList = info.records; for (var i = 0; i < bookList.length; i++) { var book = bookList[i]; //连接模板与后台响应的结果book var html = template("tpl",book);//将结果拼接在模板上之后,追加到div(bookList)中 $('#bookList').append(html); } //表示为星型组件 // $('.stars').raty(); //readOnly:true设为只读,把stars变为星型组件,stars可以理解为一个标记,在这里把标记了这个名称的变为星型组件 $('.stars').raty({readOnly:true}); //如果当前页,小于总页数,则将辅助分页的下一页的文本框的值改为:当前页+1 if(info.current < info.pages){ $('#nextPage').val(parseInt(info.current)+1) }else { $('#btnMore').hide() $('#divNoMore').show() } } }) } $(function () { //莫问首页刚打开时,为true,设为1 loadNext(true); //然后就是点击加载更多的时候,调用上面的方法,就不需要写重复的代码了,也是优化代码 //单击btnMore这个按钮,加载更多,实际上是分页显示 $('#btnMore').click(function () { loadNext(nextPage); }) })
-
首先默认是按全部显示,然后我们实现点击其他分类,点击的分类高亮显示
- 实现思路,点击类别触发单击事件,先清除原本的高亮,然后给当前点击的类别添加高亮
- html静态原型代码
-
<div class="col-8 mt-2"> <span data-category="-1" style="cursor:pointer" class="highlight font-weight-bold category mr-2">全部</span> | <#list categoryList as category> <a style="cursor:pointer" data-category="${category.categoryId}" class="text-black-50 font-weight-bold category">${category.categoryName}</a> <#--如果还有下一个,就输入|这个符号--> <#if category_has_next>|</#if> </#list> </div>
js效果实现代码:
//给导航栏的五个分类元素设置高亮: $('.category').click(function () { //移除当前高亮 $('.category').removeClass("highlight"); /* //再设置category的样式为灰色,因为类别当中你写了行内样式,所以其实可以不写 $('.category').addClass('text-black-50');*/ //设置当前点击的对应元素为高亮 $(this).addClass("highlight"); })
-
进行到这一步,我们还只是实现了页面上的效果,还没有实现点击分类和排序立马去后台进行查询显示,那么我们现在需要先去后台将功能实现
- 在bookServiceImpl,我们需要将业务写好,也就是分页查询写好,原先我们需要在业务层调用dao层的方法,通过到层的方法去执行mapper.xml里面的SQL语句,但是我们用了mybatis-plus,dao层接口继承了BaseMapper,那些简单的CRUD,框架已经帮我们写好了,我们只需要准备好需要的参数,去调用框架里面的方法就好了 ,在分页查询当中,我们主要需要用到Ipage,page(用于分页),QueryWrapper(用来组装条件,常用的可以去csdn看别人写的)
-
@Service public class BookServiceImpl implements BookService { @Autowired private BookMapper bookMapper; @Override public IPage<Book> paging(Long categoryId,String order,Integer page, Integer rows) { Page<Book> p = new Page<Book>(page, rows); //组装查询和排序条件 QueryWrapper<Book> queryWrapper = new QueryWrapper<Book>(); //按类别查,如果categoryId != null/-1,就查所有类别 if(categoryId != null && categoryId!= -1){ queryWrapper.eq("category_id",categoryId);//如果条件满足,就往SQL后面追加条件,相当于where xx == #{xx} } //排序 if(order != null){ if(order.equals("quantity")){//按数量 queryWrapper.orderByDesc("evaluation_quantity");//相当于orderby xx desc; }else if(order.equals("score")){//按评分 queryWrapper.orderByDesc("evaluation_score"); } } //按组装好的条件查询 IPage<Book> pageObject = bookMapper.selectPage(p,queryWrapper); return pageObject; } }
业务层写完之后,可以先去做个测试,看业务是否写好了,如果写好了,我们就可以通过控制层,连接前后端,从前端获取到用户传过来的参数,将数据显示到页面
- 控制层代码如下:需要前端传递类别,排序方式,以及页码
-
@Controller public class BookController { @Autowired private CategoryService categoryService; @Autowired private BookService bookService; /*显示首页*/ @GetMapping("/") public ModelAndView showIndex(){ ModelAndView modelAndView = new ModelAndView("/index"); List<Category> categoryList = categoryService.selectAll(); modelAndView.addObject("categoryList",categoryList); return modelAndView; } @RequestMapping("/books") @ResponseBody public IPage<Book> selectBooks(Long categoryId,String order,Integer page){ if(page == null){ page = 1; } IPage<Book> pageObject = bookService.paging(categoryId,order,page,2); return pageObject; }
-
- 然后,因为我们的前端页面原型已经搭建好了,那么我们就是需要通过获取用户操作后的参数,通过ajax的方式,传递给控制层,来刷新页面
- 思路:我们默认第一次是按照全部分类,根据热度排序,然后根据用户的操作,来刷新页面,那么当用户点击其他分类以及排序方式的时候,我们在触发事件让页面高亮的同时,需要利用到我们的辅助分页,实时更新其中相应标签的value值,并同时调用分页查询的方法(此时,因为我们分页查询的条件变多了,那么该获取并传递的参数,我们都需要获取并传递好),在分页方法中,我们也需要改进。
-
编写时遇到的问题和注意点
- 辅助分页当中种类categoryId和排序方式order的value值在什么时候改变,改变什么?
-
- 调用方法刷新的时候,要防止数据追加,所以要清空div之前加载过的内容
-
-
- 按钮的显示与隐藏,需要注意
-
-
- 最后的效果:
到此,我们的index页面就完成了
index.ftl页面所有代码:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Title</title>
<!--https://zhuanlan.zhihu.com/p/196777759–>
viewport 视口(可视区窗口),通过meta标签设置。
viewport 决定了我们的页面能不能在移动端正常显示。
viewport 默认值980,区间(768 ~ 1024)。
width: 视口的宽度,width的值为一个正整数,或字符串device-width(设备的实际宽度 = 设备的css像素)。
不建议设置数字(安卓设备有些不支持)
height: 视口的高度,一般不需要设置
user-scalable: 是否允许用户进行页面缩放(包括浏览器)值为no或yes,代表不允许与允许。
如果不设置user-scalable=no; 移动设备的浏览器为了给用户显示更全面的信息,所以会缩放比例(缩放的不是单个元素,而是整个页面)
initial-scale: 页面初始缩放值, 值为一个数字(可以带小数)。
设置initial-scale=1.0;时,其实跟设置width=device-width;是一样的效果。
设置initial-scale=1.0;时,表示不给移动设备的浏览器进行缩放,还原原始的值。
如果同时设置width=device-width, initial-scale=1.0;时,因为两个的值都是一样的,所以无法比较。
如果设置width=400, initial-scale=1.0;时,值不一样时,浏览器会取一个最大的值。
initial-scale有值的情况下,计算的页面公式:
缩放比 = css像素 / viewport宽度
viewport宽度 = css像素 / 缩放比
minimum-scale: 页面最小能够缩放的比例
值为一个数字(可以带小数)。
maximum-scale: 页面最大能够缩放的比例
值为一个数字(可以带小数)。
未设置viewport的时候:
1. 屏幕的宽度默认为980,但不同的型号也会不同。
2. 用window.innerWidth方法获取。
注意:
1、有的时候大家会见到同时写了不允许缩放,又写了最小与最大能够缩放的比例,那这样不是冲突了,为什么都已经写了不允许缩放了,还要写那些?
原因:
a.会有一些第三方工具能够破坏user-scalable,比方说一些给父母的手机把文字放大的工具,就会有可能。不过一般是没有问题的。
b.像iphone5下还会有黑边。
c.所以写全了,可以避免一些bug。
2、ios10不支持user-scalable=no,后面事件能解决(阻止dosument的touchstart的默认行为)。
–>
-->
<meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=no">
<meta charset="UTF-8">
<script src="resources/jquery.3.3.1.min.js"></script>
<link rel="stylesheet" href="resources/bootstrap/bootstrap.css">
<script src="resources/bootstrap/bootstrap.min.js"></script>
<script src="resources/art-template.js"></script>
<#--星型组件-->
<link rel="stylesheet" href="resources/raty/lib/jquery.raty.css">
<#--<link rel="stylesheet" href="resources/raty/lib/jquery.raty.js">-->
<script src="resources/raty/lib/jquery.raty.js"></script>
<style>
.highlight {
color: red !important;
}
a:active {
text-decoration: none !important;
}
.container {
padding: 0px;
margin: 0px;
}
.row {
padding: 0px;
margin: 0px;
}
/* .col- * {
padding: 0px;
}*/
</style>
</head>
<script>
//初始化图片目录
$.fn.raty.defaults.path="resources/raty/lib/images/";
//加载下一页
function loadNext(isFirst){
if(isFirst == true){
//首先将默认的显示清空,不然后续的分类和排序查询会追加
$('#bookList').html("");
//如果是第一页,页码设置为1,不是,就不会执行
$('#nextPage').val(1);
}
var nextPage = $('#nextPage').val();//如果是第一页,则取出来的是1
var categoryId = $('#categoryId').val();//得到辅助分页当中的类别id
var order = $('#order').val();//得到根据哪个排序的order值
$.ajax({
url: 'books',
type: 'get',
data: {"page":nextPage,"categoryId":categoryId,"order":order},
//另一种方式,序列化,就能拿到表单里面的数据,但是这样写需要注意传的数据要和User类能对上
// data:$('#frm').serialize(),
//如果不需要返回数据,就不用写
dataType: 'json',
success: function (info) {
console.log(info);
var bookList = info.records;
for (var i = 0; i < bookList.length; i++) {
var book = bookList[i];
//连接模板与后台响应的结果book
var html = template("tpl",book);//将结果拼接在模板上之后,追加到div(bookList)中
$('#bookList').append(html);
}
//表示为星型组件
// $('.stars').raty();
//readOnly:true设为只读,把stars变为星型组件,stars可以理解为一个标记,在这里把标记了这个名称的变为星型组件
$('.stars').raty({readOnly:true});
//如果当前页,小于总页数,则将辅助分页的下一页的文本框的值改为:当前页+1
if(info.current < info.pages){
$('#nextPage').val(parseInt(info.current)+1)
//这里在分类和排序时使用,因为如果某个分类只有一页时,会发生else部分的隐藏,点击其他分类时就再也看不到加载更多了
$('#btnMore').show()
$('#divNoMore').hide()
}else {
$('#btnMore').hide()
$('#divNoMore').show()
}
}
})
}
$(function () {
//默认首页刚打开时,为true,设页码为1
loadNext(true);
//然后就是点击加载更多的时候,调用上面的方法,就不需要写重复的代码了,也是优化代码
//单击btnMore这个按钮,加载更多,实际上是分页显示
$('#btnMore').click(function () {
loadNext();
})
//给导航栏的五个分类元素设置高亮:
$('.category').click(function () {
//移除当前高亮
$('.category').removeClass("highlight");
/* //再设置category的样式为灰色,因为类别当中你写了行内样式,所以其实可以不写
$('.category').addClass('text-black-50');*/
//设置当前点击的对应元素为高亮
$(this).addClass("highlight");
//同时给辅助分页的div部分赋值
//首先获取点击的类别的值
var categoryId = $(this).data("category");
//然后设置辅助分页的表单赋值<input type="hidden" id="categoryId" value="-1">
$('#categoryId').val(categoryId)
//点击其他类别之后,马上查询刷新数据
loadNext(true);//因为每次点击类别的时候,其实都是重新加载数据
})
//按热度还是评价高亮
$('.order').click(function () {
$('.order').removeClass("highlight");
$(this).addClass("highlight");
//同时给辅助分页的div部分赋值
//首先获取点击的类别的值
var order = $(this).data("order");
//然后设置辅助分页的表单赋值<input type="hidden" id="order" value="quantity">
$('#order').val(order)
//点击其他排序方式之后,马上查询刷新数据
loadNext(true);//因为每次点击其他排序的时候,其实都是重新加载数据
})
})
</script>
<#--图片对应数据的模板-->
<script type = "text/html" id="tpl">
<!--行,包含div1,div2,各占4,8格-->
<a href="detail" style="color: inherit">
<!-- 一行二列,左图,右内容-->
<div class="row mt-2">
<div class="col-4 mb-2 pr-2">
<img class="img-fluid" src="{{cover}}">
</div>
<div class="col-8 mb-2 pl-0">
<h5 class="text-truncate">{{bookName}}</h5>
<div class="mb-2 bg-light small p-2 w-100 text-truncate">{{author}}</div>
<div class="mb-2 w-100">{{subTitle}}</div>
<p>
<#--这个stars只是一个名字,你可以随便取,上面原型的星型,可以通过插件动态实现-->
<span class="stars" data-score="{{evaluationScore}}" title="gorgeous"></span>
<span class="mt-2 ml-2">{{evaluationScore}}</span>
<span class="mt-2 ml-2">{{evaluationQuantity}}人已评</span>
</p>
</div>
</div>
</a>
<hr>
</script>
<body>
<div class="container">
<!-- 导航部分;-->
<nav class="navbar navbar-light shadow mr-auto">
<!-- 导航左logo –>-->
<ul class="nav">
<li class="nav-item">
<img src="resources/images/logo2.png" class="mt-1" style="width: 100px;" alt="">
</li>
</ul>
<!-- 导航右登录图标 –>-->
<a href="login.html" class="btn btn-light btn-sm">
<img src="images/user_icon.png" style="width:2rem;margin-top:-5px;" class="mr-1">登录
</a>
</nav>
<!-- 第一行 -->
<div class="row mt-2">
<div class="col-6 mt-2">
<h4>热评好书推荐</h4>
</div>
<div class="col-8 mt-2">
<#--data-xxx:bootstrap里面自定义数据,data告诉你这是数据,xxx告诉你这个是什么数据-->
<span data-category="-1" style="cursor:pointer" class="highlight font-weight-bold category mr-2">全部</span> |
<#list categoryList as category>
<a style="cursor:pointer" data-category="${category.categoryId}"
class="text-black-50 font-weight-bold category">${category.categoryName}</a>
<#--如果还有下一个,就输入|这个符号-->
<#if category_has_next>|</#if>
</#list>
</div>
<div class="col-8 mt-2">
<#--data-xxx:bootstrap里面自定义数据,data告诉你这是数据,xxx告诉你这个是什么数据-->
<span data-order="quantity" style="cursor:pointer" class="highlight font-weight-bold text-black-50 mr-3 order" id="hot">按热度</span>
<span data-order="score" style="cursor:pointer" class="font-weight-bold text-black-50 order" id="pj">按评分</span>
</div>
</div>
<!--列表,由多行组成,每行有二个div列,div1占4列,div2占8列,共12格-->
<div id="bookList">
<!--
color: inherit,将强制继承父值
因:在a链接的情况下,该color属性通常在用户代理样式表中设置为蓝色
希望链接具有与文本其余部分相同的文本颜色,则可以使用该inherit值来强制继承和其他文本一样的颜色值
-->
<!--加载更多,即分页的按钮 -->
</div>
<button type="button" id="btnMore" nextPage="1" class="btn btn-outline-primary btn-lg btn-block">
点击加载更多
</button>
<div id="divNoMore" class="text-center text-black-50 mb-5" style="display: none">没有其他数据了</div>
<!--辅助分页-->
<div class="d-none">
<input type="hidden" id="nextPage" value="2">
<input type="hidden" id="categoryId" value="-1">
<input type="hidden" id="order" value="quantity">
</div>
</div>
</body>
</html>
<!--
quantity 数量
.text-black-50 为黑色文本添加 50% 的不透明度
.text-white-50 为白色文本添加 50% 的不透明度
cursor:pointer 鼠标指针样式手
btn-block 显示并且宽100%
d-none 隐藏
shadow 阴影框效果
btn 变为按钮,点一下可以见框范围
btn-light 按钮有背景色 鼠标移入按钮框,背景可变
btn-sm 按钮框设为小屏幕,即变小
.text-truncate {
overflow: hidden; 溢出内容隐藏
text-overflow: ellipsis; 单行文本不折行,显示不了就用省略号表示
white-space: nowrap; 段落中的文本不进行换行
}
img-fluid 图片随父元素的宽度一起变化
img-thumbnail 图片随父元素的宽度一起变化
bg-light 背景浅灰色
w-100 width: 100%
small 字大小和粗细设置
-->