| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- <template>
- <wrapper
- fill
- margin
- >
- <div class="l-flex__none l-flex--row c-sibling-item--v c-count">
- <div
- class="l-flex__none c-count__title u-color--black u-bold has-active u-ellipsis"
- @click="onChooseDepartment"
- >
- <i
- v-if="loading"
- class="el-icon-loading"
- />
- {{ group.name }}
- </div>
- <div class="l-flex__none c-count__item u-color--black u-bold u-text--center">
- <div>总数</div>
- <i
- v-if="monitor.loading"
- class="el-icon-loading"
- />
- <div v-else>{{ monitor.total }}</div>
- </div>
- <div class="l-flex__none c-count__item u-color--success dark u-bold u-text--center">
- <div>● 在线</div>
- <i
- v-if="monitor.loading"
- class="el-icon-loading"
- />
- <div v-else>{{ monitor.online }}</div>
- </div>
- <div class="l-flex__none c-count__item u-color--error dark u-bold u-text--center">
- <div>● 离线</div>
- <i
- v-if="monitor.loading"
- class="el-icon-loading"
- />
- <div v-else>{{ monitor.offline }}</div>
- </div>
- <div class="l-flex__none c-count__item u-color--info u-bold u-text--center">
- <div>● 未启用</div>
- <i
- v-if="monitor.loading"
- class="el-icon-loading"
- />
- <div v-else>{{ monitor.inactive }}</div>
- </div>
- <i
- class="el-icon-refresh o-icon md u-color--blue has-active"
- @click="onRefresh"
- />
- </div>
- <div
- v-if="__PLACEHOLDER__"
- class="l-flex__none c-sibling-item--v far c-cards"
- >
- <card
- class="c-cards__item"
- type="info"
- title="总次数"
- tip="100"
- />
- <card
- class="c-cards__item"
- type="safety"
- title="安全异常"
- desc="累计异常数:30"
- />
- <card
- class="c-cards__item"
- type="performance"
- title="性能异常"
- desc="累计异常数:30"
- count="3"
- />
- <card
- class="c-cards__item"
- type="log"
- title="日志异常"
- desc="累计异常数:30"
- count="10"
- />
- <card
- class="c-cards__item"
- type="date"
- title="即将空闲排期"
- count="100"
- />
- </div>
- <div
- ref="deviceContainer"
- class="l-flex__auto l-grid--info c-sibling-item--v far u-overflow-y--auto"
- >
- <device
- v-for="item in deviceOptions.list"
- :key="item.id"
- :device="item"
- :observer="observerDom"
- :flag="item.flag"
- />
- </div>
- <div
- v-if="!deviceOptions.loaded"
- class="c-sibling-item--v far u-text--center"
- >
- <i class="el-icon-loading" />
- </div>
- <department-drawer
- ref="departmentDrawer"
- @change="onGroupChanged"
- @loaded="onGroupLoaded"
- />
- </wrapper>
- </template>
- <script>
- import {
- subscribe,
- unsubscribe
- } from '@/utils/mqtt'
- import { ScreenshotCache } from '@/utils/cache'
- import {
- getDevicesByQuery,
- getDeviceStatisticsByPath
- } from '@/api/device'
- import Card from './components/Card'
- import Device from './components/Device'
- export default {
- name: 'Dashboard',
- components: {
- Card,
- Device
- },
- data () {
- return {
- monitor: { loading: true },
- deviceOptions: {
- list: [],
- loaded: false
- },
- loading: true,
- group: {}
- }
- },
- created () {
- this.$timer = -1
- subscribe([
- '+/+/online',
- '+/+/offline',
- '+/+/calendar/update'
- ], this.onMessage)
- },
- beforeDestroy () {
- ScreenshotCache.clear()
- clearTimeout(this.$timer)
- this.monitor = { loading: true }
- unsubscribe([
- '+/+/online',
- '+/+/offline',
- '+/+/calendar/update'
- ], this.onMessage)
- if (this.$observer) {
- this.$observer.disconnect()
- this.$observer = null
- }
- },
- methods: {
- observerDom (el) {
- if (!this.$observer) {
- this.$observer = new IntersectionObserver(entries => {
- console.log('observer')
- entries.forEach(entry => {
- const inst = entry.target.__vue__
- if (inst && inst.intoView && inst.outView) {
- if (entry.isIntersecting) {
- inst.intoView()
- } else {
- inst.outView()
- }
- } else {
- const device = this.deviceOptions.map?.[entry.target.dataset.id]
- if (device) {
- if (entry.isIntersecting) {
- !device.flag && (device.flag = Date.now())
- } else {
- device.flag && (device.flag = 0)
- }
- }
- }
- })
- }, {
- root: this.$refs.deviceContainer,
- threshold: [0.25]
- })
- }
- this.$observer.observe(el)
- },
- onMessage (topic) {
- if (!this.deviceOptions.loaded) {
- return
- }
- const result = /^\d+\/(\d+)\/(online|offline|calendar\/update)$/.exec(topic)
- if (!result) {
- return
- }
- const deviceId = result[1]
- const status = result[2]
- const device = this.deviceOptions.map?.[deviceId]
- if (device) {
- if (status === 'calendar/update') {
- device.flag = -Date.now()
- return
- }
- const onlineStatus = device.onlineStatus === 1 ? 'online' : 'offline'
- if (status === onlineStatus) {
- return
- }
- this.refreshDevices()
- }
- },
- onGroupLoaded () {
- this.loading = false
- },
- onGroupChanged ({ path, name }) {
- if (!this.group || this.group.path !== path) {
- this.group = { path, name }
- this.refreshDevices(true)
- }
- },
- onChooseDepartment () {
- this.$refs.departmentDrawer.show().then(visible => {
- this.loading = !visible
- })
- },
- onRefresh () {
- this.refreshDevices()
- },
- refreshDevices (force) {
- if (!force && this.monitor.loading) {
- return
- }
- const monitor = {
- loading: true,
- total: '-',
- online: '-',
- offline: '-',
- inactive: '-'
- }
- this.monitor = monitor
- clearTimeout(this.$timer)
- this.deviceOptions = { loaded: false }
- this.$observer?.disconnect()
- getDeviceStatisticsByPath(this.group.path).then(({ data }) => {
- const { deactivatedTotal, notConnectedTotal, offLineTotal, onLineTotal, total } = data
- monitor.total = total
- monitor.online = onLineTotal
- monitor.offline = offLineTotal + notConnectedTotal
- monitor.inactive = deactivatedTotal
- }).finally(() => {
- monitor.loading = false
- if (!this.monitor.loading) {
- this.getDevices(this.monitor.total - this.monitor.inactive)
- }
- })
- },
- sort (a, b) {
- if (a.onlineStatus === b.onlineStatus) {
- return a.createTime <= b.createTime ? 1 : -1
- }
- return a.onlineStatus === 1 ? -1 : 1
- },
- getDevices (total) {
- if (!total || total === '-') {
- this.deviceOptions = { list: [], loaded: true }
- return
- }
- const options = { list: [], loaded: false }
- this.deviceOptions = options
- getDevicesByQuery({
- pageNum: 1,
- pageSize: total,
- activate: 1,
- org: this.group.path
- }, { custom: true }).then(
- ({ data }) => {
- const map = {}
- options.list = data.sort(this.sort).map(device => {
- map[device.id] = device
- device.flag = 0
- return device
- })
- options.loaded = true
- options.map = map
- },
- ({ isCancel }) => {
- if (!isCancel && !this.monitor.loading) {
- this.$timer = setTimeout(total => {
- this.getDevices(total)
- }, 2000, total)
- }
- }
- )
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .c-count {
- justify-content: space-between;
- padding: $spacing--xs $spacing;
- border-radius: $radius;
- background-color: #fff;
- &__title {
- width: 200px;
- }
- &__item > div:first-child {
- margin-bottom: 10px;
- }
- }
- .c-cards {
- display: grid;
- grid-template-columns: repeat(5, 1fr);
- grid-column-gap: $spacing;
- &__item {
- min-width: 0;
- }
- }
- </style>
|