posts - 3, comments - 1, trackbacks - 0, articles - 0
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

2007年10月31日

 

JFreeChart是一个开源的JAVA项目,它主要用来开发各种各样的图表,这些图表包括:饼图、柱状图(普通柱状图以及堆栈柱状图)、线图、区域图、分布图、混合图、甘特图以及一些仪表盘等等。在这些不同式样的图表上可以满足目前商业系统的要求。JFreeChart是一种基于JAVA语言的图表开发技术。JFreeChart可用于ServletJSPAppletJava Appication环境中,通过JDBC可动态显示任何数据库数据,结合Itext可以输出至PDF文件。

JFreeChart主要是由三个类构成:

Aorg.jfree.chart.servlet.ChartDeleter继承自HttpSessionBindingListener,用于实现当Session 关闭时,删除临时目中的图象文件。

Borg.jfree.chart.servlet.DisplayChart继承自Httpservlet 用于处理显示图象。

Corg.jfree.chart.servlet.ServletUtilities有一系列方法,例如,saveChartAs*;saveChartAs*是把图表按照不同的形式存储为图象;sendTempFile方法被重载了很多次,用于把文件流发送response

下面以柱状图和饼图为例,介绍图形创建方法。

1 柱状图

org.jfree.chart.ChartFactory这个工厂类有createBarChartcreateStackedBarChartcreateBarChart3DcreateStackedBarChart3D,这几个工厂方法创建不同类型的柱状图,比较重要的是 PlotOrientation.VERTICAL 让平行柱垂直显示,而 PlotOrientation.HORIZONTAL 则让平行柱水平显示。对柱状图影响较大的几个类包括:org.jfree.chart.axis.CategoryAxisorg.jfree.chart.axis.ValueAxisorg.jfree.chart.renderer.BarRendererorg.jfree.chart.renderer. BarRenderer3D

具体实现步骤:

1)创建用于图形生成所要的数据集对象。

CategoryDataset dataset = DatasetUtilities.createCategoryDataset(rowKeys, columnKeys, data)

其中:rowKeys表示X轴数据,columnKeys表示Y轴数据,data表示填充柱状图所要的实际数据(来自于数据库)。

2)创建图形对象。

JFreeChart chart = ChartFactory.createBarChart3D("标题", nullnulldatasetPlotOrientation.VERTICAL,truefalsefalse)

createBarChart3D方法是ChartFactory工厂类里的一个方法,用于3D柱状图的生成,该类继承自JFreeChart。其中的八个参数分别代表:图形的标题、X轴标题、Y轴标题、dataset就是CategoryDataset类的实例对象、显示标题、启用热键、启用超键接。

3)设置图形显示的属性。

a ) ValueAxis类,设置柱到图上下边的距离。实现方法是:

ValueAxis rangeAxis = plot.getRangeAxis();

设置最高的一个柱与图片顶端的距离:

rangeAxis.setUpperMargin(0.15)

设置最低的一个柱与图片底端的距离:

rangeAxis.setLowerMargin(0.15)

borg.jfree.chart.renderer.BarRenderer3D类,设置图形上显示的数值。实现方法如下:

BarRenderer3D renderer = new BarRenderer3D();

renderer.setBaseOutlinePaint(Color.BLACK);

设置 Wall 的颜色:

renderer.setWallPaint(Color.gray);

设置每个柱的颜色:

renderer.setSeriesPaint(0, new Color(0, 0, 255));

renderer.setSeriesPaint(1, new Color(0, 100, 255));

renderer.setSeriesPaint(2, Color.GREEN);

设置每个柱的 Outline 颜色
renderer.setSeriesOutlinePaint(0, Color.BLACK);

renderer.setSeriesOutlinePaint(1, Color.BLACK);

renderer.setSeriesOutlinePaint(2, Color.BLACK);

设置每个地区所包含的平行柱之间的距离
renderer.setItemMargin(0.1);

显示每个柱的数值,并修改该数值的字体属性

renderer.setItemLabelGenerator(new StandardCategoryItemLabelGenerator());

renderer.setItemLabelFont(new Font("黑体",Font.PLAIN,12));

renderer.setItemLabelsVisible(true);

为图形加入超连接

renderer.setItemURLGenerator(new StandardCategoryURLGenerator());

