Easy Ajax with jQuery中文版

Ajax 在改变着web应用,并且带来了一种前所未有的桌面应用程序之外的震撼。但是,在这些宣传的背后我们应该意识到,其实ajax不过是——(X)HTML, Javascript以及XML,没什么新鲜的.在这个教程中,我将给你展示如何让简单的添加ajax到你的应用中去,并且教你如何使用一个流行的 javascript库Jquey进行ajax开发.

1. 什么是ajax

你以前可能听说过ajax,或者至少用过ajax的应用--比如Gmail.简单的说,ajax就是使用javascript来异步地处理数据,而不是一下子重载整个页面.SitePoint上有个教程a good introduction to Ajax.另外,ajax这个词出自Jesse James Garrett的这篇著名的文章.

不幸的是,关于ajax深入的实践教程可以说少之又少,还有就是ajax中使用的XMLHttpRequest 类对初学网页开发的人来说有很大的难度.不过庆幸的是有一大批javascript库相继出现,为实现ajax提供了简单的方法.我们今天要用到的Jquery就是其中之一.

2. 什么是JQuery

Jquery是一个成熟的Javascript库,它提供许多其他库没有的特性.当然也得承认,它有19K之大,不想moo.fx那样只有3KB之轻.你可以在这里看到对许多javascript库性能以及其他方面的比较数据.

3. 先验知识

要学习此教程,你需要有基本的javascript只是,如果你懂c风格的语言,那么你可以对javascript很快上手.其实不过是大括号,函数声明以及可有可无的行末分号(对Jquery来说;为必须).如果你想学习javascript,可以看这个教程. 另外,既然我们讨论的是web应用,基本的html只是自然是必不可少的.

4. Jquery 101

让我们简单浏览一下jQuery.要想使用jQuery,首先你必须下载这个库.下载地址在这里(目前版本1.1.2).jQuery的语法非常简单:找到,然后做.我们从文档中选择元素则使用$().这个符号就相当于 document.getElementById(),不过除了支持ID外,它还支持css选择符以及一些XPath选择符. 而且,它可以返回一个元素的数组.好,也许举个例子可以更好的说明$()的功能.

我们想使用函数来操作我们的选择符.比如,把
"Hello World!" 添加到每个class为foo的div上去,然后设置颜色为红色,我们可以这样写代码:

$("div.foo").append("Hello World!").css("color","red");

很简单啊!一般情况下,这需要两行代码来完成:

$("div.foo").append("Hello World!");
$("div.foo").css("color","red");

jQuery的链接方法可以是允许你连写你的代码,这点别的库恐怕没有.有很多jQuery的函数不需要对象,也就是说独立工作,许多ajax相关的函数都这样.比如,我们将会使用的post函数,调用方式为$.post(parameters). 更多jQuery函数信息可以来online documentation 或者 visualjquery.com.

5. 示例一:我们的第一个ajax程序

作为一个例子,我们将做一个交互概念生成器.简单的说就是让我们从列表中随机选择两个选项,然后组合成一个词组.这个例子中我们将使用web2.0特性的词语(像'Mashup', 'Folksonomy', 'Media' 等等),通常情况下我们从文本文件中获得这些选项.为节省用户用javascript下载每一个组合(或者至少每一个元素)的时间,我们将在服务器端快速生成它,并且使用jQuery在客户端获取到它.jQuery可以很好的和javascript结合使用,所以你将发现在代码中使用它将使工作变得十分容易.

服务器端代码(php):
简单起见,我们使用最简单的代码来做我们的概念生成器.不要担心他是如何工作的,注意看它是干什么的:输出一句话.注意,这段代码没有输出xml,他只是输入一个纯文本:

<?php
header("Cache-Control: no-cache");
// Ideally, you'd put these in a text file or a database.  
// Put an entry on each line of 'a.txt' and use $prefixes = file("a.txt");
// You can do the same with a separate file for $suffixes.
$prefixes = array('Mashup','2.0','Tagging','Folksonomy');
$suffixes = array('Web','Push','Media','GUI');
// This selects a random element of each array on the fly
echo $prefixes[rand(0,count($prefixes)-1)] . " is the new "  
  . $suffixes[rand(0,count($prefixes)-1)];
// Example output: Tagging is the new Media
?>


