分享
 
 
 

JAVA生成SVG图表实例之柱状图

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

做系统的时候都要配合报表展示一些图表,例如柱状图、饼图还有曲线图,以前是用VML做成通用的,数值的计算和VML在页面上的显示,全部是用javascript做的,可是VML中的文字定位不是特别舒服,javascript计算数字也不是很精确。最近学了几天SVG,觉得不错,所以就想用JAVA生成SVG文件,然后显示,网上没有类似的文章,所以我就把自己的第一个作品放出来,希望大家能够提点向我提电意见,改进。我以后会陆续放做好的饼图、曲线图。我相信这是国内第一个类似的程序,希望能够起到抛砖引玉的作用。

这个程序能够生成单柱柱状图,也能生成多柱柱状图。我的注释写得相当的详细,相信大家都能看的懂。我没有使用APACHE的Batik来生成SVG文件,而是先组成字符串然后生成SVG文件。以下是源码,我做的时候是按照12组数据设计的,当数据少的话,柱子会很宽,我正在改进。

package com.hhh.Paint;

import java.io.File;

import java.io.FileOutputStream;

import java.math.BigDecimal;

/**

* 该类是使用SVG画动态柱状图,只需调用本类的静态方法createSVG(String fileRealPath,String[]

* pData,String[] pDataName)即可,我没有使用APACHE提供的SVG类库来生成SVG文件,只是将所有SVG描述用字符串返回

*

* @author 顾国勇

* @version 1.0

*/

