Web前端(十六)-首页瀑布流展示、作品详情页、过滤器Filter、监听器Listener、读取配置文件、删除轮播图和作品对应磁盘文件、删除分类及对应作品、Vue自定义组件、网站图标问题

首页瀑布流展示

  1. 创建grid.js并在index.html页面中引入

  2. 在grid.js中创建vue对象 管理index.html页面中的.grid元素, 并在created里面发请求获取所有作品数据,把得到的数据给到vue对象中的p_arr数组

  3. 在index.html页面中 删除.grid元素里面的假数据, 通过v-for遍历数组 把作品的图片显示在img标签中

  4. 把index.js中 初始化瀑布流的代码注释掉, 在grid.js中的updated方法中添加瀑布流初始化代码

蓝条及动画

  1. 在index.html页面中 瀑布流里面图片的下面添加蓝条div

  2. 通过css样式代码控制蓝条的各种样式

  3. 在grid.js中 给瀑布流中元素添加鼠标移入移出事件 ,控制蓝条淡入淡出

  4. index.html页面中 通过css样式代码给图片img标签添加鼠标移入移出缩放动画

浏览最多和最受欢迎步骤:

  1. 在product.js里面的data里声明两个数组分别为 view_arr和 like_arr

  2. 在product.js里面的created方法里面向/viewlist发出异步请求 同时也要向/likelist发出异步请求

  3. 在ProductController里面添加 viewList方法和likeList方法 分别处理 /viewlist和/likelist 两个路径 ,在方法里面分别调用 mapper里面的viewList方法和likeList方法 两个方法的范围值同为List集合 把集合直接返回给客户端

  4. 实现mapper中调用的两个方法, 查询数据时 分别按照浏览量降序排序和点赞量降序排序 取前四条数据

  5. 在home.html页面中通过Vue中的v-for指令 将查询到的数据显示到页面

作品详情介绍:创建时间、浏览量、点赞量、显示图片

 <!--详情开始-->
 <!--显示作品标题-->
 <h2 class="page-header">{{product.title}}</h2>
 <!--显示作品创建时间、浏览量、点赞量-->
 <div>
     <!--创建时间,默认靠左-->
     <span>{{product.author}}发布于{{product.created}}</span>
     <!--浏览量、点赞量:pull-right:让元素显示到右侧-->
     <span class="pull-right">
         <span class="fa fa-eye">{{product.viewCount}}</span>
         <span class="fa fa-thumbs-o-up">{{product.likeCount}}</span>
  </span>
  </div>
     <!--给作品点赞按钮,添加点赞事件
         bootstrap中提供的居中写法text-center,使用css样式也可以-->
     <div class="text-center">
         <button class="btn btn-info" @click="like()">给作品点赞</button>
     </div>
     <!--显示作品图片,图片居中,设置外边距离按钮一段距离,超出范围不显示-->
     <div class="text-center" style="margin: 30px 0;overflow: hidden">
    <!--center-block让块级元素自身居中,text-center是让元素内容居中
             img-responsive此样式导致图片显示方式发生改变,所以text-center失效-->
         <img :src="product.url" class="img-responsive center-block" alt="">
     </div>
     <!--作品详情介绍-->
     <p class="text-center">{{product.intro}}</p>
     <!--详情结束-->
 </div>        

通过moment.js实现日期格式化

在detail.html页面中引入moment.js 文件

下载地址:Moment.js 中文网 http://momentjs.cn/

 <!--引入moment框架,专门处理日期-->
 <script src="http://cdn.staticfile.org/moment.js/2.24.0/moment.min.js"></script>
 
 created: function () {
  //从地址栏中得到作品id ?id=1
  let id = location.search.split("=")[1];
  //发请求,请求路径为/product/selectbyid?id=xxx
  axios.get("/product/selectbyid?id=" + id).then(function (response) {
    //把查询得到的数据给到vue中product对象 页面自动改变
    detail_vm.product = response.data;
    //修改时间格式 detail_vm.product.created表示product的原始创建时间 按照指定格式修改,英文名称不可变,位置可变
    detail_vm.product.created = moment(detail_vm.product.created).format("YYYY年MM月DD日 HH:mm:ss");
  }).catch(function (err) {
  alert(err);
  })
 },
 