这里,我们使用 Cache-Control 头选项是因为IE总是为同一个地址建立缓存,甚至页面内容有变化时也是.很明显,这对我们的例子很不利,因为我们在每次加载的时候重新生成一句话.我们也可以使用jQuery生成一个随机数加到地址的后边,不过这不像在服务器端这样处理比较简单.[译者著:其实作者在这里提供了ajax和IE cache冲突的两种解决方案.]

客户端代码(html)

可以开始编写前端代码了,然后我们就可以把ajax加进去了. 我们需要做的仅仅是加一个按钮,好让用户点击一下获得一句新的语句,还有一个div标签,好让我们在从服务器端接收到语句的时候显示在div里面.我们将使用jQuery选中这个div以及加载返回的那句话,我们可以使用div的id来引用它. 如果需要,你可以加载这句话到不同的元素标签中,这可能需要使用class了.不过这里,我们仅仅需要使用id就够了.此页面body标签中的内容为:

<input type="submit" id="generate" value="Generate!">
<div id="quote"></div>

一般说来,我们需要为这个按钮(就是这个id为generate的input)加上一个冗长的
onSubmit 事件. 有时,我们用onSumit事件调用一个Javascript函数. 但是在jQuery里面,我们设置不需要修改任何html代码,我们可以简单的实现行为(事件处理)和结构(html代码)的分离.

客户端代码(jQuery)

终于该使用jQuery把我们的后台和前台结合到一起了.前面我提到我们可以使用jQuery从DOM中选择元素. 首先,我们应该ixuanze这个按钮,并给它一个onClick事件响应. 在这个事件代码中,我们可以选中div并且载入内容.下面是click事件响应的写法:

$("element expression").click(function(){
 // Code goes here
});

可能你已经知道,在CSS里选择一个元素的时候我们使用#来使用元素的id属性. 你可以在jQuery里使用同样的语法.因此,要选择那个按钮,我们可以使用#generate. 注意,这种语法可以让我们把事件处理函数定义成匿名的.

Mark Wubben's JavaScript Terminology page 提供了详细的关于匿名函数的解释,有兴趣可以参考.

我们将要使用jQuery中一个比较高级的ajax函数:load(). 假设我们的代码保存为script.php.我们这样把它和我们的客户端整合起来:

$("#generate").click(function(){
 $("#quote").load("script.php");
});

只有:3行代码!现在我们已经做了一个完整的ajax随机语句生成器了!不错!

问题是javascript代码并不是在一个浏览器加载完就执行的函数内. 这样的话,这段代码就会试图去关联一个可能还没有被加载的元素.一般情况下我们使用window.load来处理这个问题,不过这种方法的局限性在于, window.load只在所有的东西(图片及其它)被加载完成后加载一次.我们对等待这些图片的加载可能毫无兴趣--我们只是需要去获得DOM中的元素罢了.

幸运的是,jQuery有一个
$(document).ready()函数,如其名,它在DOM被加载完之后就被执行.

完整的代码

下面是完整的代码,使用
$(document).ready()以及一些简单的html和css:

<html>
<head>
 <title>Ajax with jQuery Example</title>
 <script type="text/JavaScript" src="jquery.js"></script>
 <script type="text/JavaScript">
 $(document).ready(function(){
   $("#generate").click(function(){
     $("#quote p").load("script.php");
   });
 });
 </script>
<style type="text/css">
   #wrapper {
     width: 240px;
     height: 80px;
     margin: auto;
     padding: 10px;
     margin-top: 10px;
     border: 1px solid black;
     text-align: center;
   }
 </style>
</head>
<body>
 <div id="wrapper">
   <div id="quote"><p> </p></div>
   <input type="submit" id="generate" value="Generate!">
 </div>
</body>
</html>


代码可以在这里下载,注意你的jquery需要保存在php文件的同目录,并且名为 jquery.js .现在你已经熟悉jQuery了,让我们做些更复杂的:表单元素和XML处理,这才是真正的ajax!


示例二: 使用jQuery建造聊天程序

为了展示jQuery的强大,我们将建立一个具有ajax功能的聊天程序.它允许用户发布信息,并且可以实时的更新信息--但是没有任何的页面刷新.鉴于我们将要处理一个相对比较复杂的应用,我将更加深入的讲解jQuery的特性,向您展示她那些用起来很顺手的函数.

