index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  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.query.deviceId || "";
  334. state.alarmId = route.query.alarmId || "";
  335. state.modelId = route.query.modelId || "";
  336. getStationList();
  337. getequipmentmodel_list();
  338. getfetchRelatePart();
  339. });
  340. // 机型
  341. const getequipmentmodel_list = async () => {
  342. const { data } = await fetchModel();
  343. state.modelListAll = data;
  344. };
  345. //所属部件
  346. const getfetchRelatePart = async () => {
  347. const { data } = await fetchRelatePartAndAlarmType();
  348. state.fetchListAll = data;
  349. };
  350. const confirmItem = (alarmItem) => {
  351. ElMessageBox("您确定要执行此操作吗?", "提示", {
  352. confirmButtonText: "确定",
  353. cancelButtonText: "取消",
  354. type: "warning",
  355. })
  356. .then(() => {
  357. confirmAlart(alarmItem)
  358. .then((res) => {
  359. if (res.code === 200) {
  360. ElMessage.success("确认成功");
  361. store.commit("removeWarning", alarmItem);
  362. getAlarmHistoryt();
  363. }
  364. })
  365. .catch(() => {
  366. ElMessage.error("确认失败,请重试");
  367. });
  368. })
  369. .catch(() => {});
  370. };
  371. const getColumnStyle = (columnItem) => {
  372. let style = "color:";
  373. if (columnItem.endts) {
  374. style += "#05bb4c";
  375. } else {
  376. style += "var(--el-color-danger)";
  377. }
  378. return style;
  379. };
  380. const state = reactive({
  381. typeList: [
  382. // {
  383. // label: "升压站",
  384. // value: "booststation",
  385. // },
  386. // {
  387. // label: "自定义",
  388. // value: "custom",
  389. // },
  390. {
  391. label: "风机",
  392. value: "windturbine",
  393. },
  394. {
  395. label: "光伏",
  396. value: "inverter",
  397. },
  398. ],
  399. c: "windturbine",
  400. stationId: "",
  401. alarmId: "",
  402. typeVal: "windturbine",
  403. windturbineList: [],
  404. deviceId: "",
  405. modelListAll: {},
  406. fetchListAll: {},
  407. modelId: "", //型号
  408. components: "", //部件
  409. description: "", //描述
  410. dateTime: [],
  411. startDate: null,
  412. endDate: null,
  413. tableData: [],
  414. isshowwindturbineName: true,
  415. tableHeader: [
  416. { title: "时间", code: "ts", width: "150" },
  417. { title: "场站", code: "stationname", width: "150" },
  418. { title: "机组", code: "devicename", width: "150" },
  419. { title: "报警信息", code: "description", width: "250" },
  420. { title: "故障原因", code: "faultCause" },
  421. // { title: "故障编码", code: "nemCode", width: "100" },
  422. // { title: "故障解决方法", code: "resolvent" },
  423. // { title: "级别", code: "rank", width: "80" },
  424. { title: "报警解除时间", code: "endtsName", width: "150" },
  425. {
  426. title: "状态",
  427. code: "isCloseName",
  428. width: "80",
  429. style: getColumnStyle,
  430. // width: 100,
  431. },
  432. { title: "是否确认", code: "confirmed", width: "100" },
  433. { title: "类型", code: "deviceTypeName", width: "80" },
  434. ],
  435. tableHeader1: [
  436. { title: "时间", code: "ts", width: "150" },
  437. { title: "升压站", code: "stationname", width: "150" },
  438. { title: "报警信息", code: "description", width: "250" },
  439. // { title: "级别", code: "rank", width: "80" },
  440. { title: "报警解除时间", code: "endtsName", width: "150" },
  441. {
  442. title: "状态",
  443. code: "isCloseName",
  444. style: getColumnStyle,
  445. // width: 100,
  446. width: "80",
  447. },
  448. { title: "是否确认", code: "confirmed", width: "100" },
  449. { title: "类型", code: "deviceTypeName", width: "80" },
  450. ],
  451. });
  452. // 场站列表/升压站列表
  453. const stationList = ref([]);
  454. const getStationList = async () => {
  455. const { data } = await getWpList(state.typeVal);
  456. stationList.value = data;
  457. if (state.deviceId) {
  458. let station = data.find((i) => {
  459. let st = i.id.split("_")[2];
  460. let dt = state.deviceId.split("_")[2];
  461. if (st == dt) {
  462. return i;
  463. }
  464. });
  465. state.stationId = station?.id;
  466. } else {
  467. state.stationId =
  468. state.typeVal == "windturbine" ? "SXJ_KGDL_DJY_FDC_STA" : data[0]?.id;
  469. }
  470. if (stationList.value.length) {
  471. await getWindturbineList();
  472. await getAlarmHistoryt();
  473. }
  474. };
  475. // watch(
  476. // () => stationList,
  477. // (val, old) => {
  478. // val?.value?.length &&
  479. // nextTick(async () => {
  480. // await getWindturbineList();
  481. // await getAlarmHistoryt();
  482. // });
  483. // },
  484. // {
  485. // deep: true,
  486. // immediate: true,
  487. // }
  488. // );
  489. watch(
  490. () => route,
  491. (val, old) => {
  492. state.deviceId = route.query.deviceId || "";
  493. state.alarmId = route.query.alarmId || "";
  494. state.modelId = route.query.modelId || "";
  495. // nextTick(async () => {
  496. // if (route.params.deviceId && route.params.alarmId) {
  497. // await getAlarmHistoryt();
  498. // }
  499. // });
  500. },
  501. {
  502. deep: true,
  503. immediate: true,
  504. }
  505. );
  506. //型号列表
  507. const modelList = computed(() => {
  508. if (state.stationId == "") {
  509. return [];
  510. } else {
  511. state.modelId = route.query.deviceId
  512. ? route.query.modelId
  513. : state.modelListAll[state.stationId]?.[0]?.id || "";
  514. return state.modelListAll[state.stationId];
  515. }
  516. });
  517. //部件列表
  518. const componentList = computed(() => {
  519. if (state.stationId == "") {
  520. return [];
  521. } else {
  522. if (state.stationId.includes("FDC")) {
  523. return state.fetchListAll?.fjbj;
  524. } else {
  525. return state.fetchListAll?.gfbj;
  526. }
  527. }
  528. });
  529. //get 风机
  530. const getWindturbineList = async () => {
  531. const { data } = await fetchWindturbineList(state.stationId);
  532. state.windturbineList = data;
  533. };
  534. const query = reactive({
  535. page: 1,
  536. limit: 21,
  537. pageTotal: null,
  538. });
  539. const tableLoading = ref(false);
  540. // 获取历史记录表
  541. const getAlarmHistoryt = async () => {
  542. // if (route.params.deviceId && route.params.alarmId) {
  543. // state.stationId = "";
  544. // }
  545. tableLoading.value = true;
  546. let params = {
  547. pageNum: query.page,
  548. pageSize: query.limit,
  549. alarmId: state.alarmId,
  550. alarmType: "custom",
  551. deviceType: state.typeVal,
  552. stationid: state.stationId,
  553. deviceid:
  554. state.deviceId || (state.typeVal == "booststation" ? "" : state.deviceId),
  555. modelId: state.typeVal == "booststation" ? "" : state.modelId,
  556. components: state.components,
  557. description: state.description,
  558. begin: state.dateTime[0],
  559. end: state.dateTime[1],
  560. };
  561. const { data } = await alarm_history(params);
  562. tableLoading.value = false;
  563. query.pageTotal = data?.total;
  564. data?.ls?.forEach((ele) => {
  565. ele.isCloseName = ele.endts ? "已解除" : "未解除";
  566. ele.deviceTypeName = tableFilter(ele.deviceType);
  567. ele.endtsName = ele.endts > 0 ? formatTime(ele.endts) : "";
  568. });
  569. state.tableData = data?.ls;
  570. };
  571. //报警类型变化
  572. const typechange = () => {
  573. state.alarmId = "";
  574. state.deviceId = "";
  575. state.isshowwindturbineName = state.typeVal == "booststation" ? false : true;
  576. };
  577. // 批量导出
  578. const export2Excel = async () => {
  579. let params = {
  580. pageNum: query.page,
  581. pageSize: query.pageTotal,
  582. alarmType: state.typeVal,
  583. stationid: state.stationId,
  584. deviceid: state.typeVal == "booststation" ? "" : state.deviceId,
  585. modelId: state.modelId,
  586. components: state.components,
  587. description: state.description,
  588. begin: state.dateTime[0],
  589. end: state.dateTime[1],
  590. };
  591. if (state.dateTime[1] - state.dateTime[0] > 6 * 24 * 60 * 60 * 1000) {
  592. this.$message({
  593. message: "导出时间范围不能大于7天",
  594. type: "warning",
  595. });
  596. } else {
  597. let tableHeader = [];
  598. let tableKey = [];
  599. const { data } = await alarm_history(params);
  600. if (state.isshowwindturbineName) {
  601. tableHeader = state.tableHeader.map((item) => item.title);
  602. tableKey = state.tableHeader.map((item) => item.code);
  603. } else {
  604. tableHeader = state.tableHeader1.map((item) => item.title);
  605. tableKey = state.tableHeader1.map((item) => item.code);
  606. }
  607. const stationName = stationList.value.find((ele) => {
  608. return ele.id === state.stationId;
  609. }).name;
  610. const fileName = `${stationName} ${state.dateTime[0]} ~ ${state.dateTime[1]} 数据表`;
  611. outExportExcel(
  612. tableHeader,
  613. tableKey,
  614. data.ls.map((item) => {
  615. return {
  616. ...item,
  617. ts: formatTime(item.ts),
  618. rank: tableFilter(item.rank),
  619. deviceType: tableFilter(item.deviceType),
  620. };
  621. }),
  622. fileName
  623. );
  624. ElMessage.success(`导出成功!`);
  625. }
  626. };
  627. // 分页导航
  628. const handlePageChange = (val) => {
  629. query.page = val;
  630. getAlarmHistoryt();
  631. };
  632. // 时间格式化
  633. const formatTime = (val) => {
  634. return dayjs(val).format("YYYY-MM-DD HH:mm:ss");
  635. };
  636. // 格式化
  637. const obj = {
  638. 1: "低级",
  639. 2: "低中级",
  640. 3: "中级",
  641. 4: "中高级",
  642. 5: "高级",
  643. booststation: "升压站",
  644. custom: "自定义",
  645. windturbine: "风机",
  646. inverter: "光伏",
  647. };
  648. const messageTypeObj = {
  649. 1: "触发",
  650. 3: "解除",
  651. };
  652. const tableFilter = (val) => {
  653. return obj[val];
  654. };
  655. const messageTypeFilter = (val) => {
  656. return messageTypeObj[val];
  657. };
  658. </script>
  659. <style scoped lang="less">
  660. p {
  661. padding: 0;
  662. margin: 0;
  663. }
  664. .custom-warning {
  665. height: 100%;
  666. width: 100%;
  667. padding: 0 20px;
  668. padding-bottom: 10px;
  669. .form-wrapper ::v-deep {
  670. display: flex;
  671. flex-direction: column;
  672. padding-top: 10px;
  673. position: relative;
  674. .search-wrapper {
  675. display: flex;
  676. align-items: center;
  677. font-size: 14px;
  678. font-family: Microsoft YaHei;
  679. font-weight: 400;
  680. color: #b3b3b3;
  681. margin-bottom: 10px;
  682. .search-item {
  683. display: flex;
  684. margin-right: 10px;
  685. max-width: 450px;
  686. align-items: center;
  687. .label {
  688. margin-right: 10px;
  689. text-align: right;
  690. white-space: nowrap;
  691. // width: 60px;
  692. }
  693. .search-content {
  694. flex: 1;
  695. }
  696. }
  697. }
  698. .btns {
  699. display: flex;
  700. justify-content: flex-end;
  701. margin-right: 10px;
  702. position: absolute;
  703. right: 0;
  704. top: 53px;
  705. }
  706. .buttons {
  707. background-color: rgba(5, 187, 76, 0.2);
  708. border: 1px solid #3b6c53;
  709. color: #b3b3b3;
  710. font-size: 14px;
  711. &:hover {
  712. background-color: rgba(5, 187, 76, 0.5);
  713. color: #ffffff;
  714. }
  715. }
  716. }
  717. .table-wrapper {
  718. height: calc(100% - 43px);
  719. width: 100%;
  720. .leftContent {
  721. width: 242px;
  722. height: 41px;
  723. display: flex;
  724. align-items: center;
  725. background: url("~@/assets/imgs/title_left_bg1.png") no-repeat;
  726. span {
  727. font-size: 16px;
  728. font-family: Microsoft YaHei;
  729. font-weight: 400;
  730. color: #05bb4c;
  731. margin-left: 25px;
  732. }
  733. }
  734. .pagination-wrapper :deep {
  735. text-align: right;
  736. margin-top: 10px;
  737. }
  738. }
  739. }
  740. </style>