index.vue 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643
  1. <!-- -->
  2. <template>
  3. <div class="chart-show">
  4. <header class="chart-header" @click="openNew">
  5. <span class="chart-title" @click.stop @dblclick="copyText">{{ language === 'ch'?chartInfo.ChartName: chartInfo.ChartNameEn}}</span>
  6. </header>
  7. <template v-if="haveData">
  8. <div
  9. class="chart-wrapper"
  10. id="chart-wrapper"
  11. v-loading="loading"
  12. element-loading-text="加载中..."
  13. >
  14. <chart :options="options" :chartId="chartInfo.ChartInfoId || 0" />
  15. <div class="mark"></div>
  16. </div>
  17. </template>
  18. <div class="chart-wrapper notfound" v-else>
  19. <i class="el-icon-warning"></i>哎吆,你的图飞了,赶快去找管理员救命吧~
  20. </div>
  21. <div class="bootom-source">
  22. <div v-if="$route.query.source=='smartReportGetImg'" class="text_oneLine" style="font-size:11px">
  23. source:<span style="display:inline">{{language === 'ch' ? chartInfo.ChartSource : chartInfo.ChartSourceEn}}</span>
  24. </div>
  25. <strong v-else style="flex:1;" class="text_oneLine">source: <em> {{language === 'ch' ? chartInfo.ChartSource : chartInfo.ChartSourceEn}}</em></strong>
  26. <ul class="right-action" @click.stop>
  27. <li v-if="$route.query.source==='ybxcx'"><collectBtn/></li>
  28. <li @click="copyUrl" class="copy" v-if="isShare"><i class="el-icon-share"/>分享</li>
  29. <li @click="refreshChart" v-if="chartInfo.UniqueCode&&$route.query.source!=='smartReportGetImg'"><i class="el-icon-refresh"/>刷新</li>
  30. </ul>
  31. </div>
  32. </div>
  33. </template>
  34. <script lang="ts">
  35. import { defineComponent, reactive, toRefs, onMounted, ref } from 'vue';
  36. import { ElMessage } from 'element-plus';
  37. import _ from 'lodash';
  38. import { useRoute } from 'vue-router';
  39. import chart from '@/components/chart.vue';
  40. import { IState } from './typing';
  41. import { ChartApi } from '@/request/api';
  42. import { IChartType, IDataProps, ILunarItem, IParams, ISeasonDataItemProps } from '@/types';
  43. import Highcharts from 'highcharts';
  44. import { defaultOpts, seasonOptions } from '@/utils/chartOptions';
  45. import moment from 'moment';
  46. import collectBtn from './components/collectBtn.vue'
  47. // 散点x
  48. const scatterXAxis = {
  49. tickPosition: 'inside',
  50. lineColor: '#bfbfbf',
  51. tickColor: '#bfbfbf',
  52. tickLength:5,
  53. ordinal: false,
  54. type: 'linear',
  55. }
  56. //基础y轴
  57. const basicYAxis = {
  58. tickLength:5,
  59. lineWidth: 1,
  60. lineColor: '#bfbfbf',
  61. tickColor: '#bfbfbf',
  62. // offset: 0,
  63. visible: true,
  64. gridLineWidth: 0,
  65. tickPosition: 'inside',
  66. endOnTick: false,
  67. startOnTick: false,
  68. showLastLabel: true
  69. }
  70. export default defineComponent({
  71. components: {
  72. chart,
  73. collectBtn
  74. },
  75. setup() {
  76. const route = useRoute();
  77. const loading = ref(false);
  78. const haveData = ref(true);
  79. const code = ref(route.query.code);
  80. const isShare = ref(route.query.fromType === 'share');
  81. // 语言 中英文 ch en 默认中文
  82. const language = ref(route.query.lang || 'ch');
  83. const screen = ref(document.body.clientWidth < 1200 ? 'phone' : 'pc');
  84. const state = reactive<IState>({
  85. options: {},
  86. chartInfo: {},
  87. dataList: [],
  88. sourceName: '',
  89. /* 奇怪柱形图 */
  90. barDateList: [],//柱形图的绘图数据
  91. barXIdData: [],//柱形图的x轴id
  92. barEdbData: [],//柱形图的表格数据 只用于取值
  93. chartLimit: {},
  94. /* 商品价格曲线 */
  95. commodityChartData: [],
  96. commodityXData: [],
  97. commodityEdbList: [],
  98. /* 统计频率图 */
  99. statisticFrequencyData: {}
  100. });
  101. onMounted((): void => {
  102. getChartInfo();
  103. });
  104. // 打开新窗口
  105. const openNew = (): void => {
  106. window.open(window.location.href,'_blank');
  107. }
  108. /* 处理图表来源 只限第三方 */
  109. const dealSourceHandle = (): void => {
  110. // 取出第三方来源
  111. const arr = state.dataList.filter(item => item.EdbType === 1 && item.Source!== 9).map(item => ({
  112. key: item.Source,
  113. name: item.SourceName,
  114. })
  115. );
  116. let res_arr = arr.length ? _.uniqBy(arr,'key') : [];
  117. let str = '';
  118. res_arr.forEach((item: any) => {
  119. str += `${item.name}, `
  120. })
  121. state.sourceName = str;
  122. }
  123. /* 获取图表数据信息 */
  124. const getChartInfo = async (type='') => {
  125. if(!code.value) {
  126. haveData.value = false;
  127. return
  128. }
  129. loading.value = true;
  130. try {
  131. const { Data } = await ChartApi.getChart({
  132. UniqueCode: code.value || '',
  133. });
  134. loading.value = false;
  135. state.chartInfo = Data.ChartInfo;
  136. state.dataList = Data.ChartInfo.Source === 1 ? Data.EdbInfoList : [Data.EdbInfoList[0]];
  137. //处理英文研报英文设置不全就展示中文
  138. setLangFromEnReport();
  139. document.title = language.value==='ch'?Data.ChartInfo.ChartName:Data.ChartInfo.ChartNameEn;
  140. //eta图
  141. if(Data.ChartInfo.Source === 1) {
  142. const typeMap = {
  143. 7: initBarData,
  144. 10: initSectionScatterData
  145. }
  146. typeMap[state.chartInfo.ChartType] ? typeMap[state.chartInfo.ChartType](Data) : setOptions();
  147. }
  148. const sourceTypeMap = {
  149. 2: initCommodityData,
  150. 3: initRelevanceChartData,
  151. 4: initStatisticChartData,
  152. 5: initCommodityData,
  153. 6: initFittingEquation,
  154. 7: initStatisticChartData,
  155. 8: initStatisticChartData,
  156. 9: initStatisticChartData,
  157. }
  158. sourceTypeMap[Data.ChartInfo.Source] && sourceTypeMap[Data.ChartInfo.Source](Data);
  159. haveData.value = true;
  160. type === 'refresh' && ElMessage.success('刷新成功');
  161. //水印配置
  162. const markDom = document.querySelector('.mark')
  163. Data.WaterMark&&(markDom.style.backgroundImage = `url(${Data.WaterMark})`)
  164. }catch (e) {
  165. loading.value = false;
  166. haveData.value = false;
  167. }
  168. };
  169. /* 相关性图表 */
  170. const initRelevanceChartData=(data)=>{
  171. const relevanceUnitEnMap={
  172. '年': 'Year',
  173. '季': 'Season',
  174. '月': 'Month',
  175. '周': 'Week',
  176. '天': 'Day',
  177. }
  178. // 处理X轴
  179. let xAxis={
  180. categories: data.ChartInfo.Source===3 ? data.XEdbIdValue : data.DataResp.XDateTimeValue,
  181. tickWidth: 1,
  182. title: {
  183. text: data.ChartInfo.Source===3 ? (language.value=='ch'?`期数(${data.CorrelationChartInfo.LeadUnit})`:`stage(${relevanceUnitEnMap[data.CorrelationChartInfo.LeadUnit]})`):null,
  184. align: 'high',
  185. rotation: 0,
  186. x: 0,
  187. y:2,
  188. offset: 20,
  189. },
  190. tickInterval: 1,
  191. offset:0,
  192. tickmarkPlacement:'on'
  193. }
  194. // 处理Y轴
  195. let yAxis={
  196. ...basicYAxis,
  197. title: {
  198. text: language.value=='ch'?'相关性系数':'Correlation coefficient',
  199. align: 'high',
  200. rotation: 0,
  201. y: -15,
  202. offset: 0,
  203. },
  204. labels: {
  205. formatter: function (ctx) {
  206. let val = ctx.value;
  207. return val;
  208. },
  209. align: 'center',
  210. },
  211. // min: -1,
  212. // max: 1,
  213. opposite: false,
  214. tickWidth: 1,
  215. // tickInterval:0.2,
  216. }
  217. //处理series
  218. let seriesData=[]
  219. data.YDataList.forEach(item=>{
  220. let serie_item = {
  221. data: item.Value,
  222. type: 'spline',
  223. yAxis: 0,
  224. name: language.value=='ch'?data.ChartInfo.ChartName:data.ChartInfo.ChartNameEn,
  225. color: item.Color,
  226. chartType: 'linear',
  227. lineWidth: 3,
  228. marker: {
  229. enabled: false
  230. }
  231. };
  232. seriesData.push(serie_item)
  233. })
  234. const { LeadValue,LeadUnit } = data.CorrelationChartInfo;
  235. let tooltip = {
  236. formatter: function() {
  237. const that:any = this;
  238. let str='';
  239. if(language.value=='ch'){
  240. str = `<p>相关性系数:${that.y.toFixed(4)}</p><br><p>领先${ data.ChartInfo.Source===3 ?that.x+'期' : LeadValue+LeadUnit}</p>`
  241. }else{
  242. str = `<p>Correlation coefficient:${that.y.toFixed(4)}</p><br><p>lead${ data.ChartInfo.Source===3 ? that.x+'stage' : LeadValue+relevanceUnitEnMap[LeadUnit]}</p>`
  243. }
  244. return str
  245. },
  246. }
  247. state.options = {
  248. isRelevanceChart:data.ChartInfo.Source===3,
  249. title: {
  250. text:'',
  251. },
  252. series: seriesData,
  253. yAxis: [yAxis] ,
  254. xAxis:xAxis,
  255. tooltip
  256. }
  257. }
  258. /* 拟合方程曲线 */
  259. const initFittingEquation = (data) => {
  260. state.dataList = [data.DataResp];
  261. setDefaultLineOptions();
  262. }
  263. /* 散点截面图 */
  264. const sectionScatterData = ref<any>({});
  265. const initSectionScatterData = (data: { DataResp:any,EdbInfoList:any[] }) => {
  266. sectionScatterData.value = data.DataResp;
  267. state.chartLimit.min=Number(data.DataResp.YMinValue)
  268. state.chartLimit.max=Number(data.DataResp.YMaxValue)
  269. state.chartLimit.x_min=Number(data.DataResp.XMinValue)
  270. state.chartLimit.x_max=Number(data.DataResp.XMaxValue)
  271. //校验英文名称是否完整
  272. if(route.query.fromPage === 'en' && language.value==='en') {
  273. const { XNameEn,XUnitNameEn,YNameEn,YUnitNameEn } = data.DataResp;
  274. // DataList可能是null
  275. const DataList = data.DataResp.DataList||[]
  276. let isAllEn = true;
  277. if(!XNameEn || !XUnitNameEn || !YNameEn || !YUnitNameEn) isAllEn = false;
  278. if(DataList.some(_ => !_.NameEn)) isAllEn = false;
  279. console.log(isAllEn)
  280. language.value = isAllEn ? 'en' : 'ch';
  281. }
  282. setSectionScatterChart()
  283. }
  284. /* 截面散点图设置 sectionScatterData */
  285. const setSectionScatterChart = () => {
  286. const { chartLimit } = state;
  287. const { DataList,XName,XNameEn,XUnitName,XUnitNameEn,YName,YNameEn,YUnitName,YUnitNameEn } = sectionScatterData.value;
  288. if(!DataList){
  289. return
  290. }
  291. const { min,max,x_min,x_max } = chartLimit;
  292. //y轴
  293. let yAxis = {
  294. ...basicYAxis,
  295. title: {
  296. text: language.value === 'ch' ? YName : YNameEn,
  297. align: 'middle',
  298. },
  299. opposite: false,
  300. reversed: false,
  301. min: Number(min),
  302. max: Number(max),
  303. tickWidth: 1,
  304. }
  305. //x轴
  306. let xAxis = {
  307. ...scatterXAxis,
  308. title: {
  309. text: language.value === 'ch' ? XName : XNameEn,
  310. align: 'middle',
  311. },
  312. min: Number(x_min),
  313. max: Number(x_max),
  314. }
  315. //数据列
  316. let series: any[] = [];
  317. DataList.forEach(item => {
  318. //数据列
  319. let series_item = {
  320. data: [] as any[],
  321. type: 'scatter',
  322. name: language.value === 'ch' ? item.Name : item.NameEn,
  323. color: item.Color,
  324. lineWidth: 0,
  325. chartType: 'linear',
  326. zIndex:1,
  327. visible: true
  328. }
  329. item.EdbInfoList.forEach(_ => {
  330. series_item.data.push({
  331. x: _.XValue,
  332. y: _.YValue,
  333. dataLabels: {
  334. enabled: _.IsShow,
  335. allowOverlap: true,
  336. align: 'left',
  337. format: language.value === 'ch' ? _.Name : _.NameEn
  338. }
  339. })
  340. })
  341. series.push(series_item);
  342. //趋势线
  343. if(item.ShowTrendLine) {
  344. let trend_data = item.TrendLimitData.map((_,_index) => (
  345. _index === item.TrendLimitData.length-1 ? {
  346. x: _.X,
  347. y: _.Y,
  348. dataLabels: {
  349. enabled: item.ShowRSquare || item.ShowFitEquation,
  350. align: 'left',
  351. color: '#666',
  352. x: 20,
  353. y: 30,
  354. zIndex: 9,
  355. allowOverlap: true,
  356. formatter: function(){
  357. let tag = '';
  358. item.ShowRSquare && item.ShowFitEquation
  359. ? tag =`<span>${item.TrendLine}</span><br><span>R²=${item.RSquare}</span>`
  360. : item.ShowRSquare && !item.ShowFitEquation
  361. ? tag =`<span>R²=${item.RSquare}</span>`
  362. : item.ShowFitEquation && !item.ShowRSquare
  363. ? tag =`<span>${item.TrendLine}</span>`
  364. : ''
  365. return tag
  366. }
  367. }
  368. } : {
  369. x: _.X,
  370. y: _.Y,
  371. }
  372. ))
  373. let trend_item = {
  374. data: trend_data,
  375. type: 'spline',
  376. linkedTo: ':previous',
  377. color: item.Color,
  378. lineWidth: 1,
  379. chartType: 'linear',
  380. enableMouseTracking: false,
  381. dashStyle:'Dash',
  382. zIndex: 2,
  383. visible: true,
  384. marker: {
  385. enabled: false
  386. }
  387. }
  388. series.push(trend_item)
  389. }
  390. })
  391. let tooltip = {
  392. formatter: function() {
  393. const that:any = this;
  394. let str = '';
  395. if(language.value === 'ch') {
  396. let series_obj = DataList.find(_ => _.Name === that.series.name);
  397. let ponit_obj = series_obj.EdbInfoList.find(_ => _.XValue ===that.x && _.YValue===that.y);
  398. str=`<b>${ ponit_obj.Name }</b>`;
  399. str += `<br><span style="color:${that.color}">\u25CF</span>${ponit_obj.XName}: ${that.x} ${ponit_obj.XDate}<br>`;
  400. str += `<span style="color:${that.color}">\u25CF</span>${ponit_obj.YName}: ${that.y} ${ponit_obj.YDate}`;
  401. }else {
  402. let series_obj = DataList.find(_ => _.NameEn === that.series.name);
  403. let ponit_obj = series_obj.EdbInfoList.find(_ => _.XValue ===that.x && _.YValue===that.y);
  404. str=`<b>${ ponit_obj.NameEn }</b>`;
  405. str += `<br><span style="color:${that.color}">\u25CF</span>${ponit_obj.XNameEn}: ${that.x} ${ponit_obj.XDate}<br>`;
  406. str += `<span style="color:${that.color}">\u25CF</span>${ponit_obj.YNameEn}: ${that.y} ${ponit_obj.YDate}`;
  407. }
  408. return str
  409. }
  410. }
  411. state.options = {
  412. title: {
  413. text:''
  414. },
  415. series,
  416. yAxis: [yAxis],
  417. xAxis,
  418. tooltip
  419. }
  420. }
  421. const correlationChartInfo = ref<any>({});
  422. /* 统计特征 标准差 百分比 频率 滚动相关性*/
  423. const initStatisticChartData = (data: { DataResp:any,ChartInfo:any,CorrelationChartInfo:any }) => {
  424. if(data.ChartInfo.Source === 9) { //频率图
  425. state.statisticFrequencyData = data.DataResp;
  426. setStatisticFrequency();
  427. }else {
  428. state.dataList = [data.DataResp];
  429. if(data.ChartInfo.Source === 4) correlationChartInfo.value = data.CorrelationChartInfo;
  430. setDefaultLineOptions();
  431. }
  432. }
  433. /* 统计频率图 */
  434. const setStatisticFrequency = () => {
  435. const { DataList,LeftMaxValue,LeftMinValue,RightMaxValue,RightMinValue } = state.statisticFrequencyData;
  436. if(!DataList){
  437. return
  438. }
  439. let xAxis = {
  440. ...scatterXAxis,
  441. tickWidth: 1,
  442. title: {
  443. text: ``,
  444. align: 'high',
  445. rotation: 0,
  446. x: 0,
  447. offset: 20,
  448. }
  449. }
  450. //y和系列
  451. let yAxis:any[] = [],series:any[] = [];
  452. DataList.forEach((item,index) => {
  453. let y_item = {
  454. ...basicYAxis,
  455. title: {
  456. text: language.value === 'ch' ? item.Unit : item.UnitEn,
  457. align: 'high',
  458. rotation: 0,
  459. y: -15,
  460. offset: 0,
  461. },
  462. opposite: item.IsAxis===1?false:true,
  463. min: index===0? Number(LeftMinValue):Number(RightMinValue),
  464. max: index===0? Number(LeftMaxValue):Number(RightMaxValue),
  465. tickWidth: 1,
  466. }
  467. let series_item = {
  468. data: item.Value.map(_ =>[_.X,_.Y]),
  469. type: 'spline',
  470. yAxis: index,
  471. name: language.value === 'ch' ? item.Name : item.NameEn,
  472. color: item.Color,
  473. lineWidth: 3,
  474. chartType: 'linear',
  475. zIndex:1
  476. }
  477. series.push(series_item);
  478. yAxis.push(y_item)
  479. })
  480. let tooltip = {
  481. formatter: function() {
  482. let that:any = this;
  483. let xList = DataList[0].Value.map(_ =>_.X);
  484. let step = xList[1]-xList[0];
  485. let data_interval = `[${Number(that.x).toFixed(2)},${Number(that.x+step).toFixed(2)}]`;
  486. let str=`<b>${ data_interval }</b>`;
  487. that.points.forEach(item => {
  488. str += `<br><span style="color:${item.color}">\u25CF</span>${item.series.name}: ${item.y}%`
  489. })
  490. return str
  491. },
  492. shared: true
  493. }
  494. state.options = {
  495. title: {
  496. text:''
  497. },
  498. tooltip,
  499. series,
  500. yAxis,
  501. xAxis
  502. }
  503. }
  504. //处理英文研报的图表英文设置不全的情况
  505. const setLangFromEnReport = () => {
  506. //来源于英文研报
  507. if(route.query.fromPage !== 'en') return
  508. let is_name_en = state.chartInfo.ChartNameEn ? true : false;//名称是否有英文
  509. let is_target_en = [2,9].includes(state.chartInfo.ChartType) ? true : state.dataList.every(_ => _.EdbNameEn);//指标是否有英文
  510. console.log(is_name_en,is_target_en)
  511. language.value = (is_name_en && is_target_en) ? 'en' : 'ch';
  512. }
  513. // 曲线图x轴显示计算年限差 >1年 显示年/月 <=1 显示月/日
  514. const xTimeDiffer = () => {
  515. const end_date = state.chartInfo.DateType === 5
  516. ? state.chartInfo.EndDate
  517. : state.chartInfo.DateType === 6
  518. ? new Date()
  519. : '';
  520. //年限差
  521. const year_differ = moment(end_date).diff(
  522. moment(state.chartInfo.StartDate),
  523. 'years',
  524. true
  525. );
  526. // console.log(year_differ)
  527. if ([5, 6].includes(state.chartInfo.DateType) && year_differ <= 1) {
  528. return true
  529. } else {
  530. return false
  531. }
  532. }
  533. /* 曲线图 */
  534. const setDefaultLineOptions = () => {
  535. const { dataList,chartInfo } = state;
  536. //拼接标题 数据列
  537. let data = [] as any[],ydata = [] as any[],minTimeArr: number[] = [],maxTimeArr: number[] = [];
  538. let rightTwoIndex = dataList.findIndex(item => item.IsAxis ===2);
  539. // const chartData = _.cloneDeep(dataList);
  540. //有右二轴时排个序 按照左 右 右2的顺序
  541. let chartData = dataList.some(_ =>_.IsAxis===2) ? changeEdbOrder(dataList) : _.cloneDeep(dataList);
  542. chartData.forEach((item:IDataProps ,index:number) => {
  543. //轴位置值相同的下标
  544. let sameSideIndex = chartData.findIndex(
  545. (i:IDataProps) => i.IsAxis === item.IsAxis
  546. );
  547. //y轴
  548. let textEn = item.Unit?item.UnitEn:''
  549. let yItem = {
  550. ...basicYAxis,
  551. labels: {
  552. formatter: function (ctx: any) {
  553. let val = ctx.value;
  554. return sameSideIndex !== index ? '' : val;
  555. },
  556. align: 'center',
  557. x: [0,2].includes(item.IsAxis) ? 5 : -5,
  558. style: {
  559. fontSize: '10px',
  560. },
  561. },
  562. title: {
  563. text:language.value=='ch'?sameSideIndex !== index ? '' : `${item.Unit}`:textEn,
  564. // text: null,
  565. align: 'high',
  566. rotation: 0,
  567. y: -15,
  568. x: (item.IsAxis===0 && rightTwoIndex>-1) ? -chartData[rightTwoIndex].Unit.length*12 : 0,
  569. textAlign: item.IsAxis===1 ? 'left' : 'right',
  570. reserveSpace: false
  571. },
  572. opposite: [0,2].includes(item.IsAxis),
  573. reversed: item.IsOrder,
  574. min: Number(chartData[sameSideIndex].MinData),
  575. max: Number(chartData[sameSideIndex].MaxData),
  576. tickWidth: sameSideIndex !== index ? 0 : 1,
  577. visible: sameSideIndex === index
  578. }
  579. // //拼接标题 判断相同指标名称拼接来源
  580. let dynamic_title = item.EdbName;
  581. let dynamic_arr = chartData.filter(
  582. (item: IDataProps) => dynamic_title === item.EdbName
  583. );
  584. // 拼接配置 IsAxis左轴1 右轴0 IsOrder正序false 逆序true EdbInfoType是否是领先指标
  585. let dynamic_tag = concatDynamicTag(item)
  586. // 英文后缀
  587. let dynamic_tag_en = concatDynamicTag(item,'en')
  588. //数据列
  589. let nameCh:String = dynamic_arr.length > 1
  590. ? `${item.EdbName}(${item.SourceName})${dynamic_tag}`
  591. : `${item.EdbName}${dynamic_tag}`
  592. let nameEn:String=item.EdbNameEn?`${item.EdbNameEn}${dynamic_tag_en}`:''
  593. let name :String=language.value == 'ch'?nameCh:nameEn
  594. //预测指标配置
  595. let predict_params = item.EdbInfoCategoryType === 1 ? getPredictParams(item) : {};
  596. let obj = {
  597. data: [] as any[],
  598. type: 'spline',
  599. yAxis: sameSideIndex,
  600. name,
  601. color: item.ChartColor,
  602. lineWidth: Number(item.ChartWidth),
  603. ...predict_params
  604. };
  605. item.DataList = item.DataList || []
  606. for (let i of item.DataList) {
  607. obj.data.push([i.DataTimestamp, i.Value]);
  608. }
  609. if(item.DataList.length){
  610. minTimeArr.push(item.DataList[0].DataTimestamp)
  611. maxTimeArr.push(item.DataList[item.DataList.length-1].DataTimestamp)
  612. }
  613. data.push(obj);
  614. ydata.push(yItem);
  615. })
  616. // 范围为1年内 x轴显示为月/日 否则默认年/月
  617. let xAxis:any = {};
  618. const isLessThanOneYear:boolean = xTimeDiffer();
  619. let minTime: number=Math.min(...minTimeArr);
  620. let maxTime=Math.max(...maxTimeArr);
  621. let step = setXaxisStep(maxTime-minTime);
  622. xAxis = {
  623. ...defaultOpts.xAxis,
  624. tickInterval: screen.value === 'phone' ? step : undefined,
  625. labels: {
  626. formatter: function (ctx: any) {
  627. return isLessThanOneYear
  628. ? Highcharts.dateFormat('%m/%d', ctx.value)
  629. : Highcharts.dateFormat('%y/%m', ctx.value);
  630. },
  631. style: {
  632. fontSize: '10px',
  633. },
  634. }
  635. }
  636. state.options = {
  637. series: data,
  638. yAxis: ydata,
  639. xAxis
  640. };
  641. //滚动相关性独立tooltip
  642. if(chartInfo.Source === 4) {
  643. const relevanceUnitEnMap={
  644. '年': 'Year',
  645. '季': 'Season',
  646. '月': 'Month',
  647. '周': 'Week',
  648. '天': 'Day',
  649. }
  650. const { LeadValue,LeadUnit } = correlationChartInfo.value;
  651. state.options.tooltip = {
  652. formatter: function() {
  653. let that:any = this;
  654. let str = '';
  655. if(language.value=='ch'){
  656. str = `${Highcharts.dateFormat('%Y/%m/%d',that.x)}<br><p>相关性系数:${that.y.toFixed(4)}</p><br><p>领先${LeadValue+LeadUnit}</p>`
  657. }else{
  658. str = `${Highcharts.dateFormat('%Y/%m/%d',that.x)}<br><p>Correlation coefficient:${that.y.toFixed(4)}</p><br><p>lead${LeadValue+relevanceUnitEnMap[LeadUnit]}</p>`
  659. }
  660. return str
  661. },
  662. }
  663. }
  664. }
  665. /* 设置x轴步长 刻度数量多一点 */
  666. const setXaxisStep = (timestamp: number) => {
  667. // return timestamp / 6 < 24 * 3600 * 1000 * 30 ? 24 * 3600 * 1000 * 30 : timestamp / 6;
  668. return timestamp / 6;
  669. }
  670. /* 指标顺序调整 IsAxis: 0右轴 1左轴 2右2*/
  671. const changeEdbOrder = (data: any[]) => {
  672. // 左轴指标
  673. let left_edbs = data.filter(_ => _.IsAxis===1);
  674. //右轴指标
  675. let right_edbs = data.filter(_ => !_.IsAxis);
  676. // 右2轴指标
  677. let right_two_edbs = data.filter(_ => _.IsAxis === 2);
  678. // 按 左 右 右2顺序排列
  679. return [left_edbs,right_edbs,right_two_edbs].flat(Infinity);
  680. }
  681. /* 堆叠图/组合图设置
  682. 本来和曲线图逻辑基本一致兼容下即可 为了以后便于维护和阅读还是拆开写吧
  683. */
  684. const setStackOrCombinChart = () => {
  685. const { dataList,chartInfo } = state;
  686. //拼接标题 数据列
  687. let data = [] as any[],ydata = [] as any[],minTimeArr: number[] = [],maxTimeArr: number[] = [];
  688. // const chartData = _.cloneDeep(dataList);
  689. //有右二轴时排个序 按照左 右 右2的顺序
  690. let chartData = dataList.some(_ =>_.IsAxis===2) ? changeEdbOrder(dataList) : _.cloneDeep(dataList);
  691. //支持的图表类型
  692. const chartTypeMap: IChartType = {
  693. 3: 'areaspline',
  694. 4: 'column',
  695. 6: ''
  696. };
  697. let chartStyle = chartTypeMap[chartInfo.ChartType];
  698. chartData.forEach((item:IDataProps ,index:number) => {
  699. //轴位置值相同的下标
  700. let sameSideIndex = chartData.findIndex(
  701. (i:IDataProps) => i.IsAxis === item.IsAxis
  702. );
  703. //堆叠图的yAxis必须一致 数据列所对应的y轴
  704. let serie_yIndex = index;
  705. if([3,4].includes(chartInfo.ChartType)) {
  706. // 类型为堆叠图时公用第一个指标y轴
  707. serie_yIndex = 0;
  708. } else if(chartInfo.ChartType ===6 && ['areaspline','column'].includes(item.ChartStyle)) {
  709. // 组合图找第一个堆叠柱状或面积的作为公用
  710. serie_yIndex = chartData.findIndex((i:IDataProps) => i.ChartStyle === item.ChartStyle);
  711. }
  712. //数据对应的y轴是公用轴则配置也共享
  713. item.IsAxis = serie_yIndex === index ? item.IsAxis : chartData[serie_yIndex].IsAxis;
  714. item.IsOrder = serie_yIndex === index ? item.IsOrder : chartData[serie_yIndex].IsOrder;
  715. // 右2轴下标
  716. let rightTwoIndex = [3,4].includes(chartInfo.ChartType)
  717. ? -1
  718. : dataList.findIndex(item => item.IsAxis===2);
  719. //y轴
  720. let textEn = item.Unit?item.UnitEn:''
  721. let yItem = {
  722. ...basicYAxis,
  723. labels: {
  724. formatter: function (ctx: any) {
  725. let val = ctx.value;
  726. return sameSideIndex !== index ? '' : val;
  727. },
  728. align: 'center',
  729. x: [0,2].includes(item.IsAxis) ? 5 : -5,
  730. style: {
  731. fontSize: '10px',
  732. },
  733. },
  734. title: {
  735. text:language.value=='ch'?sameSideIndex !== index ? '' : `${item.Unit}`:textEn,
  736. // text: null,
  737. align: 'high',
  738. rotation: 0,
  739. y: -15,
  740. x: (item.IsAxis===0 && rightTwoIndex>-1) ? -chartData[rightTwoIndex].Unit.length*12 : 0,
  741. textAlign: item.IsAxis===1 ? 'left' : 'right',
  742. reserveSpace: false
  743. },
  744. opposite: [0,2].includes(item.IsAxis),
  745. reversed: item.IsOrder,
  746. min: Number(chartData[sameSideIndex].MinData),
  747. max: Number(chartData[sameSideIndex].MaxData),
  748. tickWidth: sameSideIndex !== index ? 0 : 1,
  749. visible: serie_yIndex === index && sameSideIndex ===index
  750. }
  751. // //拼接标题 判断相同指标名称拼接来源
  752. let dynamic_title = item.EdbName;
  753. let dynamic_arr = chartData.filter(
  754. (item: IDataProps) => dynamic_title === item.EdbName
  755. );
  756. // 拼接配置 IsAxis左轴1 右轴0 IsOrder正序false 逆序true EdbInfoType是否是领先指标
  757. let dynamic_tag = concatDynamicTag(item)
  758. // 英文后缀
  759. let dynamic_tag_en = concatDynamicTag(item,'en')
  760. //数据列
  761. let nameCh:String = dynamic_arr.length > 1
  762. ? `${item.EdbName}(${item.SourceName})${dynamic_tag}`
  763. : `${item.EdbName}${dynamic_tag}`
  764. let nameEn:String=item.EdbNameEn?`${item.EdbNameEn}${dynamic_tag_en}`:''
  765. let name :String=language.value == 'ch'?nameCh:nameEn
  766. //预测指标配置
  767. let predict_params = item.EdbInfoCategoryType === 1 ? getPredictParams(item,chartStyle) : {};
  768. let obj = {
  769. data: [] as any[],
  770. type: chartStyle || item.ChartStyle,
  771. yAxis: serie_yIndex,
  772. name,
  773. color: item.ChartColor,
  774. lineWidth: (chartInfo.ChartType === 6 && item.ChartStyle === 'spline') ? Number(item.ChartWidth) : 0,
  775. fillColor: (chartInfo.ChartType === 3 || (chartInfo.ChartType === 6 && item.ChartStyle === 'areaspline')) ? item.ChartColor : undefined,
  776. zIndex: (chartInfo.ChartType === 6 && item.ChartStyle === 'spline') ? 1 : 0, //防止组合图曲线被遮住
  777. borderWidth: 1,
  778. borderColor: item.ChartColor,
  779. ...predict_params
  780. };
  781. item.DataList = item.DataList || []
  782. for (let i of item.DataList) {
  783. obj.data.push([i.DataTimestamp, i.Value]);
  784. }
  785. if(item.DataList.length){
  786. minTimeArr.push(item.DataList[0].DataTimestamp)
  787. maxTimeArr.push(item.DataList[item.DataList.length-1].DataTimestamp)
  788. }
  789. data.push(obj);
  790. ydata.push(yItem);
  791. })
  792. // 范围为1年内 x轴显示为月/日 否则默认年/月
  793. let xAxis:any = {};
  794. const isLessThanOneYear:boolean = xTimeDiffer();
  795. let minTime: number=Math.min(...minTimeArr);
  796. let maxTime=Math.max(...maxTimeArr);
  797. let step = setXaxisStep(maxTime-minTime);
  798. xAxis = {
  799. ...defaultOpts.xAxis,
  800. tickInterval: screen.value === 'phone' ? step : undefined,
  801. labels: {
  802. formatter: function (ctx: any) {
  803. return isLessThanOneYear
  804. ? Highcharts.dateFormat('%m/%d', ctx.value)
  805. : Highcharts.dateFormat('%y/%m', ctx.value);
  806. },
  807. style: {
  808. fontSize: '10px',
  809. },
  810. }
  811. }
  812. state.options = {
  813. series: data,
  814. yAxis: ydata,
  815. xAxis
  816. };
  817. }
  818. /* 季节图 */
  819. const setSeasonOptions = () => {
  820. const chartData = state.dataList[0];
  821. // 农历数据需要去除第一项 在ETA1.0.5之后,除了这里 农历和公历处理逻辑一样
  822. if(!chartData.DataList){
  823. return
  824. }
  825. const chartDataHandle=state.chartInfo.Calendar === '农历'?
  826. chartData.DataList.filter((item:ISeasonDataItemProps, index:number) => index > 0):
  827. chartData.DataList
  828. let seasonYdata:any[] = [],
  829. seasonData:any[] = [],
  830. chart = {
  831. spacing: [5, 8, 2, 8],
  832. };
  833. /* 公历数据处理 处理数据列 y轴 */
  834. // if (state.chartInfo.Calendar === '公历')
  835. for (let j of chartDataHandle) {
  836. //预测指标配置
  837. let predict_params = chartData.EdbInfoCategoryType === 1 ? getSeasonPredictParams(j.CuttingDataTimestamp) : {};
  838. let serie_item = {
  839. data: [] as any[],
  840. type: chartData.ChartStyle,
  841. yAxis: 0,
  842. name: j.ChartLegend,
  843. ...predict_params
  844. };
  845. const data_array = _.cloneDeep(j.DataList);
  846. data_array &&
  847. data_array.forEach((item: IParams) => {
  848. serie_item.data.push([item.DataTimestamp, item.Value]);
  849. });
  850. const index = chartDataHandle.findIndex(
  851. (item: ISeasonDataItemProps) => item.ChartLegend === j.ChartLegend
  852. );
  853. let textEn = chartData.Unit?chartData.UnitEn:''
  854. const s_yItem = {
  855. labels: {
  856. formatter: function (ctx: any) {
  857. let val = ctx.value;
  858. return index !== 0 ? '' : val;
  859. },
  860. align: 'center',
  861. style: {
  862. fontSize: '10px',
  863. },
  864. x: -5,
  865. },
  866. title: {
  867. text:language.value=='ch'?`${chartData.Unit}`:textEn,
  868. // text: null,
  869. align: 'high',
  870. rotation: 0,
  871. y: -15,
  872. offset: -(10 * chartData.Unit.length),
  873. },
  874. max: Number(chartData.MaxData),
  875. min: Number(chartData.MinData),
  876. ...defaultOpts.yAxis,
  877. };
  878. seasonData.push(serie_item);
  879. seasonYdata.push(s_yItem);
  880. }
  881. /* 农历数据处理 */
  882. // let filterArr =
  883. // state.chartInfo.Calendar === '农历'
  884. // ? chartData.DataList.List.filter(
  885. // (item: ILunarItem, index: number) => index > 0
  886. // )
  887. // : [];
  888. // if (state.chartInfo.Calendar === '农历')
  889. // for (let j of filterArr) {
  890. // //预测指标配置
  891. // let predict_params = chartData.EdbInfoCategoryType === 1 ? getSeasonPredictParams(j.CuttingDataTimestamp) : {};
  892. // let serie_item = {
  893. // data: [] as any[],
  894. // type: chartData.ChartStyle,
  895. // yAxis: 0,
  896. // name: j.Year,
  897. // ...predict_params
  898. // };
  899. // const data_array = _.cloneDeep(j.Items);
  900. // data_array &&
  901. // data_array.forEach((item: IParams) => {
  902. // serie_item.data.push([item.DataTimestamp, item.Value]);
  903. // });
  904. // const index = filterArr.findIndex(
  905. // (item: ILunarItem) => item.Year === j.Year
  906. // );
  907. // let textEn = chartData.Unit?chartData.UnitEn:''
  908. // const s_yItem = {
  909. // labels: {
  910. // formatter: function (ctx: any) {
  911. // let val = ctx.value;
  912. // return index !== 0 ? '' : val;
  913. // },
  914. // align: 'center',
  915. // style: {
  916. // fontSize: '10px',
  917. // },
  918. // x: -5,
  919. // },
  920. // title: {
  921. // text:language.value=='ch'?`${chartData.Unit}`:textEn,
  922. // // text: null,
  923. // align: 'high',
  924. // rotation: 0,
  925. // y: -15,
  926. // offset: -(10 * chartData.Unit.length),
  927. // },
  928. // max: Number(chartData.MaxData),
  929. // min: Number(chartData.MinData),
  930. // ...defaultOpts.yAxis,
  931. // };
  932. // seasonData.push(serie_item);
  933. // seasonYdata.push(s_yItem);
  934. // }
  935. // 季节图x轴显示月/日 周度指标额外处理时间轴显示
  936. const xAxis = {
  937. ...defaultOpts.xAxis,
  938. tickInterval: screen.value === 'phone' ? 24 * 3600 * 1000 * 60 : undefined,
  939. labels: {
  940. formatter: function (ctx: any) {
  941. return Highcharts.dateFormat('%m/%d', ctx.value);
  942. },
  943. style: {
  944. fontSize: '10px',
  945. },
  946. },
  947. };
  948. // 季节图提示框显示 月/日
  949. defaultOpts.tooltip = {
  950. split: false,
  951. shared: true,
  952. dateTimeLabelFormats: {
  953. // 时间格式化字符
  954. day: '%m/%d',
  955. week: '%m/%d',
  956. month: '%m/%d',
  957. year: '%m/%d',
  958. },
  959. xDateFormat: '%m/%d',
  960. // valueDecimals: 2,
  961. };
  962. //农历默认选中一年数据并隐藏按钮 公历显示全部数据
  963. // let rangeSelector = state.chartInfo.Calendar === '农历'
  964. // ? {
  965. // enabled: true,
  966. // selected: 0,
  967. // inputStyle: {
  968. // display: 'none',
  969. // },
  970. // labelStyle: {
  971. // display: 'none',
  972. // },
  973. // buttonTheme: {
  974. // style: {
  975. // display: 'none',
  976. // },
  977. // },
  978. // buttons: [
  979. // {
  980. // type: 'month',
  981. // count: 12,
  982. // text: '12月',
  983. // },
  984. // {
  985. // type: 'month',
  986. // count: 15,
  987. // text: '15月',
  988. // },
  989. // {
  990. // type: 'all',
  991. // text: '全部',
  992. // },
  993. // ],
  994. // }
  995. // : {
  996. // enabled: false,
  997. // };
  998. state.options = {
  999. colors:seasonOptions.colors.slice(-chartDataHandle.length),
  1000. // state.chartInfo.Calendar === '公历'
  1001. // ? seasonOptions.colors.slice(-chartData.DataList.length)
  1002. // : seasonOptions.colors.slice(-filterArr.length),
  1003. series: seasonData,
  1004. yAxis: seasonYdata,
  1005. xAxis,
  1006. // rangeSelector,
  1007. chart
  1008. };
  1009. }
  1010. /* 散点图 第一个指标值为x轴 第二个指标为y轴*/
  1011. const setScatterChartOptions = () => {
  1012. const { dataList,chartInfo } = state;
  1013. // 取2个指标中日期相同的数据
  1014. const real_data: any[] = [];
  1015. let tmpData_date: any = {};//用来取点对应的日期
  1016. let data1 = _.cloneDeep(dataList)[0].DataList || [];
  1017. let data2 = _.cloneDeep(dataList)[1].DataList || [];
  1018. data1.forEach((_item: IParams) => {
  1019. data2.forEach((_item2: IParams) => {
  1020. if(_item.DataTimestamp === _item2.DataTimestamp) {
  1021. //日期
  1022. let itemIndex =_item.Value + "_" +_item2.Value
  1023. if(tmpData_date[itemIndex]) {
  1024. tmpData_date[itemIndex].push( moment(_item.DataTimestamp).format('YYYY/MM/DD'))
  1025. } else {
  1026. tmpData_date[itemIndex] = [moment(_item.DataTimestamp).format('YYYY/MM/DD')]
  1027. }
  1028. //值
  1029. real_data.push({
  1030. x: _item.Value,
  1031. y: _item2.Value
  1032. })
  1033. }
  1034. })
  1035. })
  1036. real_data.sort((x,y) => x-y);
  1037. //悬浮窗 拼接日期 原始指标名称
  1038. let tooltip = {
  1039. formatter: function() {
  1040. const that: any = this;
  1041. return language.value=='ch'?
  1042. `<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>
  1043. ${dataList[0].EdbName}: <span style="font-weight: 600"> ${that.x}</span><br>
  1044. ${dataList[1].EdbName}: <span style="font-weight: 600"> ${that.y}</span>
  1045. `:
  1046. `<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>
  1047. ${dataList[0].EdbNameEn}: <span style="font-weight: 600"> ${that.x}</span><br>
  1048. ${dataList[1].EdbNameEn}: <span style="font-weight: 600"> ${that.y}</span>
  1049. `
  1050. }
  1051. }
  1052. const { IsOrder,ChartColor } = dataList[0];
  1053. //y轴
  1054. let yAxis = {
  1055. title: {
  1056. text: language.value=='ch'?`${dataList[1].Unit}`:dataList[1].Unit?dataList[1].UnitEn:'',
  1057. // text: null,
  1058. align: 'high',
  1059. rotation: 0,
  1060. y: -5,
  1061. offset: -(10 * dataList[1].Unit.length),
  1062. },
  1063. labels: {
  1064. formatter: function (ctx: any) {
  1065. return ctx.value;
  1066. },
  1067. align: 'center',
  1068. },
  1069. opposite: false,
  1070. reversed: IsOrder,
  1071. min: Number(dataList[0].MinData),
  1072. max: Number(dataList[0].MaxData),
  1073. tickWidth: 1,
  1074. tickLength: 5,
  1075. lineWidth: 1,
  1076. lineColor: '#bfbfbf',
  1077. tickColor: '#bfbfbf',
  1078. offset: 0,
  1079. visible: true,
  1080. gridLineWidth: 0,
  1081. tickPosition: 'inside',
  1082. endOnTick: false,
  1083. startOnTick: false,
  1084. showLastLabel: true,
  1085. tickPixelInterval: 50
  1086. }
  1087. //数据列
  1088. let series: any = {
  1089. data: [],
  1090. type: 'scatter',
  1091. name: language.value == 'ch'?`${chartInfo.ChartName}${IsOrder ? '(逆序)' : ''}`:`${chartInfo.ChartNameEn}${IsOrder ? '(reserve)' : ''}`,
  1092. color: ChartColor,
  1093. lineWidth: 0
  1094. }
  1095. real_data.forEach(_ => {
  1096. series.data.push([_.x,_.y])
  1097. })
  1098. state.options = {
  1099. title: {
  1100. text:''
  1101. },
  1102. series: [ series ],
  1103. yAxis,
  1104. xAxis: {
  1105. ...scatterXAxis,
  1106. title: {
  1107. text: language.value=='ch'?`${dataList[0].Unit}`:dataList[0].Unit?dataList[0].UnitEn:'',
  1108. // text: null,
  1109. align: 'high',
  1110. rotation: 0,
  1111. x: 0,
  1112. offset: 20,
  1113. },
  1114. },
  1115. tooltip
  1116. }
  1117. console.log(state.options)
  1118. }
  1119. /* 获取图表详情后赋值柱状图数据 */
  1120. const initBarData = (data: { XEdbIdValue: number[]; YDataList: any; EdbInfoList: any; ChartInfo: any; }) => {
  1121. const { XEdbIdValue,YDataList,EdbInfoList,ChartInfo } = data;
  1122. state.barDateList = YDataList;
  1123. state.barXIdData = XEdbIdValue;
  1124. state.barEdbData = EdbInfoList;
  1125. state.chartLimit = {
  1126. min: Number(ChartInfo.LeftMin),
  1127. max: Number(ChartInfo.LeftMax),
  1128. }
  1129. setBarChart();
  1130. }
  1131. /* 奇怪柱状图 和以上逻辑无公用点 依赖数据为单独的数据
  1132. x轴为指标名称的柱形图 以日期作为series
  1133. */
  1134. const setBarChart = () => {
  1135. const {barDateList,barXIdData,chartLimit,barEdbData,chartInfo} = state;
  1136. let seriesData: { data: any; type: string; yAxis: number; name: any; color: any; chartType: string; }[] = [];
  1137. const data = _.cloneDeep(barDateList);
  1138. let categories = language.value==='ch'
  1139. ? barXIdData.map((_:number) => barEdbData.find((edb: { EdbInfoId: number; }) => edb.EdbInfoId===_).EdbAliasName)
  1140. : barXIdData.map((_:number) => barEdbData.find((edb: { EdbInfoId: number; }) => edb.EdbInfoId===_).EdbNameEn)
  1141. //x轴
  1142. let xAxis = {
  1143. ...scatterXAxis,
  1144. categories,
  1145. tickWidth: 1,
  1146. title: {
  1147. text: ``,
  1148. align: 'high',
  1149. rotation: 0,
  1150. x: 0,
  1151. offset: 20,
  1152. },
  1153. }
  1154. const { max,min } = chartLimit;
  1155. console.log(max,min)
  1156. //y轴
  1157. let yAxis = {
  1158. ...basicYAxis,
  1159. title: {
  1160. text: language.value==='ch' ? chartInfo.Unit : chartInfo.UnitEn,
  1161. align: 'high',
  1162. rotation: 0,
  1163. y: -15,
  1164. offset: 0,
  1165. },
  1166. labels: {
  1167. formatter: function (ctx:any) {
  1168. let val = ctx.value;
  1169. return val;
  1170. },
  1171. align: 'center',
  1172. },
  1173. min: Number(min),
  1174. max: Number(max),
  1175. opposite: false,
  1176. tickWidth: 1,
  1177. }
  1178. //数据列
  1179. data.forEach((item: { Value: number; Name: string; Date: string; Color: string; }) => {
  1180. let serie_item = {
  1181. data: item.Value,
  1182. type: 'column',
  1183. yAxis: 0,
  1184. name: language.value==='ch' ? (item.Name || item.Date) : item.Date,
  1185. color: item.Color,
  1186. chartType: 'linear'
  1187. };
  1188. seriesData.push(serie_item)
  1189. })
  1190. state.options = {
  1191. title: {
  1192. text:''
  1193. },
  1194. plotOptions: {
  1195. column:{
  1196. stacking: null,
  1197. },
  1198. },
  1199. series: seriesData,
  1200. yAxis: [ yAxis ],
  1201. xAxis
  1202. }
  1203. }
  1204. /* 商品价格曲线获取详情赋值 */
  1205. const initCommodityData = (data: { XDataList: any[]; YDataList: any; EdbInfoList: any; ChartInfo: any; DataResp?:any }) => {
  1206. const { XDataList,YDataList,EdbInfoList,ChartInfo,DataResp } = data;
  1207. state.commodityEdbList = EdbInfoList;
  1208. state.commodityChartData = ChartInfo.Source===5?DataResp.YDataList:YDataList;
  1209. state.commodityXData = ChartInfo.Source===5?DataResp.XDataList:XDataList;
  1210. if(ChartInfo.Source===5) {
  1211. state.chartInfo = {
  1212. ...state.chartInfo,
  1213. ProfitName: DataResp.ProfitName,
  1214. ProfitNameEn: DataResp.ProfitNameEn
  1215. }
  1216. }
  1217. state.chartLimit = {
  1218. min: Number(ChartInfo.LeftMin),
  1219. max: Number(ChartInfo.LeftMax)
  1220. }
  1221. setCommodityChart();
  1222. }
  1223. /* 商品价格曲线设置 绘图逻辑同奇怪柱形图*/
  1224. const setCommodityChart = () => {
  1225. const {chartLimit,commodityChartData,commodityXData,commodityEdbList,chartInfo} = state;
  1226. let seriesData:any[] = [];
  1227. const data = _.cloneDeep(commodityChartData);
  1228. //x轴
  1229. let xAxis = {
  1230. ...scatterXAxis,
  1231. categories: commodityXData.map(_ => language.value === 'ch' ? _.Name:_.NameEn),
  1232. tickWidth: 1,
  1233. title: {
  1234. text: ``,
  1235. align: 'high',
  1236. rotation: 0,
  1237. x: 0,
  1238. offset: 20,
  1239. },
  1240. }
  1241. const { max,min } = chartLimit;
  1242. //y轴
  1243. let yAxis = {
  1244. ...basicYAxis,
  1245. title: {
  1246. text: language.value === 'ch' ? commodityEdbList[0].Unit : commodityEdbList[0].UnitEn,
  1247. align: 'high',
  1248. rotation: 0,
  1249. y: -15,
  1250. offset: 0,
  1251. },
  1252. labels: {
  1253. formatter: function (ctx:any) {
  1254. let val = ctx.value;
  1255. return val;
  1256. },
  1257. align: 'center',
  1258. },
  1259. min: Number(min),
  1260. max: Number(max),
  1261. opposite: false,
  1262. tickWidth: 1,
  1263. }
  1264. //数据列
  1265. data.forEach((item: { Value: number[]; Name: string; Date: string; Color: string;NameEn: string,XEdbInfoIdList: number[],NoDataEdbList: number[] }) => {
  1266. //处理首或/尾全是无效数据的以null填充
  1267. let filterData = filterInvalidData(item)
  1268. let serie_item = {
  1269. data: filterData,
  1270. type: 'spline',
  1271. yAxis: 0,
  1272. name: language.value === 'ch' ? item.Name : item.NameEn,
  1273. color: item.Color,
  1274. chartType: 'linear',
  1275. lineWidth: 3,
  1276. marker: {
  1277. enabled: false
  1278. }
  1279. };
  1280. seriesData.push(serie_item)
  1281. })
  1282. //tooltip
  1283. let tooltip = {
  1284. formatter: function() {
  1285. const ctx: any = this;
  1286. let str: string = '';
  1287. if(language.value === 'ch') {
  1288. ctx.points.forEach((item: { series:{name: string},y: number,color: string }) => {
  1289. let obj_item = data.find((_:any) => _.Name === item.series.name);
  1290. let index = commodityXData.findIndex(_ => _.Name === ctx.x);
  1291. //合约显示
  1292. let haveContract = obj_item.XEdbInfoIdList[index];
  1293. if(haveContract) {
  1294. // 利润曲线指标名
  1295. let edb_name = chartInfo.Source === 5
  1296. ? (index === 0 ? obj_item.NameList[index] : `${chartInfo.ProfitName}(${obj_item.NameList[index]})`)
  1297. : commodityEdbList.find(_ => _.EdbInfoId === obj_item.XEdbInfoIdList[index]).EdbName;
  1298. str+=`<b>${ edb_name }</b>`
  1299. if(!obj_item.NoDataEdbList.includes(obj_item.XEdbInfoIdList[index])) {
  1300. str += `<br><span style="color:${item.color}">\u25CF</span>${obj_item.Date}: ${item.y}<br>`
  1301. }else {
  1302. str += `<br><span style="color:${item.color}">\u25CF</span>${obj_item.Date}: 无<br>`
  1303. }
  1304. }
  1305. })
  1306. }else {
  1307. ctx.points.forEach((item: { series:{name: string},y: number,color: string }) => {
  1308. let obj_item = data.find((_: any) => _.NameEn === item.series.name);
  1309. let index = commodityXData.findIndex(_ => _.NameEn === ctx.x);
  1310. let haveContract = obj_item.XEdbInfoIdList[index];
  1311. if(haveContract) {
  1312. let edb_name = chartInfo.Source === 5
  1313. ? (index === 0 ? obj_item.NameList[index] : `${chartInfo.ProfitNameEn}(${obj_item.NameList[index]})`)
  1314. : commodityEdbList.find(_ => _.EdbInfoId === obj_item.XEdbInfoIdList[index]).EdbNameEn;
  1315. str+=`<b>${ edb_name }</b>`
  1316. if(!obj_item.NoDataEdbList.includes(obj_item.XEdbInfoIdList[index])) {
  1317. str += `<br><span style="color:${item.color}">\u25CF</span>${obj_item.Date}: ${item.y}<br>`
  1318. }else {
  1319. str += `<br><span style="color:${item.color}">\u25CF</span>${obj_item.Date}: 无<br>`
  1320. }
  1321. }
  1322. })
  1323. }
  1324. return str || '无合约'
  1325. },
  1326. shared: true
  1327. }
  1328. state.options = {
  1329. title: {
  1330. text:''
  1331. },
  1332. series: seriesData,
  1333. yAxis: [ yAxis ],
  1334. xAxis,
  1335. tooltip
  1336. }
  1337. };
  1338. /* 处理无效数据为null */
  1339. const filterInvalidData = (item:{ Value: number[]; Name: string; Date: string; Color: string;NameEn: string,XEdbInfoIdList: number[],NoDataEdbList: number[] })=> {
  1340. let validateArr = item.XEdbInfoIdList.filter(_ => _&&!item.NoDataEdbList.includes(_));
  1341. let first_index = item.XEdbInfoIdList.findIndex(_ => _ === validateArr[0]);
  1342. let last_index = item.XEdbInfoIdList.findIndex(_ => _ === validateArr[validateArr.length-1]);
  1343. console.log('first_index',first_index)
  1344. console.log('last_index',last_index)
  1345. let arr = item.Value.map((item,index:number) => {
  1346. if(index < first_index || index > last_index) {
  1347. return null
  1348. }else {
  1349. return item
  1350. }
  1351. })
  1352. return arr;
  1353. }
  1354. /* 设置options */
  1355. const setOptions = () => {
  1356. // ChartType: 1曲线图 2季节图 3面积图 4柱状图 5散点图 6组合图 季节图中公历和农历数据结构不同
  1357. const { chartInfo } = state;
  1358. const chartSetMap: any = {
  1359. 1: setDefaultLineOptions,
  1360. 2: setSeasonOptions,
  1361. 3: setStackOrCombinChart,
  1362. 4: setStackOrCombinChart,
  1363. 5: setScatterChartOptions,
  1364. 6: setStackOrCombinChart
  1365. };
  1366. chartSetMap[chartInfo.ChartType]()
  1367. };
  1368. /* 拼接动态的指标名称小标签 */
  1369. const concatDynamicTag = ({ IsAxis,IsOrder,EdbInfoType,LeadValue,LeadUnit }: IDataProps,lang:String='ch'): string => {
  1370. // IsAxis左轴1 右轴0 2右2轴
  1371. //IsOrder正序false 逆序true
  1372. //EdbInfoType是否是领先指标
  1373. const axisLabelMap:any = lang=='ch'?{
  1374. 0: '右轴',
  1375. 2: '右2轴'
  1376. }:{
  1377. 0: 'RHS',
  1378. 2: '2-RHS'
  1379. }
  1380. const orderLabelMap:any = lang=='ch'?{
  1381. 1: '逆序'
  1382. }:{
  1383. 1: 'REV'
  1384. }
  1385. const edbInfoMap:any = lang=='ch'?{
  1386. 0: '领先'
  1387. }:{
  1388. 0: 'Lead'
  1389. }
  1390. const leadUnitEnMap:any = {
  1391. '年': 'Y',
  1392. '季': 'Q',
  1393. '月': 'M',
  1394. '周': 'W',
  1395. '天': 'D',
  1396. }
  1397. //英文领先单位转换
  1398. const edbLeadUnit = lang=='ch' ? LeadUnit : leadUnitEnMap[LeadUnit];
  1399. let axis_tag = axisLabelMap[IsAxis] || '';
  1400. //逆序拼接
  1401. let order_tag = orderLabelMap[Number(IsOrder)] ? `${axis_tag ? ',': ''}${orderLabelMap[Number(IsOrder)]}` : ''
  1402. //领先拼接
  1403. let edb_tag = edbInfoMap[EdbInfoType] ? `${(axis_tag||order_tag) ? ',' : '' }${edbInfoMap[EdbInfoType]} ${LeadValue}${edbLeadUnit}` : '';
  1404. return (axis_tag || order_tag || edb_tag) ? `(${axis_tag}${order_tag}${edb_tag})` : ''
  1405. }
  1406. /* 预测配置 分区 */
  1407. const getPredictParams = ({LatestDate,MoveLatestDate,PredictChartColor,ChartStyle}: IDataProps,chartStyle='') => {
  1408. return {
  1409. zoneAxis: 'x',
  1410. zones: [{
  1411. value: new Date(MoveLatestDate||LatestDate).getTime()+1
  1412. }, {
  1413. dashStyle: 'ShortDot',
  1414. color:(ChartStyle==='column' || chartStyle==='column') ? 'transparent' : PredictChartColor
  1415. }]
  1416. }
  1417. }
  1418. /* 季节图预测数据 年份=分割点年份做分割 年份>分割点年份全为预测 */
  1419. const getSeasonPredictParams = (timestamp: number) => {
  1420. return timestamp
  1421. ? {
  1422. zoneAxis: 'x',
  1423. zones: [{
  1424. value: new Date(timestamp).getTime()+1
  1425. }, {
  1426. dashStyle: 'ShortDot',
  1427. }]
  1428. }
  1429. : {}
  1430. }
  1431. /* 复制标题 */
  1432. const copyText = () => {
  1433. const { chartInfo } = state;
  1434. let input = document.createElement("input");
  1435. input.value = chartInfo.ChartName;
  1436. document.body.appendChild(input)
  1437. input.select();
  1438. document.execCommand('copy');
  1439. document.body.removeChild(input);
  1440. ElMessage.success('复制标题成功')
  1441. }
  1442. /* 分享链接 */
  1443. const copyUrl = () => {
  1444. let input = document.createElement("input");
  1445. input.value = location.href;
  1446. document.body.appendChild(input)
  1447. input.select();
  1448. document.execCommand('copy');
  1449. document.body.removeChild(input);
  1450. ElMessage.success('复制链接成功')
  1451. }
  1452. const refreshChart = _.debounce(async () => {
  1453. loading.value = true;
  1454. let res: any=null
  1455. if([1,6,7,8,9].includes(state.chartInfo.Source)){
  1456. res=await ChartApi.refreshChart({UniqueCode: state.chartInfo.UniqueCode})
  1457. }else if([2,5].includes(state.chartInfo.Source)){
  1458. res=await ChartApi.refreshCommordityChart({UniqueCode: state.chartInfo.UniqueCode});
  1459. }else if([3,4].includes(state.chartInfo.Source)){
  1460. res=await ChartApi.refreshRelevanceChart({UniqueCode: state.chartInfo.UniqueCode});
  1461. }
  1462. loading.value = false;
  1463. res.Ret === 200 && getChartInfo('refresh');
  1464. },400)
  1465. return {
  1466. ...toRefs(state),
  1467. loading,
  1468. haveData,
  1469. getChartInfo,
  1470. openNew,
  1471. copyUrl,
  1472. isShare,
  1473. language,
  1474. refreshChart,
  1475. copyText
  1476. };
  1477. },
  1478. });
  1479. </script>
  1480. <style scoped lang="less">
  1481. @import './index.less';
  1482. </style>