index.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. <template>
  2. <div class="run-screen-container">
  3. <!-- 页面标题 -->
  4. <div class="page-title">
  5. <h1>盛世鹰眸质控系统[生产运行](安检站)</h1>
  6. </div>
  7. <!-- 模块一:运行数据 -->
  8. <div class="module-title-wrapper">
  9. <div class="module-title">模块一:运行数据</div>
  10. </div>
  11. <!-- 第一行 -->
  12. <el-row :gutter="20" class="row-margin">
  13. <!-- 左边50%:两个div -->
  14. <el-col :span="12">
  15. <el-row :gutter="20" style="height: 100%;">
  16. <el-col :span="12">
  17. <div class="data-card">
  18. <div class="card-title">旅检过检总人数</div>
  19. <div class="card-value-wrapper" style="color: #E8B140;">
  20. <span class="card-value">{{ runData.passengerTotal }}</span>
  21. <span class="card-unit">人</span>
  22. </div>
  23. </div>
  24. </el-col>
  25. <el-col :span="12">
  26. <div class="data-card">
  27. <div class="card-title">行检过检总行李数</div>
  28. <div class="card-value-wrapper" style="color: #26B6BE;">
  29. <span class="card-value">{{ runData.luggageTotal }}</span>
  30. <span class="card-unit">件</span>
  31. </div>
  32. </div>
  33. </el-col>
  34. </el-row>
  35. </el-col>
  36. <!-- 右边50%:T1旅检过检人数折线图 -->
  37. <el-col :span="12">
  38. <div class="chart-card">
  39. <div class="chart-title">T1旅检过检人数</div>
  40. <div ref="t1TravelChart" class="chart-container"></div>
  41. </div>
  42. </el-col>
  43. </el-row>
  44. <!-- 第二行 -->
  45. <el-row :gutter="20" class="row-margin">
  46. <el-col :span="12">
  47. <div class="chart-card">
  48. <div class="chart-title">T2旅检过检人数</div>
  49. <div ref="t2TravelChart" class="chart-container"></div>
  50. </div>
  51. </el-col>
  52. <el-col :span="12">
  53. <div class="chart-card">
  54. <div class="chart-title">行检过检数</div>
  55. <div ref="luggageChart" class="chart-container"></div>
  56. </div>
  57. </el-col>
  58. </el-row>
  59. <!-- 第三行 -->
  60. <el-row :gutter="20" class="row-margin">
  61. <el-col :span="8">
  62. <div class="data-card">
  63. <div class="card-title">国内货站总过检数</div>
  64. <div class="card-value-wrapper" style="color: #E8B140;padding: 30px;">
  65. <span class="card-value">{{ runData.domesticCargoTotal }}</span>
  66. <span class="card-unit">件</span>
  67. </div>
  68. </div>
  69. </el-col>
  70. <el-col :span="8">
  71. <div class="data-card">
  72. <div class="card-title">国际货站总过检数</div>
  73. <div class="card-value-wrapper" style="color: #26B6BE;padding: 30px;">
  74. <span class="card-value">{{ runData.intlCargoTotal }}</span>
  75. <span class="card-unit">件</span>
  76. </div>
  77. </div>
  78. </el-col>
  79. <el-col :span="8">
  80. <div class="data-card">
  81. <div class="card-title">道口车辆过检数</div>
  82. <div class="card-value-wrapper" style="color: #5680C9;padding: 30px;">
  83. <span class="card-value">{{ runData.vehicleCheckTotal }}</span>
  84. <span class="card-unit">辆</span>
  85. </div>
  86. </div>
  87. </el-col>
  88. </el-row>
  89. <!-- 第四行 -->
  90. <el-row :gutter="20" class="row-margin">
  91. <el-col :span="12">
  92. <div class="chart-card">
  93. <div class="chart-title">货物过检数</div>
  94. <div ref="cargoChart" class="chart-container"></div>
  95. </div>
  96. </el-col>
  97. <el-col :span="12">
  98. <div class="chart-card">
  99. <div class="chart-title">车辆过检数</div>
  100. <div ref="vehicleChart" class="chart-container"></div>
  101. </div>
  102. </el-col>
  103. </el-row>
  104. <!-- 模块二:查获/收缴数据 -->
  105. <div class="module-title-wrapper">
  106. <div class="module-title">模块二:查获/收缴数据</div>
  107. </div>
  108. <!-- 第一行 -->
  109. <el-row :gutter="20" class="row-margin">
  110. <el-col :span="12">
  111. <div class="chart-card">
  112. <div class="chart-title">查获数据</div>
  113. <div ref="seizePieChart" class="chart-container"></div>
  114. </div>
  115. </el-col>
  116. <el-col :span="12">
  117. <div class="chart-card">
  118. <div class="chart-title">大队查获数对比图</div>
  119. <div ref="teamSeizeBarChart" class="chart-container"></div>
  120. </div>
  121. </el-col>
  122. </el-row>
  123. <!-- 第二行 -->
  124. <el-row :gutter="20" class="row-margin">
  125. <el-col :span="12">
  126. <div class="chart-card">
  127. <div class="chart-title">T1区域各大队查获数对比图</div>
  128. <div ref="t1TeamBarChart" class="chart-container"></div>
  129. </div>
  130. </el-col>
  131. <el-col :span="12">
  132. <div class="chart-card">
  133. <div class="chart-title">T2区域各大队查获数对比图</div>
  134. <div ref="t2TeamBarChart" class="chart-container"></div>
  135. </div>
  136. </el-col>
  137. </el-row>
  138. <!-- 第三行 -->
  139. <el-row :gutter="20" class="row-margin">
  140. <el-col :span="12">
  141. <div class="chart-card">
  142. <div class="chart-title">不合格充电宝劝阻数组</div>
  143. <div ref="powerBankPieChart" class="chart-container"></div>
  144. </div>
  145. </el-col>
  146. <el-col :span="12">
  147. <div class="chart-card">
  148. <div class="chart-title">待检区收缴禁限带物品数据表</div>
  149. <div ref="waitingAreaPieChart" class="chart-container"></div>
  150. </div>
  151. </el-col>
  152. </el-row>
  153. </div>
  154. </template>
  155. <script setup name="RunScreen">
  156. import { ref, onMounted, onUnmounted, nextTick } from 'vue'
  157. import * as echarts from 'echarts'
  158. import {
  159. getOperationSummary,
  160. getT1PassengerTrend,
  161. getT2PassengerTrend,
  162. getLuggageCheckTrend,
  163. getCargoTrend,
  164. getVehicleCheckTrend,
  165. getSeizureCategory,
  166. getBrigadeSeizureCompare,
  167. getPowerBankSummary,
  168. getPendingConfiscate
  169. } from '@/api/runData/runScreen.js'
  170. // 图表引用
  171. const t1TravelChart = ref(null)
  172. const t2TravelChart = ref(null)
  173. const luggageChart = ref(null)
  174. const cargoChart = ref(null)
  175. const vehicleChart = ref(null)
  176. const seizePieChart = ref(null)
  177. const teamSeizeBarChart = ref(null)
  178. const t1TeamBarChart = ref(null)
  179. const t2TeamBarChart = ref(null)
  180. const powerBankPieChart = ref(null)
  181. const waitingAreaPieChart = ref(null)
  182. // 图表实例
  183. let t1TravelChartInstance = null
  184. let t2TravelChartInstance = null
  185. let luggageChartInstance = null
  186. let cargoChartInstance = null
  187. let vehicleChartInstance = null
  188. let seizePieChartInstance = null
  189. let teamSeizeBarChartInstance = null
  190. let t1TeamBarChartInstance = null
  191. let t2TeamBarChartInstance = null
  192. let powerBankPieChartInstance = null
  193. let waitingAreaPieChartInstance = null
  194. // 运行数据
  195. const runData = ref({
  196. travelInspectionTotal: 0,
  197. luggageInspectionTotal: 0,
  198. domesticCargoTotal: 0,
  199. internationalCargoTotal: 0,
  200. vehicleInspectionTotal: 0
  201. })
  202. const t1PassengerData = ref([])
  203. const t2PassengerData = ref([])
  204. const luggageCheckData = ref([])
  205. const cargoData = ref([])
  206. const vehicleCheckData = ref([])
  207. const seizureCategoryData = ref([])
  208. const brigadeSeizureData = ref([])
  209. const powerBankData = ref({})
  210. const pendingConfiscateData = ref({})
  211. async function loadData() {
  212. try {
  213. const query = {}
  214. const [
  215. summaryRes,
  216. t1PassengerRes,
  217. t2PassengerRes,
  218. luggageRes,
  219. cargoRes,
  220. vehicleRes,
  221. seizureRes,
  222. brigadeRes,
  223. powerBankRes,
  224. pendingRes
  225. ] = await Promise.all([
  226. getOperationSummary(query),
  227. getT1PassengerTrend(query),
  228. getT2PassengerTrend(query),
  229. getLuggageCheckTrend(query),
  230. getCargoTrend(query),
  231. getVehicleCheckTrend(query),
  232. getSeizureCategory(query),
  233. getBrigadeSeizureCompare(query),
  234. getPowerBankSummary(query),
  235. getPendingConfiscate(query)
  236. ])
  237. if (summaryRes.data) {
  238. runData.value = summaryRes.data
  239. }
  240. if (t1PassengerRes.data) {
  241. t1PassengerData.value = t1PassengerRes.data
  242. }
  243. if (t2PassengerRes.data) {
  244. t2PassengerData.value = t2PassengerRes.data
  245. }
  246. if (luggageRes.data) {
  247. luggageCheckData.value = luggageRes.data
  248. }
  249. if (cargoRes.data) {
  250. cargoData.value = cargoRes.data
  251. }
  252. if (vehicleRes.data) {
  253. vehicleCheckData.value = vehicleRes.data
  254. }
  255. if (seizureRes.data) {
  256. seizureCategoryData.value = seizureRes.data
  257. }
  258. if (brigadeRes.data) {
  259. brigadeSeizureData.value = brigadeRes.data
  260. }
  261. if (powerBankRes.data) {
  262. powerBankData.value = powerBankRes.data
  263. }
  264. if (pendingRes.data) {
  265. pendingConfiscateData.value = pendingRes.data
  266. }
  267. nextTick(() => {
  268. initCharts()
  269. })
  270. } catch (error) {
  271. console.error('加载数据失败:', error)
  272. }
  273. }
  274. // 初始化所有图表
  275. function initCharts() {
  276. // T1旅检过检人数折线图
  277. if (t1TravelChart.value) {
  278. t1TravelChartInstance = echarts.init(t1TravelChart.value)
  279. const t1Data = t1PassengerData.value || []
  280. t1TravelChartInstance.setOption({
  281. tooltip: { trigger: 'axis' },
  282. legend: { data: ['A区', 'B区'], top: 30 },
  283. xAxis: {
  284. type: 'category',
  285. data: t1Data.map(item => item.recordDate || item.azone || '')
  286. },
  287. yAxis: { type: 'value' },
  288. series: [
  289. {
  290. name: 'A区',
  291. data: t1Data.map(item => item.azone || 0),
  292. type: 'line',
  293. smooth: true,
  294. lineStyle: { color: '#E8B140' },
  295. areaStyle: { color: 'rgba(232, 177, 64, 0.2)' }
  296. },
  297. {
  298. name: 'B区',
  299. data: t1Data.map(item => item.bzone || 0),
  300. type: 'line',
  301. smooth: true,
  302. lineStyle: { color: '#5470c6' },
  303. areaStyle: { color: 'rgba(84, 112, 198, 0.2)' }
  304. }
  305. ]
  306. })
  307. }
  308. // T2旅检过检人数折线图
  309. if (t2TravelChart.value) {
  310. t2TravelChartInstance = echarts.init(t2TravelChart.value)
  311. const t2Data = t2PassengerData.value || []
  312. t2TravelChartInstance.setOption({
  313. tooltip: { trigger: 'axis' },
  314. legend: { data: ['国内旅检', '国际旅检'], top: 30 },
  315. xAxis: {
  316. type: 'category',
  317. data: t2Data.map(item => item.recordDate || '')
  318. },
  319. yAxis: { type: 'value' },
  320. series: [
  321. {
  322. name: '国内旅检',
  323. data: t2Data.map(item => item.domesticPassenger || 0),
  324. type: 'line',
  325. smooth: true,
  326. lineStyle: { color: '#91cc75' },
  327. areaStyle: { color: 'rgba(145, 204, 117, 0.2)' }
  328. },
  329. {
  330. name: '国际旅检',
  331. data: t2Data.map(item => item.intlPassenger || 0),
  332. type: 'line',
  333. smooth: true,
  334. lineStyle: { color: '#fac858' },
  335. areaStyle: { color: 'rgba(250, 200, 88, 0.2)' }
  336. }
  337. ]
  338. })
  339. }
  340. // 行检过检数折线图
  341. if (luggageChart.value) {
  342. luggageChartInstance = echarts.init(luggageChart.value)
  343. const luggageData = luggageCheckData.value || []
  344. luggageChartInstance.setOption({
  345. tooltip: { trigger: 'axis' },
  346. legend: { data: ['T1行检', 'T2行检'], top: 30 },
  347. xAxis: {
  348. type: 'category',
  349. data: luggageData.map(item => item.recordDate || '')
  350. },
  351. yAxis: { type: 'value' },
  352. series: [
  353. {
  354. name: 'T1行检',
  355. data: luggageData.map(item => item.t1LuggageCheck || 0),
  356. type: 'line',
  357. smooth: true,
  358. lineStyle: { color: '#ee6666' },
  359. areaStyle: { color: 'rgba(238, 102, 102, 0.2)' }
  360. },
  361. {
  362. name: 'T2行检',
  363. data: luggageData.map(item => item.t2LuggageCheck || 0),
  364. type: 'line',
  365. smooth: true,
  366. lineStyle: { color: '#73c0de' },
  367. areaStyle: { color: 'rgba(115, 192, 222, 0.2)' }
  368. }
  369. ]
  370. })
  371. }
  372. // 货物过检数折线图
  373. if (cargoChart.value) {
  374. cargoChartInstance = echarts.init(cargoChart.value)
  375. const cargoTrendData = cargoData.value || []
  376. cargoChartInstance.setOption({
  377. tooltip: { trigger: 'axis' },
  378. legend: { data: ['国内货站', '国际货站'], top: 30 },
  379. xAxis: {
  380. type: 'category',
  381. data: cargoTrendData.map(item => item.recordDate || '')
  382. },
  383. yAxis: { type: 'value' },
  384. series: [
  385. {
  386. name: '国内货站',
  387. data: cargoTrendData.map(item => item.domesticCargo || 0),
  388. type: 'line',
  389. smooth: true,
  390. lineStyle: { color: '#E8B140' },
  391. areaStyle: { color: 'rgba(232, 177, 64, 0.2)' }
  392. },
  393. {
  394. name: '国际货站',
  395. data: cargoTrendData.map(item => item.intlCargo || 0),
  396. type: 'line',
  397. smooth: true,
  398. lineStyle: { color: '#26B6BE' },
  399. areaStyle: { color: 'rgba(38, 182, 190, 0.2)' }
  400. }
  401. ]
  402. })
  403. }
  404. // 车辆过检数折线图
  405. if (vehicleChart.value) {
  406. vehicleChartInstance = echarts.init(vehicleChart.value)
  407. const vehicleData = vehicleCheckData.value || []
  408. vehicleChartInstance.setOption({
  409. tooltip: { trigger: 'axis' },
  410. legend: { data: ['南侧车检', '北侧车检'], top: 30 },
  411. xAxis: {
  412. type: 'category',
  413. data: vehicleData.map(item => item.recordDate || '')
  414. },
  415. yAxis: { type: 'value' },
  416. series: [
  417. {
  418. name: '南侧车检',
  419. data: vehicleData.map(item => item.southVehicleCheck || 0),
  420. type: 'line',
  421. smooth: true,
  422. lineStyle: { color: '#E8B140' },
  423. areaStyle: { color: 'rgba(232, 177, 64, 0.2)' }
  424. },
  425. {
  426. name: '北侧车检',
  427. data: vehicleData.map(item => item.northVehicleCheck || 0),
  428. type: 'line',
  429. smooth: true,
  430. lineStyle: { color: '#5470c6' },
  431. areaStyle: { color: 'rgba(84, 112, 198, 0.2)' }
  432. }
  433. ]
  434. })
  435. }
  436. // 查获数据环形图
  437. if (seizePieChart.value) {
  438. seizePieChartInstance = echarts.init(seizePieChart.value)
  439. const seizureData = seizureCategoryData.value || []
  440. seizePieChartInstance.setOption({
  441. tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
  442. legend: { orient: 'vertical', right: 10, top: 'center' },
  443. series: [{
  444. name: '查获数据',
  445. type: 'pie',
  446. radius: ['40%', '70%'],
  447. center: ['40%', '50%'],
  448. data: seizureData.map(item => ({ name: item.name, value: item.total })),
  449. emphasis: {
  450. itemStyle: {
  451. shadowBlur: 10,
  452. shadowOffsetX: 0,
  453. shadowColor: 'rgba(0, 0, 0, 0.5)'
  454. }
  455. },
  456. label: {
  457. formatter: '{b}: {c} ({d}%)'
  458. }
  459. }]
  460. })
  461. }
  462. // 大队查获数对比柱状图
  463. if (teamSeizeBarChart.value) {
  464. teamSeizeBarChartInstance = echarts.init(teamSeizeBarChart.value)
  465. const brigadeData = brigadeSeizureData.value || []
  466. const shifts = [...new Set(brigadeData.map(item => item.shift))]
  467. const brigades = [...new Set(brigadeData.map(item => item.brigade))]
  468. const series = brigades.map(brigade => ({
  469. name: brigade,
  470. data: shifts.map(shift => {
  471. const item = brigadeData.find(d => d.shift === shift && d.brigade === brigade)
  472. return item ? item.grandTotal : 0
  473. }),
  474. type: 'bar',
  475. label: {
  476. show: true,
  477. position: 'top',
  478. formatter: '{c}'
  479. }
  480. }))
  481. teamSeizeBarChartInstance.setOption({
  482. tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
  483. legend: { data: brigades, top: 30 },
  484. xAxis: {
  485. type: 'category',
  486. data: shifts
  487. },
  488. yAxis: { type: 'value' },
  489. series: series
  490. })
  491. }
  492. // T1区域各大队查获数对比柱状图
  493. if (t1TeamBarChart.value) {
  494. t1TeamBarChartInstance = echarts.init(t1TeamBarChart.value)
  495. const brigadeData = brigadeSeizureData.value || []
  496. const shifts = [...new Set(brigadeData.map(item => item.shift))]
  497. const brigades = [...new Set(brigadeData.map(item => item.brigade))]
  498. const series = brigades.map(brigade => ({
  499. name: brigade,
  500. data: shifts.map(shift => {
  501. const item = brigadeData.find(d => d.shift === shift && d.brigade === brigade)
  502. return item ? item.t1Total : 0
  503. }),
  504. type: 'bar',
  505. label: {
  506. show: true,
  507. position: 'top',
  508. formatter: '{c}'
  509. }
  510. }))
  511. t1TeamBarChartInstance.setOption({
  512. tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
  513. legend: { data: brigades, top: 30 },
  514. xAxis: {
  515. type: 'category',
  516. data: shifts
  517. },
  518. yAxis: { type: 'value' },
  519. series: series
  520. })
  521. }
  522. // T2区域各大队查获数对比柱状图
  523. if (t2TeamBarChart.value) {
  524. t2TeamBarChartInstance = echarts.init(t2TeamBarChart.value)
  525. const brigadeData = brigadeSeizureData.value || []
  526. const shifts = [...new Set(brigadeData.map(item => item.shift))]
  527. const brigades = [...new Set(brigadeData.map(item => item.brigade))]
  528. const series = brigades.map(brigade => ({
  529. name: brigade,
  530. data: shifts.map(shift => {
  531. const item = brigadeData.find(d => d.shift === shift && d.brigade === brigade)
  532. return item ? item.t2Total : 0
  533. }),
  534. type: 'bar',
  535. label: {
  536. show: true,
  537. position: 'top',
  538. formatter: '{c}'
  539. }
  540. }))
  541. t2TeamBarChartInstance.setOption({
  542. tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
  543. legend: { data: brigades, top: 30 },
  544. xAxis: {
  545. type: 'category',
  546. data: shifts
  547. },
  548. yAxis: { type: 'value' },
  549. series: series
  550. })
  551. }
  552. // 不合格充电宝劝阻数组环形图
  553. if (powerBankPieChart.value) {
  554. powerBankPieChartInstance = echarts.init(powerBankPieChart.value)
  555. const pbData = powerBankData.value || {}
  556. const powerBankChartData = [
  557. { name: '邮寄', value: pbData.mailTotal || 0 },
  558. { name: '暂存', value: pbData.tempStoreTotal || 0 },
  559. { name: '自弃', value: pbData.abandonTotal || 0 },
  560. { name: '召回', value: pbData.recallTotal || 0 },
  561. { name: '无3C标识', value: pbData.no3cTotal || 0 },
  562. { name: '标识不清', value: pbData.unclearMarkTotal || 0 },
  563. { name: '超规数量', value: pbData.excessQtyTotal || 0 },
  564. { name: '其他', value: pbData.bothTotal || 0 }
  565. ].filter(item => item.value > 0)
  566. powerBankPieChartInstance.setOption({
  567. tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
  568. legend: { orient: 'vertical', right: 10, top: 'center' },
  569. series: [{
  570. name: '不合格充电宝',
  571. type: 'pie',
  572. radius: ['40%', '70%'],
  573. center: ['40%', '50%'],
  574. data: powerBankChartData,
  575. emphasis: {
  576. itemStyle: {
  577. shadowBlur: 10,
  578. shadowOffsetX: 0,
  579. shadowColor: 'rgba(0, 0, 0, 0.5)'
  580. }
  581. },
  582. label: {
  583. formatter: '{b}: {c} ({d}%)'
  584. }
  585. }]
  586. })
  587. }
  588. // 待检区收缴禁限带物品数据表环形图
  589. if (waitingAreaPieChart.value) {
  590. waitingAreaPieChartInstance = echarts.init(waitingAreaPieChart.value)
  591. const pendingData = pendingConfiscateData.value || {}
  592. const pendingChartData = [
  593. { name: '火种', value: pendingData.fireSourceTotal || 0 },
  594. { name: '液态物品', value: pendingData.liquidTotal || 0 },
  595. { name: '其他', value: pendingData.otherTotal || 0 }
  596. ].filter(item => item.value > 0)
  597. waitingAreaPieChartInstance.setOption({
  598. tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
  599. legend: { orient: 'vertical', right: 10, top: 'center' },
  600. series: [{
  601. name: '禁限带物品',
  602. type: 'pie',
  603. radius: ['40%', '70%'],
  604. center: ['40%', '50%'],
  605. data: pendingChartData,
  606. emphasis: {
  607. itemStyle: {
  608. shadowBlur: 10,
  609. shadowOffsetX: 0,
  610. shadowColor: 'rgba(0, 0, 0, 0.5)'
  611. }
  612. },
  613. label: {
  614. formatter: '{b}: {c} ({d}%)'
  615. }
  616. }]
  617. })
  618. }
  619. }
  620. // 窗口大小变化时重新调整图表大小
  621. function handleResize() {
  622. const charts = [
  623. t1TravelChartInstance, t2TravelChartInstance, luggageChartInstance,
  624. cargoChartInstance, vehicleChartInstance, seizePieChartInstance,
  625. teamSeizeBarChartInstance, t1TeamBarChartInstance, t2TeamBarChartInstance,
  626. powerBankPieChartInstance, waitingAreaPieChartInstance
  627. ]
  628. charts.forEach(chart => {
  629. if (chart) {
  630. chart.resize()
  631. }
  632. })
  633. }
  634. // 组件挂载时初始化图表
  635. onMounted(() => {
  636. loadData()
  637. window.addEventListener('resize', handleResize)
  638. })
  639. // 组件卸载时销毁图表
  640. onUnmounted(() => {
  641. const charts = [
  642. t1TravelChartInstance, t2TravelChartInstance, luggageChartInstance,
  643. cargoChartInstance, vehicleChartInstance, seizePieChartInstance,
  644. teamSeizeBarChartInstance, t1TeamBarChartInstance, t2TeamBarChartInstance,
  645. powerBankPieChartInstance, waitingAreaPieChartInstance
  646. ]
  647. charts.forEach(chart => {
  648. if (chart) {
  649. chart.dispose()
  650. }
  651. })
  652. window.removeEventListener('resize', handleResize)
  653. })
  654. </script>
  655. <style scoped>
  656. .run-screen-container {
  657. padding: 20px;
  658. background: #f0f2f5;
  659. min-height: 100vh;
  660. }
  661. .page-title {
  662. text-align: center;
  663. margin-bottom: 30px;
  664. }
  665. .page-title h1 {
  666. font-size: 28px;
  667. color: #333;
  668. font-weight: bold;
  669. margin: 0;
  670. }
  671. .module-title-wrapper {
  672. text-align: center;
  673. width: 100%;
  674. background: #fff;
  675. border-radius: 8px;
  676. padding: 15px 20px;
  677. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  678. margin-bottom: 20px;
  679. }
  680. .module-title {
  681. font-size: 20px;
  682. font-weight: bold;
  683. color: #333;
  684. margin: 0;
  685. }
  686. .module-section {
  687. width: 100%;
  688. background: #fff;
  689. border-radius: 8px;
  690. padding: 20px;
  691. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  692. margin-bottom: 20px;
  693. }
  694. .row-margin {
  695. margin-bottom: 20px;
  696. }
  697. .data-card {
  698. background: #fff;
  699. border-radius: 8px;
  700. padding: 15px;
  701. color: #333;
  702. height: 100%;
  703. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  704. border: 1px solid #e6e8eb;
  705. }
  706. .card-title {
  707. font-size: 16px;
  708. color: #333;
  709. font-weight: bold;
  710. text-align: left;
  711. }
  712. .card-value-wrapper {
  713. height: 100%;
  714. display: flex;
  715. flex-direction: column;
  716. align-items: center;
  717. justify-content: center;
  718. }
  719. .card-value {
  720. font-size: 50px;
  721. font-weight: bold;
  722. }
  723. .card-unit {
  724. font-size: 16px;
  725. }
  726. .chart-card {
  727. background: #fff;
  728. border-radius: 8px;
  729. padding: 15px;
  730. border: 1px solid #e6e8eb;
  731. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  732. height: 300px;
  733. }
  734. .chart-title {
  735. font-size: 16px;
  736. font-weight: bold;
  737. color: #333;
  738. margin-bottom: 15px;
  739. text-align: left;
  740. }
  741. .chart-container {
  742. width: 100%;
  743. height: calc(100% - 30px);
  744. }
  745. /* 响应式设计 */
  746. @media (max-width: 1200px) {
  747. .data-card {
  748. height: 100px;
  749. }
  750. .card-value {
  751. font-size: 30px;
  752. }
  753. .chart-card {
  754. height: 250px;
  755. }
  756. }
  757. @media (max-width: 768px) {
  758. .run-screen-container {
  759. padding: 10px;
  760. }
  761. .module-section {
  762. padding: 15px;
  763. }
  764. .data-card {
  765. height: 80px;
  766. padding: 15px;
  767. }
  768. .card-title {
  769. font-size: 14px;
  770. }
  771. .card-value {
  772. font-size: 20px;
  773. }
  774. .chart-card {
  775. height: 200px;
  776. }
  777. }
  778. </style>