index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. <template>
  2. <div
  3. class="custom-warning"
  4. v-loading="tableLoading"
  5. element-loading-text="加载中..."
  6. element-loading-background="rgba(4, 12, 11, 0.8)"
  7. >
  8. <div class="form-wrapper">
  9. <div class="search-wrapper">
  10. <div class="search-item">
  11. <span class="label">类型:</span>
  12. <div class="search-content">
  13. <el-select
  14. v-model="state.typeVal"
  15. clearable
  16. size="mini"
  17. placeholder="全部"
  18. popper-class="select"
  19. @change="
  20. () => {
  21. getStationList();
  22. typechange();
  23. }
  24. "
  25. >
  26. <el-option
  27. v-for="item in state.typeList"
  28. :key="item.value"
  29. :value="item.value"
  30. :label="item.label"
  31. >
  32. </el-option>
  33. </el-select>
  34. </div>
  35. </div>
  36. <div class="search-item">
  37. <span class="label">{{
  38. state.isshowwindturbineName ? "场站:" : "升压站:"
  39. }}</span>
  40. <div class="search-content">
  41. <el-select
  42. v-model="state.stationId"
  43. clearable
  44. size="mini"
  45. placeholder="全部"
  46. popper-class="select"
  47. @change="getWindturbineList"
  48. >
  49. <el-option
  50. v-for="item in stationList"
  51. :key="item.id"
  52. :value="item.id"
  53. :label="item.name"
  54. ></el-option>
  55. </el-select>
  56. </div>
  57. </div>
  58. <div class="search-item" v-if="state.isshowwindturbineName">
  59. <span class="label">机组:</span>
  60. <div class="search-content">
  61. <el-select
  62. v-model="state.deviceId"
  63. clearable
  64. size="mini"
  65. placeholder="全部"
  66. popper-class="select"
  67. >
  68. <el-option
  69. v-for="item in state.windturbineList"
  70. :key="item.id"
  71. :value="item.id"
  72. :label="item.name"
  73. >
  74. </el-option>
  75. </el-select>
  76. </div>
  77. </div>
  78. <div class="search-item" v-if="state.isshowwindturbineName">
  79. <span class="label">型号:</span>
  80. <div class="search-content">
  81. <el-select
  82. v-model="state.modelId"
  83. clearable
  84. size="mini"
  85. placeholder="全部"
  86. popper-class="select"
  87. >
  88. <el-option
  89. v-for="item in modelList"
  90. :key="item.id"
  91. :value="item.id"
  92. :label="item.name"
  93. >
  94. </el-option>
  95. </el-select>
  96. </div>
  97. </div>
  98. <div class="search-item" v-if="state.isshowwindturbineName">
  99. <span class="label">部件:</span>
  100. <div class="search-content">
  101. <el-select
  102. v-model="state.components"
  103. clearable
  104. size="mini"
  105. placeholder="全部"
  106. popper-class="select"
  107. >
  108. <el-option
  109. v-for="item in componentList"
  110. :key="item.id"
  111. :value="item.id"
  112. :label="item.name"
  113. >
  114. </el-option>
  115. </el-select>
  116. </div>
  117. </div>
  118. <div class="search-item">
  119. <span class="label">描述:</span>
  120. <div class="search-content">
  121. <el-input
  122. v-model="state.description"
  123. size="mini"
  124. placeholder="请输入..."
  125. ></el-input>
  126. </div>
  127. </div>
  128. <div class="search-item">
  129. <span class="label">日期:</span>
  130. <div class="search-content">
  131. <el-date-picker
  132. v-model="state.dateTime"
  133. size="mini"
  134. type="datetimerange"
  135. range-separator="-"
  136. format="YYYY-MM-DD HH:mm:ss"
  137. value-format="YYYY-MM-DD HH:mm:ss"
  138. start-placeholder="开始"
  139. end-placeholder="结束"
  140. popper-class="date-select"
  141. >
  142. </el-date-picker>
  143. </div>
  144. </div>
  145. </div>
  146. <div class="btns">
  147. <el-button class="buttons" round size="mini" @click="getAlarmHistoryt"
  148. >查询</el-button
  149. >
  150. <el-button
  151. size="mini"
  152. class="buttons"
  153. round
  154. @click="export2Excel"
  155. :disabled="state.tableData?.length == 0 ? true : false"
  156. >
  157. 导出</el-button
  158. >
  159. <el-button
  160. round
  161. class="buttons"
  162. size="mini"
  163. :disabled="!state.tableData?.length"
  164. @click="confirmItem(state.tableData)"
  165. >确认本页</el-button
  166. >
  167. </div>
  168. </div>
  169. <div class="table-wrapper">
  170. <div class="leftContent">
  171. <span>{{ pageTitle }}</span>
  172. </div>
  173. <el-table
  174. size="mini"
  175. :data="state.tableData"
  176. height="calc(100% - 35px - 55px)"
  177. style="width: 100%"
  178. stripe
  179. >
  180. <template v-if="state.isshowwindturbineName">
  181. <el-table-column
  182. v-for="item in state.tableHeader"
  183. :label="item.title"
  184. :prop="item.code"
  185. :key="item.code"
  186. :width="item.width || ''"
  187. show-overflow-tooltip
  188. header-align="center"
  189. :align="
  190. item.code == 'description' || item.code == 'faultCause'
  191. ? 'left'
  192. : 'center'
  193. "
  194. >
  195. <template #default="scope">
  196. <p :style="item.style && item.style(scope.row)">
  197. <span v-if="item.code == 'rank'">
  198. {{ tableFilter(scope.row.rank) }}
  199. </span>
  200. <span v-else-if="item.code == 'ts'">
  201. {{ formatTime(scope.row.ts) }}
  202. </span>
  203. <span
  204. :style="`color:${
  205. scope.row.confirmed ? '#05bb4c' : 'var(--el-color-danger)'
  206. }`"
  207. v-else-if="item.code == 'confirmed'"
  208. >
  209. {{ scope.row.confirmed ? "是" : "否" }}
  210. </span>
  211. <span v-else>
  212. {{
  213. scope.row[item.code] != "NULL" ? scope.row[item.code] : ""
  214. }}
  215. </span>
  216. </p>
  217. </template>
  218. </el-table-column>
  219. <el-table-column
  220. label="操作"
  221. width="100"
  222. header-align="center"
  223. align="center"
  224. >
  225. <template #default="scope">
  226. <el-button
  227. style="color: #05bb4c"
  228. type="text"
  229. @click="confirmItem([scope.row])"
  230. >确认本条</el-button
  231. >
  232. </template>
  233. </el-table-column>
  234. </template>
  235. <template v-else>
  236. <el-table-column
  237. v-for="item in state.tableHeader1"
  238. :label="item.title"
  239. :prop="item.code"
  240. :key="item.code"
  241. :width="item.width || ''"
  242. show-overflow-tooltip
  243. header-align="center"
  244. >
  245. <template #default="scope">
  246. <p :style="item.style && item.style(scope.row)">
  247. <span v-if="item.code == 'rank'">
  248. {{ tableFilter(scope.row.rank) }}
  249. </span>
  250. <span v-else-if="item.code == 'ts'">
  251. {{ formatTime(scope.row.ts) }}
  252. </span>
  253. <span
  254. :style="`color:${
  255. scope.row.confirmed ? '#05bb4c' : 'var(--el-color-danger)'
  256. }`"
  257. v-else-if="item.code == 'confirmed'"
  258. >
  259. {{ scope.row.confirmed ? "是" : "否" }}
  260. </span>
  261. <span v-else>
  262. {{
  263. scope.row[item.code] != "NULL" ? scope.row[item.code] : ""
  264. }}
  265. </span>
  266. </p>
  267. </template>
  268. </el-table-column>
  269. <el-table-column
  270. label="操作"
  271. width="100"
  272. header-align="center"
  273. align="center"
  274. >
  275. <template #default="scope">
  276. <el-button
  277. style="color: #05bb4c"
  278. type="text"
  279. @click="confirmItem([scope.row])"
  280. >确认本条</el-button
  281. >
  282. </template>
  283. </el-table-column>
  284. </template>
  285. </el-table>
  286. <div class="pagination-wrapper">
  287. <el-pagination
  288. layout="total, sizes, prev, pager, next"
  289. :current-page="query.page"
  290. :page-size="query.limit"
  291. :page-sizes="[21, 100, 500, 1000]"
  292. :total="query.pageTotal"
  293. @size-change="
  294. (value) => {
  295. query.page = 1;
  296. query.limit = value;
  297. getAlarmHistoryt();
  298. }
  299. "
  300. @current-change="handlePageChange"
  301. ></el-pagination>
  302. </div>
  303. </div>
  304. </div>
  305. </template>
  306. <script setup>
  307. import {
  308. watch,
  309. reactive,
  310. nextTick,
  311. computed,
  312. onMounted,
  313. ref,
  314. onActivated,
  315. onUpdated,
  316. } from "vue";
  317. import { useRouter, useRoute } from "vue-router";
  318. import dayjs from "dayjs";
  319. import {
  320. alarm_history,
  321. new_alarm_history,
  322. fetchWindturbineList,
  323. fetchModel,
  324. fetchRelatePartAndAlarmType,
  325. getWpList,
  326. confirmAlart,
  327. } from "@/api/zhbj/index.js";
  328. import { ElMessage, ElMessageBox } from "element-plus";
  329. import { outExportExcel } from "@/tools/excel/exportExcel.js"; //引入文件
  330. import { useStore } from "vuex";
  331. const pageTitle = "预警查询";
  332. const store = useStore();
  333. const route = useRoute();
  334. onMounted(() => {
  335. state.dateTime = [
  336. dayjs().startOf("day").format("YYYY-MM-DD HH:mm:ss"),
  337. dayjs().format("YYYY-MM-DD HH:mm:ss"),
  338. ];
  339. state.deviceId = route.query.deviceId || "";
  340. state.alarmId = route.query.alarmId || "";
  341. state.modelId = route.query.modelId || "";
  342. getStationList();
  343. getequipmentmodel_list();
  344. getfetchRelatePart();
  345. });
  346. // 机型
  347. const getequipmentmodel_list = async () => {
  348. const { data } = await fetchModel();
  349. state.modelListAll = data;
  350. };
  351. //所属部件
  352. const getfetchRelatePart = async () => {
  353. const { data } = await fetchRelatePartAndAlarmType();
  354. state.fetchListAll = data;
  355. };
  356. const confirmItem = (alarmItem) => {
  357. ElMessageBox("您确定要执行此操作吗?", "提示", {
  358. confirmButtonText: "确定",
  359. cancelButtonText: "取消",
  360. type: "warning",
  361. })
  362. .then(() => {
  363. confirmAlart(alarmItem)
  364. .then((res) => {
  365. if (res.code === 200) {
  366. ElMessage.success("确认成功");
  367. store.commit("removeWarning", alarmItem);
  368. getAlarmHistoryt();
  369. }
  370. })
  371. .catch(() => {
  372. ElMessage.error("确认失败,请重试");
  373. });
  374. })
  375. .catch(() => {});
  376. };
  377. const getColumnStyle = (columnItem) => {
  378. let style = "color:";
  379. if (columnItem.endts) {
  380. style += "#05bb4c";
  381. } else {
  382. style += "var(--el-color-danger)";
  383. }
  384. return style;
  385. };
  386. const state = reactive({
  387. typeList: [
  388. // {
  389. // label: "升压站",
  390. // value: "booststation",
  391. // },
  392. // {
  393. // label: "自定义",
  394. // value: "custom",
  395. // },
  396. {
  397. label: "风机",
  398. value: "windturbine",
  399. },
  400. {
  401. label: "光伏",
  402. value: "inverter",
  403. },
  404. ],
  405. c: "windturbine",
  406. stationId: "",
  407. alarmId: "",
  408. typeVal: "windturbine",
  409. windturbineList: [],
  410. deviceId: "",
  411. modelListAll: {},
  412. fetchListAll: {},
  413. modelId: "", //型号
  414. components: "", //部件
  415. description: "", //描述
  416. dateTime: [],
  417. startDate: null,
  418. endDate: null,
  419. tableData: [],
  420. isshowwindturbineName: true,
  421. tableHeader: [
  422. { title: "时间", code: "ts", width: "150" },
  423. { title: "场站", code: "stationname", width: "150" },
  424. { title: "机组", code: "devicename", width: "150" },
  425. { title: "报警信息", code: "description", width: "250" },
  426. { title: "故障原因", code: "faultCause" },
  427. // { title: "故障编码", code: "nemCode", width: "100" },
  428. // { title: "故障解决方法", code: "resolvent" },
  429. // { title: "级别", code: "rank", width: "80" },
  430. { title: "报警解除时间", code: "endtsName", width: "150" },
  431. {
  432. title: "状态",
  433. code: "isCloseName",
  434. width: "80",
  435. style: getColumnStyle,
  436. // width: 100,
  437. },
  438. { title: "是否确认", code: "confirmed", width: "100" },
  439. { title: "类型", code: "deviceTypeName", width: "80" },
  440. ],
  441. tableHeader1: [
  442. { title: "时间", code: "ts", width: "150" },
  443. { title: "升压站", code: "stationname", width: "150" },
  444. { title: "报警信息", code: "description", width: "250" },
  445. // { title: "级别", code: "rank", width: "80" },
  446. { title: "报警解除时间", code: "endtsName", width: "150" },
  447. {
  448. title: "状态",
  449. code: "isCloseName",
  450. style: getColumnStyle,
  451. // width: 100,
  452. width: "80",
  453. },
  454. { title: "是否确认", code: "confirmed", width: "100" },
  455. { title: "类型", code: "deviceTypeName", width: "80" },
  456. ],
  457. });
  458. // 场站列表/升压站列表
  459. const stationList = ref([]);
  460. const getStationList = async () => {
  461. const { data } = await getWpList(state.typeVal);
  462. stationList.value = data;
  463. if (state.deviceId) {
  464. let station = data.find((i) => {
  465. let st = i.id.split("_")[2];
  466. let dt = state.deviceId.split("_")[2];
  467. if (st == dt) {
  468. return i;
  469. }
  470. });
  471. state.stationId = station?.id;
  472. } else {
  473. state.stationId =
  474. state.typeVal == "windturbine" ? "SXJ_KGDL_DJY_FDC_STA" : data[0]?.id;
  475. }
  476. if (stationList.value.length) {
  477. await getWindturbineList();
  478. await getAlarmHistoryt();
  479. }
  480. };
  481. // watch(
  482. // () => stationList,
  483. // (val, old) => {
  484. // val?.value?.length &&
  485. // nextTick(async () => {
  486. // await getWindturbineList();
  487. // await getAlarmHistoryt();
  488. // });
  489. // },
  490. // {
  491. // deep: true,
  492. // immediate: true,
  493. // }
  494. // );
  495. watch(
  496. () => route,
  497. (val, old) => {
  498. state.deviceId = route.query.deviceId || "";
  499. state.alarmId = route.query.alarmId || "";
  500. state.modelId = route.query.modelId || "";
  501. // nextTick(async () => {
  502. // if (route.params.deviceId && route.params.alarmId) {
  503. // await getAlarmHistoryt();
  504. // }
  505. // });
  506. },
  507. {
  508. deep: true,
  509. immediate: true,
  510. }
  511. );
  512. //型号列表
  513. const modelList = computed(() => {
  514. if (state.stationId == "") {
  515. return [];
  516. } else {
  517. state.modelId = route.query.deviceId
  518. ? route.query.modelId
  519. : state.modelListAll[state.stationId]?.[0]?.id || "";
  520. return state.modelListAll[state.stationId];
  521. }
  522. });
  523. //部件列表
  524. const componentList = computed(() => {
  525. if (state.stationId == "") {
  526. return [];
  527. } else {
  528. if (state.stationId.includes("FDC")) {
  529. return state.fetchListAll?.fjbj;
  530. } else {
  531. return state.fetchListAll?.gfbj;
  532. }
  533. }
  534. });
  535. //get 风机
  536. const getWindturbineList = async () => {
  537. const { data } = await fetchWindturbineList(state.stationId);
  538. state.windturbineList = data;
  539. };
  540. const query = reactive({
  541. page: 1,
  542. limit: 21,
  543. pageTotal: null,
  544. });
  545. const tableLoading = ref(false);
  546. // 获取历史记录表
  547. const getAlarmHistoryt = async () => {
  548. // if (route.params.deviceId && route.params.alarmId) {
  549. // state.stationId = "";
  550. // }
  551. tableLoading.value = true;
  552. let params = {
  553. pageNum: query.page,
  554. pageSize: query.limit,
  555. alarmId: state.alarmId,
  556. alarmType: "custom",
  557. deviceType: state.typeVal,
  558. stationid: state.stationId,
  559. deviceid:
  560. state.deviceId || (state.typeVal == "booststation" ? "" : state.deviceId),
  561. modelId: state.typeVal == "booststation" ? "" : state.modelId,
  562. components: state.components,
  563. description: state.description,
  564. begin: state.dateTime[0],
  565. end: state.dateTime[1],
  566. };
  567. const { data } = await alarm_history(params);
  568. tableLoading.value = false;
  569. query.pageTotal = data?.total;
  570. data?.ls?.forEach((ele) => {
  571. ele.isCloseName = ele.endts ? "已解除" : "未解除";
  572. ele.deviceTypeName = tableFilter(ele.deviceType);
  573. ele.endtsName = ele.endts > 0 ? formatTime(ele.endts) : "";
  574. });
  575. state.tableData = data?.ls;
  576. };
  577. //报警类型变化
  578. const typechange = () => {
  579. state.alarmId = "";
  580. state.deviceId = "";
  581. state.isshowwindturbineName = state.typeVal == "booststation" ? false : true;
  582. };
  583. // 批量导出
  584. const export2Excel = async () => {
  585. let params = {
  586. pageNum: query.page,
  587. pageSize: query.pageTotal,
  588. alarmType: state.typeVal,
  589. stationid: state.stationId,
  590. deviceid: state.typeVal == "booststation" ? "" : state.deviceId,
  591. modelId: state.modelId,
  592. components: state.components,
  593. description: state.description,
  594. begin: state.dateTime[0],
  595. end: state.dateTime[1],
  596. };
  597. if (state.dateTime[1] - state.dateTime[0] > 6 * 24 * 60 * 60 * 1000) {
  598. this.$message({
  599. message: "导出时间范围不能大于7天",
  600. type: "warning",
  601. });
  602. } else {
  603. let tableHeader = [];
  604. let tableKey = [];
  605. const { data } = await alarm_history(params);
  606. if (state.isshowwindturbineName) {
  607. tableHeader = state.tableHeader.map((item) => item.title);
  608. tableKey = state.tableHeader.map((item) => item.code);
  609. } else {
  610. tableHeader = state.tableHeader1.map((item) => item.title);
  611. tableKey = state.tableHeader1.map((item) => item.code);
  612. }
  613. const stationName = stationList.value.find((ele) => {
  614. return ele.id === state.stationId;
  615. }).name;
  616. const fileName = `${stationName} ${state.dateTime[0]} ~ ${state.dateTime[1]} 数据表`;
  617. outExportExcel(
  618. tableHeader,
  619. tableKey,
  620. data.ls.map((item) => {
  621. return {
  622. ...item,
  623. ts: formatTime(item.ts),
  624. rank: tableFilter(item.rank),
  625. deviceType: tableFilter(item.deviceType),
  626. };
  627. }),
  628. fileName
  629. );
  630. ElMessage.success(`导出成功!`);
  631. }
  632. };
  633. // 分页导航
  634. const handlePageChange = (val) => {
  635. query.page = val;
  636. getAlarmHistoryt();
  637. };
  638. // 时间格式化
  639. const formatTime = (val) => {
  640. return dayjs(val).format("YYYY-MM-DD HH:mm:ss");
  641. };
  642. // 格式化
  643. const obj = {
  644. 1: "低级",
  645. 2: "低中级",
  646. 3: "中级",
  647. 4: "中高级",
  648. 5: "高级",
  649. booststation: "升压站",
  650. custom: "自定义",
  651. windturbine: "风机",
  652. inverter: "光伏",
  653. };
  654. const messageTypeObj = {
  655. 1: "触发",
  656. 3: "解除",
  657. };
  658. const tableFilter = (val) => {
  659. return obj[val];
  660. };
  661. const messageTypeFilter = (val) => {
  662. return messageTypeObj[val];
  663. };
  664. </script>
  665. <style scoped lang="less">
  666. p {
  667. padding: 0;
  668. margin: 0;
  669. }
  670. .custom-warning {
  671. height: 100%;
  672. width: 100%;
  673. padding: 0 20px;
  674. padding-bottom: 10px;
  675. .form-wrapper ::v-deep {
  676. display: flex;
  677. flex-direction: column;
  678. padding-top: 10px;
  679. position: relative;
  680. .search-wrapper {
  681. display: flex;
  682. align-items: center;
  683. font-size: 14px;
  684. font-family: Microsoft YaHei;
  685. font-weight: 400;
  686. color: #b3b3b3;
  687. margin-bottom: 10px;
  688. .search-item {
  689. display: flex;
  690. margin-right: 10px;
  691. max-width: 450px;
  692. align-items: center;
  693. .label {
  694. margin-right: 10px;
  695. text-align: right;
  696. white-space: nowrap;
  697. // width: 60px;
  698. }
  699. .search-content {
  700. flex: 1;
  701. }
  702. }
  703. }
  704. .btns {
  705. display: flex;
  706. justify-content: flex-end;
  707. margin-right: 10px;
  708. position: absolute;
  709. right: 0;
  710. top: 53px;
  711. }
  712. .buttons {
  713. background-color: rgba(5, 187, 76, 0.2);
  714. border: 1px solid #3b6c53;
  715. color: #b3b3b3;
  716. font-size: 14px;
  717. &:hover {
  718. background-color: rgba(5, 187, 76, 0.5);
  719. color: #ffffff;
  720. }
  721. }
  722. }
  723. .table-wrapper {
  724. height: calc(100% - 43px);
  725. width: 100%;
  726. .leftContent {
  727. width: 242px;
  728. height: 41px;
  729. display: flex;
  730. align-items: center;
  731. background: url("~@/assets/imgs/title_left_bg1.png") no-repeat;
  732. span {
  733. font-size: 16px;
  734. font-family: Microsoft YaHei;
  735. font-weight: 400;
  736. color: #05bb4c;
  737. margin-left: 25px;
  738. }
  739. }
  740. .pagination-wrapper :deep {
  741. text-align: right;
  742. margin-top: 10px;
  743. }
  744. }
  745. }
  746. </style>