HighChart体验之旅2 - 对HighChart控件的再次封装
上一篇中有关于HighChart的使用,原本思路是通过后台通过对应的类实体中设置一系列的Highchart的参数,不过经过高人@Lave Zhang :http://www.cnblogs.com/lavezhang/ 提点,发现这样其实挺不靠谱的~,毕竟我们在前台使用这个HighChart的最理想状态是什么情况呢?,仔细分析想来,一般业务场景下能用到此类控件无非是为了出报表对应上相应的表格数据做出一份比较详情完整的报表,理想的业务场景是我们开发使用这个东西的时候最好传入数据的接口最好是统一格式,而不必因为你要展示的是图形饼图,混合型图,点阵图亦或是气泡图自己组织成Highchart能识别的图形对应的格式数据,我们只需告诉要传入的X轴数据时什么,Y轴数据时什么,具体数据的来源可能是你通过某个AJAX请求获取到的后台数据源List<TModel>,TModel指代的就是你实际业务中需要展示的一张二维表数据源,疑惑是自己在前台组织的一张二维表数据源都可以,为此,我又决定自己动手丰衣足食了~......也就是说需要封装个jQuery插件再次对Highchart的使用只是提供给相对统一的数据参数接口~当然这是根据现在工作中实际用到的项目需求封装的,再次共享下,分享下思路
插件:
(function ($) { $.extend($.fn, { jDataChart: function (setting) { var options = $.extend({ //需要展示的位置(哪个DOM元素内) renderTo: $(document.body), //图表类型:bar,line,spline,column,pie,area,areaspline,combine,bubble,scatter chartType: "", //图表大标题 title: "", //图表小标题 subtitle: "", //X轴名称 xAxisName: "", //维度列-需要将数据集中的那一列当成维度展现(例如时间,网站,频道等),相当于yAxisColumn中每个Y轴的Key xAxisColumn: "", //Y轴设置,可添加多条Y轴, key-Y轴名称,oppositeOption-true/false(true为right, false为left) yAxisSetting: [{ key: "", oppositeOption: false}], //指标列-需要将数据集中的那几列当成指标来展示(例如Impressiosn,Clicks,UV,CTR等) //key-指标列明,chartType-图形类型,yIndex-每个指标集需要对应的具体Y轴索引 yAxisColumn: [{ key: "", chartType: "", yIndex: 0, color: null}], //图表数据源,是一个JSON的LIST对象,常用的一张二维表数据例如List<TModel>, dataSource: {}, //Point MouseOver事件 mouseOver: function (x, y, category) { }, //Point MouseOver事件 mouseOut: function (x, y, category) { }, //Point MouseOver事件 click: function (x, y, category) { }, //是否开启导出按钮 exportButton: true, //图标容器大小 containerSize:{width:null,height:null}, //图标标题 legend:{} }, setting); options.renderTo = (typeof options.renderTo == "string" ? $(options.renderTo) : options.renderTo); var _renderTo = options.renderTo, _chartType = options.chartType.toString().toLowerCase(), _title = options.title.toString(), _subtitle = options.subtitle.toString(), _xAxisName = options.xAxisName.toString(), _dataSource = options.dataSource, _yAxisSettingSource = options.yAxisSetting, _yAxisColumnSource = options.yAxisColumn, _exportBtn = options.exportButton, _legend = options.legend, _xAxisCategoryVal = [], _xAxisSettingArr = {}, _yAxisSettingArr = [], _yAxisColumnVal = [], _pointX = 0, _pointY = 0, _chartObj = {}, _toolTipArr = {}; var _mouseOverEvent = options.mouseOver, _mouseOutEvent = options.mouseOut, _clickEvent = options.click; //设置X轴(维度),Y轴指标列(根据不同图形类型转化成不同X轴,Y轴数据源) var getChartSetting = function () { _chartType = options.chartType.toString().toLowerCase(); _title = options.title.toString() _subtitle = options.subtitle.toString(); _xAxisName = options.xAxisName.toString(); _yAxisSettingSource = options.yAxisSetting, _yAxisColumnSource = options.yAxisColumn, _toolTipArr = _chartType == "pie" ? { useHTML: false, formatter: function() { var totalY = 0; var s = "" + this.series.name + ": <b>" + Highcharts.numberFormat(this.y, 0) + "</b><br/>百分比:<b>" + this.percentage.toFixed(2) + "%</b>"; return s; } } : { crosshairs: true, shared: true, useHTML: false }; var _xAxisCategoryArr = [], _yAxisSettings = [], _yAxisColumnArr = []; var _xAxisKey = options.xAxisColumn.toString(); //设置X轴数据 $(_dataSource).each(function (index, item) { _xAxisCategoryArr.push(item[_xAxisKey] == undefined ? "" : item[_xAxisKey].toString()); }) _xAxisCategoryVal = _xAxisCategoryArr; //设置Y轴相关信息 $(_yAxisSettingSource).each(function (index, item) { var _tempObj; if (_yAxisSettingSource.length > 1) { _tempObj = { title: { text: item.key }, opposite: item.oppositeOption }; } else { _tempObj = { title: { text: item.key} }; } _yAxisSettings.push(_tempObj); }) _yAxisSettingArr = _yAxisSettings; _xAxisSettingArr = _getXAxisSettingArr(_chartType); //设置Y轴数据 var _tempObj; if (_chartType == "bubble" || _chartType == "scatter") { var maxLength = _chartType == "bubble" ? 3 : 2; $(_dataSource).each(function (idx, dataItem) { var _tempName = dataItem[_xAxisKey] == undefined ? "" : dataItem[_xAxisKey].toString() _tempObj = { name: _tempName, data: [] }; var tempArr = []; $(_yAxisColumnSource).each(function (index, item) { //根据是bubble还是scatter决定添加数据的个数(bubble-3个,scatter-2个) if (index < maxLength) { var pieItemVal = dataItem[item.key.toString()] == undefined ? 0 : parseFloat(dataItem[item.key.toString()]); tempArr.push(pieItemVal); } }) _tempObj.data.push(tempArr); _yAxisColumnArr.push(_tempObj); }); } else { $(_yAxisColumnSource).each(function (index, item) { if (_chartType == "pie") { if (index < 1) { _tempObj = { name: item.key, data: [], type: "pie", allowPointSelect: true, showInLegend: true }; var pieItemName, pieItemVal, pieModel; $(_dataSource).each(function (idx, dataItem) { pieItemName = dataItem[_xAxisKey] == undefined ? "" : dataItem[_xAxisKey].toString(); pieItemVal = dataItem[item.key.toString()] == undefined ? 0 : parseFloat(dataItem[item.key.toString()]); pieModel = { name: pieItemName, y: pieItemVal }; _tempObj.data.push(pieModel); }); _yAxisColumnArr.push(_tempObj); } } else { if (_chartType == "combine" && _yAxisSettingSource.length > 1) { _tempObj = { name: item.key, data: [], type: item.chartType, yAxis: item.yIndex, color: item.color }; } else { _tempObj = { name: item.key, data: [], type: _chartType == "stack" ? "column" : item.chartType, color: item.color }; } $(_dataSource).each(function (idx, dataItem) { var dataVal = dataItem[item.key.toString()] == undefined ? 0 : parseFloat(dataItem[item.key.toString()]); _tempObj.data.push(dataVal); }); _yAxisColumnArr.push(_tempObj); } }); } _yAxisColumnVal = _yAxisColumnArr } //根据类型获取相应设置 var _getXAxisSettingArr = function (type) { var temp; switch (type.toString().toLowerCase()) { case "bubble": { temp = { title: { text: _xAxisName }, startOnTick: false, endOnTick: false }; }; break; case "scatter": { temp = { title: { text: _xAxisName }, startOnTick: false, endOnTick: false }; }; break; default: { temp = { categories: _xAxisCategoryVal, title: { text: _xAxisName }, labels: { rotation: -45, align: 'right', style: { fontSize: '13px', fontFamily: 'Verdana, sans-serif' } } }; }; break; } return temp; } var draw = function () { //画图之前转化相应的XY信息 getChartSetting(); var chart = new Highcharts.Chart({ chart: { renderTo: $(_renderTo).get(0), type: _chartType, width:options.containerSize.width, height:options.containerSize.height, }, title: { text: _title }, subtitle: { text: _subtitle }, tooltip: _toolTipArr, xAxis: _xAxisSettingArr, yAxis: _yAxisSettingArr, legend: _legend, exporting: { enabled:_exportBtn }, credits:{ enabled:false }, plotOptions: { pie: { allowPointSelect: true, cursor: 'pointer', dataLabels: { enabled: true }, showInLegend: true }, column: { stacking: _chartType == "stack" ? "normal" : "" }, scatter: { marker: { radius: 5, symbol: "circle" } }, series: { point: { events: { mouseOver: function () { _mouseOverEvent(this.x, this.y, this.category); }, mouseOut: function () { _mouseOutEvent(this.x, this.y, this.category); }, click: function () { _clickEvent(this.x, this.y, this.category); } } } } }, series: _yAxisColumnVal }); _chartObj = chart; } //获取图表控件基本设置属性 this.getChartOptions = function () { return options; } //设置图标控件基本属性 this.setChartOptions = function (settings) { options.title = settings.title; options.subtitle = settings.subtitle; options.xAxisName = settings.xAxisName; options.yAxisSetting = settings.yAxisSetting; options.yAxisColumn = settings.yAxisColumn; } //获取图表控件对象 this.getChartObject = function () { return _chartObj; } //刷新图表控件 this.refresh = function () { draw(); } //控件初始化事件 this.create = function () { draw(); } return this; } }); })(jQuery)
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %> <%@ Import Namespace="DavidHighChartDemo.Models" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> DataChart封装控件测试 </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2> DataChart封装控件测试</h2> <%= Html.Label("选择日期维度:") %> <br /> <div> <input type="button" id="btnDay" name="btnDay" value="日" typevalue="<%=(int)TimeTypeEnum.Day %>" style="width: 100px;" /> <input type="button" id="btnWeek" name="btnWeek" value="周" typevalue="<%=(int)TimeTypeEnum.Week %>" style="width: 100px;" /> <input type="button" id="btnYear" name="btnYear" value="月" typevalue="<%=(int)TimeTypeEnum.Month %>" style="width: 100px;" /> <%= Html.Hidden("hdDimension", (int)TimeTypeEnum.Day) %> </div> <br /> <%-- <%=Html.Label("请选择报告指标:") %> <div id="metricCheckGroup"> <%=Html.CheckBox("impressions", false, new { @value = "impressions", @class = "mleft3" })%> <%=Html.Label("Impressions") %> <%=Html.CheckBox("clicks", false, new { @value = "clicks", @class = "mleft3" })%><%=Html.Label("Clicks") %> <%=Html.CheckBox("ctr", false, new { @value = "clicks", @class = "mleft3" })%><%=Html.Label("CTR") %> <%=Html.CheckBox("uv", false, new { @value = "uv", @class = "mleft3" })%><%=Html.Label("UV") %> </div>--%> <br /> <%=Html.Label("请选择图标:") %><%=Html.DropDownList("ddlChartType", new List<SelectListItem>() { new SelectListItem() { Text = "混合型", Value=((int)HighchartTypeEnum.混合型).ToString(), Selected=true }, new SelectListItem() { Text = "饼图型", Value=((int)HighchartTypeEnum.饼图型).ToString() }, new SelectListItem() { Text = "多柱状图", Value=((int)HighchartTypeEnum.多柱状图).ToString() }, new SelectListItem() { Text = "多流线图", Value=((int)HighchartTypeEnum.多流线图).ToString() }, new SelectListItem() { Text = "多横柱图", Value=((int)HighchartTypeEnum.多横柱图).ToString() }, new SelectListItem() { Text = "层叠图", Value=((int)HighchartTypeEnum.层叠图).ToString() }, new SelectListItem() { Text = "区域图", Value=((int)HighchartTypeEnum.区域图).ToString() }, new SelectListItem() { Text = "气泡图", Value=((int)HighchartTypeEnum.气泡图).ToString() }, new SelectListItem() { Text = "点阵图", Value=((int)HighchartTypeEnum.点阵图).ToString() } }, null, new { @onchange = "javascript:chartChangeEvent()" })%> <br /> <%= Html.Label("图形控件:") %> <br /> <div id="highChartDiv"> </div> <script language="javascript" type="text/javascript"> $(function () { bindClick(); getChartSource(); }); var dc, chartTypeArr = ["combine", "pie", "column", "spline", "bar", "stack", "areaspline", "bubble", "scatter"]; var bindClick = function () { $("input:button").bind("click", function () { $("#hdDimension").val($(this).attr("typeValue")); getChartSource(); }); $("#metricCheckGroup").find("input:checkbox").bind("click", function () { dc.refresh(); }) }; var getChartSource = function () { var type = chartTypeArr[parseInt($("#ddlChartType").val()) - 1].toString(); var dimensionType = ["ReportDateStr", "ReportWeekStr", "ReportMonthStr"]; var xAxisParamVal = dimensionType[parseInt($("#hdDimension").val()) - 1].toString(); $.ajax({ url: "/DavidTest/GetDataChartSource", type: "post", data: { filterValue: 1, dimensionType: $("#hdDimension").val() }, dataType: "json", success: function (data) { var chartSource = data.chartSource; dc = new $.fn.jDataChart({ renderTo: $("#highChartDiv"), //需要展示的位置(哪个DOM元素内) chartType: type, //图标类型:bar,line,spline,column,pie,area,areaspline,combine,bubble,scatter title: "订单数据报告", //图标大标题 subtitle: "报告详情", //图标小标题 xAxisName: "时间轴", //X轴名称 xAxisColumn: xAxisParamVal, //维度列-需要将数据集中的那一列当成维度展现(例如时间,网站,频道等),相当于yAxisColumn中每个Y轴的Key yAxisSetting: [{ key: "Impressions&Clicks&UV", oppositeOption: false }, { key: "CTR", oppositeOption: true}], //Y轴设置,可添加多条Y轴, key-Y轴名称,oppositeOption-true/false(true为right, false为left) yAxisColumn: [{ key: "Impressions", chartType: "column", yIndex: 0 }, { key: "Clicks", chartType: "column", yIndex: 0 }, { key: "UV", chartType: "column", yIndex: 0 }, { key: "CTR", chartType: "spline", yIndex: 1}], //指标列-需要将数据集中的那几列当成指标来展示(例如Impressiosn,Clicks,UV,CTR等) //key-指标列明,chartType-图形类型,yIndex-每个指标集需要对应的具体Y轴索引 dataSource: chartSource, //图标数据源,是一个JSON的LIST对象,常用的一张二维表数据例如List<TModel> click: function (x, y, category) { alert("x值:" + x + " y值:" + y + " category:" + category); } }); dc.create(); //Y轴设置可以添加多个Y轴,标准格式:[{key: keyVal, oppositeOption:true/false(true为right, false为left) }] // yAxisSetting: [{ key: "Impressions&Clicks&UV", oppositeOption: false }, { key: "CTR", oppositeOption: true}], //指标列添加标准格式为[{key: keyVal, chartType: chartTypeVal, yIndex: yIndexVal }]; // yAxisColumn: [{ key: "Impressions", chartType: "column", yIndex: 0 }, { key: "Clicks", chartType: "column", yIndex: 0 }, { key: "UV", chartType: "column", yIndex: 0 }, { key: "CTR", chartType: "spline", yIndex: 1}], //饼图格式 // yAxisSetting: [{ key: "", oppositeOption: false}], // yAxisColumn: [{ key: "Impressions", chartType: null, yIndex: null }, { key: "Clicks", chartType: null, yIndex: null }, { key: "UV", chartType: null, yIndex: null}], //常见多Series设置 // yAxisSetting: [{ key: "Clicks", oppositeOption: false}]; // yAxisColumn: [{ key: "Clicks", chartType: null, yIndex: null}]; // yAxisSetting: [{ key: "", oppositeOption: false}], // yAxisColumn: [{ key: "Impressions", chartType: null, yIndex: null }, { key: "Clicks", chartType: null, yIndex: null }, { key: "UV", chartType: null, yIndex: null}], }, error: function (XMLHttpRequest, textStatus, errorThrown) { alert(errorThrown); } }); } var chartChangeEvent = function () { var dcOption = dc.getChartOptions(); var type = chartTypeArr[parseInt($("#ddlChartType").val()) - 1].toString(); setChangeChartOptions(dcOption, type); dc.refresh(); } var setChangeChartOptions = function (dcOption, type) { switch (type) { case "combine": { dcOption.chartType = type; dcOption.xAxisName = dcOption.xAxisName; dcOption.yAxisSetting = [{ key: "Impressions&Clicks", oppositeOption: false }, { key: "CTR", oppositeOption: true}]; dcOption.yAxisColumn = [{ key: "Impressions", chartType: "column", yIndex: 0 }, { key: "Clicks", chartType: "column", yIndex: 0 }, { key: "CTR", chartType: "spline", yIndex: 1}]; }; break; case "pie": { dcOption.chartType = type; dcOption.xAxisName = ""; dcOption.yAxisSetting = [{ key: "", oppositeOption: false}]; dcOption.yAxisColumn = [{ key: "Impressions", chartType: null, yIndex: null}]; }; break; case "bubble": { dcOption.chartType = type; dcOption.xAxisName = "Impressions"; dcOption.yAxisSetting = [{ key: "Clicks", oppositeOption: false}]; dcOption.yAxisColumn = [{ key: "Impressions", chartType: null, yIndex: null }, { key: "Clicks", chartType: null, yIndex: null }, { key: "UV", chartType: null, yIndex: null}]; }; break; case "scatter": { dcOption.chartType = type; dcOption.xAxisName = "Impressions"; dcOption.yAxisSetting = [{ key: "Clicks", oppositeOption: false}]; dcOption.yAxisColumn = [{ key: "Impressions", chartType: null, yIndex: null }, { key: "Clicks", chartType: null, yIndex: null}]; }; break; default: { dcOption.chartType = type; dcOption.yAxisSetting = [{ key: "Impressions&Clicks&UV", oppositeOption: false}]; dcOption.yAxisColumn = [{ key: "Impressions", chartType: null, yIndex: null }, { key: "Clicks", chartType: null, yIndex: null }, { key: "UV", chartType: null, yIndex: null}]; }; break; } } </script> </asp:Content>
后台:
/// <summary> /// 获取数据图表的数据源-直接是个List<TMode>的集合队列 /// </summary> /// <returns></returns> public JsonResult GetDataChartSource() { int queryId = Request.Form["filterValue"] == null ? 0 : Convert.ToInt32(Request.Form["filterValue"]); int dimensionType = Request.Form["dimensionType"] == null ? 0 : Convert.ToInt32(Request.Form["dimensionType"]); TimeTypeEnum type = (TimeTypeEnum)Enum.Parse(typeof(TimeTypeEnum), dimensionType.ToString()); IList<ReportDataModel> reportDataLs = new List<ReportDataModel>() { new ReportDataModel(){ ReportId=1, ReportName="David测试订单", ReportDate=new DateTime(2013,4,10), Impressions=10000, Clicks=5513, UV=45241}, new ReportDataModel(){ ReportId=1, ReportName="David测试订单", ReportDate=new DateTime(2013,4,11), Impressions=23121, Clicks=5111, UV=53532}, new ReportDataModel(){ ReportId=1, ReportName="David测试订单", ReportDate=new DateTime(2013,4,12), Impressions=76511, Clicks=4522, UV=34234}, new ReportDataModel(){ ReportId=1, ReportName="David测试订单", ReportDate=new DateTime(2013,4,13), Impressions=96511, Clicks=7362, UV=41133}, new ReportDataModel(){ ReportId=1, ReportName="David测试订单", ReportDate=new DateTime(2013,4,14), Impressions=42231, Clicks=4224, UV=42612}, new ReportDataModel(){ ReportId=1, ReportName="David测试订单", ReportDate=new DateTime(2013,4,15), Impressions=34244, Clicks=6242, UV=51311}, new ReportDataModel(){ ReportId=1, ReportName="David测试订单", ReportDate=new DateTime(2013,4,16), Impressions=86511, Clicks=3424, UV=84322}, new ReportDataModel(){ ReportId=1, ReportName="David测试订单", ReportDate=new DateTime(2013,4,17), Impressions=31311, Clicks=6234, UV=77342}, new ReportDataModel(){ ReportId=1, ReportName="David测试订单", ReportDate=new DateTime(2013,4,18), Impressions=23131, Clicks=7242, UV=61111}, new ReportDataModel(){ ReportId=2, ReportName="David测试订单2", ReportDate=new DateTime(2013,4,19), Impressions=41311, Clicks=3244, UV=72421}, new ReportDataModel(){ ReportId=2, ReportName="David测试订单2", ReportDate=new DateTime(2013,4,10), Impressions=10000, Clicks=5513, UV=45241}, new ReportDataModel(){ ReportId=2, ReportName="David测试订单2", ReportDate=new DateTime(2013,4,11), Impressions=23121, Clicks=5111, UV=53532}, new ReportDataModel(){ ReportId=2, ReportName="David测试订单2", ReportDate=new DateTime(2013,4,12), Impressions=34232, Clicks=4522, UV=34234}, new ReportDataModel(){ ReportId=2, ReportName="David测试订单2", ReportDate=new DateTime(2013,4,13), Impressions=96511, Clicks=7362, UV=41133}, new ReportDataModel(){ ReportId=2, ReportName="David测试订单2", ReportDate=new DateTime(2013,4,14), Impressions=96511, Clicks=4224, UV=42612}, new ReportDataModel(){ ReportId=2, ReportName="David测试订单2", ReportDate=new DateTime(2013,4,15), Impressions=34244, Clicks=6242, UV=51311}, new ReportDataModel(){ ReportId=2, ReportName="David测试订单2", ReportDate=new DateTime(2013,4,16), Impressions=96511, Clicks=3424, UV=84322}, new ReportDataModel(){ ReportId=2, ReportName="David测试订单2", ReportDate=new DateTime(2013,4,17), Impressions=31311, Clicks=6234, UV=77342}, new ReportDataModel(){ ReportId=2, ReportName="David测试订单2", ReportDate=new DateTime(2013,4,18), Impressions=96511, Clicks=7242, UV=61111}, new ReportDataModel(){ ReportId=2, ReportName="David测试订单2", ReportDate=new DateTime(2013,4,19), Impressions=41311, Clicks=3244, UV=72421}, }; if (queryId > 0) reportDataLs = reportDataLs.Where(n => n.ReportId == queryId).ToList<ReportDataModel>(); switch (type) { case TimeTypeEnum.Week: { var result = from item in reportDataLs group item by item.ReportWeek into reportDataWeek select new { ReportWeekStr = reportDataWeek.Key, Impressions = reportDataWeek.Sum(n => n.Impressions), Clicks = reportDataWeek.Sum(n => n.Clicks), CTR = reportDataWeek.Sum(n => n.Clicks) == 0 ? 0 : Math.Round((double)reportDataWeek.Sum(n => n.Clicks) / reportDataWeek.Sum(n => n.Impressions), 6), UV = reportDataWeek.Max(n => n.UV) }; return Json(new { chartSource = result }, JsonRequestBehavior.AllowGet); }; case TimeTypeEnum.Month: { var result = from item in reportDataLs group item by item.ReportYear into reportDataYear select new { ReportMonthStr = reportDataYear.Key, Impressions = reportDataYear.Sum(n => n.Impressions), Clicks = reportDataYear.Sum(n => n.Clicks), CTR = reportDataYear.Sum(n => n.Clicks) == 0 ? 0 : Math.Round((double)reportDataYear.Sum(n => n.Clicks) / reportDataYear.Sum(n => n.Impressions), 6), UV = reportDataYear.Max(n => n.UV) }; return Json(new { chartSource = result }, JsonRequestBehavior.AllowGet); }; default: { var result = reportDataLs; return Json(new { chartSource = reportDataLs }, JsonRequestBehavior.AllowGet); } } }
此文中用到的Highchart控件是最新版本 Highcharts JS v3.0.1 (2013-04-09),大家可自行从官网下载,地址:http://www.highcharts.com/
jQuery版本1.7.2 不过已经尝试,1.4.2也可以支持。
插件可能会有bug还在不断完善中~,希望能给需要用的朋友提供一点启发~
注:本文章中涉及技术共享版权归本人所有,如发现未经允许用作非法用作商业用途,将进行法律追究
如果你觉得这篇文章对你有用,欢迎推荐[推荐]
如果你觉得文章内有错误欢迎指出^0^~
如果您想转载本博客,请注明出处
如果您对本文有意见或者建议,欢迎留言
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则作者保留追究法律责任的权利。