public class PaintHistogram {

/**

* 正值所用的12种颜色

*/

public static String[][] color = { { "#ff2222", "#ffaaaa", "#aa1111" },

{ "#d0b83a", "#f2e692", "#9f8703" },

{ "#ffdf00", "#fef195", "#ce9a31" },

{ "#22FF22", "#aaffaa", "green" },

{ "#ff9e00", "#ffbe08", "#bd7500" },

{ "#799AE1", "#9aabEe", "#5778a1" },

{ "#99c741", "#aef292", "#3e941b" },

{ "#d0b83a", "#f2e692", "#9f8703" },

{ "#319a00", "#66cc00", "#297110" },

{ "#c27f34", "#d6a97b", "#82522b" },

{ "#2222ff", "#aaaaff", "#1111aa" },

{ "#ff2222", "#ffaaaa", "#aa1111" } }; //,{"#99c741","#aef292","#3e941b"}

/**

* 负值所用的颜色

*/

public static String[][] colorNeg = { { "black", "#4F4F4F", "#757575" },

{ "#1A0B0F", "#4F4F4F", "#757575" } };

/**

* 存放转换成double型的数据,用于单例柱形

*/

public static double[] data;

/**

* 存放转换成double型的数据,用于多例柱形

*/

public static double[][] data1;

/**

* 数据总数,默认值为0

*/

public static int dataNum = 0;

/**

* 默认柱状图X轴的起始X值

*/

public static double HistogramXSx = 50.0;

/**

* 默认柱状图X轴的Y值

*/

public static double HistogramXy = 440.0;

/**

* 将元数据从String转换成double,用于单例柱形

*

* @param data

* 字符串数组形式的元数据

* @return 转换好的数据

*/

static double[] convertDataToDouble(String[] data) {

double[] dData = new double[data.length];

dataNum = 0;

for (int i = 0; i < data.length; i++) {

if (data[i] != null && !data[i].equalsIgnoreCase("")

&& !data[i].equalsIgnoreCase(" ")) {

dData[i] = Double.valueOf(data[i]).doubleValue();

dataNum += 1;

} else {

dData[i] = new Double(0.0).doubleValue();

dataNum += 1;

}

}

return dData;

}

/**

* 将元数据从String转换成double,用于多例柱形

*

* @param data

* 字符串数组形式的元数据

* @return 转换好的数据

*/

static double[][] convertDataToDouble(String[][] data) {

double[][] dData = new double[data.length][data[0].length];

dataNum = 0;

for (int i = 0; i < data.length; i++) {

for (int j = 0; j < data[i].length; j++) {

if (data[i][j] != null && !data[i][j].equalsIgnoreCase("")

&& !data[i][j].equalsIgnoreCase(" ")) {

dData[i][j] = Double.parseDouble(data[i][j]);//Double.valueOf(data[i][j]).doubleValue();

dataNum += 1;

} else {

dData[i][j] = new Double(0.0).doubleValue();

dataNum += 1;

}

}

}

return dData;

}

/**

* @param fileRealPath

* 指定的SVG文件的全路径,用于单例柱形

* @param pData

* 元数据数组

* @param pDataName

* 元数据数组名称

*/

public static void createSVG(String fileRealPath, String[] pData,

String[] pDataName) throws Exception {

String sFile = paint(pData, pDataName);

try {

byte[] byteFil = sFile.getBytes("UTF-8");

File svgFile = new File(fileRealPath);

if (svgFile.exists()) {

svgFile.delete();

}

FileOutputStream fos = new FileOutputStream(svgFile);

fos.write(byteFil);

fos.close();

} catch (Exception ex) {

System.out.print(ex.getMessage());

}

}

/**

* @param fileRealPath

* 指定的SVG文件的全路径,用于多例柱形

* @param pData

* 元数据数组

* @param pDataName

* 元数据数组名称

*/

public static void createSVG(String fileRealPath, String[][] pData,

String[] pDataName) {

try {

String sFile = paint(pData, pDataName);

byte[] byteFil = sFile.getBytes("UTF-8");

File svgFile = new File(fileRealPath);

if (svgFile.exists()) {

svgFile.delete();

}

FileOutputStream fos = new FileOutputStream(svgFile);

fos.write(byteFil);

fos.close();

} catch (Exception ex) {

System.out.print("createSVG:" + ex.getMessage());

}

}

/**

* 根据原始数据过滤出最大最小值,已经考虑了数据正负的各种情况(全是正数,全是负数,有正有负),如果元数据中最大值或最小值的绝对值是小于1的,那最后返回的相应值的绝对值是1;

* 如果最大或最小的绝对值小于100,那最后返回的相应值的绝对值是10的倍数;如果最大或最小的绝对值大于100,那最后返回的相应值的绝对值是50的倍数

*

* @param data

* <B>原始数据 </B>

* @return <B>包含绝对值最大的值 </B>

*/

static double getValueMax(double[] pData) {

//存放所有数据值

double max = pData[0];

double min = pData[0];

for (int i = 0; i < pData.length; i++) {

if (pData[i] > max)

max = pData[i];

if (pData[i] < min)

min = pData[i];

}

//全是负的

if (max <= 0 && min < 0) {

min = Math.floor(min);

double tMin = Math.abs(min);

max = 0;

if (tMin <= 1)

min = -1;

else {

min = -(tMin < 100 ? Math.ceil(tMin / 10) * 10 : Math

.ceil(tMin / 50) * 50);

}

}

//有正有负

if (min < 0 && max > 0) {

max = Math.ceil(max);

if (max <= 1)

max = 1;

else {

max = max < 100 ? Math.ceil(max / 10) * 10 : Math

.ceil(max / 50) * 50;

}

min = Math.abs(Math.floor(min));

if (min <= 1)

min = -1;

else {

min = -(min < 100 ? Math.ceil(min / 10) * 10 : Math

.ceil(min / 50) * 50);

}

}

//全是正的

if (min >= 0 && max >= 0) {

if (max == 0.0) {

max = min = 0.0;

} else {

if (max <= 1)

max = 1;

else {

max = max < 100 ? Math.ceil(max / 10) * 10 : Math

.ceil(max / 50) * 50;

}

}

}

double absMax = Math.abs(max) > Math.abs(min) ? Math.abs(max) : Math

.abs(min);

return absMax;

}

/**

* 类初始化,主要是声明了文件开头和滤镜,对于画正负Y轴都通用

*

* @return SVG文件头

*/

static String initialize() {

//文件头声明

StringBuffer sFile = new StringBuffer();

sFile.append("<?xml version='1.0' encoding='UTF-8'?>");

sFile.append("\n");

sFile

.append("<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN' 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>");

sFile.append("\n");

sFile

.append("<svg width='700' height='500' viewBox='0 0 700 500' xmlns='http://www.w3.org/2000/svg'>");

sFile.append("\n");

sFile.append("<desc>Histogram</desc>");

sFile.append("\n");

sFile

.append("<rect x='0' y='0' width='700' height='500' fill='none' stroke='blue' stroke-width='1'/>");

sFile.append("\n");

//定义滤镜和箭头

sFile.append("<defs>");

sFile.append("\n");

sFile

.append(" <marker id='Triangle' viewBox='0 0 10 10' refX='0' refY='5' markerUnits='strokeWidth' markerWidth='4' markerHeight='10' orient='auto'>");

sFile.append("\n");

sFile.append(" <path d='M 0 0 L 10 5 L 0 10 z'/>");

sFile.append("\n");

sFile.append(" </marker>");

sFile.append("\n");

sFile.append(" <linearGradient id='linear1'>");

sFile.append("\n");

sFile.append(" <stop offset='0%' stop-color='red'/>");

sFile.append("\n");

sFile.append(" <stop offset='30%' stop-color='white'/>");

sFile.append("\n");

sFile.append(" <stop offset='100%' stop-color='blue'/>");

sFile.append("\n");

sFile.append(" </linearGradient>");

sFile.append("\n");

sFile

.append(" <filter id='MyFilter' filterUnits='userSpaceOnUse' x='0' y='0' width='100%' height='100%'>");

sFile.append("\n");

sFile

.append(" <feGaussianBlur in='SourceAlpha' stdDeviation='3' result='blur'/>");

sFile.append("\n");

sFile

.append(" <feOffset in='blur' dx='4' dy='4' result='offsetBlur'/>");

sFile.append("\n");

sFile.append("\n");

sFile

.append(" <feSpecularLighting in='blur' surfaceScale='.5' specularConstant='2.5' specularExponent='128' lighting-color='#bbbbbb' result='specOut'>");

sFile.append("\n");

sFile.append(" <fePointLight x='-600' y='-100' z='2200'/>");

sFile.append("\n");

sFile.append(" </feSpecularLighting>");

sFile.append("\n");

sFile

.append(" <feComposite in='specOut' in2='SourceAlpha' operator='in' result='specOut'/>");

sFile.append("\n");

sFile

.append(" <feComposite in='SourceGraphic' in2='specOut' operator='arithmetic' k1='0' k2='1' k3='1' k4='0' result='litPaint'/>");

sFile.append("\n");

sFile.append(" <feMerge>");

sFile.append("\n");

sFile.append(" <feMergeNode in='offsetBlur'/>");

sFile.append("\n");

sFile.append(" <feMergeNode in='litPaint'/>");

sFile.append("\n");

sFile.append(" </feMerge>");

sFile.append("\n");

sFile.append(" </filter>");

sFile.append("\n");

sFile.append("<script type=\"text/javascript\">");

sFile.append("\n");

sFile.append("<![CDATA[");

sFile.append("\n");

sFile.append(" function showValue(value) {");

sFile.append("\n");

sFile.append(" alert(value);");

sFile.append("\n");

sFile.append(" }");

sFile.append("\n");

sFile.append("]]>");

sFile.append("\n");

sFile.append("</script>");

sFile.append("\n");

sFile.append("</defs>");

sFile.append("\n");

//X轴

sFile

.append("<line x1='50' y1='440' x2='660' y2='440' stroke-width='2' stroke-opacity='.4' fill='blue' stroke='#333300' />");

sFile.append("\n");

//Y轴

sFile

.append("<line x1='50' y1='440' x2='50' y2='40' stroke-width='2' stroke-opacity='.4' fill='blue' stroke='#333300' />");

sFile.append("\n");

sFile

.append("<line x1='60' y1='30' x2='60' y2='430' stroke-width='0.5' stroke='#333300'/>");

/* Y轴刻度线 */

for (int i = 0; i < 11; i++) {

double tempY1 = 40 + i * 40.0; //每根Y轴刻度线的起始位置

sFile.append("<line x1='50' y1='" + tempY1 + "' x2='60' y2='"

+ (tempY1 - 10) + "' stroke-width='1' stroke='#333300'/>");

sFile.append("<line x1='60' y1='" + (tempY1 - 10)

+ "' x2='670' y2='" + (tempY1 - 10)

+ "' stroke='#333300'/>");

sFile.append("\n");

// sFile.append("<polygon points='50," + tempY1 + " 660," + tempY1

// + " 670," + (tempY1 - 10) + " 60," + (tempY1 - 10)

// + " ' ");

// sFile

// .append("

// style='fill:rgb(37,170,219);stroke:rgb(37,170,219);stroke-width:1;fill-opacity:0.3;stroke-opacity:0;opacity:0.8'/>");

// sFile.append("\n");

}

return sFile.toString();

}

/**

* @param args

*/

public static void main(String[] args) {

try {

String[][] data = { { "54", "44", "122" }, { "", "12", "-468" },

{ "78", "520", "10" } };

String[] data1 = { "54", "4424.225", "12200" ,"-4658" };

String[] dataName = { "营业所一", "二二二二", "III" };

PaintHistogram.createSVG("d:\\t1.svg", data, dataName);

PaintHistogram.createSVG("d:\\t2.svg", data1, dataName);

} catch (Exception ex) {

System.out.print(ex.getMessage());

}

}

/**

* 画出柱状及刻度等,适用于X轴每格都只显示一个柱型

*

* @param pData

* 元数据

* @param pDataName

* 元数据的名称

*/

static String paint(String[] pData, String[] pDataName) throws Exception {

StringBuffer sFile = new StringBuffer();

data = convertDataToDouble(pData);

double valueMM = getValueMax(data); //取得最大值

sFile.append(initialize());

//X Y步长

double yStep = 400.0 / valueMM;

double xStep = (new BigDecimal(600 / pDataName.length).setScale(3,

BigDecimal.ROUND_HALF_UP)).doubleValue();

double colWidth = xStep / 2; //柱型的宽度

double colWidthPre = xStep / 4; //每个单元格起始与柱型的距离

double colWidthNext = xStep / 4; //每个单元格结束与柱型的距离

/* x轴刻度线 */

for (int i = 0; i < pDataName.length; i++) {

double tempX1 = i == 0 ? HistogramXSx + i * xStep : HistogramXSx

+ i * xStep + 10; //每根X轴刻度线的起始位置

//double tempX2 = tempX1 + colWidthPre; //每根柱状的起始位置

sFile.append("<line x1='" + tempX1 + "' y1='" + HistogramXy

+ "' x2='" + (tempX1 + 10.0) + "' y2='"

+ (HistogramXy - 10)

+ "' stroke-width='1' stroke='#333300'/>");

sFile.append("\n");

}

/* 画Y轴刻度值 */

int maxValue = (int) valueMM;

int valueX; //Y轴刻度值起始的X坐标

String fontsize = "";

if (maxValue > 10000) {

valueX = 5;

fontsize = "11px";

} else {

valueX = 15;

fontsize = "13px";

}

for (int i = 0; i < 10; i++) {

sFile.append("<text x='" + valueX + "' y='" + (45 + i * 40)

+ "' fill='black' font-family='Verdana' font-size='"

+ fontsize + "'>");

sFile.append("\n");

sFile.append((maxValue / 10) * (10 - i));

sFile.append("\n");

sFile.append("</text>");

sFile.append("\n");

}

/* 画矩形 */

sFile.append("<g style='filter:url(#MyFilter)'>");

sFile.append("\n");

for (int i = 0; i < pDataName.length; i++) {

double tempX1 = i == 0 ? HistogramXSx + i * xStep : HistogramXSx

+ i * xStep + 10; //每根X轴刻度线的起始位置

double tempX2 = tempX1 + colWidthPre; //每根柱状的起始位置

double colHeight = Math.ceil(Math.abs(data[i]) * yStep);

double tempY = 440 - colHeight;

String[] colorHistogram; //三个柱面的颜色

if (data[i] < 0) {

colorHistogram = colorNeg[0];

sFile.append(paintHistogram(colWidth, colHeight, tempX2, tempY,

colorHistogram, data[i]));

} else if (data[i] > 0) {

int n = i % color.length;

colorHistogram = color[n];

sFile.append(paintHistogram(colWidth, colHeight, tempX2, tempY,

colorHistogram, data[i]));

}

/* 添加X轴字段说明 字无法在一行内显示的话,需要换行 */

int n = (int) xStep / 13;//每行显示几个字符

int m = pDataName[i].length() % n == 0 ? pDataName[i].length() / n

: (int) (pDataName[i].length() / n + 1);//总共显示几行

int l = 0;//记录总共处理了多少字符

if (m > 1) {

sFile

.append("<text fill='blue' font-size='15px' font-family='STFangsong'>");

for (int j = 0; j < m; j++) {

for (int z = 0; z < n; z++) {

l = j * n + z;

if (l == pDataName[i].length()) {

break;

}

sFile.append("<tspan x='" + (tempX1 + z * 15) + "' y='"

+ (460 + j * 20) + "' >");

sFile.append(pDataName[i].substring(l, l + 1));

sFile.append("</tspan>");

}

if (l == pDataName[i].length()) {

break;

}

}

sFile.append("</text>");

sFile.append("\n");

} else {

sFile

.append("<text x='"

+ (5 + tempX1)

+ "' y='460' fill='blue' font-size='15px' font-family='STFangsong'>"

+ pDataName[i] + "</text>");

sFile.append("\n");

}

}

sFile.append("</g>");

sFile.append("\n");

sFile.append("</svg>");

return sFile.toString();

}

/**

* 画出柱状及刻度等,适用于X轴每格都显示多个柱型

*

* @param pData

* 具体的元数据

* @param pDataName

* 元数据的名称

*/

static String paint(String[][] pData, String[] pDataName) {

StringBuffer sFile = new StringBuffer();

data1 = convertDataToDouble(pData);

//取得最大值

double[] tempValue = new double[dataNum];

for (int i = 0; i < data1.length; i++) {

for (int j = 0; j < data1[i].length; j++) {

tempValue[i * data1[i].length + j] = data1[i][j];

}

}

double valueMM = getValueMax(tempValue);

sFile.append(initialize());

double yStep = 400.0 / valueMM;

double xStep = (new BigDecimal(600 / pDataName.length).setScale(3,

BigDecimal.ROUND_HALF_UP)).doubleValue();

double colWidthSum = xStep / 2; //多个柱型的宽度之和

double colWidthPre = xStep / 4; //每个单元格起始与柱形的距离

double colWidthNext = xStep / 4; //每个单元格结束与柱形的距离

double colWidth = colWidthSum / data1[0].length; //单个柱形的宽度

/* x轴刻度线 */

for (int i = 0; i < pDataName.length; i++) {

double tempX1 = i == 0 ? HistogramXSx + i * xStep : HistogramXSx

+ i * xStep + 10; //每根X轴刻度线的起始位置

//double tempX2 = tempX1 + colWidthPre; //每根柱形的起始位置

sFile.append("<line x1='" + tempX1 + "' y1='" + HistogramXy

+ "' x2='" + (tempX1 + 10.0) + "' y2='"

+ (HistogramXy - 10)

+ "' stroke-width='1' stroke='#333300'/>");

sFile.append("\n");

}

/* 画Y轴刻度值 */

int maxValue = (int) valueMM;

int valueX;//Y轴刻度值起始的X坐标

String fontsize = "";

if (maxValue > 10000) {

valueX = 5;

fontsize = "11px";

} else {

valueX = 15;

fontsize = "13px";

}

for (int i = 0; i < 10; i++) {

sFile.append("<text x='" + valueX + "' y='" + (45 + i * 40)

+ "' fill='black' font-family='Verdana' font-size='"

+ fontsize + "'>");

sFile.append("\n");

sFile.append((maxValue / 10) * (10 - i));

sFile.append("\n");

sFile.append("</text>");

sFile.append("\n");

}

/* 画矩形 */

sFile.append("<g style='filter:url(#MyFilter)'>");

sFile.append("\n");

for (int i = 0; i < pDataName.length; i++) {

//每根X轴刻度线的起始位置

double tempX1 = i == 0 ? HistogramXSx + i * xStep : HistogramXSx

+ i * xStep + 10;

for (int j = 0; j < data1[i].length; j++) {

double tempX2 = tempX1 + colWidthPre + j * colWidth; //每根柱形的起始位置

double colHeight = Math.ceil(Math.abs(data1[i][j]) * yStep);

double tempY = 440 - colHeight;

String[] colorHistogram; //三个柱面的颜色

if (data1[i][j] < 0) {

colorHistogram = colorNeg[0];

sFile.append(paintHistogram(colWidth, colHeight, tempX2,

tempY, colorHistogram, data1[i][j]));

} else if (data1[i][j] > 0) {

int n = j % color.length;

colorHistogram = color[n];

sFile.append(paintHistogram(colWidth, colHeight, tempX2,

tempY, colorHistogram, data1[i][j]));

}

}

/* 添加X轴字段说明 字无法在一行内显示的话,需要换行 */

int n = (int) xStep / 13;//每行显示几个字符

int m = pDataName[i].length() % n == 0 ? pDataName[i].length() / n

: (int) (pDataName[i].length() / n + 1);//总共显示几行

int l = 0;//记录总共处理了多少字符

if (m > 1) {

sFile

.append("<text fill='blue' font-size='15px' font-family='STFangsong'>");

for (int j = 0; j < m; j++) {

for (int z = 0; z < n; z++) {

l = j * n + z;

if (l == pDataName[i].length()) {

break;

}

sFile.append("<tspan x='" + (tempX1 + z * 15) + "' y='"

+ (460 + j * 20) + "' >");

sFile.append(pDataName[i].substring(l, l + 1));

sFile.append("</tspan>");

}

if (l == pDataName[i].length()) {

break;

}

}

sFile.append("</text>");

sFile.append("\n");

} else {

sFile

.append("<text x='"

+ (5 + tempX1)

+ "' y='460' fill='blue' font-size='15px' font-family='STFangsong'>"

+ pDataName[i] + "</text>");

sFile.append("\n");

}

}

sFile.append("</g>");

sFile.append("\n");

sFile.append("</svg>");

return sFile.toString();

}

/**

* 画单一柱形

*

* @param colWidth

* 柱形宽度

* @param colHeight

* 柱形高度

* @param x

* 柱形X值

* @param y

* 柱形Y值

* @param color

* 柱形三个面的颜色

* @param dataValue

* 柱形代表的值

* @return SVG字符串

*/

static String paintHistogram(double colWidth, double colHeight, double x,

double y, String[] color, double dataValue) {

StringBuffer sFile = new StringBuffer();

double dOffset = (colWidth / 3) > 10.0 ? 10.0 : (colWidth / 3);//斜面的偏移量

sFile.append("<rect width='" + colWidth + "' height='" + colHeight

+ "' x='" + x + "' y='" + y + "' fill='" + color[0]

+ "' onclick='showValue(" + dataValue + ")'/>");

sFile.append("\n");

sFile.append("<polygon points='" + x + "," + y + " " + (x + dOffset)

+ "," + (y - dOffset) + " " + (x + dOffset + colWidth) + ","

+ (y - dOffset) + " " + (x + colWidth) + "," + y

+ "' style='fill:" + color[1] + ";stroke:" + color[1]

+ ";stroke-width:1;' onclick='showValue(" + dataValue + ")'/>");

sFile.append("\n");

sFile.append("<polygon points='" + (x + dOffset + colWidth) + ","

+ (y - dOffset) + " " + (x + colWidth) + "," + y + " "

+ (x + colWidth) + "," + 440 + " " + (x + dOffset + colWidth)

+ "," + (y - dOffset + colHeight) + "' style='fill:" + color[2]

+ ";stroke:" + color[2]

+ ";stroke-width:1;' onclick='showValue(" + dataValue + ")'/>");

return sFile.toString();

}

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有