timestamp字段类型相关问题

  • 此类型的字段表示时间, 默认状态是当数据发生改变时此字段的数据会自动变成当前时间, 默认自动更改

    例如:当进入作品详情页面时,时间自动变成当前时间

  • 可以通过以下SQL语句禁止自动更改 ,在数据库中执行下列语句:

     alter table product change created created timestamp not null default current_timestamp;
  • 打开自动更改

     alter table product change created created timestamp not null default current_timestamp on update current_timestamp;

详情页删除作品

  1. 在detail.js文件中的created方法里面发请求询问服务器是否登录过, 把得到的布尔值赋值给vue里面的isLogin变量

  2. 在detail.html页面中给作品点赞按钮的下面添加删除按钮 并且通过v-if判断只有当登录的情况下才能显示

  3. 给删除按钮添加点击事件 点击时向/deleteproduct?id=xxx地址发出请求,删除完之后显示到首页 

给作品点赞

  1. 给点赞按钮添加点击事件

  2. 在事件方法中向/like?id=xxx发出请求, Controller中处理此请求, 调用mapper中的likeById方法

  3. 实现mapper中的likeById方法, 修改数据库中likeCount数量+1

  4. 为了解决重复点赞的问题, 通过Session保存点过赞的id信息, 点赞之前先判断点过赞 

浏览量

  1. 在ProductController的detail方法中,当通过id查询作品信息时,认为用户浏览过作品一次, 所以把代码写在此方法中

  2. 通过禁止重复点赞的模式, 禁止重复添加浏览量

浏览量和点赞量

 methods:{
  like:function() {
  //发出点赞请求
  axios.get("/like?id="+detail_vm.product.id).then(function (response) {
            //根据获取返回的数据判断是否点过赞
            if (response.data==1){
                alert("点赞成功!");
                //location.reload();//这种方法也能实现页面点赞次数+1,但是会多一次请求
                //让vue中变量的属性发生改变,页面自动改变
                detail_vm.product.likeCount++;
            }else {
                alert("不能重复点赞!");
            }
  });
  }
 }
 
 ProductController添加请求方法
    /**
      * 查询浏览量最多的作品
      * @return
      */
    @RequestMapping("/product/select/view")
    public List<Product> selectView(){
        return mapper.selectView();
    }
 
    /**
      * 查询点赞量最多的作品
      * @return
      */
    @RequestMapping("/product/select/like")
    public List<Product> selectLike(){
        return mapper.selectLike();
    }
 
 ProductMapper添加映射关系
  /**
      * 根据id给对应作品点赞
      * @param id
      */
    @Update("update product set like_count=like_count+1 where id=#{id}")
    void like(int id);
 
    /**
      * 根据id给对应作品浏览量+1
      * @param id
      */
    @Update("update product set view_count=view_count+1 where id=#{id}")
    void view(int id);        

过滤器Filter

1.什么是过滤器?

  • 过滤器和Servlet一样是Web容器(Tomcat)中的组件,可以在客户端请求资源之前或之后触发的代码,可以将请求多个资源时做的重复工作放在过滤器中执行,这样只需要写一遍, 从而提高开发效率。

    例如:访问后台管理页面时必须处于登录状态,需要提前判断是否处于登录状态,单独一个个设置比较麻烦,可以使用过滤器完成。即:将要请求的页面放在过滤器前,根据具体情况允许其是否放行。

2.如何使用过滤器?

1. 创建Filter类

2.在XXXApplication.java中添加注解:@ServletComponentScan

 package cn.tedu.vrd;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.web.servlet.ServletComponentScan;
 
 //Web容器组件扫描,作用:就是为了找到刚刚创建的MyFilter过滤器并加载
 @ServletComponentScan
 @SpringBootApplication
 public class VrdApplication {
     public static void main(String[] args) {
         SpringApplication.run(VrdApplication.class, args);
    }
 }

3.过滤器文件相关代码

 package cn.tedu.tvrd.filter;
 
 import cn.tedu.tvrd.entity.User;
 
 import javax.servlet.*;
 import javax.servlet.annotation.WebFilter;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import java.io.IOException;
 
 /*告诉编译器这个类是一个过滤器,urlPatterns里面写匹配路径:
 urlParttens几种匹配路径的方式:
 - 精确匹配: /send.html、/admin.html  
 - 后缀匹配: *.jpg、*.png、*.html    
 - 路径匹配: /banner/*、/product/*  
 - 全部匹配: /* 客户端发出的任何请求都会被过滤器处理  
 */
 @WebFilter(filterName = "MyFilter",urlPatterns = {"/send.html","/admin.html",})
 public class MyFilter implements Filter {
     //当过滤器销毁时执行
     public void destroy() {
    }
     
     //当客户端请求资源时执行
     public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
         //判断当前客户端是否登录过,登录过放行,否则拦截
         //将父类型转成子类型, 因为用到的方法是子类型中的方法,强制类型转换
         HttpServletRequest request = (HttpServletRequest) req;
         HttpServletResponse response = (HttpServletResponse) resp;
         //通过request得到Session对象
         HttpSession session = request.getSession();
         //从Session中判断是否有user对象
         User user = (User)session.getAttribute("user");
         if (user!=null){//登录过
             chain.doFilter(req, resp);//放行
        }else{//没登录过
             response.sendRedirect("/login.html");//重定向到登录页面
        }
    }
     //当过滤器初始化时执行
     public void init(FilterConfig config) throws ServletException {
 
    }
 }

监听器Listener

  • 监听器也是Web容器中的组件

  • 作用:类似Web前端开发过程中的事件,监听器监听的是工程运行过程中的一些时间点,包括:工程启动,工程停止,Session创建和销毁等。

  • 如何使用监听器?

    1. 创建Listener

    2. 在Application.java中添加注解:@ServletComponentScan

       package cn.tedu.vrd;
       
       import org.springframework.boot.SpringApplication;
       import org.springframework.boot.autoconfigure.SpringBootApplication;
       import org.springframework.boot.web.servlet.ServletComponentScan;
       
       //Web容器组件扫描,作用:就是为了找到刚刚创建的MyFilter过滤器并加载、监听器并加载
       @ServletComponentScan
       @SpringBootApplication
       public class VrdApplication {
           public static void main(String[] args) {
               SpringApplication.run(VrdApplication.class, args);
          }
       }
    1. 相关代码:

     package cn.tedu.vrd.listener;
     
     import javax.servlet.ServletContextEvent;
     import javax.servlet.ServletContextListener;
     import javax.servlet.annotation.WebListener;
     import javax.servlet.http.HttpSessionAttributeListener;
     import javax.servlet.http.HttpSessionEvent;
     import javax.servlet.http.HttpSessionListener;
     import javax.servlet.http.HttpSessionBindingEvent;
     
     @WebListener()
     public class MyListener implements ServletContextListener,
             HttpSessionListener, HttpSessionAttributeListener {
     
         // Public constructor is required by servlet spec
         public MyListener() {
        }
     
         // -------------------------------------------------------
         // ServletContextListener implementation
         // -------------------------------------------------------
         public void contextInitialized(ServletContextEvent sce) {
           /* This method is called when the servlet context is
              initialized(when the Web application is deployed).
              You can initialize servlet context related data here.
           */
             System.out.println("工程开始部署了!");
        }
     
         //使用SpringBoot内置Tomcat的方式,此方法测试无效
         public void contextDestroyed(ServletContextEvent sce) {
           /* This method is invoked when the Servlet Context
              (the Web application) is undeployed or
              Application Server shuts down.
           */
             System.out.println("工程停止部署时执行!");
        }
     
         //可以使用session创建和销毁统计网站在线人数(估计大约人数,参考用):新建一个全局变量,进人+1,走人-1
         // -------------------------------------------------------
         // HttpSessionListener implementation
         // -------------------------------------------------------
         public void sessionCreated(HttpSessionEvent se) {
             /* Session is created. */
             System.out.println("创建Session对象了");
        }
     
         public void sessionDestroyed(HttpSessionEvent se) {
             /* Session is destroyed. */
             System.out.println("Session销毁了");
        }
     
         // -------------------------------------------------------
         // HttpSessionAttributeListener implementation
         // -------------------------------------------------------
     
         public void attributeAdded(HttpSessionBindingEvent sbe) {
           /* This method is called when an attribute
              is added to a session.
           */
        }
     
         public void attributeRemoved(HttpSessionBindingEvent sbe) {
           /* This method is called when an attribute
              is removed from a session.
           */
        }
     
         public void attributeReplaced(HttpSessionBindingEvent sbe) {
           /* This method is invoked when an attribute
              is replaced in a session.
           */
        }
     }
     

读取application.properties配置文件数据

  • application.properties相关代码

     #测试:配置自己项目中用到的数据
     name=tom
     job=teacher
     
     #配置保存上传文件的路径
     dirPath=D:/upload
  • 如何读取?

     //此注解会自动找到application.properties配置文件中的name、job对应的值
     //赋值给此变量,内容不能是中文
     @Value("${name}")
     private String name;
     
     @Value("${job}")
     private String job;
     
     @Test
     void test07(){
      System.out.println(name+","+job);
     }

    对应项目中的优化:

     //读取配置文件里面的数据
     @Value("${dirPath}")
     private String dirPath;

