HomePage.vue 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513
  1. <template>
  2. <Layout>
  3. <template #left>
  4. <div class="left">
  5. <div class="left-top">
  6. <template
  7. v-for="(item, index) in leftContent.topCount"
  8. :key="index"
  9. >
  10. <div>
  11. <template
  12. v-for="(it, id) in item"
  13. :key="id"
  14. >
  15. <div>
  16. <Icon
  17. :name="it.icon"
  18. :size="96"
  19. style="margin-right: 20px;"
  20. />
  21. <div>
  22. <p>
  23. <span>{{ it.num }}</span>
  24. {{ it.until }}
  25. </p>
  26. <p>{{ it.name }}</p>
  27. <span class="steps">
  28. {{ it.step }}
  29. </span>
  30. </div>
  31. </div>
  32. </template>
  33. </div>
  34. </template>
  35. </div>
  36. <div class="left-content">
  37. <template
  38. v-for="(item, index) in leftContent.charts"
  39. :key="index"
  40. >
  41. <Box
  42. :width="662"
  43. :height="455"
  44. :name="item.name"
  45. >
  46. <SelectDate
  47. v-if="index === 0"
  48. @date-change="selectDatechange"
  49. />
  50. <Echart :option="item.option" />
  51. </Box>
  52. </template>
  53. </div>
  54. </div>
  55. </template>
  56. <template #right>
  57. <div class="right">
  58. <template
  59. v-for="(itemd, indexd) in rightContent"
  60. :key="indexd"
  61. >
  62. <Box
  63. :width="655"
  64. :height="622"
  65. :name="itemd.name"
  66. >
  67. <div
  68. v-if="indexd === 0"
  69. class="statistics"
  70. >
  71. <div class="statistics-top">
  72. <template
  73. v-for="(item, index) in itemd.content[0]"
  74. :key="index"
  75. >
  76. <div>
  77. <p>{{ item.num }}</p>
  78. <p>{{ item.name }}</p>
  79. </div>
  80. </template>
  81. </div>
  82. <div class="statistics-content">
  83. <template
  84. v-for="(item, ids) in itemd.content[1]"
  85. :key="ids"
  86. >
  87. <div>
  88. <Icon
  89. :name="item.icon"
  90. :size="74"
  91. />
  92. <div>
  93. <p>
  94. {{ item.name }}
  95. </p>
  96. <p>
  97. <span>{{ item.num }}</span>
  98. </p>
  99. </div>
  100. </div>
  101. </template>
  102. </div>
  103. </div>
  104. <div
  105. v-else
  106. class="classes"
  107. >
  108. <div
  109. v-if="indexd === 1"
  110. class="classes-top"
  111. >
  112. <template
  113. v-for="(item, id) in itemd.content[0]"
  114. :key="id"
  115. >
  116. <div>
  117. <Icon
  118. :name="item.icon"
  119. :size="74"
  120. />
  121. <div>
  122. <p>
  123. {{ item.name }}
  124. </p>
  125. <p>
  126. <span>{{ item.num }}</span>
  127. {{ item.util }}
  128. </p>
  129. </div>
  130. </div>
  131. </template>
  132. </div>
  133. <div
  134. v-else-if="indexd === 2"
  135. class="classes-top"
  136. >
  137. <template
  138. v-for="(item, id) in itemd.content[0]"
  139. :key="id"
  140. >
  141. <div>
  142. <n-progress
  143. type="circle"
  144. :percentage="+item.num"
  145. :offset-degree="180"
  146. :color="item.color"
  147. :indicator-text-color="item.color"
  148. />
  149. <div>
  150. <p>
  151. {{ item.name }}
  152. </p>
  153. <p>
  154. <span>{{ item.num }}</span>
  155. {{ item.util }}
  156. </p>
  157. </div>
  158. </div>
  159. </template>
  160. </div>
  161. <div
  162. v-else
  163. class="classes-top"
  164. >
  165. <template
  166. v-for="(item, id) in itemd.content[0]"
  167. :key="id"
  168. >
  169. <div :style="`justify-content:${id !== 0 ? 'flex-start' : 'center'};`">
  170. <Icon
  171. v-if="id === 0"
  172. :name="item.icon"
  173. :size="74"
  174. />
  175. <n-progress
  176. v-else
  177. type="circle"
  178. :percentage="+item.num"
  179. :offset-degree="180"
  180. :color="item.color"
  181. :indicator-text-color="item.color"
  182. />
  183. <div>
  184. <p>
  185. {{ item.name }}
  186. </p>
  187. <p>
  188. <span>{{ item.num }}</span>
  189. {{ item.util }}
  190. </p>
  191. </div>
  192. </div>
  193. </template>
  194. </div>
  195. <div
  196. v-if="indexd === 1"
  197. class="classes-scroll"
  198. >
  199. <div class="classes-scroll-head">
  200. <span>线路名称</span>
  201. <span>
  202. 班次
  203. <i>计划/实际</i>
  204. </span>
  205. </div>
  206. <div class="scrollBox">
  207. <Vue3Marquee
  208. vertical
  209. :duration="5"
  210. clone
  211. style="width: 100%;"
  212. >
  213. <template
  214. v-for="(item, index) in itemd.content[1]"
  215. :key="index"
  216. >
  217. <div class="classes-scroll-content">
  218. <div>
  219. <div>
  220. <span>{{ item.lineName }}<i>({{ item.licensePlate }})</i></span>
  221. <span>始</span>
  222. <span>{{ item.startStation }}</span>
  223. <span>终</span>
  224. <span>{{ item.endStation }}</span>
  225. </div>
  226. <div>{{ item.planClasses }}/{{ item.realityClasses }}</div>
  227. </div>
  228. <div>
  229. <n-progress
  230. type="line"
  231. :percentage="item.realityClasses / item.planClasses * 100"
  232. :show-indicator="false"
  233. processing
  234. />
  235. </div>
  236. </div>
  237. </template>
  238. </Vue3Marquee>
  239. </div>
  240. </div>
  241. <div
  242. v-else-if="indexd === 3"
  243. class="classes-scroll"
  244. >
  245. <div class="classes-scroll-head">
  246. <span>线路名称</span>
  247. <span>
  248. 发车
  249. <i>已发数/准点数</i>
  250. </span>
  251. </div>
  252. <div class="scrollBox">
  253. <Vue3Marquee
  254. vertical
  255. :duration="5"
  256. clone
  257. style="width: 100%;"
  258. >
  259. <template
  260. v-for="(item, index) in itemd.content[1]"
  261. :key="index"
  262. >
  263. <div class="classes-scroll-content">
  264. <div>
  265. <div>
  266. <span>{{ item.lineName }}<i>({{ item.licensePlate }})</i></span>
  267. <span>始</span>
  268. <span>{{ item.startStation }}</span>
  269. <span>终</span>
  270. <span>{{ item.endStation }}</span>
  271. </div>
  272. <div>{{ item.total }}/{{ item.onTime }}</div>
  273. </div>
  274. <div>
  275. <n-progress
  276. type="line"
  277. :percentage="item.onTime / item.total * 100"
  278. :show-indicator="false"
  279. class="resetStyle"
  280. processing
  281. />
  282. </div>
  283. </div>
  284. </template>
  285. </Vue3Marquee>
  286. </div>
  287. </div>
  288. <template v-else>
  289. <div
  290. v-for="(item, index) in itemd.content[1]"
  291. :key="index"
  292. class="classes-tables"
  293. >
  294. <div class="classes-tables-head">
  295. <span
  296. v-for="(itm, id) in item.head"
  297. :key="id"
  298. >{{ itm }}</span>
  299. </div>
  300. <div class="classes-tables-body">
  301. <div
  302. v-for="(it, ids) in item.body"
  303. :key="ids"
  304. :class="'status'+it.status"
  305. >
  306. <span>{{ it.lineName }}({{ it.licensePlate }})</span>
  307. <span>{{ it.direction===1?'上行':'下行' }}</span>
  308. <span>{{ it.planTime }}</span>
  309. <span>{{ it.realityTime }}</span>
  310. <span :class="'status'+it.status">{{ statusMap[it.status] }}</span>
  311. </div>
  312. </div>
  313. </div>
  314. </template>
  315. </div>
  316. </Box>
  317. </template>
  318. </div>
  319. </template>
  320. </Layout>
  321. </template>
  322. <script setup lang='ts'>
  323. import Layout from '@/components/layout.vue'
  324. import { onMounted, onUnmounted, ref } from 'vue'
  325. import Box from '@/components/box.vue'
  326. import SelectDate from '@/components/selectDate.vue'
  327. import { Vue3Marquee } from 'vue3-marquee'
  328. import { graphic } from 'echarts'
  329. import Echart from '@/components/chart.vue'
  330. import HomeService from '../services/homepage.service'
  331. const homeService = new HomeService()
  332. const statusMap:any = {
  333. 0: '正常',
  334. 1: '超出',
  335. 2: '异常'
  336. }
  337. const datas = [
  338. {
  339. value: 99,
  340. name: '系列一'
  341. },
  342. {
  343. value: 97,
  344. name: '系列二'
  345. },
  346. {
  347. value: 94,
  348. name: '系列三'
  349. },
  350. {
  351. value: 90,
  352. name: '系列四'
  353. },
  354. {
  355. value: 88,
  356. name: '系列五'
  357. },
  358. {
  359. value: 84,
  360. name: '系列6'
  361. },
  362. {
  363. value: 81,
  364. name: '系列7'
  365. },
  366. {
  367. value: 79,
  368. name: '系列8'
  369. },
  370. {
  371. value: 75,
  372. name: '系列9'
  373. },
  374. {
  375. value: 65.88,
  376. name: '系列a'
  377. }
  378. ]
  379. const leftContent:any = ref({
  380. topCount: [
  381. [
  382. {
  383. num: 6.8, icon: '11', name: '总刷卡量', until: '亿人次', step: '+5'
  384. },
  385. {
  386. num: 6.8, icon: '12', name: '总客流量', until: '亿人次', step: '-9'
  387. },
  388. {
  389. num: 6.8, icon: '13', name: '安全行驶', until: '万公里', step: '+15'
  390. }
  391. ],
  392. [
  393. {
  394. num: 6.8, icon: '14', name: '累计减少碳排放', until: '吨', step: '+5'
  395. },
  396. {
  397. num: 6.8, icon: '15', name: '累计责任事故率', until: '起/百万公里', step: '+5'
  398. },
  399. {
  400. num: 6.8, icon: '16', name: '乘客满意度', until: '%'
  401. }
  402. ]
  403. ],
  404. charts: [
  405. {
  406. name: '客运量',
  407. option: {
  408. grid: {
  409. containLabel: true,
  410. top: 100,
  411. bottom: 50,
  412. left: '5%',
  413. right: '5%'
  414. },
  415. tooltip: {
  416. trigger: 'axis',
  417. formatter: '{b}<br /> {a0}:{c0}万 <br />{a1}:{c1}%',
  418. axisPointer: {
  419. type: 'shadow'
  420. }
  421. },
  422. legend: {
  423. top: 45,
  424. lett: 'center',
  425. itemGap: 30,
  426. textStyle: {
  427. fontSize: 18,
  428. color: '#C9D2FA'
  429. }
  430. },
  431. xAxis: {
  432. triggerEvent: true,
  433. data: [
  434. '1月',
  435. '2月',
  436. '3月',
  437. '4月',
  438. '5月',
  439. '6月'
  440. ],
  441. axisLabel: {
  442. interval: 0,
  443. fontSize: 18,
  444. color: '#C9D2FA'
  445. }
  446. },
  447. yAxis: [
  448. {
  449. name: '客运量/万',
  450. nameLocation: 'center',
  451. nameGap: 50,
  452. nameTextStyle: {
  453. color: '#fff',
  454. fontSize: 18
  455. },
  456. splitNumber: 2,
  457. axisLabel: {
  458. fontSize: 18,
  459. color: '#C9D2FA'
  460. },
  461. splitLine: {
  462. lineStyle: {
  463. type: 'dashed',
  464. color: '#3E4A82'
  465. }
  466. }
  467. },
  468. {
  469. name: '增长率%',
  470. nameLocation: 'center',
  471. nameGap: 50,
  472. nameTextStyle: {
  473. color: '#fff',
  474. fontSize: 18
  475. },
  476. splitNumber: 2,
  477. position: 'right', // 放在右边
  478. axisLabel: {
  479. fontSize: 18,
  480. color: '#C9D2FA',
  481. formatter(value: number) {
  482. // 在标签后面添加百分号
  483. return `${value}%`
  484. }
  485. },
  486. splitLine: {
  487. lineStyle: {
  488. type: 'dashed',
  489. color: '#3E4A82'
  490. }
  491. }
  492. }
  493. ],
  494. series: [
  495. {
  496. name: '客运量',
  497. type: 'bar',
  498. silent: true,
  499. itemStyle: {
  500. color: new graphic.LinearGradient(0, 0, 0, 1, [
  501. {
  502. offset: 0,
  503. color: '#FFD35D'
  504. },
  505. {
  506. offset: 0.5,
  507. color: '#7ec2f3'
  508. },
  509. {
  510. offset: 1,
  511. color: '#1890ff'
  512. }
  513. ])
  514. },
  515. barWidth: 30,
  516. data: [ 200, 108, 200, 40, 210, 100 ]
  517. },
  518. {
  519. name: '同比增长率',
  520. type: 'line',
  521. yAxisIndex: 1, // 与第二个 y 轴关联
  522. itemStyle: {
  523. color: '#FEB74D' // 设置折线颜色为黄色
  524. },
  525. data: [ 100, 80, 120, 60, 90, 70 ] // 折线图的数据
  526. }
  527. ],
  528. dataZoom: {
  529. type: 'slider',
  530. height: 20,
  531. bottom: 8,
  532. handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
  533. handleSize: '100%',
  534. handleStyle: {
  535. color: '#409eff'
  536. },
  537. textStyle: {
  538. color: '#666'
  539. },
  540. fillerColor: 'rgba(255,255,255,0.2)',
  541. borderColor: 'rgba(64,158,225,0.3)'
  542. }
  543. },
  544. btns: [ '总', '年', '月', '日' ],
  545. checkId: 0
  546. },
  547. {
  548. name: '消费占比',
  549. option: {
  550. title: {
  551. text: '消费人次/万人', // 图标内容文本
  552. subtext: '10',
  553. left: 'center', // 图标内容水平居中
  554. top: 'center', // 图标内容垂直居中
  555. // 文本样式
  556. textStyle: {
  557. color: '#fff', // 图标内容文字颜色
  558. fontSize: '18px', // 图标内容文字大小
  559. fontWeight: 'normal'
  560. },
  561. subtextStyle: {
  562. color: '#fff', // 图标内容文字颜色
  563. fontSize: '24px', // 图标内容文字大小
  564. fontWeight: 'normal'
  565. }
  566. },
  567. grid: {
  568. icon: '',
  569. containLabel: true
  570. },
  571. tooltip: {
  572. show: true,
  573. formatter: '{b}: {c} ,占比: {d}%'
  574. },
  575. // 图表图例
  576. legend: {
  577. type: 'scroll',
  578. orient: 'horizontal', // 图例排列方向
  579. icon: 'circle', // 图例样式为圆形
  580. itemWidth: 10, // 图例图形的宽度
  581. itemHeight: 16, // 图例图形的高度
  582. itemGap: 10, // 图例项之间的间隔
  583. left: 'center', // 图例距离容器右侧的距离
  584. bottom: 0, // 图例垂直居中
  585. textStyle: {
  586. color: 'white', // 图例文字颜色
  587. fontSize: 20
  588. }
  589. },
  590. yAxis: [ ],
  591. series: [
  592. {
  593. type: 'pie', // 图表类型为饼图
  594. radius: [ '45%', '60%' ], // 控制内外圆环的半径,30%代表内圆,60%代表外圆
  595. avoidLabelOverlap: true, // 是否启用防止标签重叠策略
  596. showEmptyCircle: true, // 是否在无数据的时候显示一个占位圆
  597. label: {
  598. formatter: '{b}\n{d}%',
  599. color: 'white'
  600. },
  601. data: [
  602. { name: '0次', value: 30619 },
  603. { name: '1次', value: 5921 },
  604. { name: '2次', value: 1153 },
  605. { name: '3次', value: 266 },
  606. { name: '大于3次', value: 87 },
  607. { name: '大于4次', value: 999 },
  608. { name: '12次', value: 5921 },
  609. { name: '22次', value: 1153 },
  610. { name: '32次', value: 266 },
  611. { name: '大于32次', value: 87 },
  612. { name: '大于42次', value: 999 }
  613. ]
  614. }
  615. ]
  616. }
  617. },
  618. {
  619. name: '客流排名',
  620. option: {
  621. tooltip: {
  622. trigger: 'axis',
  623. axisPointer: {
  624. type: 'shadow'
  625. }
  626. },
  627. grid: {
  628. top: 20,
  629. left: 20,
  630. bottom: 20,
  631. right: '10%',
  632. containLabel: true
  633. },
  634. xAxis: {
  635. type: 'value',
  636. splitLine: {
  637. lineStyle: {
  638. color: '#fff'
  639. }
  640. },
  641. axisLabel: {
  642. color: '#fff',
  643. fontSize: 18
  644. }
  645. },
  646. yAxis: [ {
  647. type: 'category',
  648. inverse: true,
  649. axisTick: {
  650. alignWithLabel: true,
  651. lineStyle: {
  652. color: '#fff'
  653. }
  654. },
  655. axisLine: {
  656. lineStyle: {
  657. color: '#fff'
  658. }
  659. },
  660. data: [ '雨城区', '雨城区', '雨城区', '雨城区', '雨城区', '雨城区', '雨城区', '雨城区' ],
  661. axisLabel: {
  662. fontSize: 18,
  663. color: '#fff',
  664. margin: 10,
  665. rich: {
  666. a1: {
  667. backgroundColor: '#FFFF0050',
  668. width: 30,
  669. height: 28,
  670. align: 'center',
  671. borderRadius: 15,
  672. fontSize: 18,
  673. borderColor: '#FFFF00',
  674. borderType: 'solid',
  675. borderWidth: 1,
  676. padding: [ 2, 0, 0, 0 ]
  677. },
  678. a2: {
  679. backgroundColor: '#F2F2F250',
  680. width: 30,
  681. height: 28,
  682. align: 'center',
  683. borderRadius: 15,
  684. fontSize: 18,
  685. borderColor: '#F2F2F2',
  686. borderType: 'solid',
  687. borderWidth: 1,
  688. padding: [ 2, 0, 0, 0 ]
  689. },
  690. a3: {
  691. backgroundColor: '#FAAD0E50',
  692. width: 30,
  693. height: 28,
  694. align: 'center',
  695. borderRadius: 15,
  696. fontSize: 18,
  697. borderColor: '#FAAD0E',
  698. borderType: 'solid',
  699. borderWidth: 1,
  700. padding: [ 2, 0, 0, 0 ]
  701. },
  702. b: {
  703. backgroundColor: '#00FFFF50',
  704. width: 30,
  705. height: 28,
  706. align: 'center',
  707. borderRadius: 15,
  708. fontSize: 18,
  709. borderColor: '#00FFFF',
  710. borderType: 'solid',
  711. borderWidth: 1,
  712. padding: [ 2, 0, 0, 0 ]
  713. },
  714. c: {
  715. width: 100,
  716. fontSize: 18
  717. }
  718. },
  719. formatter: (params: string, _id: number) => {
  720. const id = _id + 1
  721. if (_id < 3) {
  722. return `{a${id}|${id}} {c|${params}}`
  723. }
  724. return `{b|${id}} {c|${params}}`
  725. }
  726. }
  727. } ],
  728. series: [
  729. {
  730. z: 2,
  731. name: '客流排名',
  732. type: 'bar',
  733. barWidth: 25,
  734. zlevel: 1,
  735. data: datas.map((item, i) => ({
  736. value: item.value
  737. })),
  738. label: {
  739. position: 'inside',
  740. color: '#fff',
  741. fontSize: 16
  742. },
  743. itemStyle: {
  744. borderRadius: [ 0, 15, 15, 0 ],
  745. color: new graphic.LinearGradient(0, 0, 1, 0, [
  746. {
  747. offset: 0,
  748. color: 'rgba(20,167,250,0.3)'
  749. },
  750. {
  751. offset: 1,
  752. color: 'rgba(0,255,209,1)'
  753. }
  754. ])
  755. }
  756. }
  757. ]
  758. }
  759. },
  760. {
  761. name: '线路客流排名',
  762. option: {
  763. tooltip: {
  764. trigger: 'axis',
  765. axisPointer: {
  766. type: 'shadow'
  767. }
  768. },
  769. grid: {
  770. top: 20,
  771. left: 20,
  772. bottom: 20,
  773. right: '10%',
  774. containLabel: true
  775. },
  776. xAxis: {
  777. type: 'value',
  778. splitLine: {
  779. lineStyle: {
  780. color: '#fff'
  781. }
  782. },
  783. axisLabel: {
  784. color: '#fff',
  785. fontSize: 18
  786. }
  787. },
  788. yAxis: [ {
  789. type: 'category',
  790. inverse: true,
  791. axisTick: {
  792. alignWithLabel: true,
  793. lineStyle: {
  794. color: '#fff'
  795. }
  796. },
  797. axisLine: {
  798. lineStyle: {
  799. color: '#fff'
  800. }
  801. },
  802. data: [ '1路(45689)', '2路(45689)', '3路(45689)', '4路(45689)', '5路(45689)', '6路(45689)', '7路(45689)', '8路(45689)' ],
  803. axisLabel: {
  804. fontSize: 18,
  805. color: '#fff',
  806. margin: 20,
  807. rich: {
  808. a1: {
  809. backgroundColor: '#FFFF0050',
  810. width: 30,
  811. height: 28,
  812. align: 'center',
  813. borderRadius: 15,
  814. fontSize: 18,
  815. borderColor: '#FFFF00',
  816. borderType: 'solid',
  817. borderWidth: 1,
  818. padding: [ 2, 0, 0, 0 ]
  819. },
  820. a2: {
  821. backgroundColor: '#F2F2F250',
  822. width: 30,
  823. height: 28,
  824. align: 'center',
  825. borderRadius: 15,
  826. fontSize: 18,
  827. borderColor: '#F2F2F2',
  828. borderType: 'solid',
  829. borderWidth: 1,
  830. padding: [ 2, 0, 0, 0 ]
  831. },
  832. a3: {
  833. backgroundColor: '#FAAD0E50',
  834. width: 30,
  835. height: 28,
  836. align: 'center',
  837. borderRadius: 15,
  838. fontSize: 18,
  839. borderColor: '#FAAD0E',
  840. borderType: 'solid',
  841. borderWidth: 1,
  842. padding: [ 2, 0, 0, 0 ]
  843. },
  844. b: {
  845. backgroundColor: '#00FFFF50',
  846. width: 30,
  847. height: 28,
  848. align: 'center',
  849. borderRadius: 15,
  850. fontSize: 18,
  851. borderColor: '#00FFFF',
  852. borderType: 'solid',
  853. borderWidth: 1,
  854. padding: [ 2, 0, 0, 0 ]
  855. },
  856. c: {
  857. width: 35,
  858. fontSize: 18
  859. }
  860. },
  861. formatter: (params: string, _id: number) => {
  862. const id = _id + 1
  863. if (_id < 3) {
  864. return `{a${id}|${id}} {c|${params}}`
  865. }
  866. return `{b|${id}} {c|${params}}`
  867. }
  868. }
  869. } ],
  870. series: [
  871. {
  872. z: 2,
  873. name: '线路客流排名',
  874. type: 'bar',
  875. barWidth: 25,
  876. zlevel: 1,
  877. data: datas.map((item, i) => ({
  878. value: item.value
  879. })),
  880. label: {
  881. position: 'inside',
  882. color: '#fff',
  883. fontSize: 16
  884. },
  885. itemStyle: {
  886. borderRadius: [ 0, 15, 15, 0 ],
  887. color: new graphic.LinearGradient(0, 0, 1, 0, [
  888. {
  889. offset: 0,
  890. color: 'rgba(20,167,250,0.3)'
  891. },
  892. {
  893. offset: 1,
  894. color: 'rgba(246,189,22,1)'
  895. }
  896. ])
  897. }
  898. }
  899. ]
  900. }
  901. }
  902. ]
  903. })
  904. const rightContent = ref([
  905. {
  906. name: '舆情统计',
  907. content: [
  908. [
  909. { name: '报警总数', num: 0 },
  910. { name: '报警企业', num: 0 },
  911. { name: '待反馈', num: 0 },
  912. { name: '待解除', num: 0 }
  913. ],
  914. [
  915. { name: '事件', num: 0, icon: '17' },
  916. { name: '舆情', num: 0, icon: '18' },
  917. { name: '高风险', num: 0, icon: '19' },
  918. { name: '汛情', num: 0, icon: '20' },
  919. { name: '网络检查', num: 0, icon: '21' },
  920. { name: '火险', num: 0, icon: '22' },
  921. { name: '驾驶员报警', num: 0, icon: '23' },
  922. { name: '充电', num: 0, icon: '24' },
  923. { name: '电池高温', num: 0, icon: '25' }
  924. ]
  925. ]
  926. },
  927. {
  928. name: '班次完成率',
  929. content: [
  930. [
  931. {
  932. name: '昨日总班次', num: 0, icon: '26', util: '次'
  933. },
  934. {
  935. name: '昨日完成率', num: 0, icon: '27', util: '%'
  936. }
  937. ],
  938. [
  939. {
  940. lineName: '1路', licensePlate: '45567', startStation: '始发站牌名称', endStation: '始发站牌名称', planClasses: 100, realityClasses: 70
  941. },
  942. {
  943. lineName: '2路', licensePlate: '45567', startStation: '始发站牌名称', endStation: '始发站牌名称', planClasses: 100, realityClasses: 70
  944. },
  945. {
  946. lineName: '3路', licensePlate: '45567', startStation: '始发站牌名称', endStation: '始发站牌名称', planClasses: 100, realityClasses: 70
  947. },
  948. {
  949. lineName: '4路', licensePlate: '45567', startStation: '始发站牌名称', endStation: '始发站牌名称', planClasses: 100, realityClasses: 70
  950. },
  951. {
  952. lineName: '5路', licensePlate: '45567', startStation: '始发站牌名称', endStation: '始发站牌名称', planClasses: 100, realityClasses: 70
  953. }
  954. ]
  955. ]
  956. },
  957. {
  958. name: '班次准点率',
  959. content: [
  960. [
  961. {
  962. name: '昨日首末班准点率', num: 70, icon: '26', util: '%', color: '#00FFFF'
  963. },
  964. {
  965. name: '昨日中途站准点率', num: 70, icon: '29', util: '%', color: '#FAAD0E'
  966. }
  967. ],
  968. [
  969. {
  970. head: [ '今日首班', '方向', '计划', '实际', '状态' ],
  971. body: [
  972. {
  973. direction: 1,
  974. planTime: 5,
  975. realityTime: 5,
  976. status: 0,
  977. type: 0,
  978. licensePlate: 'fsdfdsf',
  979. lineName: '12路'
  980. }
  981. ]
  982. },
  983. {
  984. head: [ '昨日末班', '方向', '计划', '实际', '状态' ],
  985. body: [
  986. {
  987. direction: 1,
  988. planTime: 5,
  989. realityTime: 5,
  990. status: 0,
  991. type: 0,
  992. licensePlate: 'fsdfdsf',
  993. lineName: '12路'
  994. }
  995. ]
  996. }
  997. ]
  998. ]
  999. },
  1000. {
  1001. name: '发车准点率',
  1002. content: [
  1003. [
  1004. {
  1005. name: '昨日准点班次数', num: 70, icon: '26', util: '次', color: '#04FF77'
  1006. },
  1007. {
  1008. name: '昨日准点率', num: 70, icon: '29', util: '%', color: '#04FF77'
  1009. }
  1010. ],
  1011. [
  1012. {
  1013. lineName: '1路', licensePlate: '(45567)', startStation: '始发站牌名称', endStation: '始发站牌名称', total: 100, onTime: 70
  1014. },
  1015. {
  1016. lineName: '1路', licensePlate: '(45567)', startStation: '始发站牌名称', endStation: '始发站牌名称', total: 100, onTime: 70
  1017. },
  1018. {
  1019. lineName: '1路', licensePlate: '(45567)', startStation: '始发站牌名称', endStation: '始发站牌名称', total: 100, onTime: 70
  1020. },
  1021. {
  1022. lineName: '1路', licensePlate: '(45567)', startStation: '始发站牌名称', endStation: '始发站牌名称', total: 100, onTime: 70
  1023. },
  1024. {
  1025. lineName: '1路', licensePlate: '(45567)', startStation: '始发站牌名称', endStation: '始发站牌名称', total: 100, onTime: 70
  1026. }
  1027. ]
  1028. ]
  1029. }
  1030. ] as Any)
  1031. async function getBusSummaryInfo() {
  1032. const res = await homeService.getBusSummaryInfo()
  1033. leftContent.value.topCount[0][0].num = res.swipeCount
  1034. leftContent.value.topCount[0][1].num = res.passengerFlow
  1035. leftContent.value.topCount[0][2].num = res.mileageDriven
  1036. leftContent.value.topCount[1][0].num = res.carbonEmission
  1037. leftContent.value.topCount[1][1].num = res.accidentRate
  1038. leftContent.value.topCount[1][2].num = res.satisfaction
  1039. }
  1040. getBusSummaryInfo()
  1041. async function getBusTrafficVolume(type: number) {
  1042. const res = await homeService.getBusTrafficVolume(type)
  1043. const xData = res.map((item: any) => item.time)
  1044. const trafficVolumeData = res.map((item: any) => item.trafficVolume)
  1045. const growthRateData = res.map((item: any) => item.growthRate)
  1046. leftContent.value.charts[0].option.xAxis!.data = xData
  1047. leftContent.value.charts[0].option.series[0].data = trafficVolumeData
  1048. leftContent.value.charts[0].option.series[1].data = growthRateData
  1049. }
  1050. async function getBusStatistics() {
  1051. const { consumptionProportion = [], flowRanking = [], lineFlowRanking = [] } = await homeService.getBusStatistics()
  1052. leftContent.value.charts[1].option.series[0].data = consumptionProportion
  1053. leftContent.value.charts[1].option.title.subtext = consumptionProportion.reduce((prev: any, item:any) => {
  1054. prev += +item.value
  1055. return prev
  1056. }, 0).toFixed(2)
  1057. leftContent.value.charts[2].option.yAxis[0].data = flowRanking.map((item: any) => item.name)
  1058. leftContent.value.charts[2].option.series[0].data = flowRanking
  1059. leftContent.value.charts[3].option.yAxis[0].data = lineFlowRanking.map((item: any) => item.name)
  1060. leftContent.value.charts[3].option.series[0].data = lineFlowRanking
  1061. }
  1062. async function getBusWarning() {
  1063. const {
  1064. warningNum, companyNum, feedbackNum, secureNum, warningSummary
  1065. } = await homeService.getBusWarning()
  1066. rightContent.value[0].content[0][0].num = warningNum
  1067. rightContent.value[0].content[0][1].num = companyNum
  1068. rightContent.value[0].content[0][2].num = feedbackNum
  1069. rightContent.value[0].content[0][3].num = secureNum
  1070. rightContent.value[0].content[1][0].num = warningSummary.accident
  1071. rightContent.value[0].content[1][1].num = warningSummary.sentiment
  1072. rightContent.value[0].content[1][2].num = warningSummary.risk
  1073. rightContent.value[0].content[1][3].num = warningSummary.flood
  1074. rightContent.value[0].content[1][4].num = warningSummary.network
  1075. rightContent.value[0].content[1][5].num = warningSummary.fire
  1076. rightContent.value[0].content[1][6].num = warningSummary.warning
  1077. rightContent.value[0].content[1][7].num = warningSummary.charge
  1078. rightContent.value[0].content[1][8].num = warningSummary.battery
  1079. }
  1080. getBusWarning()
  1081. async function getClassesCompletionRate() {
  1082. const data = await homeService.getClassesCompletionRate()
  1083. const allPlan = data.reduce((prev: any, item: { planClasses: number }) => {
  1084. prev += item.planClasses
  1085. return prev
  1086. }, 0)
  1087. const allReal = data.reduce((prev: any, item: { realityClasses: number }) => {
  1088. prev += item.realityClasses
  1089. return prev
  1090. }, 0)
  1091. rightContent.value[1].content[0][0].num = allPlan
  1092. rightContent.value[1].content[0][1].num = (allReal / allPlan * 100).toFixed(0)
  1093. rightContent.value[1].content[1] = data
  1094. }
  1095. getClassesCompletionRate()
  1096. async function getDepartPunctualityRateList() {
  1097. const data = await homeService.getDepartPunctualityRateList()
  1098. const allTotal = data.reduce((prev: any, item: { total: number }) => {
  1099. prev += item.total
  1100. return prev
  1101. }, 0)
  1102. const allOnTime = data.reduce((prev: any, item: { onTime: number }) => {
  1103. prev += item.onTime
  1104. return prev
  1105. }, 0)
  1106. rightContent.value[3].content[0][0].num = allOnTime
  1107. rightContent.value[3].content[0][1].num = (allOnTime / allTotal * 100).toFixed(0)
  1108. rightContent.value[3].content[1] = data
  1109. }
  1110. getDepartPunctualityRateList()
  1111. async function getClassesPunctualityRate() {
  1112. const data = await homeService.getClassesPunctualityRate()
  1113. const todayData = data.filter((item: { type: number }) => item.type === 1)
  1114. const yesterdayData = data.filter((item: { type: number }) => item.type === 0)
  1115. rightContent.value[2].content[1][0].body = todayData
  1116. rightContent.value[2].content[1][1].body = yesterdayData
  1117. }
  1118. getClassesPunctualityRate()
  1119. const curentType = ref(1)
  1120. function selectDatechange(type: any) {
  1121. curentType.value = type
  1122. getBusTrafficVolume(type)
  1123. }
  1124. onMounted(() => {
  1125. selectDatechange(curentType.value)
  1126. getBusStatistics()
  1127. })
  1128. const timer = setInterval(() => {
  1129. selectDatechange(curentType.value)
  1130. getBusSummaryInfo()
  1131. getBusStatistics()
  1132. }, 5 * 1000)
  1133. onUnmounted(() => {
  1134. clearInterval(timer)
  1135. })
  1136. </script>
  1137. <style lang="scss" scoped>
  1138. .left {
  1139. &-top {
  1140. &>div {
  1141. display: flex;
  1142. }
  1143. &>div:first-child,
  1144. &>div:nth-child(2) {
  1145. background: linear-gradient(180deg, rgba(33, 133, 232, 0.0902) 0%, rgba(0, 170, 255, 0) 99%);
  1146. &>div {
  1147. display: flex;
  1148. color: white;
  1149. text-shadow: 1px 1px 5px rgba(0, 255, 255, 0.647058823529412);
  1150. width: 440px;
  1151. height: 160px;
  1152. align-items: center;
  1153. justify-content: center;
  1154. font-family: "Impact Normal", Impact, sans-serif;
  1155. &>div {
  1156. position: relative;
  1157. &>p:first-child {
  1158. font-size: 18px;
  1159. span {
  1160. font-size: 50px;
  1161. }
  1162. }
  1163. &>p:last-child {
  1164. font-size: 24px;
  1165. }
  1166. .steps {
  1167. position: absolute;
  1168. top: -60px;
  1169. left: 15px;
  1170. font-size: 50px;
  1171. animation: step 0.5s infinite alternate;
  1172. }
  1173. }
  1174. }
  1175. }
  1176. }
  1177. &-content {
  1178. display: flex;
  1179. justify-content: space-between;
  1180. flex-wrap: wrap;
  1181. :deep(.box-content) {
  1182. position: relative;
  1183. }
  1184. }
  1185. }
  1186. .right {
  1187. display: flex;
  1188. justify-content: space-between;
  1189. flex-wrap: wrap;
  1190. white-space: nowrap;
  1191. text-shadow: 1px 1px 5px rgba(0, 255, 255, 0.647058823529412);
  1192. .statistics {
  1193. padding: 20px;
  1194. &-top {
  1195. display: flex;
  1196. justify-content: space-between;
  1197. &>div {
  1198. width: 145px;
  1199. height: 145px;
  1200. font-size: 20px;
  1201. color: #80FFFF;
  1202. text-align: center;
  1203. background: rgba(33, 133, 232, 0.098);
  1204. border: 1px solid rgba(0, 255, 255, 0.298);
  1205. &>p:first-child {
  1206. padding-top: 33px;
  1207. padding-bottom: 10px;
  1208. font-weight: 700;
  1209. font-style: normal;
  1210. font-size: 40px;
  1211. color: #FFFFFF;
  1212. text-align: center;
  1213. }
  1214. &>p:last-child {
  1215. font-size: 20px;
  1216. }
  1217. }
  1218. }
  1219. &-content {
  1220. display: flex;
  1221. flex-wrap: wrap;
  1222. justify-content: space-between;
  1223. &>div {
  1224. display: flex;
  1225. width: 205px;
  1226. overflow: hidden;
  1227. margin-top: 40px;
  1228. &>div {
  1229. width: 100px;
  1230. color: white;
  1231. padding-left: 20px;
  1232. &>p {
  1233. font-size: 20px;
  1234. span {
  1235. font-size: 34px;
  1236. font-weight: 400;
  1237. }
  1238. }
  1239. }
  1240. }
  1241. }
  1242. }
  1243. .classes {
  1244. padding: 20px;
  1245. &-top {
  1246. display: flex;
  1247. justify-content: space-between;
  1248. &>div {
  1249. width: 300px;
  1250. height: 150px;
  1251. font-size: 20px;
  1252. color: #80FFFF;
  1253. text-align: center;
  1254. background: rgba(33, 133, 232, 0.098);
  1255. border: 1px solid rgba(0, 255, 255, 0.298);
  1256. display: flex;
  1257. align-items: center;
  1258. justify-content: center;
  1259. &>div {
  1260. // padding-left: 20px;
  1261. margin-left: 10px;
  1262. text-align: left;
  1263. &:first-child {
  1264. width: 99px;
  1265. height: 99px;
  1266. }
  1267. &>p:last-child {
  1268. color: white;
  1269. font-family: 'Impact Normal', 'Impact', sans-serif;
  1270. &>span {
  1271. font-size: 34px;
  1272. font-weight: 400;
  1273. margin-right: 5px;
  1274. }
  1275. }
  1276. }
  1277. }
  1278. }
  1279. &-scroll {
  1280. &-head {
  1281. margin-top: 40px;
  1282. margin-bottom: 20px;
  1283. display: flex;
  1284. justify-content: space-between;
  1285. font-size: 20px;
  1286. color: #80FFFF;
  1287. &>span:last-child {
  1288. color: white;
  1289. i {
  1290. color: #80FFFF;
  1291. }
  1292. }
  1293. }
  1294. &-content {
  1295. font-family: Arial Normal;
  1296. width: 100%;
  1297. &>div:first-child {
  1298. display: flex;
  1299. justify-content: space-between;
  1300. align-items: center;
  1301. &>div:first-child {
  1302. display: flex;
  1303. font-size: 18px;
  1304. align-items: flex-end;
  1305. text-shadow: none;
  1306. &>span {
  1307. &:first-child {
  1308. width: 185px;
  1309. height: 48px;
  1310. border-radius: 5px;
  1311. background: rgba(33, 133, 232, 0.298);
  1312. border: 3px solid #80FFFF;
  1313. font-size: 24px;
  1314. color: white;
  1315. text-align: center;
  1316. line-height: 40px;
  1317. overflow: hidden;
  1318. margin-right: 10px;
  1319. i {
  1320. font-size: 20px;
  1321. margin-left: 10px;
  1322. }
  1323. }
  1324. &:nth-child(4),
  1325. &:nth-child(2) {
  1326. width: 30px;
  1327. height: 30px;
  1328. color: #FFFFFF;
  1329. border-radius: 50%;
  1330. border: 1px solid #2bb972;
  1331. background: #2BB97249;
  1332. text-align: center;
  1333. line-height: 30px;
  1334. margin: 0 5px;
  1335. }
  1336. &:nth-child(4) {
  1337. border: 1px solid #E73D41;
  1338. background: #E73D4149;
  1339. margin-left: 10px;
  1340. }
  1341. &:nth-child(3),
  1342. &:last-child {
  1343. color: #81D3F8;
  1344. padding-bottom: 5px;
  1345. }
  1346. }
  1347. }
  1348. &>div:last-child {
  1349. font-size: 24px;
  1350. text-align: right;
  1351. color: #FFFFFF;
  1352. }
  1353. }
  1354. &>div:last-child {
  1355. height: 40px;
  1356. padding: 13px 0;
  1357. :deep(.resetStyle .n-progress-graph-line-fill) {
  1358. background: linear-gradient(to right, #945fb9, rgba(0, 212, 255, 1)) !important;
  1359. }
  1360. }
  1361. }
  1362. }
  1363. .scrollBox {
  1364. height: 270px;
  1365. overflow: hidden;
  1366. }
  1367. &-tables {
  1368. text-shadow: none;
  1369. font-family: 'Arial Normal', 'Arial', sans-serif;
  1370. font-weight: 400;
  1371. text-align: center;
  1372. font-size: 20px;
  1373. color: #FFFFFF;
  1374. .status1{
  1375. color: #FAAD0E;
  1376. }
  1377. .status2{
  1378. color: #E73D41;
  1379. }
  1380. // &:nth-child(3)>div:nth-child(2)>div:last-child,
  1381. // &:nth-child(2)>div:nth-child(2)>div:first-child {
  1382. // color: #E73D41;
  1383. // }
  1384. &:nth-child(3)>div:first-child>span:first-child {
  1385. color: #FAAD0E;
  1386. }
  1387. &-head {
  1388. display: flex;
  1389. align-items: center;
  1390. background: rgba(86, 137, 240, 0.298);
  1391. height: 40px;
  1392. margin-top: 20px;
  1393. &>span {
  1394. width: 112px;
  1395. font-weight: 700;
  1396. &:first-child {
  1397. width: 182px;
  1398. color: #04FF77;
  1399. }
  1400. }
  1401. }
  1402. &-body {
  1403. color: #81D3F8;
  1404. &>div {
  1405. display: flex;
  1406. align-items: center;
  1407. height: 50px;
  1408. &>span {
  1409. width: 112px;
  1410. &:first-child {
  1411. width: 182px;
  1412. }
  1413. }
  1414. }
  1415. }
  1416. }
  1417. }
  1418. }
  1419. @keyframes step {
  1420. 0% {
  1421. opacity: 0;
  1422. }
  1423. 100% {
  1424. opacity: 1;
  1425. }
  1426. }
  1427. </style>