renderer.setToolTipGenerator(new StandardCategoryToolTipGenerator());

2 饼图

org.jfree.chart.plot包,包含创建饼形图的所有方法和属性。

笔者根据业务需求创建了setURLGenerator(PieURLGenerator generator)方法,在图片上建立连接,就是图片不同部分连接不同的资源。

setSectionLabelType(int type)方法:

指定 section 标签的类型,共有 7 种类型。如果不指定,默认是 NAME_LABELS,其中类型分别是:

PiePlot.NO_LABELS

PiePlot.NAME_LABELS

PiePlot.VALUE_LABELS

PiePlot.PERCENT_LABELS PiePlot.NAME_AND_VALUE_LABELSPiePlot. NAME_AND_PERCENT_LABELSPiePlot.VALUE_AND_PERCENT_LABELS

setDefaultOutlinePaint(java.awt.Paint paint)方法,指定 section 轮廓线的颜色,如果不指定,默认值为NULL

setDefaultOutlineStroke(java.awt.Stroke stroke)方法,指定 section 轮廓线的厚度。

setRadius(double percent) setExplodePercent(int section, double percent)方法,抽离 section,就是把某一section从饼形图剥离出来,需要两个方法一起使用。

setStartAngle(double angle)方法,设置第一个section开始位置,默认从12点钟方向开始。

setPaint(int section, java.awt.Paint paint)方法指定section的颜色。

setDirection(int direction)方法指定section顺序,默认是顺时针方向。顺时针:PiePlot.CLOCKWISE;逆时针:PiePlot.ANTICLOCKWISE

具体实现步骤:

1)创建用于图形生成所要的数据集对象。

首先实例化类DefaultPieDataset dataset = new DefaultPieDataset()。然后利用DefaultPieDataset类提供的setValuevalue1,value2)方法,把从数据库里提取的数据存入DefaultPieDataset对象。其中value1是数据名称、value2是数据值。

2)创建图形对象。

首先实例化JFreeChart chart = ChartFactory.createPieChart3D(title, dataset, true, true, false)createPieChart3D方法是用于饼图生成的主要方法。其中title代表图形的标题、dataset就是DefaultPieDataset对象的实例。

3)设置图形显示的属性。

String filename = ServletUtilities.saveChartAsPNG(jFreeChart, 700, 450, info, session);

ChartUtilities.writeImageMap(pw, filename, info);

pw.flush()

saveChartAsPNG方法在ServletUtilities工厂类定义完成。主要用于把图形对象JFreeChart以图片的形式保存。其中的jFreeChart就是JFreeChart对象的实例。该方法返回一个文件名。

writeImageMappw, filename, info)方法用于把保存的图片文件以字节流的形式写入用户界面。

其中pwjava.io包的PrintWriter类的实例对象,该对象创建一个图形输出流。Filename是输出图片的文件名。该文件名来自ServletUtilities.saveChartAsPNG方法创建。

参数info用于图形信息的显示。

ChartRenderingInfo info=new ChartRenderingInfo(new StandardEntityCollection())创建。

最后输出完成图形,调用pw.flush()方法关闭IO流。

------------------------------------------------------------------------------------------

使用JFreeChart生成各种样式的图表

限于篇幅的问题我们在这里只实现两种常用的图表,其他类型图表读者可以触类旁通。我们先给出柱状图的实现,饼图的实现再来跟柱状图进行比较。

1 柱状图


package lius.chart.demo;

import java.io.*;

