Detail.vue 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626
  1. <script setup>
  2. import chartBox from './component/chartBox.vue'
  3. import noAuth from './component/noAuth.vue'
  4. import sharePoster from '../components/SharePoster.vue'
  5. import { Popup, Toast,Picker } from 'vant';
  6. import {ref,onMounted, reactive, watch,computed} from 'vue'
  7. import {useRoute, useRouter,onBeforeRouteUpdate} from 'vue-router'
  8. import moment from 'moment'
  9. import _ from 'lodash';
  10. import Highcharts from 'highcharts/highstock';
  11. import {apiChartInfo,apiChartList,apiChartSave,apiChartBeforeAndNext,apiChartRefresh} from '@/api/hzyb/chart.js'
  12. const router=useRouter()
  13. const route=useRoute()
  14. document.title='图表详情'
  15. localStorage.setItem('hzyb-token',route.query.token)
  16. // 散点x轴
  17. const scatterXAxis = {
  18. tickPosition: 'inside',
  19. lineColor: '#bfbfbf',
  20. tickColor: '#bfbfbf',
  21. tickLength:5,
  22. ordinal: false,
  23. type: 'linear',
  24. }
  25. // 基础y轴配置
  26. const basicYAxis = {
  27. tickWidth: 1,
  28. tickLength: 5,
  29. lineWidth: 1,
  30. lineColor: '#bfbfbf',
  31. tickColor: '#bfbfbf',
  32. offset: 0,
  33. visible: true,
  34. gridLineWidth: 0,
  35. tickPosition: 'inside',
  36. endOnTick: false,
  37. startOnTick: false,
  38. showLastLabel: true,
  39. tickPixelInterval: 50,
  40. }
  41. //基础x轴配置
  42. const basicXAxis={
  43. tickPosition: 'inside',
  44. lineColor: '#bfbfbf',
  45. tickColor: '#bfbfbf',
  46. tickLength:5,
  47. type: 'datetime',
  48. ordinal: false,
  49. dateTimeLabelFormats: {
  50. day: '%y/%m',
  51. week: '%y/%m',
  52. month: '%y/%m',
  53. year: '%y/%m',
  54. },
  55. xDateFormat:'%Y-%m-%d'
  56. }
  57. // 获取用户信息
  58. import {apiUserInfo} from '@/api/hzyb/user'
  59. let canSave=ref(false)//是否有保存功能
  60. const getUserInfo=async ()=>{
  61. const res=await apiUserInfo({Authorization:route.query.token})
  62. if(res.code===200){
  63. if(res.data.is_inner){
  64. canSave.value=true
  65. }
  66. }
  67. }
  68. getUserInfo()
  69. let showDate=ref(false)
  70. let startDate=ref('')
  71. let endDate=ref('')
  72. let columns=ref([])
  73. // type 1:年-月 2:年
  74. const makeTimeData=(type)=>{
  75. let curYear=new Date().getFullYear()
  76. let yearArr=[]
  77. let monthArr=[]
  78. for (let i = 2010; i <= curYear; i++) {
  79. yearArr.push(i.toString())
  80. }
  81. for (let i = 1; i < 13; i++) {
  82. monthArr.push(i<10?'0'+i:i.toString())
  83. }
  84. if(type==1){
  85. let index1=yearArr.indexOf(startDate.value.split('-')[0])
  86. let index2=monthArr.indexOf(startDate.value.split('-')[1])
  87. let index3=yearArr.indexOf(endDate.value.split('-')[0])
  88. let index4=monthArr.indexOf(endDate.value.split('-')[1])
  89. return [
  90. {values:yearArr,defaultIndex: index1>-1?index1:yearArr.length-1},
  91. {values:monthArr,defaultIndex: index2>-1?index2:monthArr.length-1},
  92. {values:yearArr,defaultIndex: index3>-1?index3:yearArr.length-1},
  93. {values:monthArr,defaultIndex: index4>-1?index4:monthArr.length-1}
  94. ]
  95. }else{
  96. let index1=yearArr.indexOf(startDate.value)
  97. let index2=yearArr.indexOf(endDate.value)
  98. return [
  99. {values:yearArr,defaultIndex: index1>-1?index1:yearArr.length-1},
  100. {values:yearArr,defaultIndex: index2>-1?index2:yearArr.length-1}
  101. ]
  102. }
  103. }
  104. const handleShowDate=()=>{
  105. // if(columns.value.length===0){
  106. if( sameOptionType.value.includes(resData.value.ChartInfo.ChartType)){//曲线图
  107. columns.value=makeTimeData(1)
  108. }else if(resData.value.ChartInfo.ChartType===2){//季节性图表
  109. columns.value=makeTimeData(2)
  110. }
  111. // }
  112. showDate.value=true
  113. }
  114. // 确定选择时间
  115. const handleConfirmDate=(e)=>{
  116. let start='',end=''
  117. if(sameOptionType.value.includes(resData.value.ChartInfo.ChartType)){
  118. start=e[0]+'-'+e[1]
  119. end=e[2]+'-'+e[3]
  120. }else if(resData.value.ChartInfo.ChartType===2){
  121. start=e[0]
  122. end=e[1]
  123. }
  124. if(new Date(end)>=new Date(start)){
  125. startDate.value=start
  126. endDate.value=end
  127. dateType.value=5
  128. getChartInfo()
  129. showDate.value=false
  130. }else{
  131. Toast('结束时间不能小于开始时间')
  132. }
  133. }
  134. // 选择年份时间段
  135. let dateTypeList=ref([
  136. {
  137. name: '15年至今',
  138. value: 3,
  139. },
  140. // {
  141. // name: '18年至今',
  142. // value: 7,
  143. // },
  144. // {
  145. // name: '19年至今',
  146. // value: 8,
  147. // },
  148. {
  149. name: '20年至今',
  150. value: 9,
  151. },
  152. {
  153. name: '21年至今',
  154. value: 4,
  155. },
  156. {
  157. name: '22年至今',
  158. value: 11,
  159. },
  160. {
  161. name: '全部',
  162. value: 0,
  163. },
  164. ])
  165. let dateType=ref(null)
  166. const dateTypeClick=(item)=>{
  167. startDate.value=''
  168. endDate.value=''
  169. dateType.value=item.value
  170. getChartInfo()
  171. }
  172. let calendarType=ref('')//季节图 公历/农历
  173. // 公历/农历切换
  174. const calendarTypeChange=(val)=>{
  175. calendarType.value=val
  176. // startDate.value=''
  177. // endDate.value=''
  178. getChartInfo()
  179. }
  180. // 获取详情
  181. let ChartInfoId=route.query.ChartInfoId
  182. let chartData=ref({
  183. series:[],
  184. xAxis:[],
  185. yAxis:[],
  186. })// 图表配置数据
  187. let resData=ref(null)//接口详情数据
  188. let loading=ref(false)
  189. const sameOptionType = ref([1,3,4,5,6]);//筛选框一样的图表类型 曲线/面积/柱状/散点/组合 常规图
  190. const chartItemStyleArr = ref([
  191. { label: '曲线图', key: 1 ,value: 'spline'},
  192. { label: '面积图', key: 3 ,value: 'areaspline'},
  193. { label: '柱状图', key: 4 ,value: 'column'},
  194. { label: '散点图', key: 5 ,value: 'scatter'}
  195. ])//组合图配置时可选类型
  196. let noauth=ref(false)
  197. let noAuthData=ref(null)
  198. // 如果type:init 则是初始化获取数据
  199. const getChartInfo=async (type)=>{
  200. // resData.value=null
  201. loading.value=true
  202. const res=await apiChartInfo({
  203. ChartInfoId:ChartInfoId,
  204. DateType:dateType.value,
  205. StartDate:startDate.value&&sameOptionType.value.includes(resData.value.ChartInfo.ChartType)?startDate.value:'',
  206. EndDate:endDate.value&&sameOptionType.value.includes(resData.value.ChartInfo.ChartType)?endDate.value:'',
  207. SeasonStartDate:startDate.value&&resData.value.ChartInfo.ChartType===2?startDate.value:'',
  208. SeasonEndDate:endDate.value&&resData.value.ChartInfo.ChartType===2?endDate.value:'',
  209. Calendar:calendarType.value,
  210. Authorization:route.query.token,
  211. MyChartClassifyId:Number(route.query.MyChartClassifyId)
  212. })
  213. loading.value=false
  214. if(res.code===200){
  215. resData.value=res.data
  216. // document.title=res.data.ChartInfo.ChartName
  217. // 设置highchart配置 ChartType: 1曲线图 2季节图:季节图中公历和农历数据结构不同
  218. if( res.data.ChartInfo.ChartType !==2 ){
  219. if(type=='init'){
  220. dateType.value=res.data.ChartInfo.DateType
  221. startDate.value=res.data.ChartInfo.StartDate||''
  222. endDate.value=res.data.ChartInfo.EndDate||''
  223. calendarType.value=res.data.ChartInfo.Calendar||'公历'
  224. }
  225. const chartSetMap = {
  226. 1: setSplineOpt,
  227. 3: setStackOrCombinChart,
  228. 4: setStackOrCombinChart,
  229. 5: setScatterOptions,
  230. 6: setStackOrCombinChart
  231. };
  232. chartSetMap[res.data.ChartInfo.ChartType](res.data.EdbInfoList)
  233. }else{
  234. if(type=='init'){
  235. dateType.value=res.data.ChartInfo.DateType
  236. startDate.value=res.data.ChartInfo.SeasonStartDate||''
  237. endDate.value=res.data.ChartInfo.SeasonEndDate||''
  238. calendarType.value=res.data.ChartInfo.Calendar||'公历'
  239. }
  240. setSeasonOpt(res.data.EdbInfoList[0])
  241. }
  242. // 向小程序发送分享数据
  243. let postData = {
  244. params:{
  245. chartInfoId:ChartInfoId,
  246. searchVal:decodeURIComponent(route.query.searchVal)||'',
  247. MyChartId:route.query.MyChartId||'',
  248. MyChartClassifyId:route.query.MyChartClassifyId||'',
  249. },
  250. title: res.data.ChartInfo.ChartName,
  251. shareImg:res.data.ChartInfo.ChartImage
  252. };
  253. wx.miniProgram.postMessage({ data: postData });
  254. }else if(res.code==403){
  255. noauth.value=true
  256. noAuthData.value=res.data
  257. }
  258. }
  259. getChartInfo('init')
  260. // 路由改变 解决从搜索页返回数据不刷新问题
  261. onBeforeRouteUpdate((nval)=>{
  262. console.log('路由改变',nval);
  263. ChartInfoId=nval.query.ChartInfoId
  264. dateType.value=null
  265. startDate.value=''
  266. endDate.value=''
  267. getChartInfo('init')
  268. })
  269. // 上下线设置
  270. let showLimit=ref(false)
  271. let hasLeftAxis=ref(false)//是否有左轴
  272. let hasRightAxis=ref(false)//是否有右轴
  273. let axisLimitData=reactive({//左右轴极值
  274. leftMin:0,
  275. leftMax:0,
  276. rightMin:0,
  277. rightMax:0
  278. })
  279. const handleConfirmSetLimit=()=>{//确定修改极值(研究员会调用一次保存)
  280. if(canSave.value){
  281. handleSaveChart()
  282. }
  283. showLimit.value=false
  284. }
  285. const handleCloseLimit=()=>{//点击遮罩层关闭弹窗或者点击取消关闭
  286. getChartInfo()
  287. showLimit.value=false
  288. }
  289. // 设置常规图配置 曲线
  290. const setSplineOpt=(data)=>{
  291. let series=[]
  292. let yAxis=[]
  293. let xAxis = {}
  294. let temYLeftArr=[]
  295. let temYRightArr=[]
  296. let temYLeftIndex=data.findIndex((item) => item.IsAxis)
  297. let temYRightIndex=data.findIndex((item) => !item.IsAxis)
  298. let minAndMaxTimeTemArr=[]//存放所有指标的最大最小时间
  299. data.forEach((item,index)=>{
  300. //轴位置值相同的下标
  301. let sameSideIndex = data.findIndex(i => i.IsAxis === item.IsAxis);
  302. let dynamic_title = item.EdbName;
  303. let dynamic_arr = data.filter(
  304. (item) => dynamic_title === item.EdbName
  305. );
  306. //处理数据列name
  307. let temName= setDyncmicSerieName(item,dynamic_arr)
  308. let seriesItemObj={
  309. data:[],
  310. dataGrouping:{
  311. enabled:false
  312. },
  313. type: 'spline',
  314. yAxis:index,
  315. name:temName,
  316. color: item.ChartColor,
  317. lineWidth: Number(item.ChartWidth),
  318. visible:true,
  319. LatestDate:item.LatestDate,
  320. LatestValue:item.LatestValue
  321. }
  322. item.DataList = item.DataList || [];
  323. for (let i of item.DataList) {
  324. seriesItemObj.data.push([i.DataTimestamp, i.Value]);
  325. }
  326. series.push(seriesItemObj)
  327. // 设置y轴
  328. if(item.IsAxis){
  329. temYLeftArr.push(item)
  330. }else{
  331. temYRightArr.push(item)
  332. }
  333. let yItem={
  334. ...basicYAxis,
  335. IsAxis:item.IsAxis,
  336. labels: {
  337. formatter: function (ctx) {
  338. return sameSideIndex !== index ? '' : ctx.value;
  339. },
  340. align: 'center',
  341. y:5,
  342. },
  343. tickWidth: sameSideIndex !== index ? 0 : 1,
  344. title: {
  345. text: sameSideIndex !== index ? '' : `单位:${item.Unit}`,
  346. align: 'high',
  347. rotation: 0,
  348. y: -15,
  349. offset: 0,
  350. },
  351. opposite: item.IsAxis === 0,
  352. reversed: item.IsOrder,
  353. min: item.MinData,
  354. max: item.MaxData,
  355. chartEdbInfo:item//指标数据用于在保存时读取指标数据
  356. }
  357. yAxis.push(yItem)
  358. if(item.DataList.length>0){
  359. minAndMaxTimeTemArr.push(item.DataList[0].DataTimestamp)
  360. minAndMaxTimeTemArr.push(item.DataList[item.DataList.length-1].DataTimestamp)
  361. }
  362. })
  363. // 设置值
  364. chartData.value.series=series
  365. // 设置x轴
  366. // 找出所有指标的最大和最小时间 分成6段
  367. let minTime=Math.min.apply(null,minAndMaxTimeTemArr)
  368. let maxTime=Math.max.apply(null,minAndMaxTimeTemArr)
  369. const bool_time = xTimeDiffer(minTime,maxTime)
  370. if(bool_time){
  371. xAxis={
  372. ...basicXAxis,
  373. labels: {
  374. formatter: (ctx)=> {
  375. return Highcharts.dateFormat('%m/%d', ctx.value)
  376. }
  377. }
  378. }
  379. }
  380. // console.log(((maxTime-minTime)/6)/(24*3600*1000),':天');
  381. // let maxYear=new Date(maxTime).getFullYear()
  382. // let minYear=new Date(minTime).getFullYear()
  383. xAxis={
  384. ...basicXAxis,
  385. tickInterval:((maxTime-minTime)/6)/(24*3600*1000)>30?(maxTime-minTime)/6:24*3600*1000*30,
  386. }
  387. chartData.value.xAxis=[xAxis]
  388. yAxis.forEach(item=>{
  389. if(item.IsAxis){//左轴
  390. hasLeftAxis.value=true
  391. // item.min=getAxisMaxOrMin(temYLeftArr,'min')
  392. // item.max=getAxisMaxOrMin(temYLeftArr,'max')
  393. // axisLimitData.leftMin=getAxisMaxOrMin(temYLeftArr,'min')
  394. // axisLimitData.leftMax=getAxisMaxOrMin(temYLeftArr,'max')
  395. item.min=data[temYLeftIndex].MinData
  396. item.max=data[temYLeftIndex].MaxData
  397. axisLimitData.leftMin=data[temYLeftIndex].MinData
  398. axisLimitData.leftMax=data[temYLeftIndex].MaxData
  399. }else{
  400. hasRightAxis.value=true
  401. // item.min=getAxisMaxOrMin(temYRightArr,'min')
  402. // item.max=getAxisMaxOrMin(temYRightArr,'max')
  403. // axisLimitData.rightMin=getAxisMaxOrMin(temYRightArr,'min')
  404. // axisLimitData.rightMax=getAxisMaxOrMin(temYRightArr,'max')
  405. item.min=data[temYRightIndex].MinData
  406. item.max=data[temYRightIndex].MaxData
  407. axisLimitData.rightMin=data[temYRightIndex].MinData
  408. axisLimitData.rightMax=data[temYRightIndex].MaxData
  409. }
  410. })
  411. chartData.value.yAxis=yAxis
  412. chartData.value.rangeSelector={ enabled: false}
  413. }
  414. /* 堆叠图/组合图设置
  415. 本来和曲线图逻辑基本一致兼容下即可 为了以后便于维护和阅读还是拆开写吧
  416. */
  417. const setStackOrCombinChart = data => {
  418. //图表类型
  419. const chartTypeMap = {
  420. 3: 'areaspline',
  421. 4: 'column',
  422. 6: ''
  423. };
  424. let chartStyle = chartTypeMap[resData.value.ChartInfo.ChartType];
  425. let series=[]
  426. let yAxis=[]
  427. let xAxis = {}
  428. let temYLeftArr=[]
  429. let temYRightArr=[]
  430. let temYLeftIndex,temYRightIndex;
  431. let minAndMaxTimeTemArr=[]//存放所有指标的最大最小时间
  432. data.forEach((item,index)=>{
  433. //轴位置值相同的下标
  434. let sameSideIndex = data.findIndex(i => i.IsAxis === item.IsAxis);
  435. //堆叠图的yAxis必须一致 数据列所对应的y轴
  436. let serie_yIndex = index;
  437. if([3,4].includes(resData.value.ChartInfo.ChartType)) {
  438. // 类型为堆叠图时公用第一个指标y轴
  439. serie_yIndex = 0;
  440. } else if(resData.value.ChartInfo.ChartType ===6 && ['areaspline','column'].includes(item.ChartStyle)) {
  441. // 组合图找第一个堆叠柱状或面积的作为公用
  442. serie_yIndex = data.findIndex(i => i.ChartStyle === item.ChartStyle);
  443. }
  444. //数据对应的y轴是公用轴则配置也共享
  445. item.IsAxis = serie_yIndex === index ? item.IsAxis : data[serie_yIndex].IsAxis;
  446. item.IsOrder = serie_yIndex === index ? item.IsOrder : data[serie_yIndex].IsOrder;
  447. temYLeftIndex = [3,4].includes(resData.value.ChartInfo.ChartType)
  448. ? (data[serie_yIndex].IsAxis ? serie_yIndex : -1)
  449. : data.findIndex((item) => item.IsAxis);
  450. temYRightIndex = [3,4].includes(resData.value.ChartInfo.ChartType)
  451. ? (data[serie_yIndex].IsAxis ? -1 : serie_yIndex)
  452. : data.findIndex((item) => !item.IsAxis);
  453. let dynamic_title = item.EdbName;
  454. let dynamic_arr = data.filter(
  455. (item) => dynamic_title === item.EdbName
  456. );
  457. //处理数据列name
  458. let temName= setDyncmicSerieName(item,dynamic_arr)
  459. let seriesItemObj={
  460. data:[],
  461. dataGrouping:{
  462. enabled:false
  463. },
  464. type: chartStyle || item.ChartStyle,
  465. yAxis:serie_yIndex,
  466. name:temName,
  467. color: item.ChartColor,
  468. lineWidth: (resData.value.ChartInfo.ChartType === 6 && item.ChartStyle === 'spline') ? Number(item.ChartWidth) : 0,
  469. fillColor: (resData.value.ChartInfo.ChartType === 3 || (resData.value.ChartInfo.ChartType === 6 && item.ChartStyle === 'areaspline')) ? item.ChartColor : undefined,
  470. visible:true,
  471. LatestDate:item.LatestDate,
  472. LatestValue:item.LatestValue
  473. }
  474. item.DataList = item.DataList || [];
  475. for (let i of item.DataList) {
  476. seriesItemObj.data.push([i.DataTimestamp, i.Value]);
  477. }
  478. series.push(seriesItemObj)
  479. // 设置y轴
  480. if(item.IsAxis){
  481. temYLeftArr.push(item)
  482. }else{
  483. temYRightArr.push(item)
  484. }
  485. let yItem={
  486. ...basicYAxis,
  487. IsAxis:item.IsAxis,
  488. labels: {
  489. formatter: function (ctx) {
  490. return sameSideIndex !== index ? '' : ctx.value;
  491. },
  492. align: 'center',
  493. y:5,
  494. },
  495. title: {
  496. text: sameSideIndex !== index ? '' : `单位:${item.Unit}`,
  497. align: 'high',
  498. rotation: 0,
  499. y: -15,
  500. offset: 0,
  501. },
  502. opposite: item.IsAxis === 0,
  503. reversed: item.IsOrder,
  504. min: item.MinData,
  505. max: item.MaxData,
  506. tickWidth: sameSideIndex !== index ? 0 : 1,
  507. visible: serie_yIndex === index ? true : false,
  508. chartEdbInfo:item//指标数据用于在保存时读取指标数据
  509. }
  510. yAxis.push(yItem)
  511. if(item.DataList.length>0){
  512. minAndMaxTimeTemArr.push(item.DataList[0].DataTimestamp)
  513. minAndMaxTimeTemArr.push(item.DataList[item.DataList.length-1].DataTimestamp)
  514. }
  515. })
  516. // 设置值
  517. chartData.value.series=series
  518. // 设置x轴
  519. // 找出所有指标的最大和最小时间 分成6段
  520. let minTime=Math.min.apply(null,minAndMaxTimeTemArr)
  521. let maxTime=Math.max.apply(null,minAndMaxTimeTemArr)
  522. const bool_time = xTimeDiffer(minTime,maxTime)
  523. if(bool_time){
  524. xAxis={
  525. ...basicXAxis,
  526. labels: {
  527. formatter: (ctx)=> {
  528. return Highcharts.dateFormat('%m/%d', ctx.value)
  529. }
  530. }
  531. }
  532. }
  533. xAxis={
  534. ...basicXAxis,
  535. tickInterval:((maxTime-minTime)/6)/(24*3600*1000)>30?(maxTime-minTime)/6:24*3600*1000*30,
  536. }
  537. chartData.value.xAxis=[xAxis]
  538. yAxis.forEach(item=>{
  539. if(item.IsAxis){//左轴
  540. hasLeftAxis.value=true
  541. item.min=data[temYLeftIndex].MinData
  542. item.max=data[temYLeftIndex].MaxData
  543. axisLimitData.leftMin=data[temYLeftIndex].MinData
  544. axisLimitData.leftMax=data[temYLeftIndex].MaxData
  545. }else{
  546. hasRightAxis.value=true
  547. item.min=data[temYRightIndex].MinData
  548. item.max=data[temYRightIndex].MaxData
  549. axisLimitData.rightMin=data[temYRightIndex].MinData
  550. axisLimitData.rightMax=data[temYRightIndex].MaxData
  551. }
  552. })
  553. chartData.value.yAxis=yAxis
  554. chartData.value.rangeSelector={ enabled: false}
  555. }
  556. /* 拼接数据列动态name */
  557. const setDyncmicSerieName = (item,dynamic_arr) => {
  558. // 拼接配置 IsAxis左轴1 右轴0 IsOrder正序false 逆序true EdbInfoType是否是领先指标
  559. let dynamic_tag =item.IsAxis && item.IsOrder && item.EdbInfoType
  560. ? '(逆序)'
  561. : !item.IsAxis && item.IsOrder && item.EdbInfoType
  562. ? '(右轴,逆序)'
  563. : !item.IsAxis && !item.IsOrder && item.EdbInfoType
  564. ? '(右轴)'
  565. : !item.IsAxis && !item.IsOrder && !item.EdbInfoType
  566. ? `(右轴,领先${item.LeadValue}${item.LeadUnit})`
  567. : !item.IsAxis && item.IsOrder && !item.EdbInfoType
  568. ? `(右轴,逆序,领先${item.LeadValue}${item.LeadUnit})`
  569. : item.IsAxis && item.IsOrder && !item.EdbInfoType
  570. ? `(逆序,领先${item.LeadValue}${item.LeadUnit})`
  571. : item.IsAxis && !item.IsOrder && !item.EdbInfoType
  572. ? `(领先${item.LeadValue}${item.LeadUnit})`
  573. : '';
  574. let temName = dynamic_arr.length > 1
  575. ? `${item.EdbName}(${item.SourceName})${dynamic_tag}`
  576. : `${item.EdbName}${dynamic_tag}`
  577. if(temName.length>20){
  578. let temArr=[]
  579. for(let i=0;i<temName.length/20;i++){
  580. temArr.push(temName.slice(i*20,i*20+20))
  581. }
  582. // console.log(temArr);
  583. temName=temArr.join('<br>')
  584. }
  585. return temName
  586. }
  587. //设置季节图配置
  588. const setSeasonOpt=(data)=>{
  589. hasLeftAxis.value=true
  590. const colorsArr=['#4B0082','#7FFFAA','#FF4500','#808000','#EEE8AA','#849EC1','#8A4294','#578B5A','#FDA8C7','#53B3FF','#999999','#000000','#FFDF0C','#FF0000','#0033FF']
  591. let series=[],yAxis=[]
  592. //农历默认选中一年数据并隐藏按钮 公历显示全部数据
  593. let rangeSelector={}
  594. // 公历
  595. if(calendarType.value==='公历'){
  596. data.DataList.forEach((item,index)=>{
  597. let seriesItem={
  598. data:[],
  599. dataGrouping:{
  600. enabled:false
  601. },
  602. type:data.ChartStyle,
  603. yAxis:index,
  604. name:item.Year,
  605. color:colorsArr.slice(-data.DataList.length)[index],
  606. visible:true
  607. }
  608. item.DataList=item.DataList||[]
  609. for(let i of item.DataList){
  610. seriesItem.data.push([i.DataTimestamp, i.Value])
  611. }
  612. series.push(seriesItem)
  613. let yAxisItem={
  614. IsAxis:data.IsAxis,
  615. labels: {
  616. // formatter: function (ctx) {
  617. // return ctx.value;
  618. // },
  619. align: 'center',
  620. y:5
  621. },
  622. title: {
  623. text: `单位:${data.Unit}`,
  624. align: 'high',
  625. rotation: 0,
  626. y: -15,
  627. offset: 0,
  628. },
  629. max: Number(data.MaxData),
  630. min: Number(data.MinData),
  631. lineWidth: 1,
  632. lineColor: '#bfbfbf',
  633. tickColor: '#bfbfbf',
  634. offset: 0,
  635. opposite: false,
  636. reversed: false,
  637. visible: true,
  638. gridLineWidth: 0,
  639. tickWidth: 1,
  640. tickLength:5,
  641. tickPosition: 'inside',
  642. endOnTick: false,
  643. startOnTick: false,
  644. showLastLabel: true, //显示最后刻度值
  645. tickPixelInterval: 50,
  646. // chartEdbInfo:item//指标数据
  647. }
  648. yAxis.push(yAxisItem)
  649. })
  650. rangeSelector={ enabled: false}
  651. }
  652. // 农历
  653. if(calendarType.value==='农历'){
  654. let filterArr=data.DataList.List&&data.DataList.List.slice(1,data.DataList.List.length)||[]
  655. console.log('aaa',filterArr);
  656. filterArr.forEach((item,index)=>{
  657. let seriesItem={
  658. data:[],
  659. dataGrouping:{
  660. enabled:false
  661. },
  662. type:data.ChartStyle,
  663. yAxis:index,
  664. name:item.Year,
  665. color:colorsArr.slice(-filterArr.length)[index],
  666. visible:true
  667. }
  668. let temarr=item.Items||[]
  669. for(let i of temarr){
  670. seriesItem.data.push([i.DataTimestamp, i.Value])
  671. }
  672. series.push(seriesItem)
  673. let yAxisItem={
  674. IsAxis:data.IsAxis,
  675. labels: {
  676. formatter: function (ctx) {
  677. return ctx.value;
  678. },
  679. align: 'center',
  680. y:5
  681. },
  682. title: {
  683. text: `单位:${data.Unit}`,
  684. align: 'high',
  685. rotation: 0,
  686. y: -15,
  687. offset: 0,
  688. },
  689. max: Number(data.MaxData),
  690. min: Number(data.MinData),
  691. lineWidth: 1,
  692. lineColor: '#bfbfbf',
  693. tickColor: '#bfbfbf',
  694. offset: 0,
  695. opposite: false,
  696. reversed: false,
  697. visible: true,
  698. gridLineWidth: 0,
  699. tickWidth: 1,
  700. tickLength:5,
  701. tickPosition: 'inside',
  702. endOnTick: false,
  703. startOnTick: false,
  704. showLastLabel: true, //显示最后刻度值
  705. tickPixelInterval: 50,
  706. // chartEdbInfo:item//指标数据用于在保存时读取指标数据
  707. }
  708. yAxis.push(yAxisItem)
  709. })
  710. rangeSelector={
  711. enabled: true,
  712. selected: 0,
  713. inputStyle: {
  714. display: 'none',
  715. },
  716. labelStyle: {
  717. display: 'none',
  718. },
  719. buttonTheme: {
  720. style: {
  721. display: 'none',
  722. },
  723. },
  724. buttons: [
  725. {
  726. type: 'month',
  727. count: 12,
  728. text: '12月',
  729. },
  730. {
  731. type: 'month',
  732. count: 15,
  733. text: '15月',
  734. },
  735. {
  736. type: 'all',
  737. text: '全部',
  738. }
  739. ]
  740. }
  741. }
  742. chartData.value.chart={ spacing: [30, 8, 2, 8]}
  743. chartData.value.series=series
  744. chartData.value.yAxis=yAxis
  745. chartData.value.rangeSelector=rangeSelector
  746. // 设置坐标轴极值
  747. hasLeftAxis.value=true
  748. axisLimitData.leftMin=Number(data.MinData)
  749. axisLimitData.leftMax=Number(data.MaxData)
  750. // 季节图x轴显示月/日
  751. let xAxis={
  752. tickPosition: 'inside',
  753. lineColor: '#bfbfbf',
  754. tickColor: '#bfbfbf',
  755. tickLength:5,
  756. type: 'datetime',
  757. ordinal: false,
  758. dateTimeLabelFormats: {
  759. day: '%y/%m',
  760. week: '%y/%m',
  761. month: '%y/%m',
  762. year: '%y/%m',
  763. },
  764. labels: {
  765. formatter: (ctx)=> {
  766. return Highcharts.dateFormat('%m/%d', ctx.value)
  767. }
  768. }
  769. }
  770. xAxis={
  771. ...xAxis,
  772. tickInterval:24*3600*1000*60,//季节图
  773. }
  774. chartData.value.xAxis=[xAxis]
  775. // 季节图提示框显示 月/日
  776. chartData.value.tooltip={
  777. split: false,
  778. shared: true,
  779. dateTimeLabelFormats: {
  780. // 时间格式化字符
  781. day: '%m/%d',
  782. week: '%m/%d',
  783. month: '%m/%d',
  784. year: '%m/%d',
  785. },
  786. xDateFormat: '%m/%d',
  787. }
  788. }
  789. /* 散点图 第一个指标值为x轴 第二个指标为y轴*/
  790. const setScatterOptions = (dataList) => {
  791. const { ChartInfo } = resData.value;
  792. // 取2个指标中日期相同的数据
  793. const real_data = [];
  794. let tmpData_date = {};//用来取点对应的日期
  795. let data1 = _.cloneDeep(dataList)[0].DataList || [];
  796. let data2 = _.cloneDeep(dataList)[1].DataList || [];
  797. data1.forEach((_item) => {
  798. data2.forEach((_item2) => {
  799. if(_item.DataTimestamp === _item2.DataTimestamp) {
  800. //日期
  801. let itemIndex =_item.Value + "_" +_item2.Value
  802. if(tmpData_date[itemIndex]) {
  803. tmpData_date[itemIndex].push( moment(_item.DataTimestamp).format('YYYY/MM/DD'))
  804. } else {
  805. tmpData_date[itemIndex] = [moment(_item.DataTimestamp).format('YYYY/MM/DD')]
  806. }
  807. //值
  808. real_data.push({
  809. x: _item.Value,
  810. y: _item2.Value
  811. })
  812. }
  813. })
  814. })
  815. real_data.sort((x,y) => x-y);
  816. //悬浮窗 拼接日期 原始指标名称
  817. let tooltip = {
  818. formatter: function() {
  819. return `<strong>${ tmpData_date[this.x+'_'+this.y].length > 4 ? tmpData_date[this.x+'_'+this.y].slice(0,4).join()+'...' : tmpData_date[this.x+'_'+this.y].join() }</strong><br>
  820. ${dataList[0].EdbName}: <span style="font-weight: 600"> ${this.x}</span><br>
  821. ${dataList[1].EdbName}: <span style="font-weight: 600"> ${this.y}</span>
  822. `
  823. }
  824. }
  825. const { IsOrder,ChartColor } = dataList[0];
  826. //y轴
  827. let yAxis = {
  828. title: {
  829. text: `单位:${dataList[1].Unit}`,
  830. align: 'high',
  831. rotation: 0,
  832. y: -15,
  833. offset: 0,
  834. },
  835. labels: {
  836. formatter: function (ctx) {
  837. return ctx.value;
  838. },
  839. align: 'center',
  840. },
  841. opposite: false,
  842. reversed: IsOrder,
  843. min: Number(dataList[0].MinData),
  844. max: Number(dataList[0].MaxData),
  845. tickWidth: 1,
  846. tickLength: 5,
  847. lineWidth: 1,
  848. lineColor: '#bfbfbf',
  849. tickColor: '#bfbfbf',
  850. offset: 0,
  851. visible: true,
  852. gridLineWidth: 0,
  853. tickPosition: 'inside',
  854. endOnTick: false,
  855. startOnTick: false,
  856. showLastLabel: true,
  857. tickPixelInterval: 50
  858. }
  859. //数据列
  860. let series = {
  861. data: [],
  862. type: 'scatter',
  863. name: `${ChartInfo.ChartName}${IsOrder ? '(逆序)' : ''}`,
  864. color: ChartColor,
  865. visible:true,
  866. lineWidth: 0
  867. }
  868. real_data.forEach(_ => {
  869. series.data.push([_.x,_.y])
  870. })
  871. chartData.value = {
  872. title: {
  873. text:''
  874. },
  875. series: [ series ],
  876. yAxis,
  877. xAxis: {
  878. ...scatterXAxis,
  879. title: {
  880. text: `单位:${dataList[0].Unit}`,
  881. align: 'high',
  882. rotation: 0,
  883. x: 0,
  884. offset: 20,
  885. },
  886. },
  887. tooltip
  888. }
  889. }
  890. // 查询范围为1年内 x轴显示为月/日 否则默认年/月
  891. const xTimeDiffer=(minTime,maxTime)=>{
  892. //年限差
  893. let year_differ=moment(maxTime).diff(moment(minTime),'years',true)
  894. console.log('年限差',year_differ)
  895. if (year_differ<=1) {
  896. console.log('true');
  897. return true;
  898. } else {
  899. console.log('false');
  900. return false;
  901. }
  902. }
  903. // 找出最大最小值 arr 数据, type:min max
  904. const getAxisMaxOrMin=(arr,type)=>{
  905. let minArr=[],maxArr=[],resNum=0
  906. arr.forEach(item=>{
  907. minArr.push(item.MinData)
  908. maxArr.push(item.MaxData)
  909. })
  910. if(type==='min'){
  911. resNum=Math.min.apply(null,minArr)
  912. }else{
  913. resNum=Math.max.apply(null,maxArr)
  914. }
  915. return resNum
  916. }
  917. // 监听极值变化
  918. watch(
  919. ()=>axisLimitData,
  920. (nval)=>{
  921. // 只有当修改极值弹窗弹起时才去修改
  922. if(!showLimit.value) return
  923. console.log('极值改变');
  924. chartData.value.yAxis.forEach(item=>{
  925. if(item.IsAxis){//左轴
  926. item.min=nval.leftMin
  927. item.max=nval.leftMax
  928. }else{
  929. item.min=nval.rightMin
  930. item.max=nval.rightMax
  931. }
  932. })
  933. },
  934. {
  935. deep:true
  936. }
  937. )
  938. // 前去搜索
  939. const handleGoSearch=()=>{
  940. router.push({
  941. path:'/hzyb/chart/search',
  942. query:{
  943. token:route.query.token
  944. }
  945. })
  946. }
  947. // 获取当前图表
  948. let searchVal=decodeURIComponent(route.query.searchVal)
  949. let searchListData=ref([])//搜索的数据
  950. const getSearchListData=async ()=>{
  951. const res=await apiChartList({Keywords:searchVal,Page:1,Limit:10000,Authorization:route.query.token})
  952. if(res.code===200){
  953. searchListData.value=res.data
  954. }
  955. }
  956. let chartBeforeAndNextData=ref(null)
  957. // 翻页
  958. const pageChange=async (type)=>{
  959. // 搜索情况
  960. if(searchVal){
  961. if(type==='before'){
  962. let index=searchListData.value.findIndex(item=>item.ChartInfoId==resData.value.ChartInfo.ChartInfoId)
  963. if(!searchListData.value[index-1]){
  964. Toast('当前已是第一张图')
  965. }else{
  966. calendarType.value=''
  967. router.replace({
  968. query:{
  969. ...route.query,
  970. ChartInfoId:searchListData.value[index-1].ChartInfoId,
  971. }
  972. })
  973. }
  974. }
  975. if(type==='next'){
  976. let index=searchListData.value.findIndex(item=>item.ChartInfoId==resData.value.ChartInfo.ChartInfoId)
  977. if(!searchListData.value[index+1]){
  978. Toast('当前已是最后一张图')
  979. }else{
  980. calendarType.value=''
  981. router.replace({
  982. query:{
  983. ...route.query,
  984. ChartInfoId:searchListData.value[index+1].ChartInfoId,
  985. }
  986. })
  987. }
  988. }
  989. return
  990. }
  991. // 非搜索情况可切换分类
  992. const res=await apiChartBeforeAndNext({
  993. MyChartId:route.query.MyChartId,
  994. MyChartClassifyId:route.query.MyChartClassifyId,
  995. Authorization:route.query.token
  996. })
  997. if(res.code===200){
  998. chartBeforeAndNextData.value=res.data
  999. if(type==='before'){
  1000. if(chartBeforeAndNextData.value.PrevChart.ChartInfoId===0){
  1001. Toast('当前已是第一张图')
  1002. }else{
  1003. if(chartBeforeAndNextData.value.PrevChart.Switch){
  1004. let temStr=chartBeforeAndNextData.value.PrevChart.MyChartClassifyName
  1005. setTimeout(() => {
  1006. Toast({
  1007. message:`您正在浏览${temStr}中的图表`,
  1008. duration: 3000,
  1009. })
  1010. }, 500);
  1011. }
  1012. calendarType.value=''
  1013. router.replace({
  1014. query:{
  1015. ...route.query,
  1016. ChartInfoId:chartBeforeAndNextData.value.PrevChart.ChartInfoId,
  1017. MyChartId:chartBeforeAndNextData.value.PrevChart.MyChartId,
  1018. MyChartClassifyId:chartBeforeAndNextData.value.PrevChart.MyChartClassifyId,
  1019. token:route.query.token
  1020. }
  1021. })
  1022. }
  1023. }else if(type==='next'){
  1024. if(chartBeforeAndNextData.value.NextChart.ChartInfoId===0){
  1025. Toast('当前已是最后一张图')
  1026. }else{
  1027. if(chartBeforeAndNextData.value.NextChart.Switch){
  1028. let temStr=chartBeforeAndNextData.value.NextChart.MyChartClassifyName
  1029. setTimeout(() => {
  1030. Toast({
  1031. message:`您正在浏览${temStr}中的图表`,
  1032. duration: 3000,
  1033. })
  1034. }, 500);
  1035. }
  1036. calendarType.value=''
  1037. router.replace({
  1038. query:{
  1039. ...route.query,
  1040. ChartInfoId:chartBeforeAndNextData.value.NextChart.ChartInfoId,
  1041. MyChartId:chartBeforeAndNextData.value.NextChart.MyChartId,
  1042. MyChartClassifyId:chartBeforeAndNextData.value.NextChart.MyChartClassifyId,
  1043. token:route.query.token
  1044. }
  1045. })
  1046. }
  1047. }
  1048. }
  1049. }
  1050. onMounted(()=>{
  1051. if(searchVal){
  1052. getSearchListData()
  1053. }
  1054. })
  1055. // 保存
  1056. const handleSaveChart=async ()=>{
  1057. let params={}
  1058. if(sameOptionType.value.includes(resData.value.ChartInfo.ChartType)){//曲线图
  1059. let arr=chartData.value.yAxis.map(item=>{
  1060. return {
  1061. ChartColor: item.chartEdbInfo.ChartColor,
  1062. ChartStyle: item.chartEdbInfo.ChartStyle,
  1063. ChartWidth: Number(item.chartEdbInfo.ChartWidth),
  1064. EdbInfoId: item.chartEdbInfo.EdbInfoId,
  1065. EdbInfoType: item.chartEdbInfo.EdbInfoType,
  1066. IsAxis: item.chartEdbInfo.IsAxis,
  1067. IsOrder: item.chartEdbInfo.IsOrder,
  1068. LeadUnit: item.chartEdbInfo.EdbInfoType ? '' : item.chartEdbInfo.LeadUnit,
  1069. LeadValue: item.chartEdbInfo.EdbInfoType ? 0 : Number(item.chartEdbInfo.LeadValue),
  1070. MaxData: Number(item.max),
  1071. MinData: Number(item.min),
  1072. }
  1073. })
  1074. params={
  1075. ChartInfoId: resData.value.ChartInfo.ChartInfoId || 0,
  1076. ChartEdbInfoList: arr,
  1077. DateType: dateType.value,
  1078. StartDate:startDate.value,
  1079. EndDate: endDate.value,
  1080. }
  1081. }else if(resData.value.ChartInfo.ChartType===2){//季节图
  1082. let arr=resData.value.EdbInfoList.map(item=>{
  1083. return {
  1084. ChartColor: item.ChartColor,
  1085. ChartStyle: item.ChartStyle,
  1086. ChartWidth: Number(item.ChartWidth),
  1087. EdbInfoId: item.EdbInfoId,
  1088. EdbInfoType: item.EdbInfoType,
  1089. IsAxis: item.IsAxis,
  1090. IsOrder: item.IsOrder,
  1091. LeadUnit: item.EdbInfoType ? '' : item.LeadUnit,
  1092. LeadValue: item.EdbInfoType ? 0 : Number(item.LeadValue),
  1093. MaxData: Number(chartData.value.yAxis[0].max),
  1094. MinData: Number(chartData.value.yAxis[0].min),
  1095. }
  1096. })
  1097. params={
  1098. ChartInfoId: resData.value.ChartInfo.ChartInfoId || 0,
  1099. ChartEdbInfoList: arr,
  1100. Calendar: calendarType.value,
  1101. SeasonStartDate:startDate.value,
  1102. SeasonEndDate: endDate.value,
  1103. }
  1104. }
  1105. const res=await apiChartSave(params)
  1106. if(res.code===200){
  1107. Toast.success('保存成功')
  1108. }
  1109. }
  1110. // 刷新图表
  1111. const handleRefreshChart=async ()=>{
  1112. const res=await apiChartRefresh({ChartInfoId:Number(ChartInfoId)})
  1113. if(res.code===200){
  1114. setTimeout(() => {
  1115. Toast.success(res.msg)
  1116. }, 200);
  1117. getChartInfo()
  1118. }
  1119. }
  1120. let pageBoxPosition=reactive({
  1121. top:window.innerHeight-165,
  1122. left:window.innerWidth-50,
  1123. temTop:0,
  1124. temLeft:0,
  1125. })
  1126. const pageTouchmove=(e)=>{
  1127. const touchObj=e.touches[0]
  1128. let top=touchObj.clientY-82
  1129. let left=touchObj.clientX-25
  1130. if(left<=0){
  1131. left=0
  1132. }
  1133. if(left>window.innerWidth-50){
  1134. left=window.innerWidth-50
  1135. }
  1136. if(top<=0){
  1137. top=0
  1138. }
  1139. if(top>window.innerHeight-115){
  1140. top=window.innerHeight-115
  1141. }
  1142. pageBoxPosition.top=top
  1143. pageBoxPosition.left=left
  1144. event.preventDefault();//阻止页面移动
  1145. }
  1146. // 生成海报所需跳转到小程序页面参数
  1147. const code_scene=computed(()=>{
  1148. let obj= {
  1149. chartInfoId:ChartInfoId,
  1150. searchVal:decodeURIComponent(route.query.searchVal)||'',
  1151. MyChartId:route.query.MyChartId||'',
  1152. MyChartClassifyId:route.query.MyChartClassifyId||'',
  1153. from:'share'
  1154. }
  1155. return JSON.stringify(obj)
  1156. })
  1157. // 生成海报图片所需要的数据
  1158. const posterParams=computed(()=>{
  1159. return {
  1160. chart_name:resData.value.ChartInfo.ChartName,
  1161. chart_image:resData.value.ChartInfo.ChartImage
  1162. }
  1163. })
  1164. </script>
  1165. <template>
  1166. <div class="chart-detail" v-if="!loading&&!noauth">
  1167. <div class="chart-title">{{resData.ChartInfo.ChartName}}</div>
  1168. <div class="top-box">
  1169. <div class="flex calendar-box" style="float:left" @click="handleShowDate">
  1170. <img src="../../../assets/hzyb/chart/calendar.png" alt="">
  1171. <span class="date">{{startDate||'开始日期'}}</span>
  1172. <span style="margin:0 5px">至</span>
  1173. <span class="date">{{endDate||'结束日期'}}</span>
  1174. </div>
  1175. <img class="icon" src="../../../assets/hzyb/chart/search.png" alt="" @click="handleGoSearch">
  1176. <share-poster
  1177. :shareData="{
  1178. type:'chart_detail',
  1179. code_scene:code_scene,
  1180. code_page:'pages-chart/chartDetail',
  1181. data:posterParams
  1182. }"
  1183. ></share-poster>
  1184. <img class="icon" src="../../../assets/hzyb/chart/save.png" alt="" @click="handleSaveChart" v-if="canSave">
  1185. <img class="icon" src="../../../assets/hzyb/chart/refresh.png" alt="" @click="handleRefreshChart">
  1186. </div>
  1187. <chartBox :options='chartData' v-if="!loading"></chartBox>
  1188. <div class="flex source-box">
  1189. <div :style="{flex:resData&&resData.ChartInfo.ChartType===2?1:2}"><span v-if="resData&&resData.ChartInfo.ChartType!==2">来源:{{resData&&resData.ChartInfo.ChartSource}}</span></div>
  1190. <div class="season-change-box" style="flex:1" v-if="resData&&resData.ChartInfo.ChartType===2">
  1191. <span :class="calendarType==='农历'&&'active'" @click="calendarTypeChange('农历')">农历</span>
  1192. <span :class="calendarType==='公历'&&'active'" @click="calendarTypeChange('公历')">公历</span>
  1193. </div>
  1194. <span style="color:#E3B377;flex:1;text-align:right" @click="showLimit=true">上下限设置</span>
  1195. </div>
  1196. <div class="source-box" style="margin-top:5px" v-if="resData&&resData.ChartInfo.ChartType===2">来源:{{resData&&resData.ChartInfo.ChartSource}}</div>
  1197. <!-- 日期类型 -->
  1198. <div class="date-type-box" v-if="resData&&sameOptionType.includes(resData.ChartInfo.ChartType)">
  1199. <div
  1200. :class="['item',item.value==dateType?'active':'']"
  1201. v-for="item in dateTypeList"
  1202. :key="item.value"
  1203. @click="dateTypeClick(item)"
  1204. >{{item.name}}</div>
  1205. </div>
  1206. <!-- 最新值 -->
  1207. <div class="latest-value-wrap" v-if="resData">
  1208. <p style="margin-bottom:10px">最新数值</p>
  1209. <ul class="list" v-if="sameOptionType.includes(resData.ChartInfo.ChartType)">
  1210. <li v-for="item in chartData.series" :key="item.name">
  1211. <p style="color:#333">{{moment(item.LatestDate).format('YYYY-MM-DD')}}</p>
  1212. <p :style="{color:item.color,flex:1}">{{item.name.length>20?item.name.replace(/<br>/g,''):item.name}}</p>
  1213. <p style="color:#1F243A">{{item.LatestValue}}</p>
  1214. </li>
  1215. </ul>
  1216. <ul class="list" v-else>
  1217. <li>
  1218. <p style="color:#333">{{moment(resData.EdbInfoList[0].LatestDate).format('YYYY-MM-DD')}}</p>
  1219. <p :style="{color:resData.EdbInfoList[0].ChartColor,flex:1}">{{resData.ChartInfo.ChartName}}</p>
  1220. <p style="color:#1F243A">{{resData.EdbInfoList[0].LatestValue}}</p>
  1221. </li>
  1222. </ul>
  1223. </div>
  1224. <!-- 上一张下一张图切换 -->
  1225. <div
  1226. v-if="$route.query.from!='share'"
  1227. class="change-page-wrap"
  1228. :style="{left:pageBoxPosition.left+'px',top:pageBoxPosition.top+'px'}"
  1229. @touchmove.stop="pageTouchmove"
  1230. @touchstart.stop="pageTouchStart"
  1231. >
  1232. <div class="top" @click.stop="pageChange('before')"></div>
  1233. <div class="bot" @click.stop="pageChange('next')"></div>
  1234. </div>
  1235. <!-- 日期选择 -->
  1236. <Popup
  1237. v-model:show="showDate"
  1238. position="bottom"
  1239. round
  1240. >
  1241. <Picker
  1242. title=""
  1243. :columns="columns"
  1244. @cancel="showDate=false"
  1245. @confirm="handleConfirmDate"
  1246. />
  1247. </Popup>
  1248. <!-- 上下限设置 -->
  1249. <Popup
  1250. v-model:show="showLimit"
  1251. position="bottom"
  1252. :overlay-style="{background:'rgba(0,0,0,0)'}"
  1253. @click-overlay="handleCloseLimit"
  1254. >
  1255. <div class="set-limit-box">
  1256. <div class="flex limit-top">
  1257. <span style="color:#A7A7A7" @click="handleCloseLimit">取消</span>
  1258. <span>上下限设置</span>
  1259. <span style="color:#E3B377" @click="handleConfirmSetLimit">确定</span>
  1260. </div>
  1261. <div class="con">
  1262. <p style="margin:20px 0" class="top">
  1263. <span>上限</span>
  1264. <span>下限</span>
  1265. </p>
  1266. <p style="margin-bottom:25px" v-if="hasLeftAxis">
  1267. <span style="margin-right:35px">左轴</span>
  1268. <input type="number" v-model="axisLimitData.leftMax">
  1269. <span style="margin:0 10px">至</span>
  1270. <input type="number" v-model="axisLimitData.leftMin">
  1271. </p>
  1272. <p v-if="hasRightAxis">
  1273. <span style="margin-right:35px">右轴</span>
  1274. <input type="number" v-model="axisLimitData.rightMax">
  1275. <span style="margin:0 10px">至</span>
  1276. <input type="number" v-model="axisLimitData.rightMin">
  1277. </p>
  1278. </div>
  1279. </div>
  1280. </Popup>
  1281. </div>
  1282. <!-- 无权限 -->
  1283. <noAuth v-if="noauth" :data="noAuthData"></noAuth>
  1284. </template>
  1285. <style lang="scss" scoped>
  1286. ::v-deep(.highcharts-axis-title) {
  1287. font-size: 20px;
  1288. }
  1289. .chart-detail{
  1290. .flex{
  1291. display: flex;
  1292. }
  1293. .chart-title{
  1294. padding: 40px 34px 20px 34px;
  1295. font-size: 32px;
  1296. font-weight: bold;
  1297. color: #1F243A;
  1298. letter-spacing: 2px;
  1299. }
  1300. .top-box{
  1301. padding: 20px 34px 40px 34px;
  1302. height: 40px;
  1303. .calendar-box{
  1304. align-items: center;
  1305. img{
  1306. width: 40px;
  1307. height: 40px;
  1308. margin-right: 18px;
  1309. }
  1310. .date{
  1311. width: 128px;
  1312. height: 40px;
  1313. background: #F6F6F6;
  1314. border: 1px solid #E5E5E5;
  1315. border-radius: 4px;
  1316. text-align: center;
  1317. line-height: 44px;
  1318. font-size: 24px;
  1319. color: #1F243A;
  1320. }
  1321. }
  1322. .icon{
  1323. float: right;
  1324. width: 40px;
  1325. height: 40px;
  1326. margin-left: 30px;
  1327. }
  1328. }
  1329. .select-date-box-head{
  1330. padding: 40px 34px;
  1331. justify-content: space-between;
  1332. }
  1333. .select-date-box{
  1334. // height:50vh;
  1335. .left,.right{
  1336. flex: 1;
  1337. }
  1338. }
  1339. .source-box{
  1340. padding: 0 34px;
  1341. justify-content: space-between;
  1342. align-items: center;
  1343. font-size: 28px;
  1344. margin-bottom: 10px;
  1345. width: 100vw;
  1346. margin-top: 50px;
  1347. .season-change-box{
  1348. height: 50px;
  1349. margin-bottom: 10px;
  1350. padding-right: 34px;
  1351. span{
  1352. display: inline-block;
  1353. width: 100px;
  1354. line-height: 50px;
  1355. text-align: center;
  1356. border: 1px solid #ededed;
  1357. float: right;
  1358. border-radius: 4px;
  1359. &:first-child{
  1360. border-left: none;
  1361. }
  1362. &:last-child{
  1363. border-right: none;
  1364. }
  1365. }
  1366. .active{
  1367. color: #fff;
  1368. background-color: #E3B377;
  1369. }
  1370. }
  1371. }
  1372. .date-type-box{
  1373. padding: 0 34px;
  1374. margin-top: 38px;
  1375. display: flex;
  1376. overflow-x: auto;
  1377. border-bottom: 1px solid #F6F6F6;
  1378. .item{
  1379. // flex: 1;
  1380. width: 20vw;
  1381. padding: 0 8px;
  1382. text-align: center;
  1383. font-size: 24px;
  1384. padding-bottom: 14px;
  1385. color: #1F243A;
  1386. }
  1387. .active{
  1388. color: #E3B377;
  1389. border-bottom: 6px solid #E3B377;
  1390. }
  1391. }
  1392. .van-tabs{
  1393. border-bottom: 1px solid #F6F6F6;
  1394. }
  1395. .latest-value-wrap{
  1396. margin-top: 60px;
  1397. padding: 0 34px;
  1398. margin-bottom: 30px;
  1399. .list{
  1400. box-sizing: border-box;
  1401. li{
  1402. display: flex;
  1403. justify-content: space-between;
  1404. align-items: flex-start;
  1405. padding: 30px 0;
  1406. width: 100%;
  1407. box-sizing: border-box;
  1408. border-top: 1px solid #F6F6F6;
  1409. text-align: center;
  1410. font-size:24px;
  1411. box-sizing: border-box;
  1412. // &:nth-child(odd){
  1413. // border-right: 1px solid #F6F6F6;
  1414. // }
  1415. p:nth-child(1){
  1416. width: 150px;
  1417. flex-shrink: 0;
  1418. }
  1419. p:nth-child(3){
  1420. flex-shrink: 0;
  1421. font-size: 28px;
  1422. width: 150px;
  1423. line-height: 1;
  1424. position: relative;
  1425. top: 3px;
  1426. }
  1427. }
  1428. .one{
  1429. flex: 1;
  1430. border-right: none !important;
  1431. }
  1432. }
  1433. }
  1434. .set-limit-box{
  1435. min-height: 450px;
  1436. padding-top: 5px;
  1437. .limit-top{
  1438. padding: 30px 34px;
  1439. background: #FFFFFF;
  1440. box-shadow: 0px 0 12px rgba(206, 206, 206, 0.3);
  1441. justify-content: space-between;
  1442. font-size: 32px;
  1443. }
  1444. .con{
  1445. font-size: 28px;
  1446. padding-left: 34px;
  1447. .top{
  1448. span:first-child{
  1449. margin-left: 196px;
  1450. }
  1451. span:last-child{
  1452. margin-left: 188px;
  1453. }
  1454. }
  1455. input{
  1456. width: 181px;
  1457. height: 50px;
  1458. font-size: 24px;
  1459. background: #F6F6F6;
  1460. border: 1px solid #E5E5E5;
  1461. border-radius: 4px;
  1462. box-sizing: border-box;
  1463. padding: 0 10px;
  1464. }
  1465. }
  1466. }
  1467. .change-page-wrap{
  1468. position: fixed;
  1469. right: 0;
  1470. bottom: 50px;
  1471. width: 50PX;
  1472. height: 115PX;
  1473. background-image: url('../../../assets/hzyb/chart/before-next.png');
  1474. background-size: cover;
  1475. z-index: 10;
  1476. .top,.bot{
  1477. height: 50%;
  1478. }
  1479. }
  1480. }
  1481. </style>