useChartRender.ts 64 KB

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