首先,我们来规划一下这个应用.我们不需要太多东西--一个前端,一个后端,一个jQuery的库就够了.但是,将会有一些代码处理前端和后端的结合,所以我们首先应该知道预期从系统里看到什么.

规划服务器端

此应用的后端需要处理信息的提交以及输出.把这个铭记在心,这样我们就可以为后端代码构建一个草图:
  • 链接数据库
  • 如果有信息提交
    • 把信息插入到数据库
    • 删除老的信息

  • 从数据库获取信息并以XML显示
正如你所看到的,它很简单并且直白.如果你需要使用另外的语言来实现后端程序,你就可以使用这个规划来指导你.

规划客户端

前端需要使用ajax处理反馈,就像我们在第一个例子中做的那样.它需要处理信息的提交,并且用最新的消息不间断的更新聊天窗体.然而,我们将要用到另一个特性--我们将使用the current UNIX timestamp来展示那个消息被加载了,并且只是抓取这个信息,由此减少贷款使用以及服务器负载.下面就是客户端规划的草图:
  • 页面加载的时候
    • 设置当前的timestamp为0 (所有的信息都在此之后提交, 比如,所有的信息都会被获取)
    • 调用函数获取消息

  • function(函数): 获取新消息
    • 使用POST向服务其发送请求
    • 调用函数处理XML响应
    • 增加一个定时器并且每秒钟调用一次此函数(服务器的性能好的话可以提高此频率)

  • function: 转换新消息的XML
    • 根据XML中的内容设定当前的timestamp
    • 如果状态是 '2',则表示没有新消息,终止函数调用
    • 否则,对反馈回来的每个消息进行处理, 以下面的格式加到聊天窗口的最上端:
      • 作者: 信息

  • 提交信息的时候:
    • 使用 POST向服务器发送请求, 需要指定:
      • 作者姓名(用户指定)
      • 消息体(用户指定)
      • 标识这是一个post请求
      • 最后一次向服务器请求的timestamp

    • 保持输入文本框为空,便于用户在此输入信息
    • 调用处理XML响应的函数 (以便让信息实时呈现出来)

看上去比服务器端有点复杂了,但是庆幸有了jQuery,代码不会很长.

规划数据库

我们将使用MySql数据库来存储信息(虽然任何sql数据库都能胜任此工作,并且代码相差也不是很大). 我们需要一个表,有四个列:消息id, 消息作者名,消息体,以及一个数字时间戳(timestamp). 下面使我们创建这个表的sql代码:

CREATE TABLE `messages` (
 `id` int(7) NOT NULL auto_increment,
 `user` varchar(255) NOT NULL,
 `msg` text NOT NULL,
 `time` int(9) NOT NULL,
 PRIMARY KEY  (`id`)
);


由于我们不能判断到底消息有多长,我们现在就只使用一个text字段.

服务器端代码(XML)

要建立后端,我们首先应该知道我们需要后端输出什么(来决定前端和后端的接口),由此向后工作, 下面是一个简单的XML结构:

<?xml version="1.0"?>
<response>
 <status>1</status>
 <time>1170323512</time>
 <message>
   <author>John Citizen</author>
   <text>Hello world!</text>
 </message>
 <message>
   <author>John Citizen</author>
   <text>Hello world again!</text>
 </message>
</response>


注意我们已经增加一个标签'status', 它的值为1. 就如我上面提到的,一个状态值为1则标示新信息请求成功,2则表示请求成功但是没有新信息. 每个消息的实例都包括作者以及其信息.

服务器端代码(php)

现在,回到服务器端.我们将使用php来实现,不过由于输出的是xml,所以你可以使用你喜欢的任何语言来写后段代码,比如Perl或者asp. 我们先定义一些配置信息这样便于我们以后对其进行修改. 我们需要数据库连接的具体信息, 我们要存储的消息的数目(数据库可以处理成千上万行数据,所以这个可以适当的设大点),以及用户进到聊天室的时候消息显示的数目. 代码如下:

$dbhost = "localhot";
$dbuser = "root";
$dbpass = "";
$dbname = "chat";
$store_num = 10;
$display_num = 10;