import org.jfree.data.*;
import org.jfree.chart.*;
import org.jfree.chart.plot.*;
/**
* 该类用于演示最简单的柱状图生成
* @author Winter Lau
*/
public class BarChartDemo {

public static void main(String[] args) throws IOException{

CategoryDataset dataset = getDataSet2();
JFreeChart chart = ChartFactory.createBarChart3D(
"水果产量图", // 图表标题
"水果", // 目录轴的显示标签
"产量", // 数值轴的显示标签
dataset, // 数据集
PlotOrientation.VERTICAL, // 图表方向:水平、垂直
true, // 是否显示图例(对于简单的柱状图必须是false)
false, // 是否生成工具
false // 是否生成URL链接
);

FileOutputStream fos_jpg = null;
try {
fos_jpg = new FileOutputStream("D:""fruit.jpg");
ChartUtilities.writeChartAsJPEG(fos_jpg,100,chart,400,300,null);
} finally {
try {
fos_jpg.close();
} catch (Exception e) {}
}
}
/**
* 获取一个演示用的简单数据集对象
* @return
*/
private static CategoryDataset getDataSet() {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(100, null, "苹果");
dataset.addValue(200, null, "梨子");
dataset.addValue(300, null, "葡萄");
dataset.addValue(400, null, "香蕉");
dataset.addValue(500, null, "荔枝");
return dataset;
}
/**
* 获取一个演示用的组合数据集对象
* @return
*/
private static CategoryDataset getDataSet2() {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(100, "北京", "苹果");
dataset.addValue(100, "上海", "苹果");
dataset.addValue(100, "广州", "苹果");
dataset.addValue(200, "北京", "梨子");
dataset.addValue(200, "上海", "梨子");
dataset.addValue(200, "广州", "梨子");
dataset.addValue(300, "北京", "葡萄");
dataset.addValue(300, "上海", "葡萄");
dataset.addValue(300, "广州", "葡萄");
dataset.addValue(400, "北京", "香蕉");
dataset.addValue(400, "上海", "香蕉");
dataset.addValue(400, "广州", "香蕉");
dataset.addValue(500, "北京", "荔枝");
dataset.addValue(500, "上海", "荔枝");
dataset.addValue(500, "广州", "荔枝");
return dataset;
}
}

程序运行结束后生成的图片文件效果如下图所示:


图4

如果是使用简单的数据即使用getDataSet方法获取数据集时产生的图片文件如下:


图5

2 饼图

对于饼图而言,数据集的获取用的不是同一个数据集类,另外饼图不支持同一个类别的项目中还有子项目这样的数据。我们只给出创建饼图的代码,至于写图表到一个文件则与柱状图一致,无需重复。


package lius.chart.demo;

import java.io.*;

import org.jfree.data.*;
import org.jfree.chart.*;
/**
* 用于演示饼图的生成
* @author Winter Lau
*/
public class PieChartDemo {

public static void main(String[] args) throws IOException{
DefaultPieDataset data = getDataSet();
JFreeChart chart = ChartFactory.createPie3DChart("水果产量图", // 图表标题
data,
true, // 是否显示图例
false,
false
);
//写图表对象到文件,参照柱状图生成源码
}
/**
* 获取一个演示用的简单数据集对象
* @return
*/
private static DefaultPieDataset getDataSet() {
DefaultPieDataset dataset = new DefaultPieDataset();
dataset.setValue("苹果",100);
dataset.setValue("梨子",200);
dataset.setValue("葡萄",300);
dataset.setValue("香蕉",400);
dataset.setValue("荔枝",500);
return dataset;
}
}

生成的饼图文件效果如下:


图6





回页首


将生成的图表移到浏览器上

为了将生成的图表直接传给客户端浏览器,只需要将前面两个例子中的文件流换成是通过HttpServletResponse对象获取到的输出流,详细代码清单如下:


package lius.chart.demo;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;

import org.jfree.data.*;
import org.jfree.chart.*;
/**
* 演示通过servlet直接输出图表
* @author Winter Lau
*/
public class ChartDemoServlet extends HttpServlet {

public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
res.setContentType("image/jpeg");
DefaultPieDataset data = getDataSet();
JFreeChart chart = ChartFactory.createPie3DChart("水果产量图",
data,
true,
false,
false
);

ChartUtilities.writeChartAsJPEG(res.getOutputStream(),
100,chart,400,300,null);
}
/**
* 获取一个演示用的简单数据集对象
* @return
*/
private static DefaultPieDataset getDataSet() {
DefaultPieDataset dataset = new DefaultPieDataset();
dataset.setValue("苹果",100);
dataset.setValue("梨子",200);
dataset.setValue("葡萄",300);
dataset.setValue("香蕉",400);
dataset.setValue("荔枝",500);
return dataset;
}
}




高级主题

