index.vue 48 KB

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