component.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. <template>
  2. <customHeader
  3. :fieldNames="{ label: 'systemname', value: 'id', options: 'children' }"
  4. :options="options"
  5. :optionValue="optionValue"
  6. @change="changeSelectRow"
  7. >
  8. {{ mainTitle }}
  9. </customHeader>
  10. <slot :monitor-data="monitorData"></slot>
  11. <div class="scene-box">
  12. <div class="center-container">
  13. <template v-if="activeKey == 'monitor'">
  14. <slot name="monitor">
  15. <ModuleCommon
  16. v-for="cfg in mainConfig.configs"
  17. :key="cfg.deviceType"
  18. :show-style="cfg.showStyle"
  19. :module-data="cfg.moduleData"
  20. :module-name="cfg.moduleName"
  21. :device-type="cfg.deviceType"
  22. :data="monitorData"
  23. :visible="true"
  24. />
  25. </slot>
  26. </template>
  27. <div v-else class="history-group">
  28. <div v-if="showDeviceList && deviceList.length > 0" class="device-button-group">
  29. <div
  30. v-for="(device, index) in deviceList"
  31. :class="{ 'device-button': true, 'device-active': deviceActive == device.deviceType }"
  32. :key="index"
  33. @click="deviceChange(index)"
  34. >
  35. {{ device.deviceName }}
  36. </div>
  37. </div>
  38. <div class="history-container">
  39. <slot name="history" :device-type="deviceType" :device-id="optionValue">
  40. <HistoryTable
  41. v-if="activeKey == 'monitor_history'"
  42. class="vent-margin-t-20"
  43. :columns-type="deviceType"
  44. :device-type="deviceType"
  45. :sysId="optionValue"
  46. :scroll="{ y: 650 }"
  47. v-bind="monitorHistoryConfig"
  48. />
  49. </slot>
  50. <slot name="handler" :device-type="deviceType" :device-id="optionValue">
  51. <HandlerHistoryTable
  52. v-if="activeKey == 'handler_history'"
  53. class="vent-margin-t-20"
  54. columns-type="operator_history"
  55. :deviceType="deviceType"
  56. v-bind="handlerHistoryConfig"
  57. />
  58. </slot>
  59. <slot name="alarm" :device-type="deviceType" :device-id="optionValue">
  60. <AlarmHistoryTable
  61. v-if="activeKey == 'faultRecord'"
  62. columns-type="alarm"
  63. :device-type="deviceType"
  64. :sys-id="optionValue"
  65. v-bind="alarmHistoryConfig"
  66. />
  67. </slot>
  68. </div>
  69. </div>
  70. </div>
  71. <BottomMenu @change="changeActive" />
  72. </div>
  73. </template>
  74. <script setup lang="ts">
  75. import customHeader from '/@/components/vent/customHeader.vue';
  76. import { ref, onMounted, onUnmounted } from 'vue';
  77. import { getDevice, sysList } from '../comment/comment.api';
  78. import BottomMenu from '/@/views/vent/comment/components/bottomMenu.vue';
  79. import ModuleCommon from '../../home/configurable/components/ModuleCommon.vue';
  80. import HistoryTable from '../comment/HistoryTable.vue';
  81. import HandlerHistoryTable from '../comment/HandlerHistoryTable.vue';
  82. import AlarmHistoryTable from '../comment/AlarmHistoryTable.vue';
  83. import { useRouter } from 'vue-router';
  84. import { Config } from '../../deviceManager/configurationTable/types';
  85. import { message } from 'ant-design-vue';
  86. import _ from 'lodash';
  87. type DeviceType = { deviceType: string; deviceName: string; datalist: any[] };
  88. const props = withDefaults(
  89. defineProps<{
  90. mainTitle: string;
  91. /** 是否显示历史数据上方的设备列表 */
  92. showDeviceList?: boolean;
  93. /** 请求场景数据传入的类型字符 */
  94. strtype: string;
  95. /** 请求场景数据传入的页面类型字符 */
  96. pagetype?: string;
  97. /** 获取各表格配置时依赖的设备类型 */
  98. // deviceType: string;
  99. /** 主要模块配置 */
  100. mainConfig: {
  101. configs: Config[];
  102. /** 获取该场景所含设备及其监测信息的API */
  103. monitorApi?: (params: { deviceType: string; deviceId: number | string }) => Promise<any>;
  104. /** 定时获取监测信息的配置,单位为毫秒,不传入即默认,传0即停用 */
  105. timer?: number;
  106. };
  107. /** 历史数据配置 */
  108. monitorHistoryConfig: {
  109. /** 请求历史数据时传入的类型字符 */
  110. columnsType?: string;
  111. /** 如果默认的设备类型不适用,可以传递固定的类型 */
  112. deviceType?: string;
  113. /** 仅展示已绑定设备,选择是则从系统中获取sysId下已绑定设备。仅能查询到已绑定设备的历史数据 */
  114. onlyBounedDevices?: boolean;
  115. /** 显示历史数据曲线图 */
  116. showHistoryCurve?: boolean;
  117. };
  118. /** 操作历史配置 */
  119. handlerHistoryConfig: {
  120. /** 请求操作历史时传入的类型字符 */
  121. columnsType?: string;
  122. /** 如果默认的设备类型不适用,可以传递固定的类型 */
  123. deviceType?: string;
  124. /** 获取操作历史的API,可以不提供以使用默认的请求 */
  125. deviceListApi?: (params: any) => Promise<any[]>;
  126. };
  127. /** 报警历史配置 */
  128. alarmHistoryConfig: {
  129. /** 请求报警历史时传入的类型字符 */
  130. columnsType?: string;
  131. /** 如果默认的设备类型不适用,可以传递固定的类型 */
  132. deviceType?: string;
  133. /** 获取报警历史的API,可以不提供以使用默认的请求 */
  134. list?: (params: any) => Promise<any[]>;
  135. /** 获取设备以供报警历史过滤的API,可以不提供以使用默认的请求 */
  136. deviceListApi?: (params: any) => Promise<any[]>;
  137. };
  138. }>(),
  139. {
  140. mainConfig: () => ({
  141. configs: [],
  142. }),
  143. monitorHistoryConfig: () => ({}),
  144. handlerHistoryConfig: () => ({}),
  145. alarmHistoryConfig: () => ({}),
  146. pagetype: 'normal',
  147. showDeviceList: true,
  148. }
  149. );
  150. const { currentRoute } = useRouter();
  151. const activeKey = ref('monitor');
  152. function changeActive(activeValue) {
  153. activeKey.value = activeValue;
  154. }
  155. const options = ref([]);
  156. const optionValue = ref('');
  157. /** 获取左上角场景选择框数据的方法,如果此时初始场景未赋值则选择首项做初始化 */
  158. async function getSysDataSource() {
  159. const res = await sysList({ strtype: props.strtype, pagetype: props.pagetype }).catch(() => {
  160. message.error('获取场景数据时发生了错误');
  161. });
  162. // 初始时选择第一条数据
  163. options.value = res.records || [];
  164. if (!optionValue.value) {
  165. changeSelectRow(options.value[0]['id']);
  166. }
  167. }
  168. // 切换检测数据
  169. function changeSelectRow(deviceID) {
  170. optionValue.value = deviceID;
  171. getDeviceList();
  172. }
  173. //关联设备
  174. const deviceList = ref<DeviceType[]>([]);
  175. const deviceActive = ref('');
  176. const deviceType = ref('');
  177. function deviceChange(index) {
  178. deviceType.value = deviceList.value[index]?.deviceType || '';
  179. deviceActive.value = deviceList.value[index]?.deviceType || '';
  180. }
  181. // 查询关联设备列表
  182. async function getDeviceList() {
  183. const { msgTxt = [] } = await getDevice({ devicetype: 'sys', systemID: optionValue.value }).catch(() => {
  184. message.error('获取已绑定设备时发生了错误');
  185. });
  186. deviceList.value = msgTxt.reduce((arr, item) => {
  187. const data = item.datalist.map((data: any) => {
  188. return Object.assign(data, data.readData);
  189. });
  190. // sys代表场景本身,应该过滤掉去处理该场景下的关联设备
  191. if (item.type != 'sys') {
  192. arr.unshift({
  193. deviceType: item.type,
  194. deviceName: item.typeName || item.datalist[0].typeName,
  195. datalist: data,
  196. });
  197. }
  198. return arr;
  199. }, []);
  200. if (!deviceActive.value) {
  201. deviceChange(0);
  202. }
  203. }
  204. let timer: NodeJS.Timeout;
  205. const monitorData = ref<any>({});
  206. /** 获取本场景下所绑定的设备,将监测数据赋值 */
  207. async function getMonitor() {
  208. if (props.mainConfig.monitorApi) {
  209. monitorData.value = await props.mainConfig
  210. .monitorApi({
  211. deviceType: deviceType.value,
  212. deviceId: optionValue.value,
  213. })
  214. .catch(() => {
  215. message.error('获取已绑定设备时发生了错误');
  216. });
  217. } else if (optionValue.value) {
  218. const { msgTxt = [] } = await getDevice({ devicetype: 'sys', systemID: optionValue.value }).catch(() => {
  219. message.error('获取已绑定设备时发生了错误');
  220. });
  221. msgTxt.forEach((item) => {
  222. _.set(monitorData.value, item.type, item.datalist);
  223. });
  224. }
  225. }
  226. onMounted(async () => {
  227. if (currentRoute.value && currentRoute.value['query'] && currentRoute.value['query']['id']) {
  228. optionValue.value = currentRoute.value['query']['id'] as string;
  229. }
  230. await getSysDataSource();
  231. if (props.mainConfig.timer !== 0) {
  232. timer = setInterval(() => {
  233. getMonitor();
  234. }, props.mainConfig.timer || 5000);
  235. } else {
  236. getMonitor();
  237. }
  238. });
  239. onUnmounted(() => {
  240. clearInterval(timer);
  241. });
  242. </script>
  243. <style lang="less" scoped>
  244. @import '/@/design/vent/modal.less';
  245. @ventSpace: zxm;
  246. .scene-box {
  247. margin-top: 40px;
  248. pointer-events: none;
  249. .history-group {
  250. margin-top: 80px;
  251. padding: 0 10px;
  252. .history-container {
  253. pointer-events: auto;
  254. background: #6195af1a;
  255. // width: 100%;
  256. border: 1px solid #00fffd22;
  257. padding: 10px 0;
  258. box-shadow: 0 0 20px #44b4ff33 inset;
  259. }
  260. }
  261. .device-button-group {
  262. // margin: 0 20px;
  263. padding: 0 10px;
  264. display: flex;
  265. pointer-events: auto;
  266. position: relative;
  267. &::after {
  268. position: absolute;
  269. content: '';
  270. width: 100%;
  271. height: 2px;
  272. top: 30px;
  273. left: -1px;
  274. border-bottom: 1px solid #0efcff;
  275. }
  276. .device-button {
  277. padding: 4px 15px;
  278. position: relative;
  279. display: flex;
  280. justify-content: center;
  281. align-items: center;
  282. font-size: 14px;
  283. color: #fff;
  284. cursor: pointer;
  285. margin: 0 3px;
  286. &::before {
  287. content: '';
  288. position: absolute;
  289. top: 0;
  290. right: 0;
  291. bottom: 0;
  292. left: 0;
  293. border: 1px solid #6176af;
  294. transform: skewX(-38deg);
  295. background-color: rgba(0, 77, 103, 85%);
  296. z-index: -1;
  297. }
  298. }
  299. .device-active {
  300. // color: #0efcff;
  301. &::before {
  302. border-color: #0efcff;
  303. box-shadow: 1px 1px 3px 1px #0efcff inset;
  304. }
  305. }
  306. }
  307. }
  308. .center-container {
  309. width: 100%;
  310. height: calc(100% - 150px);
  311. }
  312. .input-box {
  313. display: flex;
  314. align-items: center;
  315. padding-left: 10px;
  316. .input-title {
  317. color: #73e8fe;
  318. width: auto;
  319. }
  320. .@{ventSpace}-input-number {
  321. border-color: #ffffff88 !important;
  322. }
  323. margin-right: 10px;
  324. }
  325. </style>