很多情况我们不仅仅要求可以在浏览器上显示一个图表,我们更需要客户可以直接在图表上做一下交互的操作,例如获取信息提示,点击图表某个部分进行更详细信 息的展示等等。例如前面生成的简单柱状图,用户需要在看到柱状图后点击某种水果例如是苹果即可看到各个地区苹果产量的情况。为此就要求该图形具有交互操作 的功能。在HTML中为了让一个图像具有可交互的功能就必须给该图像定义一个Map对象。下表节选一段具有该功能的HTML代码


<MAP NAME="chartMap">
<AREA SHAPE="RECT" COORDS="81,15,126,254" href="?series=0&category=100" title="100 = 7,048"
onclick="javascript:clickChart('100');return false;">
<AREA SHAPE="RECT" COORDS="143,27,188,255" href="?series=0&category=200" title="200 = 6,721"
onclick="javascript: clickChart ('200');return false;">
<AREA SHAPE="RECT" COORDS="205,54,250,255" href="?series=0&category=300" title="300 = 5,929"
onclick="javascript: clickChart ('300');return false;">
<AREA SHAPE="RECT" COORDS="267,85,312,255" href="?series=0&category=400" title="400 = 5,005"
onclick="javascript: clickChart ('400');return false;">
<AREA SHAPE="RECT" COORDS="329,17,374,255" href="?series=0&category=Diet" title="Diet = 7,017" onclick="javascript:
clickChart ('Diet');return false;">
</MAP>

由此就产生了一个问题:如果 根据一个图像来生成对应的MAP对象。我们回头看看刚才的代码,在创建一个图表对象时候有两个参数,我们举柱状图的例子来讲这两个参数就是 ChartFactory. createBarChart3D方法中的最后两个参数,这两个参数的类型都是布尔值。这两个参数意思分别是:是否创建工具提示(tooltip)以及是 否生成URL。这两个参数分别对应着MAP中一个AREA的title属性以及href属性。

可是我想知道的是怎么来产生这个MAP啊!哈哈,不要着急,JFreeChart已经帮我们做好生成MAP对象的功 能。为了生成MAP对象就要引入另外一个对象:ChartRenderingInfo。因为JFreeChart没有直接的方法利用一个图表对象直接生成 MAP数据,它需要一个中间对象来过渡,这个对象就是ChartRenderingInfo。下图是生成MAP数据的流程图:


图7

如 上图所示,ChartUtilities类是整个流程的核心,它周围的对象都是一些例如数据对象或者是文件等。这个流程简单描述如下:首先创建一个 ChartRenderingInfo对象并在调用ChartUtilities的writeChartAsJPEG时作为最后一个参数传递进去。调用该 方法结束后将产生一个图像文件以及一个填充好MAP数据的ChartRenderingInfo对象,有了这个对象我们还是没有办法获取具体的MAP数 据,我们还必须借助于ChartUtilities的writeImageMap方法来将ChartRenderingInfo对象读取出来,获取MAP 数据的代码片断如下:


PrintWriter w = null;
FileOutputStream fos_jpg = null;
FileOutputStream fos_cri = null;
try{
//根据不同类型的图表使用不同类,以下是针对饼图的操作
PiePlot plot = (PiePlot) chart.getPlot();
plot.setURLGenerator(new StandardPieURLGenerator(url));
//设置工具提示
plot.setToolTipGenerator(new StandardPieToolTipGenerator());
fos_jpg = new FileOutputStream(“d:""fruit.jpg”);
ChartUtilities.writeChartAsJPEG(
fos_jpg,
100,
chart,
400,
300,
info);
fos_cri = new FileOutputStream(__d:""fruit.map__);
w = new PrintWriter(fos_cri);
ChartUtilities.writeImageMap(w, __mapname__, info);
w.flush();
}finally{
try{
w.close();
}catch(Exception e){}
try{
fos_cri.close();
}catch(Exception e){}
try{
fos_jpg.close();
}catch(Exception e){}
}

打开文件D:" fruit.map,文件的内容就是要写到页面上的MAP数据。把生成的图像文件以及MAP数据文件写到页面上即可完成热点图表的功能。至于怎么结合两者 之间的关系例如图像的useMap属性值必须与MAP对象的名称结合起来,必须根据实际的应用情况进行相应的处理。笔者建议把二者通过标签库封装起来,图 像文件的名称以及MAP对象的名称由标签库统一进行控制,这样可以保证二者的一致性。



