前言
今日内容为旅游网项目的旅游线路名称查询,详情页展示,线路收藏功能。
1. 旅游线路名称查询功能
1.1 查询参数的传递
在header.html
中添加如下js
// 给搜索按钮绑定单击事件,获取搜索输入框的内容
$("#search_btn").click(function () {
// 用户输入的线路名称
var rname = $("#search_input").val();
var cid = getParameter("cid");
// 跳转路径 http://localhost/travel/route_list.html?cid=5, 拼接上rname=xxx
location.href = "http://localhost/travel/route_list.html?cid="+cid+"&rname="+rname;
});
在route_list.html
中添加代码
$(function () {
// 获取cid的参数值
var cid = getParameter("cid");
// 获取rname的参数值
var rname = getParameter("rname");
// 判断rname不为null
if (rname){
// url 解码
rname = window.decodeURIComponent(rname);
}
});
1.2 后台代码修改
主要就是重构查询总记录数方法和查询当前页数据集合方法,添加一个模糊查询的参数rname
设计到重构的模块及方法:
RouteServlet
部分代修改及添加
// 添加以下代码
// 接受rname 参数
String rname = request.getParameter("rname");
rname = new String(rname.getBytes("iso-8859-1"),"utf-8"); // 重新编码rnmae,解决get请求乱码问题
// 修改查询方法,添加rname参数
// 3. 调用service查询PageBean对象
PageBean<Route> pb = routeService.pageQuery(cid, currentPage, pageSize,rname);
RouteServiceImpl
代码修改
// 添加rname参数
public PageBean<Route> pageQuery(int cid, int currentPage, int pageSize, String rname) {}
// 以下方法均添加rname参数
int totalCount = routeDao.findTotalCount(cid,rname);
List<Route> list = routeDao.findByPage(cid, start, pageSize,rname);
RouteDaoImpl
代码修改
@Override
public int findTotalCount(int cid, String rname) {
// String sql = "select count(*) from tab_route where cid = ?";
// 1. 定义sql模板,拼接sql时注意空格
String sql = "select count(*) from tab_route where 1 = 1 ";
StringBuilder sb = new StringBuilder(sql);
List params = new ArrayList(); // 条件参数
// 2. 判断参数是否有值
if (cid != 0){
sb.append(" and cid = ? ");
params.add(cid); // 添加?对应值
}
if (rname != null && rname.length() > 0){
sb.append(" and rname like ? ");
params.add("%"+rname+"%");
}
sql = sb.toString(); // 转换为字符串
return template.queryForObject(sql,Integer.class,params.toArray()); // params.toArray() 集合转换为数组
}
@Override
public List<Route> findByPage(int cid, int start, int pageSize, String rname) {
// String sql = "select * from tab_route where cid = ? limit ? , ?";
// 1. 定义sql模板
String sql = "select * from tab_route where 1 = 1 ";
StringBuilder sb = new StringBuilder(sql);
List params = new ArrayList(); // 条件参数集合
// 2. 判断参数是否有值
if (cid != 0){
sb.append(" and cid = ? ");
params.add(cid); // 添加?对应值
}
if (rname != null && rname.length() > 0){
sb.append(" and rname like ? ");
params.add("%"+rname+"%");
}
sb.append(" limit ? , ? "); // 分页条件限制
sql = sb.toString();
// 加入分页条件参数
params.add(start);
params.add(pageSize);
return template.query(sql,new BeanPropertyRowMapper<Route>(Route.class),params.toArray());
}
1.3 前台代码修改
主要将rname
参数添加入load
方法中,并且修改所有的跳转页面标签,加上rname
参数。
注意在标签处添加
rname
参数时,该参数是一个字符串,故需使用“\”进行转义。
<script>
$(function () {
// var search = location.search;
// // alert(search);
// // 切割字符串,拿到第二个值
// var cid = search.split("=")[1];
// 获取cid的参数值
var cid = getParameter("cid");
// 获取rname的参数值
var rname = getParameter("rname");
// 判断rname不为null
if (rname){
// url 解码
rname = window.decodeURIComponent(rname);
}
// 当页码加载完毕后,调用load方法,
load(cid,null,rname);
})
function load(cid, currentPage,rname) {
// 发送ajax请求,请求route/pageQuery,传递cid
$.get("route/pageQuery",{cid:cid,currentPage:currentPage,rname:rname},function (pb) {
// 解析PageBean数据,展示到页面上
// 1. 分页工具条数据展示
// 1.1 展示总页码和总记录数
$("#totalPage").html(pb.totalPage);
$("#totalCount").html(pb.totalCount);
var lis = "";
var fristPage = '<li onclick="javascript:load('+cid+',1,\''+rname+'\')"><a href="javascript:void(0)">首页</a></li>';
// 计算上一页的页码
var beforeNum = pb.currentPage - 1;
if (beforeNum <= 0){
beforeNum = 1;
}
var beforePage = '<li onclick="javascript:load('+cid+','+beforeNum+',\''+rname+'\')" class="threeword"><a href="javascript:void(0)">上一页</a></li>';
lis += fristPage;
lis += beforePage;
// 1.2 展示分页页码
/*
仿百度分页样式:前五后四
1. 一共展示10个页码,能够达到前5后4的效果
2. 如果前边不足5个,后边补齐10个
3. 如果后边不足4个,前边补齐10个
*/
// 定义开始位置begin和结束位置end
var begin; // 开始位置
var end; // 结束位置
// 1. 显示10个页码
if(pb.totalPage < 10){
// 总页码不足10页
begin = 1;
end = pb.totalPage;
}else{
// 总页码超过10页
// 实现前5后4效果
begin = pb.currentPage - 5;
end = pb.currentPage + 4;
// 进行数据修正
// 2. 如果前边不够5个,后边补齐10个
if (begin < 1){
begin = 1;
end = begin + 9;
}
// 3. 如果后边不足4个,前边补齐10个
if (end > pb.totalPage){
end = pb.totalPage;
begin = end - 9;
}
}
for (var i = begin; i <= end; i++) {
var li;
// 判断当前页码是否等于i
if (pb.currentPage == i){
li = '<li class="curPage" onclick="javascript:load('+cid+','+i+',\''+rname+'\')"><a href="javascript:void(0)">'+i+'</a></li>';
}else{
// 创建页码的li
li = '<li onclick="javascript:load('+cid+','+i+',\''+rname+'\')"><a href="javascript:void(0)">'+i+'</a></li>';
}
// 拼接进字符串
lis += li;
}
// 计算下一页页码
var nextNum = pb.currentPage + 1;
if(nextNum >= pb.totalPage){
nextNum = pb.totalPage;
}
var nextPage = '<li onclick="javascript:load('+cid+','+nextNum+',\''+rname+'\')" class="threeword"><a href="javascript:void(0);">下一页</a></li>';
var lastPage = '<li onclick="javascript:load('+cid+','+pb.totalPage+',\''+rname+'\')" class="threeword"><a href="javascript:;">末页</a></li>';
lis += nextPage;
lis += lastPage;
// 将lis内容设置到ul中
$("#pageNum").html(lis);
// 2. 列表数据展示
var route_lis = "";
for (var i = 0; i < pb.list.length; i++) {
// 获取数据 {rid": 1,"rname": "xxx","price": 999.0,xxxx}
var route = pb.list[i];
var li = '<li>\n' +
' <div class="img"><img src="'+route.rimage+'" style="width: 299px"></div>\n' +
' <div class="text1">\n' +
' <p>'+route.rname+'</p>\n' +
' <br/>\n' +
' <p>'+route.routeIntroduce+'</p>\n' +
' </div>\n' +
' <div class="price">\n' +
' <p class="price_num">\n' +
' <span>¥</span>\n' +
' <span>'+route.price+'</span>\n' +
' <span>起</span>\n' +
' </p>\n' +
' <p><a href="route_detail.html">查看详情</a></p>\n' +
' </div>\n' +
' </li>';
route_lis += li;
}
$("#route").html(route_lis);
// 每次点击翻页后,定位到页面顶部
window.scrollTo(0,0);
});
}
</script>
2. 旅游线路的详情展示
2.1 分析
2.2 后台代码实现
- 在
RouteServlet
中创建一个findOne
方法,查询route
对象并返回给客户端
/**
* @Description: 根据id查询一个旅游线路的详细信息
* @Author: zero
* @param request
* @param response
* @return void
* @Date 2019/7/26 20:56
*/
public void findOne(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 接收id
String rid = request.getParameter("rid");
// 2. 调用service查询route对象
Route route = routeService.findOne(Integer.parseInt(rid));
// 3. 转为json返回客户端
writeValue(route,response);
}
- 在
RouteServiceImpl
中新建findOne
方法,根据rid
查询三表数据,封装进route
对象
/**
* @Description: 根据rid查询三个表数据,封装进route对象
* @Author: zero
* @param rid
* @return 封装好的route对象
* @Date 2019/7/26 21:16
*/
@Override
public Route findOne(int rid) {
// 1. 根据id查询route对象 routeDao
Route route = routeDao.findOne(rid);
// 2. 根据rid线路id查询tab_route_img,将集合设置到route对象中
List<RouteImg> img = routeImgDao.findImg(rid);
// 2.1 将img集合设置到route对象中
route.setRouteImgList(img);
// 3. 根据sid卖家id查询tab_seller查询卖家信息,设置到route对象中
Seller seller = sellerDao.findSeller(route.getSid());
// 3.1 设置到route对象中
route.setSeller(seller);
return route;
}
- 修改
RouteDao
层,添加findOne
方法,新建两个Dao及其对应实现类,分别是RouteImgDao
和SellerDao
,查询图片信息和卖家信息。
// RouteDaoImpl
/**
* 根据id查询
* @param rid
* @return
*/
@Override
public Route findOne(int rid) {
return template.queryForObject("select * from tab_route where rid = ?",
new BeanPropertyRowMapper<Route>(Route.class),rid);
}
// RouteImgDaoImpl
/**
* 根据rid查询图片信息
* @param rid
* @return
*/
@Override
public List<RouteImg> findImg(int rid) {
return template.query("select * from tab_route_img where rid = ?",
new BeanPropertyRowMapper<RouteImg>(RouteImg.class),rid);
}
// SellerDaoImpl
/**
* 根据sid查询卖家信息
* @param sid
* @return
*/
@Override
public Seller findSeller(int sid) {
return template.queryForObject("select * from tab_seller where sid = ?",
new BeanPropertyRowMapper<Seller>(Seller.class),sid);
}
2.3 前台代码实现
在Route_detail.html
中加载后获取rid,然后发送ajax请求,获取route对象,解析对象数据,填充入html中
<script>
$(document).ready(function() {
goImg();
});
function goImg() {
//焦点图效果
//点击图片切换图片
$('.little_img').on('mousemove', function() {
$('.little_img').removeClass('cur_img');
var big_pic = $(this).data('bigpic');
$('.big_img').attr('src', big_pic);
$(this).addClass('cur_img');
});
//上下切换
var picindex = 0;
var nextindex = 4;
$('.down_img').on('click',function(){
var num = $('.little_img').length;
if((nextindex + 1) <= num){
$('.little_img:eq('+picindex+')').hide();
$('.little_img:eq('+nextindex+')').show();
picindex = picindex + 1;
nextindex = nextindex + 1;
}
});
$('.up_img').on('click',function(){
var num = $('.little_img').length;
if(picindex > 0){
$('.little_img:eq('+(nextindex-1)+')').hide();
$('.little_img:eq('+(picindex-1)+')').show();
picindex = picindex - 1;
nextindex = nextindex - 1;
}
});
//自动播放
// var timer = setInterval("auto_play()", 5000);
}
//自动轮播方法
function auto_play() {
var cur_index = $('.prosum_left dd').find('a.cur_img').index();
cur_index = cur_index - 1;
var num = $('.little_img').length;
var max_index = 3;
if ((num - 1) < 3) {
max_index = num - 1;
}
if (cur_index < max_index) {
var next_index = cur_index + 1;
var big_pic = $('.little_img:eq(' + next_index + ')').data('bigpic');
$('.little_img').removeClass('cur_img');
$('.little_img:eq(' + next_index + ')').addClass('cur_img');
$('.big_img').attr('src', big_pic);
} else {
var big_pic = $('.little_img:eq(0)').data('bigpic');
$('.little_img').removeClass('cur_img');
$('.little_img:eq(0)').addClass('cur_img');
$('.big_img').attr('src', big_pic);
}
}
$(function () {
// 1. 获取id
var rid = getParameter("rid");
// 2. 发送请求, route/findOne
$.get("route/findOne",{rid:rid},function (route) {
// 3. 解析数据填充html
$("#rname").html(route.rname);
$("#routeIntroduce").html(route.routeIntroduce);
$("#price").html(route.price);
$("#sname").html(route.seller.sname);
$("#consphone").html(route.seller.consphone);
$("#address").html(route.seller.address);
// 图片展示
var dd_str = '<a class="up_img up_img_disable"></a>';
// 遍历routeImgList
for (var i = 0; i < route.routeImgList.length; i++) {
var a_str;
// 判断图片是否大于4,将大于4的图片设置为display:none
if (i >= 4) {
a_str = '<a title="" class="little_img" data-bigpic="'+route.routeImgList[i].bigPic+'" style="display:none;">\n' +
' <img src="'+route.routeImgList[i].smallPic+'">\n' +
' </a>';
} else {
a_str = '<a title="" class="little_img" data-bigpic="'+route.routeImgList[i].bigPic+'">\n' +
' <img src="'+route.routeImgList[i].smallPic+'">\n' +
' </a>';
}
dd_str += a_str;
}
dd_str += '<a class="down_img down_img_disable" style="margin-bottom: 0;"></a>';
$("#dd").html(dd_str);
// 图片展示和切换代码的调用
goImg();
});
});
</script>
需注意:在
route_list.html
中的查看详情URL处修改代码,传递rid值:<p><a href="route_detail.html?rid='+route.rid+'">查看详情</a></p>\n'
3. 旅游线路收藏功能
3.1 分析
- 表结构信息:
3.1.1 判断当前登录用户是否收藏过该线路
当页面加载完成后,发送ajax请求,获取用户是否收藏的标记,根据此标记展示不同的按钮样式。
3.2 后台代码编写
- 修改
RouteServlet
,添加isFavorite
方法
/**
* @Description: 根据线路id和用户id,判断用户是否收藏了该线路
* @Author: zero
* @param request
* @param response
* @return void
* @Date 2019/7/27 23:11
*/
public void isFavorite(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取rid,线路id
String rid = request.getParameter("rid");
// 2. 获取udi,通过当前登录用户对象,session
User user = (User) request.getSession().getAttribute("user");
// 2.1 如果user对象为null,则表示未登录,设置uid=0
int uid; // 用户id
if (user == null){
// 用户未登录
uid = 0;
}else{
// 用户已登录
uid = user.getUid();
}
// 3. 调用FavoriteService查询,传递rid,uid
boolean flag = favoriteService.isFavorite(Integer.parseInt(rid), uid);
// 4. 写回客户端flag标记
writeValue(flag,response);
}
FavoriteServiceImpl
添加isFavorite
方法
@Override
public boolean isFavorite(int rid, int uid) {
Favorite favorite = favoriteDao.findFavorite(rid, uid);
return favorite != null ? true : false; // 如果对象有值,则表示收藏过了,返回true,反之表示未收藏过返回false。
}
FavoriteDaoImpl
添加findFavorite
方法
/**
* @Description: 根据线路id和用户id查询数据
* @Author: zero
* @param rid
* @param uid
* @return 查询到的Favorite对象
* @Date 2019/7/27 22:34
*/
@Override
public Favorite findFavorite(int rid, int uid) {
Favorite favorite = null;
try{
favorite = template.queryForObject("select * from tab_favorite where rid = ? and uid = ? ",
new BeanPropertyRowMapper<Favorite>(Favorite.class), rid, uid);
}catch (DataAccessException e){
e.printStackTrace();
}
return favorite;
}
3.3 前台代码编写
在route.detail.html
中添加isFavorite
的js方法,在页面加载完毕后请求。
function isFavorite() {
// 发送请求,判断用户是否收藏过该线路
var rid = getParameter("rid");
$.get("route/isFavorite",{rid:rid},function (flag) {
if (flag){
// 用户已经收藏过了
// <a class="btn already" disabled="disabled">
// 设置收藏按钮的样式
$("#favorite").addClass("already");
$("#favorite").attr("disabled","disabled");
// 删除按钮的点击事件
$("#favorite").removeAttr("onclick");
} else {
// 用户没有收藏过
}
});
};
3.4 收藏次数的动态展示
前台代码:
// 设置收藏次数
$("#favoriteNumber").html("已收藏"+route.count+"次");
后台添加一个查询次数方法即可
// RouteService, 在加载页面时调用findOne方法,同时将收藏次数也封装进Route对象中
// 4. 查询收藏次数
int count = favoriteDao.findFavoriteCount(route.getRid());
route.setCount(count);
// FavoriteDao
/**
* @Description: 根据rid查询收藏次数
* @Author: zero
* @param rid
* @return 收藏次数
* @Date 2019/7/27 23:20
*/
@Override
public int findFavoriteCount(int rid) {
return template.queryForObject("select count(*) from tab_favorite where rid = ?", Integer.class,rid);
}
3.5 点击按钮收藏线路
3.5.1 分析
3.5.2 代码实现
- 前台代码
// 点击收藏按钮触发的方法
function addFavorite() {
var rid = getParameter("rid");
// 1. 判断用户是否登录
$.get("user/findOne",{},function (user) {
if (user){
// 用户登录了
// 调用添加功能
$.get("route/addFavorite",{rid:rid},function () {});
// 刷新页面
location.reload();
} else{
// 用户未登录
alert("您未登录,请登录");
location.href = "http://localhost/travel/login.html";
}
});
};
- 后台代码,在
RouteServlet
中添加addFavorite
方法
/**
* @Description: 添加收藏功能
* @Author: zero
* @param request
* @param response
* @return void
* @Date 2019/7/27 23:46
*/
public void addFavorite(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取线路id rid
String rid = request.getParameter("rid");
// 2. 获取用户对象 uid
User user = (User) request.getSession().getAttribute("user");
int uid; // 用户id
if (user == null){
// 用户未登录
return;
}else{
// 用户已登录
uid = user.getUid();
}
// 3. 调用service添加
favoriteService.add(Integer.parseInt(rid), uid);
}
在
FavoriteServiceImpl
中添加add
方法@Override public void add(int rid, int uid) { favoriteDao.add(rid,uid); }
在
FavoriteDaoImpl
中添加add
方法/** * @Description: 添加收藏功能 * @Author: zero * @param rid * @param uid * @return void * @Date 2019/7/27 23:54 */ @Override public void add(int rid, int uid) { template.update("insert into tab_favorite values(?,?,?)",rid,new Date(),uid); }