rhkb.vue 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135
  1. <template>
  2. <div class="page-container">
  3. <el-row :gutter="16" class="summary">
  4. <!-- 原有的统计卡片部分保持不变 -->
  5. <el-col :sm="3" :xs="12">
  6. <SummaryCard
  7. :value="device.total || 0"
  8. icon="fa-solid:project-diagram"
  9. icon-bg-color="text-blue-500"
  10. icon-color="bg-blue-100"
  11. title="设备数"
  12. />
  13. </el-col>
  14. <el-col :sm="3" :xs="12">
  15. <SummaryCard
  16. :value="maintain.total || 0"
  17. icon="fa-solid:list"
  18. icon-bg-color="text-pink-500"
  19. icon-color="bg-blue-100"
  20. title="维修工单"
  21. />
  22. </el-col>
  23. <el-col :sm="3" :xs="12">
  24. <SummaryCard
  25. :value="fill.unfilledCount || 0"
  26. icon="fa-solid:times-circle"
  27. icon-bg-color="text-purple-500"
  28. icon-color="bg-purple-100"
  29. title="运行未填写"
  30. />
  31. </el-col>
  32. <el-col :sm="3" :xs="12">
  33. <SummaryCard
  34. :value="fill.filledCount || 0"
  35. icon="fa-solid:award"
  36. icon-bg-color="text-purple-500"
  37. icon-color="bg-purple-100"
  38. title="运行已填写"
  39. />
  40. </el-col>
  41. <el-col :sm="3" :xs="12">
  42. <SummaryCard
  43. :value="by.todo || 0"
  44. icon="fa-solid:times-circle"
  45. icon-bg-color="text-green-500"
  46. icon-color="bg-green-100"
  47. title="未执行保养"
  48. />
  49. </el-col>
  50. <el-col :sm="3" :xs="12">
  51. <SummaryCard
  52. :value="by.finished || 0"
  53. icon="fa-solid:award"
  54. icon-bg-color="text-green-500"
  55. icon-color="bg-green-100"
  56. title="已执行保养"
  57. />
  58. </el-col>
  59. <el-col :sm="3" :xs="12">
  60. <SummaryCard
  61. :value="inspectt.todo || 0"
  62. icon="fa-solid:times-circle"
  63. icon-bg-color="text-yellow-500"
  64. icon-color="bg-yellow-100"
  65. title="待填写巡检"
  66. />
  67. </el-col>
  68. <el-col :sm="3" :xs="12">
  69. <SummaryCard
  70. :value="inspectt.finished || 0"
  71. icon="fa-solid:award"
  72. icon-bg-color="text-yellow-500"
  73. icon-color="bg-yellow-100"
  74. title="已填写巡检"
  75. />
  76. </el-col>
  77. <!-- 其他统计卡片... -->
  78. </el-row>
  79. <!-- 第二行:图表行 -->
  80. <el-row :gutter="16" class="mb-4 mt-3">
  81. <el-col :span="7">
  82. <el-card class="chart-card" shadow="never">
  83. <template #header>
  84. <div class="flex items-center">
  85. <span class="text-base font-medium " style="color: #b6c8da">设备状态统计</span>
  86. </div>
  87. </template>
  88. <div ref="statusChartRef" class="h-[290px]"></div>
  89. </el-card>
  90. </el-col>
  91. <el-col :span="7">
  92. <el-card class="chart-card" shadow="never" >
  93. <template #header>
  94. <div class="flex items-center">
  95. <span class="text-base font-medium " style="color: #b6c8da">设备类别TOP数量</span>
  96. </div>
  97. </template>
  98. <div ref="topContainer" class="h-[290px]"></div>
  99. </el-card>
  100. </el-col>
  101. <el-col :span="10">
  102. <el-card class="chart-card" shadow="never">
  103. <template #header>
  104. <div class="flex items-center justify-between">
  105. <span class="text-base font-medium " style="color: #b6c8da">近七日运维成本</span>
  106. </div>
  107. </template>
  108. <div ref="chartContainer" class="h-[290px]"></div>
  109. </el-card>
  110. </el-col>
  111. </el-row>
  112. <!-- 第三行:消息统计行 -->
  113. <el-row :gutter="16" class="mb-1">
  114. <el-col :span="7">
  115. <el-card class="chart-card" shadow="never">
  116. <template #header>
  117. <div class="flex items-center">
  118. <span class="text-base font-medium " style="color: #b6c8da">近7天物料消耗TOP5</span>
  119. </div>
  120. </template>
  121. <div ref="materialChartRef" class="h-[320px]"></div>
  122. </el-card>
  123. </el-col>
  124. <el-col :span="17">
  125. <el-card class="chart-card" shadow="never">
  126. <template #header>
  127. <div class="flex items-center justify-between">
  128. <span class="text-base font-medium " style="color: #b6c8da">工单数量情况</span>
  129. </div>
  130. </template>
  131. <div ref="qxRef" class="h-[320px]"></div>
  132. </el-card>
  133. </el-col>
  134. </el-row>
  135. <el-row :gutter="16" class="mb-1 mt-4">
  136. <el-col :span="12">
  137. <el-card class="chart-card" shadow="never">
  138. <template #header>
  139. <div class="flex items-center justify-between">
  140. <span class="text-base font-medium " style="color: #b6c8da">累计注气/注水量统计</span>
  141. </div>
  142. </template>
  143. <div ref="zqlChartRef" class="h-[300px]"></div>
  144. </el-card>
  145. </el-col>
  146. <el-col :span="12">
  147. <el-card class="chart-card" shadow="never">
  148. <template #header>
  149. <div class="flex items-center justify-between">
  150. <span class="text-base font-medium " style="color: #b6c8da">当日注气/注水量统计</span>
  151. </div>
  152. </template>
  153. <div ref="todayZqlRef" class="h-[300px]"></div>
  154. </el-card>
  155. </el-col>
  156. </el-row>
  157. </div>
  158. </template>
  159. <script setup lang="ts" name="Index">
  160. import * as echarts from 'echarts/core'
  161. import { BarChart, GaugeChart, LineChart, PieChart } from 'echarts/charts' // 显式导入柱状图模块
  162. import {
  163. GridComponent,
  164. LegendComponent,
  165. TitleComponent,
  166. ToolboxComponent,
  167. TooltipComponent
  168. } from 'echarts/components'
  169. import { LabelLayout, UniversalTransition } from 'echarts/features'
  170. import { CanvasRenderer } from 'echarts/renderers'
  171. import { useElementSize } from '@vueuse/core'
  172. import { IotStatApi } from '@/api/pms/stat'
  173. import SummaryCard from '@/components/SummaryCard/index.vue'
  174. import { reactive, ref } from 'vue'
  175. /** IoT 首页 */
  176. defineOptions({ name: 'IotRhStat' })
  177. echarts.use([
  178. TooltipComponent,
  179. LegendComponent,
  180. PieChart,
  181. CanvasRenderer,
  182. LabelLayout,
  183. TitleComponent,
  184. ToolboxComponent,
  185. GridComponent,
  186. LineChart,
  187. UniversalTransition,
  188. GaugeChart,
  189. BarChart
  190. ])
  191. const by = ref({
  192. todo: undefined,
  193. finished: undefined
  194. })
  195. const fill = ref({
  196. filledCount: undefined,
  197. unfilledCount: undefined
  198. })
  199. const inspectt = ref({
  200. finished: 0,
  201. todo: 0
  202. })
  203. const backendData = ref([])
  204. const statusChartRef = ref() // 设备数量统计的图表
  205. const materialChartRef = ref() // 设备数量统计的图表
  206. const zqlChartRef = ref() // 注气量统计的图表
  207. const todayZqlRef = ref() // 注气量统计的图表
  208. const device = ref({
  209. total: undefined,
  210. today: undefined
  211. })
  212. const maintain = ref({
  213. total: undefined,
  214. today: undefined
  215. })
  216. const work = ref({
  217. total: undefined,
  218. today: undefined
  219. })
  220. const inspect = ref({
  221. total: undefined,
  222. today: undefined
  223. })
  224. const status = ref({
  225. finished: 0,
  226. todo: 0
  227. })
  228. const todayStatus = ref({
  229. finished: 0,
  230. todo: 0
  231. })
  232. const typeData = ref({})
  233. const materialData = ref({})
  234. const orderSevenData = ref({})
  235. const ywcbSevenData = ref({})
  236. const zqlData = ref({})
  237. const zqlTodayData = ref({})
  238. /** 获取统计数据 */
  239. const getStats = () => {
  240. initYwcbChart()
  241. // 获取基础统计数据
  242. IotStatApi.getDeviceCount('rh').then((res) => {
  243. device.value = res
  244. })
  245. IotStatApi.getMaintainCount('rh').then((res) => {
  246. maintain.value = res
  247. })
  248. IotStatApi.getMainWorkCount("").then((res) => {
  249. work.value = res
  250. })
  251. IotStatApi.getInspectCount("").then((res) => {
  252. inspect.value = res
  253. })
  254. // IotStatApi.getMaintenanceStatus("").then((res) => {
  255. // status.value = res
  256. // // initCharts()
  257. // })
  258. IotStatApi.getMaintenanceTodayStatus("").then((res) => {
  259. todayStatus.value = res
  260. initTopChart()
  261. })
  262. IotStatApi.getDeviceStatusCount('rh').then((res) => {
  263. typeData.value = res
  264. initDeviceStatusCharts()
  265. })
  266. // IotStatApi.getSafeCount().then((res) => {
  267. // safe.value = res
  268. // })
  269. IotStatApi.getMaterial().then((res) => {
  270. materialData.value = res
  271. initMaterials()
  272. })
  273. IotStatApi.getOrderSeven('rh').then((res) => {
  274. orderSevenData.value = res
  275. initQxChart()
  276. })
  277. IotStatApi.getOrderYwcb('rh').then((res) => {
  278. ywcbSevenData.value = res
  279. initYwcbChart()
  280. })
  281. IotStatApi.getMaintenanceStatus('rh').then((res) => {
  282. by.value = res
  283. })
  284. IotStatApi.getRhZql('rh').then((res) => {
  285. zqlData.value = res
  286. initZqlChart()
  287. })
  288. IotStatApi.getRhZqlToday('rh').then((res) => {
  289. zqlTodayData.value = res
  290. initTodayZqlChart()
  291. })
  292. const fillQueryParams = reactive({
  293. startTime: Date.now() - 7 * 24 * 60 * 60 * 1000, // 设置默认开始时间为 7 天前
  294. endTime: Date.now(), // 设置默认结束时间为当前时间
  295. createTime: [],
  296. deptId: '', // 选中的部门ID
  297. status: null // 填写状态
  298. })
  299. IotStatApi.getInspectStatuss(fillQueryParams, 'rh').then((res) => {
  300. inspectt.value = res
  301. })
  302. fillQueryParams.deptId = '157'
  303. IotStatApi.getDeptStatistics(fillQueryParams).then((res) => {
  304. fill.value = res.totalList[0] || []
  305. })
  306. }
  307. let materialInstance;
  308. const initMaterials = () => {
  309. if (!materialChartRef.value) return
  310. const option = {
  311. tooltip: {
  312. trigger: 'item'
  313. },
  314. legend: {
  315. orient: 'horizontal', // 水平排列图例项
  316. bottom: '0%', // 放置在底部
  317. icon: 'circle',
  318. textStyle: {
  319. color:'#B6C8DA'
  320. }
  321. },
  322. series: [
  323. {
  324. name: '',
  325. type: 'pie',
  326. radius: ['50%', '80%'],
  327. avoidLabelOverlap: false,
  328. center: ['50%', '44%'],
  329. label: {
  330. show: false,
  331. position: 'outside',
  332. color:'#B6C8DA'
  333. },
  334. emphasis: {
  335. label: {
  336. show: true,
  337. fontSize: 15,
  338. fontWeight: 'bold'
  339. }
  340. },
  341. labelLine: {
  342. show: false
  343. },
  344. data: materialData.value
  345. }
  346. ]
  347. }
  348. // 初始化图表
  349. materialInstance = echarts.init(materialChartRef.value)
  350. materialInstance.setOption(option)
  351. // 窗口缩放监听
  352. window.addEventListener('resize', handleMaterialResize)
  353. handleMaterialResize()
  354. }
  355. let zqlTodayInstance = null
  356. const initTodayZqlChart = async () => {
  357. if (!todayZqlRef.value) return
  358. zqlTodayInstance = echarts.init(todayZqlRef.value)
  359. const option = {
  360. tooltip: {
  361. trigger: 'axis',
  362. axisPointer: {
  363. type: 'cross',
  364. label: {
  365. backgroundColor: '#6a7985'
  366. }
  367. }
  368. },
  369. legend: {
  370. data: zqlTodayData.value.series.map((item) => item.name),
  371. top: 30,
  372. textStyle: {
  373. color: '#B6C8DA'
  374. }
  375. },
  376. grid: {
  377. left: '3%',
  378. right: '4%',
  379. bottom: '3%',
  380. containLabel: true
  381. },
  382. xAxis: {
  383. type: 'category',
  384. boundaryGap: false,
  385. data: zqlTodayData.value.xAxis,
  386. axisLine: {
  387. lineStyle: {
  388. color: '#B6C8DA' // X轴线白色半透明
  389. }
  390. },
  391. axisLabel: {
  392. color: '#B6C8DA',
  393. formatter: (value) => value.split('-').join('/') // 显示为 2023/01
  394. }
  395. },
  396. yAxis: [
  397. {
  398. type: 'value',
  399. name:'当日注气量(m³)',
  400. axisLabel: {
  401. color: '#B6C8DA',
  402. formatter: '{value}'
  403. },
  404. splitLine: {
  405. show: true, // 显示水平网格线(默认显示)
  406. lineStyle: {
  407. // 水平网格线颜色(可设置为纯色或带透明度的颜色)
  408. color: '#457794', // 浅灰色半透明
  409. // 可选:设置线条类型(实线/虚线/点线)
  410. type: 'dashed'
  411. }
  412. },
  413. axisLine: {
  414. lineStyle: {
  415. color: '#B6C8DA'
  416. }
  417. },
  418. position: 'left' // 左侧 Y 轴
  419. },
  420. {
  421. type: 'value',
  422. name:'当日注水量(m³)',
  423. axisLabel: {
  424. color: '#B6C8DA',
  425. formatter: '{value}'
  426. },
  427. splitLine: {
  428. show: true, // 显示水平网格线(默认显示)
  429. lineStyle: {
  430. // 水平网格线颜色(可设置为纯色或带透明度的颜色)
  431. color: '#457794', // 浅灰色半透明
  432. // 可选:设置线条类型(实线/虚线/点线)
  433. type: 'dashed'
  434. }
  435. },
  436. axisLine: {
  437. lineStyle: {
  438. color: '#B6C8DA'
  439. }
  440. },
  441. position: 'right', // 右侧 Y 轴
  442. splitLine: {
  443. show: false // 隐藏右侧 Y 轴的分割线
  444. }
  445. }
  446. ],
  447. series: zqlTodayData.value.series.map((item, index) => {
  448. // 假设前两条曲线使用左侧 Y 轴,后两条曲线使用右侧 Y 轴
  449. const yAxisIndex = index < 1 ? 0 : 1
  450. return {
  451. name: item.name,
  452. type: 'line',
  453. smooth: true,
  454. symbol: 'circle',
  455. symbolSize: 8,
  456. itemStyle: {
  457. color: ['#5470c6', '#f1d209'][index]
  458. },
  459. areaStyle: {
  460. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  461. { offset: 0, color: 'rgba(84,112,198,0.4)' },
  462. { offset: 1, color: 'rgba(84,112,198,0.1)' }
  463. ])
  464. },
  465. data: item.data,
  466. yAxisIndex: yAxisIndex // 指定使用的 Y 轴
  467. }
  468. })
  469. }
  470. zqlTodayInstance.setOption(option)
  471. // 窗口缩放监听
  472. window.addEventListener('resize', handleZqlTodayResize)
  473. handleZqlTodayResize()
  474. }
  475. /** 初始化图表 */
  476. const initDeviceStatusCharts = () => {
  477. // 设备数量统计
  478. echarts.init(statusChartRef.value).setOption({
  479. tooltip: {
  480. trigger: 'item'
  481. },
  482. legend: {
  483. orient: 'horizontal', // 水平排列图例项
  484. bottom: '0%', // 放置在底部
  485. icon: 'circle',
  486. textStyle: {
  487. color: '#B6C8DA'
  488. }
  489. },
  490. series: [
  491. {
  492. name: '',
  493. type: 'pie',
  494. radius: ['50%', '80%'],
  495. avoidLabelOverlap: false,
  496. center: ['50%', '44%'],
  497. label: {
  498. show: false,
  499. position: 'outside',
  500. color: 'white'
  501. },
  502. emphasis: {
  503. label: {
  504. show: true,
  505. fontSize: 15,
  506. fontWeight: 'bold'
  507. }
  508. },
  509. labelLine: {
  510. show: false
  511. },
  512. data: typeData.value
  513. }
  514. ]
  515. })
  516. }
  517. /** 初始化消息统计图表 */
  518. const chartContainer = ref(null)
  519. let chartInstance;
  520. // 模拟数据获取
  521. const fetchChartData = async () => {
  522. // 模拟异步请求
  523. return new Promise((resolve) => {
  524. setTimeout(() => {
  525. resolve({
  526. months: ['空压机', '增压机', '提纯撬'],
  527. repairs: [10, 30, 90]
  528. })
  529. }, 300)
  530. })
  531. }
  532. // 初始化图表配置
  533. const initYwcbChart = async () => {
  534. if (!chartContainer.value) return
  535. // 获取数据
  536. const option = {
  537. tooltip: {
  538. trigger: 'axis',
  539. axisPointer: {
  540. type: 'shadow'
  541. },
  542. // formatter: (params) => {
  543. // return `${params[0].axisValue}<br/>
  544. // ${params[0].marker} ${params[0].seriesName}: ${params[0].value}`
  545. // }
  546. },
  547. legend: {
  548. data: ['当日运维成本'],
  549. top: 1,
  550. textStyle: {
  551. color: '#B6C8DA'
  552. }
  553. },
  554. grid: {
  555. left: '3%',
  556. right: '4%',
  557. bottom: '1%',
  558. containLabel: true
  559. },
  560. xAxis: {
  561. type: 'category',
  562. data: ywcbSevenData.value.xAxis,
  563. axisLabel: {
  564. color: '#B6C8DA',
  565. formatter: (value) => value.split('-').join('/')
  566. },
  567. axisLine: {
  568. lineStyle: {
  569. color: '#B6C8DA' // X轴线白色半透明
  570. }
  571. },
  572. },
  573. yAxis: {
  574. type: 'value',
  575. axisLabel: {
  576. color: '#B6C8DA',
  577. // formatter: (value) => Math.floor(value).toString()
  578. },
  579. splitLine: {
  580. show: true, // 显示水平网格线(默认显示)
  581. lineStyle: {
  582. // 水平网格线颜色(可设置为纯色或带透明度的颜色)
  583. color: '#457794', // 浅灰色半透明
  584. // 可选:设置线条类型(实线/虚线/点线)
  585. type: 'dashed'
  586. }
  587. },
  588. axisLine: {
  589. lineStyle: {
  590. color: '#B6C8DA'
  591. }
  592. }
  593. },
  594. series: [
  595. {
  596. name: '当日运维成本',
  597. type: 'bar',
  598. itemStyle: {
  599. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#f69606' }])
  600. },
  601. emphasis: {
  602. focus: 'series'
  603. },
  604. data: ywcbSevenData.value.series
  605. }
  606. ]
  607. }
  608. // 初始化图表
  609. chartInstance = echarts.init(chartContainer.value)
  610. chartInstance.setOption(option)
  611. // 窗口缩放监听
  612. window.addEventListener('resize', handleResize)
  613. handleResize()
  614. }
  615. // 自适应调整
  616. const handleResize = () => {
  617. chartInstance?.resize()
  618. }
  619. // 自适应调整
  620. const handleQxResize = () => {
  621. qxInstance?.resize()
  622. }
  623. const handleZqlResize = () => {
  624. zqlInstance?.resize()
  625. }
  626. const handleZqlTodayResize = () => {
  627. zqlTodayInstance?.resize()
  628. }
  629. const handleMaterialResize = () => {
  630. materialInstance?.resize()
  631. }
  632. const topContainer = ref(null)
  633. let topInstance;
  634. // 响应式容器尺寸
  635. const { width, height } = useElementSize(topContainer)
  636. // 初始化图表配置
  637. const getTopOption = () => {
  638. // backendData.value = data
  639. const data = backendData.value.sort((a, b) => a.value - b.value)
  640. return {
  641. tooltip: {
  642. trigger: 'axis',
  643. axisPointer: { type: 'shadow' },
  644. formatter: (params) => {
  645. const item = params[0]
  646. return `${item.name}<br/>${item.marker} ${item.value.toLocaleString()}`
  647. }
  648. },
  649. grid: {
  650. height: '200px',
  651. left: '6%',
  652. right: '6%',
  653. bottom: '18%',
  654. containLabel: true
  655. },
  656. xAxis: {
  657. type: 'value',
  658. axisLabel: {
  659. color: '#B6C8DA',
  660. formatter: (value) => {
  661. if (value >= 10000) return `${(value / 10000).toFixed(1)}万`
  662. return value.toLocaleString()
  663. }
  664. },
  665. axisLine: {
  666. lineStyle: {
  667. color: '#B6C8DA' // X轴线白色半透明
  668. }
  669. },
  670. splitLine: {
  671. show: true, // 显示水平网格线(默认显示)
  672. lineStyle: {
  673. // 水平网格线颜色(可设置为纯色或带透明度的颜色)
  674. color: '#457794', // 浅灰色半透明
  675. // 可选:设置线条类型(实线/虚线/点线)
  676. type: 'dashed'
  677. }
  678. }
  679. },
  680. yAxis: {
  681. type: 'category',
  682. data: data.map((item) => item.category),
  683. axisTick: { show: false },
  684. axisLabel: { color: '#B6C8DA' },
  685. axisLine: {
  686. lineStyle: {
  687. color: '#B6C8DA' // X轴线白色半透明
  688. }
  689. }
  690. },
  691. series: [
  692. {
  693. type: 'bar',
  694. data: data.map((item) => item.value),
  695. itemStyle: {
  696. color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
  697. { offset: 0, color: '#83bff6' },
  698. { offset: 0.7, color: '#188df0' },
  699. { offset: 1, color: '#188df0' }
  700. ]),
  701. borderRadius: [0, 8, 8, 0]
  702. },
  703. label: {
  704. show: true,
  705. position: 'right',
  706. formatter: '{@value}',
  707. color: '#B6C8DA',
  708. fontWeight: 'bold'
  709. },
  710. emphasis: {
  711. itemStyle: {
  712. shadowBlur: 10,
  713. shadowColor: 'rgba(0, 0, 0, 0.5)'
  714. }
  715. }
  716. }
  717. ]
  718. }
  719. }
  720. // 初始化图表
  721. const initTopChart = async () => {
  722. await IotStatApi.getDeviceTypeCount('rh').then((res) => {
  723. backendData.value = res
  724. })
  725. if (!topContainer.value) return
  726. topInstance = echarts.init(topContainer.value)
  727. updateTopChart()
  728. }
  729. // 更新图表
  730. const updateTopChart = () => {
  731. if (!topInstance) return
  732. topInstance.setOption(getTopOption())
  733. }
  734. // 自适应调整
  735. watch([width, height], () => {
  736. topInstance?.resize()
  737. })
  738. // 监听数据变化
  739. watch(
  740. backendData,
  741. () => {
  742. updateTopChart()
  743. },
  744. { deep: true }
  745. )
  746. const qxRef = ref(null)
  747. let qxInstance;
  748. let zqlInstance;
  749. const initZqlChart = () => {
  750. if (!zqlChartRef.value) return
  751. // 获取数据
  752. // ECharts配置
  753. const option = {
  754. tooltip: {
  755. trigger: 'axis',
  756. axisPointer: {
  757. type: 'shadow',
  758. label: {
  759. backgroundColor: '#6a7985'
  760. }
  761. }
  762. // trigger: 'axis',
  763. // axisPointer: {
  764. // type: 'shadow'
  765. // },
  766. // formatter: (params) => {
  767. // return `${params[0].axisValue}<br/>
  768. // ${params[0].marker} ${params[0].seriesName}: ${params[0].value}`
  769. // }
  770. },
  771. legend: {
  772. data: zqlData.value.series.map((item) => item.name),
  773. top: 30,
  774. textStyle: {
  775. color: '#B6C8DA'
  776. }
  777. },
  778. grid: {
  779. left: '3%',
  780. right: '4%',
  781. bottom: '3%',
  782. containLabel: true
  783. },
  784. xAxis: {
  785. type: 'category',
  786. data: zqlData.value.xAxis,
  787. axisLine: {
  788. lineStyle: {
  789. color: '#B6C8DA' // X轴线白色半透明
  790. }
  791. },
  792. axisLabel: {
  793. color: '#B6C8DA',
  794. formatter: (value) => value.split('-').join('/')
  795. }
  796. },
  797. yAxis: [
  798. {
  799. type: 'value',
  800. name: '累计注气量(m³)',
  801. axisLabel: {
  802. color: '#B6C8DA',
  803. formatter: '{value}'
  804. },
  805. splitLine: {
  806. show: true, // 显示水平网格线(默认显示)
  807. lineStyle: {
  808. // 水平网格线颜色(可设置为纯色或带透明度的颜色)
  809. color: '#457794', // 浅灰色半透明
  810. // 可选:设置线条类型(实线/虚线/点线)
  811. type: 'dashed'
  812. }
  813. },
  814. axisLine: {
  815. lineStyle: {
  816. color: '#B6C8DA'
  817. }
  818. },
  819. position: 'left' // 左侧 Y 轴
  820. },
  821. {
  822. type: 'value',
  823. name: '累计注水量(m³)',
  824. axisLabel: {
  825. color: '#B6C8DA',
  826. formatter: '{value}'
  827. },
  828. splitLine: {
  829. show: true, // 显示水平网格线(默认显示)
  830. lineStyle: {
  831. // 水平网格线颜色(可设置为纯色或带透明度的颜色)
  832. color: '#457794', // 浅灰色半透明
  833. // 可选:设置线条类型(实线/虚线/点线)
  834. type: 'dashed'
  835. }
  836. },
  837. axisLine: {
  838. lineStyle: {
  839. color: '#B6C8DA'
  840. }
  841. },
  842. position: 'right', // 右侧 Y 轴
  843. splitLine: {
  844. show: false // 隐藏右侧 Y 轴的分割线
  845. }
  846. }
  847. ],
  848. series: zqlData.value.series.map((item, index) => {
  849. // 假设前两条曲线使用左侧 Y 轴,后两条曲线使用右侧 Y 轴
  850. const yAxisIndex = index < 1 ? 0 : 1
  851. return {
  852. name: item.name,
  853. type: 'bar',
  854. smooth: true,
  855. symbol: 'circle',
  856. symbolSize: 8,
  857. itemStyle: {
  858. color: [ '#91cc75','#f1d209'][index]
  859. },
  860. areaStyle: {
  861. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  862. { offset: 0, color: 'rgba(84,112,198,0.4)' },
  863. { offset: 1, color: 'rgba(84,112,198,0.1)' }
  864. ])
  865. },
  866. data: item.data,
  867. yAxisIndex: yAxisIndex // 指定使用的 Y 轴
  868. }
  869. })
  870. }
  871. // 初始化图表
  872. zqlInstance = echarts.init(zqlChartRef.value)
  873. zqlInstance.setOption(option)
  874. // 窗口缩放监听
  875. window.addEventListener('resize', handleZqlResize)
  876. handleZqlResize()
  877. }
  878. const initQxChart = () => {
  879. if (!qxRef.value) return
  880. qxInstance = echarts.init(qxRef.value)
  881. debugger
  882. const option = {
  883. tooltip: {
  884. trigger: 'axis',
  885. axisPointer: {
  886. type: 'cross',
  887. label: {
  888. backgroundColor: '#6a7985'
  889. }
  890. }
  891. },
  892. legend: {
  893. data: orderSevenData.value.series.map((item) => item.name),
  894. top: 30,
  895. textStyle: {
  896. color:'#B6C8DA'
  897. }
  898. },
  899. grid: {
  900. left: '3%',
  901. right: '4%',
  902. bottom: '3%',
  903. containLabel: true
  904. },
  905. xAxis: {
  906. type: 'category',
  907. boundaryGap: false,
  908. data: orderSevenData.value.xAxis,
  909. axisLabel: {
  910. color: '#B6C8DA',
  911. formatter: (value) => value.split('-').join('/')
  912. },
  913. axisLine: {
  914. lineStyle: {
  915. color: '#B6C8DA' // X轴线白色半透明
  916. }
  917. },
  918. },
  919. yAxis: [
  920. {
  921. type: 'value',
  922. axisLabel: {
  923. color: '#B6C8DA',
  924. formatter: '{value}'
  925. },
  926. splitLine: {
  927. show: true, // 显示水平网格线(默认显示)
  928. lineStyle: {
  929. // 水平网格线颜色(可设置为纯色或带透明度的颜色)
  930. color: '#457794', // 浅灰色半透明
  931. // 可选:设置线条类型(实线/虚线/点线)
  932. type: 'dashed'
  933. }
  934. },
  935. axisLine: {
  936. lineStyle: {
  937. color: '#B6C8DA'
  938. }
  939. },
  940. position: 'left' // 左侧 Y 轴
  941. },
  942. {
  943. type: 'value',
  944. axisLabel: {
  945. color: '#B6C8DA',
  946. formatter: '{value}'
  947. },
  948. splitLine: {
  949. show: true, // 显示水平网格线(默认显示)
  950. lineStyle: {
  951. // 水平网格线颜色(可设置为纯色或带透明度的颜色)
  952. color: '#457794', // 浅灰色半透明
  953. // 可选:设置线条类型(实线/虚线/点线)
  954. type: 'dashed'
  955. }
  956. },
  957. axisLine: {
  958. lineStyle: {
  959. color: '#B6C8DA'
  960. }
  961. },
  962. position: 'right', // 右侧 Y 轴
  963. splitLine: {
  964. show: false // 隐藏右侧 Y 轴的分割线
  965. }
  966. }
  967. ],
  968. series: orderSevenData.value.series.map((item, index) => {
  969. // 假设前两条曲线使用左侧 Y 轴,后两条曲线使用右侧 Y 轴
  970. const yAxisIndex = index < 2 ? 0 : 1
  971. return {
  972. name: item.name,
  973. type: 'line',
  974. smooth: true,
  975. symbol: 'circle',
  976. symbolSize: 8,
  977. itemStyle: {
  978. color: ['#5470c6', '#f1d209', '#e14f0f', '#91cc75'][index]
  979. },
  980. areaStyle: {
  981. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  982. { offset: 0, color: 'rgba(84,112,198,0.4)' },
  983. { offset: 1, color: 'rgba(84,112,198,0.1)' }
  984. ])
  985. },
  986. data: item.data,
  987. yAxisIndex: yAxisIndex // 指定使用的 Y 轴
  988. }
  989. })
  990. }
  991. qxInstance.setOption(option)
  992. // 窗口缩放监听
  993. window.addEventListener('resize', handleQxResize)
  994. handleQxResize()
  995. }
  996. // 响应式调整
  997. const resizeQxChart = () => qxInstance?.resize()
  998. const resizeZqlChart = () => zqlInstance?.resize()
  999. const resizeZqlTodayChart = () => zqlTodayInstance?.resize()
  1000. const resizeMaterialChart = () => materialInstance?.resize()
  1001. /** 初始化 */
  1002. onMounted(() => {
  1003. getStats()
  1004. // initChart()
  1005. // initTopChart()
  1006. // initActiveChart()
  1007. // initQxChart()
  1008. window.addEventListener('resize', resizeQxChart)
  1009. // fetchTop()
  1010. window.addEventListener('resize', () => topInstance?.resize())
  1011. })
  1012. onBeforeUnmount(() => {
  1013. chartInstance?.dispose()
  1014. window.removeEventListener('resize', () => chartInstance?.resize())
  1015. topInstance?.dispose()
  1016. window.removeEventListener('resize', handleResize)
  1017. qxInstance?.dispose()
  1018. window.removeEventListener('resize', resizeQxChart)
  1019. zqlInstance?.dispose()
  1020. window.removeEventListener('resize', resizeZqlChart)
  1021. zqlTodayInstance?.dispose()
  1022. window.removeEventListener('resize', resizeZqlTodayChart)
  1023. materialInstance?.dispose()
  1024. window.removeEventListener('resize', resizeMaterialChart)
  1025. })
  1026. </script>
  1027. <style lang="scss" scoped>
  1028. .page-container {
  1029. background-color: #3a6fa3;
  1030. min-height: 100vh;
  1031. padding: 20px;
  1032. }
  1033. .summary {
  1034. margin-bottom: 20px;
  1035. }
  1036. ::v-deep .chart-card {
  1037. background-color: rgba(0, 0, 0, 0.3);
  1038. border-radius: 8px;
  1039. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
  1040. transition: all 0.3s ease;
  1041. border: none;
  1042. &:hover {
  1043. box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
  1044. }
  1045. }
  1046. .safety-days-card {
  1047. .safety-days-content {
  1048. display: flex;
  1049. flex-direction: column;
  1050. align-items: center;
  1051. justify-content: center;
  1052. height: 150px;
  1053. position: relative;
  1054. .days-number {
  1055. font-size: 58px;
  1056. font-weight: bold;
  1057. color: darkorange;
  1058. line-height: 1;
  1059. transition: all 0.3s ease;
  1060. }
  1061. .days-number:hover {
  1062. transform: scale(1.05);
  1063. }
  1064. .days-label {
  1065. font-size: 20px;
  1066. color: white;
  1067. margin-top: 8px;
  1068. }
  1069. .safety-desc {
  1070. font-size: 14px;
  1071. color: #999;
  1072. position: absolute;
  1073. bottom: 10px;
  1074. text-align: center;
  1075. width: 90%;
  1076. }
  1077. }
  1078. }
  1079. @media (max-width: 768px) {
  1080. .page-container {
  1081. padding: 10px;
  1082. }
  1083. }
  1084. ::v-deep .el-card__header {
  1085. border-bottom: none !important;
  1086. padding-bottom: 0;
  1087. }
  1088. </style>