删除轮播图和作品时删除磁盘对应文件

  • new File("图片路径").delete()

 /**
 * 删除轮播图图片
 * @param id
 * 注意:可以先删除磁盘中对应文件,不能先删除数据库中对应的数据,否则查询不到
 */
 @RequestMapping("/banner/delete")
 public void delete(int id){
     //通过轮播图id得到轮播图的文件名
     String fileName = mapper.selectFileName(id);
     //拼接出图片完整路径
     String filePath = dirPath + "/" + fileName;
     //删除磁盘中对应id的文件
     new File(filePath).delete();
 
     //删除对应id对应的轮播图
     mapper.delete(id);
 }
 
 BannerMapper中代码:
 /**
 * 查询轮播图id对应的图片路径
 * @param id
 * @return
 */
 @Select("select url from banner where id=#{id}")
 String selectFileName(int id);
 
 
 /**
 * 删除作品
 * @param id
 */
 @RequestMapping("/product/delete")
 public void delete(int id){
     //注意:先查询,再从数据库删除数据,否则查询不到数据
     //通过id查询作品信息:product包含url信息
     Product product = mapper.selectById(id);
     //获取作品路径
     String filePath = dirPath + product.getUrl();
     //删除文件
     new File(filePath).delete();
 
     //从数据库中删除id对应的作品
     mapper.delete(id);
 }
 

删除当前分类时同时删除该类别所对应作品

 CategoryController:
 /**
 * 删除分类
 * @param id
 */
 @RequestMapping("/category/delete")
 public void delete(int id){
     mapper.delete(id);
     //删除和当前分类相关的作品,此时的id为分类id
     productMapper.deleteByCid(id);
 }
 
 ProductMapper:
 /**
 * 根据分类cid删除对应的作品
 * @param id
 */
 @Delete("delete from product where category_id=#{id}")
 void deleteByCid(int id);
   

Vue自定义组件

  • 当项目中多个页面需要显示相同的内容时,可以将这部分标签内容封装成一个自定义组件,从而提高开发效率

  • 如何使用?

  1. 新建一个v.js文件,声明自定义组件

     //声明一个自定义组件,名为myview
     Vue.component('myview',{
         props:["arr","fn","isShow"],/*声明下面的变量名、方法名*/
         template:`
         <div id="mydiv">
             <!--在自定义的组件中,是不能直接访问Vue对象中的变量的,
                 只能访问props中声明的变量,同理,方法也不能访问-->
             <ul>
                 <!--遍历数组,添加点击事件-->
                 <li v-for="name in arr" @click="fn(name)">{{name}}</li>
             </ul>
             <h1 v-if="isShow">看不见我!</h1>
         </div>
         `
     })
     
     /*创建vue对象*/
     let vm = new Vue({
         el:"myview",/*与上面的组件名一致*/
         data:{
             arr:["张三","李四","王五"], /*要显示的内容,对应props里面的属性*/
             isShow:false
        },
         methods:{
             /*点击事件*/
             fn:function (name) {
                 alert(name);//在name上点击显示name
            }
        }
     })
  2. 新建v1.html,调用v.js文件

     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>Title</title>
     </head>
     <body>
     <!--:arr访问的是props里面声明的变量,arr访问的是vue中的变量,
          属性中不允许有大写字母,驼峰命名会变成多个单词用-连接-->
     <!--组件名+属性对应关系-->
     <myview :arr="arr" :fn="fn" :is-show="isShow"></myview>
     <!--引入vue-->
     <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
     <!--引入外部js-->
     <script src="v.js"></script>
     </body>
     </html>

注意事项:

  • 自定义组件中不能直接访问vue对象中的属性或者方法, 需要在props里面进行传值

  • 变量和方法的传值方式是一样的

  • 如果props里面定义的变量是驼峰命名传值时需要改成:xxx-xxx

    • myNames -> :my-names

网站图标问题

  1. 准备一个文件名为favicon.ico 的图片

    附:favicon制作 - 在线工具 https://tool.lu/favicon/

  2. 把图片放到工程的根路径 也就是static文件夹下面

  3. ReBuild工程并重新启动, shift+f5刷新页面

 

posted @ 2021-08-16 20:53  Coder_Cui  阅读(110)  评论(0编辑  收藏  举报