旅游网项目03

前言

今日内容为旅游网项目的旅游线路名称查询,详情页展示,线路收藏功能。

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

设计到重构的模块及方法:

  1. 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);
  1. 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);
  1. 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>&yen;</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 分析

route_info

route_info2

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及其对应实现类,分别是RouteImgDaoSellerDao,查询图片信息和卖家信息。
// 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 分析

  • 表结构信息:

table

3.1.1 判断当前登录用户是否收藏过该线路

当页面加载完成后,发送ajax请求,获取用户是否收藏的标记,根据此标记展示不同的按钮样式。

favorite

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 分析

clickFavorite

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);
    }
    

  转载请注明: Zero的博客 旅游网项目03

 上一篇
travel项目开发日志 travel项目开发日志
1. 项目简介黑马旅游网是一个前后端分离的Web项目,后端采用了MVC设计模式。前端通过Ajax来请求后端服务器,获取json数据,然后填充到前端页面,部分不经常变动数据(如导航栏数据),使用到了redis来做数据缓存以减少对数据库的访问。
2019-07-29
下一篇 
旅游网项目02 旅游网项目02
1. 前言旅游网项目,今日内容主要为优化Servlet、分类数据查询和分页查询。 2. 优化Servlet2.1 目的为了减少Servlet的数量,现在是一个功能一个Servlet,将其优化为相同功能的Servlet为一个模块。相当于在数
2019-07-23
  目录