index.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861
  1. <template>
  2. <div
  3. ref="wrapper"
  4. v-loading="!loaded"
  5. element-loading-background="transparent"
  6. >
  7. <template v-if="loaded">
  8. <div
  9. v-if="linkDeviceMap"
  10. class="c-link-state l-flex--row"
  11. :class="className"
  12. >
  13. <div
  14. class="c-link-state__main"
  15. :style="styles"
  16. >
  17. <div
  18. v-for="item in items"
  19. :key="item.key"
  20. class="o-link-item"
  21. :class="item.className"
  22. @click="onClick(item)"
  23. >
  24. <div
  25. v-if="item.status !== -2"
  26. class="o-link-item__name"
  27. >
  28. {{ item.label }}
  29. </div>
  30. <template v-if="item.key === 'led'">
  31. <device-player
  32. :device="led"
  33. autoplay
  34. retry
  35. keep
  36. />
  37. <i
  38. v-if="led.powerStatus"
  39. class="o-link-item__status"
  40. :class="led.powerStatus"
  41. />
  42. </template>
  43. </div>
  44. <div
  45. v-for="line in lines"
  46. :key="line.key"
  47. class="o-line"
  48. :class="line.className"
  49. />
  50. </div>
  51. </div>
  52. <div
  53. v-else
  54. class="l-flex--row center"
  55. >
  56. <el-empty description="暂无相关配置" />
  57. </div>
  58. </template>
  59. <slot />
  60. </div>
  61. </template>
  62. <script>
  63. import { ThirdPartyDevice } from '@/constant.js'
  64. import {
  65. Status,
  66. Power,
  67. getReceivingCardStatusFromServer
  68. } from '@/utils/adapter'
  69. import { getThirdPartyDevicesByThirdPartyDevice } from '@/api/mesh.js'
  70. import { getDevice } from '@/api/device.js'
  71. const LinkItems = Object.freeze([
  72. {
  73. key: 'msr',
  74. label: '浪潮屏媒安播云平台'
  75. },
  76. {
  77. key: ThirdPartyDevice.BOX,
  78. alias: 'device',
  79. label: '浪潮超高清媒体播控器',
  80. info: 'boxInfo'
  81. },
  82. {
  83. key: 'led',
  84. label: 'LED大屏'
  85. },
  86. {
  87. key: ThirdPartyDevice.GATEWAY,
  88. alias: 'gateway',
  89. label: '浪潮物联网关',
  90. canClick: true
  91. },
  92. {
  93. key: ThirdPartyDevice.LED_CAMERA,
  94. alias: 'led_camera',
  95. label: 'LED屏监测摄像头',
  96. canClick: true
  97. },
  98. {
  99. key: ThirdPartyDevice.TRAFFIC_CAMERA,
  100. alias: 'traffic_camera',
  101. label: '人流量监测摄像头',
  102. canClick: true
  103. },
  104. {
  105. key: ThirdPartyDevice.SENDING_CARD,
  106. alias: 'sending_card',
  107. label: '发送控制设备',
  108. canClick: true
  109. },
  110. {
  111. key: ThirdPartyDevice.RECEIVING_CARD,
  112. alias: 'receiving_card',
  113. label: '接收卡',
  114. canClick: true
  115. }
  116. ])
  117. const requiredMap = {
  118. [ThirdPartyDevice.BOX]: 1,
  119. [ThirdPartyDevice.GATEWAY]: 1,
  120. [ThirdPartyDevice.LED_CAMERA]: 1,
  121. [ThirdPartyDevice.TRAFFIC_CAMERA]: 1
  122. }
  123. const LineFromeTo = {
  124. 1: ['msr', { key: ThirdPartyDevice.BOX, enable: 1 }],
  125. 2: [ThirdPartyDevice.BOX, { key: ThirdPartyDevice.SENDING_CARD, enable: 1 }],
  126. 3: [ThirdPartyDevice.SENDING_CARD, { key: ThirdPartyDevice.RECEIVING_CARD, enable: 1 }],
  127. 4: [ThirdPartyDevice.RECEIVING_CARD, { key: 'led', enable: 1 }],
  128. 5: ['msr', { key: ThirdPartyDevice.GATEWAY, enable: 1 }],
  129. 6: ['msr', { key: ThirdPartyDevice.LED_CAMERA, enable: 1 }],
  130. 15: ['msr', { key: ThirdPartyDevice.TRAFFIC_CAMERA, enable: 1 }],
  131. 7: ['msr', { key: ThirdPartyDevice.TRAFFIC_CAMERA, enable: 1 }],
  132. 8: ['msr', { key: ThirdPartyDevice.TRAFFIC_CAMERA, enable: 1 }],
  133. 9: ['msr', { key: ThirdPartyDevice.LED_CAMERA, enable: 1 }],
  134. // 10: [ThirdPartyDevice.GATEWAY, { key: 'led', enable: 1 }],
  135. 11: ['msr', { key: ThirdPartyDevice.GATEWAY, enable: 1 }, { key: ThirdPartyDevice.LED_CAMERA, enable: 1 }, { key: ThirdPartyDevice.TRAFFIC_CAMERA, enable: 1 }],
  136. 12: [ThirdPartyDevice.BOX, { key: ThirdPartyDevice.SENDING_CARD, enable: 0 }, { key: ThirdPartyDevice.RECEIVING_CARD, enable: 1 }],
  137. 13: [ThirdPartyDevice.BOX, { key: ThirdPartyDevice.SENDING_CARD, enable: 0 }, { key: ThirdPartyDevice.RECEIVING_CARD, enable: 0 }, { key: 'led', enable: 1 }],
  138. 14: [ThirdPartyDevice.BOX, { key: ThirdPartyDevice.SENDING_CARD, enable: 0 }, { key: ThirdPartyDevice.RECEIVING_CARD, enable: 0 }, { key: 'led', enable: 1 }]
  139. }
  140. export default {
  141. name: 'FullLink',
  142. props: {
  143. device: {
  144. type: Object,
  145. required: true
  146. },
  147. theme: {
  148. type: String,
  149. default: 'dark'
  150. },
  151. square: {
  152. type: [Boolean, String],
  153. default: false
  154. },
  155. addListener: {
  156. type: Function,
  157. default: null
  158. },
  159. removeListener: {
  160. type: Function,
  161. default: null
  162. }
  163. },
  164. data () {
  165. return {
  166. linkDeviceMap: null,
  167. scale: 1,
  168. loading: false,
  169. loaded: false,
  170. led: null
  171. }
  172. },
  173. computed: {
  174. targetId () {
  175. return this.device.id
  176. },
  177. className () {
  178. return [this.theme, this.square ? 'square' : ''].join(' ')
  179. },
  180. linkState () {
  181. return { ...this.linkDeviceMap }
  182. },
  183. items () {
  184. const map = this.linkState
  185. return LinkItems.map(({ key, alias, label, info, canClick }) => {
  186. const status = map[key]
  187. return {
  188. key, status, canClick,
  189. label: info && map[info] || label,
  190. className: [
  191. status === Status.NONE
  192. ? 'u-hidden'
  193. : status === Status.LOADING
  194. ? 'loading'
  195. : status === Status.ERROR
  196. ? 'tip el-icon-error u-color--error'
  197. : status === Status.OK
  198. ? ''
  199. : 'tip el-icon-warning u-color--warning',
  200. alias || key,
  201. canClick && status !== Status.NONE ? 'u-pointer' : ''
  202. ].join(' ')
  203. }
  204. })
  205. },
  206. lines () {
  207. const state = this.linkState
  208. return Object.keys(LineFromeTo).map(key => {
  209. const from = LineFromeTo[key][0]
  210. const to = LineFromeTo[key].slice(1)
  211. const enable = state[from] > Status.NONE && !to.some(
  212. ({ key, enable }) => enable && state[key] === Status.NONE
  213. ) && !to.some(
  214. ({ key, enable }) => !enable && state[key] > Status.NONE
  215. )
  216. return {
  217. key: `line${key}`,
  218. className: [
  219. `l${key}`,
  220. enable
  221. ? this.hasLink(state[from]) && !to.some(({ key, enable }) => enable && !this.hasLink(state[key]))
  222. ? 'linked'
  223. : ''
  224. : 'u-hidden'
  225. ].join(' ')
  226. }
  227. })
  228. },
  229. styles () {
  230. return {
  231. transform: `scale(${this.scale})`
  232. }
  233. }
  234. },
  235. created () {
  236. if (this.addListener) {
  237. this.addListener(this.onMessage)
  238. }
  239. this.getThirdPartyDevicesByThirdPartyDevice()
  240. this.$timer = setInterval(this.getThirdPartyDevicesByThirdPartyDevice, 5000)
  241. },
  242. mounted () {
  243. this.checkScale()
  244. window.addEventListener('resize', this.checkScale)
  245. },
  246. beforeDestroy () {
  247. if (this.removeListener) {
  248. this.removeListener(this.onMessage)
  249. }
  250. window.removeEventListener('resize', this.checkScale)
  251. clearInterval(this.$timer)
  252. },
  253. methods: {
  254. hasLink (status) {
  255. return status > Status.LOADING && status !== Status.ERROR
  256. },
  257. onMessage (value, key) {
  258. console.log('full link', key, value)
  259. if (!key || key === ThirdPartyDevice.MULTI_FUNCTION_CARD || key === ThirdPartyDevice.RECEIVING_CARD) {
  260. this.$receiverStatus = value[ThirdPartyDevice.RECEIVING_CARD].status
  261. if (value[ThirdPartyDevice.MULTI_FUNCTION_CARD].status > Status.LOADING) {
  262. this.$multiCard = value[ThirdPartyDevice.MULTI_FUNCTION_CARD]
  263. }
  264. if (this.linkDeviceMap) {
  265. if (this.linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] > Status.NONE && this.$multiCard) {
  266. const multiCardStatus = this.$multiCard.status
  267. if (multiCardStatus === Status.OK) {
  268. const switchStatus = this.$multiCard.switchStatus
  269. this.linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] = switchStatus === Power.ON ? Status.OK : switchStatus === Power.OFF ? Status.ERROR : Status.WARNING
  270. } else {
  271. this.linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] = multiCardStatus
  272. }
  273. }
  274. if (this.linkDeviceMap[ThirdPartyDevice.RECEIVING_CARD] > Status.NONE && this.$receiverStatus >= Status.OK) {
  275. this.linkDeviceMap[ThirdPartyDevice.RECEIVING_CARD] = this.$receiverStatus
  276. }
  277. this.onUpdateLed()
  278. }
  279. }
  280. },
  281. checkScale () {
  282. const elm = this.$refs.wrapper
  283. const width = elm.clientWidth
  284. const height = elm.clientHeight
  285. if (width === 0) {
  286. this.scale = this.square
  287. ? (height / 640).toFixed(2)
  288. : (height / 420).toFixed(2)
  289. } else if (height === 0) {
  290. this.scale = this.square
  291. ? (width / 804).toFixed(2)
  292. : (width / 966).toFixed(2)
  293. } else {
  294. this.scale = this.square
  295. ? Math.min(width / 804, height / 640).toFixed(2)
  296. : Math.min(width / 966, height / 420).toFixed(2)
  297. }
  298. },
  299. onOnlyBox () {
  300. this.linkDeviceMap = {
  301. msr: Status.OK,
  302. led: Status.ERROR,
  303. [ThirdPartyDevice.BOX]: Status.ERROR,
  304. [ThirdPartyDevice.GATEWAY]: Status.NONE,
  305. [ThirdPartyDevice.LED_CAMERA]: Status.NONE,
  306. [ThirdPartyDevice.TRAFFIC_CAMERA]: Status.NONE,
  307. [ThirdPartyDevice.SENDING_CARD]: Status.NONE,
  308. [ThirdPartyDevice.RECEIVING_CARD]: Status.NONE,
  309. [ThirdPartyDevice.MULTI_FUNCTION_CARD]: Status.NONE
  310. }
  311. this.led = {
  312. id: this.targetId,
  313. onlineStatus: 0,
  314. powerStatus: '',
  315. by: [ThirdPartyDevice.BOX]
  316. }
  317. return this.updateBoxStatus().then(() => {
  318. clearInterval(this.$timer)
  319. this.$timer = setInterval(this.updateBoxStatus, 5000)
  320. this.loaded = true
  321. })
  322. },
  323. updateBoxStatus () {
  324. return getDevice(this.targetId, { custom: true }).then(({ data }) => {
  325. this.linkDeviceMap[ThirdPartyDevice.BOX] = data.onlineStatus === 1 ? Status.OK : Status.ERROR
  326. this.onUpdateLed()
  327. })
  328. },
  329. getThirdPartyDevices () {
  330. Promise.all([
  331. getThirdPartyDevicesByThirdPartyDevice(this.targetId, [
  332. ThirdPartyDevice.GATEWAY,
  333. ThirdPartyDevice.RECEIVING_CARD,
  334. ThirdPartyDevice.SENDING_CARD,
  335. ThirdPartyDevice.LED_CAMERA,
  336. ThirdPartyDevice.TRAFFIC_CAMERA,
  337. ThirdPartyDevice.BOX,
  338. ThirdPartyDevice.MULTI_FUNCTION_CARD
  339. ], { custom: true }),
  340. Promise.resolve(this.$multiCard)
  341. ]).then(([{ data: nodes }, multiCard]) => {
  342. if (!nodes.length) {
  343. return this.onOnlyBox()
  344. }
  345. this.$deviceTypes = []
  346. const linkDeviceMap = {}
  347. const by = [ThirdPartyDevice.BOX]
  348. nodes.forEach(({ nodeType, instance }) => {
  349. if (nodeType === ThirdPartyDevice.RECEIVING_CARD) {
  350. if (instance) {
  351. linkDeviceMap[nodeType] = this.$receiverStatus ?? Status.LOADING
  352. by.push(nodeType)
  353. }
  354. } else if (nodeType === ThirdPartyDevice.MULTI_FUNCTION_CARD) {
  355. if (instance) {
  356. if (multiCard) {
  357. const multiCardStatus = multiCard.status
  358. if (multiCardStatus === Status.OK) {
  359. const switchStatus = multiCard.switchStatus
  360. linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] = switchStatus === Power.ON ? Status.OK : switchStatus === Power.OFF ? Status.ERROR : Status.WARNING
  361. } else {
  362. linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] = multiCardStatus
  363. }
  364. } else {
  365. linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] = Status.LOADING
  366. }
  367. }
  368. } else if (nodeType === ThirdPartyDevice.SENDING_CARD) {
  369. linkDeviceMap[nodeType] = Status.OK
  370. } else if (requiredMap[nodeType]) {
  371. if (instance) {
  372. const status = instance.onlineStatus === 1 ? Status.OK : Status.ERROR
  373. if (linkDeviceMap[nodeType] > Status.LOADING && linkDeviceMap[nodeType] !== status) {
  374. linkDeviceMap[nodeType] = Status.WARNING
  375. } else {
  376. linkDeviceMap[nodeType] = status
  377. }
  378. this.$deviceTypes.push(nodeType)
  379. if (nodeType === ThirdPartyDevice.BOX) {
  380. linkDeviceMap.boxInfo = instance.remark
  381. }
  382. }
  383. }
  384. })
  385. if (linkDeviceMap[ThirdPartyDevice.RECEIVING_CARD] > Status.NONE) {
  386. linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD] = Status.NONE
  387. }
  388. this.linkDeviceMap = {
  389. msr: Status.OK,
  390. led: Status.ERROR,
  391. [ThirdPartyDevice.BOX]: Status.NONE,
  392. [ThirdPartyDevice.GATEWAY]: Status.NONE,
  393. [ThirdPartyDevice.LED_CAMERA]: Status.NONE,
  394. [ThirdPartyDevice.TRAFFIC_CAMERA]: Status.NONE,
  395. [ThirdPartyDevice.SENDING_CARD]: Status.NONE,
  396. [ThirdPartyDevice.RECEIVING_CARD]: Status.NONE,
  397. [ThirdPartyDevice.MULTI_FUNCTION_CARD]: Status.NONE,
  398. ...linkDeviceMap
  399. }
  400. this.led = {
  401. id: this.targetId,
  402. onlineStatus: 0,
  403. powerStatus: '',
  404. by
  405. }
  406. this.onUpdateLed()
  407. if (this.linkDeviceMap[ThirdPartyDevice.RECEIVING_CARD] > Status.NONE) {
  408. getReceivingCardStatusFromServer(this.device.id)
  409. }
  410. this.loaded = true
  411. return true
  412. }).finally(() => {
  413. this.loading = false
  414. })
  415. },
  416. getThirdPartyDevicesByThirdPartyDevice () {
  417. if (this.loading) {
  418. return
  419. }
  420. this.loading = true
  421. if (!this.linkDeviceMap) {
  422. this.getThirdPartyDevices()
  423. return
  424. }
  425. getThirdPartyDevicesByThirdPartyDevice(this.targetId, this.$deviceTypes, { custom: true }).then(({ data: nodes }) => {
  426. const linkDeviceMap = {}
  427. nodes.forEach(({ nodeType, instance }) => {
  428. const onlineStatus = instance.onlineStatus === 1 ? Status.OK : Status.ERROR
  429. if (linkDeviceMap[nodeType] > Status.LOADING && linkDeviceMap[nodeType] !== onlineStatus) {
  430. linkDeviceMap[nodeType] = Status.WARNING
  431. } else {
  432. linkDeviceMap[nodeType] = onlineStatus
  433. }
  434. })
  435. this.linkDeviceMap = {
  436. ...this.linkDeviceMap,
  437. ...linkDeviceMap
  438. }
  439. this.onUpdateLed()
  440. }).finally(() => {
  441. this.loading = false
  442. })
  443. },
  444. onUpdateLed () {
  445. const linkDeviceMap = this.linkDeviceMap
  446. const by = this.led.by
  447. const mutliStatus = this.linkDeviceMap[ThirdPartyDevice.MULTI_FUNCTION_CARD]
  448. const linkError = by.some(key => linkDeviceMap[key] === Status.ERROR)
  449. const isOK = (mutliStatus === Status.NONE || mutliStatus === Status.OK) && !by.some(key => linkDeviceMap[key] !== Status.OK)
  450. this.linkDeviceMap.led = linkError ? Status.ERROR : isOK ? Status.OK : Status.WARNING
  451. this.led.onlineStatus = isOK ? 1 : 0
  452. this.led.powerStatus = mutliStatus === Status.LOADING
  453. ? 'el-icon-loading'
  454. : mutliStatus === Status.ERROR
  455. ? 'off'
  456. : mutliStatus === Status.WARNING
  457. ? 'power el-icon-warning u-color--warning'
  458. : ''
  459. },
  460. onClick (item) {
  461. if (!item.canClick) {
  462. return
  463. }
  464. this.$emit('click', item)
  465. }
  466. }
  467. }
  468. </script>
  469. <style lang="scss" scoped>
  470. @mixin getPosition($left, $top, $width, $height) {
  471. top: $top;
  472. left: $left;
  473. width: $width;
  474. height: $height;
  475. }
  476. .c-link-state {
  477. width: 100%;
  478. height: 100%;
  479. overflow: hidden;
  480. &__main {
  481. position: relative;
  482. width: 966px;
  483. height: 420px;
  484. transform-origin: left center;
  485. }
  486. &.dark {
  487. color: $black;
  488. }
  489. &.light {
  490. color: #fff;
  491. }
  492. &.square {
  493. .c-link-state__main {
  494. width: 804px;
  495. height: 640px;
  496. }
  497. .o-link-item {
  498. &.msr {
  499. top: 0;
  500. left: 0;
  501. }
  502. &.device {
  503. top: 75px;
  504. left: 286px;
  505. }
  506. &.sending_card {
  507. top: 75px;
  508. left: 472px;
  509. }
  510. &.receiving_card {
  511. top: 75px;
  512. left: 660px;
  513. }
  514. &.gateway {
  515. top: 432px;
  516. left: 52px;
  517. width: 138px;
  518. height: 138px;
  519. }
  520. &.led_camera {
  521. top: 280px;
  522. left: 460px;
  523. }
  524. &.traffic_camera {
  525. top: 400px;
  526. left: 290px;
  527. }
  528. &.led {
  529. top: 400px;
  530. left: 440px;
  531. }
  532. }
  533. .o-line {
  534. &.l1 {
  535. top: 166px;
  536. left: 232px;
  537. width: 60px;
  538. transform: none;
  539. }
  540. &.l2 {
  541. top: 166px;
  542. left: 418px;
  543. width: 60px;
  544. }
  545. &.l3 {
  546. top: 166px;
  547. left: 604px;
  548. width: 60px;
  549. }
  550. &.l4 {
  551. top: 202px;
  552. left: 728px;
  553. width: 198px;
  554. }
  555. &.l5 {
  556. top: 334px;
  557. left: 122px;
  558. width: 108px;
  559. transform: rotate(90deg);
  560. }
  561. &.l6,
  562. &.l15 {
  563. top: 334px;
  564. left: 122px;
  565. width: 120px;
  566. }
  567. &.l7 {
  568. top: 334px;
  569. left: 240px;
  570. width: 120px;
  571. }
  572. &.l8 {
  573. top: 454px;
  574. left: 240px;
  575. }
  576. &.l9 {
  577. top: 334px;
  578. left: 240px;
  579. width: 234px;
  580. }
  581. &.l10 {
  582. top: 522px;
  583. left: 184px;
  584. width: 256px;
  585. }
  586. &.l11 {
  587. top: 230px;
  588. left: 122px;
  589. width: 104px;
  590. height: 2px;
  591. transform: rotate(90deg);
  592. transform-origin: left;
  593. }
  594. }
  595. }
  596. }
  597. @keyframes move {
  598. from {
  599. background-position: 0 0;
  600. }
  601. to {
  602. background-position: 3em 0;
  603. }
  604. }
  605. .o-link-item {
  606. position: absolute;
  607. text-align: center;
  608. background-position: 0 0;
  609. background-size: 100% 100%;
  610. background-repeat: no-repeat;
  611. z-index: 1;
  612. &.el-icon-error::before {
  613. position: absolute;
  614. top: 50%;
  615. left: 50%;
  616. color: #ff0000;
  617. transform: translate(-50%, 50%);
  618. }
  619. &__name {
  620. position: absolute;
  621. top: 100%;
  622. left: 50%;
  623. font-size: 14px;
  624. line-height: 1;
  625. white-space: nowrap;
  626. transform: translateX(-50%);
  627. }
  628. &.msr {
  629. @include getPosition(0, 42px, 242px, 236px);
  630. background-image: url("~@/assets/link/msr.svg");
  631. }
  632. &.device {
  633. @include getPosition(270px, 0, 138px, 138px);
  634. background-image: url("~@/assets/link/box_u8h.png");
  635. }
  636. &.sending_card {
  637. @include getPosition(484px, 0, 138px, 138px);
  638. background-image: url("~@/assets/link/sending_card.png");
  639. }
  640. &.receiving_card {
  641. @include getPosition(720px, 0, 138px, 138px);
  642. background-image: url("~@/assets/link/receiving_card.png");
  643. .o-link-item__name {
  644. transform: translateX(8px);
  645. }
  646. }
  647. &.gateway {
  648. @include getPosition(260px, 270px, 138px, 138px);
  649. background-image: url("~@/assets/link/gateway.svg");
  650. }
  651. &.led_camera {
  652. @include getPosition(480px, 178px, 80px, 80px);
  653. background-image: url("~@/assets/link/camera.png");
  654. }
  655. &.traffic_camera {
  656. @include getPosition(480px, 278px, 80px, 80px);
  657. background-image: url("~@/assets/link/camera.png");
  658. }
  659. &.led {
  660. @include getPosition(604px, 208px, 352px, 198px);
  661. .o-link-item__name {
  662. top: -8px;
  663. left: auto;
  664. right: 0;
  665. bottom: auto;
  666. transform: translateY(-100%);
  667. }
  668. }
  669. &.loading::after {
  670. content: "\e6cf";
  671. position: absolute;
  672. top: calc(50% - $font-size--3xl / 2);
  673. left: calc(50% - $font-size--3xl / 2);
  674. width: $font-size--3xl;
  675. height: $font-size--3xl;
  676. color: #fff;
  677. font-size: $font-size--3xl;
  678. font-family: element-icons !important;
  679. animation: rotating 2s linear infinite;
  680. }
  681. &.tip::before {
  682. position: absolute;
  683. top: 50%;
  684. left: 50%;
  685. transform: translate(-50%, -50%);
  686. z-index: 9;
  687. }
  688. &.led {
  689. &.tip::before {
  690. display: none;
  691. }
  692. &.loading::after {
  693. color: #606266;
  694. }
  695. }
  696. &__status {
  697. position: absolute;
  698. top: 6px;
  699. right: 8px;
  700. width: 24px;
  701. height: 24px;
  702. z-index: 9;
  703. &.off {
  704. background: url("~@/assets/icon_off.svg") 0 0 / 100% 100% no-repeat;
  705. }
  706. &.power {
  707. display: inline-flex;
  708. justify-content: flex-end;
  709. align-items: center;
  710. font-size: $font-size;
  711. &::after {
  712. content: "电源";
  713. margin-left: $spacing--3xs;
  714. font-size: $font-size--sm;
  715. white-space: nowrap;
  716. }
  717. }
  718. }
  719. }
  720. .o-line {
  721. position: absolute;
  722. background-color: $gray--light;
  723. &.linked {
  724. background-size: 3em 1em;
  725. background-image: linear-gradient(
  726. -90deg,
  727. transparent 0em,
  728. transparent 1em,
  729. #026af2 1em,
  730. #026af2 2em,
  731. transparent 3em
  732. );
  733. animation: move 0.6s linear infinite;
  734. }
  735. &.l1 {
  736. transform: skewY(-30deg);
  737. transform-origin: left;
  738. @include getPosition(166px, 170px, 154px, 2px);
  739. }
  740. &.l2 {
  741. @include getPosition(402px, 70px, 86px, 2px);
  742. }
  743. &.l3 {
  744. @include getPosition(622px, 70px, 118px, 2px);
  745. }
  746. &.l4 {
  747. @include getPosition(790px, 100px, 110px, 2px);
  748. transform: rotate(90deg);
  749. transform-origin: left;
  750. }
  751. &.l5 {
  752. @include getPosition(152px, 256px, 150px, 2px);
  753. transform: skewY(30deg);
  754. transform-origin: left;
  755. }
  756. &.l6,
  757. &.l15 {
  758. @include getPosition(230px, 208px, 200px, 2px);
  759. }
  760. &.l6.linked ~ &.l15 {
  761. display: none;
  762. }
  763. &.l7 {
  764. @include getPosition(428px, 208px, 96px, 2px);
  765. transform: rotate(90deg);
  766. transform-origin: left;
  767. }
  768. &.l8 {
  769. @include getPosition(428px, 304px, 66px, 2px);
  770. }
  771. &.l9 {
  772. @include getPosition(428px, 208px, 66px, 2px);
  773. }
  774. &.l10 {
  775. @include getPosition(368px, 360px, 238px, 2px);
  776. }
  777. &.l11 {
  778. @include getPosition(402px, 70px, 86px, 2px);
  779. }
  780. &.l12 {
  781. @include getPosition(402px, 70px, 340px, 2px);
  782. }
  783. &.l13 {
  784. @include getPosition(402px, 70px, 386px, 2px);
  785. }
  786. &.l14 {
  787. transform: rotate(90deg);
  788. transform-origin: left;
  789. @include getPosition(788px, 68px, 160px, 2px);
  790. }
  791. }
  792. </style>