index.vue 20 KB

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