index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. <template>
  2. <div class="sensor-container">
  3. <a-spin :spinning="loading">
  4. <a-tabs class="tabs-box" type="card" v-model:activeKey="activeKey" @change="tabChange">
  5. <a-tab-pane key="1" tab="实时监测">
  6. <div class="box-bg table-box" style="margin-bottom: 10px">
  7. <div>
  8. <label style="color: var(--vent-font-color)">设备类型:</label>
  9. <Select
  10. @change="handleSensorChange"
  11. :options="deviceTypeOption"
  12. :fieldNames="{ label: 'itemText', value: 'itemValue' }"
  13. v-model:value="deviceKind"
  14. style="width: 200px; margin-bottom: 5px; color: black"
  15. placeholder="请选择设备类型"
  16. :allowClear="true"
  17. />
  18. <MonitorTable
  19. ref="SensorMonitorRef"
  20. :is-show-select="false"
  21. :columnsType="deviceKind + '_monitor'"
  22. :dataSource="dataSource"
  23. design-scope="modelsensor_monitor"
  24. @select-row="getSelectRow"
  25. :deviceType="deviceKind"
  26. :scroll="{ y: chartsColumns.length > 0 ? 600 : 600 }"
  27. size="''"
  28. title="传感器监测"
  29. >
  30. <template #filterCell="{ column, record }">
  31. <a-tag v-if="column.dataIndex === 'warnFlag'" :color="record.warnFlag == '0' ? 'green' : 'red'">{{
  32. record.warnFlag == '0' ? '正常' : '报警'
  33. }}</a-tag>
  34. <a-tag v-if="column.dataIndex === 'netStatus'" :color="record.netStatus == '0' ? 'default' : 'green'">{{
  35. record.netStatus == '0' ? '断开' : '连接'
  36. }}</a-tag>
  37. </template>
  38. </MonitorTable>
  39. </div>
  40. <!-- v-if="chartsColumns.length > 0" -->
  41. <div class="charts-box" v-if="false">
  42. <BarAndLine
  43. :chartsColumnsType="selectData.deviceType"
  44. xAxisPropType="readTime"
  45. :dataSource="detailDataSource"
  46. height="300px"
  47. :chartsColumns="chartsColumns"
  48. chartsType="detail"
  49. :option="echartsOption"
  50. @refresh="refreshEchatrs"
  51. />
  52. </div>
  53. </div>
  54. </a-tab-pane>
  55. <a-tab-pane key="2" tab="历史数据">
  56. <div class="tab-item table-box box-bg padding-0" v-if="activeKey == '2'">
  57. <HistoryTable
  58. :columns-type="deviceKind"
  59. :device-type="deviceKind"
  60. :device-list-api="list.bind(null, { devicetype: selectData.deviceType })"
  61. @change="historyDataSourceChange"
  62. designScope="modelsensor-history"
  63. :scroll="{ y: chartsColumns.length > 0 ? 400 : 600 }"
  64. />
  65. <div class="charts-box" v-if="chartsColumns.length > 0">
  66. <BarAndLine
  67. :chartsColumnsType="selectData.deviceType"
  68. xAxisPropType="ttime"
  69. :dataSource="historyDataSource"
  70. height="300px"
  71. :chartsColumns="chartsColumns"
  72. chartsType="history"
  73. :option="echartsOption1"
  74. @refresh="refreshEchatrs"
  75. />
  76. </div>
  77. </div>
  78. </a-tab-pane>
  79. <a-tab-pane key="3" tab="报警历史">
  80. <div class="tab-item box-bg" v-if="activeKey == '3'">
  81. <AlarmHistoryTable columns-type="alarm" device-type="modelsensor" designScope="alarm-history" />
  82. </div>
  83. </a-tab-pane>
  84. <!-- <a-tab-pane key="4" tab="操作历史">
  85. <div class="tab-item box-bg">
  86. <HandlerHistoryTable columns-type="operator_history" device-type="modelsensor" designScope="alarm-history" />
  87. </div>
  88. </a-tab-pane> -->
  89. </a-tabs>
  90. <div class="title-text">
  91. {{ selectData.strinstallpos || selectData.strname }}
  92. </div>
  93. </a-spin>
  94. </div>
  95. </template>
  96. <script setup lang="ts">
  97. import BarAndLine from '/@/components/chart/BarAndLine.vue';
  98. import { Select } from 'ant-design-vue';
  99. import { onBeforeMount, ref, onMounted, onUnmounted, toRaw, reactive, nextTick } from 'vue';
  100. import MonitorTable from '../comment/MonitorTable.vue';
  101. import HistoryTable from '../comment/HistoryTable.vue';
  102. import AlarmHistoryTable from '../comment/AlarmHistoryTable.vue';
  103. import HandlerHistoryTable from '../comment/HandlerHistoryTable.vue';
  104. import { list, getTableList } from './sensor.api';
  105. import { list as baseList } from '../../deviceManager/sensorTabel/sensor.api';
  106. import { deviceList } from '../../deviceManager/comment/pointTabel/point.api';
  107. import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
  108. import { cloneDeep } from 'lodash-es';
  109. import customHeader from '/@/components/vent/customHeader.vue';
  110. const SensorMonitorRef = ref();
  111. const deviceBaseList = ref([]);
  112. const activeKey = ref('1');
  113. const deviceKind = ref('');
  114. const deviceTypeOption = ref([]);
  115. // 默认初始是第一行
  116. const selectRowIndex = ref(0);
  117. const dataSource = ref([]);
  118. const detailDataSource = ref<any[]>([]);
  119. const historyDataSource = ref<any[]>([]);
  120. const chartsColumns = ref<any[]>([]);
  121. const loading = ref(true);
  122. const echartsOption = {
  123. grid: {
  124. top: '20%',
  125. left: '10px',
  126. right: '5px',
  127. bottom: '5%',
  128. containLabel: true,
  129. },
  130. toolbox: {
  131. feature: {},
  132. },
  133. };
  134. const echartsOption1 = {
  135. grid: {
  136. top: '20%',
  137. left: '10px',
  138. right: '5px',
  139. bottom: '10%',
  140. containLabel: true,
  141. },
  142. toolbox: {
  143. feature: {},
  144. },
  145. };
  146. const selectData = reactive({
  147. strname: '',
  148. deviceType: '',
  149. deviceID: '',
  150. });
  151. const tabChange = (activeKeyVal) => {
  152. activeKey.value = activeKeyVal;
  153. detailDataSource.value = [];
  154. if (activeKeyVal == 1) {
  155. nextTick(() => {
  156. SensorMonitorRef.value.setSelectedRowKeys([selectData.deviceID]);
  157. });
  158. }
  159. };
  160. // https获取监测数据
  161. let timer: null | NodeJS.Timeout = null;
  162. const getMonitor = (flag?) => {
  163. if (Object.prototype.toString.call(timer) === '[object Null]') {
  164. timer = setTimeout(
  165. async () => {
  166. await getDataSource(deviceKind.value);
  167. if (timer) {
  168. timer = null;
  169. }
  170. getMonitor();
  171. },
  172. flag ? 0 : 1000
  173. );
  174. }
  175. };
  176. const getDataSource = async (devicetype) => {
  177. const type = devicetype ? devicetype : 'modelsensor';
  178. const res = await list({ devicetype: type, pagetype: 'normal' });
  179. dataSource.value = res.msgTxt[0].datalist || [];
  180. dataSource.value.map((data: any) => {
  181. const readData = data.readData;
  182. data = Object.assign(data, readData);
  183. return data;
  184. });
  185. const data: any = toRaw(dataSource.value[selectRowIndex.value]); //maxarea
  186. Object.assign(selectData, data);
  187. // if (chartsColumns.value.length <= 0 && selectData.deviceType) {
  188. // handleChange(selectData.deviceType);
  189. // }
  190. // 如果当前为监测tab
  191. if (activeKey.value === '1') {
  192. // const isHas = detailDataSource.value.find((item) => item['readTime'] === selectData['readTime']);
  193. // // 获取选中数据
  194. // if (!isHas) {
  195. // if (detailDataSource.value.length < 15) {
  196. // detailDataSource.value.push({ ...selectData });
  197. // } else {
  198. // detailDataSource.value.shift();
  199. // detailDataSource.value.push({ ...selectData });
  200. // }
  201. // }
  202. const dataList = cloneDeep(detailDataSource.value);
  203. if (dataList.length < 15) {
  204. dataList.push({ ...selectData });
  205. } else {
  206. dataList.shift();
  207. dataList.push({ ...selectData });
  208. }
  209. detailDataSource.value = dataList;
  210. }
  211. if (loading.value) {
  212. loading.value = false;
  213. }
  214. return data;
  215. };
  216. const getSelectRow = (selectRow, index) => {
  217. if (!selectRow) return;
  218. selectRowIndex.value = index;
  219. const baseData: any = deviceBaseList.value.find((baseData: any) => baseData.id === selectRow.deviceID);
  220. Object.assign(selectData, selectRow, baseData);
  221. if (selectData.deviceType) {
  222. // if (timer) {
  223. // clearTimeout(timer);
  224. // timer = undefined;
  225. // }
  226. if (activeKey.value === '1') detailDataSource.value = [];
  227. if (activeKey.value === '2') historyDataSource.value = [];
  228. handleChange(selectData.deviceType);
  229. }
  230. };
  231. // 获取设备基本信息列表
  232. const getDeviceBaseList = () => {
  233. getTableList({ pageSize: 1000 }).then((res) => {
  234. deviceBaseList.value = res.records;
  235. });
  236. };
  237. function handleChange(type) {
  238. chartsColumns.value = getTableHeaderColumns(type + '_chart');
  239. console.log('[ type ] >', type, chartsColumns.value);
  240. }
  241. function handleSensorChange(type) {
  242. loading.value = true;
  243. handleChange(type);
  244. timer = null;
  245. dataSource.value = [];
  246. detailDataSource.value = [];
  247. }
  248. function refreshEchatrs() {
  249. timer = null;
  250. // getMonitor(true);
  251. console.log('echarts 刷新');
  252. }
  253. function historyDataSourceChange(dataSource) {
  254. historyDataSource.value = dataSource;
  255. if (historyDataSource.value.length > 0) handleChange(historyDataSource.value[0].gdevicetype);
  256. }
  257. onBeforeMount(() => {
  258. getDeviceBaseList();
  259. });
  260. onMounted(async () => {
  261. const res = await deviceList({ devicetype: 'modelsensor' });
  262. const obj = res.find((item) => item.itemValue === 'modelsensor');
  263. deviceTypeOption.value = obj ? obj.children : [];
  264. deviceKind.value = deviceTypeOption.value[0]['itemValue'] || 'modelsensor_monitor';
  265. handleChange(deviceKind.value);
  266. await getMonitor(true);
  267. });
  268. onUnmounted(() => {
  269. if (timer) {
  270. clearTimeout(timer);
  271. timer = undefined;
  272. }
  273. });
  274. </script>
  275. <style lang="less" scoped>
  276. @import '/@/design/theme.less';
  277. @import '/@/design/vent/modal.less';
  278. .padding-0 {
  279. padding: 10px 0 !important;
  280. }
  281. .sensor-container {
  282. height: 100%;
  283. position: relative;
  284. top: 65px;
  285. padding: 10px;
  286. z-index: 999;
  287. // max-height: calc(100vh - 150px);
  288. .@{ventSpace}-tabs {
  289. max-height: calc(100% - 100px);
  290. .tab-item {
  291. max-height: calc(100% - 170px);
  292. overflow-y: auto;
  293. }
  294. }
  295. .title-text {
  296. position: absolute;
  297. top: -24px;
  298. left: 0;
  299. width: 100%;
  300. text-align: center;
  301. color: var(--vent-font-color);
  302. }
  303. .table-box {
  304. // height: calc(60% - 150px);
  305. height: 780px;
  306. padding: 20px 10px;
  307. overflow-y: auto;
  308. }
  309. .box-bg {
  310. // border: 1px solid #4d7ad855;
  311. // border-radius: 2px;
  312. // // background-color: #001d3055;
  313. // -webkit-backdrop-filter: blur(8px);
  314. // backdrop-filter: blur(8px);
  315. // box-shadow: 0 0 10px #5984e055 inset;
  316. // background-color: #00b3ff12;
  317. padding-bottom: 10px;
  318. border: 1px solid #44d3ff70;
  319. border-radius: 2px;
  320. -webkit-backdrop-filter: blur(8px);
  321. box-shadow: 0 0 20px #44b4ff33 inset;
  322. background-color: #ffffff11;
  323. overflow-y: auto;
  324. }
  325. .charts-box {
  326. height: calc(40% - 80px);
  327. padding: 5px 10px;
  328. margin-top: 10px;
  329. }
  330. }
  331. :deep(.@{ventSpace}-tabs-tabpane-active) {
  332. height: 100%;
  333. }
  334. :deep(.@{ventSpace}-tabs-card) {
  335. .@{ventSpace}-tabs-tab {
  336. background: var(--vent-device-manager-control-btn-hover);
  337. border-color: #74e9fe;
  338. border-radius: 0%;
  339. &:hover {
  340. color: #64d5ff;
  341. }
  342. }
  343. .@{ventSpace}-tabs-tab.@{ventSpace}-tabs-tab-active .@{ventSpace}-tabs-tab-btn {
  344. color: aqua;
  345. }
  346. .@{ventSpace}-tabs-nav::before {
  347. border-color: #74e9fe;
  348. }
  349. .@{ventSpace}-picker,
  350. .@{ventSpace}-select-selector {
  351. width: 100% !important;
  352. background: #00000017 !important;
  353. border: 1px solid @vent-form-item-border !important;
  354. input,
  355. .@{ventSpace}-select-selection-item,
  356. .@{ventSpace}-picker-suffix {
  357. color: #fff !important;
  358. }
  359. .@{ventSpace}-select-selection-placeholder {
  360. color: #b7b7b7 !important;
  361. }
  362. }
  363. .@{ventSpace}-pagination-next,
  364. .action,
  365. .@{ventSpace}-select-arrow,
  366. .@{ventSpace}-picker-separator {
  367. color: var(--vent-font-color) !important;
  368. }
  369. .@{ventSpace}-table-cell-row-hover {
  370. background: #264d8833 !important;
  371. }
  372. .@{ventSpace}-table-row-selected {
  373. background: #00c0a311 !important;
  374. td {
  375. background-color: #00000000 !important;
  376. }
  377. }
  378. .@{ventSpace}-table-thead {
  379. // background: linear-gradient(#004a8655 0%, #004a86aa 10%) !important;
  380. background: #3d9dd45d !important;
  381. & > tr > th,
  382. .@{ventSpace}-table-column-title {
  383. // color: #70f9fc !important;
  384. border-color: var(--vent-base-border) !important;
  385. border-left: none !important;
  386. border-right: none !important;
  387. padding: 7px;
  388. }
  389. }
  390. .@{ventSpace}-table-tbody {
  391. tr > td {
  392. padding: 12px;
  393. }
  394. }
  395. .@{ventSpace}-table-tbody > tr:hover.@{ventSpace}-table-row > td {
  396. background-color: #26648855 !important;
  397. }
  398. .jeecg-basic-table-row__striped {
  399. // background: #97efff11 !important;
  400. td {
  401. background-color: #97efff11 !important;
  402. }
  403. }
  404. :deep(.vent-form) {
  405. .@{ventSpace}-select-dropdown {
  406. color: #000000 !important;
  407. }
  408. }
  409. }
  410. </style>