rhkb.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
  1. <template>
  2. <el-row :gutter="16" class="summary">
  3. <!-- 原有的统计卡片部分保持不变 -->
  4. <el-col v-loading="loading" :sm="3" :xs="12">
  5. <SummaryCard
  6. :value="device.total || 0"
  7. icon="fa-solid:project-diagram"
  8. icon-bg-color="text-blue-500"
  9. icon-color="bg-blue-100"
  10. title="设备数"
  11. />
  12. </el-col>
  13. <el-col v-loading="loading" :sm="3" :xs="12">
  14. <SummaryCard
  15. :value="maintain.total || 0"
  16. icon="fa-solid:list"
  17. icon-bg-color="text-pink-500"
  18. icon-color="bg-blue-100"
  19. title="维修工单"
  20. />
  21. </el-col>
  22. <el-col v-loading="loading" :sm="3" :xs="12">
  23. <SummaryCard
  24. :value="fill.unfilledCount || 0"
  25. icon="fa-solid:times-circle"
  26. icon-bg-color="text-purple-500"
  27. icon-color="bg-purple-100"
  28. title="运行未填写"
  29. />
  30. </el-col>
  31. <el-col v-loading="loading" :sm="3" :xs="12">
  32. <SummaryCard
  33. :value="fill.filledCount || 0"
  34. icon="fa-solid:award"
  35. icon-bg-color="text-purple-500"
  36. icon-color="bg-purple-100"
  37. title="运行已填写"
  38. />
  39. </el-col>
  40. <el-col v-loading="loading" :sm="3" :xs="12">
  41. <SummaryCard
  42. :value="by.todo || 0"
  43. icon="fa-solid:times-circle"
  44. icon-bg-color="text-green-500"
  45. icon-color="bg-green-100"
  46. title="未执行保养"
  47. />
  48. </el-col>
  49. <el-col v-loading="loading" :sm="3" :xs="12">
  50. <SummaryCard
  51. :value="by.finished || 0"
  52. icon="fa-solid:award"
  53. icon-bg-color="text-green-500"
  54. icon-color="bg-green-100"
  55. title="已执行保养"
  56. />
  57. </el-col>
  58. <el-col v-loading="loading" :sm="3" :xs="12">
  59. <SummaryCard
  60. :value="inspectt.todo || 0"
  61. icon="fa-solid:times-circle"
  62. icon-bg-color="text-yellow-500"
  63. icon-color="bg-yellow-100"
  64. title="待填写巡检"
  65. />
  66. </el-col>
  67. <el-col v-loading="loading" :sm="3" :xs="12">
  68. <SummaryCard
  69. :value="inspectt.finished || 0"
  70. icon="fa-solid:award"
  71. icon-bg-color="text-yellow-500"
  72. icon-color="bg-yellow-100"
  73. title="已填写巡检"
  74. />
  75. </el-col>
  76. <!-- 其他统计卡片... -->
  77. </el-row>
  78. <!-- 第二行:图表行 -->
  79. <el-row :gutter="16" class="mb-4 mt-3">
  80. <el-col :span="7">
  81. <el-card class="chart-card" shadow="never">
  82. <template #header>
  83. <div class="flex items-center">
  84. <span class="text-base font-medium text-gray-600">设备状态统计</span>
  85. </div>
  86. </template>
  87. <div ref="statusChartRef" class="h-[290px]"></div>
  88. </el-card>
  89. </el-col>
  90. <el-col :span="7">
  91. <el-card class="chart-card" shadow="never">
  92. <template #header>
  93. <div class="flex items-center">
  94. <span class="text-base font-medium text-gray-600">设备类别TOP5数量</span>
  95. </div>
  96. </template>
  97. <div ref="topContainer" class="h-[290px]"></div>
  98. </el-card>
  99. </el-col>
  100. <el-col :span="10">
  101. <el-card class="chart-card" shadow="never">
  102. <template #header>
  103. <div class="flex items-center justify-between">
  104. <span class="text-base font-medium text-gray-600">当日运维成本</span>
  105. </div>
  106. </template>
  107. <div ref="chartContainer" class="h-[290px]"></div>
  108. </el-card>
  109. </el-col>
  110. </el-row>
  111. <!-- 第三行:消息统计行 -->
  112. <el-row :gutter="16" class="mb-1">
  113. <el-col :span="7">
  114. <el-card class="chart-card" shadow="never">
  115. <template #header>
  116. <div class="flex items-center">
  117. <span class="text-base font-medium text-gray-600">近7天物料消耗TOP5</span>
  118. </div>
  119. </template>
  120. <div ref="materialChartRef" class="h-[320px]"></div>
  121. </el-card>
  122. </el-col>
  123. <el-col :span="17">
  124. <el-card class="chart-card" shadow="never">
  125. <template #header>
  126. <div class="flex items-center justify-between">
  127. <span class="text-base font-medium text-gray-600">工单数量情况</span>
  128. </div>
  129. </template>
  130. <div ref="qxRef" class="h-[320px]"></div>
  131. </el-card>
  132. </el-col>
  133. </el-row>
  134. <el-row :gutter="16" class="mb-1 mt-4">
  135. <el-col :span="12">
  136. <el-card class="chart-card" shadow="never">
  137. <template #header>
  138. <div class="flex items-center justify-between">
  139. <span class="text-base font-medium text-gray-600">累计注气量统计(m³)</span>
  140. </div>
  141. </template>
  142. <div ref="zqlChartRef" class="h-[300px]"></div>
  143. </el-card>
  144. </el-col>
  145. <el-col :span="12">
  146. <el-card class="chart-card" shadow="never">
  147. <template #header>
  148. <div class="flex items-center justify-between">
  149. <span class="text-base font-medium text-gray-600">当日注气量统计(m³)</span>
  150. </div>
  151. </template>
  152. <div ref="todayZqlRef" class="h-[300px]"></div>
  153. </el-card>
  154. </el-col>
  155. </el-row>
  156. <!-- TODO 第四行:地图 -->
  157. </template>
  158. <script setup lang="ts" name="Index">
  159. import * as echarts from 'echarts/core'
  160. import { BarChart, GaugeChart, LineChart, PieChart } from 'echarts/charts' // 显式导入柱状图模块
  161. import {
  162. GridComponent,
  163. LegendComponent,
  164. TitleComponent,
  165. ToolboxComponent,
  166. TooltipComponent
  167. } from 'echarts/components'
  168. import { LabelLayout, UniversalTransition } from 'echarts/features'
  169. import { CanvasRenderer } from 'echarts/renderers'
  170. import { useElementSize } from '@vueuse/core'
  171. import { IotStatApi } from '@/api/pms/stat'
  172. import SummaryCard from '@/components/SummaryCard/index.vue'
  173. import { reactive, ref } from 'vue'
  174. /** IoT 首页 */
  175. defineOptions({ name: 'IotRhStat' })
  176. echarts.use([
  177. TooltipComponent,
  178. LegendComponent,
  179. PieChart,
  180. CanvasRenderer,
  181. LabelLayout,
  182. TitleComponent,
  183. ToolboxComponent,
  184. GridComponent,
  185. LineChart,
  186. UniversalTransition,
  187. GaugeChart,
  188. BarChart
  189. ])
  190. const dateRange = ref<[Date, Date] | null>(null)
  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 queryParams = reactive({
  204. startTime: Date.now() - 7 * 24 * 60 * 60 * 1000, // 设置默认开始时间为 7 天前
  205. endTime: Date.now() // 设置默认结束时间为当前时间
  206. })
  207. const backendData = ref([])
  208. const statusChartRef = ref() // 设备数量统计的图表
  209. const materialChartRef = ref() // 设备数量统计的图表
  210. const zqlChartRef = ref() // 注气量统计的图表
  211. const todayZqlRef = ref() // 注气量统计的图表
  212. const device = ref({
  213. total: undefined,
  214. today: undefined
  215. })
  216. const maintain = ref({
  217. total: undefined,
  218. today: undefined
  219. })
  220. const work = ref({
  221. total: undefined,
  222. today: undefined
  223. })
  224. const inspect = ref({
  225. total: undefined,
  226. today: undefined
  227. })
  228. const status = ref({
  229. finished: 0,
  230. todo: 0
  231. })
  232. const todayStatus = ref({
  233. finished: 0,
  234. todo: 0
  235. })
  236. const typeData = ref({})
  237. const materialData = ref({})
  238. const orderSevenData = ref({})
  239. const zqlData = ref({})
  240. const zqlTodayData = ref({})
  241. const safe = ref()
  242. /** 获取统计数据 */
  243. const getStats = () => {
  244. initYwcbChart()
  245. // 获取基础统计数据
  246. IotStatApi.getDeviceCount('rh').then((res) => {
  247. device.value = res
  248. })
  249. IotStatApi.getMaintainCount('rh').then((res) => {
  250. maintain.value = res
  251. })
  252. IotStatApi.getMainWorkCount().then((res) => {
  253. work.value = res
  254. })
  255. IotStatApi.getInspectCount().then((res) => {
  256. inspect.value = res
  257. })
  258. IotStatApi.getMaintenanceStatus().then((res) => {
  259. status.value = res
  260. // initCharts()
  261. })
  262. IotStatApi.getMaintenanceTodayStatus().then((res) => {
  263. todayStatus.value = res
  264. initTopChart()
  265. })
  266. IotStatApi.getDeviceStatusCount('rh').then((res) => {
  267. typeData.value = res
  268. initDeviceStatusCharts()
  269. })
  270. // IotStatApi.getSafeCount().then((res) => {
  271. // safe.value = res
  272. // })
  273. IotStatApi.getMaterial().then((res) => {
  274. materialData.value = res
  275. initMaterials()
  276. })
  277. IotStatApi.getOrderSeven('rh').then((res) => {
  278. orderSevenData.value = res
  279. initQxChart()
  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: null, // 选中的部门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 = null
  308. const initMaterials = () => {
  309. if (!materialChartRef.value) return
  310. const option = {
  311. tooltip: {
  312. trigger: 'item'
  313. },
  314. legend: {
  315. // top: '5%',
  316. // right: '10%',
  317. // align: 'left',
  318. // orient: 'vertical',
  319. // icon: 'circle'
  320. orient: 'horizontal', // 水平排列图例项
  321. bottom: '0%', // 放置在底部
  322. icon: 'circle'
  323. },
  324. series: [
  325. {
  326. name: '',
  327. type: 'pie',
  328. radius: ['50%', '80%'],
  329. avoidLabelOverlap: false,
  330. center: ['50%', '44%'],
  331. label: {
  332. show: false,
  333. position: 'outside'
  334. },
  335. emphasis: {
  336. label: {
  337. show: true,
  338. fontSize: 15,
  339. fontWeight: 'bold'
  340. }
  341. },
  342. labelLine: {
  343. show: false
  344. },
  345. data: materialData.value
  346. }
  347. ]
  348. }
  349. // 初始化图表
  350. materialInstance = echarts.init(materialChartRef.value)
  351. materialInstance.setOption(option)
  352. // 窗口缩放监听
  353. window.addEventListener('resize', handleMaterialResize)
  354. handleMaterialResize()
  355. }
  356. let zqlTodayInstance = null
  357. const initTodayZqlChart = async () => {
  358. if (!todayZqlRef.value) return
  359. // 获取数据
  360. // ECharts配置
  361. const option = {
  362. tooltip: {
  363. trigger: 'axis',
  364. axisPointer: {
  365. type: 'shadow'
  366. },
  367. formatter: (params) => {
  368. return `${params[0].axisValue}<br/>
  369. ${params[0].marker} ${params[0].seriesName}: ${params[0].value}`
  370. }
  371. },
  372. legend: {
  373. data: ['当日注气量'],
  374. top: 25
  375. },
  376. grid: {
  377. left: '3%',
  378. right: '4%',
  379. bottom: '3%',
  380. containLabel: true
  381. },
  382. xAxis: {
  383. type: 'category',
  384. data: zqlTodayData.value.xAxis,
  385. axisLabel: {
  386. formatter: (value) => value.split('-').join('/') // 显示为 2023/01
  387. }
  388. },
  389. yAxis: {
  390. type: 'value',
  391. axisLabel: {
  392. formatter: (value) => Math.floor(value).toString()
  393. }
  394. },
  395. series: zqlTodayData.value.series.map((item, index) => {
  396. // 假设前两条曲线使用左侧 Y 轴,后两条曲线使用右侧 Y 轴
  397. const yAxisIndex = index < 2 ? 0 : 1
  398. return {
  399. name: item.name,
  400. type: 'bar',
  401. smooth: true,
  402. symbol: 'circle',
  403. symbolSize: 8,
  404. itemStyle: {
  405. color: [ '#91cc75'][index]
  406. },
  407. areaStyle: {
  408. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  409. { offset: 0, color: 'rgba(84,112,198,0.4)' },
  410. { offset: 1, color: 'rgba(84,112,198,0.1)' }
  411. ])
  412. },
  413. data: item.data,
  414. yAxisIndex: yAxisIndex // 指定使用的 Y 轴
  415. }
  416. })
  417. }
  418. // 初始化图表
  419. zqlTodayInstance = echarts.init(todayZqlRef.value)
  420. zqlTodayInstance.setOption(option)
  421. // 窗口缩放监听
  422. window.addEventListener('resize', handleZqlTodayResize)
  423. handleZqlTodayResize()
  424. }
  425. /** 初始化图表 */
  426. const initDeviceStatusCharts = () => {
  427. // 设备数量统计
  428. echarts.init(statusChartRef.value).setOption({
  429. tooltip: {
  430. trigger: 'item'
  431. },
  432. legend: {
  433. orient: 'horizontal', // 水平排列图例项
  434. bottom: '0%', // 放置在底部
  435. icon: 'circle'
  436. },
  437. series: [
  438. {
  439. name: '',
  440. type: 'pie',
  441. radius: ['50%', '80%'],
  442. avoidLabelOverlap: false,
  443. center: ['50%', '44%'],
  444. label: {
  445. show: false,
  446. position: 'outside'
  447. },
  448. emphasis: {
  449. label: {
  450. show: true,
  451. fontSize: 15,
  452. fontWeight: 'bold'
  453. }
  454. },
  455. labelLine: {
  456. show: false
  457. },
  458. data: typeData.value
  459. }
  460. ]
  461. })
  462. }
  463. /** 初始化消息统计图表 */
  464. const chartContainer = ref(null)
  465. let chartInstance = null
  466. // 模拟数据获取
  467. const fetchChartData = async () => {
  468. // 模拟异步请求
  469. return new Promise((resolve) => {
  470. setTimeout(() => {
  471. resolve({
  472. months: ['空压机', '增压机', '提纯撬'],
  473. repairs: [10, 30, 90]
  474. })
  475. }, 300)
  476. })
  477. }
  478. // 初始化图表配置
  479. const initYwcbChart = async () => {
  480. if (!chartContainer.value) return
  481. // 获取数据
  482. const { months, faults, repairs } = await fetchChartData()
  483. // const months = ['空压机','增压机','提纯撬']
  484. // ECharts配置
  485. const option = {
  486. tooltip: {
  487. trigger: 'axis',
  488. axisPointer: {
  489. type: 'shadow'
  490. },
  491. formatter: (params) => {
  492. return `${params[0].axisValue}<br/>
  493. ${params[0].marker} ${params[0].seriesName}: ${params[0].value}`
  494. }
  495. },
  496. legend: {
  497. data: ['当日运维成本'],
  498. top: 1
  499. },
  500. grid: {
  501. left: '3%',
  502. right: '4%',
  503. bottom: '1%',
  504. containLabel: true
  505. },
  506. xAxis: {
  507. type: 'category',
  508. data: zqlTodayData.value.xAxis,
  509. axisLabel: {
  510. rotate: 45,
  511. margin: 15
  512. }
  513. },
  514. yAxis: {
  515. type: 'value',
  516. axisLabel: {
  517. formatter: (value) => Math.floor(value).toString()
  518. }
  519. },
  520. series: [
  521. {
  522. name: '当日运维成本',
  523. type: 'bar',
  524. itemStyle: {
  525. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#f69606' }])
  526. },
  527. emphasis: {
  528. focus: 'series'
  529. },
  530. data: repairs
  531. }
  532. ]
  533. }
  534. // 初始化图表
  535. chartInstance = echarts.init(chartContainer.value)
  536. chartInstance.setOption(option)
  537. // 窗口缩放监听
  538. window.addEventListener('resize', handleResize)
  539. handleResize()
  540. }
  541. // 自适应调整
  542. const handleResize = () => {
  543. chartInstance?.resize()
  544. }
  545. // 自适应调整
  546. const handleTopResize = () => {
  547. topInstance?.resize()
  548. }
  549. const handleQxResize = () => {
  550. qxInstance?.resize()
  551. }
  552. const handleZqlResize = () => {
  553. zqlInstance?.resize()
  554. }
  555. const handleZqlTodayResize = () => {
  556. zqlTodayInstance?.resize()
  557. }
  558. const handleMaterialResize = () => {
  559. materialInstance?.resize()
  560. }
  561. const topContainer = ref(null)
  562. let topInstance = null
  563. // 响应式容器尺寸
  564. const { width, height } = useElementSize(topContainer)
  565. // 初始化图表配置
  566. const getTopOption = () => {
  567. // backendData.value = data
  568. const data = backendData.value.sort((a, b) => a.value - b.value)
  569. return {
  570. tooltip: {
  571. trigger: 'axis',
  572. axisPointer: { type: 'shadow' },
  573. formatter: (params) => {
  574. const item = params[0]
  575. return `${item.name}<br/>${item.marker} ${item.value.toLocaleString()}`
  576. }
  577. },
  578. grid: {
  579. height: '200px',
  580. left: '6%',
  581. right: '6%',
  582. bottom: '18%',
  583. containLabel: true
  584. },
  585. xAxis: {
  586. type: 'value',
  587. axisLabel: {
  588. formatter: (value) => {
  589. if (value >= 10000) return `${(value / 10000).toFixed(1)}万`
  590. return value.toLocaleString()
  591. }
  592. }
  593. },
  594. yAxis: {
  595. type: 'category',
  596. data: data.map((item) => item.category),
  597. axisTick: { show: false },
  598. axisLabel: {}
  599. },
  600. series: [
  601. {
  602. type: 'bar',
  603. data: data.map((item) => item.value),
  604. itemStyle: {
  605. color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
  606. { offset: 0, color: '#83bff6' },
  607. { offset: 0.7, color: '#188df0' },
  608. { offset: 1, color: '#188df0' }
  609. ]),
  610. borderRadius: [0, 8, 8, 0]
  611. },
  612. label: {
  613. show: true,
  614. position: 'right',
  615. formatter: '{@value}',
  616. color: '#333',
  617. fontWeight: 'bold'
  618. },
  619. emphasis: {
  620. itemStyle: {
  621. shadowBlur: 10,
  622. shadowColor: 'rgba(0, 0, 0, 0.5)'
  623. }
  624. }
  625. }
  626. ]
  627. }
  628. }
  629. // 初始化图表
  630. const initTopChart = async () => {
  631. await IotStatApi.getDeviceTypeCount('rh').then((res) => {
  632. backendData.value = res
  633. })
  634. if (!topContainer.value) return
  635. topInstance = echarts.init(topContainer.value)
  636. updateTopChart()
  637. }
  638. // 更新图表
  639. const updateTopChart = () => {
  640. if (!topInstance) return
  641. topInstance.setOption(getTopOption())
  642. }
  643. // 自适应调整
  644. watch([width, height], () => {
  645. topInstance?.resize()
  646. })
  647. // 监听数据变化
  648. watch(
  649. backendData,
  650. () => {
  651. updateTopChart()
  652. },
  653. { deep: true }
  654. )
  655. const qxRef = ref(null)
  656. let qxInstance = null
  657. let zqlInstance = null
  658. const initZqlChart = () => {
  659. if (!zqlChartRef.value) return
  660. zqlInstance = echarts.init(zqlChartRef.value)
  661. debugger
  662. const option = {
  663. tooltip: {
  664. trigger: 'axis',
  665. axisPointer: {
  666. type: 'cross',
  667. label: {
  668. backgroundColor: '#6a7985'
  669. }
  670. }
  671. },
  672. legend: {
  673. data: zqlData.value.series.map((item) => item.name),
  674. top: 30
  675. },
  676. grid: {
  677. left: '3%',
  678. right: '4%',
  679. bottom: '3%',
  680. containLabel: true
  681. },
  682. xAxis: {
  683. type: 'category',
  684. boundaryGap: false,
  685. data: zqlData.value.xAxis,
  686. axisLabel: {
  687. formatter: (value) => value.split('-').join('/') // 显示为 2023/01
  688. }
  689. },
  690. yAxis: [
  691. {
  692. type: 'value',
  693. axisLabel: {
  694. formatter: '{value}'
  695. },
  696. position: 'left' // 左侧 Y 轴
  697. },
  698. {
  699. type: 'value',
  700. axisLabel: {
  701. formatter: '{value}'
  702. },
  703. position: 'right', // 右侧 Y 轴
  704. splitLine: {
  705. show: false // 隐藏右侧 Y 轴的分割线
  706. }
  707. }
  708. ],
  709. series: zqlData.value.series.map((item, index) => {
  710. // 假设前两条曲线使用左侧 Y 轴,后两条曲线使用右侧 Y 轴
  711. const yAxisIndex = index < 2 ? 0 : 1
  712. return {
  713. name: item.name,
  714. type: 'line',
  715. smooth: true,
  716. symbol: 'circle',
  717. symbolSize: 8,
  718. itemStyle: {
  719. color: ['#5470c6', '#f1d209', '#e14f0f', '#91cc75'][index]
  720. },
  721. areaStyle: {
  722. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  723. { offset: 0, color: 'rgba(84,112,198,0.4)' },
  724. { offset: 1, color: 'rgba(84,112,198,0.1)' }
  725. ])
  726. },
  727. data: item.data,
  728. yAxisIndex: yAxisIndex // 指定使用的 Y 轴
  729. }
  730. })
  731. }
  732. zqlInstance.setOption(option)
  733. // 窗口缩放监听
  734. window.addEventListener('resize', handleZqlResize)
  735. handleZqlResize()
  736. }
  737. const initQxChart = () => {
  738. if (!qxRef.value) return
  739. qxInstance = echarts.init(qxRef.value)
  740. debugger
  741. const option = {
  742. tooltip: {
  743. trigger: 'axis',
  744. axisPointer: {
  745. type: 'cross',
  746. label: {
  747. backgroundColor: '#6a7985'
  748. }
  749. }
  750. },
  751. legend: {
  752. data: orderSevenData.value.series.map((item) => item.name),
  753. top: 30
  754. },
  755. grid: {
  756. left: '3%',
  757. right: '4%',
  758. bottom: '3%',
  759. containLabel: true
  760. },
  761. xAxis: {
  762. type: 'category',
  763. boundaryGap: false,
  764. data: orderSevenData.value.xAxis,
  765. axisLabel: {
  766. formatter: (value) => value.split('-').join('/') // 显示为 2023/01
  767. }
  768. },
  769. yAxis: [
  770. {
  771. type: 'value',
  772. axisLabel: {
  773. formatter: '{value}'
  774. },
  775. position: 'left' // 左侧 Y 轴
  776. },
  777. {
  778. type: 'value',
  779. axisLabel: {
  780. formatter: '{value}'
  781. },
  782. position: 'right', // 右侧 Y 轴
  783. splitLine: {
  784. show: false // 隐藏右侧 Y 轴的分割线
  785. }
  786. }
  787. ],
  788. series: orderSevenData.value.series.map((item, index) => {
  789. // 假设前两条曲线使用左侧 Y 轴,后两条曲线使用右侧 Y 轴
  790. const yAxisIndex = index < 2 ? 0 : 1
  791. return {
  792. name: item.name,
  793. type: 'line',
  794. smooth: true,
  795. symbol: 'circle',
  796. symbolSize: 8,
  797. itemStyle: {
  798. color: ['#5470c6', '#f1d209', '#e14f0f', '#91cc75'][index]
  799. },
  800. areaStyle: {
  801. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  802. { offset: 0, color: 'rgba(84,112,198,0.4)' },
  803. { offset: 1, color: 'rgba(84,112,198,0.1)' }
  804. ])
  805. },
  806. data: item.data,
  807. yAxisIndex: yAxisIndex // 指定使用的 Y 轴
  808. }
  809. })
  810. }
  811. qxInstance.setOption(option)
  812. // 窗口缩放监听
  813. window.addEventListener('resize', handleQxResize)
  814. handleQxResize()
  815. }
  816. // 响应式调整
  817. const resizeQxChart = () => qxInstance?.resize()
  818. const resizeZqlChart = () => zqlInstance?.resize()
  819. const resizeZqlTodayChart = () => zqlTodayInstance?.resize()
  820. const resizeMaterialChart = () => materialInstance?.resize()
  821. /** 初始化 */
  822. onMounted(() => {
  823. getStats()
  824. // initChart()
  825. // initTopChart()
  826. // initActiveChart()
  827. // initQxChart()
  828. window.addEventListener('resize', resizeQxChart)
  829. // fetchTop()
  830. window.addEventListener('resize', () => topInstance?.resize())
  831. })
  832. onBeforeUnmount(() => {
  833. chartInstance?.dispose()
  834. window.removeEventListener('resize', () => chartInstance?.resize())
  835. topInstance?.dispose()
  836. window.removeEventListener('resize', handleResize)
  837. qxInstance?.dispose()
  838. window.removeEventListener('resize', resizeQxChart)
  839. zqlInstance?.dispose()
  840. window.removeEventListener('resize', resizeZqlChart)
  841. zqlTodayInstance?.dispose()
  842. window.removeEventListener('resize', resizeZqlTodayChart)
  843. materialInstance?.dispose()
  844. window.removeEventListener('resize', resizeMaterialChart)
  845. })
  846. </script>
  847. <style lang="scss" scoped></style>