useChartRender.ts 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117
  1. import { reactive, toRefs, onMounted, ref,watch } from "vue";
  2. import _ from "lodash";
  3. import {
  4. IChartType,
  5. IDataProps,
  6. IParams,
  7. ISeasonDataItemProps,
  8. } from "@/types";
  9. import Highcharts from "highcharts";
  10. import { defaultOpts, seasonOptions } from "@/utils/chartOptions";
  11. import moment from "moment";
  12. import router from '@/router'
  13. // 散点x
  14. const scatterXAxis = {
  15. tickPosition: "inside",
  16. lineColor: "#bfbfbf",
  17. tickColor: "#bfbfbf",
  18. tickLength: 5,
  19. ordinal: false,
  20. type: "linear",
  21. };
  22. //基础y轴
  23. const basicYAxis = {
  24. tickLength: 5,
  25. lineWidth: 1,
  26. lineColor: "#bfbfbf",
  27. tickColor: "#bfbfbf",
  28. // offset: 0,
  29. visible: true,
  30. gridLineWidth: 0,
  31. tickPosition: "inside",
  32. endOnTick: false,
  33. startOnTick: false,
  34. showLastLabel: true,
  35. };
  36. interface StateProps {
  37. chartLimit: {
  38. min?: number;
  39. max?: number;
  40. x_min?:number;
  41. x_max?:number;
  42. rightMin?:number;
  43. rightMax?:number;
  44. rightTwoMin?:number;
  45. rightTwoMax?:number;
  46. }
  47. dataList: any[];
  48. chartInfo: any;
  49. }
  50. /**
  51. * useChartRender 处理图表渲染 返回options
  52. * @param Data 详情数据 lange中英文
  53. * @param {
  54. * Source 1 //ETA图 ChartType: 1曲线图 2季节图 3面积图 4柱状图 5散点图 6组合图 7柱形 10截面散点 11雷达图
  55. * 2 商品价格
  56. * 3 相关性
  57. * 4 滚动相关性
  58. * 5 商品利润
  59. * 6 拟合方程
  60. * 7 统计特征/标准差
  61. * 8 统计特征/百分位
  62. * 9 统计特征/频率
  63. * 10 跨品种分析
  64. * }
  65. *
  66. */
  67. //依赖数据
  68. const state = reactive<StateProps>({
  69. chartInfo: {},
  70. dataList: [],
  71. chartLimit: {
  72. min:0, //左轴上下限
  73. max:0,
  74. rightMin:0,//右轴上下限
  75. rightMax:0,
  76. rightTwoMin:0,//右二轴上下限
  77. rightTwoMax:0,
  78. },
  79. })
  80. const language = ref('')
  81. const routeQuery = ref<any>({});//路由参
  82. watch(() => router.currentRoute.value.query,(nval) => {
  83. routeQuery.value = nval
  84. })
  85. export const useChartRender = (Data,lang='zh',) => {
  86. state.chartInfo = Data.ChartInfo
  87. state.dataList = [1,11].includes(Data.ChartInfo.Source) ? Data.EdbInfoList : [Data.EdbInfoList[0]];
  88. language.value = lang
  89. let chartOptions:any;
  90. //eta图
  91. if ([1,11].includes(Data.ChartInfo.Source)) {
  92. setLimitData(state.dataList)
  93. const typeMap = {
  94. 1: setDefaultLineOptions,
  95. 2: setSeasonOptions,
  96. 3: setStackOrCombinChart,
  97. 4: setStackOrCombinChart,
  98. 5: setScatterChartOptions,
  99. 6: setStackOrCombinChart,
  100. 7: initBarData,
  101. 10: initSectionScatterData,
  102. 11: initRadarData
  103. };
  104. chartOptions = typeMap[state.chartInfo.ChartType] && typeMap[state.chartInfo.ChartType](Data);
  105. }else {
  106. //其他source图
  107. const sourceTypeMap = {
  108. 2: initCommodityData,
  109. 3: initRelevanceChartData,
  110. 4: initStatisticChartData,
  111. 5: initCommodityData,
  112. 6: initFittingEquation,
  113. 7: initStatisticChartData,
  114. 8: initStatisticChartData,
  115. 9: initStatisticChartData,
  116. 10: initCrossVarietyChartData,
  117. };
  118. chartOptions = sourceTypeMap[Data.ChartInfo.Source] &&
  119. sourceTypeMap[Data.ChartInfo.Source](Data);
  120. }
  121. return chartOptions
  122. };
  123. /* 曲线图 */
  124. const setDefaultLineOptions = () => {
  125. const { dataList,chartInfo } = state;
  126. /* 主题样式*/
  127. const chartTheme = chartInfo.ChartThemeStyle ? JSON.parse(chartInfo.ChartThemeStyle) : null;
  128. //拼接标题 数据列
  129. let data = [] as any[],ydata = [] as any[],minTimeArr: number[] = [],maxTimeArr: number[] = [];
  130. let rightTwoIndex = dataList.findIndex(item => item.IsAxis ===2);
  131. // const chartData = _.cloneDeep(dataList);
  132. //有右二轴时排个序 按照左 右 右2的顺序
  133. let chartData = dataList.some(_ =>_.IsAxis===2) ? changeEdbOrder(dataList) : _.cloneDeep(dataList);
  134. chartData.forEach((item:IDataProps ,index:number) => {
  135. //轴位置值相同的下标
  136. let sameSideIndex = chartData.findIndex(
  137. (i:IDataProps) => i.IsAxis === item.IsAxis
  138. );
  139. //获取对应轴的上下限
  140. //非ETA图库图表也不设置自定义上下限,相关性和统计特征也会用到曲线图
  141. //若chartInfo.Source为1,需在之前调用setLimitData
  142. const isETASource = state.chartInfo.Source===1
  143. let minLimit = 0,maxLimit = 0
  144. if(isETASource){
  145. const limitMap = {
  146. 0:['rightMin','rightMax'],
  147. 1:['min','max'],
  148. 2:['rightTwoMin','rightTwoMax']
  149. }
  150. if(limitMap[item.IsAxis]){
  151. minLimit = state.chartLimit[`${limitMap[item.IsAxis][0]}`]||0
  152. maxLimit = state.chartLimit[`${limitMap[item.IsAxis][1]}`]||0
  153. }
  154. }else{
  155. minLimit = dataList[sameSideIndex].MinData
  156. maxLimit = dataList[sameSideIndex].MaxData
  157. }
  158. //y轴
  159. const textZh = item.ConvertUnit||item.Unit
  160. const textEn = textZh?item.ConvertEnUnit||item.UnitEn||item.ConvertUnit||item.Unit:''
  161. let yItem = {
  162. ...basicYAxis,
  163. labels: {
  164. formatter: function (ctx: any) {
  165. let val = ctx.value;
  166. return sameSideIndex !== index ? '' : val;
  167. },
  168. align: 'center',
  169. x: [0,2].includes(item.IsAxis) ? 5 : -5,
  170. style: {
  171. ...chartTheme&&chartTheme.yAxisOptions.style,
  172. }
  173. },
  174. title: {
  175. text:language.value=='zh'?sameSideIndex !== index ? '' : `${textZh}`:textEn,
  176. style:{
  177. ...chartTheme&&chartTheme.yAxisOptions.style
  178. },
  179. align: 'high',
  180. rotation: 0,
  181. y: -12,
  182. x: (item.IsAxis===0 && rightTwoIndex>-1) ? -chartData[rightTwoIndex].Unit.length*12 : 0,
  183. textAlign: item.IsAxis===1 ? 'left' : 'right',
  184. reserveSpace: false
  185. },
  186. opposite: [0,2].includes(item.IsAxis),
  187. reversed: item.IsOrder,
  188. min: Number(minLimit),
  189. max: Number(maxLimit),
  190. tickWidth: 1,
  191. visible: sameSideIndex === index,
  192. plotBands: setAxisPlotAreas(item.IsAxis),
  193. plotLines: setAxisPlotLines(item.IsAxis)
  194. }
  195. // //拼接标题 判断相同指标名称拼接来源
  196. let dynamic_title = item.EdbName;
  197. let dynamic_arr = chartData.filter(
  198. (item: IDataProps) => dynamic_title === item.EdbName
  199. );
  200. // 拼接配置 IsAxis左轴1 右轴0 IsOrder正序false 逆序true EdbInfoType是否是领先指标
  201. let dynamic_tag = concatDynamicTag(item)
  202. // 英文后缀
  203. let dynamic_tag_en = concatDynamicTag(item,'en')
  204. //数据列
  205. let nameCh:String = dynamic_arr.length > 1
  206. ? `${item.EdbAliasName||item.EdbName}(${item.SourceName})${dynamic_tag}`
  207. : `${item.EdbAliasName||item.EdbName}${dynamic_tag}`
  208. let nameEn:String=item.EdbNameEn?`${item.EdbNameEn}${dynamic_tag_en}`:''
  209. let name :String=language.value == 'zh'?nameCh:nameEn
  210. //预测指标配置
  211. let predict_params = item.EdbInfoCategoryType === 1 ? getPredictParams(item) : {};
  212. let obj = {
  213. data: [] as any[],
  214. type: (chartTheme&&chartTheme.lineOptions.lineType) || 'spline',
  215. dashStyle: (chartTheme&&chartTheme.lineOptions.dashStyle)||'Solid',
  216. yAxis: sameSideIndex,
  217. name,
  218. color: item.ChartColor,
  219. lineWidth: Number(item.ChartWidth)||(chartTheme&&chartTheme.lineOptions.lineWidth)||1,
  220. ...predict_params
  221. };
  222. item.DataList = item.DataList || []
  223. for (let i of item.DataList) {
  224. obj.data.push([i.DataTimestamp, i.Value]);
  225. }
  226. if(item.DataList.length){
  227. minTimeArr.push(item.DataList[0].DataTimestamp)
  228. maxTimeArr.push(item.DataList[item.DataList.length-1].DataTimestamp)
  229. }
  230. data.push(obj);
  231. ydata.push(yItem);
  232. })
  233. // 范围为1年内 x轴显示为月/日 否则默认年/月
  234. let xAxis:any = {};
  235. const isLessThanOneYear:boolean = xTimeDiffer();
  236. let minTime: number=Math.min(...minTimeArr);
  237. let maxTime=Math.max(...maxTimeArr);
  238. let step = setXaxisStep(maxTime-minTime);
  239. xAxis = {
  240. ...defaultOpts.xAxis,
  241. tickInterval: screen.value === 'phone' ? step : undefined,
  242. labels: {
  243. formatter: function (ctx: any) {
  244. return isLessThanOneYear
  245. ? Highcharts.dateFormat('%m/%d', ctx.value)
  246. : Highcharts.dateFormat('%y/%m', ctx.value);
  247. },
  248. style: {
  249. ...chartTheme&&chartTheme.xAxisOptions.style
  250. }
  251. },
  252. plotBands: setAxisPlotAreas(3,'datetime'),
  253. plotLines: setAxisPlotLines(3,'datetime')
  254. }
  255. let options:any = {};
  256. options = {
  257. series: data,
  258. yAxis: ydata,
  259. xAxis
  260. };
  261. //滚动相关性独立tooltip
  262. if(chartInfo.Source === 4) {
  263. const relevanceUnitEnMap={
  264. '年': 'Year',
  265. '季': 'Season',
  266. '月': 'Month',
  267. '周': 'Week',
  268. '天': 'Day',
  269. }
  270. const { LeadValue,LeadUnit } = statisticFrequencyRenderData.value;
  271. options.tooltip = {
  272. formatter: function() {
  273. let that:any = this;
  274. let str = '';
  275. if(language.value=='zh'){
  276. str = `${Highcharts.dateFormat('%Y/%m/%d',that.x)}<br><p>相关性系数:${that.y.toFixed(4)}</p><br><p>领先${LeadValue+LeadUnit}</p>`
  277. }else{
  278. str = `${Highcharts.dateFormat('%Y/%m/%d',that.x)}<br><p>Correlation coefficient:${that.y.toFixed(4)}</p><br><p>lead${LeadValue+relevanceUnitEnMap[LeadUnit]}</p>`
  279. }
  280. return str
  281. },
  282. }
  283. }
  284. return options;
  285. }
  286. /* 季节图 */
  287. const screen = ref(document.body.clientWidth < 1200 ? 'phone' : 'pc');
  288. const setSeasonOptions = () => {
  289. const chartData = state.dataList[0];
  290. // 农历数据需要去除第一项 在ETA1.0.5之后,除了这里 农历和公历处理逻辑一样
  291. if(!chartData.DataList){
  292. return
  293. }
  294. const chartDataHandle=state.chartInfo.Calendar === '农历'?
  295. chartData.DataList.filter((item:ISeasonDataItemProps, index:number) => index > 0):
  296. chartData.DataList
  297. let seasonYdata:any[] = [],
  298. seasonData:any[] = [];
  299. //获取对应轴的上下限
  300. let minLimit = 0,maxLimit = 0
  301. minLimit = state.chartLimit.min||0
  302. maxLimit = state.chartLimit.max||0
  303. /* 主题样式*/
  304. const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null;
  305. /*处理数据列*/
  306. for (let j of chartDataHandle) {
  307. //预测指标配置
  308. let predict_params = chartData.EdbInfoCategoryType === 1 ? getSeasonPredictParams(j.CuttingDataTimestamp) : {};
  309. let serie_item = {
  310. data: [] as any[],
  311. type: (chartTheme&&chartTheme.lineOptions.lineType) || chartData.ChartStyle,
  312. dashStyle: (chartTheme&&chartTheme.lineOptions.dashStyle)||'Solid',
  313. yAxis: 0,
  314. name: j.ChartLegend,
  315. lineWidth: (chartTheme&&chartTheme.lineOptions.lineWidth) || 1,
  316. ...predict_params
  317. };
  318. const data_array = _.cloneDeep(j.DataList);
  319. data_array &&
  320. data_array.forEach((item: IParams) => {
  321. serie_item.data.push([item.DataTimestamp, item.Value]);
  322. });
  323. seasonData.push(serie_item);
  324. }
  325. //y轴
  326. const textZh = chartData.ConvertUnit||chartData.Unit
  327. const textEn = textZh?chartData.ConvertEnUnit||chartData.UnitEn||chartData.ConvertUnit||chartData.Unit:''
  328. seasonYdata = [{
  329. ...defaultOpts.yAxis,
  330. labels: {
  331. formatter: function (ctx: any) {
  332. let val = ctx.value;
  333. return val;
  334. },
  335. align: 'center',
  336. style: {
  337. ...chartTheme&&chartTheme.yAxisOptions.style
  338. }
  339. },
  340. title: {
  341. text:language.value=='zh'?`${textZh}`:textEn,
  342. align: 'high',
  343. rotation: 0,
  344. y: -12,
  345. x: 0,
  346. textAlign: 'left',
  347. reserveSpace: false,
  348. style:{
  349. ...chartTheme&&chartTheme.yAxisOptions.style
  350. },
  351. },
  352. max: Number(maxLimit),
  353. min: Number(minLimit),
  354. plotBands: setAxisPlotAreas(1),
  355. plotLines: setAxisPlotLines(1)
  356. }];
  357. // 季节图x轴显示月/日
  358. const xAxis = {
  359. ...defaultOpts.xAxis,
  360. tickInterval: screen.value === 'phone' ? 24 * 3600 * 1000 * 60 : undefined,
  361. labels: {
  362. formatter: function (ctx: any) {
  363. return Highcharts.dateFormat('%m/%d', ctx.value);
  364. },
  365. style: {
  366. ...chartTheme&&chartTheme.xAxisOptions.style
  367. }
  368. },
  369. plotBands: setAxisPlotAreas(3,'datetime'),
  370. plotLines: setAxisPlotLines(3,'datetime')
  371. };
  372. const tooltip = {
  373. ...defaultOpts.tooltip,
  374. dateTimeLabelFormats: {
  375. // 时间格式化字符
  376. day: '%m/%d',
  377. week: '%m/%d',
  378. month: '%m/%d',
  379. year: '%m/%d',
  380. },
  381. xDateFormat: '%m/%d',
  382. }
  383. let colors = chartTheme&&chartTheme.colorsOptions.reverse();
  384. return {
  385. colors:colors.slice(-chartDataHandle.length),
  386. series: seasonData,
  387. yAxis: seasonYdata,
  388. xAxis,
  389. tooltip
  390. };
  391. }
  392. /* 堆叠图/组合图设置
  393. 本来和曲线图逻辑基本一致兼容下即可 为了以后便于维护和阅读还是拆开写吧
  394. */
  395. const setStackOrCombinChart = () => {
  396. const { dataList,chartInfo } = state;
  397. /* 主题样式*/
  398. const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null;
  399. //拼接标题 数据列
  400. let data = [] as any[],ydata = [] as any[],minTimeArr: number[] = [],maxTimeArr: number[] = [];
  401. // const chartData = _.cloneDeep(dataList);
  402. //有右二轴时排个序 按照左 右 右2的顺序
  403. let chartData = dataList.some(_ =>_.IsAxis===2) ? changeEdbOrder(dataList) : _.cloneDeep(dataList);
  404. //支持的图表类型
  405. const chartTypeMap: IChartType = {
  406. 3: 'areaspline',
  407. 4: 'column',
  408. 6: ''
  409. };
  410. let chartStyle = chartTypeMap[chartInfo.ChartType];
  411. chartData.forEach((item:IDataProps ,index:number) => {
  412. //轴位置值相同的下标
  413. let sameSideIndex = chartData.findIndex(
  414. (i:IDataProps) => i.IsAxis === item.IsAxis
  415. );
  416. //堆叠图的yAxis必须一致 数据列所对应的y轴
  417. let serie_yIndex = index;
  418. if([3,4].includes(chartInfo.ChartType)) {
  419. // 类型为堆叠图时公用第一个指标y轴
  420. serie_yIndex = 0;
  421. } else if(chartInfo.ChartType ===6 && ['areaspline','column'].includes(item.ChartStyle)) {
  422. // 组合图找第一个堆叠柱状或面积的作为公用
  423. serie_yIndex = chartData.findIndex((i:IDataProps) => i.ChartStyle === item.ChartStyle);
  424. }
  425. //数据对应的y轴是公用轴则配置也共享
  426. item.IsAxis = serie_yIndex === index ? item.IsAxis : chartData[serie_yIndex].IsAxis;
  427. item.IsOrder = serie_yIndex === index ? item.IsOrder : chartData[serie_yIndex].IsOrder;
  428. // 右2轴下标
  429. let rightTwoIndex = [3,4].includes(chartInfo.ChartType)
  430. ? -1
  431. : dataList.findIndex(item => item.IsAxis===2);
  432. //获取对应轴的上下限
  433. let minLimit = 0,maxLimit = 0
  434. const limitMap = {
  435. 0:['rightMin','rightMax'],
  436. 1:['min','max'],
  437. 2:['rightTwoMin','rightTwoMax']
  438. }
  439. if(limitMap[item.IsAxis]){
  440. minLimit = state.chartLimit[`${limitMap[item.IsAxis][0]}`]||0
  441. maxLimit = state.chartLimit[`${limitMap[item.IsAxis][1]}`]||0
  442. }
  443. //y轴
  444. const textZh = item.ConvertUnit||item.Unit
  445. const textEn = textZh?item.ConvertEnUnit||item.UnitEn||item.ConvertUnit||item.Unit:''
  446. let yItem = {
  447. ...basicYAxis,
  448. labels: {
  449. formatter: function (ctx: any) {
  450. let val = ctx.value;
  451. return sameSideIndex !== index ? '' : val;
  452. },
  453. align: 'center',
  454. x: [0,2].includes(item.IsAxis) ? 5 : -5,
  455. style:{
  456. ...chartTheme&&chartTheme.yAxisOptions.style
  457. },
  458. },
  459. title: {
  460. text:language.value=='zh'?sameSideIndex !== index ? '' : `${textZh}`:textEn,
  461. // text: null,
  462. align: 'high',
  463. rotation: 0,
  464. y: -12,
  465. x: (item.IsAxis===0 && rightTwoIndex>-1) ? -chartData[rightTwoIndex].Unit.length*12 : 0,
  466. textAlign: item.IsAxis===1 ? 'left' : 'right',
  467. reserveSpace: false,
  468. style:{
  469. ...chartTheme&&chartTheme.yAxisOptions.style
  470. },
  471. },
  472. opposite: [0,2].includes(item.IsAxis),
  473. reversed: item.IsOrder,
  474. min: Number(minLimit),
  475. max: Number(maxLimit),
  476. tickWidth: sameSideIndex !== index ? 0 : 1,
  477. visible: serie_yIndex === index && sameSideIndex ===index,
  478. plotBands: setAxisPlotAreas(item.IsAxis),
  479. plotLines: setAxisPlotLines(item.IsAxis)
  480. }
  481. // //拼接标题 判断相同指标名称拼接来源
  482. let dynamic_title = item.EdbName;
  483. let dynamic_arr = chartData.filter(
  484. (item: IDataProps) => dynamic_title === item.EdbName
  485. );
  486. // 拼接配置 IsAxis左轴1 右轴0 IsOrder正序false 逆序true EdbInfoType是否是领先指标
  487. let dynamic_tag = concatDynamicTag(item)
  488. // 英文后缀
  489. let dynamic_tag_en = concatDynamicTag(item,'en')
  490. //数据列
  491. let nameCh:String = dynamic_arr.length > 1
  492. ? `${item.EdbAliasName||item.EdbName}(${item.SourceName})${dynamic_tag}`
  493. : `${item.EdbAliasName||item.EdbName}${dynamic_tag}`
  494. let nameEn:String=item.EdbNameEn?`${item.EdbNameEn}${dynamic_tag_en}`:''
  495. let name :String=language.value == 'zh'?nameCh:nameEn
  496. //预测指标配置
  497. let predict_params = item.EdbInfoCategoryType === 1 ? getPredictParams(item,chartStyle) : {};
  498. let obj = {
  499. data: [] as any[],
  500. type: chartStyle || item.ChartStyle,
  501. yAxis: serie_yIndex,
  502. name,
  503. color: item.ChartColor,
  504. lineWidth: Number(item.ChartWidth),
  505. fillColor: (chartInfo.ChartType === 3 || (chartInfo.ChartType === 6 && item.ChartStyle === 'areaspline')) ? item.ChartColor : undefined,
  506. zIndex: (chartInfo.ChartType === 6 && ['line','spline'].includes(item.ChartStyle)) ? 1 : 0, //防止组合图曲线被遮住
  507. borderWidth: 1,
  508. borderColor: item.ChartColor,
  509. ...predict_params
  510. };
  511. item.DataList = item.DataList || []
  512. for (let i of item.DataList) {
  513. obj.data.push([i.DataTimestamp, i.Value]);
  514. }
  515. if(item.DataList.length){
  516. minTimeArr.push(item.DataList[0].DataTimestamp)
  517. maxTimeArr.push(item.DataList[item.DataList.length-1].DataTimestamp)
  518. }
  519. data.push(obj);
  520. ydata.push(yItem);
  521. })
  522. // 范围为1年内 x轴显示为月/日 否则默认年/月
  523. let xAxis:any = {};
  524. const isLessThanOneYear:boolean = xTimeDiffer();
  525. let minTime: number=Math.min(...minTimeArr);
  526. let maxTime=Math.max(...maxTimeArr);
  527. let step = setXaxisStep(maxTime-minTime);
  528. xAxis = {
  529. ...defaultOpts.xAxis,
  530. tickInterval: screen.value === 'phone' ? step : undefined,
  531. labels: {
  532. formatter: function (ctx: any) {
  533. return isLessThanOneYear
  534. ? Highcharts.dateFormat('%m/%d', ctx.value)
  535. : Highcharts.dateFormat('%y/%m', ctx.value);
  536. },
  537. style: {
  538. ...chartTheme&&chartTheme.xAxisOptions.style
  539. }
  540. },
  541. plotBands: setAxisPlotAreas(3,'datetime'),
  542. plotLines: setAxisPlotLines(3,'datetime')
  543. }
  544. return {
  545. series: data,
  546. yAxis: ydata,
  547. xAxis
  548. };
  549. }
  550. /* 散点图 第一个指标值为x轴 第二个指标为y轴*/
  551. const setScatterChartOptions = () => {
  552. const { dataList,chartInfo } = state;
  553. /* 主题样式*/
  554. const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null;
  555. // 取2个指标中日期相同的数据
  556. const real_data: any[] = [];
  557. let tmpData_date: any = {};//用来取点对应的日期
  558. let data1 = _.cloneDeep(dataList)[0].DataList || [];
  559. let data2 = _.cloneDeep(dataList)[1].DataList || [];
  560. data1.forEach((_item: IParams) => {
  561. data2.forEach((_item2: IParams) => {
  562. if(_item.DataTimestamp === _item2.DataTimestamp) {
  563. //日期
  564. let itemIndex =_item.Value + "_" +_item2.Value
  565. if(tmpData_date[itemIndex]) {
  566. tmpData_date[itemIndex].push( moment(_item.DataTimestamp).format('YYYY/MM/DD'))
  567. } else {
  568. tmpData_date[itemIndex] = [moment(_item.DataTimestamp).format('YYYY/MM/DD')]
  569. }
  570. //值
  571. real_data.push({
  572. x: _item.Value,
  573. y: _item2.Value
  574. })
  575. }
  576. })
  577. })
  578. real_data.sort((x,y) => x-y);
  579. //悬浮窗 拼接日期 原始指标名称
  580. let tooltip = {
  581. formatter: function() {
  582. const that: any = this;
  583. return language.value=='zh'?
  584. `<strong>${ tmpData_date[that.x+'_'+that.y].length > 4 ? tmpData_date[that.x+'_'+that.y].slice(0,4).join()+'...' : tmpData_date[that.x+'_'+that.y].join() }</strong><br>
  585. ${dataList[0].EdbName}: <span style="font-weight: 600"> ${that.x}</span><br>
  586. ${dataList[1].EdbName}: <span style="font-weight: 600"> ${that.y}</span>
  587. `:
  588. `<strong>${ tmpData_date[that.x+'_'+that.y].length > 4 ? tmpData_date[that.x+'_'+that.y].slice(0,4).join()+'...' : tmpData_date[that.x+'_'+that.y].join() }</strong><br>
  589. ${dataList[0].EdbNameEn}: <span style="font-weight: 600"> ${that.x}</span><br>
  590. ${dataList[1].EdbNameEn}: <span style="font-weight: 600"> ${that.y}</span>
  591. `
  592. }
  593. }
  594. const { IsOrder,ChartColor } = dataList[0];
  595. const textYZh = dataList[1].ConvertUnit||dataList[1].Unit
  596. const textYEn = textYZh?dataList[1].ConvertEnUnit||dataList[1].UnitEn||dataList[1].ConvertUnit||dataList[1].Unit:''
  597. const textX = dataList[0].ConvertUnit||dataList[0].Unit
  598. const textXEn = textX?dataList[0].ConvertEnUnit||dataList[0].UnitEn||dataList[0].ConvertUnit||dataList[0].Unit:''
  599. //获取对应轴的上下限
  600. let minLimit = 0,maxLimit = 0
  601. minLimit = state.chartLimit.min||0
  602. maxLimit = state.chartLimit.max||0
  603. //y轴
  604. let yAxis = {
  605. title: {
  606. text: language.value=='zh'?`${textYZh}`:textYEn,
  607. // text: null,
  608. align: 'high',
  609. rotation: 0,
  610. y: -5,
  611. x:0,
  612. textAlign: 'left',
  613. reserveSpace: false,
  614. style:{
  615. ...chartTheme&&chartTheme.yAxisOptions.style
  616. },
  617. },
  618. labels: {
  619. formatter: function (ctx: any) {
  620. return ctx.value;
  621. },
  622. align: 'center',
  623. style:{
  624. ...chartTheme&&chartTheme.yAxisOptions.style
  625. },
  626. },
  627. opposite: false,
  628. reversed: IsOrder,
  629. min: Number(minLimit),
  630. max: Number(maxLimit),
  631. tickWidth: 1,
  632. tickLength: 5,
  633. lineWidth: 1,
  634. lineColor: '#bfbfbf',
  635. tickColor: '#bfbfbf',
  636. offset: 0,
  637. visible: true,
  638. gridLineWidth: 0,
  639. tickPosition: 'inside',
  640. endOnTick: false,
  641. startOnTick: false,
  642. showLastLabel: true,
  643. tickPixelInterval: 50,
  644. plotBands: setAxisPlotAreas(1),
  645. plotLines: setAxisPlotLines(1)
  646. }
  647. //数据列
  648. let series: any = {
  649. data: [],
  650. type: 'scatter',
  651. name: language.value == 'zh'?`${chartInfo.ChartName}${IsOrder ? '(逆序)' : ''}`:`${chartInfo.ChartNameEn}${IsOrder ? '(reserve)' : ''}`,
  652. color: ChartColor,
  653. chartType: 'linear',
  654. marker: {
  655. radius: (chartTheme&&chartTheme.lineOptions.radius)||5,
  656. },
  657. }
  658. real_data.forEach(_ => {
  659. series.data.push([_.x,_.y])
  660. })
  661. return {
  662. title: {
  663. text:''
  664. },
  665. series: [ series ],
  666. yAxis,
  667. xAxis: {
  668. ...scatterXAxis,
  669. title: {
  670. text: language.value=='zh'?`${textX}`:textXEn,
  671. align: 'high',
  672. rotation: 0,
  673. x: 0,
  674. offset: 20,
  675. style: {
  676. ...chartTheme&&chartTheme.xAxisOptions.style
  677. },
  678. },
  679. labels: {
  680. style: {
  681. ...chartTheme&&chartTheme.xAxisOptions.style
  682. }
  683. },
  684. plotBands: setAxisPlotAreas(3),
  685. plotLines: setAxisPlotLines(3)
  686. },
  687. tooltip
  688. }
  689. }
  690. /* 奇怪柱状图
  691. 获取图表详情后赋值奇怪柱状图数据 */
  692. const barChartRenderData = ref<any>({})
  693. const initBarData = (data: { XEdbIdValue: number[]; YDataList: any; EdbInfoList: any; ChartInfo: any; }) => {
  694. const { XEdbIdValue,YDataList,EdbInfoList,ChartInfo } = data;
  695. barChartRenderData.value = {
  696. barDateList: YDataList,
  697. barXIdData: XEdbIdValue,
  698. barEdbData: EdbInfoList,
  699. chartInfo: ChartInfo
  700. }
  701. state.chartLimit = {
  702. min: Number(ChartInfo.LeftMin),
  703. max: Number(ChartInfo.LeftMax),
  704. }
  705. return setBarChart()
  706. }
  707. const setBarChart = () => {
  708. const {barDateList,barXIdData,barEdbData,chartInfo} = barChartRenderData.value;
  709. const { chartLimit } = state;
  710. let seriesData: { data: any; type: string; yAxis: number; name: any; color: any; chartType: string; }[] = [];
  711. const data = _.cloneDeep(barDateList);
  712. /* 主题样式*/
  713. const chartTheme = chartInfo.ChartThemeStyle ? JSON.parse(chartInfo.ChartThemeStyle) : null;
  714. let categories = language.value==='zh'
  715. ? barXIdData.map((_:number) => barEdbData.find((edb: { EdbInfoId: number; }) => edb.EdbInfoId===_).EdbAliasName)
  716. : barXIdData.map((_:number) => barEdbData.find((edb: { EdbInfoId: number; }) => edb.EdbInfoId===_).EdbNameEn)
  717. //x轴
  718. let xAxis = {
  719. ...scatterXAxis,
  720. categories,
  721. tickWidth: 1,
  722. labels: {
  723. style: {
  724. ...chartTheme&&chartTheme.xAxisOptions.style
  725. }
  726. }
  727. }
  728. const { max,min } = chartLimit;
  729. console.log(max,min)
  730. //y轴
  731. let yAxis = {
  732. ...basicYAxis,
  733. title: {
  734. text: language.value==='zh' ? chartInfo.Unit : (chartInfo.UnitEn||chartInfo.Unit),
  735. align: 'high',
  736. rotation: 0,
  737. y: -12,
  738. x:0,
  739. textAlign: 'left',
  740. reserveSpace: false,
  741. style:{
  742. ...chartTheme&&chartTheme.yAxisOptions.style
  743. },
  744. },
  745. labels: {
  746. formatter: function (ctx:any) {
  747. let val = ctx.value;
  748. return val;
  749. },
  750. align: 'center',
  751. style:{
  752. ...chartTheme&&chartTheme.yAxisOptions.style
  753. },
  754. },
  755. min: Number(min),
  756. max: Number(max),
  757. opposite: false,
  758. tickWidth: 1,
  759. plotBands: setAxisPlotAreas(1),
  760. plotLines: setAxisPlotLines(1)
  761. }
  762. //数据列
  763. data.forEach((item: { Value: number; Name: string; Date: string; Color: string; }) => {
  764. let serie_item = {
  765. data: item.Value,
  766. type: 'column',
  767. yAxis: 0,
  768. name: language.value==='zh' ? (item.Name || item.Date) : item.Date,
  769. color: item.Color,
  770. chartType: 'linear'
  771. };
  772. seriesData.push(serie_item)
  773. })
  774. return {
  775. title: {
  776. text:''
  777. },
  778. plotOptions: {
  779. column:{
  780. stacking: null,
  781. },
  782. },
  783. series: seriesData,
  784. yAxis: [ yAxis ],
  785. xAxis
  786. }
  787. }
  788. /* 散点截面图 */
  789. const sectionScatterRenderData = ref<any>({});
  790. const initSectionScatterData = (data: { DataResp:any,EdbInfoList:any[] }) => {
  791. sectionScatterRenderData.value = data.DataResp;
  792. state.chartLimit.min=Number(data.DataResp.YMinValue)
  793. state.chartLimit.max=Number(data.DataResp.YMaxValue)
  794. state.chartLimit.x_min=Number(data.DataResp.XMinValue)
  795. state.chartLimit.x_max=Number(data.DataResp.XMaxValue)
  796. //校验英文名称是否完整
  797. // if(routeQuery.value.fromPage === 'en' && language.value==='en') {
  798. // const { XNameEn,XUnitNameEn,YNameEn,YUnitNameEn } = data.DataResp;
  799. // // DataList可能是null
  800. // const DataList = data.DataResp.DataList||[]
  801. // let isAllEn = true;
  802. // if(!XNameEn || !XUnitNameEn || !YNameEn || !YUnitNameEn) isAllEn = false;
  803. // if(DataList.some(_ => !_.NameEn)) isAllEn = false;
  804. // console.log(isAllEn)
  805. // language.value = isAllEn ? 'en' : 'zh';
  806. // }
  807. return setSectionScatterChart()
  808. }
  809. /* 截面散点图设置 sectionScatterData */
  810. const setSectionScatterChart = () => {
  811. const { chartLimit } = state;
  812. const { DataList,XName,XNameEn,YName,YNameEn } = sectionScatterRenderData.value;
  813. /* 主题样式*/
  814. const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null;
  815. if(!DataList){
  816. return
  817. }
  818. const { min,max,x_min,x_max } = chartLimit;
  819. //y轴
  820. let yAxis = {
  821. ...basicYAxis,
  822. title: {
  823. text: language.value === 'zh' ? YName : YNameEn,
  824. align: 'middle',
  825. style:{
  826. ...chartTheme&&chartTheme.yAxisOptions.style
  827. },
  828. },
  829. labels: {
  830. style:{
  831. ...chartTheme&&chartTheme.yAxisOptions.style
  832. },
  833. },
  834. opposite: false,
  835. reversed: false,
  836. min: Number(min),
  837. max: Number(max),
  838. tickWidth: 1,
  839. plotBands: setAxisPlotAreas(1),
  840. plotLines: setAxisPlotLines(1)
  841. }
  842. //x轴
  843. let xAxis = {
  844. ...scatterXAxis,
  845. title: {
  846. text: language.value === 'zh' ? XName : XNameEn,
  847. align: 'middle',
  848. style: {
  849. ...chartTheme&&chartTheme.xAxisOptions.style
  850. },
  851. },
  852. labels: {
  853. style:{
  854. ...chartTheme&&chartTheme.xAxisOptions.style
  855. },
  856. },
  857. min: Number(x_min),
  858. max: Number(x_max),
  859. plotBands: setAxisPlotAreas(3),
  860. plotLines: setAxisPlotLines(3)
  861. }
  862. //数据列
  863. let series: any[] = [];
  864. DataList.forEach(item => {
  865. //数据列
  866. let series_item = {
  867. data: [] as any[],
  868. type: 'scatter',
  869. name: language.value === 'zh' ? item.Name : item.NameEn,
  870. color: item.Color,
  871. lineWidth: 0,
  872. chartType: 'linear',
  873. zIndex:1,
  874. marker: {
  875. radius: (chartTheme&&chartTheme.lineOptions.radius)||5,
  876. },
  877. visible: true
  878. }
  879. item.EdbInfoList.forEach(_ => {
  880. series_item.data.push({
  881. x: _.XValue,
  882. y: _.YValue,
  883. dataLabels: {
  884. enabled: _.IsShow,
  885. allowOverlap: true,
  886. align: 'left',
  887. format: language.value === 'zh' ? _.Name : _.NameEn
  888. }
  889. })
  890. })
  891. series.push(series_item);
  892. //趋势线
  893. if(item.ShowTrendLine) {
  894. let trend_data = item.TrendLimitData.map((_,_index) => (
  895. _index === item.TrendLimitData.length-1 ? {
  896. x: _.X,
  897. y: _.Y,
  898. dataLabels: {
  899. enabled: item.ShowRSquare || item.ShowFitEquation,
  900. align: 'left',
  901. color: '#666',
  902. x: 20,
  903. y: 30,
  904. zIndex: 9,
  905. allowOverlap: true,
  906. formatter: function(){
  907. let tag = '';
  908. item.ShowRSquare && item.ShowFitEquation
  909. ? tag =`<span>${item.TrendLine}</span><br><span>R²=${item.RSquare}</span>`
  910. : item.ShowRSquare && !item.ShowFitEquation
  911. ? tag =`<span>R²=${item.RSquare}</span>`
  912. : item.ShowFitEquation && !item.ShowRSquare
  913. ? tag =`<span>${item.TrendLine}</span>`
  914. : ''
  915. return tag
  916. }
  917. }
  918. } : {
  919. x: _.X,
  920. y: _.Y,
  921. }
  922. ))
  923. let trend_item = {
  924. data: trend_data,
  925. type: 'spline',
  926. linkedTo: ':previous',
  927. color: item.Color,
  928. lineWidth: 1,
  929. chartType: 'linear',
  930. enableMouseTracking: false,
  931. dashStyle:'Dash',
  932. zIndex: 2,
  933. visible: true,
  934. marker: {
  935. enabled: false
  936. }
  937. }
  938. series.push(trend_item)
  939. }
  940. })
  941. let tooltip = {
  942. formatter: function() {
  943. const that:any = this;
  944. let str = '';
  945. if(language.value === 'zh') {
  946. let series_obj = DataList.find(_ => _.Name === that.series.name);
  947. let ponit_obj = series_obj.EdbInfoList.find(_ => _.XValue ===that.x && _.YValue===that.y);
  948. str=`<b>${ ponit_obj.Name }</b>`;
  949. str += `<br><span style="color:${that.color}">\u25CF</span>${ponit_obj.XName}: ${that.x} ${ponit_obj.XDate}<br>`;
  950. str += `<span style="color:${that.color}">\u25CF</span>${ponit_obj.YName}: ${that.y} ${ponit_obj.YDate}`;
  951. }else {
  952. let series_obj = DataList.find(_ => _.NameEn === that.series.name);
  953. let ponit_obj = series_obj.EdbInfoList.find(_ => _.XValue ===that.x && _.YValue===that.y);
  954. str=`<b>${ ponit_obj.NameEn }</b>`;
  955. str += `<br><span style="color:${that.color}">\u25CF</span>${ponit_obj.XNameEn}: ${that.x} ${ponit_obj.XDate}<br>`;
  956. str += `<span style="color:${that.color}">\u25CF</span>${ponit_obj.YNameEn}: ${that.y} ${ponit_obj.YDate}`;
  957. }
  958. return str
  959. }
  960. }
  961. return {
  962. title: {
  963. text:''
  964. },
  965. series,
  966. yAxis: [yAxis],
  967. xAxis,
  968. tooltip
  969. }
  970. }
  971. /* 商品价格曲线/利润曲线
  972. */
  973. const commodityChartRenderData = ref<any>({})
  974. const initCommodityData = (data: { XDataList: any[]; YDataList: any; EdbInfoList: any; ChartInfo: any; DataResp?:any }) => {
  975. const { XDataList,YDataList,EdbInfoList,ChartInfo,DataResp } = data;
  976. commodityChartRenderData.value = {
  977. commodityEdbList: EdbInfoList,
  978. commodityChartData: ChartInfo.Source===5?DataResp.YDataList:YDataList,
  979. commodityXData: ChartInfo.Source===5?DataResp.XDataList:XDataList
  980. }
  981. if(ChartInfo.Source===5) {
  982. state.chartInfo = {
  983. ...state.chartInfo,
  984. ProfitName: DataResp.ProfitName,
  985. ProfitNameEn: DataResp.ProfitNameEn
  986. }
  987. }
  988. state.chartLimit = {
  989. min: Number(ChartInfo.LeftMin),
  990. max: Number(ChartInfo.LeftMax)
  991. }
  992. return setCommodityChart();
  993. }
  994. /* 商品价格曲线设置 绘图逻辑同奇怪柱形图*/
  995. const setCommodityChart = () => {
  996. const {chartLimit,chartInfo} = state;
  997. const { commodityChartData,commodityXData,commodityEdbList } = commodityChartRenderData.value;
  998. let seriesData:any[] = [];
  999. const data = _.cloneDeep(commodityChartData);
  1000. /* 主题样式*/
  1001. const chartTheme = chartInfo.ChartThemeStyle ? JSON.parse(chartInfo.ChartThemeStyle) : null;
  1002. //x轴
  1003. let xAxis = {
  1004. ...scatterXAxis,
  1005. categories: commodityXData.map(_ => language.value === 'zh' ? _.Name:_.NameEn),
  1006. tickWidth: 1,
  1007. labels: {
  1008. style: {
  1009. ...chartTheme&&chartTheme.xAxisOptions.style
  1010. }
  1011. }
  1012. }
  1013. const { max,min } = chartLimit;
  1014. //y轴
  1015. let yAxis = {
  1016. ...basicYAxis,
  1017. title: {
  1018. text: language.value === 'zh' ? commodityEdbList[0].Unit : commodityEdbList[0].UnitEn,
  1019. align: 'high',
  1020. rotation: 0,
  1021. y: -12,
  1022. textAlign: 'left',
  1023. reserveSpace: false,
  1024. style:{
  1025. ...chartTheme&&chartTheme.yAxisOptions.style
  1026. },
  1027. },
  1028. labels: {
  1029. formatter: function (ctx:any) {
  1030. let val = ctx.value;
  1031. return val;
  1032. },
  1033. align: 'center',
  1034. style:{
  1035. ...chartTheme&&chartTheme.yAxisOptions.style
  1036. },
  1037. },
  1038. min: Number(min),
  1039. max: Number(max),
  1040. opposite: false,
  1041. tickWidth: 1,
  1042. }
  1043. //数据列
  1044. data.forEach((item: { Value: number[]; Name: string; Date: string; Color: string;NameEn: string,XEdbInfoIdList: number[],NoDataEdbList: number[] }) => {
  1045. //处理首或/尾全是无效数据的以null填充
  1046. let filterData = filterInvalidData(item)
  1047. let serie_item = {
  1048. data: filterData,
  1049. type: (chartTheme&&chartTheme.lineOptions.lineType) || 'spline',
  1050. dashStyle: (chartTheme&&chartTheme.lineOptions.dashStyle)||'Solid',
  1051. yAxis: 0,
  1052. name: language.value === 'zh' ? item.Name : item.NameEn,
  1053. color: item.Color,
  1054. chartType: 'linear',
  1055. lineWidth: (chartTheme&&chartTheme.lineOptions.lineWidth) || 3,
  1056. marker: {
  1057. enabled: false
  1058. }
  1059. };
  1060. seriesData.push(serie_item)
  1061. })
  1062. //tooltip
  1063. let tooltip = {
  1064. formatter: function() {
  1065. const ctx: any = this;
  1066. let str: string = '';
  1067. if(language.value === 'zh') {
  1068. ctx.points.forEach((item: { series:{name: string},y: number,color: string }) => {
  1069. let obj_item = data.find((_:any) => _.Name === item.series.name);
  1070. let index = commodityXData.findIndex(_ => _.Name === ctx.x);
  1071. //合约显示
  1072. let haveContract = obj_item.XEdbInfoIdList[index];
  1073. if(haveContract) {
  1074. // 利润曲线指标名
  1075. let edb_name = chartInfo.Source === 5
  1076. ? (index === 0 ? obj_item.NameList[index] : `${chartInfo.ProfitName}(${obj_item.NameList[index]})`)
  1077. : commodityEdbList.find(_ => _.EdbInfoId === obj_item.XEdbInfoIdList[index]).EdbName;
  1078. str+=`<b>${ edb_name }</b>`
  1079. if(!obj_item.NoDataEdbList.includes(obj_item.XEdbInfoIdList[index])) {
  1080. str += `<br><span style="color:${item.color}">\u25CF</span>${obj_item.Date}: ${item.y}<br>`
  1081. }else {
  1082. str += `<br><span style="color:${item.color}">\u25CF</span>${obj_item.Date}: 无<br>`
  1083. }
  1084. }
  1085. })
  1086. }else {
  1087. ctx.points.forEach((item: { series:{name: string},y: number,color: string }) => {
  1088. let obj_item = data.find((_: any) => _.NameEn === item.series.name);
  1089. let index = commodityXData.findIndex(_ => _.NameEn === ctx.x);
  1090. let haveContract = obj_item.XEdbInfoIdList[index];
  1091. if(haveContract) {
  1092. let edb_name = chartInfo.Source === 5
  1093. ? (index === 0 ? obj_item.NameList[index] : `${chartInfo.ProfitNameEn}(${obj_item.NameList[index]})`)
  1094. : commodityEdbList.find(_ => _.EdbInfoId === obj_item.XEdbInfoIdList[index]).EdbNameEn;
  1095. str+=`<b>${ edb_name }</b>`
  1096. if(!obj_item.NoDataEdbList.includes(obj_item.XEdbInfoIdList[index])) {
  1097. str += `<br><span style="color:${item.color}">\u25CF</span>${obj_item.Date}: ${item.y}<br>`
  1098. }else {
  1099. str += `<br><span style="color:${item.color}">\u25CF</span>${obj_item.Date}: 无<br>`
  1100. }
  1101. }
  1102. })
  1103. }
  1104. return str || '无合约'
  1105. },
  1106. shared: true
  1107. }
  1108. return {
  1109. title: {
  1110. text:''
  1111. },
  1112. series: seriesData,
  1113. yAxis: [ yAxis ],
  1114. xAxis,
  1115. tooltip
  1116. }
  1117. };
  1118. /* 处理无效数据为null */
  1119. const filterInvalidData = (item:{ Value: number[]; Name: string; Date: string; Color: string;NameEn: string,XEdbInfoIdList: number[],NoDataEdbList: number[] })=> {
  1120. let validateArr = item.XEdbInfoIdList.filter(_ => _&&!item.NoDataEdbList.includes(_));
  1121. let first_index = item.XEdbInfoIdList.findIndex(_ => _ === validateArr[0]);
  1122. let last_index = item.XEdbInfoIdList.findIndex(_ => _ === validateArr[validateArr.length-1]);
  1123. console.log('first_index',first_index)
  1124. console.log('last_index',last_index)
  1125. let arr = item.Value.map((item,index:number) => {
  1126. if(index < first_index || index > last_index) {
  1127. return null
  1128. }else {
  1129. return item
  1130. }
  1131. })
  1132. return arr;
  1133. }
  1134. /* 相关性图表 */
  1135. const initRelevanceChartData=(data)=>{
  1136. /* 主题样式*/
  1137. const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null;
  1138. const relevanceUnitEnMap={
  1139. '年': 'Year',
  1140. '季': 'Season',
  1141. '月': 'Month',
  1142. '周': 'Week',
  1143. '天': 'Day',
  1144. }
  1145. // 处理X轴
  1146. let xAxis={
  1147. categories: data.ChartInfo.Source===3 ? data.XEdbIdValue : data.DataResp.XDateTimeValue,
  1148. tickWidth: 1,
  1149. title: {
  1150. text: data.ChartInfo.Source===3 ? (language.value=='zh'?`期数(${data.CorrelationChartInfo.LeadUnit})`:`stage(${relevanceUnitEnMap[data.CorrelationChartInfo.LeadUnit]})`):null,
  1151. align: 'high',
  1152. rotation: 0,
  1153. style: {
  1154. ...chartTheme&&chartTheme.xAxisOptions.style
  1155. }
  1156. },
  1157. labels: {
  1158. style: {
  1159. ...chartTheme&&chartTheme.xAxisOptions.style
  1160. }
  1161. },
  1162. tickInterval: 1,
  1163. offset:0,
  1164. tickmarkPlacement:'on'
  1165. }
  1166. // 处理Y轴
  1167. let yAxis={
  1168. ...basicYAxis,
  1169. title: {
  1170. text: language.value=='zh'?'相关性系数':'Correlation coefficient',
  1171. align: 'high',
  1172. rotation: 0,
  1173. y: -12,
  1174. textAlign: 'left',
  1175. reserveSpace: false,
  1176. style:{
  1177. ...chartTheme&&chartTheme.yAxisOptions.style
  1178. },
  1179. },
  1180. labels: {
  1181. formatter: function (ctx) {
  1182. let val = ctx.value;
  1183. return val;
  1184. },
  1185. align: 'center',
  1186. style:{
  1187. ...chartTheme&&chartTheme.yAxisOptions.style
  1188. },
  1189. },
  1190. opposite: false,
  1191. tickWidth: 1,
  1192. }
  1193. //处理series
  1194. let seriesData:any[]=[]
  1195. data.YDataList.forEach(item=>{
  1196. let serie_item = {
  1197. data: item.Value,
  1198. type: (chartTheme&&chartTheme.lineOptions.lineType) || 'spline',
  1199. dashStyle: (chartTheme&&chartTheme.lineOptions.dashStyle)||'Solid',
  1200. yAxis: 0,
  1201. name: language.value=='zh'?data.ChartInfo.ChartName:data.ChartInfo.ChartNameEn,
  1202. color: item.Color,
  1203. chartType: 'linear',
  1204. lineWidth: (chartTheme&&chartTheme.lineOptions.lineWidth) || 3,
  1205. marker: {
  1206. enabled: false
  1207. }
  1208. };
  1209. seriesData.push(serie_item)
  1210. })
  1211. const { LeadValue,LeadUnit } = data.CorrelationChartInfo;
  1212. let tooltip = {
  1213. formatter: function() {
  1214. const that:any = this;
  1215. let str='';
  1216. if(language.value=='zh'){
  1217. str = `<p>相关性系数:${that.y.toFixed(4)}</p><br><p>领先${ data.ChartInfo.Source===3 ?that.x+'期' : LeadValue+LeadUnit}</p>`
  1218. }else{
  1219. str = `<p>Correlation coefficient:${that.y.toFixed(4)}</p><br><p>lead${ data.ChartInfo.Source===3 ? that.x+'stage' : LeadValue+relevanceUnitEnMap[LeadUnit]}</p>`
  1220. }
  1221. return str
  1222. },
  1223. }
  1224. return {
  1225. isRelevanceChart:data.ChartInfo.Source===3,
  1226. title: {
  1227. text:'',
  1228. },
  1229. series: seriesData,
  1230. yAxis: [yAxis] ,
  1231. xAxis:xAxis,
  1232. tooltip
  1233. }
  1234. }
  1235. /* 统计特征
  1236. 标准差
  1237. 百分比
  1238. 频率
  1239. 滚动相关性*/
  1240. const statisticFrequencyRenderData = ref<any>({});
  1241. const initStatisticChartData = (data: { DataResp:any,ChartInfo:any,CorrelationChartInfo:any }) => {
  1242. if(data.ChartInfo.Source === 9) { //频率图
  1243. statisticFrequencyRenderData.value = data.DataResp;
  1244. return setStatisticFrequency();
  1245. }else {
  1246. state.dataList = [data.DataResp];
  1247. if(data.ChartInfo.Source === 4) statisticFrequencyRenderData.value = data.CorrelationChartInfo;
  1248. return setDefaultLineOptions();
  1249. }
  1250. }
  1251. /* 统计频率图 */
  1252. const setStatisticFrequency = () => {
  1253. const { DataList,LeftMaxValue,LeftMinValue,RightMaxValue,RightMinValue } = statisticFrequencyRenderData.value
  1254. if(!DataList){
  1255. return
  1256. }
  1257. /* 主题样式*/
  1258. const chartTheme = state.chartInfo.ChartThemeStyle ? JSON.parse(state.chartInfo.ChartThemeStyle) : null;
  1259. let xAxis = {
  1260. ...scatterXAxis,
  1261. tickWidth: 1,
  1262. labels: {
  1263. style: {
  1264. ...chartTheme&&chartTheme.xAxisOptions.style
  1265. }
  1266. }
  1267. }
  1268. //y和系列
  1269. let yAxis:any[] = [],series:any[] = [];
  1270. DataList.forEach((item,index) => {
  1271. let y_item = {
  1272. ...basicYAxis,
  1273. title: {
  1274. text: language.value === 'zh' ? item.Unit : item.UnitEn,
  1275. align: 'high',
  1276. rotation: 0,
  1277. y: -12,
  1278. reserveSpace: false,
  1279. style:{
  1280. ...chartTheme&&chartTheme.yAxisOptions.style
  1281. },
  1282. },
  1283. labels: {
  1284. style:{
  1285. ...chartTheme&&chartTheme.yAxisOptions.style
  1286. },
  1287. },
  1288. opposite: item.IsAxis===1?false:true,
  1289. min: index===0? Number(LeftMinValue):Number(RightMinValue),
  1290. max: index===0? Number(LeftMaxValue):Number(RightMaxValue),
  1291. tickWidth: 1,
  1292. }
  1293. let series_item = {
  1294. data: item.Value.map(_ =>[_.X,_.Y]),
  1295. dashStyle: (chartTheme&&chartTheme.lineOptions.dashStyle)||'Solid',
  1296. type: (chartTheme&&chartTheme.lineOptions.lineType) || 'spline',
  1297. yAxis: index,
  1298. name: language.value === 'zh' ? item.Name : item.NameEn,
  1299. color: item.Color,
  1300. lineWidth: (chartTheme&&chartTheme.lineOptions.lineWidth)||3,
  1301. chartType: 'linear',
  1302. zIndex:1
  1303. }
  1304. series.push(series_item);
  1305. yAxis.push(y_item)
  1306. })
  1307. let tooltip = {
  1308. formatter: function() {
  1309. let that:any = this;
  1310. let xList = DataList[0].Value.map(_ =>_.X);
  1311. let step = xList[1]-xList[0];
  1312. let data_interval = `[${Number(that.x).toFixed(2)},${Number(that.x+step).toFixed(2)}]`;
  1313. let str=`<b>${ data_interval }</b>`;
  1314. that.points.forEach(item => {
  1315. str += `<br><span style="color:${item.color}">\u25CF</span>${item.series.name}: ${item.y}%`
  1316. })
  1317. return str
  1318. },
  1319. shared: true
  1320. }
  1321. return {
  1322. title: {
  1323. text:''
  1324. },
  1325. tooltip,
  1326. series,
  1327. yAxis,
  1328. xAxis
  1329. }
  1330. }
  1331. /* 拟合方程曲线 曲线图绘图逻辑*/
  1332. const initFittingEquation = (data) => {
  1333. state.dataList = [data.DataResp];
  1334. return setDefaultLineOptions();
  1335. }
  1336. /* 跨品种分析 */
  1337. const crossVarietyChartRenderData = ref<any>({})
  1338. const initCrossVarietyChartData = (data:{ DataResp:any,ChartInfo:any,EdbInfoList:any }) => {
  1339. state.dataList = data.EdbInfoList;
  1340. crossVarietyChartRenderData.value = data.DataResp;
  1341. /* 历史数据chartInfo里全是空 兼容下历史数据不崩 */
  1342. state.chartLimit.min=data.ChartInfo.LeftMin?Number(data.ChartInfo.LeftMin):Number(data.DataResp.YMinValue)
  1343. state.chartLimit.max=data.ChartInfo.LeftMax?Number(data.ChartInfo.LeftMax):Number(data.DataResp.YMaxValue)
  1344. state.chartLimit.x_min=data.ChartInfo.XMin?Number(data.ChartInfo.XMin):Number(data.DataResp.XMinValue)
  1345. state.chartLimit.x_max=data.ChartInfo.XMax?Number(data.ChartInfo.XMax):Number(data.DataResp.XMaxValue)
  1346. //二次校验英文配置是否完整
  1347. // if(routeQuery.value.fromPage === 'en' && language.value==='en') {
  1348. // const { XNameEn,YNameEn,DataList } = data.DataResp;
  1349. // let isAllEn = true;
  1350. // let haveOneNoEn = DataList.some(_ => !_.NameEn);
  1351. // if(!XNameEn || !YNameEn || haveOneNoEn) isAllEn = false;
  1352. // language.value = isAllEn ? 'en' : 'zh';
  1353. // }
  1354. return setCrossVarietyChart()
  1355. }
  1356. /* 跨品种分析 */
  1357. const setCrossVarietyChart = () => {
  1358. const { chartLimit,dataList,chartInfo } = state;
  1359. /* 主题样式*/
  1360. const chartTheme = chartInfo.ChartThemeStyle ? JSON.parse(chartInfo.ChartThemeStyle) : null;
  1361. const { min,max,x_min,x_max } = chartLimit;
  1362. const { DataList,XName,YName,XNameEn,YNameEn } = crossVarietyChartRenderData.value;
  1363. //y轴
  1364. let yAxis = {
  1365. ...basicYAxis,
  1366. title: {
  1367. text: language.value === 'zh' ? YName : YNameEn,
  1368. align: 'middle',
  1369. style: {
  1370. ...chartTheme&&chartTheme.xAxisOptions.style
  1371. }
  1372. },
  1373. labels: {
  1374. style: {
  1375. ...chartTheme&&chartTheme.xAxisOptions.style
  1376. }
  1377. },
  1378. opposite: false,
  1379. reversed: false,
  1380. min: Number(min),
  1381. max: Number(max),
  1382. tickWidth: 1,
  1383. }
  1384. // x轴
  1385. let xAxis = {
  1386. ...scatterXAxis,
  1387. title: {
  1388. text: language.value === 'zh' ? XName : XNameEn,
  1389. align: 'middle',
  1390. style: {
  1391. ...chartTheme&&chartTheme.xAxisOptions.style
  1392. }
  1393. },
  1394. labels: {
  1395. style: {
  1396. ...chartTheme&&chartTheme.xAxisOptions.style
  1397. }
  1398. },
  1399. min: Number(x_min),
  1400. max: Number(x_max),
  1401. }
  1402. //数据列
  1403. let series:any[] = [];
  1404. const tagMap = { //标签对应文字
  1405. 1: '最新',
  1406. 3: 'Fix'
  1407. }
  1408. DataList.forEach(item => {
  1409. //数据列
  1410. let series_item = {
  1411. data: [] as any[],
  1412. type: 'scatter',
  1413. name: language.value === 'zh' ? item.Name : item.NameEn,
  1414. color: item.Color,
  1415. lineWidth: 0,
  1416. chartType: 'linear',
  1417. zIndex:1
  1418. }
  1419. item.CoordinatePointData.forEach(_ => {
  1420. series_item.data.push({
  1421. x: _.X,
  1422. y: _.Y,
  1423. dataLabels: {
  1424. enabled: _.ShowTips===1,
  1425. allowOverlap: true,
  1426. align: 'left',
  1427. format: tagMap[_.DateType] || `-${_.DaysAgo}D`,
  1428. }
  1429. })
  1430. })
  1431. series.push(series_item);
  1432. })
  1433. let tooltip = {
  1434. formatter: function() {
  1435. let that:any = this;
  1436. let str = '';
  1437. if(language.value === 'zh') {
  1438. let series_obj = DataList.find(_ => _.Name === that.series.name);
  1439. let ponit_obj = series_obj.CoordinatePointData.find(_ => _.X ===that.x && _.Y===that.y);
  1440. let xEdbInfo = dataList.find(_ => _.EdbInfoId===ponit_obj.XEdbInfoId);
  1441. let yEdbInfo = dataList.find(_ => _.EdbInfoId===ponit_obj.YEdbInfoId);
  1442. str=`<b>${ that.series.name }</b>`;
  1443. str += `<br><span style="color:${that.color}">\u25CF</span>${xEdbInfo.EdbName}: ${that.x} ${ponit_obj.XDate}<br>`;
  1444. str += `<span style="color:${that.color}">\u25CF</span>${yEdbInfo.EdbName}: ${that.y} ${ponit_obj.YDate}`;
  1445. }else {
  1446. let series_obj = DataList.find(_ => _.NameEn === that.series.name);
  1447. let ponit_obj = series_obj.CoordinatePointData.find(_ => _.X ===that.x && _.Y===that.y);
  1448. let xEdbInfo = dataList.find(_ => _.EdbInfoId===ponit_obj.XEdbInfoId);
  1449. let yEdbInfo = dataList.find(_ => _.EdbInfoId===ponit_obj.YEdbInfoId);
  1450. str=`<b>${ that.series.name }</b>`;
  1451. str += `<br><span style="color:${that.color}">\u25CF</span>${xEdbInfo.EdbNameEn}: ${that.x} ${ponit_obj.XDate}<br>`;
  1452. str += `<span style="color:${that.color}">\u25CF</span>${yEdbInfo.EdbNameEn}: ${that.y} ${ponit_obj.YDate}`;
  1453. }
  1454. return str
  1455. }
  1456. }
  1457. return {
  1458. title: {
  1459. text:''
  1460. },
  1461. series,
  1462. yAxis: [yAxis],
  1463. xAxis,
  1464. tooltip
  1465. }
  1466. }
  1467. /* 雷达图 */
  1468. const radarChartRenderData = ref<any>({})
  1469. const initRadarData = (data) => {
  1470. const { DataResp,EdbInfoList,ChartInfo } = data;
  1471. radarChartRenderData.value = {
  1472. YDataList: DataResp.YDataList,
  1473. XDataList: EdbInfoList.filter(_ => DataResp.XEdbIdValue.includes(_.EdbInfoId))
  1474. }
  1475. state.chartLimit.min=Number(ChartInfo.LeftMin)
  1476. state.chartLimit.max=Number(ChartInfo.LeftMax)
  1477. return setRadarChart();
  1478. }
  1479. const setRadarChart = () => {
  1480. const { YDataList,XDataList } = radarChartRenderData.value;
  1481. const { chartLimit,dataList,chartInfo } = state;
  1482. /* 主题样式*/
  1483. const chartTheme = chartInfo.ChartThemeStyle ? JSON.parse(chartInfo.ChartThemeStyle) : null;
  1484. let categories = language.value==='zh'
  1485. ? XDataList.map(_ => _.EdbAliasName||_.EdbName)
  1486. : XDataList.map(_ => _.EdbNameEn)
  1487. //x轴
  1488. let xAxis = {
  1489. lineWidth: 0,
  1490. tickLength: 0,
  1491. tickmarkPlacement: 'on',
  1492. categories,
  1493. labels: {
  1494. allowOverlap: true,
  1495. // align:'center',
  1496. distance: 20,
  1497. style: {
  1498. ...chartTheme&&chartTheme.xAxisOptions.style
  1499. }
  1500. }
  1501. }
  1502. //y轴
  1503. const { max,min } = chartLimit;
  1504. let yAxis = [{
  1505. gridLineInterpolation: 'polygon',
  1506. gridLineWidth: 1,
  1507. lineWidth: 0,
  1508. endOnTick: false,
  1509. startOnTick: false,
  1510. showLastLabel: true,
  1511. // tickAmount:4,
  1512. title: {
  1513. text: language.value==='zh' ? chartInfo.Unit : chartInfo.UnitEn,
  1514. align: 'high',
  1515. rotation: 0,
  1516. y: 5,
  1517. x:10,
  1518. textAlign: 'left',
  1519. reserveSpace: false,
  1520. style:{
  1521. ...chartTheme&&chartTheme.yAxisOptions.style
  1522. },
  1523. },
  1524. labels: {
  1525. allowOverlap: true,
  1526. style:{
  1527. ...chartTheme&&chartTheme.yAxisOptions.style
  1528. }
  1529. },
  1530. min: Number(min),
  1531. max: Number(max),
  1532. }]
  1533. //系列
  1534. let series:any[] = [];
  1535. YDataList.forEach(item => {
  1536. let serie_item = {
  1537. data: item.Value,
  1538. pointPlacement: 'on',
  1539. type: (chartTheme&&chartTheme.lineOptions.lineType) || 'line',
  1540. dashStyle: (chartTheme&&chartTheme.lineOptions.dashStyle)||'Solid',
  1541. yAxis: 0,
  1542. name: language.value==='zh' ? (item.Name || item.Date) : item.Date,
  1543. color: item.Color,
  1544. lineWidth: (chartTheme&&chartTheme.lineOptions.lineWidth) || 1,
  1545. chartType: 'linear'
  1546. };
  1547. series.push(serie_item)
  1548. })
  1549. return {
  1550. chart: {
  1551. ...defaultOpts.chart,
  1552. ...chartTheme.drawOption,
  1553. spacing: [2,10,2,10],
  1554. polar:true,
  1555. },
  1556. title: {
  1557. text:''
  1558. },
  1559. pane: {
  1560. size: '80%'
  1561. },
  1562. series,
  1563. yAxis,
  1564. xAxis
  1565. }
  1566. }
  1567. // 曲线图x轴显示计算年限差 >1年 显示年/月 <=1 显示月/日
  1568. const xTimeDiffer = () => {
  1569. const end_date = state.chartInfo.DateType === 5
  1570. ? state.chartInfo.EndDate
  1571. : state.chartInfo.DateType === 6
  1572. ? new Date()
  1573. : '';
  1574. //年限差
  1575. const year_differ = moment(end_date).diff(
  1576. moment(state.chartInfo.StartDate),
  1577. 'years',
  1578. true
  1579. );
  1580. // console.log(year_differ)
  1581. if ([5, 6].includes(state.chartInfo.DateType) && year_differ <= 1) {
  1582. return true
  1583. } else {
  1584. return false
  1585. }
  1586. }
  1587. /* 设置x轴步长 刻度数量多一点 */
  1588. const setXaxisStep = (timestamp: number) => {
  1589. return timestamp / 6;
  1590. }
  1591. /* 指标顺序调整 IsAxis: 0右轴 1左轴 2右2*/
  1592. const changeEdbOrder = (data: any[]) => {
  1593. // 左轴指标
  1594. let left_edbs = data.filter(_ => _.IsAxis===1);
  1595. //右轴指标
  1596. let right_edbs = data.filter(_ => !_.IsAxis);
  1597. // 右2轴指标
  1598. let right_two_edbs = data.filter(_ => _.IsAxis === 2);
  1599. // 按 左 右 右2顺序排列
  1600. return [left_edbs,right_edbs,right_two_edbs].flat(Infinity);
  1601. }
  1602. /* 拼接动态的指标名称小标签 */
  1603. const concatDynamicTag = (
  1604. { IsAxis, IsOrder, EdbInfoType, LeadValue, LeadUnit }: IDataProps,
  1605. lang: String = 'zh'
  1606. ): string => {
  1607. // IsAxis左轴1 右轴0 2右2轴
  1608. //IsOrder正序false 逆序true
  1609. //EdbInfoType是否是领先指标
  1610. const axisLabelMap: any =
  1611. lang == 'zh'
  1612. ? {
  1613. 0: "右轴",
  1614. 2: "右2轴",
  1615. }
  1616. : {
  1617. 0: "RHS",
  1618. 2: "2-RHS",
  1619. };
  1620. const orderLabelMap: any =
  1621. lang == 'zh'
  1622. ? {
  1623. 1: "逆序",
  1624. }
  1625. : {
  1626. 1: "REV",
  1627. };
  1628. const edbInfoMap: any =
  1629. lang == 'zh'
  1630. ? {
  1631. 0: "领先",
  1632. }
  1633. : {
  1634. 0: "Lead",
  1635. };
  1636. const leadUnitEnMap: any = {
  1637. 年: "Y",
  1638. 季: "Q",
  1639. 月: "M",
  1640. 周: "W",
  1641. 天: "D",
  1642. };
  1643. //英文领先单位转换
  1644. const edbLeadUnit = lang == 'zh' ? LeadUnit : leadUnitEnMap[LeadUnit];
  1645. let axis_tag = axisLabelMap[IsAxis] || "";
  1646. //逆序拼接
  1647. let order_tag = orderLabelMap[Number(IsOrder)]
  1648. ? `${axis_tag ? "," : ""}${orderLabelMap[Number(IsOrder)]}`
  1649. : "";
  1650. //领先拼接
  1651. let edb_tag = edbInfoMap[EdbInfoType]
  1652. ? `${axis_tag || order_tag ? "," : ""}${
  1653. edbInfoMap[EdbInfoType]
  1654. } ${LeadValue}${edbLeadUnit}`
  1655. : "";
  1656. return axis_tag || order_tag || edb_tag
  1657. ? `(${axis_tag}${order_tag}${edb_tag})`
  1658. : "";
  1659. };
  1660. /* 预测配置 分区 */
  1661. const getPredictParams = (
  1662. { LatestDate, MoveLatestDate, PredictChartColor, ChartStyle }: IDataProps,
  1663. chartStyle = ""
  1664. ) => {
  1665. return {
  1666. zoneAxis: "x",
  1667. zones: [
  1668. {
  1669. value: new Date(MoveLatestDate || LatestDate).getTime() + 1,
  1670. },
  1671. {
  1672. dashStyle: "ShortDot",
  1673. color:
  1674. ChartStyle === "column" || chartStyle === "column"
  1675. ? "transparent"
  1676. : PredictChartColor,
  1677. },
  1678. ],
  1679. };
  1680. };
  1681. /* 季节图预测数据 年份=分割点年份做分割 年份>分割点年份全为预测 */
  1682. const getSeasonPredictParams = (timestamp: number) => {
  1683. return timestamp
  1684. ? {
  1685. zoneAxis: "x",
  1686. zones: [
  1687. {
  1688. value: new Date(timestamp).getTime() + 1,
  1689. },
  1690. {
  1691. dashStyle: "ShortDot",
  1692. },
  1693. ],
  1694. }
  1695. : {};
  1696. };
  1697. /* 处理轴的标识线结构 在指定轴位置上拼接标识线
  1698. 0:右轴 1:左轴 2:右2轴 x轴固定3
  1699. axisType表示x轴类型 处理时间轴的值 datetime/null
  1700. */
  1701. const setAxisPlotLines = (axis: number, axisType: any = null) => {
  1702. const { MarkersLines, ChartType } = state.chartInfo;
  1703. if (!MarkersLines) return [];
  1704. let markerLines = JSON.parse(MarkersLines);
  1705. let arr = markerLines.filter((_) => _.isShow && _.axis === axis);
  1706. let plotLines = arr.map((_) => {
  1707. //是否是x时间轴
  1708. let isXDateAxis = axis === 3 && axisType === "datetime";
  1709. let markerValue:number;
  1710. if (isXDateAxis) {
  1711. //季节图x轴额外拼个年份
  1712. let nowYear = ChartType===2 ? new Date(state.dataList[0].DataList[1].DataList
  1713. [0].DataTimestamp).getFullYear() : '';
  1714. markerValue =
  1715. ChartType === 2
  1716. ? new Date(`${nowYear}-${_.value}`).getTime()
  1717. : new Date(_.value).getTime();
  1718. } else {
  1719. markerValue = Number(_.value);
  1720. }
  1721. return {
  1722. value: markerValue,
  1723. dashStyle: _.dashStyle,
  1724. width: Number(_.lineWidth),
  1725. color: _.color,
  1726. label: {
  1727. text: _.text || "",
  1728. verticalAlign: _.textPosition,
  1729. style: {
  1730. color: _.textColor,
  1731. fontSize: _.textFontSize,
  1732. },
  1733. },
  1734. };
  1735. });
  1736. return plotLines;
  1737. };
  1738. /* 处理标识区拼接 axisType表示x轴类型处理时间轴的值 datetime/null */
  1739. const setAxisPlotAreas = (axis: number, axisType: any = null) => {
  1740. const { MarkersAreas, ChartType } = state.chartInfo;
  1741. if (!MarkersAreas) return [];
  1742. let markerAreas = JSON.parse(MarkersAreas);
  1743. let arr = markerAreas.filter((_) => _.isShow && _.axis === axis);
  1744. let plotBands = arr.map((_) => {
  1745. //是否是x时间轴
  1746. let isXDateAxis = axis === 3 && axisType === "datetime";
  1747. let fromMarkerValue:number, toMarkerValue:number;
  1748. if (isXDateAxis) {
  1749. //季节图x轴额外拼个年份
  1750. let nowYear = ChartType===2 ? new Date(state.dataList[0].DataList[1].DataList
  1751. [0].DataTimestamp).getFullYear() : '';
  1752. fromMarkerValue =
  1753. ChartType === 2
  1754. ? new Date(`${nowYear}-${_.fromValue}`).getTime()
  1755. : new Date(_.fromValue).getTime();
  1756. toMarkerValue =
  1757. ChartType === 2
  1758. ? new Date(`${nowYear}-${_.toValue}`).getTime()
  1759. : new Date(_.toValue).getTime();
  1760. } else {
  1761. fromMarkerValue = Number(_.fromValue);
  1762. toMarkerValue = Number(_.toValue);
  1763. }
  1764. //默认label有些偏移 重新归正下
  1765. let positionMapValue = {
  1766. 'top': 12,
  1767. 'middle': 0,
  1768. 'bottom': -10
  1769. }
  1770. return {
  1771. from: fromMarkerValue,
  1772. to: toMarkerValue,
  1773. color: _.color,
  1774. label: {
  1775. text: _.text || "",
  1776. verticalAlign: _.textPosition,
  1777. y: positionMapValue[_.textPosition],
  1778. style: {
  1779. color: _.textColor,
  1780. fontSize: _.textFontSize,
  1781. },
  1782. },
  1783. };
  1784. });
  1785. return plotBands;
  1786. };
  1787. /* ----自定义上下限相关--- */
  1788. const setLimitData = (tableData:any=[])=>{
  1789. const {
  1790. //左右轴极值字段
  1791. LeftMin=0,LeftMax=0,
  1792. RightMin=0,RightMax=0,
  1793. Right2Min=0,Right2Max=0,
  1794. MinMaxSave
  1795. } = state.chartInfo
  1796. if(MinMaxSave){
  1797. state.chartLimit.min = Number(LeftMin)
  1798. state.chartLimit.max = Number(LeftMax)
  1799. state.chartLimit.rightMin = Number(RightMin)
  1800. state.chartLimit.rightMax = Number(RightMax)
  1801. state.chartLimit.rightTwoMin = Number(Right2Min)
  1802. state.chartLimit.rightTwoMax = Number(Right2Max)
  1803. //若用户修改过,则检测轴的上下限是否为空,若为空,则需要计算对应轴的上下限
  1804. checkLimit(tableData)
  1805. }else{
  1806. calcYAxislimit(tableData)
  1807. }
  1808. }
  1809. const checkLimit = (tableData:any=[])=>{
  1810. //散点图单独处理
  1811. if(state.chartInfo.ChartType===5){
  1812. if(tableData[1]){
  1813. if(Number(state.chartLimit.min)===0&&Number(state.chartLimit.max)===0){
  1814. state.chartLimit.min = tableData[1].MinData
  1815. state.chartLimit.max = tableData[1].MaxData
  1816. }
  1817. }
  1818. return
  1819. }
  1820. //若轴的上下限均为0,则不管用户有没有修改过,都重新赋值
  1821. if(Number(state.chartLimit.min)===0&&Number(state.chartLimit.max)===0){
  1822. const leftData = tableData.filter((i:IDataProps)=>i.IsAxis===1).map((i:IDataProps)=>[Number(i.MinData),Number(i.MaxData)])
  1823. if(leftData.length){
  1824. const {Max,Min} = calcLimit(leftData.flat())
  1825. state.chartLimit.min = Min
  1826. state.chartLimit.max = Max
  1827. }else{
  1828. state.chartLimit.min = 0
  1829. state.chartLimit.max = 0
  1830. }
  1831. }
  1832. if(Number(state.chartLimit.rightMin)===0&&Number(state.chartLimit.rightMax)===0){
  1833. const rightData = tableData.filter((i:IDataProps) => !i.IsAxis).map((i:IDataProps)=>[Number(i.MinData),Number(i.MaxData)])
  1834. if(rightData.length){
  1835. const {Max,Min} = calcLimit(rightData.flat())
  1836. state.chartLimit.rightMin = Min
  1837. state.chartLimit.rightMax = Max
  1838. }else{
  1839. state.chartLimit.rightMin = 0
  1840. state.chartLimit.rightMax = 0
  1841. }
  1842. }
  1843. if(Number(state.chartLimit.rightTwoMin)===0&&Number(state.chartLimit.rightTwoMax)===0){
  1844. const rightTwoData = tableData.filter((i:IDataProps)=>i.IsAxis===2).map((i:IDataProps)=>[Number(i.MinData),Number(i.MaxData)])
  1845. if(rightTwoData.length){
  1846. const {Max,Min} = calcLimit(rightTwoData.flat())
  1847. state.chartLimit.rightTwoMin = Min
  1848. state.chartLimit.rightTwoMax = Max
  1849. }else{
  1850. state.chartLimit.rightTwoMin = 0
  1851. state.chartLimit.rightTwoMax = 0
  1852. }
  1853. }
  1854. }
  1855. /* 计算y轴上下限 */
  1856. const calcYAxislimit = (tableData:any=[])=>{
  1857. //散点图单独处理
  1858. if(state.chartInfo.ChartType===5){
  1859. if(tableData[1]){
  1860. state.chartLimit.min = tableData[1].MinData
  1861. state.chartLimit.max = tableData[1].MaxData
  1862. }
  1863. return
  1864. }
  1865. //分组
  1866. const leftData = tableData.filter((i:IDataProps) => i.IsAxis === 1).map((i:IDataProps) => [Number(i.MinData), Number(i.MaxData)])
  1867. const rightData = tableData.filter((i:IDataProps) => !i.IsAxis).map((i:IDataProps) => [Number(i.MinData), Number(i.MaxData)])
  1868. const rightTwoData = tableData.filter((i:IDataProps) => i.IsAxis === 2).map((i:IDataProps) => [Number(i.MinData), Number(i.MaxData)])
  1869. //计算最大最小值
  1870. if (leftData.length) {
  1871. const {
  1872. Max,
  1873. Min
  1874. } = calcLimit(leftData.flat())
  1875. state.chartLimit.min = Min
  1876. state.chartLimit.max = Max
  1877. } else {
  1878. state.chartLimit.min = 0
  1879. state.chartLimit.max = 0
  1880. }
  1881. if (rightData.length) {
  1882. const {
  1883. Max,
  1884. Min
  1885. } = calcLimit(rightData.flat())
  1886. state.chartLimit.rightMin = Min
  1887. state.chartLimit.rightMax = Max
  1888. } else {
  1889. state.chartLimit.rightMin = 0
  1890. state.chartLimit.rightMax = 0
  1891. }
  1892. if (rightTwoData.length) {
  1893. const {
  1894. Max,
  1895. Min
  1896. } = calcLimit(rightTwoData.flat())
  1897. state.chartLimit.rightTwoMin = Min
  1898. state.chartLimit.rightTwoMax = Max
  1899. } else {
  1900. state.chartLimit.rightTwoMin = 0
  1901. state.chartLimit.rightTwoMax = 0
  1902. }
  1903. }
  1904. const calcLimit = (arr:any)=>{
  1905. return {
  1906. Max: Math.max(...arr),
  1907. Min: Math.min(...arr)
  1908. }
  1909. }
  1910. /*-------------------- */