index.vue 27 KB

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