定义一个基础类,用户工作薄的定义
public abstract class BaseGenerateSheet
{
public string SheetName { set; get; }
public IWorkbook Workbook { get; set; }
public virtual void GenSheet(ISheet sheet)
{
}
}
定义列头对象,对列,后续将使用这个对象进行表格顺序和一些列定制
/// <summary>
/// Excel列头的相关设置
/// </summary>
public class ColumnsMapping
{
#region 属性
/// <summary>
/// Excel 列头显示的值
/// </summary>
public string ColumnsText { get; set; }
/// <summary>
/// Excel 列绑定对像的属性, 可以为空
/// </summary>
public string ColumnsData { get; set; }
/// <summary>
/// Excel 列的宽度
/// </summary>
public int Width { get; set; }
/// <summary>
/// 是否需要总计行
/// </summary>
public bool IsTotal { get; set; }
/// <summary>
/// Excel列的索引
/// </summary>
public int ColumnsIndex { get; set; }
/// <summary>
/// 列关联的字段
/// </summary>
public string ColumnsField { get; set; }
#endregion
#region 构造方法
/// <summary>
/// Excel列头的相关设置
/// </summary>
public ColumnsMapping() { }
/// <summary>
/// Excel列头的相关设置
/// </summary>
public ColumnsMapping(string colText, string colData, int width, int colIndex, bool _isTotal)
{
this.ColumnsText = colText;
this.ColumnsData = colData;
this.Width = width;
this.IsTotal = _isTotal;
this.ColumnsIndex = colIndex;
}
#endregion
}
定义工作薄的具体操作的类,通过使用操作 ColumnsMapping 来生成各个单元格
/// <summary>
/// 导出Excel基类
/// 示例:
/// List<T> queryData = new List<T>();
/// GenerateExcel genExcel = new GenerateExcel();
/// genExcel.SheetList.Add(new IdentityCardMonthPayOffSheet(queryData, "sheet1"));
/// genExcel.ExportExcel(saveFilePath);
/// </summary>
public class GenerateSheet<T> : BaseGenerateSheet
{
#region 私有字段
// Excel 显示时间的样式
private ICellStyle dateStyle = null;
// Excel 显示列头的样式
private ICellStyle headStyle = null;
// Excel 显示内容的样式
private ICellStyle contentsStyle = null;
// Excel 显示总计的样式
private ICellStyle totalStyle = null;
// 列头集合
protected List<ColumnsMapping> columnHeadList = null;
// 显示的数据
protected List<T> dataSource;
private List<object> dataSource2;
//属性元数据
IDictionary<string, PropertyInfo> typePropertyInfo = new Dictionary<string, PropertyInfo>();
#endregion
#region 字体
#endregion
#region 属性
/// <summary>
/// Excel 显示时间的样式
/// </summary>
protected ICellStyle DateStyle
{
get { return dateStyle; }
set { dateStyle = value; }
}
/// <summary>
/// Excel 显示列头的样式
/// </summary>
protected ICellStyle HeadStyle
{
get { return headStyle; }
set { headStyle = value; }
}
/// <summary>
/// Excel 显示内容的样式
/// </summary>
protected ICellStyle ContentsStyle
{
get { return contentsStyle; }
set { contentsStyle = value; }
}
/// <summary>
/// Excel 显示总计的样式
/// </summary>
protected ICellStyle TotalStyle
{
get { return totalStyle; }
set { totalStyle = value; }
}
/// <summary>
/// 是否有边框 只读
/// </summary>
protected bool IsBorder { get; private set; }
protected List<ColumnsMapping> ColumnHeadList
{
get { return this.columnHeadList; }
private set { this.columnHeadList = value; }
}
#endregion
#region 构造方法
/// <summary>
/// 导出Excel基类
/// </summary>
/// <param name="_dataSource">Sheet里面显示的数据</param>
public GenerateSheet(List<T> _dataSource)
: this(_dataSource, null, string.Empty, true)
{
}
/// <summary>
/// 导出Excel基类
/// </summary>
/// <param name="_dataSource">Sheet里面显示的数据</param>
public GenerateSheet(List<T> _dataSource, string sheetName)
: this(_dataSource, null, sheetName, true)
{
}
/// <summary>
/// 导出Excel基类
/// </summary>
/// <param name="_dataSource">Sheet里面显示的数据</param>
public GenerateSheet(List<T> _dataSource, List<object> _dataSource2, string sheetName)
: this(_dataSource, _dataSource2, sheetName, true)
{
}
/// <summary>
/// 导出Excel基类
/// </summary>
/// <param name="_dataSource">Sheet里面显示的数据</param>
/// <param name="isBorder">是否有边框</param>
public GenerateSheet(List<T> _dataSource, bool isBorder)
: this(_dataSource, null, string.Empty, isBorder)
{
}
/// <summary>
/// 导出Excel基类
/// </summary>
/// <param name="_dataSource">Sheet里面显示的数据</param>
/// <param name="isBorder">是否有边框</param>
public GenerateSheet(List<T> _dataSource, List<object> _dataSource2, string sheetName, bool isBorder)
{
//if (_dataSource != null && _dataSource.Count > 0)
this.dataSource = _dataSource;
this.dataSource2 = _dataSource2;
//else
// throw new Exception("数据不能为空!");
this.IsBorder = isBorder;
this.SheetName = sheetName;
}
#endregion
#region 可以被重写的方法
/// <summary>
/// 生成Excel的Sheet
/// </summary>
/// <param name="sheet"></param>
public override void GenSheet(ISheet sheet)
{
this.SetSheetContents(sheet);
}
/// <summary>
/// 初始化列头
/// </summary>
/// <returns></returns>
protected virtual List<ColumnsMapping> InitializeColumnHeadData()
{
try
{
List<PropertyInfo> propertyList = this.GetObjectPropertyList();
List<ColumnsMapping> columnsList = new List<ColumnsMapping>();
int colIndex = 0;
foreach (PropertyInfo propertyInfo in propertyList)
{
columnsList.Add(new ColumnsMapping()
{
ColumnsData = propertyInfo.Name,
ColumnsText = propertyInfo.Name,
ColumnsIndex = colIndex,
IsTotal = false,
Width = 15
});
colIndex++;
}
return columnsList;
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 设置列头
/// </summary>
/// <param name="sheet">Excel Sheet</param>
/// <param name="rowIndex">记录Excel最大行的位置,最大值为65535</param>
public virtual void SetColumnHead(ISheet sheet, ref int rowIndex)
{
if (columnHeadList.Count > 0)
{
IRow headerRow = sheet.CreateRow(rowIndex);
foreach (ColumnsMapping columns in columnHeadList)
{
ICell newCell = headerRow.CreateCell(columns.ColumnsIndex);
newCell.SetCellValue(columns.ColumnsText);
newCell.CellStyle = headStyle;
//设置列宽
SetColumnsWidth(sheet, columns.ColumnsIndex, columns.Width);
}
rowIndex++;
}
}
/// <summary>
/// 设置Excel内容
/// </summary>
/// <param name="sheet"></param>
/// <param name="dataSource"></param>
/// <param name="rowIndex"></param>
protected virtual void SetSheetContents(ISheet sheet, List<T> dataSource, ref int rowIndex)
{
if (dataSource != null)
{
foreach (T value in dataSource)
{
#region 填充内容
IRow dataRow = sheet.CreateRow(rowIndex);
int colIndex = 0;
foreach (ColumnsMapping columns in columnHeadList)
{
if (columns.ColumnsIndex >= 0)
{
if (columns.ColumnsIndex >= colIndex)
colIndex = columns.ColumnsIndex;
else
columns.ColumnsIndex = colIndex;
ICell newCell = dataRow.CreateCell(colIndex);
string drValue = string.Empty;
if (!string.IsNullOrEmpty(columns.ColumnsData))
drValue = GetModelValue(columns.ColumnsData, value);
SetCellValue(newCell, rowIndex, drValue, columns);
colIndex++;
}
}
#endregion
rowIndex++;
}
//rowIndex++;
}
}
/// <summary>
/// 设置单元格的数据
/// </summary>
/// <param name="cell">单元格对像</param>
/// <param name="rowIndex">单元格行索引</param>
/// <param name="drValue">单元格数据</param>
/// <param name="columns">单元格的列信息</param>
protected virtual void SetCellValue(ICell cell, int rowIndex, string drValue, ColumnsMapping columns)
{
cell.CellStyle = contentsStyle;
if (!string.IsNullOrEmpty(columns.ColumnsData))
{
PropertyInfo info= typePropertyInfo[columns.ColumnsData];
if (Nullable.GetUnderlyingType(info.PropertyType) != null)
{
cell.SetCellValue(drValue);
}
else
{
switch (info.PropertyType.FullName)
{
case "System.String": //字符串类型
double result;
if (IsNumeric(drValue, out result))
{
double.TryParse(drValue, out result);
cell.SetCellValue(result);
break;
}
else
{
cell.SetCellValue(drValue);
break;
}
case "System.DateTime": //日期类型
if (string.IsNullOrEmpty(drValue) || drValue == "0001/1/1 0:00:00")
{
cell.SetCellValue("");
}
else
{
DateTime.TryParse(drValue, out DateTime dateV);
cell.SetCellValue(dateV);
cell.CellStyle = dateStyle; //格式化显示
}
break;
case "System.Boolean": //布尔型
bool boolV = false;
bool.TryParse(drValue, out boolV);
cell.SetCellValue(boolV);
break;
case "System.Int16": //整型
case "System.Int32":
case "System.Byte":
int intV = 0;
int.TryParse(drValue, out intV);
cell.SetCellValue(intV);
break;
case "System.Int64":
cell.SetCellValue(drValue.ToString());
break;
case "System.Decimal": //浮点型
case "System.Double":
double doubV = 0;
double.TryParse(drValue, out doubV);
cell.SetCellValue(doubV);
break;
case "System.DBNull": //空值处理
cell.SetCellValue("");
break;
default:
cell.SetCellValue("");
break;
}
}
}
else
{
cell.SetCellValue("");
}
}
/// <summary>
/// 设置总计单元格的数据
/// </summary>
/// <param name="cell">总计单元格</param>
/// <param name="rowIndex">当前行的索引</param>
/// <param name="startRowIndex">内容数据的开始行</param>
/// <param name="columns">当前列信息</param>
protected virtual void SetTotalCellValue(ICell cell, int rowIndex, int startRowIndex, ColumnsMapping columns)
{
if (columns.IsTotal)
{
string colItem = CellReference.ConvertNumToColString(columns.ColumnsIndex);
cell.CellStyle = totalStyle;
cell.SetCellFormula(string.Format("SUM({0}{1}:{2}{3})", colItem, startRowIndex, colItem, rowIndex));
}
}
/// <summary>
/// 在所有数据最后添加总计,当然也可以是其它的公式
/// </summary>
/// <param name="sheet">工作薄Sheet</param>
/// <param name="rowIndex">当前行</param>
/// <param name="startRowIndex">内容开始行</param>
protected virtual void SetTotal(ISheet sheet, ref int rowIndex, int startRowIndex)
{
if (rowIndex > startRowIndex)
{
IRow headerRow = sheet.CreateRow(rowIndex) as IRow;
foreach (ColumnsMapping columns in columnHeadList)
{
ICell newCell = headerRow.CreateCell(columns.ColumnsIndex);
SetTotalCellValue(newCell, rowIndex, startRowIndex, columns);
}
}
}
/// <summary>
/// 数据源2
/// </summary>
/// <param name="sheet">工作薄Sheet</param>
/// <param name="rowIndex">当前行</param>
protected virtual void SetToSecond(ISheet sheet, ref int rowIndex, List<object> dataSource2)
{
}
#endregion
#region 公共方法
/// <summary>
/// 获取属性名字
/// </summary>
/// <param name="expr"></param>
/// <returns></returns>
protected string GetPropertyName(Expression<Func<T, object>> expr)
{
var rtn = "";
if (expr.Body is UnaryExpression)
{
rtn = ((MemberExpression)((UnaryExpression)expr.Body).Operand).Member.Name;
}
else if (expr.Body is MemberExpression)
{
rtn = ((MemberExpression)expr.Body).Member.Name;
}
else if (expr.Body is ParameterExpression)
{
rtn = ((ParameterExpression)expr.Body).Type.Name;
}
return rtn;
}
protected void SetColumnsWidth(ISheet sheet, int colIndex, int width)
{
//设置列宽
sheet.SetColumnWidth(colIndex, width * 256);
}
#endregion
#region 私有方法
private void SetSheetContents(ISheet sheet)
{
if (sheet != null)
{
// 初始化相关样式
this.InitializeCellStyle();
// 初始化列头的相关数据
this.columnHeadList = InitializeColumnHeadData();
// 当前行
int rowIndex = 0;
// 设置列头
this.SetColumnHead(sheet, ref rowIndex);
// 内容开始行
int startRowIndex = rowIndex;
// 初始化属性元数据
this.InitPropertyInfo();
// 设置Excel内容
this.SetSheetContents(sheet, dataSource, ref rowIndex);
// 在所有数据最后添加总计,当然也可以是其它的公式
if (dataSource.Count > 0)
{
this.SetTotal(sheet, ref rowIndex, startRowIndex);
}
this.SetToSecond(sheet, ref rowIndex, dataSource2);
}
}
/// <summary>
/// 初始化相关对像
/// </summary>
protected virtual void InitializeCellStyle()
{
columnHeadList = new List<ColumnsMapping>();
// 初始化Excel 显示时间的样式
dateStyle = this.Workbook.CreateCellStyle();
IDataFormat dateformat = this.Workbook.CreateDataFormat();
dateStyle.DataFormat = dateformat.GetFormat("yyyy-mm-dd");
if (this.IsBorder)
{
//有边框
dateStyle.BorderBottom = BorderStyle.Thin;
dateStyle.BorderLeft = BorderStyle.Thin;
dateStyle.BorderRight = BorderStyle.Thin;
dateStyle.BorderTop = BorderStyle.Thin;
}
// 初始化Excel 列头的样式
headStyle = this.Workbook.CreateCellStyle();
headStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Left;// 文本居左
IFont font = this.Workbook.CreateFont();
font.FontHeightInPoints = 12; // 字体大小
font.Boldweight = 700; // 字体加粗
headStyle.SetFont(font);
if (this.IsBorder)
{
//有边框
headStyle.BorderBottom = BorderStyle.Thin;
headStyle.BorderLeft = BorderStyle.Thin;
headStyle.BorderRight = BorderStyle.Thin;
headStyle.BorderTop = BorderStyle.Thin;
}
// 初始化Excel 显示内容的样式
contentsStyle = this.Workbook.CreateCellStyle();
font = this.Workbook.CreateFont();
font.FontHeightInPoints = 10;
contentsStyle.SetFont(font);
if (this.IsBorder)
{
//有边框
contentsStyle.BorderBottom = BorderStyle.Thin;
contentsStyle.BorderLeft = BorderStyle.Thin;
contentsStyle.BorderRight = BorderStyle.Thin;
contentsStyle.BorderTop = BorderStyle.Thin;
}
IDataFormat textformat = this.Workbook.CreateDataFormat();
contentsStyle.DataFormat = textformat.GetFormat("text");
// 初始化Excel 显示总计的样式
totalStyle = this.Workbook.CreateCellStyle();
font = this.Workbook.CreateFont();
font.Boldweight = 700;
font.FontHeightInPoints = 10;
totalStyle.SetFont(font);
if (this.IsBorder)
{
//有边框
totalStyle.BorderBottom = BorderStyle.Thin;
totalStyle.BorderLeft = BorderStyle.Thin;
totalStyle.BorderRight = BorderStyle.Thin;
totalStyle.BorderTop = BorderStyle.Thin;
}
}
/// <summary>
/// 获取 T 对像的所有属性
/// </summary>
/// <returns></returns>
private List<PropertyInfo> GetObjectPropertyList()
{
List<PropertyInfo> result = new List<PropertyInfo>();
Type t = typeof(T);
if (t != null)
{
PropertyInfo[] piList = t.GetProperties();
foreach (var pi in piList)
{
//if (!pi.PropertyType.IsGenericType)
//{
result.Add(pi);
//}
}
}
return result;
}
/// <summary>
/// 初始化字段属性元数据
/// </summary>
public void InitPropertyInfo()
{
List<PropertyInfo> property = GetObjectPropertyList();
foreach (PropertyInfo info in property)
{
typePropertyInfo.Add(info.Name, info);
}
}
protected string GetModelValue(string FieldName, object obj)
{
try
{
if (typePropertyInfo.ContainsKey(FieldName))
{
object o = typePropertyInfo[FieldName].GetValue(obj, null);
string Value = Convert.ToString(o);
if (string.IsNullOrEmpty(Value)) return null;
return Value;
}
return null;
}
catch
{
return null;
}
}
/// <summary>
/// 判断是否为一个数字并反回值
/// </summary>
/// <param name="message"></param>
/// <param name="result"></param>
/// <returns></returns>
private bool IsNumeric(String message, out double result)
{
if (!string.IsNullOrEmpty(message))
{
Regex rex = new Regex(@"^[-]?\d+[.]?\d*$");
result = -1;
if (rex.IsMatch(message))
{
result = double.Parse(message);
return true;
}
else
return false;
}
else
{
result = 0;
return false;
}
}
#endregion
}
基础部分的类已经定义完成,下面的类用户通过基础类来生成excel文件
/// <summary>
/// 使用方法
/// GenerateExcel genExcel = new GenerateExcel();
/// genExcel.SheetList.Add(new ClueDealExportSheet(list, "sheet1"));
/// 其中 ClueDealExportSheet : GenerateSheet<Tmodel>
/// genExcel.ExportExcel(saveFilePath); 保存到指定位置
/// </summary>
public class GenerateExcel
{
#region 私有字段
protected XSSFWorkbook workbook = null;
#endregion
#region 属性
/// <summary>
/// Excel的Sheet集合
/// </summary>
List<BaseGenerateSheet> SheetList { get; set; }
#endregion
#region 构造方法
public GenerateExcel()
{
InitializeWorkbook();
}
#endregion
#region 私有方法
/// <summary>
/// 初始化相关对像
/// </summary>
private void InitializeWorkbook()
{
workbook = new XSSFWorkbook();
SheetList = new List<BaseGenerateSheet>();
#region 右击文件 属性信息
//DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation();
//dsi.Company = "http://www.kjy.cn";
//workbook.DocumentSummaryInformation = dsi;
//SummaryInformation si = PropertySetFactory.CreateSummaryInformation();
//si.Author = "百望股份有限公司"; //填加xls文件作者信息
//si.ApplicationName = "百望股份有限公司"; //填加xls文件创建程序信息
//si.LastAuthor = "百望股份有限公司"; //填加xls文件最后保存者信息
//si.Comments = "百望股份有限公司"; //填加xls文件作者信息
//si.Title = "百望股份有限公司"; //填加xls文件标题信息
//si.Subject = "百望股份有限公司"; //填加文件主题信息
//si.CreateDateTime = DateTime.Now;
//workbook.SummaryInformation = si;
#endregion
}
/// <summary>
/// 生成Excel并返回内存流
/// </summary>
/// <returns></returns>
private void ExportExcel()
{
//foreach (BaseGenerateSheet sheet in SheetList)
//{
// ISheet sh = null;
// if (string.IsNullOrEmpty(sheet.SheetName))
// sh = workbook.CreateSheet();
// else
// sh = workbook.CreateSheet(sheet.SheetName);
// sheet.Workbook = this.workbook;
// sheet.GenSheet(sh); //生成Sheet(生成sheet内容)
//}
//using (MemoryStream ms = new MemoryStream())
//{
// workbook.Write(ms);
// ms.Flush();
// ms.Position = 0;
// return ms;
//}
}
#endregion
#region 公共方法
/// <summary>
/// 添加一个sheet
/// </summary>
/// <param name="sheet"></param>
public void AddSheet(BaseGenerateSheet sheet)
{
sheet.Workbook = this.workbook; ;
this.SheetList.Add(sheet);
}
public List<BaseGenerateSheet> GetSheets()
{
return this.SheetList;
}
/// <summary>
/// 导出到Excel文件
/// </summary>
/// <param name="strFileName">保存位置</param>
public void ExportExcel(string strFileName)
{
try
{
foreach (BaseGenerateSheet sheet in SheetList)
{
ISheet sh = null;
if (string.IsNullOrEmpty(sheet.SheetName))
sh = workbook.CreateSheet();
else
sh = workbook.CreateSheet(sheet.SheetName);
//sheet.Workbook = this.workbook;
sheet.GenSheet(sh); //生成数据到Sheet(生成sheet内容)
}
using (MemoryStream ms = new MemoryStream())
{
workbook.Write(ms);
if (!Directory.Exists(Path.GetDirectoryName(strFileName)))
{
Directory.CreateDirectory(Path.GetDirectoryName(strFileName));
}
using (FileStream fs = new FileStream(strFileName, FileMode.Create, FileAccess.Write))
{
byte[] data = ms.ToArray();
fs.Write(data, 0, data.Length);
fs.Flush();
}
}
}
catch (Exception ex)
{
throw;
}
}
#endregion
}
业务方需要自定义一个 Sheet 的处理程序,继承GenerateSheet<TModel>,需要实现 InitializeColumnHeadData,此方法用户配置列头的顺序
public class ModelExportSheet : GenerateSheet<TModel>
{
private ICellStyle textStyle = null;
public ModelExportSheet(List<TModel> dataSource, string sheetName)
: base(dataSource, sheetName)
{
}
protected override List<ColumnsMapping> InitializeColumnHeadData()
{
List<ColumnsMapping> result = new List<ColumnsMapping>();
result.Add(new ColumnsMapping()
{
ColumnsText = "ID",
ColumnsData = GetPropertyName(p => p.Id),
ColumnsIndex = 0,
IsTotal = false,
Width = 15
});
result.Add(new ColumnsMapping()
{
ColumnsText = "手机",
ColumnsData = GetPropertyName(p => p.Phone),
ColumnsIndex = 1,
IsTotal = false,
Width = 15
});
return result;
}
public override void SetColumnHead(ISheet sheet, ref int rowIndex)
{
base.SetColumnHead(sheet, ref rowIndex);
if (this.ColumnHeadList.Count > 0)
{
// 所有列头居中
this.HeadStyle.Alignment = HorizontalAlignment.Center;
this.HeadStyle.VerticalAlignment = VerticalAlignment.Center;
}
}
private ICellStyle InvalidDataStyle = null; //无效数据行的样式
private ICellStyle RefundDataStyle = null; //退款数据行的样式
private IFont LinkFontStyle = null; //链接字体设置
protected override void InitializeCellStyle()
{
base.InitializeCellStyle();
this.ContentsStyle.Alignment = HorizontalAlignment.Right;
this.InvalidDataStyle = base.Workbook.CreateCellStyle();
InvalidDataStyle.FillForegroundColor = IndexedColors.Grey25Percent.Index;
InvalidDataStyle.FillPattern = FillPattern.SolidForeground;
this.RefundDataStyle = base.Workbook.CreateCellStyle();
RefundDataStyle.FillForegroundColor = IndexedColors.Red.Index;
RefundDataStyle.FillPattern = FillPattern.SolidForeground;
this.LinkFontStyle = base.Workbook.CreateFont();
LinkFontStyle.Color = IndexedColors.Blue.Index;
}
protected override void SetSheetContents(ISheet sheet, List<TModel> dataSource, ref int rowIndex)
{
if (dataSource != null)
{
foreach (TModel value in dataSource)
{
#region 填充内容
IRow dataRow = sheet.CreateRow(rowIndex);
int colIndex = 0;
foreach (ColumnsMapping columns in columnHeadList)
{
if (columns.ColumnsIndex >= 0)
{
if (columns.ColumnsIndex >= colIndex)
colIndex = columns.ColumnsIndex;
else
columns.ColumnsIndex = colIndex;
ICell newCell = dataRow.CreateCell(colIndex);
newCell.CellStyle = textStyle; //格式化显示
string drValue = string.Empty;
if (!string.IsNullOrEmpty(columns.ColumnsData))
drValue = GetModelValue(columns.ColumnsData, value);
SetCellValue(newCell, rowIndex, drValue, columns);
colIndex++;
}
}
#endregion
rowIndex++;
}
}
}
}
调用方法: GenerateExcel genExcel = new GenerateExcel();
genExcel.SheetList.Add(new ModelExportSheet(list, "sheet1"));
//其中 ModelExportSheet: GenerateSheet<Tmodel>
genExcel.ExportExcel(saveFilePath); //保存到指定位置
本文内容搬运自:https://blog.csdn.net/weixin_34124577/article/details/86209793
本文链接:https://blog.nnwk.net/article/1570
有问题请留言。版权所有,转载请在显眼位置处保留文章出处,并留下原文连接
Leave your question and I'll get back to you as soon as I see it. All rights reserved. Please keep the source and links
友情链接:
全部评论