|
|
@@ -5,7 +5,7 @@
|
|
|
>
|
|
|
<div class="l-flex__none l-flex--row c-sibling-item--v c-count">
|
|
|
<div
|
|
|
- class="l-flex__none u-color--black u-width--sm u-bold u-ellipsis has-active"
|
|
|
+ class="l-flex__none u-width--sm u-color--blue u-bold u-ellipsis has-active"
|
|
|
@click="onChooseDepartment"
|
|
|
>
|
|
|
<i
|
|
|
@@ -14,24 +14,51 @@
|
|
|
/>
|
|
|
{{ group.name }}
|
|
|
</div>
|
|
|
- <div class="l-flex__none c-count__item u-color--black u-bold u-text--center">
|
|
|
- <div>总数</div>
|
|
|
+ <div
|
|
|
+ class="l-flex__none c-count__item u-color--black u-bold u-text--center u-pointer"
|
|
|
+ @click="onChange('all')"
|
|
|
+ >
|
|
|
+ <div class="u-relative">
|
|
|
+ 总数
|
|
|
+ <i
|
|
|
+ v-if="isAllDevice && active === 'all'"
|
|
|
+ class="c-count__current el-icon-caret-left"
|
|
|
+ />
|
|
|
+ </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>
|
|
|
+ <div
|
|
|
+ class="l-flex__none c-count__item u-color--success dark u-bold u-text--center u-pointer"
|
|
|
+ @click="onChange('online')"
|
|
|
+ >
|
|
|
+ <div class="u-relative">
|
|
|
+ ● 在线
|
|
|
+ <i
|
|
|
+ v-if="isAllDevice && active === 'online'"
|
|
|
+ class="c-count__current el-icon-caret-left"
|
|
|
+ />
|
|
|
+ </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>
|
|
|
+ <div
|
|
|
+ class="l-flex__none c-count__item u-color--error dark u-bold u-text--center u-pointer"
|
|
|
+ @click="onChange('offline')"
|
|
|
+ >
|
|
|
+ <div class="u-relative">
|
|
|
+ ● 离线
|
|
|
+ <i
|
|
|
+ v-if="isAllDevice && active === 'offline'"
|
|
|
+ class="c-count__current el-icon-caret-left"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
<i
|
|
|
v-if="monitor.loading"
|
|
|
class="el-icon-loading"
|
|
|
@@ -39,7 +66,7 @@
|
|
|
<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>
|
|
|
+ <div>● 未接入</div>
|
|
|
<i
|
|
|
v-if="monitor.loading"
|
|
|
class="el-icon-loading"
|
|
|
@@ -51,12 +78,29 @@
|
|
|
@click="onRefresh"
|
|
|
/>
|
|
|
</div>
|
|
|
- <div
|
|
|
- class="l-flex__none l-flex--row inline c-sibling-item--v far u-font-size--sm u-bold has-active"
|
|
|
- @click="onAttention"
|
|
|
- >
|
|
|
- <span class="c-sibling-item">我的关注</span>
|
|
|
- <i class="c-sibling-item near el-icon-circle-plus-outline" />
|
|
|
+ <div class="l-flex__none l-flex--row c-sibling-item--v far u-font-size--sm u-bold">
|
|
|
+ <div v-if="isAllDevice">设备列表</div>
|
|
|
+ <div
|
|
|
+ v-else
|
|
|
+ class="l-flex--row inline has-active"
|
|
|
+ @click="onAttention"
|
|
|
+ >
|
|
|
+ <span class="c-sibling-item">我的关注</span>
|
|
|
+ <i class="c-sibling-item near el-icon-circle-plus-outline" />
|
|
|
+ </div>
|
|
|
+ <div class="l-flex__fill" />
|
|
|
+ <el-dropdown
|
|
|
+ v-if="isTenantAdmin"
|
|
|
+ class="l-flex__none has-active"
|
|
|
+ trigger="click"
|
|
|
+ @command="onCommand"
|
|
|
+ >
|
|
|
+ <i class="el-icon-s-operation u-font-size--md" />
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
+ <el-dropdown-item command="all">设备列表</el-dropdown-item>
|
|
|
+ <el-dropdown-item command="attention">我的关注</el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </el-dropdown>
|
|
|
</div>
|
|
|
<div
|
|
|
v-if="deviceOptions.loaded && !deviceOptions.list.length"
|
|
|
@@ -64,9 +108,15 @@
|
|
|
>
|
|
|
<el-empty
|
|
|
class="l-flex__auto l-flex--row center"
|
|
|
- description="暂无关注设备"
|
|
|
+ description="暂无设备"
|
|
|
/>
|
|
|
</div>
|
|
|
+ <div
|
|
|
+ v-if="!deviceOptions.loaded"
|
|
|
+ class="c-sibling-item--v far u-text--center"
|
|
|
+ >
|
|
|
+ <i class="el-icon-loading" />
|
|
|
+ </div>
|
|
|
<div
|
|
|
ref="deviceContainer"
|
|
|
class="l-flex__auto l-grid--info c-sibling-item--v u-overflow-y--auto"
|
|
|
@@ -77,15 +127,8 @@
|
|
|
:device="item"
|
|
|
:observer="observerDom"
|
|
|
:flag="item.flag"
|
|
|
- :status="item.status"
|
|
|
/>
|
|
|
</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"
|
|
|
remember
|
|
|
@@ -96,12 +139,14 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
+import { mapGetters } from 'vuex'
|
|
|
import {
|
|
|
subscribe,
|
|
|
unsubscribe
|
|
|
} from '@/utils/mqtt'
|
|
|
import { ScreenshotCache } from '@/utils/cache'
|
|
|
import {
|
|
|
+ getDevicesByQuery,
|
|
|
getDeviceAttentionList,
|
|
|
getDeviceStatisticsByPath
|
|
|
} from '@/api/device'
|
|
|
@@ -120,9 +165,17 @@ export default {
|
|
|
loaded: false
|
|
|
},
|
|
|
loading: false,
|
|
|
+ command: 'all',
|
|
|
+ active: 'all',
|
|
|
group: {}
|
|
|
}
|
|
|
},
|
|
|
+ computed: {
|
|
|
+ ...mapGetters(['isTenantAdmin']),
|
|
|
+ isAllDevice () {
|
|
|
+ return this.command === 'all'
|
|
|
+ }
|
|
|
+ },
|
|
|
created () {
|
|
|
this.$timer = -1
|
|
|
subscribe([
|
|
|
@@ -130,23 +183,49 @@ export default {
|
|
|
'+/+/offline',
|
|
|
'+/+/calendar/update'
|
|
|
], this.onMessage)
|
|
|
- this.getDevices()
|
|
|
},
|
|
|
beforeDestroy () {
|
|
|
ScreenshotCache.clear()
|
|
|
- clearTimeout(this.$timer)
|
|
|
+ this.onResetOptions()
|
|
|
this.monitor = { loading: true }
|
|
|
unsubscribe([
|
|
|
'+/+/online',
|
|
|
'+/+/offline',
|
|
|
'+/+/calendar/update'
|
|
|
], this.onMessage)
|
|
|
- if (this.$observer) {
|
|
|
- this.$observer.disconnect()
|
|
|
- this.$observer = null
|
|
|
- }
|
|
|
},
|
|
|
methods: {
|
|
|
+ onResetOptions () {
|
|
|
+ clearTimeout(this.$timer)
|
|
|
+ this.$observer?.disconnect()
|
|
|
+ this.deviceOptions.ignore = true
|
|
|
+ },
|
|
|
+ onCommand (command) {
|
|
|
+ if (this.command !== command) {
|
|
|
+ this.command = command
|
|
|
+ if (this.isAllDevice) {
|
|
|
+ if (this.monitor.loading) {
|
|
|
+ this.onResetOptions()
|
|
|
+ this.deviceOptions = { list: [], loaded: false }
|
|
|
+ } else {
|
|
|
+ this.getDeviesByMonitor()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.getDevicesByAttention()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onChange (type) {
|
|
|
+ if (this.active !== type) {
|
|
|
+ this.active = type
|
|
|
+ if (this.isAllDevice) {
|
|
|
+ this.getDeviesByMonitor()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!this.isAllDevice) {
|
|
|
+ this.onCommand('all')
|
|
|
+ }
|
|
|
+ },
|
|
|
observerDom (el) {
|
|
|
if (!this.$observer) {
|
|
|
this.$observer = new IntersectionObserver(entries => {
|
|
|
@@ -178,13 +257,22 @@ export default {
|
|
|
this.$observer.observe(el)
|
|
|
},
|
|
|
onMessage (topic) {
|
|
|
+ if (this.isAllDevice) {
|
|
|
+ if (this.monitor.loading || !this.deviceOptions.loaded) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!this.deviceOptions.list.length) {
|
|
|
+ this.refreshDevices()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
const result = /^\d+\/(\d+)\/(online|offline|calendar\/update)$/.exec(topic)
|
|
|
if (!result) {
|
|
|
return
|
|
|
}
|
|
|
const deviceId = result[1]
|
|
|
- const status = result[2]
|
|
|
- if (status === 'online' || status === 'offline') {
|
|
|
+ const topicKey = result[2]
|
|
|
+ if (!this.isAllDevice && (topicKey === 'online' || topicKey === 'offline')) {
|
|
|
this.refreshDevices()
|
|
|
}
|
|
|
if (!this.deviceOptions.loaded) {
|
|
|
@@ -192,12 +280,18 @@ export default {
|
|
|
}
|
|
|
const device = this.deviceOptions.map?.[deviceId]
|
|
|
if (device) {
|
|
|
- switch (status) {
|
|
|
+ switch (topicKey) {
|
|
|
case 'calendar/update':
|
|
|
device.flag = -Date.now()
|
|
|
break
|
|
|
default:
|
|
|
- device.onlineStatus = status === 'online' ? 1 : 2
|
|
|
+ if (topicKey !== (device.onlineStatus === 1 ? 'online' : 'offline')) {
|
|
|
+ if (this.isAllDevice) {
|
|
|
+ this.refreshDevices()
|
|
|
+ } else {
|
|
|
+ device.onlineStatus = topicKey === 'online' ? 1 : 2
|
|
|
+ }
|
|
|
+ }
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
@@ -231,34 +325,95 @@ export default {
|
|
|
inactive: '-'
|
|
|
}
|
|
|
this.monitor = monitor
|
|
|
+ if (this.isAllDevice) {
|
|
|
+ this.onResetOptions()
|
|
|
+ this.deviceOptions = { list: [], loaded: false }
|
|
|
+ }
|
|
|
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
|
|
|
+ monitor.offline = offLineTotal
|
|
|
+ monitor.inactive = deactivatedTotal + notConnectedTotal
|
|
|
}).finally(() => {
|
|
|
monitor.loading = false
|
|
|
+ if (this.isAllDevice) {
|
|
|
+ this.getDeviesByMonitor()
|
|
|
+ }
|
|
|
})
|
|
|
},
|
|
|
- getDevices () {
|
|
|
- this.$observer?.disconnect()
|
|
|
- const options = { list: [], loaded: false, power: [] }
|
|
|
+ sort (a, b) {
|
|
|
+ if (a.onlineStatus === b.onlineStatus) {
|
|
|
+ return a.createTime <= b.createTime ? 1 : -1
|
|
|
+ }
|
|
|
+ return a.onlineStatus === 1 ? -1 : 1
|
|
|
+ },
|
|
|
+ getDeviesByMonitor () {
|
|
|
+ this.onResetOptions()
|
|
|
+ if (this.monitor.loading) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const query = {
|
|
|
+ pageNum: 1,
|
|
|
+ activate: 1,
|
|
|
+ org: this.group.path
|
|
|
+ }
|
|
|
+ const { total, online, offline, inactive } = this.monitor
|
|
|
+ if (this.active === 'online') {
|
|
|
+ query.pageSize = online
|
|
|
+ query.onlineStatus = 1
|
|
|
+ } else if (this.active === 'offline') {
|
|
|
+ query.pageSize = offline
|
|
|
+ query.onlineStatus = 2
|
|
|
+ } else {
|
|
|
+ query.pageSize = total - inactive
|
|
|
+ }
|
|
|
+ if (query.pageSize === 0) {
|
|
|
+ this.deviceOptions = { list: [], loaded: true }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const options = { list: [], loaded: false }
|
|
|
this.deviceOptions = options
|
|
|
- getDeviceAttentionList().then(
|
|
|
+ getDevicesByQuery(query, { custom: true }).then(
|
|
|
({ data }) => {
|
|
|
- const map = {}
|
|
|
- options.list = data.map(device => {
|
|
|
- device.flag = 0
|
|
|
- map[device.id] = device
|
|
|
- return device
|
|
|
- })
|
|
|
- options.map = map
|
|
|
- options.loaded = true
|
|
|
+ if (!options.ignore) {
|
|
|
+ const map = {}
|
|
|
+ options.list = data.sort(this.sort).map(device => {
|
|
|
+ device.flag = 0
|
|
|
+ map[device.id] = device
|
|
|
+ return device
|
|
|
+ })
|
|
|
+ options.map = map
|
|
|
+ options.loaded = true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ ({ isCancel }) => {
|
|
|
+ if (!isCancel && !options.ignore && this.isAllDevice && !this.monitor.loading) {
|
|
|
+ this.$timer = setTimeout(this.getDeviesByMonitor, 2000)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ getDevicesByAttention () {
|
|
|
+ this.onResetOptions()
|
|
|
+ const options = { list: [], loaded: false }
|
|
|
+ this.deviceOptions = options
|
|
|
+ getDeviceAttentionList({ custom: true }).then(
|
|
|
+ ({ data }) => {
|
|
|
+ if (!options.ignore) {
|
|
|
+ const map = {}
|
|
|
+ options.list = data.map(device => {
|
|
|
+ device.flag = 0
|
|
|
+ map[device.id] = device
|
|
|
+ return device
|
|
|
+ })
|
|
|
+ options.map = map
|
|
|
+ options.loaded = true
|
|
|
+ }
|
|
|
},
|
|
|
({ isCancel }) => {
|
|
|
- if (!isCancel) {
|
|
|
- this.$timer = setTimeout(this.getDevices, 2000)
|
|
|
+ if (!isCancel && !options.ignore && !this.isAllDevice) {
|
|
|
+ this.$timer = setTimeout(this.getDevicesByAttention, 2000)
|
|
|
}
|
|
|
}
|
|
|
)
|
|
|
@@ -282,5 +437,13 @@ export default {
|
|
|
&__item > div:first-child {
|
|
|
margin-bottom: 10px;
|
|
|
}
|
|
|
+
|
|
|
+ &__current {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 100%;
|
|
|
+ font-size: $font-size--md;
|
|
|
+ transform: translate(0, -50%);
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|