原文出处:http://dev.csdn.net/author/lyj_china/a5cda7cefe074c6b84f0ec944041921f.html

posted @ 2007-10-31 17:09 小眼睛大智慧 阅读(199) | 评论 (0)编辑 收藏

一、page 对象
    page对象代表JSP本身,更准确地说它代表JSP被转译后的Servlet,它可以调用Servlet类所定义的方法。
        
二、config 对象
    config 对象里存放着一些Servlet 初始的数据结构。
    config 对象实现于javax.servlet.ServletConfig 接口,它共有下列四种方法:
        public String getInitParameter(name)
        public java.util.Enumeration getInitParameterNames( )
        public ServletContext getServletContext( )
        public Sring getServletName( )

三、request 对象
    request 对象包含所有请求的信息,如:请求的来源、标头、cookies和请求相关的参数值等等。
    request 对象实现javax.servlet.http.HttpServletRequest接口的,所提供的方法可以将它分为四大类:
    1.储存和取得属性方法;
        void setAttribute(String name, Object value)       设定name属性的值为value
        Enumeration getAttributeNamesInScope(int scope)    取得所有scope 范围的属性
        Object getAttribute(String name)                   取得name 属性的值
        void removeAttribute(String name)                  移除name 属性的值
    2.取得请求参数的方法
        String getParameter(String name)                   取得name 的参数值
        Enumeration getParameterNames( )                   取得所有的参数名称
        String [] getParameterValues(String name)          取得所有name 的参数值
        Map getParameterMap( )                             取得一个要求参数的Map
    3.能够取得请求HTTP 标头的方法
        String getHeader(String name)                      取得name 的标头
        Enumeration getHeaderNames()                       取得所有的标头名称
        Enumeration getHeaders(String name)                取得所有name 的标头
        int getIntHeader(String name)                      取得整数类型name 的标头
        long getDateHeader(String name)                    取得日期类型name 的标头
        Cookie [] getCookies( )                            取得与请求有关的cookies
    4.其他的方法
        String getContextPath( )                           取得Context 路径(即站台名称)
        String getMethod( )                                取得HTTP 的方法(GET、POST)
        String getProtocol( )                              取得使用的协议 (HTTP/1.1、HTTP/1.0 )
        String getQueryString( )                           取得请求的参数字符串,不过,HTTP的方法必须为GET
        String getRequestedSessionId( )                    取得用户端的Session ID
        String getRequestURI( )                            取得请求的URL,但是不包括请求的参数字符串
        String getRemoteAddr( )                            取得用户的IP 地址
        String getRemoteHost( )                            取得用户的主机名称
        int getRemotePort( )                               取得用户的主机端口
        String getRemoteUser( )                            取得用户的名称
        void etCharacterEncoding(String    encoding)       设定编码格式,用来解决窗体传递中文的问题

四、response 对象
    response 对象主要将JSP 处理数据后的结果传回到客户端。
    response 对象是实现javax.servlet.http.HttpServletResponse 接口。response对象所提供的方法。
    1.设定表头的方法
        void addCookie(Cookie cookie)                      新增cookie
        void addDateHeader(String name, long date)         新增long类型的值到name标头
        void addHeader(String name, String value)          新增String类型的值到name标头
        void addIntHeader(String name, int value)          新增int类型的值到name标头
        void setDateHeader(String name, long date)         指定long类型的值到name标头
        void setHeader(String name, String value)          指定String类型的值到name标头
        void setIntHeader(String name, int value)          指定int类型的值到name标头
    2.设定响应状态码的方法
        void sendError(int sc)                             传送状态码(status code)
        void sendError(int sc, String msg)                 传送状态码和错误信息
        void setStatus(int sc)                             设定状态码
    3.用来URL 重写(rewriting)的方法    
        String encodeRedirectURL(String    url)            对使用sendRedirect( )方法的URL予以编码

五、out 对象
    out 对象能把结果输出到网页上。
    out主要是用来控制管理输出的缓冲区(buffer)和输出流(output stream)。
        void clear( )                                      清除输出缓冲区的内容
        void clearBuffer( )                                清除输出缓冲区的内容
        void close( )                                      关闭输出流,清除所有的内容
        int getBufferSize( )                               取得目前缓冲区的大小(KB)
        int getRemaining( )                                取得目前使用后还剩下的缓冲区大小(KB)
        boolean isAutoFlush( )                             回传true表示缓冲区满时会自动清除;false表示不会自动清除并且产生异常处理
        