现在言归正传,我们继续看后端本身.数据库连接自然是必须的, 但是我们同时需要确认不让IE缓存请求数据,并且需要确认输出格式为XML. 为了能够监视所有的错误,我们设置错误汇报为"所有错误"("all errors"). 为容易操作请求数据,我们为请求中的每个参数设置一个变量,每个变量将把请求中的参数值作为其自己的值. 代码如下:

error_reporting(E_ALL);
header("Content-type: text/xml");
header("Cache-Control: no-cache");
$dbconn = mysql_connect($dbhost,$dbuser,$dbpass);
mysql_select_db($dbname,$dbconn);
foreach($_POST as $key => $value)
 $$key = mysql_real_escape_string($value, $dbconn);


foreach语句遍历所有的POST数据,并且为每个参数创建一个变量,并且给它赋值(比如path/to/file.php?variable=value将给$variable 赋值为"value").这样就简化了处理请求数据的过程,无需我们手动设定.

下面,我们将进入主要函数部分.现在我们处理消息向数据库的写入,以及根据设定的显示数目$display_num来从数据库中获取相应数目的消息. 当我们规划客户端的时候我提到过当提交消息的时候我们需要设定一个响应状态. 我们现在需要检查这个响应--我们给蚕食'action'赋值'postmsg',表示我们正在处理这个检查并且插入新数据到数据库.处理的时候我们需要插入新的unix时间戳( timestamp).

然而,我们仍然需要清理我们的数据库.根据你的数据库的限制,你可以设定存储数据的数量.一般而言,不赞成使用消息日志,所以我这里设定默认存储10条消 息.我们将使用一个函数来获取最新消息的id,并且决定是否删除.举例来说,如果我们增加第11个消息,我们使用11减去存储量(10)就得到一个id阈 值(现在是1),这样的话我们就可以删除小于等于这个阈值的所有消息,这个例子终究是删除第一条消息.有了sql,我们可以只写一条语句就处理这些.

下面的代码片段检查
'postmsg'响应,向数据库添加数据,以及清理数据库.

