第18天ajax技术和javascript加强(json)

第18天    ajax技术和javascript加强(json)  复习:  B/S架构实现文件上传的思路?      使用a标签实现文件下载功能,有什么问题?      使用Servlet实现文件下载的思路?    今日任务
  • Ajax技术
  • javaScript加强(json)
  • 课堂笔记 1、ajax介绍 1.1、什么是ajax         全称:Asynchronous JavaScript And XML(异步 JavaScript 及 XML)     Ajax的作用:实现异步请求的技术。      什么是同步请求?     场景:页面上有一个a标签,用户点击a标签,浏览器发出一个请求,然后服务器给出一个响应。     (请求,其实是用户的操作,触发的)      什么是异步(不同步)请求?     场景:在用户注册的时候,用户首先输入用户名,接下来用户继续填写其他注册信息,与此同时,浏览器自动发送了一个请求,将用户输入的用户名发送给服务器,去校验是否可用     (请求,是浏览器自己发送的,与用户没有关系)          为什么需要异步请求,或者说那些功能必须使用异步请求技术来实现?          在不刷新页面(使用a标签发送请求和使用form表单发送请求,这两种请求都会,刷新页面)的情况下,发送请求,接收响应,然后修改部分的页面,这样的需求需要异步请求实现。      总结:在不使用a标签和form表单发送请求的情况下,使用异步请求。      在这个需求中,发送请求应该谁来做?         浏览器。  让浏览器来帮助发送这个请求,那么程序员如何与浏览器沟通,让它帮助我们发送请求?         Javascript技术。    企业为什么特别喜欢使用ajax?  钱。企业的网络通信费用,按流量计费,那么使用ajax它的数据量小,所以省钱。 ajax它的数据量小——因为他不重新加载整个页面(加载部分) Ajax因为数据量小,响应速度快,用户体验好。  1.2、ajax运行机制     在页面不刷新的情况下,向服务器发送请求,达到页面和后台的异步交互。     现在主流(IE、谷歌、火狐,其他的国产浏览器一般都是使用谷歌浏览器内核)的浏览器都有ajax引擎实现——现在ajax技术,都被主流浏览器实现,我们自己不用去写Ajax引擎,这个引擎已经存在在浏览器中。 相当于大家已经有了法拉利,不用自己再造一个,只需学会使用就可以。          1.3、ajax快速入门案例 1)百度(官网) 2)下载jar和API文档 3)测试 4)笔记    API文档:  学习一门新的语言,学习步骤:
  • 学习安装和卸载
  • 关键字
  • 变量
  • 方法(函数)
  • 类(对象)
  • 学习已经给你创建好的对象
  • 学习当前语言的框架
  •  先学安卓,javaEE 掌握之后,只需要15天,学习安卓,精通,找一家公司,工作4个月 C语言:数控机床(嵌入式开发),AI,人工智能,其他编程语言底层    1.3.1、获取XMLHttpRequest对象(ajax核心对象,引擎对象定义:    代码演示: //获取ajax核心对象         function getXHR(){                      var xmlhttp;             if (window.XMLHttpRequest){             // code for IE7+, Firefox, Chrome, Opera, Safari                 xmlhttp=new XMLHttpRequest();             }else{                 // code for IE6, IE5                 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");             }             return xmlhttp;         }               alert(getXHR()); 1.3.2、向服务器发送请求使用open方法和send方法 方法截图:  代码测试: //演示:ajax请求         function test1(){             //先获取核心对象             var xhr = getXHR();             //第一个参数:请求方式             //第二个数:url,请求路径             //第三个参数:是否异步             xhr.open("get","${root}/ajax?username=张三",true);             xhr.send();         }         test1(); 效果:  Servlet代码:  package cn.itcast.web;  import java.io.IOException;  import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class AjaxTestServlet extends HttpServlet {      public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {         System.out.println("AjaxTestServlet.....");         String parameter = request.getParameter("username");         String username = new String(parameter.getBytes("iso-8859-1"),"utf-8");         System.out.println(username);     }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {         doGet(request, response);     }  }    效果:    1.3.2、接收服务器响应  API截图:  ajax代码:  //测试ajax接受数据         function test2(){             //第一步:获取核心对象             var xhr = getXHR();             //第二步:使用核心对象的open方法和send方法发送请求             //method:请求的类型;GET POST             //url:文件在服务器上的位置 (请求路径)             //asynctrue(异步)或 false(同步)             //使用open方法做请求得准备             xhr.open("get","${root}/ajaxTest",true);             xhr.send();                          //第三步:接受响应             //客户端(上海),发送一个请求,到北京的服务器,有一个网络的延迟             //服务器(北京),发出一个响应,到上海的客户端,有一个网络的延迟             //因为,没有设置等待响应,所以直接获取数据,是无法获取到的             var data = xhr.responseText;             alert(data);         }         test2();  servlet代码: package cn.itcast.servlet;  import java.io.IOException;  import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class AjaxTestServlet extends HttpServlet {      public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {          System.out.println("AjaxTestServlet.....执行......");                  //设置一个响应,返回一句话         response.setContentType("text/html;charset=utf-8");         response.getWriter().write("测试ajax响应成功!");              }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {         doGet(request, response);     }  }    1.3.3、设置onreadystatechange事件执行函数(等待服务器响应) API截图:    ajax代码演示:  //测试ajax接受数据(等待服务器响应)         function test3(){             //第一步:获取核心对象             var xhr = getXHR();             //第二步:使用核心对象的open方法和send方法发送请求             //method:请求的类型;GET POST             //url:文件在服务器上的位置 (请求路径)             //asynctrue(异步)或 false(同步)             //使用open方法做请求得准备             xhr.open("get","${root}/ajaxTest",true);             xhr.send();                          //第三步:设置等待响应             xhr.onreadystatechange = function(){                 //判断,只有readyState==4 && status == 200                 //才获取响应的数据                 if(xhr.readyState == 4 && xhr.status == 200){                     //第四步:接受响应                     var data = xhr.responseText;                     alert(data);                 }             };         }                  test3();         //小结:         //使用ajax         //第一步:获取核心对像         //第二步:发送请求,open方法和send方法         //第三步:设置等待响应         //第四步:接受响应的数据  Servlet代码演示:  package cn.itcast.servlet;  import java.io.IOException;  import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class AjaxTestServlet extends HttpServlet {      public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {          System.out.println("AjaxTestServlet.....执行......");                  //设置一个响应,返回一句话         response.setContentType("text/html;charset=utf-8");         response.getWriter().write("测试ajax响应成功!");              }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {         doGet(request, response);     }  }      ajax小结: 1)获取核心对象 //2)发送请求,使用opensend方法 //3)设置等待服务器响应,给onreadystatechange属性设置函数,并且,在函数中做判断, // 保证readyState== 4 && status == 200,才是请求已经完成,响应已经就绪 //4)获取响应的数据,根据需求,做Dom操作  注意:以后再工作中,一般不使用原生(今天学习的)的ajax代码,一般使用的是js框架(Jquery、ext js 、node js)发送ajax请求    1.4、XMLHttpRequest API 详解 1.4.1、onreadystatechange属性 是什么: 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数  代码截图:    执行机制图解:        1.4.2、open方法 是什么: 做发送请求之前准备工作的方法  代码截图:  使用post方式还是get方式?  官方建议:  老师推荐:POST  POST,没有数据长度限制(注意:很多时候,提供功能给用户使用,用户输入的数据长度,有时是没法控制的) POST,解决乱码比较简单 POST,方式更加安全    演示发送post请求和中文请求参数:  //测试ajax POST数据         function test4(){             //第一步:获取核心对象             var xhr = getXHR();             //第二步:使用核心对象的open方法和send方法发送请求             //使用open方法做请求得准备             xhr.open("post","${root}/ajaxTest",true);             //设置ajax向表单一样post数据             xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");             xhr.send("username=张三");                          //第三步:设置等待响应             xhr.onreadystatechange = function(){                 //判断,只有readyState==4 && status == 200                 //才获取响应的数据                 if(xhr.readyState == 4 && xhr.status == 200){                     //第四步:接受响应                     var data = xhr.responseText;                     alert(data);                 }             };         }     test4();        servlet修改:    效果:  1.4.3、setRequestHeader方法 是什么:    代码截图  注意:这个方法相当于,设置了表单的enctype属性的默认值,来模拟表单发送数据  1.4.4、send方法 是什么: 发送请求的方法  代码截图  注意:Post方式提交请求,请求参数写在send方法中。    1.4.5、readyState属性 是什么: 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化 代码截图    1.4.6、status属性 是什么: 存有响应状态码的属性  代码截图  复习下响应状态码: 404:请求找不到 500:服务器异常 302:重定向 200:ok 401:权限不足,做应用,对用户的界面(使用淘宝的时候买家看到的页面),对管理员界面(只有拥有管理员权限的用户,才能看到——卖家的页面),如果用户访问了管理员界面,那么就需要返回响应码为:401,表示当前用户权限不足。  管理员的页面:卖家(上传商品),客服(用户信息,用户的记录,电商,用户订单),运维(当前服务器的运行状态),老板(钱,当前系统的资金管理)    Shiro安全框架,权限。 1.4.7、responseText属性 是什么: 获取响应的数据,以字符串的形式  代码截图    1.5、案例:验证用户名是否重复(重点:必须掌握)  需求:当用户输入完用户名的时候,浏览器发送用户输入的数据(用户名),给服务器,服务器校验用户名,并且给出反馈(用户名可以使用,用户名重复,用户名不能为空,服务器忙)。  思路:
  • 当用户输入完用户名的时候?oblur事件,启动js函数,发送ajax请求
  • 浏览器发送用户输入的数据(用户名),给服务器?Ajax
  • 服务器校验用户名?查询数据库
  • 并且给出反馈。Servelt给出反馈,js处理
  •    分析案例实现的步骤:  页面上提供功能:
  • 输入框,让用户输入用户名,表单提交数据
  • 提交按钮,提交数据
  •  Ajax:
  • 获取用户输入的用户名
  • 发送请求
  • 等待响应
  • 根据响应做不同处理(可以提交表单和不可以提交)
  •    Servlet:  
  • 校验请求参数
  • 调用service方法查询
  •  Service: 调用dao查询数据  Dao: 操作数据; Select * from user where username = ?  画图分析:    功能实现:  页面修改:设置表单、输入框、提交按钮 和js实现:  <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <c:set var="root" value="${pageContext.request.contextPath}"/>  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head>   <title>My JSP 'index.jsp' starting page</title>     <meta http-equiv="pragma" content="no-cache">     <meta http-equiv="cache-control" content="no-cache">     <meta http-equiv="expires" content="0">     <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">     <meta http-equiv="description" content="This is my page">     <script type="text/javascript">     //获取核心对象的方法         function getXHR(){             var xmlhttp;             if (window.XMLHttpRequest){                 // code for IE7+, Firefox, Chrome, Opera, Safari                 xmlhttp=new XMLHttpRequest();             }else{                 // code for IE6, IE5                 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");             }             return xmlhttp;         }         function _checkName(username){              //1    获取用户输入的用户名                          //2    发送请求             var xhr = getXHR();             xhr.open("POST","${root}/checkName",true);             //设置ajax向表单一样post数据             xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");             xhr.send("username="+username);             //3    等待响应             xhr.onreadystatechange = function(){                 if(xhr.readyState == 4 && xhr.status == 200){                                          //4    根据响应做不同处理(可以提交表单和不可以提交)                     var data = xhr.responseText;                     //根据data的数据,做出不同处理                     var _msg = document.getElementById("_un");                     var _form = document.getElementById("_form");                     if(data == 1){                         //提示用户,用户名可以使用,                                                  _msg.innerHTML = "用户名可以使用";                         _form.onsubmit = function(){                             return true;                         };                     }else if(data == -1){                         //提示用户,用户名重复,                         //限制表单提交                         _msg.innerHTML = "用户名重复";                         _form.onsubmit = function(){                             return false;                         };                     }else if(data == -3){                         //提示用户,用户名不能空,                         _msg.innerHTML = "用户名不能为空";                         _form.onsubmit = function(){                             return false;                         };                     }else{                         //我们忙,你等会儿                         _msg.innerHTML = "我们忙,你等会儿";                         _form.onsubmit = function(){                             return false;                         };                                          }                 }                          };         }     </script> </head>   <body> <!-- 1    输入框,让用户输入用户名,表单提交数据         2    提交按钮,提交数据 --> <form id="_form" action="${root }/register" method="post">             <!-- _checkName(this.value) 中的this,指的是,当前标签对象 -->     请输入用户名:<input type="text" name="username" onblur="_checkName(this.value);"><span id="_un"></span><br>     <input type="submit" value="注册"> </form>           </body> </html>        Servlet: package cn.itcast.web;  import java.io.IOException; import java.io.PrintWriter;  import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  import cn.itcast.service.UserService; import cn.itcast.service.impl.UserServiceImpl;  public class CheckNameServlet extends HttpServlet {      public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {                  request.setCharacterEncoding("utf-8");         //1    校验请求参数         String username = request.getParameter("username");         //准备响应:         response.setContentType("text/html;charset=utf-8");         PrintWriter writer = response.getWriter();         if(username == null || username.trim().equals("")){             //给用户一个提示,返回一个-3,后期,页面ajax获取数据之后,根据这个-3,提示用户,用户名不能为空             writer.write("-3");             return;         }         //2    调用service方法查询         UserService userService= new UserServiceImpl();         int info = userService.checkName(username);                  //service方法执行完成之后,将标记,返回给ajax,让ajax根据返回值,做不同处理         writer.write(info+"");         return;               }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {         doGet(request, response);     }  }        Service: 接口: package cn.itcast.service;  public interface UserService {      /**      * 校验用户名的方法      * @param username      * @return      */     int checkName(String username);  }    实现类:  package cn.itcast.service.impl;  import cn.itcast.dao.UserDao; import cn.itcast.dao.impl.UserDaoImpl; import cn.itcast.service.UserService;  public class UserServiceImpl implements UserService {      @Override     public int checkName(String username) {         UserDao userDao = new UserDaoImpl();         int info = userDao.checkName(username);         return info;     }  }    Dao: 接口:  package cn.itcast.dao;  public interface UserDao {      int checkName(String username);  }    实现类:  package cn.itcast.dao.impl;  import java.sql.SQLException;  import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler;  import cn.itcast.dao.UserDao; import cn.itcast.domain.User; import cn.itcast.utils.JDBCUtils;  public class UserDaoImpl implements UserDao {      @Override     public int checkName(String username) {         QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());         String sql = "select * from user where name = ?";         try {             User user = qr.query(sql, new BeanHandler<User>(User.class), username);             //老师,咱们一般不再dao方法中做判断,为什么这次要判断?             if(user == null){                 return 1;             }else{                 return -1;             }         } catch (SQLException e) {             e.printStackTrace();             return -2;         }     }  }    2、JSON技术(重点:必须掌握2.1、json介绍与入门 提示:js继承,js闭包,js原型对象    键值对,格式的数据。类似Map集合的数据结构 properties 集合键值对数据  轻量级:键值对,结构简单 重量级:xml格式,结构复杂 2.1.1、什么是json 定义截图  语法规则截图:  中括号保存数组。    2.1.2、json数据的格式 键值对:  对象:  数组:      2.1.3、json格式文本转换js对象) API截图: 获取json对象数据和遍历json数组:  <script type="text/javascript">                  function test1(){             //需求:操作json对象             var _text = '{"firstName":"John", "lastName":"Doe"}' ;             //使用内置函数将数据转换成javascript对象             var obj = JSON.parse(_text);             alert(obj.firstName);             alert(obj.lastName);                      }      // test1();            function test2(){             //需求:操作json数组             var _text = '{ "employees" : [' +                 '{ "firstName":"John" , "lastName":"Doe" },' +                 '{ "firstName":"Anna" , "lastName":"Smith" },' +                 '{ "firstName":"Peter" , "lastName":"Jones" } ]}';             //使用内置函数将数据转换成javascript对象             var obj = JSON.parse(_text);             alert(obj.employees[0].firstName);             alert(obj.employees[1].firstName);             alert(obj.employees[2].firstName);         }      test2();     </script>    2.1.3、使用json数据,页面显示省市县信息(重点:必须掌握) 需求:完成省市县三级联动 页面截图:    效果:我要做到什么样子?
  • 页面加载完成的时候,就要加载完成省的数据
  • 选择省得时候,出现对应的市
  • 3)选择市的时候,出现对应的县      数据库分析: --获取所有的省和直辖市,parentid = 0 select * from province where parentid = 0; --获取内蒙古下的所有市 select * from province where parentid = 15; --获取锡林郭勒盟下的所有县 select * from province where parentid = 1525;          思路:
  • 页面加载完成之后,省地区的数据如何加载完成?

    Window.onload 启动js函数,

    发送ajax请求,获取省地区的数据

    将数据返回给浏览器,

    将数据添加到省一级地区的select标签中去。

  •    
  • 在用户选择下拉省一级选项之后,市地区数据如何加载完成?

    Onchange事件,启动js函数

    发送ajax请求,获取市地区的数据

    将数据返回浏览器

    将数据添加到市一级地区的select标签中去。

  •    流程:  页面js <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <c:set var="root" value="${pageContext.request.contextPath }"/> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>省市页面</title> <script type="text/javascript">     //获取ajax核心对象     function getXHR(){               var xmlhttp;         if (window.XMLHttpRequest){         // code for IE7+, Firefox, Chrome, Opera, Safari             xmlhttp=new XMLHttpRequest();         }else{             // code for IE6, IE5             xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");         }         return xmlhttp;     }     //第一步:完成省一级地区数据的加载          window.onload = function(){               //发送ajax请求,给服务器         var xhr = getXHR();         xhr.open("get","${root}/getData?parentid=0",true);         xhr.send();                  xhr.onreadystatechange = function(){                      if(xhr.readyState == 4 && xhr.status == 200){                 var data = xhr.responseText;                 //json格式字符串,转换成js对象                 var arr = JSON.parse(data);                 var _province = document.getElementById("province");                 //遍历循环数组,将数据添加到省一级地区的select标签中去                 for ( var i = 0; i < arr.length; i++) {                     //不断的创建option标签                     var _option = document.createElement("option");                     //设置当前地区的代号                     _option.value = arr[i].codeid;                     _option.innerHTML = arr[i].cityName;                     _province.appendChild(_option);                 }             }         };     };      //第二步:选择省的时候,出现对应的市     function _getCity(_this){         //获取市的数据的时候,将原来的数据清空         var _city = document.getElementById("city");         _city.length = 1;         var _area = document.getElementById("area");         _area.length = 1;         var xhr = getXHR();         xhr.open("get","${root}/getData?parentid="+_this.value,true);         xhr.send();                  xhr.onreadystatechange = function(){                      if(xhr.readyState == 4 && xhr.status == 200){                 var data = xhr.responseText;                 //json格式字符串,转换成js对象                 var arr = JSON.parse(data);                                  //遍历循环数组,将数据添加到省一级地区的select标签中去                 for ( var i = 0; i < arr.length; i++) {                     //不断的创建option标签                     var _option = document.createElement("option");                     //设置当前地区的代号                     _option.value = arr[i].codeid;                     _option.innerHTML = arr[i].cityName;                     _city.appendChild(_option);                 }             }         };     }     //第三步:选择市的时候,出现对应的县     function _getArea(_this){         //获取县的数据的时候,将原来的数据清空         var _area = document.getElementById("area");         _area.length = 1;         var xhr = getXHR();         xhr.open("get","${root}/getData?parentid="+_this.value,true);         xhr.send();                  xhr.onreadystatechange = function(){                      if(xhr.readyState == 4 && xhr.status == 200){                 var data = xhr.responseText;                 //json格式字符串,转换成js对象                 var arr = JSON.parse(data);                                  //遍历循环数组,将数据添加到省一级地区的select标签中去                 for ( var i = 0; i < arr.length; i++) {                     //不断的创建option标签                     var _option = document.createElement("option");                     //设置当前地区的代号                     _option.value = arr[i].codeid;                     _option.innerHTML = arr[i].cityName;                     _area.appendChild(_option);                 }             }         };     } </script>   </head>   <body>     <center>         <select id="province" name="province" onchange="_getCity(this);">          <option value="none">--请选择省--</option>         </select>         <select id="city" name="city" onchange="_getArea(this);">             <option value="none">--请选择市--</option>         </select>         <select id="area" name="area" >             <option value="none">--请选择县或区--</option>         </select>     </center> </body> </html>                                             Servlet代码:  package cn.itcast.servlet;  import java.io.IOException; import java.sql.SQLException; import java.util.List;  import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanListHandler;  import cn.itcast.domain.Province; import cn.itcast.utils.JDBCUtils; import flexjson.JSONSerializer;  public class GetDataServlet extends HttpServlet {      public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {          // 获取请求参数         String parameter = request.getParameter("parentid");         int parentid = Integer.parseInt(parameter);          QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());         String sql = "select * from province where parentid = ?";         try {             List<Province> list = qr.query(sql, new BeanListHandler<Province>(                     Province.class), parentid);             //使用flexjson技术将list集合解析成json格式字符串             //创建转换对象             JSONSerializer serializer = new JSONSerializer();             //调用转换的方法             String serialize = serializer.serialize(list);                          //将数据,发给浏览器             response.setContentType("text/html;charset=utf-8");             response.getWriter().write(serialize);                      } catch (SQLException e) {             e.printStackTrace();         }     }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {         doGet(request, response);     }  }      3.作业: 1)使用ajax发送请求(open方法和send方法)(10点积分) 2)使用ajax发送请求,并且接收数据(onreadystatechange、readyState、status属性)(20点积分) 3)完成验证用户名是否重复案例(20点积分) 4)json格式文本转换js对象(JSON.parse())(10点积分) 5)完成省市县三级联动案例(使用flexJson解析成json格式文本)(40点积分)

    相关内容推荐