index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. <template>
  2. <div class="dustMonitor">
  3. <customHeader>色谱仪报表分析</customHeader>
  4. <div class="content-container">
  5. <div class="file-list">
  6. <ul>
  7. <li v-for="item in selectList" :key="item.id" :class="{ selected: item.id === selectedFileId }" @click="handleFileClick(item)">
  8. {{ item.fileName }}
  9. </li>
  10. </ul>
  11. </div>
  12. <div class="table-container">
  13. <a-table :columns="computedColumns" :data-source="tableData" size="small" :pagination="false" :scroll="{ y: 300 }" class="tableW">
  14. <template #bodyCell="{ column, record }">
  15. <template v-if="column.dataIndex === 'action'">
  16. <a class="action-link" @click="toDetail(record)">数据分析</a>
  17. </template>
  18. </template>
  19. </a-table>
  20. <div class="data-container">
  21. <div id="lineChart" class="line-chart"></div>
  22. <div class="data-content">
  23. <div class="title">煤自燃阶段统计分析</div>
  24. <div class="explain">测点共计{{ total }}个</div>
  25. <div class="progress-label">潜伏期阶段:{{ qfqCount }}</div>
  26. <Progress :percent="qfqPercent" size="default" strokeColor="green" :show-info="true" :format="() => qfqCount" />
  27. <div class="progress-label">缓慢氧化升温阶段:{{ latentCount }}</div>
  28. <Progress :percent="latentPercent" size="default" strokeColor="yellow" :show-info="true" :format="() => latentCount" />
  29. <div class="progress-label">加速氧化升温阶段:{{ selfHeatingCount }}</div>
  30. <Progress :percent="selfHeatingPercent" size="default" strokeColor="orange‌" :show-info="true" :format="() => selfHeatingCount" />
  31. <div class="progress-label">剧烈氧化升温阶段:{{ combustionCount }}</div>
  32. <Progress :percent="combustionPercent" size="default" strokeColor="red" :show-info="true" :format="() => combustionCount" />
  33. </div>
  34. </div>
  35. </div>
  36. </div>
  37. <a-modal style="width: 600px; height: 300px" title="爆炸三角形" v-model:visible="modalVisible" :draggable="true" :footer="null">
  38. <div class="blast-delta-container">
  39. <BlastDelta :posMonitor="posMonitor" />
  40. </div>
  41. </a-modal>
  42. </div>
  43. </template>
  44. <script setup lang="ts">
  45. import { ref, onMounted, computed, reactive, shallowRef } from 'vue';
  46. import { columns, Hjtcolumns } from './bundleSpy-table.data';
  47. import { getbundleSpyInfoList, getAllFileList, getAllFileListById } from './bundleSpy-table.api';
  48. import customHeader from '/@/components/vent/customHeader.vue';
  49. import * as echarts from 'echarts';
  50. import BlastDelta from './modal/blastDelta.vue';
  51. import { Progress } from 'ant-design-vue';
  52. import { useGlobSetting } from '/@/hooks/setting';
  53. // import 'ant-design-vue/dist/antd.css'; // 引入样式
  54. let selectList = ref<any[]>([]);
  55. let formSearch = reactive({
  56. pageNum: 1,
  57. pageSize: 1000,
  58. id: '',
  59. fileName: '',
  60. });
  61. const total = ref(0);
  62. const { sysOrgCode } = useGlobSetting();
  63. const qfqCount = ref(0); // 潜伏期
  64. const latentCount = ref(0); // 缓慢氧化阶段(潜伏期)
  65. const selfHeatingCount = ref(0); // 加速氧化阶段(自热期)
  66. const combustionCount = ref(0); // 剧烈氧化阶段(燃烧期)
  67. const qfqPercent = ref(0); // 潜伏期(潜伏期)
  68. const latentPercent = ref(0); // 缓慢氧化阶段(潜伏期)
  69. const selfHeatingPercent = ref(0); // 加速氧化阶段(自热期)
  70. const combustionPercent = ref(0); // 剧烈氧化阶段(燃烧期)
  71. let tableData = ref<any[]>([]);
  72. let selectedFileId = ref<string | null>(null);
  73. let modalVisible = ref(false);
  74. const posMonitor = shallowRef({});
  75. const computedColumns = computed(() => {
  76. switch (sysOrgCode) {
  77. case 'sdmtjtdltmkhjtj':
  78. return Hjtcolumns; // 活鸡兔对应的列配置
  79. default:
  80. return columns; // 默认情况下返回的列配置
  81. }
  82. });
  83. //获取色谱仪报表
  84. async function getTableList(params: any) {
  85. let res = await getbundleSpyInfoList({ type: 'bundleSpy', ...params });
  86. const content = res.content;
  87. let contentArr = JSON.parse(content);
  88. // const contentNewArr = computed(() => {
  89. // return contentArr.map((item) => {
  90. // let internalFireWarnLevel = '';
  91. // const co = item.co_ave;
  92. // const co2 = item.co2_ave;
  93. // const c2h4 = item.c2h4_ave;
  94. // const c2h2 = item.c2h2_ave;
  95. // const coRatio = co / co2;
  96. // if (co >= 0 && co <= 13.75) {
  97. // internalFireWarnLevel = '潜伏期阶段';
  98. // } else if (co > 13.75 && co < 67.2 && coRatio < 0.095) {
  99. // internalFireWarnLevel = '缓慢氧化升温阶段';
  100. // } else if ((co >= 67.2 && co < 1606.3) || (coRatio >= 0.095 && coRatio < 0.322) || c2h4 < 2) {
  101. // internalFireWarnLevel = '加速氧化阶段';
  102. // } else if (co >= 1606.3 || coRatio >= 0.322 || c2h4 >= 2 || c2h2 > 0) {
  103. // internalFireWarnLevel = '剧烈氧化阶段';
  104. // }
  105. // return { ...item, internalFireWarnLevel };
  106. // });
  107. // });
  108. total.value = contentArr.length;
  109. qfqCount.value = contentArr.filter((item: any) => item.internalFireWarnLevel === '潜伏期阶段').length;
  110. latentCount.value = contentArr.filter((item: any) => item.internalFireWarnLevel === '缓慢氧化升温阶段').length;
  111. selfHeatingCount.value = contentArr.filter((item: any) => item.internalFireWarnLevel === '加速氧化升温阶段').length;
  112. combustionCount.value = contentArr.filter((item: any) => item.internalFireWarnLevel === '剧烈氧化升温阶段').length;
  113. qfqPercent.value = (qfqCount.value / total.value) * 100;
  114. latentPercent.value = (latentCount.value / total.value) * 100;
  115. selfHeatingPercent.value = (selfHeatingCount.value / total.value) * 100;
  116. combustionPercent.value = (combustionCount.value / total.value) * 100;
  117. tableData.value = contentArr;
  118. updateChart(contentArr);
  119. }
  120. async function getTableListById(params: any) {
  121. let res = await getAllFileListById({ ...params });
  122. const content = res.content;
  123. let contentArr = JSON.parse(content);
  124. total.value = contentArr.length;
  125. qfqCount.value = contentArr.filter((item: any) => item.internalFireWarnLevel === '潜伏期阶段').length;
  126. latentCount.value = contentArr.filter((item: any) => item.internalFireWarnLevel === '缓慢氧化升温阶段').length;
  127. selfHeatingCount.value = contentArr.filter((item: any) => item.internalFireWarnLevel === '加速氧化升温阶段').length;
  128. combustionCount.value = contentArr.filter((item: any) => item.internalFireWarnLevel === '剧烈氧化升温阶段').length;
  129. qfqPercent.value = (qfqCount.value / total.value) * 100;
  130. latentPercent.value = (latentCount.value / total.value) * 100;
  131. selfHeatingPercent.value = (selfHeatingCount.value / total.value) * 100;
  132. combustionPercent.value = (combustionCount.value / total.value) * 100;
  133. tableData.value = contentArr;
  134. updateChart(contentArr);
  135. }
  136. //跳转到爆炸三角形
  137. function toDetail(record: any) {
  138. posMonitor.value = record;
  139. console.log(posMonitor.value);
  140. modalVisible.value = true;
  141. }
  142. //折线图
  143. function updateChart(data: any) {
  144. const chartDom = document.getElementById('lineChart');
  145. const myChart = echarts.init(chartDom);
  146. const categories = data.map((item: any) => item.jcdd);
  147. const c2h2AveValues = data.map((item: any) => parseFloat(item.c2h2_ave));
  148. const c2h4AveValues = data.map((item: any) => parseFloat(item.c2h4_ave));
  149. const ch4AveValues = data.map((item: any) => parseFloat(item.ch4_ave));
  150. const co2AveValues = data.map((item: any) => parseFloat(item.co2_ave));
  151. const coAveValues = data.map((item: any) => parseFloat(item.co_ave));
  152. const o2AveValues = data.map((item: any) => parseFloat(item.o2_ave));
  153. const n2AveValues = data.map((item: any) => parseFloat(item.n2_ave));
  154. const c2h6AveValues = data.map((item: any) => parseFloat(item.c2h6_ave));
  155. const ch4AveBqValues = data.map((item: any) => parseFloat(item.ch4_ave_bq));
  156. const o2AveBqValues = data.map((item: any) => parseFloat(item.o2_ave_bq));
  157. const getSeriesConfig = (sysOrgCode) => {
  158. switch (sysOrgCode) {
  159. case 'sdmtjtdltmkhjtj':
  160. return [
  161. {
  162. name: 'CH₄闭内',
  163. data: ch4AveValues,
  164. yAxisIndex: 1,
  165. type: 'bar',
  166. },
  167. {
  168. name: 'O₂闭内',
  169. data: ch4AveValues,
  170. yAxisIndex: 1,
  171. type: 'bar',
  172. },
  173. {
  174. name: 'CO₂闭内',
  175. data: co2AveValues,
  176. yAxisIndex: 1,
  177. type: 'bar',
  178. },
  179. {
  180. name: 'CO闭内',
  181. data: coAveValues,
  182. yAxisIndex: 1,
  183. type: 'bar',
  184. },
  185. {
  186. name: 'CH₄闭前',
  187. data: ch4AveBqValues,
  188. yAxisIndex: 1,
  189. type: 'bar',
  190. },
  191. {
  192. name: 'O₂闭前',
  193. data: o2AveBqValues,
  194. yAxisIndex: 1,
  195. type: 'bar',
  196. },
  197. ];
  198. default:
  199. return [
  200. {
  201. name: 'C₂H₂平均值',
  202. data: c2h2AveValues,
  203. type: 'bar',
  204. yAxisIndex: 1,
  205. },
  206. {
  207. name: 'C₂H₄平均值',
  208. data: c2h4AveValues,
  209. type: 'bar',
  210. yAxisIndex: 1,
  211. },
  212. {
  213. name: 'CH₄平均值',
  214. data: ch4AveValues,
  215. yAxisIndex: 1,
  216. type: 'bar',
  217. },
  218. {
  219. name: 'CO₂平均值',
  220. data: co2AveValues,
  221. yAxisIndex: 1,
  222. type: 'bar',
  223. },
  224. {
  225. name: 'CO平均值',
  226. data: coAveValues,
  227. yAxisIndex: 1,
  228. type: 'bar',
  229. },
  230. {
  231. name: 'O₂平均值',
  232. data: o2AveValues,
  233. yAxisIndex: 0,
  234. type: 'bar',
  235. },
  236. {
  237. name: 'N₂平均值',
  238. data: n2AveValues,
  239. yAxisIndex: 0,
  240. type: 'bar',
  241. },
  242. {
  243. name: 'C2H6平均值',
  244. data: c2h6AveValues,
  245. yAxisIndex: 1,
  246. type: 'bar',
  247. },
  248. ];
  249. }
  250. };
  251. const seriesConfig = getSeriesConfig(sysOrgCode);
  252. const option = {
  253. title: {
  254. text: '色谱仪报表分析',
  255. textStyle: {
  256. color: '#ffffff', // 设置标题颜色
  257. },
  258. left: 'center', // 水平居中
  259. top: '0', // 设置标题距离顶部的距离
  260. },
  261. tooltip: {
  262. trigger: 'axis',
  263. backgroundColor: 'rgba(28, 72, 105, 0.5)', // 设置 tooltip 背景为透明
  264. textStyle: {
  265. color: '#ffffff', // 设置 tooltip 字体颜色为白色
  266. },
  267. axisPointer: {
  268. label: {
  269. show: true,
  270. backgroundColor: '#071c44',
  271. },
  272. },
  273. },
  274. legend: {
  275. top: '9%',
  276. textStyle: {
  277. color: '#ffffffff',
  278. },
  279. width: '80%', // 设置图例的宽度
  280. orient: 'horizontal', // 水平布局
  281. pageIconColor: '#ffffff', // 设置翻页图标颜色
  282. pageTextStyle: {
  283. color: '#ffffff', // 设置翻页文字颜色
  284. },
  285. },
  286. xAxis: {
  287. type: 'category',
  288. data: categories,
  289. splitLine: { show: true, lineStyle: { color: 'rgba(28, 72, 105, 0.5)' } },
  290. axisLabel: {
  291. interval: 0, // 显示所有标签
  292. color: '#ffffff', // 设置 x 轴字体颜色
  293. formatter: function (value: string) {
  294. return value.length > 12 ? value.slice(0, 12) + '...' : value; // 截断长标签
  295. },
  296. },
  297. },
  298. yAxis: [
  299. {
  300. type: 'value',
  301. name: 'O₂/N₂',
  302. max: 100,
  303. splitLine: { show: true, lineStyle: { color: 'rgba(21,80,126,.5)' } },
  304. axisLabel: {
  305. color: '#ffffff',
  306. },
  307. },
  308. {
  309. type: 'value',
  310. name: '其他气体',
  311. splitLine: { show: true, lineStyle: { color: 'rgba(21,80,126,.5)' } },
  312. axisLabel: {
  313. color: '#ffffff', // 设置 y 轴字体颜色
  314. },
  315. },
  316. ],
  317. dataZoom: [
  318. {
  319. type: 'slider', // 会创建一个滑块来选择要显示的区域
  320. start: 0, // 初始选中范围的起始百分比
  321. end: (5 / categories.length) * 100, // 初始选中范围的结束百分比,根据数据条数调整
  322. minSpan: (5 / categories.length) * 100, // 最小选中范围,根据数据条数调整
  323. maxSpan: (5 / categories.length) * 100, // 最大选中范围,根据数据条数调整
  324. show: true,
  325. height: 10, // 设置滑块高度
  326. bottom: 1, // 设置滑块距离容器底部的距离
  327. borderColor: 'transparent', // 设置边框颜色为透明
  328. backgroundColor: '#F6F7FB', // 设置背景颜色
  329. handleIcon: 'path://M512,512m-448,0a448,448,0,1,0,896,0a448,448,0,1,0,-896,0Z', // 设置手柄图标为圆形
  330. handleColor: '#C2D2FF', // 设置手柄颜色
  331. handleSize: 13, // 设置手柄大小
  332. handleStyle: {
  333. borderColor: '#C2D2FF', // 设置手柄边框颜色
  334. },
  335. fillerColor: '#C2D2FF', // 设置选中范围的填充颜色
  336. },
  337. ],
  338. grid: {
  339. top: '21%', // 设置 grid 距离顶部的距离,增加间隔
  340. left: '3%',
  341. right: '4%',
  342. bottom: '3%',
  343. containLabel: true,
  344. },
  345. series: seriesConfig,
  346. };
  347. myChart.setOption(option);
  348. }
  349. //获取所有文件列表
  350. async function getAllFile() {
  351. let res = await getAllFileList({ type: 'bundleSpy' });
  352. selectList.value = res.records.map((item: any) => ({
  353. id: item.id,
  354. fileName: item.fileName,
  355. }));
  356. if (selectList.value.length > 0) {
  357. formSearch.id = selectList.value[0].id;
  358. getSearch();
  359. }
  360. }
  361. // 处理文件点击事件
  362. function handleFileClick(item: any) {
  363. formSearch.id = item.id;
  364. formSearch.fileName = item.fileName;
  365. selectedFileId.value = item.id;
  366. getSearch();
  367. }
  368. //查询
  369. function getSearch() {
  370. // const selectedFile = selectList.value.find((item) => item.id === formSearch.id);
  371. const params = {
  372. id: formSearch.id,
  373. // fileName: selectedFile ? selectedFile.fileName : '',
  374. };
  375. getTableListById(params);
  376. }
  377. onMounted(() => {
  378. getTableList({ type: 'bundleSpy' });
  379. getAllFile().then(() => {
  380. if (selectList.value.length > 0) {
  381. formSearch.id = selectList.value[0].id;
  382. selectedFileId.value = selectList.value[0].id;
  383. getSearch();
  384. }
  385. });
  386. });
  387. </script>
  388. <style lang="less" scoped>
  389. @import '/@/design/theme.less';
  390. .content-container {
  391. display: flex;
  392. width: 100%;
  393. height: 100%;
  394. padding-top: 54px;
  395. }
  396. .file-list {
  397. width: 20%;
  398. padding: 10px;
  399. margin-right: 10px;
  400. margin-bottom: 50px;
  401. border: 1px solid #99e8ff66;
  402. background: #27546e1a;
  403. box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
  404. -moz-box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
  405. -webkit-box-shadow: 0px 0px 50px 1px rgb(149 235 255 / 5%) inset;
  406. }
  407. .file-list ul {
  408. list-style: none;
  409. padding: 0;
  410. }
  411. .file-list li {
  412. color: #fff;
  413. padding: 5px;
  414. cursor: pointer;
  415. }
  416. .file-list li:hover,
  417. .file-list li.selected {
  418. background: #1c4869;
  419. }
  420. .table-container {
  421. margin-top: 10px;
  422. width: 80%;
  423. box-sizing: border-box;
  424. }
  425. .dustMonitor {
  426. width: 100%;
  427. height: 100%;
  428. padding: 10px 10px 15px 10px;
  429. box-sizing: border-box;
  430. position: relative;
  431. }
  432. :deep(.zxm-table-thead > tr > th:last-child) {
  433. border-right: 1px solid #91e9fe !important;
  434. }
  435. :deep(.zxm-picker-input > input) {
  436. color: #fff;
  437. }
  438. :deep(.zxm-select:not(.zxm-select-customize-input) .zxm-select-selector) {
  439. border: 1px solid var(--vent-form-item-border) !important;
  440. background-color: #ffffff00 !important;
  441. }
  442. :deep(.zxm-select-selection-item) {
  443. color: #fff !important;
  444. }
  445. .data-container {
  446. margin-top: 40px;
  447. display: flex;
  448. width: 100%;
  449. height: 100%;
  450. }
  451. .line-chart {
  452. flex: 3; /* 占据 3/4 的空间 */
  453. width: 100%;
  454. height: 400px;
  455. }
  456. .data-content {
  457. flex: 1; /* 占据 1/4 的空间 */
  458. height: 400px;
  459. display: flex;
  460. flex-direction: column; /* 垂直排列进度条 */
  461. // align-items: center; /* 水平居中 */
  462. margin: 10px;
  463. .title {
  464. font-size: 18px;
  465. font-weight: 600;
  466. color: #fff;
  467. margin-bottom: 20px;
  468. }
  469. .explain {
  470. color: var(--vent-table-action-link);
  471. margin-top: 18px;
  472. }
  473. }
  474. .progress {
  475. width: 100%;
  476. height: 20px;
  477. margin-top: 10px;
  478. }
  479. .progress-label {
  480. margin-top: 20px;
  481. text-align: left;
  482. margin-bottom: 5px;
  483. color: #fff;
  484. }
  485. ::deep .progress-text {
  486. color: #fff !important; /* 自定义百分比文字颜色 */
  487. }
  488. .blast-delta-container {
  489. margin: 50px;
  490. }
  491. .yellow-progress .ant-progress-bg {
  492. background-color: yellow !important;
  493. }
  494. :deep(.zxm-table-thead > tr > th:last-child) {
  495. border-right: 1px solid #91e9fe !important;
  496. }
  497. </style>