index.vue 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. <template>
  2. <div class="scene-box" v-if="routerParam !== 'home'">
  3. <!-- <div class="scene-box"> -->
  4. <div class="device-header">智能通风管控系统</div>
  5. <div class="select-node" :class="{ 'node-select-show': !treeShow, 'node-select-hide': treeShow, }" @click="showTree('treeShow', true)">
  6. <SvgIcon class="is-expansion-icon put-away-icon" size="38" name="expansion" />
  7. <span class="title">{{ treeNodeTitle }}</span>
  8. </div>
  9. <div class="device-select" :class="{ 'device-select-show': treeShow, 'device-select-hide': !treeShow, }" >
  10. <SvgIcon class="is-expansion-icon expansion-icon" size="28" name="put-away" @click="showTree('treeShow', false)"/>
  11. <div class="device-select-box">
  12. <a-tree :show-line="true" :tree-data="treeData" v-model:selectedKeys="selectedKeys"
  13. v-model:expandedKeys="expandedKeys" @select="onSelect">
  14. </a-tree>
  15. </div>
  16. </div>
  17. <div class="location-icon" :class="{ 'location-btn-show': !locationSettingShow, 'location-btn-hide': locationSettingShow, }" @click="showTree('location', true)" >
  18. <SvgIcon size="18" name="put-away" />
  19. <span class="location-text">定位图标显示</span>
  20. </div>
  21. <div class="location-select" :class="{ 'location-select-show': locationSettingShow, 'location-select-hide': !locationSettingShow, }">
  22. <div class="location-select-box">
  23. <div class="location-top-title" @click="showTree('location', false)">
  24. <SvgIcon class="is-expansion-icon location-expansion-icon" size="28" name="expansion" />
  25. <div class="title">定位图标显示</div>
  26. </div>
  27. <div class="location-container">
  28. <template v-for="location in locationList" :key="location.deviceType">
  29. <div class="location-item">
  30. <div class="item-title">{{ location.title }}&nbsp;:</div>
  31. <div>
  32. <a-radio-group v-model:value="location.Visible" :name="location.deviceType">
  33. <a-radio :value="1">是</a-radio>
  34. <a-radio :value="0">否</a-radio>
  35. </a-radio-group>
  36. </div>
  37. </div>
  38. </template>
  39. <div class="location-bottom-btn">
  40. <span @click="setLocation">提交</span>
  41. </div>
  42. </div>
  43. </div>
  44. </div>
  45. <div class="bottom-tabs-box" @mousedown="setDivHeight($event, 250, scroll)" id="monitorBox">
  46. <div class="to-small" @click="toHome"></div>
  47. <div class="device-button-group" v-if="deviceList.length > 0">
  48. <div class="device-button" :class="{ 'device-active': deviceActive == device.deviceType }"
  49. v-for="(device, index) in deviceList" :key="index" @click="monitorChange(index)">{{ device.deviceName }}</div>
  50. <div class="enter-detail" @click="goDetail()">
  51. <send-outlined />
  52. {{ treeNodeTitle }}详情
  53. </div>
  54. </div>
  55. <a-tabs class="tabs-box" v-model:activeKey="activeKey" @change="tabChange">
  56. <a-tab-pane key="1" tab="实时监测">
  57. <template v-if="deviceType == 'fan' && activeKey == '1' && isRefresh">
  58. <GroupMonitorTable :dataSource="dataSource" :columnsType="`${deviceType}_monitor`" />
  59. </template>
  60. <template v-else-if="activeKey == '1' && isRefresh">
  61. <MonitorTable ref="monitorTable" :columnsType="`${deviceType}_monitor`" :dataSource="dataSource"
  62. design-scope="device_monitor" :isShowPagination="false" :isShowActionColumn="true" title="设备监测" :scroll="scroll">
  63. <template #action="{ record }">
  64. <TableAction :actions="[
  65. {
  66. label: '详情',
  67. onClick: goDetail.bind(null, record),
  68. },
  69. {
  70. label: '定位',
  71. onClick: goLocation.bind(null, record),
  72. },
  73. ]" />
  74. </template>
  75. <template #filterCell="{ column, record }">
  76. <template v-if="deviceType.startsWith('gate')">
  77. <template v-if="record.frontGateOpenCtrl">
  78. <a-tag v-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 0 && record.frontGateClose == 0" color="red">正在打开</a-tag>
  79. <a-tag v-else-if="column.dataIndex === 'frontGateOpen'" color="processing">打开</a-tag>
  80. </template>
  81. <template v-else>
  82. <a-tag v-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 0 && record.frontGateClose == 0" color="red">正在关闭</a-tag>
  83. <a-tag v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 0 && record.frontGateClose == 1" color="default">关闭</a-tag>
  84. <a-tag v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 1 && record.frontGateClose == 0" color="default">打开</a-tag>
  85. </template>
  86. <template v-if="record.rearGateOpenCtrl">
  87. <a-tag v-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 0 && record.rearGateClose == 0" color="red">正在打开</a-tag>
  88. <a-tag v-else-if="column.dataIndex === 'rearGateOpen'" color="processing">打开</a-tag>
  89. </template>
  90. <template v-else>
  91. <a-tag v-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 0 && record.rearGateClose == 0" color="red">正在关闭</a-tag>
  92. <a-tag v-else-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 0 && record.rearGateClose == 1" color="default">关闭</a-tag>
  93. <a-tag v-else-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 1 && record.rearGateClose == 0" color="default">打开</a-tag>
  94. </template>
  95. </template>
  96. <template v-if="deviceType.startsWith('windrect')">
  97. <a-tag v-if="column.dataIndex === 'sign'" :color="record.sign == 0 ? '#95CF65' : record.sign == 1 ? '#4590EA' : '#9876AA'"> {{
  98. record.sign == 0 ? '高位' : record.sign == 1 ? '中位' : '低位'
  99. }}</a-tag>
  100. <template v-if="record && column && column.dataIndex === 'isRun' && record.isRun">
  101. <a-tag v-if="record.isRun == -2 || record.isRun == -1" :color="record.isRun == -2 ? '#95CF65' : '#ED5700'">{{
  102. record.isRun == -2 ? '空闲' : '等待'
  103. }}</a-tag>
  104. <a-tag v-else-if="record.isRun == 100" color="#4693FF">完成</a-tag>
  105. <Progress v-else :percent="Number(record.isRun)" size="small" status="active" />
  106. </template>
  107. </template>
  108. <a-tag v-if="column.dataIndex === 'warnFlag'" :color="record.warnFlag == 0 ? 'green' : record.warnFlag == 1 ? '#FF5812' : 'gray'"> {{
  109. record.warnFlag == 0 ? '正常' : record.warnFlag == 1 ? '报警' : record.warnFlag == 2 ? '断开' : '未监测'
  110. }}</a-tag>
  111. <a-tag v-if="column.dataIndex === 'netStatus'" :color="record.netStatus == 0 ? 'default' : 'green'">{{
  112. record.netStatus == 0 ? '断开' : '连接'
  113. }}</a-tag>
  114. </template>
  115. </MonitorTable>
  116. </template>
  117. </a-tab-pane>
  118. <a-tab-pane key="2" tab="历史数据">
  119. <div class="tab-item">
  120. <HistoryTable ref="historyTable" v-if="activeKey == '2' && isRefresh" :sysId="systemID" :columns-type="`${deviceType}`"
  121. :device-type="deviceType"
  122. :device-list-api="getDeviceList.bind(null, { strtype: deviceType, sysId: systemID })"
  123. designScope="device-history"
  124. :scroll="scroll"
  125. />
  126. </div>
  127. </a-tab-pane>
  128. <a-tab-pane key="3" tab="报警历史">
  129. <div class="tab-item">
  130. <AlarmHistoryTable ref="alarmHistoryTable" v-if="activeKey == '3' && isRefresh" :sysId="systemID" columns-type="alarm"
  131. :device-type="deviceType"
  132. :device-list-api="getDeviceList.bind(null, { strtype: deviceType, sysId: systemID })"
  133. :scroll="scroll"
  134. designScope="alarm-history" />
  135. </div>
  136. </a-tab-pane>
  137. <a-tab-pane key="4" tab="操作历史">
  138. <div class="tab-item">
  139. <HandlerHistoryTable ref="handlerHistoryTable" v-if="activeKey == '4' && isRefresh" :sysId="systemID"
  140. columns-type="operatorhistory" :device-type="deviceType"
  141. :device-list-api="getDeviceList.bind(null, { strtype: deviceType, sysId: systemID })"
  142. :scroll="scroll"
  143. designScope="operator-history" />
  144. </div>
  145. </a-tab-pane>
  146. </a-tabs>
  147. </div>
  148. <component v-if="modalVisible" :is="currentModal" v-model:visible="modalVisible" :dataSource="dataSource"
  149. :activeID="activeID" />
  150. </div>
  151. </template>
  152. <script setup lang="ts">
  153. import { ref, onMounted, onUnmounted, ComponentOptions, shallowRef, nextTick, watch, reactive } from 'vue'
  154. import { SendOutlined } from '@ant-design/icons-vue';
  155. import { list, getDeviceList, getDeviceTypeList } from './device.api'
  156. import AlarmHistoryTable from '../comment/AlarmHistoryTable.vue';
  157. import HistoryTable from '../comment/HistoryTable.vue';
  158. import HandlerHistoryTable from '../comment/HandlerHistoryTable.vue';
  159. import MonitorTable from '../comment/MonitorTable.vue';
  160. import GroupMonitorTable from '../comment/GroupMonitorTable.vue';
  161. import { TreeProps, message, Progress } from 'ant-design-vue';
  162. import { TableAction } from '/@/components/Table';
  163. import FiberModal from './modal/fiber.modal.vue';
  164. import BundleModal from './modal/bundle.modal.vue'
  165. import DustModal from './modal/dust.modal.vue'
  166. import { SvgIcon } from '/@/components/Icon';
  167. import { getActions } from '/@/qiankun/state';
  168. import { useRouter } from 'vue-router';
  169. import { setDivHeight } from '/@/utils/event';
  170. type DeviceType = { deviceType: string, deviceName: string, datalist: any[] };
  171. const router = useRouter()
  172. const actions = getActions();
  173. actions.setGlobalState({ pageObj: { pageType: 'home' } });
  174. const monitorTable = ref()
  175. const historyTable = ref()
  176. const alarmHistoryTable = ref()
  177. const handlerHistoryTable = ref()
  178. const routerParam = ref('home') // 默认进来时首页
  179. // 模态框
  180. const currentModal = shallowRef<Nullable<ComponentOptions>>(null); //模态框
  181. const modalVisible = ref<Boolean>(false); // 模态框是否可见
  182. //
  183. // const drawerHeight = ref(240) // 监测框最小高度
  184. const treeShow = ref(true) //是否显示树形菜单
  185. const locationSettingShow = ref(false) //是否显示树形菜单
  186. const treeNodeTitle = ref('') // 选中的树形标题
  187. const locationList = ref([]) //巷道定位图标显示列表
  188. const deviceList = ref<DeviceType[]>([]) //关联设备列表
  189. const deviceActive = ref('')
  190. const activeKey = ref('1'); // tab key
  191. const dataSource = shallowRef([]) // 实时监测数据
  192. const activeID = ref('') // 打开详情modal时监测的设备id
  193. const deviceType = ref('') // 监测设备类型
  194. const systemType = ref('')
  195. const systemID = ref('') // 系统监测时,系统id
  196. const selectedKeys = ref<string[]>([]);
  197. const expandedKeys = ref<string[]>([]);
  198. const isRefresh = ref(true)
  199. const scroll = reactive({
  200. y: 240
  201. })
  202. const treeData = ref<TreeProps['treeData']>([]);
  203. //树形菜单选择事件
  204. const onSelect: TreeProps['onSelect'] = (keys, e) => {
  205. deviceType.value = ''
  206. systemID.value = ''
  207. deviceList.value = []
  208. if (e.node.parent && (e.node.parent.node.type.toString()).startsWith('sys')) {
  209. systemType.value = e.node.parent.node.type
  210. deviceType.value = e.node.parent.node.type
  211. systemID.value = e.node.type
  212. // 传递工作面id信息,用于定位
  213. actions.setGlobalState({ locationObj: { pageType: deviceType.value, deviceid: systemID.value }, pageObj: null });
  214. } else {
  215. systemType.value = e.node.type
  216. deviceType.value = e.node.type
  217. }
  218. selectedKeys.values = keys
  219. treeNodeTitle.value = e.node.title
  220. if(timer) clearTimeout(timer)
  221. timer = undefined
  222. if (monitorTable.value) monitorTable.value.setLoading(true)
  223. nextTick(() => {
  224. setTimeout(() => {
  225. timer = null
  226. getMonitor()
  227. }, 0)
  228. })
  229. };
  230. function tabChange(activeKeyVal) {
  231. activeKey.value = activeKeyVal;
  232. };
  233. function showTree(flag, value) {
  234. if(flag == 'treeShow') treeShow.value = value
  235. if (flag == 'location') locationSettingShow.value = value
  236. }
  237. async function getDeviceType() {
  238. const result = await getDeviceTypeList({})
  239. if (result.length > 0) {
  240. const dataSource = <TreeProps['treeData']>[]
  241. let key = '0'
  242. const getData = (resultList, dataSourceList, keyVal) => {
  243. resultList.forEach((item, index) => {
  244. if (item.children && item.children.length > 0) {
  245. const children = getData(item.children, [], `${keyVal}-${index}`)
  246. dataSourceList.push({
  247. children: children,
  248. title: item.itemText,
  249. key: `${keyVal}-${index}`,
  250. type: item.itemValue,
  251. });
  252. } else {
  253. dataSourceList.push({
  254. children: [],
  255. title: item.itemText,
  256. key: `${keyVal}-${index}`,
  257. type: item.itemValue,
  258. });
  259. }
  260. });
  261. return dataSourceList
  262. }
  263. treeData.value = getData(result, dataSource, key)
  264. }
  265. }
  266. // https获取监测数据
  267. let timer: null | NodeJS.Timeout = undefined;
  268. function getMonitor() {
  269. if(deviceType.value){
  270. if (timer) timer = null
  271. if (Object.prototype.toString.call(timer) === '[object Null]') {
  272. timer = setTimeout(async () => {
  273. await getDataSource()
  274. if (timer) {
  275. getMonitor();
  276. }
  277. }, 1000);
  278. }
  279. }
  280. };
  281. async function getDataSource() {
  282. if (deviceType.value && deviceType.value.startsWith('sys') && systemID.value) {
  283. const res = await list({ devicetype: 'sys', systemID: systemID.value });
  284. const result = res.msgTxt;
  285. const deviceArr = <DeviceType[]>[]
  286. result.forEach(item => {
  287. const data = item['datalist'].filter((data: any) => {
  288. const readData = data.readData;
  289. return Object.assign(data, readData);
  290. })
  291. if (item.type != 'sys') {
  292. deviceArr.unshift({ deviceType: item.type, deviceName: item['typeName'] ? item['typeName'] : item['datalist'][0]['typeName'], datalist: data })
  293. }
  294. })
  295. deviceList.value = deviceArr
  296. if(deviceArr.length > 0){
  297. if(deviceArr[1]){
  298. deviceActive.value = deviceArr[1].deviceType
  299. monitorChange(1)
  300. }else{
  301. deviceActive.value = deviceArr[0].deviceType
  302. monitorChange(0)
  303. }
  304. }
  305. } else {
  306. const res = await list({ devicetype: deviceType.value, pagetype: 'normal' })
  307. if (res.msgTxt[0]) {
  308. dataSource.value = res.msgTxt[0].datalist || [];
  309. dataSource.value.filter((data: any) => {
  310. const readData = data.readData;
  311. return Object.assign(data, readData);
  312. });
  313. }
  314. }
  315. }
  316. function goLocation(record) {
  317. actions.setGlobalState({ locationId: record.deviceID, locationObj: null, pageObj: null });
  318. }
  319. function goDetail(record?) {
  320. if (record) {
  321. if (deviceType.value.startsWith('fiber')) {
  322. activeID.value = record.deviceID
  323. currentModal.value = FiberModal
  324. modalVisible.value = true;
  325. } else if (deviceType.value.startsWith('dusting')) { //bundletube
  326. activeID.value = record.deviceID
  327. currentModal.value = DustModal
  328. modalVisible.value = true;
  329. } else if (deviceType.value.startsWith('bundletube')) {
  330. activeID.value = record.deviceID
  331. currentModal.value = BundleModal
  332. modalVisible.value = true;
  333. } else if (deviceType.value.indexOf("gate") != -1) {
  334. const newPage = router.resolve({ path: '/monitorChannel/monitor-gate' })
  335. window.open(newPage.href, '_blank')
  336. } else if (deviceType.value.indexOf("window") != -1) {
  337. const newPage = router.resolve({ path: '/monitorChannel/monitor-window' })
  338. window.open(newPage.href, '_blank')
  339. } else if (deviceType.value.indexOf("windrect") != -1) {
  340. const newPage = router.resolve({ path: '/monitorChannel/monitor-windrect' })
  341. window.open(newPage.href, '_blank')
  342. } else if (deviceType.value.indexOf("fanmain") != -1) {
  343. const newPage = router.resolve({ path: '/monitorChannel/monitor-fan-main' })
  344. window.open(newPage.href, '_blank')
  345. } else if (deviceType.value.indexOf("fanlocal") != -1) {
  346. const newPage = router.resolve({ path: '/monitorChannel/monitor-fan-local' })
  347. window.open(newPage.href, '_blank')
  348. } else if (deviceType.value.indexOf("nitrogen") != -1) {
  349. const newPage = router.resolve({ path: '/compressor-home' })
  350. window.open(newPage.href, '_blank')
  351. } else if (deviceType.value.indexOf("pulping") != -1) {
  352. const newPage = router.resolve({ path: '/grout-home' })
  353. window.open(newPage.href, '_blank')
  354. } else if (deviceType.value.indexOf("pressurefan") != -1) {
  355. const newPage = router.resolve({ path: '/nitrogen/home' })
  356. window.open(newPage.href, '_blank')
  357. } else if (deviceType.value.indexOf("chamber") != -1) {
  358. const newPage = router.resolve({ path: '/chamber-home' })
  359. window.open(newPage.href, '_blank')
  360. } else {
  361. message.info('待开发。。。')
  362. }
  363. } else {
  364. if (systemType.value.indexOf("sys_dongshi") != -1) {
  365. const newPage = router.resolve({ path: '/chamber-home', query: { id: systemID.value } })
  366. window.open(newPage.href, '_blank')
  367. } else {
  368. message.info('待开发。。。')
  369. }
  370. }
  371. }
  372. function toHome() {
  373. deviceList.value = []
  374. if(timer) clearTimeout(timer)
  375. timer = undefined
  376. deviceType.value = ''
  377. actions.setGlobalState({ pageObj: { pageType: 'home' } });
  378. }
  379. async function findTreeDataValue(data: [], obj) {
  380. const findDeviceType = (data: [], obj) => {
  381. let type = ''
  382. if (obj.deviceid) {
  383. type = obj.deviceid
  384. } else {
  385. type = obj.deviceType
  386. }
  387. data.find((item: any) => {
  388. if (item.children.length > 0) {
  389. findDeviceType(item.children, obj)
  390. }
  391. if (item.type == type) {
  392. deviceType.value = obj.deviceid ? 'sys' : item.type
  393. if (obj.deviceid) systemID.value = obj.deviceid
  394. selectedKeys.value = [item.key]
  395. expandedKeys.value = [item.key]
  396. treeNodeTitle.value = item.title
  397. }
  398. })
  399. }
  400. findDeviceType(data, obj)
  401. if (timer === undefined) {
  402. timer = null
  403. await getDataSource()
  404. getMonitor()
  405. }
  406. }
  407. function monitorChange(index) {
  408. dataSource.value = []
  409. deviceActive.value = deviceType.value = deviceList.value[index].deviceType
  410. if (activeKey.value == '1' && monitorTable.value) {
  411. monitorTable.value.setLoading(true)
  412. dataSource.value = deviceList.value[index].datalist
  413. }
  414. if (activeKey.value == '2' && historyTable.value) {
  415. historyTable.value.setLoading(true)
  416. }
  417. if (activeKey.value == '3' && alarmHistoryTable.value) {
  418. alarmHistoryTable.value.setLoading(true)
  419. }
  420. if (activeKey.value == '4' && handlerHistoryTable.value) {
  421. handlerHistoryTable.value.setLoading(true)
  422. }
  423. }
  424. /**
  425. * 设置巷道设备定位图标的显示与隐藏
  426. */
  427. function setLocation() {
  428. let locationStr = ''
  429. locationList.value.forEach((item: any) => {
  430. if(item.Visible) {
  431. locationStr = locationStr ? locationStr +','+ item.value : item.value
  432. }
  433. })
  434. actions.setGlobalState({ locationId: null, locationObj: null, pageObj:null, locationPlane: locationStr });
  435. setTimeout(() => {
  436. message.success('设置成功')
  437. }, 600)
  438. }
  439. watch(() => scroll.y, () => {
  440. isRefresh.value = false
  441. nextTick(() => {
  442. isRefresh.value = true
  443. } )
  444. })
  445. onMounted(async () => {
  446. actions.onGlobalStateChange((newState, prev) => {
  447. for (const key in newState) {
  448. if (key === 'pageObj') {
  449. const pageObj = newState[key]
  450. if (pageObj) {
  451. routerParam.value = pageObj.pageType
  452. if(routerParam.value !== 'home'){
  453. if (pageObj.deviceid) {
  454. findTreeDataValue(treeData.value, { deviceid: pageObj.deviceid })
  455. } else {
  456. findTreeDataValue(treeData.value, { deviceType: pageObj.pageType })
  457. }
  458. }
  459. const posShowData = pageObj.locationPlane
  460. if (posShowData) {
  461. locationList.value = posShowData
  462. }
  463. }
  464. }
  465. }
  466. })
  467. await getDeviceType()
  468. })
  469. onUnmounted(() => {
  470. if (timer) {
  471. clearTimeout(timer);
  472. }
  473. timer = undefined;
  474. })
  475. </script>
  476. <style lang="less" scoped >
  477. @import '/@/design/vent/modal.less';
  478. @ventSpace: zxm;
  479. .device-header {
  480. position: fixed;
  481. width: 100%;
  482. height: 56px;
  483. background: url('/@/assets/images/vent/home/modal-top.png');
  484. text-align: center;
  485. line-height: 56px;
  486. font-size: 28px;
  487. color: #ffffffdd;
  488. font-weight: 600;
  489. z-index: -1;
  490. }
  491. .select-node {
  492. position: fixed;
  493. top: 60px;
  494. left: 10px;
  495. color: #fff;
  496. display: flex;
  497. justify-content: center;
  498. font-size: 22px;
  499. .title {
  500. margin-left: 10px;
  501. }
  502. }
  503. .expansion-icon {
  504. background: url('/@/assets/images/vent/home/tree-icon-bg.png') no-repeat;
  505. background-size: contain;
  506. position: absolute;
  507. left: 190px;
  508. top: 25px;
  509. &:hover {
  510. background: url('/@/assets/images/vent/home/tree-icon-hover-bg.png') no-repeat;
  511. background-size: contain;
  512. }
  513. }
  514. .device-select {
  515. width: 250px;
  516. height: 500px;
  517. background: url('/@/assets/images/vent/home/tree-bg.png') no-repeat;
  518. position: fixed;
  519. top: 60px;
  520. left: 10px;
  521. background-size: contain;
  522. pointer-events: auto;
  523. padding: 20px 10px 30px 10px;
  524. }
  525. .is-expansion-icon {
  526. padding: 5px;
  527. pointer-events: auto;
  528. z-index: 999;
  529. }
  530. .device-select-show {
  531. left: 10px;
  532. animation-name: treeShow;
  533. /* 持续时间 */
  534. animation-duration: 1s;
  535. transition: all 1s cubic-bezier(0.165, 0.84, 0.44, 1) .5s;
  536. }
  537. .device-select-hide {
  538. left: -250px;
  539. animation-name: treeHide;
  540. /* 持续时间 */
  541. animation-duration: 1s;
  542. transition: all 1s cubic-bezier(0.165, 0.84, 0.44, 1) .5s;
  543. }
  544. .node-select-show {
  545. width: 276px;
  546. height: 44px;
  547. background: url('/@/assets/images/vent/home/tree-expansion-bg.png') no-repeat;
  548. left: 10px;
  549. animation-name: treeShow;
  550. /* 持续时间 */
  551. animation-duration: 1s;
  552. transition: all 1s cubic-bezier(0.165, 0.84, 0.44, 1) .5s;
  553. display: flex;
  554. align-items: center;
  555. margin-left: 0;
  556. justify-content: flex-start;
  557. pointer-events: auto;
  558. &:hover{
  559. background: url('/@/assets/images/vent/home/tree-expansion-hover-bg.png') no-repeat;
  560. }
  561. .put-away-icon {
  562. position: relative;
  563. display: inline-block;
  564. left: 4px;
  565. }
  566. }
  567. .node-select-hide {
  568. left: -400px;
  569. animation-name: treeHide;
  570. /* 持续时间 */
  571. animation-duration: 1s;
  572. transition: all 1s cubic-bezier(0.165, 0.84, 0.44, 1) .5s;
  573. }
  574. .device-select-box {
  575. width: 208px;
  576. height: 450px;
  577. overflow-y: auto;
  578. color: #fff;
  579. :deep(.zxm-tree) {
  580. background: transparent !important;
  581. color: #fff !important;
  582. .zxm-tree-switcher {
  583. background: transparent !important;
  584. }
  585. .zxm-tree-node-content-wrapper.zxm-tree-node-selected {
  586. background-color: #00b1c8;
  587. }
  588. .zxm-tree-node-content-wrapper:hover {
  589. background-color: #00b1c855;
  590. }
  591. input {
  592. height: 0px !important;
  593. }
  594. }
  595. &::-webkit-scrollbar-track {
  596. -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
  597. border-radius: 10px;
  598. background: #ededed22;
  599. height: 100px;
  600. }
  601. &::-webkit-scrollbar-thumb {
  602. -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
  603. background: #4288A444;
  604. }
  605. }
  606. .location-icon{
  607. width: 46px;
  608. height: 178px;
  609. position: absolute;
  610. top: 60px;
  611. // right: 0;
  612. background: url('/@/assets/images/vent/home/location-bg.png') no-repeat;
  613. background-size: contain;
  614. writing-mode: vertical-lr;
  615. line-height: 46px;
  616. color: #fff;
  617. padding-top: 10px;
  618. pointer-events: auto;
  619. cursor: pointer;
  620. &:hover{
  621. background: url('/@/assets/images/vent/home/location-hover-bg.png') no-repeat;
  622. }
  623. .location-text{
  624. padding-top: 20px;
  625. letter-spacing: 3px;
  626. font-size: 16px;
  627. }
  628. }
  629. .location-select{
  630. position: fixed;
  631. top: 60px;
  632. // right: 240px;
  633. pointer-events: auto;
  634. .location-select-box{
  635. width: 100%;
  636. height: 100%;
  637. position: relative;
  638. &::before{
  639. content: "";
  640. position: absolute;
  641. width: 230px;
  642. height: 500px;
  643. top: 0;
  644. left: 0;
  645. background: url('/@/assets/images/vent/home/tree-bg.png') no-repeat;
  646. background-size: contain;
  647. transform: rotateY(180deg);
  648. z-index: -1;
  649. // &:hover {
  650. // background: url('/@/assets/images/vent/home/tree-icon-hover-bg.png') no-repeat;
  651. // background-size: contain;
  652. // }
  653. }
  654. .location-top-title{
  655. color: #fff;
  656. position: absolute;
  657. width: 225px;
  658. height: 68px;
  659. background: url('/@/assets/images/vent/home/turn-location-top-bg.png') no-repeat;
  660. background-size: contain;
  661. top: 5px;
  662. left: 5px;
  663. display: flex;
  664. flex-direction: row;
  665. justify-content: space-between;
  666. align-items: flex-end;
  667. .title{
  668. font-size: 18px;
  669. position: relative;
  670. top: -14px;
  671. right: 15px;
  672. }
  673. }
  674. .location-expansion-icon {
  675. background: url('/@/assets/images/vent/home/tree-icon-cover-bg.png') no-repeat;
  676. background-size: contain;
  677. position: relative;
  678. left: 10px;
  679. top: -15px;
  680. padding: 5px;
  681. &:hover {
  682. background: url('/@/assets/images/vent/home/tree-icon-cover-hover-bg.png') no-repeat;
  683. background-size: contain;
  684. }
  685. }
  686. }
  687. .location-container{
  688. width: 200px;
  689. height: 390px;
  690. position: absolute;
  691. display: flex;
  692. flex-direction: column;
  693. top: 80px;
  694. left: 18px;
  695. overflow-y: auto;
  696. .location-item{
  697. color: #fff;
  698. line-height: 30px;
  699. display: flex;
  700. justify-content: space-between;
  701. background-image: linear-gradient(to left, #39f5ff05, #39f5ff10);
  702. margin: 3px 0;
  703. .item-title{
  704. width: 80px;
  705. text-align: right;
  706. color: #87f1ff;
  707. }
  708. }
  709. .location-bottom-btn{
  710. width: 100%;
  711. color: #fff;
  712. display: flex;
  713. justify-content: flex-end;
  714. margin-top: 20px;
  715. span{
  716. display: inline-block;
  717. width: 100%;
  718. background: #00709955;
  719. border-radius: 3px;
  720. border: 1px solid rgba(174, 243, 255, 0.3);
  721. text-align: center;
  722. padding: 2px 0;
  723. cursor: pointer;
  724. &:hover{
  725. background: #00557422;
  726. }
  727. }
  728. }
  729. }
  730. }
  731. .location-select-show{
  732. right: 240px;
  733. animation-name: locationShow;
  734. /* 持续时间 */
  735. animation-duration: 1s;
  736. transition: all 1s cubic-bezier(0.165, 0.84, 0.44, 1) .5s;
  737. }
  738. .location-select-hide{
  739. right: -2px;
  740. animation-name: locationHide;
  741. /* 持续时间 */
  742. animation-duration: 1s;
  743. transition: all 1s cubic-bezier(0.165, 0.84, 0.44, 1) .5s;
  744. }
  745. .location-btn-show{
  746. right: -0px;
  747. animation-name: locationBtnShow;
  748. /* 持续时间 */
  749. animation-duration: 1s;
  750. transition: all 1s cubic-bezier(0.165, 0.84, 0.44, 1) .5s;
  751. }
  752. .location-btn-hide{
  753. right: -240px;
  754. animation-name: locationBtnHide;
  755. /* 持续时间 */
  756. animation-duration: 1s;
  757. transition: all 1s cubic-bezier(0.165, 0.84, 0.44, 1) .5s;
  758. }
  759. .bottom-tabs-box {
  760. position: relative;
  761. .to-small {
  762. width: 60px;
  763. height: 60px;
  764. background: url('/@/assets/images/vent/home/tosmall.png') no-repeat center;
  765. background-size: auto;
  766. position: absolute;
  767. top: -62px;
  768. right: 36px;
  769. border-radius: 10px;
  770. padding: 8px;
  771. backdrop-filter: blur(10px);
  772. background-color: rgba(45, 86, 137, 0.418);
  773. &:hover {
  774. background-color: rgba(79, 104, 134, 0.418);
  775. }
  776. }
  777. .device-button-group {
  778. position: absolute;
  779. top: -27px;
  780. left: 30px;
  781. display: flex;
  782. width: 100%;
  783. .device-button {
  784. width: 148px;
  785. height: 26px;
  786. background: url('/@/assets/images/vent/home/tab21.png');
  787. display: flex;
  788. justify-content: center;
  789. align-items: center;
  790. color: #FFF;
  791. position: relative;
  792. cursor: pointer;
  793. &:nth-child(1){
  794. left: calc(-8px * 1);
  795. }
  796. &:nth-child(2){
  797. left: calc(-8px * 2);
  798. }
  799. &:nth-child(3){
  800. left: calc(-8px * 3);
  801. }
  802. &:nth-child(4){
  803. left: calc(-8px * 4);
  804. }
  805. &:nth-child(5){
  806. left: calc(-8px * 5);
  807. }
  808. &:nth-child(6){
  809. left: calc(-8px * 6);
  810. }
  811. &:nth-child(7){
  812. left: calc(-8px * 7);
  813. }
  814. &:nth-child(8){
  815. left: calc(-8px * 8);
  816. }
  817. &:nth-child(9){
  818. left: calc(-8px * 9);
  819. }
  820. &:first-child
  821. {
  822. background: url('/@/assets/images/vent/home/tab11.png');
  823. }
  824. }
  825. .device-active {
  826. background: url('/@/assets/images/vent/home/tab2-active.png');
  827. &:first-child
  828. {
  829. background: url('/@/assets/images/vent/home/tab1-active.png');
  830. }
  831. // color: #0efcff;
  832. &::before {
  833. border-color: #0efcff;
  834. box-shadow: 1px 1px 3px 1px #0efcff inset;
  835. }
  836. }
  837. }
  838. .enter-detail {
  839. color: #fff;
  840. cursor: pointer;
  841. position: absolute;
  842. right: 120px;
  843. top: -6px;
  844. padding: 5px;
  845. // border: 1px transparent solid;
  846. border-radius: 5px;
  847. margin-left: 8px;
  848. margin-right: 8px;
  849. width: auto;
  850. // height: 40px;
  851. // border: 1px solid #65dbea;
  852. height: 33px !important;
  853. display: flex;
  854. align-items: center;
  855. justify-content: center;
  856. color: #fff;
  857. padding: 5px 15px 5px 15px;
  858. cursor: pointer;
  859. &:hover {
  860. background: linear-gradient(#2cd1ff55, #1eb0ff55);
  861. }
  862. &::before {
  863. width: calc(100% - 6px);
  864. height: 27px;
  865. content: '';
  866. position: absolute;
  867. top: 3px;
  868. right: 0;
  869. left: 3px;
  870. bottom: 0;
  871. z-index: -1;
  872. border-radius: inherit;
  873. /*important*/
  874. background: linear-gradient(#1fa6cb, #127cb5);
  875. }
  876. &::after {
  877. width: calc(100% + 32px);
  878. height: 10px;
  879. content: '';
  880. position: absolute;
  881. top: 35px;
  882. right: 0;
  883. left: -16px;
  884. bottom: 0;
  885. z-index: -1;
  886. border-radius: inherit;
  887. /*important*/
  888. background: url('/@/assets/images/vent/short-light.png') no-repeat;
  889. background-position: center;
  890. background-size: 100%;
  891. z-index: 999;
  892. }
  893. }
  894. }
  895. @keyframes treeShow {
  896. 0% {
  897. left: -400px;
  898. opacity: 0;
  899. }
  900. 100% {
  901. left: 10px;
  902. opacity: 1;
  903. }
  904. }
  905. @keyframes treeHide {
  906. 0% {
  907. left: 10px;
  908. opacity: 1;
  909. }
  910. 100% {
  911. left: -400px;
  912. opacity: 0;
  913. }
  914. }
  915. @keyframes locationShow {
  916. 0% {
  917. right: 0px;
  918. opacity: 0;
  919. }
  920. 100% {
  921. right: 240px;
  922. opacity: 1;
  923. }
  924. }
  925. @keyframes locationHide {
  926. 0% {
  927. right: 240px;
  928. opacity: 1;
  929. }
  930. 100% {
  931. right: 0;
  932. opacity: 0;
  933. }
  934. }
  935. @keyframes locationBtnShow {
  936. 0% {
  937. right: -240px;
  938. opacity: 0;
  939. }
  940. 100% {
  941. right: -2px;
  942. opacity: 1;
  943. }
  944. }
  945. @keyframes locationBtnHide {
  946. 0% {
  947. right: -2px;
  948. opacity: 1;
  949. }
  950. 100% {
  951. right: -240px;
  952. opacity: 0;
  953. }
  954. }
  955. :deep(.@{ventSpace}-tabs-tabpane-active) {
  956. // overflow: auto;
  957. height: 100%;
  958. }
  959. :deep(.zxm-select-dropdown) {
  960. left: 0 !important;
  961. }</style>