前言
今日内容为旅游网项目的旅游线路名称查询,详情页展示,线路收藏功能。
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); }