if(@$action == "postmsg")
{
 mysql_query("INSERT INTO messages (`user`,`msg`,`time`)
       VALUES ('$name','$message',".time().")");
 mysql_query("DELETE FROM messages WHERE id <= ".
       (mysql_insert_id($dbconn)-$store_num),$dbconn);
}


使用其他服务器端技术的开发者可以写出等价的代码.注意我们使用time函数来获取unix时间戳.我们可以假设在sql执行过程中这个时间不会改变(即 便是很慢的服务器,这段代码也可以每秒钟执行几百次). 所以当我们向客户端返回一个时间戳的时候,我们可以简单的再次调用time函数,这个值也是可信的.

剩下的工作就是要处理从数据库获取最新的消息并且以XML的形式输出.这里就要用到我上面写的那段xml代码了.然而,大部分代码是sql,我们使用sql的强大来处理这个工作,代码的执行时间不受影响. 下面是sql查询的要求:

  • 它只是获取作者以及信息
  • 它只是获取还没有被下载的信息--客户端有一个最后请求的时间戳,因此这个时间戳可以被插入到sql中
  • 需要对消息进行排序一遍让最新的消息排在最后,并且允许逆序显示.
  • 根据配置信息限制获取的消息数量.

任何熟悉sql的人都不会反对这很简单.剩下的就是这些代码了.先看看查询语句:

$messages = mysql_query("SELECT user,msg
            FROM messages
            WHERE time>$time
            ORDER BY id ASC
            LIMIT $display_num",$dbconn);

剩下的代码就相当基础,如果没有结果,就设置状态代码为0,否则,设置为1,输出最初的xml,每个消息的xml以及最终的xml.就是这些了,代码如下:

if(mysql_num_rows($messages) == 0) $status_code = 2;
else $status_code = 1;

echo "<?xml version=\"1.0\"?>\n";
echo "<response>\n";
echo "\t<status>$status_code</status>\n";
echo "\t<time>".time()."</time>\n";
while($message = mysql_fetch_array($messages))
{
 echo "\t<message>\n";
 echo "\t\t<author>$message[user]</author>\n";
 echo "\t\t<text>$message[msg]</text>\n";
 echo "\t</message>\n";
}
echo "</response>";

最终的代码在压缩包里,所以不需要复制上面的代码.现在后端程序完成了,下面我们就该更有趣的工作了--使用html和jQuery!

客户端代码(html)

在使用jQuery之前,我们先处理一下html的页面.这样的话,当我们必须确定使用那些元素来获取或者更新使用jQuery返回的数据时,我们就知道 怎么做了.我们不需要很多东西:一个外围div,一个消息段落以及一个有用户名和消息文本框的form,还有一个submit按钮.最后还需要加一个加载 信息时的提示--我们可以使用jQuery轻松的移除它.下面就是代码:


<div id="wrapper">
<p id="messagewindow"><span id="loading">Loading...</span></p>
<form id="chatform">
Name: <input type="text" id="author" />
Message: <input type="text" id="msg" />
<input type="submit" value="ok" /><br />
</form>
</div>


现在,我们继续看jQuery在客户端的使用。首先,我们需要生命当前消息的时间戳为0,并且在服务器端调用函数来加载消息:

timestamp = 0;
updateMsg();

然后,我们将完成表单提交的代码。jQuery允许我们为表单的submit事件添加一个事件钩子,就像在html代码中直接写onSubmit事件的效果一样,但是不需要动任何html代码。下面是submit事件:

$("form#chatform").submit(function(){ /* Code */ });

这里我们用CSS选择符语法来引用到一个id为'chatform'的表单元素,一旦我们进入表单提交的代码,我们可以使用jQuery的$.post来发送一个POST请求.在$.post的调用中,我们可以使用表单元素的id来选择它的值,就像我们先前演示的那样.知道了这些,让我们看一下我们的ajax调用:

$.post("backend.php",{ message: $("#msg").val(),
name: $("#author").val(), action: "postmsg", time: timestamp }, function(xml) {

注意这里传过去的参数数组是以大括号括起来的.如果你有多个参数的话,可以简单的以逗号分割,并且使用JSON格式,像上面演示的那样.由还可以使用 jquery的ajax函数发送一个get请求, 会有一个json风格的响应,可以使用jQuery把反馈的文本转换成更容易阅读的形式.要记住,这种方式只有对get请求类型才起作用,对我们这里使用 的post则无效.因此,我们这里将使用最简单的XML.

现在,我们看一下XML响应.为代码重用,我们在这里为处理xml创建一个函数并且调用它:

addMessages(xml);

我们将在后边实现它,以便我们现在专注实现表单提交代码.我们现在写出来的代码就是$.post需要的全部了,因此我们加个大括号以及一个返回false的提示(return false; ).这行将使标准浏览器提交表单错误代码的时候失效.浏览器并不把请求发送到另一个页面--因为我们已经处理了事件提交,不需要由浏览器来处理了.这既是代码的全部:

$("form#chatform").submit(function(){
 $.post(
"backend.php",{
       message: $(
"#msg").val(),
       name: $(
"#author").val(),
       action: 
"postmsg",
       time: timestamp
     }
function(xml) {
   addMessages(xml);
 }
);
 
return false;
}
);

 

现在让我们回头看看addMessages()函数,来处理xml响应信息.使用jQuery的DOM操作以及遍历函数使它的实现变得很简单. 记得我曾经提到的状态代码么?现在我们该看看怎么处理它了:

if($("status",xml).text() == "2") return;

我还没有提过jQuery的相关上下文.这里函数调用的xml告诉jQuery不要区在html文档中找,而应在服务器端发送给我们的xml文件中.

这行代码检查状态代码为2,则表示完成了一次成功的请求但是没有新信息添加到窗体中去.return关键字终止函数调用.然后我们设置为xml的时间戳设值.

timestamp = $("time",xml).text();

再次,我们从xml的time标签获得值.

现在,我们继续看jQuery遍历数组的函数:each(); jQuery有一个遍历数组的有趣方法.我们使用一个标准的选择符声明,并且each()函数传递一个参数--一个函数处理相匹配元素的每一个实例. 在我们的示例中,元素就是服务器端返回的每一个<message>标签的实例.每个实例代表一条要显示的消息.参数--实例的id被传给函数.我们可以使用jQuery的get()方法来获取新内容--其实就是<message>标签的xml. 下面代码展示我们如何选择它:

$("message",xml).each(function(id) {
 message = $("message",xml).get(id);

我们然后就可以使用jQuery的$函数来获取message的值.既然我们得到了我们需要的所有数据,我们就应该把它加到消息显示区里面去.这个消息显示窗体有一个叫做'messagewindow'的id,因此我们使用$("#messagewindow")来选择到它并且使用prepend()来添加我们的数据:

$("#messagewindow").prepend("<b>"+$("author",message).text()+
         "</b>: "+$("text",message).text()+
         "<br />");

就是这些了,把他们放在一起,现在我们的函数就是:

function addMessages(xml) {
 
if($("status",xml).text() == "2"return;
 timestamp 
= $("time",xml).text();
 $(
"message",xml).each(function(id) {
   message 
= $("message",xml).get(id);
   $(
"#messagewindow").prepend("<b>"+$("author",message).text()+
             
"</b>: "+$("text",message).text()+
             
"<br />");
 }
);
}

 

最后,我们需要来实现我们在代码开始调用的函数updateMsg, 这个函数要到服务器查询新信息,并且调用上面的addMessages来响应.同时需要设置一个超时时间,好让聊天窗口自动更新. 要开始做这些,我们只需要向服务器提交一个时间戳,引起这个$.post调用如下:

$.post("backend.php",{ time: timestamp }, function(xml) {

我上面提到,我们需要在这时移除我们的loading消息,因此,我们在这个span上调用remove函数:

$("#loading").remove();

然后,我们在xml对象中收到xml响应,把它传递给我们的
addMessages 函数:

addMessages(xml);

最后我们调用javascript的setTimeOut()函数,来间断执行代码.这就是完整的代码:

function updateMsg() {
 $.post("backend.php",{ time: timestamp }, function(xml) {
   $("#loading").remove();
   addMessages(xml);
 });
 setTimeout('updateMsg()', 4000);
}

整合代码

现在我们把所有的代码整合到一起.代码可以在这里下载( this downloadable zip file).不过,请看这里,我们加了一些html和css到我们的程序中:
<html>
<head>
 
<title>Ajax with jQuery Example</title>
 
<script type="text/JavaScript" src="jquery.js"></script>
 
<script type="text/JavaScript">
   $(document).ready(
function(){
     timestamp 
= 0;
     updateMsg();
     $(
"form#chatform").submit(function(){
       $.post(
"backend.php",{
             message: $(
"#msg").val(),
             name: $(
"#author").val(),
             action: 
"postmsg",
             time: timestamp
           }
function(xml) {
         $(
"#msg").empty();
         addMessages(xml);
       }
);
       
return false;
     }
);
   }
);
   
function addMessages(xml) {
     
if($("status",xml).text() == "2"return;
     timestamp 
= $("time",xml).text();
     $(
"message",xml).each(function(id) {
       message 
= $("message",xml).get(id);
       $(
"#messagewindow").prepend("<b>"+$("author",message).text()+
                     
"</b>: "+$("text",message).text()+
                     
"<br />");
     }
);
   }

   
function updateMsg() {
     $.post(
"backend.php",{ time: timestamp }function(xml) {
       $(
"#loading").remove();
       addMessages(xml);
     }
);
     setTimeout(
'updateMsg()'4000);
   }

 
</script>
 
<style type="text/css">
   #messagewindow 
{
     height
: 250px;
     border
: 1px solid;
     padding
: 5px;
     overflow
: auto;
   
}

   #wrapper 
{
     margin
: auto;
     width
: 438px;
   
}

 
</style>
</head>
<body>
 
<div id="wrapper">
 
<id="messagewindow"><span id="loading">Loading...</span></p>
 
<form id="chatform">
 Name: 
<input type="text" id="author" />
 Message: 
<input type="text" id="msg" />    
 
<input type="submit" value="ok" /><br />
 
</form>
 
</div>
</body>
</html>

你看到了,仅仅使用22行javascript代码,8行html以及大约50行php,我们就实现了一个完整的基于ajax的web 聊天室应用程序.试一下吧,然后加入到你自己的网站上.创建你自己的ajax程序,使用这里的技术,或者你有自己的好点子.用这里的代码去做电信东西.如 果你觉得使用xml很不爽,那你可以直接在你的应用程序中生成html,然后使用load来加载到客户端.如果你想的话,可以试试用功能强大的xml标签属性以及jQuery的attr()函数. 现在你应该已经很惊讶于用jQuery实现ajax的简单了吧!

[全文完]

posted @ 2008-04-01 20:36  yangjun  阅读(722)  评论(0编辑  收藏