六、session 对象
    session对象表示目前个别用户的会话(session)状况。
    session对象实现javax.servlet.http.HttpSession接口,HttpSession接口所提供的方法
        long getCreationTime()                             取得session产生的时间,单位是毫秒
        String getId()                                     取得session 的ID
        long getLastAccessedTime()                         取得用户最后通过这个session送出请求的时间
        long getMaxInactiveInterval()                      取得最大session不活动的时间,若超过这时间,session 将会失效
        void invalidate()                                  取消session 对象,并将对象存放的内容完全抛弃
        boolean isNew()                                    判断session 是否为"新"的
        void setMaxInactiveInterval(int    interval)       设定最大session不活动的时间,若超过这时间,session 将会失效

七、application对象
    application对象最常被使用在存取环境的信息。
    因为环境的信息通常都储存在ServletContext中,所以常利用application对象来存取ServletContext中的信息。
    application 对象实现javax.servlet.ServletContext 接口,ServletContext接口容器所提供的方法
        int getMajorVersion( )                             取得Container主要的Servlet API版本
        int getMinorVersion( )                             取得Container次要的Servlet API 版本
        String getServerInfo( )                            取得Container的名称和版本
        String getMimeType(String file)                    取得指定文件的MIME 类型
        ServletContext getContext(String uripath)          取得指定Local URL的Application context
        String getRealPath(String path)                    取得本地端path的绝对路径
        void log(String message)                           将信息写入log文件中
        void log(String message, Throwable throwable)      将stack trace 所产生的异常信息写入log文件中

八、pageContext对象
    pageContext对象能够存取其他隐含对象。
    1.pageContext对象存取其他隐含对象属性的方法,此时需要指定范围的参数。
        Object getAttribute(String name, int scope)
        Enumeration getAttributeNamesInScope(int scope)
        void removeAttribute(String name, int scope)
        void setAttribute(String name, Object value, int scope)
    范围参数有四个,分别代表四种范围:PAGE_SCOPE、REQUEST_SCOPE、SESSION_SCOPE、APPLICATION_SCOPE
    2.PageContext对象取得其他隐含对象的方法
        Exception getException( )                          回传目前网页的异常,不过此网页要为error page,
        JspWriter getOut( )                                回传目前网页的输出流,例如:out 
        Object getPage( )                                  回传目前网页的Servlet 实体(instance),例如:page
        ServletRequest getRequest( )                       回传目前网页的请求,例如:request
        ServletResponse getResponse( )                     回传目前网页的响应,例如:response
        ServletConfig getServletConfig( )                  回传目前此网页的ServletConfig 对象,例如:config
        ServletContext getServletContext( )                回传目前此网页的执行环境(context),例如:application
        HttpSession getSession( )                          回传和目前网页有联系的会话(session),例如:session
    3.PageContext对象提供取得属性的方法
        Object getAttribute(String name, int scope)        回传name 属性,范围为scope的属性对象,回传类型为Object
        Enumeration getAttributeNamesInScope(int scope)    回传所有属性范围为scope 的属性名称,回传类型为Enumeration
        int getAttributesScope(String name)                回传属性名称为name 的属性范围
        void removeAttribute(String name)                  移除属性名称为name 的属性对象
        void removeAttribute(String name, int scope)       移除属性名称为name,范围为scope 的属性对象
        void setAttribute(String name, Object value, int scope)        指定属性对象的名称为name、值为value、范围为scope
        Object findAttribute(String name)                  寻找在所有范围中属性名称为name 的属性对象

九、exception对象
    若要使用exception 对象时,必须在page 指令中设定。<%@ page isErrorPage="true" %>才能使用。
    exception提供的三个方法:
        getMessage( )
        getLocalizedMessage( )、
        printStackTrace(new java.io.PrintWriter(out)) 

原文出处:http://www.javaresearch.org/article/68164.htm

posted @ 2007-10-31 17:07 小眼睛大智慧 阅读(148) | 评论 (0)编辑 收藏