hqchart
Version:
HQChart - H5, 微信小程序 沪深/港股/数字货币/期货/美股 K线图(kline),走势图,缩放,拖拽,十字光标,画图工具,截图,筹码图. 分析家语法,通达信语法,(麦语法),第3方数据对接
1,453 lines (1,198 loc) • 127 kB
JavaScript
/*
Copyright (c) 2018 jones
http://www.apache.org/licenses/LICENSE-2.0
开源项目 https://github.com/jones2000/HQChart
jones_2000@163.com
封装T型报价列表控件 (H5版本)
不提供内置测试数据
*/
function JSTReportChart(divElement)
{
this.DivElement=divElement;
this.JSChartContainer; //表格控件
this.ResizeListener; //大小变动监听
//h5 canvas
this.CanvasElement=document.createElement("canvas");
this.CanvasElement.className='jstreport-drawing';
this.CanvasElement.id=Guid();
this.CanvasElement.setAttribute("tabindex",0);
if (this.CanvasElement.style) this.CanvasElement.style.outline='none';
if(divElement.hasChildNodes())
{
JSConsole.Chart.Log("[JSTReportChart::JSReportChart] divElement hasChildNodes", divElement.childNodes);
}
divElement.appendChild(this.CanvasElement);
this.OnSize=function()
{
//画布大小通过div获取 如果有style里的大小 使用style里的
var height=this.DivElement.offsetHeight;
var width=this.DivElement.offsetWidth;
if (this.DivElement.style.height && this.DivElement.style.width)
{
if (this.DivElement.style.height.includes("px"))
height=parseInt(this.DivElement.style.height.replace("px",""));
if (this.DivElement.style.width.includes("px"))
width=parseInt(this.DivElement.style.width.replace("px",""));
}
this.CanvasElement.height=height;
this.CanvasElement.width=width;
this.CanvasElement.style.width=this.CanvasElement.width+'px';
this.CanvasElement.style.height=this.CanvasElement.height+'px';
var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
this.CanvasElement.height*=pixelTatio;
this.CanvasElement.width*=pixelTatio;
JSConsole.Chart.Log(`[JSTReportChart::OnSize] devicePixelRatio=${window.devicePixelRatio}, height=${this.CanvasElement.height}, width=${this.CanvasElement.width}`);
if (this.JSChartContainer && this.JSChartContainer.OnSize)
{
this.JSChartContainer.OnSize();
}
}
this.SetOption=function(option)
{
var chart=this.CreateJSTReportChartContainer(option);
if (!chart) return false;
if (option.OnCreatedCallback) option.OnCreatedCallback(chart);
this.JSChartContainer=chart;
this.DivElement.JSChart=this; //div中保存一份
if (option.EnableResize==true) this.CreateResizeListener();
if (option.MinuteChartTooltip && option.MinuteChartTooltip.Enable) chart.InitalMinuteChartTooltip(option.MinuteChartTooltip); //分时图
if (option.FloatTooltip && option.FloatTooltip.Enable) chart.InitalFloatTooltip(option.FloatTooltip); //提示信息
if (option.Symbol)
{
chart.Draw();
var name=option.Symbol;
if (option.Name) name=option.Name;
chart.ChangeSymbol(option.Symbol, {Name:name}); //下载列表码表
}
}
this.CreateJSTReportChartContainer=function(option)
{
var chart=new JSTReportChartContainer(this.CanvasElement);
chart.Create(option);
if (option.NetworkFilter) chart.NetworkFilter=option.NetworkFilter;
if (IFrameSplitOperator.IsNonEmptyArray(option.Column)) chart.SetColumn(option.Column);
if (IFrameSplitOperator.IsNonEmptyArray(option.Tab)) chart.SetTab(option.Tab);
if (IFrameSplitOperator.IsNumber(option.TabSelected)) chart.SetSelectedTab(option.TabSelected);
if (IFrameSplitOperator.IsBool(option.EnableDragRow)) chart.EnableDragRow=option.EnableDragRow;
if (IFrameSplitOperator.IsNumber(option.DragRowType)) chart.DragRowType=option.DragRowType;
if (IFrameSplitOperator.IsBool(option.EnableDragHeader)) chart.EnableDragHeader=option.EnableDragHeader;
if (IFrameSplitOperator.IsBool(option.EnablePageCycle)) chart.EnablePageCycle=option.EnablePageCycle;
if (IFrameSplitOperator.IsNumber(option.FixedRowCount)) chart.SetFixedRowCount(option.FixedRowCount); //固定行
if (option.SortInfo)
{
var item=option.SortInfo;
if (IFrameSplitOperator.IsNumber(item.Field)) chart.SortInfo.Field=item.Field;
if (IFrameSplitOperator.IsNumber(item.Sort)) chart.SortInfo.Sort=item.Sort;
}
this.SetChartBorder(chart, option);
//是否自动更新
if (option.IsAutoUpdate!=null) chart.IsAutoUpdate=option.IsAutoUpdate;
if (option.AutoUpdateFrequency>0) chart.AutoUpdateFrequency=option.AutoUpdateFrequency;
if (IFrameSplitOperator.IsBool(option.EnableFilter)) chart.EnableFilter=option.EnableFilter;
//注册事件
if (option.EventCallback)
{
for(var i=0;i<option.EventCallback.length;++i)
{
var item=option.EventCallback[i];
chart.AddEventCallback(item);
}
}
return chart;
}
this.SetChartBorder=function(chart, option)
{
if (!option.Border) return;
var item=option.Border;
if (IFrameSplitOperator.IsNumber(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left;
if (IFrameSplitOperator.IsNumber(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right;
if (IFrameSplitOperator.IsNumber(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top;
if (IFrameSplitOperator.IsNumber(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom=option.Border.Bottom;
var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
chart.Frame.ChartBorder.Left*=pixelTatio;
chart.Frame.ChartBorder.Right*=pixelTatio;
chart.Frame.ChartBorder.Top*=pixelTatio;
chart.Frame.ChartBorder.Bottom*=pixelTatio;
}
this.CreateResizeListener=function()
{
this.ResizeListener = new ResizeObserver((entries)=>{ this.OnDivResize(entries); });
this.ResizeListener.observe(this.DivElement);
}
this.OnDivResize=function(entries)
{
JSConsole.Chart.Log("[JSTReportChart::OnDivResize] entries=", entries);
this.OnSize();
}
/////////////////////////////////////////////////////////////////////////////
//对外接口
//切换股票代码接口
this.ChangeSymbol=function(symbol, option)
{
if (this.JSChartContainer) this.JSChartContainer.ChangeSymbol(symbol,option);
}
this.SetColumn=function(aryColumn, option)
{
if (this.JSChartContainer) this.JSChartContainer.SetColumn(aryColumn,option);
}
//事件回调
this.AddEventCallback=function(obj)
{
if(this.JSChartContainer && typeof(this.JSChartContainer.AddEventCallback)=='function')
{
JSConsole.Chart.Log('[JSTReportChart:AddEventCallback] obj=', obj);
this.JSChartContainer.AddEventCallback(obj);
}
}
//重新加载配置
this.ReloadResource=function(option)
{
if(this.JSChartContainer && typeof(this.JSChartContainer.ReloadResource)=='function')
{
JSConsole.Chart.Log('[JSTReportChart:ReloadResource] ');
this.JSChartContainer.ReloadResource(option);
}
}
this.ChartDestroy=function()
{
if (this.JSChartContainer && typeof (this.JSChartContainer.ChartDestroy) == 'function')
{
this.JSChartContainer.ChartDestroy();
}
}
this.Draw=function()
{
if(this.JSChartContainer && typeof(this.JSChartContainer.Draw)=='function')
{
JSConsole.Chart.Log('[JSTReportChart:Draw] ');
this.JSChartContainer.Draw();
}
}
}
JSTReportChart.Init=function(divElement)
{
var jsChartControl=new JSTReportChart(divElement);
jsChartControl.OnSize();
return jsChartControl;
}
//自定义风格
JSTReportChart.SetStyle=function(option)
{
if (option) g_JSChartResource.SetStyle(option);
}
//获取颜色配置 (设置配必须啊在JSChart.Init()之前)
JSTReportChart.GetResource=function()
{
return g_JSChartResource;
}
JSTReportChart.GetfloatPrecision=function(symbol)
{
return GetfloatPrecision(symbol);
}
function HQTReportItem()
{
this.Symbol;
this.Name;
this.YClose;
this.Open;
this.Price;
this.High;
this.Low;
this.Amount;
this.Vol;
this.Positon; //持仓量
this.Increase; //涨幅
this.UpDown; //涨跌
this.Exchange; //换手
this.Amplitude; //振幅
this.BuyPrice; //买价/量
this.BuyVol;
this.SellPrice; //卖价/量
this.SellVol;
this.AvPrice; //均价
this.LimitHigh; //涨停价
this.LimitLow; //跌停价
this.CloseLine; //{Data:[], Max:, Min:, Count: }
this.ExtendData; //扩展数据
}
function JSTReportChartContainer(uielement)
{
this.ClassName='JSTReportChartContainer';
this.Frame; //框架画法
this.ChartPaint=[]; //图形画法
this.ChartSplashPaint=null; //等待提示
this.LoadDataSplashTitle="数据加载中"; //下载数据提示信息
this.SplashTitle={ StockList:"下载码表中....." } ;
this.Canvas=uielement.getContext("2d"); //画布
this.Tooltip=document.createElement("div");
this.Tooltip.className='jstreport-tooltip';
this.Tooltip.style.background=g_JSChartResource.TooltipBGColor;
this.Tooltip.style.opacity=g_JSChartResource.TooltipAlpha;
this.Tooltip.style["pointer-events"]="none";
this.Tooltip.id=Guid();
uielement.parentNode.appendChild(this.Tooltip);
this.Symbol; //期权对应的品种代码
this.Name; //期权对应的品种名称
this.NetworkFilter; //数据回调接口
this.Data={ XOffset:0, YOffset:0, Data:[], Price:null, MatchExePrice:null }; //股票列表 Price=标的物市场价格 MatchExePrice=标的物市场价格匹配的行权价格
this.BorderData={ MapData:null }; //key=Field Value:[null, {ExePrice} ,{ExePrice} ]
this.SourceData={ Data:[] } ; //原始股票顺序(排序还原用) {ExePrice=行权价格 LeftData:, RightData}
this.FixedRowData={ Data:[] }; //[ { TData:{ LeftData:[], RightData:[] }} , ...]; 顶部固定行Data:[{ Value:, Text:, Color:, TextAgiln: }]
this.DelayUpdateTimer=null; //延迟更新
this.DelayUpdateFrequency=500; //延迟更新时间
this.MapStockData;
this.MapExePriceData;
this.FlashBG=new Map();
this.FlashBGTimer=null; //闪烁背景 Value:{ LastTime:数据最后的时间, Data: { Key:ID, BGColor:, Time: , Count: 次数 } };
this.GlobalOption={ FlashBGCount:0 }
this.SortInfo={ Field:-1, Sort:0 }; //排序信息 {Field:排序字段id, Sort:0 不排序 1升序 2降序 }
//事件回调
this.mapEvent=new Map(); //通知外部调用 key:JSCHART_EVENT_ID value:{Callback:回调,}
this.AutoUpdateTimer=null;
this.AutoUpdateFrequency=15000; //15秒更新一次数据
this.UIElement=uielement;
this.LastPoint=new Point(); //鼠标位置
this.IsOnTouch=false;
this.TouchDrag;
this.TouchMoveMinAngle=70; //左右移动最小角度
this.YStepPixel=5*GetDevicePixelRatio();
this.XStepPixel=10*GetDevicePixelRatio();
//拖拽滚动条
this.DragXScroll=null; //{Start:{x,y}, End:{x, y}}
this.EnablePageCycle=false; //是否循环翻页
this.TooltipMinuteChart; //分时图
this.FloatTooltip; //浮框提示
this.LastMouseStatus={ MoveStatus:null, TooltipStatus:null, MouseOnStatus:null };
this.IsDestroy=false; //是否已经销毁了
this.ChartDestroy=function() //销毁
{
this.IsDestroy=true;
this.StopAutoUpdate();
this.DestroyMinuteChartTooltip();
this.DestroyFloatTooltip();
}
this.InitalMinuteChartTooltip=function(option)
{
if (this.TooltipMinuteChart) return;
this.TooltipMinuteChart=new JSTooltipMinuteChart();
this.TooltipMinuteChart.Inital(this, option);
this.TooltipMinuteChart.Create();
}
this.DestroyMinuteChartTooltip=function()
{
if (!this.TooltipMinuteChart) return;
this.TooltipMinuteChart.Destroy();
this.TooltipMinuteChart=null;
}
//data={ Symbol }
this.ShowMinuteChartTooltip=function(x,y, data)
{
if (!this.TooltipMinuteChart) return;
var rtClient=this.UIElement.getBoundingClientRect();
var rtScroll=GetScrollPosition();
var offsetLeft=rtClient.left+rtScroll.Left;
var offsetTop=rtClient.top+rtScroll.Top;
data.Offset={ Left:offsetLeft, Top:offsetTop };
this.TooltipMinuteChart.Show(data, x,y);
}
this.HideMinuteChartTooltip=function()
{
if (!this.TooltipMinuteChart) return;
this.TooltipMinuteChart.Hide();
}
this.InitalFloatTooltip=function(option)
{
if (this.FloatTooltip) return;
this.FloatTooltip=new JSFloatTooltip();
this.FloatTooltip.Inital(this, option);
this.FloatTooltip.Create();
}
this.HideFloatTooltip=function()
{
if (!this.FloatTooltip) return;
this.FloatTooltip.Hide();
}
this.DestroyFloatTooltip=function()
{
if (!this.FloatTooltip) return;
this.FloatTooltip.Destroy();
this.FloatTooltip=null;
}
this.DrawFloatTooltip=function(point,toolTip)
{
if (!this.FloatTooltip) return;
this.UpdateFloatTooltip(point, toolTip)
}
this.UpdateFloatTooltip=function(point, toolTip)
{
if (!this.FloatTooltip) return;
var sendData=
{
Tooltip:toolTip,
Point:point,
DataType:4,
};
this.FloatTooltip.Update(sendData);
}
//清空固定行数据
this.ClearFixedRowData=function()
{
this.FixedRowData.Data=[];
}
//设置固定行
this.SetFixedRowCount=function(value)
{
var chart=this.ChartPaint[0];
if (!chart) return;
chart.FixedRowCount=value;
}
//创建
this.Create=function(option)
{
this.UIElement.JSChartContainer=this;
//创建等待提示
this.ChartSplashPaint = new ChartSplashPaint();
this.ChartSplashPaint.Canvas = this.Canvas;
this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle);
this.ChartSplashPaint.IsEnableSplash=true;
//创建框架
this.Frame=new JSTReportFrame();
this.Frame.ChartBorder=new ChartBorder();
this.Frame.ChartBorder.UIElement=this.UIElement;
this.Frame.ChartBorder.Top=30;
this.Frame.ChartBorder.Left=5;
this.Frame.ChartBorder.Bottom=20;
this.Frame.Canvas=this.Canvas;
this.ChartSplashPaint.Frame = this.Frame;
//创建表格
var chart=new ChartTReport();
chart.Frame=this.Frame;
chart.ChartBorder=this.Frame.ChartBorder;
chart.Canvas=this.Canvas;
chart.UIElement=this.UIElement;
chart.GetEventCallback=(id)=> { return this.GetEventCallback(id); }
chart.GetExePriceDataCallback=(exePrice)=>{ return this.GetExePriceData(exePrice);}
chart.GetFlashBGDataCallback=(symbol, time)=>{ return this.GetFlashBGData(symbol, time); }
chart.Data=this.Data;
chart.BorderData=this.BorderData;
chart.GlobalOption=this.GlobalOption;
chart.FixedRowData=this.FixedRowData;
chart.SortInfo=this.SortInfo;
this.ChartPaint[0]=chart;
if (option)
{
if (IFrameSplitOperator.IsBool(option.IsShowHeader)) chart.IsShowHeader=option.IsShowHeader; //是否显示表头
if (IFrameSplitOperator.IsNumber(option.FixedColumn)) chart.FixedColumn=option.FixedColumn; //固定列
if (IFrameSplitOperator.IsNumber(option.BorderLine)) this.Frame.BorderLine=option.BorderLine; //边框
if (IFrameSplitOperator.IsBool(option.ItemBorder)) chart.IsDrawBorder=option.ItemBorder; //单元格边框
if (IFrameSplitOperator.IsNumber(option.SelectedModel)) chart.SelectedModel=option.SelectedModel;
}
var bRegisterKeydown=true;
var bRegisterWheel=true;
if (option)
{
if (option.KeyDown===false)
{
bRegisterKeydown=false;
JSConsole.Chart.Log('[JSTReportChartContainer::Create] not register keydown event.');
}
if (option.Wheel===false)
{
bRegisterWheel=false;
JSConsole.Chart.Log('[JSTReportChartContainer::Create] not register wheel event.');
}
}
if (bRegisterKeydown) this.UIElement.addEventListener("keydown", (e)=>{ this.OnKeyDown(e); }, true); //键盘消息
if (bRegisterWheel) this.UIElement.addEventListener("wheel", (e)=>{ this.OnWheel(e); }, true); //上下滚动消息
this.UIElement.ondblclick=(e)=>{ this.UIOnDblClick(e); }
this.UIElement.onmousedown=(e)=> { this.UIOnMouseDown(e); }
this.UIElement.onmousemove=(e)=>{ this.UIOnMouseMove(e);}
this.UIElement.onmouseout=(e)=>{ this.UIOnMounseOut(e); }
this.UIElement.onmouseleave=(e)=>{ this.UIOnMouseleave(e); }
this.UIElement.oncontextmenu=(e)=> { this.UIOnContextMenu(e); }
/*
this.UIElement.onmouseup=(e)=>{ this.UIOnMounseUp(e); }
//手机拖拽
this.UIElement.ontouchstart=(e)=> { this.OnTouchStart(e); }
this.UIElement.ontouchmove=(e)=> {this.OnTouchMove(e); }
this.UIElement.ontouchend=(e)=> {this.OnTouchEnd(e); }
*/
}
this.Draw=function()
{
if (this.UIElement.width<=0 || this.UIElement.height<=0) return;
this.Canvas.clearRect(0,0,this.UIElement.width,this.UIElement.height);
var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
this.Canvas.lineWidth=pixelTatio; //手机端需要根据分辨率比调整线段宽度
this.LastMouseStatus.MouseOnStatus=null;
if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash)
{
this.Frame.Draw( { IsEnableSplash:this.ChartSplashPaint.IsEnableSplash} );
this.ChartSplashPaint.Draw();
return;
}
this.Frame.Draw();
this.Frame.DrawLogo();
//框架内图形
for(var i=0;i<this.ChartPaint.length;++i)
{
var item=this.ChartPaint[i];
if (item.IsDrawFirst)
item.Draw();
}
for(var i=0; i<this.ChartPaint.length; ++i)
{
var item=this.ChartPaint[i];
if (!item.IsDrawFirst)
item.Draw();
}
if (this.GlobalOption.FlashBGCount>0)
{
this.DelayDraw(500);
}
}
this.DelayDraw=function(frequency)
{
if (typeof (this.FlashBGTimer) == 'number')
{
clearTimeout(this.FlashBGTimer);
this.FlashBGTimer = null;
}
this.FlashBGTimer=setTimeout(()=>
{
this.Draw();
},frequency);
}
this.ClearData=function()
{
this.SourceData.Data=[];
this.Data.Data=[];
this.Data.Price=null;
this.Data.XOffset=0; //清空偏移
this.Data.YOffset=0;
this.MapStockData=new Map();
this.MapExePriceData=null;
this.BorderData.MapData=null;
}
this.StopAutoUpdate=function()
{
this.CancelAutoUpdate();
this.AutoUpdateEvent(false,'JSTReportChartContainer::StopAutoUpdate');
if (!this.IsAutoUpdate) return;
this.IsAutoUpdate=false;
}
//设置事件回调
//{event:事件id, callback:回调函数}
this.AddEventCallback=function(object)
{
if (!object || !object.event || !object.callback) return;
var data={Callback:object.callback, Source:object};
this.mapEvent.set(object.event,data);
}
this.RemoveEventCallback=function(eventid)
{
if (!this.mapEvent.has(eventid)) return;
this.mapEvent.delete(eventid);
}
this.GetEventCallback=function(id) //获取事件回调
{
if (!this.mapEvent.has(id)) return null;
var item=this.mapEvent.get(id);
return item;
}
this.OnSize=function()
{
if (!this.Frame) return;
this.SetSizeChange(true);
this.Draw();
this.DelayUpdateStockData();
}
this.SetSizeChange=function(bChanged)
{
for(var i=0;i<this.ChartPaint.length;++i)
{
var chart=this.ChartPaint[i];
if (chart) chart.SizeChange=bChanged;
}
}
this.ChangeSymbol=function(symbol, option)
{
this.CancelAutoUpdate();
this.ClearData();
this.Symbol=symbol;
this.Name=symbol;
if (option)
{
if (option.Name) this.Name=option.Name;
}
this.RequestStockListData();
}
this.CancelAutoUpdate=function() //关闭停止更新
{
if (typeof (this.AutoUpdateTimer) == 'number')
{
clearTimeout(this.AutoUpdateTimer);
this.AutoUpdateTimer = null;
}
}
this.SetColumn=function(aryColunm, option)
{
var chart=this.ChartPaint[0];
if (!chart) return;
chart.SetColumn(aryColunm);
chart.SizeChange=true;
if (option && option.Redraw) this.Draw();
}
//请求列表
this.RequestStockListData=function()
{
this.ChartSplashPaint.SetTitle(this.SplashTitle.StockList);
this.ChartSplashPaint.EnableSplash(true);
this.Draw();
var self=this;
if (this.NetworkFilter)
{
var obj=
{
Name:'JSTReportChartContainer::RequestStockListData', //类名::
Explain:'T型报价列表数据',
Self:this,
PreventDefault:false
};
this.NetworkFilter(obj, function(data)
{
if (!data) return;
if (data.symbol!=self.Symbol) return;
self.ChartSplashPaint.EnableSplash(false);
self.RecvStockListData(data);
});
if (obj.PreventDefault==true) return; //已被上层替换,不调用默认的网络请求
}
throw { Name:'JSTReportChartContainer::RequestStockListData', Error:'(T型报价列表数据)不提供内置测试数据' };
}
this.RecvStockListData=function(data)
{
this.MapExePriceData=new Map();
this.MapStockData=new Map();
if (IFrameSplitOperator.IsNonEmptyArray(data.data))
{
//0=行权价格 1=左边期权代码 2=右侧期权代码 3=左侧期权名称 4=右侧期权名称
for(var i=0;i<data.data.length;++i)
{
var item=data.data[i];
var exePrice=item[0];
var leftData=new HQTReportItem();
leftData.Symbol=leftData.Name=item[1];
if (item[3]) leftData.Name=item[3];
var rightData=new HQTReportItem();
rightData.Symbol=rightData.Name=item[2];
if (item[4]) rightData.Name=item[4];
this.MapStockData.set(leftData.Symbol, leftData);
this.MapStockData.set(rightData.Symbol, rightData);
var dataItem={ ExePrice:exePrice, LeftData:leftData, RightData:rightData };
this.MapExePriceData.set(dataItem.ExePrice, dataItem);
this.SourceData.Data.push(exePrice);
this.Data.Data.push(exePrice);
}
}
if (IFrameSplitOperator.IsNumber(data.price))
{
this.Data.Price=data.price;
}
if ( IFrameSplitOperator.IsNumber(data.Decimal))
{
var chart=this.ChartPaint[0];
if (chart) chart.DefaultDecimal=data.Decimal;
}
this.Draw();
this.UpdateStockData();
}
this.GetExePriceData=function(exePrice)
{
if (!this.MapExePriceData) return null;
if (!this.MapExePriceData.has(exePrice)) return null;
return this.MapExePriceData.get(exePrice);
}
this.UpdateStockData=function()
{
if (!IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return;
if (this.MapStockData.size<=0) return;
var arySymbol=[];
for(var mapItem of this.MapStockData)
{
arySymbol.push(mapItem[0]);
}
if (!IFrameSplitOperator.IsNonEmptyArray(arySymbol)) return;
this.RequestStockData(arySymbol);
}
//下载期权数据
this.RequestStockData=function(arySymbol)
{
var self=this;
if (this.NetworkFilter)
{
var chart=this.ChartPaint[0];
var obj=
{
Name:'JSTReportChartContainer::RequestStockData', //类名::函数名
Explain:'T型报价列表期权数据',
Request:{ Data: { stocks: arySymbol, symbol:this.Symbol, fixedRowCount:chart.FixedRowCount } },
Self:this,
PreventDefault:false
};
this.NetworkFilter(obj, function(data)
{
self.RecvStockData(data);
self.AutoUpdate();
});
if (obj.PreventDefault==true) return;
}
throw { Name:'JSTReportChartContainer::RequestStockData', Error:'(T型报价列表期权数据)不提供内置测试数据' };
}
this.RecvStockData=function(data)
{
var setUpdateSymbol=new Set(); //更新的股票列表
if (IFrameSplitOperator.IsNonEmptyArray(data.data))
{
for(var i=0;i<data.data.length;++i)
{
var item=data.data[i];
var symbol=item[0]; //0=证券代码;
if (!symbol) continue;
var stock=null;
if (this.MapStockData.has(symbol))
{
stock=this.MapStockData.get(symbol);
}
else
{
stock=new HQTReportItem();
stock.Symbol=symbol;
this.MapStockData.set(symbol, stock);
}
this.ReadStockJsonData(stock, item);
if (!setUpdateSymbol.has(symbol)) setUpdateSymbol.add(symbol);
}
}
if (IFrameSplitOperator.IsNumber(data.price))
{
this.Data.Price=data.price;
this.Data.MatchExePrice=null;
}
if (IFrameSplitOperator.IsNonEmptyArray(data.fixedRowData))
{
for(var i=0;i<data.fixedRowData.length;++i)
{
var item=data.fixedRowData[i];
if (!item || !item.TData) continue;
this.FixedRowData.Data[i]=item;
}
}
var chart=this.ChartPaint[0];
//实时数据排序
if (chart && (this.SortInfo.Sort==1 || this.SortInfo.Sort==2 ) && IFrameSplitOperator.IsNumber(this.SortInfo.Field) && this.SortInfo.Field>=0)
{
var column=chart.Column[this.SortInfo.Field];
var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_TREPORT_LOCAL_SORT);
if (event && event.Callback)
{
var sendData={ Column:column, SortInfo:this.SortInfo, SymbolList:this.Data.Data, Result:null };
event.Callback (event, sendData, this);
if (Array.isArray(sendData.Result)) this.Data.Data=sendData.Result;
}
else
{
this.Data.Data.sort((left, right)=> { return this.LocalSort(left, right, column, this.SortInfo.Sort, this.SortInfo.CellType); });
}
}
this.CalculateData();
this.Draw();
}
//计算统计数据
this.CalculateData=function()
{
this.Data.MatchExePrice=null;
if (!this.MapExePriceData || this.MapExePriceData.size<=0) return;
var leftMaxPosition={ Max:null, ExePrice:null, CellType:1 };
var rightMaxPosition={ Max:null,ExePrice:null, CellType:2 };
this.BorderData.MapData=new Map();
var aryPrice=[];
for(var mapItem of this.MapExePriceData)
{
var item=mapItem[1];
var leftData=item.LeftData;
var rightData=item.RightData;
if (leftData && IFrameSplitOperator.IsNumber(leftData.Position))
{
if (leftMaxPosition.Max==null || leftMaxPosition.Max<leftData.Position)
{
leftMaxPosition.Max=leftData.Position;
leftMaxPosition.ExePrice=mapItem[0];
}
}
if (rightData && IFrameSplitOperator.IsNumber(rightData.Position))
{
if (rightMaxPosition.Max==null || rightMaxPosition.Max<rightData.Position)
{
rightMaxPosition.Max=rightData.Position;
rightMaxPosition.ExePrice=mapItem[0];
}
}
aryPrice.push(item.ExePrice);
}
var aryData=[null, null, null];
if (leftMaxPosition.ExePrice) aryData[1]=leftMaxPosition;
if (rightMaxPosition.ExePrice) aryData[2]=rightMaxPosition;
this.BorderData.MapData.set(TREPORT_COLUMN_ID.POSITION_ID, { Data:aryData });
if (IFrameSplitOperator.IsNumber(this.Data.Price))
{
aryPrice.sort((left, right)=>{ return left-right;});
var prePrice=null;
for(var i=0;i<aryPrice.length;++i)
{
var value=aryPrice[i];
if (value>this.Data.Price)
{
this.Data.MatchExePrice=prePrice
break;
}
prePrice=value;
}
}
}
//读取单条股票json数据
this.ReadStockJsonData=function(stock, item)
{
//0=证券代码 1=股票名称 2=昨收 3=开 4=高 5=低 6=收 7=成交量 8=成交金额, 9=买价 10=买量 11=卖价 12=卖量 13=均价 14=持仓 16=涨停价 17=跌停价
//21=涨幅% 22=涨跌 24=振幅%
//30=全局扩展数据 31=当前板块扩展数据
// 101-110 数值
if (IFrameSplitOperator.IsString(item[1])) stock.Name=item[1];
if (IFrameSplitOperator.IsNumber(item[2])) stock.YClose=item[2];
if (IFrameSplitOperator.IsNumber(item[3])) stock.Open=item[3];
if (IFrameSplitOperator.IsNumber(item[4])) stock.High=item[4];
if (IFrameSplitOperator.IsNumber(item[5])) stock.Low=item[5];
if (IFrameSplitOperator.IsNumber(item[6])) stock.Price=item[6];
if (IFrameSplitOperator.IsNumber(item[7])) stock.Vol=item[7];
if (IFrameSplitOperator.IsNumber(item[8])) stock.Amount=item[8];
if (IFrameSplitOperator.IsNumber(item[9])) stock.BuyPrice=item[9];
if (IFrameSplitOperator.IsNumber(item[10])) stock.BuyVol=item[10];
if (IFrameSplitOperator.IsNumber(item[11])) stock.SellPrice=item[11];
if (IFrameSplitOperator.IsNumber(item[12])) stock.SellVol=item[12];
if (IFrameSplitOperator.IsNumber(item[13])) stock.AvPrice=item[13]; //均价
if (IFrameSplitOperator.IsNumber(item[14])) stock.Position=item[14]; //持仓
if (IFrameSplitOperator.IsNumber(item[16])) stock.LimitHigh=item[16]; //涨停价
if (IFrameSplitOperator.IsNumber(item[17])) stock.LimitLow=item[17]; //跌停价
if (IFrameSplitOperator.IsNumber(item[21])) stock.Increase=item[21]; //涨幅%
if (IFrameSplitOperator.IsNumber(item[22])) stock.UpDown=item[22]; //涨跌
if (IFrameSplitOperator.IsNumber(item[24])) stock.Amplitude=item[24]; //振幅%
if (item[27]) stock.NameEx=item[27]; //扩展名字
//衍生数据计算
if (!IFrameSplitOperator.IsNumber(item[21])) //涨幅%
{
if (IFrameSplitOperator.IsNumber(stock.Price) && IFrameSplitOperator.IsNumber(stock.YClose) && stock.YClose!=0)
stock.Increase=(stock.Price-stock.YClose)/stock.YClose*100;
}
if (!IFrameSplitOperator.IsNumber(item[22])) //涨跌
{
if (IFrameSplitOperator.IsNumber(stock.Price) && IFrameSplitOperator.IsNumber(stock.YClose))
stock.UpDown=stock.Price-stock.YClose;
}
if (!IFrameSplitOperator.IsNumber(item[24])) //振幅%
{
if (IFrameSplitOperator.IsNumber(stock.High) && IFrameSplitOperator.IsNumber(stock.Low) && IFrameSplitOperator.IsNumber(stock.YClose) && stock.YClose!=0)
stock.Amplitude=(stock.High-stock.Low)/stock.YClose*100;
}
if (item[30])
stock.ExtendData=item[30]; //30=全局扩展数据
if (item[32]) stock.CloseLine=item[32]; //32=收盘价线
if (item[33]) stock.KLine=item[33]; //33=K线
if (item[34]) stock.ExePrice=item[34]; //34=行权价设置 { BGColor:背景色, Text:, TextColor }
//10个数值型 101-199
if (IFrameSplitOperator.IsNumber(item[101])) stock.ReserveNumber1=item[101];
if (IFrameSplitOperator.IsNumber(item[102])) stock.ReserveNumber2=item[102];
if (IFrameSplitOperator.IsNumber(item[103])) stock.ReserveNumber3=item[103];
if (IFrameSplitOperator.IsNumber(item[104])) stock.ReserveNumber4=item[104];
if (IFrameSplitOperator.IsNumber(item[105])) stock.ReserveNumber5=item[105];
if (IFrameSplitOperator.IsNumber(item[106])) stock.ReserveNumber6=item[106];
if (IFrameSplitOperator.IsNumber(item[107])) stock.ReserveNumber7=item[107];
if (IFrameSplitOperator.IsNumber(item[108])) stock.ReserveNumber8=item[108];
if (IFrameSplitOperator.IsNumber(item[109])) stock.ReserveNumber9=item[109];
if (IFrameSplitOperator.IsNumber(item[110])) stock.ReserveNumber10=item[110];
//10个字符型 201-299
if (IFrameSplitOperator.IsString(item[201]) || IFrameSplitOperator.IsObject(item[201])) stock.ReserveString1=item[201];
if (IFrameSplitOperator.IsString(item[202]) || IFrameSplitOperator.IsObject(item[202])) stock.ReserveString2=item[202];
if (IFrameSplitOperator.IsString(item[203]) || IFrameSplitOperator.IsObject(item[203])) stock.ReserveString3=item[203];
if (IFrameSplitOperator.IsString(item[204]) || IFrameSplitOperator.IsObject(item[204])) stock.ReserveString4=item[204];
if (IFrameSplitOperator.IsString(item[205]) || IFrameSplitOperator.IsObject(item[205])) stock.ReserveString5=item[205];
if (IFrameSplitOperator.IsString(item[206]) || IFrameSplitOperator.IsObject(item[206])) stock.ReserveString6=item[206];
if (IFrameSplitOperator.IsString(item[207]) || IFrameSplitOperator.IsObject(item[207])) stock.ReserveString7=item[207];
if (IFrameSplitOperator.IsString(item[208]) || IFrameSplitOperator.IsObject(item[208])) stock.ReserveString8=item[208];
if (IFrameSplitOperator.IsString(item[209]) || IFrameSplitOperator.IsObject(item[209])) stock.ReserveString9=item[209];
if (IFrameSplitOperator.IsString(item[210]) || IFrameSplitOperator.IsObject(item[210])) stock.ReserveString10=item[210];
}
this.AutoUpdate=function(waitTime) //waitTime 更新时间
{
this.CancelAutoUpdate();
if (!this.IsAutoUpdate) return;
var self = this;
var marketStatus=2;
var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_TREPORT_MARKET_STATUS);
if (event && event.Callback)
{
var sendData={ MarketStatus:2 };
event.Callback(event, sendData, this);
if (IFrameSplitOperator.IsNumber(sendData.MarketStatus)) marketStatus=sendData.MarketStatus;
}
if (marketStatus==0 || marketStatus==3) return; //闭市,盘后
var frequency=this.AutoUpdateFrequency;
if (marketStatus==1) //盘前
{
this.AutoUpdateTimer=setTimeout(function()
{
self.AutoUpdate();
},frequency);
}
else if (marketStatus==2) //盘中
{
this.AutoUpdateTimer=setTimeout(function()
{
self.UpdateStockData();
},frequency);
}
}
//delay=是否延迟
this.DelayUpdateStockData=function()
{
if (this.DelayUpdateTimer!=null)
{
clearTimeout(this.DelayUpdateTimer);
this.DelayUpdateTimer = null;
}
var frequency=this.DelayUpdateFrequency;
this.DelayUpdateTimer=setTimeout(()=>
{
this.UpdateStockData();
},frequency);
}
this.UIOnDblClick=function(e)
{
var pixelTatio = GetDevicePixelRatio();
var x = (e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio;
var y = (e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio;
var chart=this.GetTReportChart();
if (chart) chart.OnDblClick(x,y,e);
}
this.UIOnMouseDown=function(e)
{
var pixelTatio = GetDevicePixelRatio();
var x = (e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio;
var y = (e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio;
var chart=this.ChartPaint[0];
if (!chart) return;
var clickData=chart.OnMouseDown(x,y,e);
if (!clickData) return;
if ((clickData.Type==2) && (e.button==0 || e.button==2)) //点击行
{
if (clickData.Redraw==true) this.Draw();
}
else if (clickData.Type==3 && e.button==0) //表头
{
this.OnClickHeader(clickData, e);
}
//document.onmousemove=(e)=>{ this.DocOnMouseMove(e); }
//document.onmouseup=(e)=> { this.DocOnMouseUp(e); }
}
this.GetTReportChart=function()
{
var chart=this.ChartPaint[0];
if (!chart) return null;
return chart;
}
this.UIOnMouseMove=function(e)
{
var pixelTatio = GetDevicePixelRatio();
var x = (e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio;
var y = (e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio;
var oldMouseOnStatus=this.LastMouseStatus.MouseOnStatus;
this.LastMouseStatus.OnMouseMove=null;
var bShowTooltip=false;
this.LastMouseStatus.TooltipStatus=null;
var bShowChartTooltip=false;
var chartTooltipData=null;
this.LastMouseStatus.OnMouseMove={ X:x, Y:y };
var mouseStatus={ Cursor:"default", Name:"Default"};; //鼠标状态
var report=this.GetTReportChart();
var bDraw=false;
if (report)
{
var tooltipData=report.GetTooltipData(x,y); //单元格提示信息
if (tooltipData)
{
if (tooltipData.Type==20)
{
if (tooltipData.Data && tooltipData.Data.Symbol)
{
bShowChartTooltip=true;
chartTooltipData={ Symbol:tooltipData.Data.Symbol, Rect:tooltipData.Rect };
}
}
else
{
this.LastMouseStatus.TooltipStatus={ X:x, Y:y, Data:tooltipData, ClientX:e.clientX, ClientY:e.clientY };
bShowTooltip=true;
}
}
}
/* 目前没有用到
var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_REPORT_MOUSE_MOVE);
if (event)
{
var sendData={X:x, Y:y, Cell:cell };
event.Callback(event,sendData,this);
}
*/
if (mouseStatus) this.UIElement.style.cursor=mouseStatus.Cursor;
if (bShowChartTooltip)
{
this.ShowMinuteChartTooltip(null, null, chartTooltipData);
}
else
{
this.HideMinuteChartTooltip();
}
if (bShowTooltip)
{
var xTooltip = e.clientX-this.UIElement.getBoundingClientRect().left;
var yTooltip = e.clientY-this.UIElement.getBoundingClientRect().top;
this.DrawFloatTooltip({X:xTooltip, Y:yTooltip, YMove:20/pixelTatio},this.LastMouseStatus.TooltipStatus.Data);
}
else
{
this.HideFloatTooltip();
}
}
this.UIOnMounseOut=function(e)
{
this.HideMinuteChartTooltip();
}
this.UIOnMouseleave=function(e)
{
this.HideMinuteChartTooltip();
}
this.UIOnContextMenu=function(e)
{
e.preventDefault();
var x = e.clientX-this.UIElement.getBoundingClientRect().left;
var y = e.clientY-this.UIElement.getBoundingClientRect().top;
this.OnRightMenu(x,y,e); //右键菜单事件
}
this.OnRightMenu=function(x,y,e)
{
}
//点表头
this.OnClickHeader=function(clickData, e)
{
var header=clickData.Header;
if (header.Column && header.Column.Sort==1)
{
var data={ CellType:header.CellType, ColumnIndex:header.ColumnIndex }
this.SortHeader(header.Column, data);
}
}
//排序
this.SortHeader=function(column, sortData)
{
var sortInfo={ Field:this.SortInfo.Field, Sort:this.SortInfo.Sort, CellType:this.SortInfo.CellType };
var arySortType=column.SortType;
if (sortInfo.Field!=sortData.ColumnIndex || sortInfo.CellType!=sortData.CellType)
{
sortInfo.Field=sortData.ColumnIndex;
sortInfo.CellType=sortData.CellType;
sortInfo.Sort=arySortType[0]
}
else
{
if (arySortType.length==1)
{
sortInfo.Sort=arySortType[0];
}
else
{
for(var i=0;i<arySortType.length;++i)
{
if (sortInfo.Sort==arySortType[i])
{
sortInfo.Sort=arySortType[(i+1)%arySortType.length];
break;
}
}
}
}
if (sortInfo.Sort==0) //还原
{
this.Data.Data=[];
for(var i=0;i<this.SourceData.Data.length;++i)
{
this.Data.Data.push(this.SourceData.Data[i]);
}
}
else
{
var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_TREPORT_LOCAL_SORT);
if (event && event.Callback)
{
var sendData={ Column:column, SortInfo:sortInfo, SymbolList:this.Data.Data, Result:null };
event.Callback (event, sendData, this);
if (Array.isArray(sendData.Result)) this.Data.Data=sendData.Result;
}
else
{
this.Data.Data.sort((left, right)=> { return this.LocalSort(left, right, column, sortInfo.Sort, sortInfo.CellType); });
}
}
this.Data.YOffset=0;
this.SortInfo.Field=sortInfo.Field;
this.SortInfo.Sort=sortInfo.Sort;
this.SortInfo.CellType=sortInfo.CellType;
this.Draw();
this.DelayUpdateStockData();
}
//本地排序
this.LocalSort=function(left, right, column, sortType, cellType)
{
switch(column.Type)
{
case TREPORT_COLUMN_ID.SYMBOL_ID:
case TREPORT_COLUMN_ID.NAME_ID:
return this.LocalStringSort(left, right, column, sortType, cellType);
case TREPORT_COLUMN_ID.PRICE_ID:
case TREPORT_COLUMN_ID.VOL_ID:
case TREPORT_COLUMN_ID.UPDOWN_ID:
case TREPORT_COLUMN_ID.BUY_PRICE_ID:
case TREPORT_COLUMN_ID.SELL_PRICE_ID:
case TREPORT_COLUMN_ID.AMOUNT_ID:
case TREPORT_COLUMN_ID.BUY_VOL_ID:
case TREPORT_COLUMN_ID.SELL_VOL_ID:
case TREPORT_COLUMN_ID.YCLOSE_ID:
case TREPORT_COLUMN_ID.OPEN_ID:
case TREPORT_COLUMN_ID.HIGH_ID:
case TREPORT_COLUMN_ID.LOW_ID:
case TREPORT_COLUMN_ID.AVERAGE_PRICE_ID:
case TREPORT_COLUMN_ID.EXE_PRICE_ID: //行权价格
case TREPORT_COLUMN_ID.POSITION_ID: //持仓量
case TREPORT_COLUMN_ID.AMPLITUDE_ID:
case TREPORT_COLUMN_ID.INCREASE_ID:
case TREPORT_COLUMN_ID.RESERVE_NUMBER1_ID:
case TREPORT_COLUMN_ID.RESERVE_NUMBER2_ID:
case TREPORT_COLUMN_ID.RESERVE_NUMBER3_ID:
case TREPORT_COLUMN_ID.RESERVE_NUMBER4_ID:
case TREPORT_COLUMN_ID.RESERVE_NUMBER5_ID:
case TREPORT_COLUMN_ID.RESERVE_NUMBER6_ID:
case TREPORT_COLUMN_ID.RESERVE_NUMBER7_ID:
case TREPORT_COLUMN_ID.RESERVE_NUMBER8_ID:
case TREPORT_COLUMN_ID.RESERVE_NUMBER9_ID:
case TREPORT_COLUMN_ID.RESERVE_NUMBER10_ID:
return this.LocalNumberSort(left, right, column, sortType, cellType);
default:
return 0;
}
}
this.LocalNumberSort=function(left, right, column, sortType, cellType)
{
var leftStock=this.GetExePriceData(left);
var rightStock=this.GetExePriceData(right);
var leftValue=-99999999999999, rightValue=-99999999999999;
if (sortType==2) leftValue=rightValue=99999999999999;
var filedName=MAP_TREPORT_COLUMN_FIELD.get(column.Type);
if (cellType==0) //行权价格
{
if (leftStock && IFrameSplitOperator.IsNumber(leftStock.ExePrice)) leftValue=leftStock.ExePrice;
if (rightStock && IFrameSplitOperator.IsNumber(rightStock.ExePrice)) rightValue=rightStock.ExePrice;
}
else if (cellType==1)
{
if (leftStock && leftStock.LeftData)
{
var value=leftStock.LeftData[filedName];
if (IFrameSplitOperator.IsNumber(value)) leftValue=value;
}
if (rightStock && rightStock.LeftData)
{
var value=rightStock.LeftData[filedName];
if (IFrameSplitOperator.IsNumber(value)) rightValue=value;
}
}
else if (cellType==2)
{
if (leftStock && leftStock.RightData)
{
var value=leftStock.RightData[filedName];
if (IFrameSplitOperator.IsNumber(value)) leftValue=value;
}
if (rightStock && rightStock.RightData)
{
var value=rightStock.RightData[filedName]
if (IFrameSplitOperator.IsNumber(value)) rightValue=value;
}
}
if (sortType==1)
{
if (rightValue<leftValue) return -1;
else if (rightValue<leftValue) return 1;
else return 0;
}
else
{
if (leftValue<rightValue) return -1;
else if (leftValue>rightValue) return 1;
else return 0;
}
}
this.OnWheel=function(e) //滚轴
{
JSConsole.Chart.Log('[JSTReportChartContainer::OnWheel]',e);
if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return;
if (!this.Data || !IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return;
var x = e.clientX-this.UIElement.getBoundingClientRect().left;
var y = e.clientY-this.UIElement.getBoundingClientRect().top;
var isInClient=false;
this.Canvas.beginPath();
this.Canvas.rect(this.Frame.ChartBorder.GetLeft(),this.Frame.ChartBorder.GetTop(),this.Frame.ChartBorder.GetWidth(),this.Frame.ChartBorder.GetHeight());
isInClient=this.Canvas.isPointInPath(x,y);
if (!isInClient) return;
var chart=this.GetTReportChart();
if (!chart) return;
var wheelValue=e.wheelDelta;
if (!IFrameSplitOperator.IsObjectExist(e.wheelDelta))
wheelValue=e.deltaY* -0.01;
if (wheelValue<0) //下
{
this.LastMouseStatus.TooltipStatus=null;
this.HideMinuteChartTooltip();
this.HideFloatTooltip();
var result=this.MoveSelectedRow(1,{ EnablePageCycle:this.EnablePageCycle })
if (result)
{
if (result.Redraw) this.Draw();
if (result.Update) this.DelayUpdateStockData();
}
}
else if (wheelValue>0) //上
{
this.LastMouseStatus.TooltipStatus=null;
this.HideMinuteChartTooltip();
this.HideFloatTooltip();
var result=this.MoveSelectedRow(-1,{ EnablePageCycle:this.EnablePageCycle} );
if (result)
{
if (result.Redraw) this.Draw();
if (result.Update) this.DelayUpdateStockData();
}
}
if(e.preventDefault) e.preventDefault();
else e.returnValue = false;
}
this.OnKeyDown=function(e)
{
if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnabl