Files
app-govern/pages/device/APF/detail.vue

735 lines
21 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<Cn-page :loading="loading" noPadding>
<view slot="body">
<view class="detail" :style="{ opacity: domLoading ? '0' : '1' }" style="overflow: hidden">
<view class="detail-header">
<Cn-htmlToImg domId="header" @renderFinish="renderFinish">
<view class="header" id="header" ref="header" @click="previewImg">
<img
:src="topoImg"
style="width: 375px; display: block"
mode="widthFix"
@load="domLoading = false"
/>
<view
class="point"
:style="{ left: item.lat + 'px', top: item.lng + 'px' }"
v-for="(item, index) in topolodyData"
:key="index"
>
<view class="grid-card mt10" style="width: fit-content">
<view class="grid-card-content-1">
<view class="item">{{ item.label }}</view>
<view class="item" v-for="(child, childIndex) in item.value" :key="childIndex">
<text>{{ child.label }}</text>
<text style="font-weight: 700">{{ child.value }}</text>
</view>
</view>
</view>
</view>
<view class="module" v-if="IOData.length">
<view class="grid-card">
<view class="grid-card-content-6">
<template v-for="(item, index) in IOData">
<view class="item">{{ item.clDid }}</view>
<view class="item" style="font-weight: 700">
{{ Math.floor(item.value) || '-' }}
<view v-if="item.value"> °C</view>
</view>
</template>
<template v-for="(item, index) in 3 - (IOData.length % 3)">
<view class="item"></view>
<view class="item"> </view>
</template>
</view>
</view>
</view>
</view>
</Cn-htmlToImg>
<!-- <view class="des">
<text>设备基础信息</text>
<text class="ml10">设备状态</text>
</view> -->
<view class="nav" style="margin-top: -10rpx">
<view
class="nav-menu"
:class="{ 'nav-menu-active': navMenuActive == index }"
v-for="(item, index) in navMenuList"
:key="index"
@click="navMenuClick(index)"
>{{ item.text }}
</view>
</view>
</view>
<view style="padding: 0 20rpx">最新数据时间 {{ time }}</view>
<view class="content" v-if="!loading">
<view v-if="navMenuActive == 0">
<basic :basicData="basicData"></basic>
</view>
<view v-else-if="navMenuActive == 1">
<xieBo :dataTime="dataTime" :basicData="basicData"></xieBo>
</view>
<view v-else-if="navMenuActive == 2">
<power :basicData="basicData"></power>
</view>
<!-- <view v-else-if="navMenuActive == 3">
<oscillogram></oscillogram>
</view> -->
<view v-else-if="navMenuActive == 3">
<IO :IOData="IOData" :ndid="pageOptions.ndid"></IO>
</view>
<view style="height: 20rpx"></view>
</view>
<!-- <uni-fab
ref="fab"
direction="vertical"
horizontal="right"
vertical="bottom"
:content="content"
@trigger="trigger"
/> -->
<hover-menu :btnList="content" @trigger="trigger"></hover-menu>
<uni-popup ref="share" type="share" :safe-area="false">
<uni-popup-share title="分享到"></uni-popup-share>
<view style="height: 40rpx; background: #fff"></view>
</uni-popup>
<!-- 完成调试输入框 -->
<!-- <uni-popup ref="inputDialog" type="dialog">
<uni-popup-dialog
ref="inputClose"
mode="input"
:title="dialogType"
placeholder="请输入备注"
:before-close="true"
@close="dialogInputClose"
@confirm="dialogInputConfirm"
></uni-popup-dialog>
</uni-popup> -->
<!-- 输入框示例 -->
<uni-popup ref="inputDialog" type="dialog">
<uni-popup-dialog
ref="inputClose"
type="info"
mode="input"
:title="dialogType"
value="对话框预置提示内容!"
placeholder="请输入内容"
@confirm="dialogInputConfirm"
>
<uni-easyinput
type="textarea"
:maxlength="250"
autoHeight
v-model="remarkContent"
placeholder="请输入备注"
></uni-easyinput>
</uni-popup-dialog>
</uni-popup>
</view>
</view>
</Cn-page>
</template>
<script>
import basic from './comp/basic.vue'
import xieBo from './comp/xieBo.vue'
import power from './comp/power.vue'
import oscillogram from './comp/oscillogram.vue'
import IO from './comp/IO.vue'
import { queryTopologyDiagram, deleteDevice, cancelDebug, finishDebug } from '@/common/api/device'
import { manualAccess } from '@/common/api/accessBoot'
import { MQTT_IP, MQTT_OPTIONS } from '@/common/js/mqtt.js'
import mqtt from 'mqtt'
import { base64ToPath, pathToBase64 } from 'image-tools'
import hoverMenu from '@/hover-menu/components/hover-menu/hover-menu.vue';
export default {
components: {
basic,
xieBo,
power,
oscillogram,
IO,
hoverMenu
},
data() {
return {
dataTime: '',
time: '',
remarkContent: '',
dialogType: '',
domLoading: true,
loading: true,
deviceInfo: {},
navMenuActive: 0,
navHeight: 0,
img: '',
topoImg: '',
navMenuList: [
{
text: '基本',
id: 'fc8c86dbc3f2d9810f5cd8f53c295415'
},
{
text: '谐波',
id: '769f3db0bce070d35acf8fa92c998eb6'
},
{
text: '功率',
id: 'a16aceae7d1565bf9f94dd7410cf9bce'
},
// {
// text: '波形',
// },
{
text: '其他',
},
],
content: [
{
iconPath: '/static/report.png',
text: '告警',
},
// {
// iconPath: '/static/record.png',
// text: '记录',
// },
{
iconPath: '/static/about.png',
text: '关于',
},
{
iconPath: '/static/access.png',
text: '接入',
},
],
client: null,
timer: null,
devId: '',
dictData: [],
isPrimaryUser: 0,
userInfo: {},
topolodyData: [],
basicData: [],
IOData: [],
pageOptions: {},
}
},
methods: {
trigger(e) {
console.log(e)
if (e.text === '分享') {
uni.navigateTo({ url: '/pages/device/share?id=' + this.devId })
} else if (e.text === '删除') {
uni.showModal({
title: '提示',
content: '确定删除该设备吗?',
success: (res) => {
if (res.confirm) {
console.log('用户点击确定')
deleteDevice(this.devId).then((res) => {
uni.showToast({
title: '删除成功',
icon: 'none',
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
})
} else if (res.cancel) {
console.log('用户点击取消')
}
},
})
} else if (e.text === '下载') {
this.$util.toast('下载成功')
} else if (e.text === '记录') {
uni.navigateTo({ url: '/pages/device/APF/record' })
} else if (e.text === '告警') {
uni.navigateTo({ url: '/pages/device/APF/report?id=' + this.devId })
} else if (e.text === '关于') {
uni.navigateTo({ url: '/pages/device/APF/about?id=' + this.devId })
} else if (e.text === '移交') {
uni.navigateTo({ url: '/pages/device/transfer?id=' + this.devId })
} else if (e.text === '反馈') {
uni.navigateTo({ url: '/pages/device/feedback' })
} else if (e.text === '用户') {
uni.navigateTo({ url: '/pages/device/user?id=' + this.devId + '&isPrimaryUser=' + this.isPrimaryUser })
} else if (e.text === '报表') {
this.$util.toast('效果是直接打开报表')
} else if (e.text === '版本') {
this.$util.toast('功能暂未开放')
} else if (e.text === '模版') {
this.$util.toast('效果是功能暂未开放直接打开报表')
} else if (e.text === '编辑') {
let data = JSON.parse(JSON.stringify(this.deviceInfo))
data.appsLineTopologyDiagramPO.forEach((item) => {
delete item.value
})
uni.navigateTo({
url: '/pages/device/edit?deviceInfo=' + encodeURIComponent(JSON.stringify(data)),
})
} else if (e.text === '取消调试') {
this.cancelDebug()
} else if (e.text === '完成调试') {
this.finishDebug()
} else if (e.text === '接入') {
manualAccess({ nDid: this.pageOptions.ndid }).then((res) => {
this.$util.toast(res.message)
})
}
// this.$refs.fab.close()
},
clickItem(item) {
console.log(item);
},
// 取消调试
cancelDebug() {
this.dialogType = '取消调试'
this.$refs.inputDialog.open()
},
// 完成调试
finishDebug() {
this.dialogType = '完成调试'
this.$refs.inputDialog.open()
},
dialogInputClose() {
this.$refs.inputDialog.close()
},
dialogInputConfirm(val) {
if (this.dialogType == '取消调试') {
cancelDebug({
deviceId: this.devId,
remark: this.remarkContent,
type: Number(this.pageOptions.process),
}).then((res) => {
this.$util.toast(res.message)
setTimeout(() => {
uni.navigateBack()
}, 1500)
})
} else {
finishDebug({
deviceId: this.devId,
remark: this.remarkContent,
type: Number(this.pageOptions.process),
}).then((res) => {
console.log(res)
this.$util.toast(res.message)
setTimeout(() => {
uni.navigateBack()
}, 1500)
})
}
},
async navMenuClick(idx) {
// console.log("🚀 ~ navMenuClick ~ idx:", idx)
if (idx != 3) {
await this.client.end()
await setTimeout(() => {
this.initMqtt(this.navMenuList[idx].id)
},500)
}
setTimeout(() => {
this.navMenuActive = idx
uni.pageScrollTo({ scrollTop: 0, duration: 0 })
}, 500)
},
downloadImg() {
uni.downloadFile({
url: this.deviceInfo.filePath,
success: (res) => {
pathToBase64(res.tempFilePath).then((res) => {
this.topoImg = res
})
},
})
},
init() {
console.log('init')
this.loading = true
this.domLoading = true
queryTopologyDiagram(this.devId).then((res) => {
res.data.filePath = this.$config.static + res.data.filePath
this.deviceInfo = res.data
this.downloadImg()
uni.setNavigationBarTitle({ title: this.deviceInfo.devName || '设备详情' })
this.topolodyData = this.topolodyData.filter((item) => {
let index = this.deviceInfo.appsLineTopologyDiagramPO.findIndex((element) => {
element.label = element.name
item.label = element.name
return element.linePostion === item.linePostion
})
if (index > -1) {
item.lat = this.deviceInfo.appsLineTopologyDiagramPO[index].lat
item.lng = this.deviceInfo.appsLineTopologyDiagramPO[index].lng
return true
} else {
// 把多余的监测点过滤掉
return false
}
})
console.log(this.topolodyData)
if (this.client) {
this.loading = false
} else {
this.initMqtt(this.navMenuList[0].id)
}
})
},
renderFinish(e) {
this.img = e
},
previewImg() {
if (!this.img) {
uni.showLoading({
title: '图片生成中',
mask: false,
})
setTimeout(() => {
this.previewImg()
}, 500)
} else {
uni.hideLoading()
base64ToPath(this.img).then((res) => {
console.log(res)
uni.previewImage({
urls: [res],
})
})
}
},
initMqtt(id) {
MQTT_OPTIONS.clientId = uni.getStorageSync('devCode')
// #ifdef APP-PLUS
this.client = mqtt.connect('wx://' + MQTT_IP, MQTT_OPTIONS)
// #endif
// #ifdef H5
this.client = mqtt.connect('ws://' + MQTT_IP, MQTT_OPTIONS)
// #endif
this.client
.on('connect', () => {
console.log('连接成功')
this.client.subscribe(`/zl/TemperData/${this.devId}`, (err) => {
if (!err) {
console.log(`订阅成功:/zl/TemperData/${this.devId}`)
}
})
this.client.subscribe(`/zl/devData/${this.devId}/${id}`, (err) => {
if (!err) {
console.log(`订阅成功:/zl/devData/${this.devId}/${id}`)
// 默认推送
this.client.publish(`/zl/askDevData/${this.devId}/${id}`)
this.client.publish(`/zl/askTemperData/${this.devId}`)
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
this.timer = setInterval(() => {
console.log('askDevData')
this.client.publish(`/zl/askDevData/${this.devId}/${id}`)
this.client.publish(`/zl/askTemperData/${this.devId}`)
}, 1000 * 60)
}
})
})
.on('reconnect', function (error) {
console.log(error)
// console.log('正在重连...', that.topic)
})
.on('error', function (error) {
console.log('连接失败...', error)
})
.on('end', function () {
console.log('连接断开')
})
.on('message', (topic, message) => {
console.log('接收推送信息:', JSON.parse(message.toString()), topic)
console.log("🚀 ~ .on ~ topic:", topic)
if (topic === `/zl/devData/${this.devId}/${id}`) {
if ((!message.toString() || message.toString().length < 10) && this.loading) {
this.$util.toast('该设备暂无数据')
}
this.loading = false
this.handlerData(JSON.parse(message.toString()))
} else if (topic === `/zl/TemperData/${this.devId}`) {
// this.basicData.forEach((item) => {
// if (item.statisticalName === '温度' && item.phase === 'avg') {
// item.statisticalData = message.toString()
// }
// })
this.IOData = JSON.parse(message.toString())
// this.IOData = this.IOData.filter((item) => item.value !== 0)
this.IOData.forEach((item) => {
if (!item.value) {
item.value = ''
} else {
if (!Number.isInteger(item.value)) {
item.value = item.value.toFixed(2)
}
}
})
}
})
},
handlerData(data) {
this.basicData = data
this.topolodyData.forEach((element) => {
let arr = []
element.showKey.forEach((key) => {
data.forEach((item) => {
if (item.statisticalName === key && item.phase === 'avg') {
if (key === 'Apf_RmsI_TolOut(A)') {
arr.push({
label: '总输出电流:',
value: Math.round(item.statisticalData) + 'A',
})
} else {
arr.push({
label: '电流畸变率:',
value: Math.round(item.statisticalData) + '%',
})
// arr.push('电流畸变率:' + item.statisticalData + '%')
}
}
if (item.time) {
this.dataTime = item.time.seconds
this.time = this.$util.parseTime(this.dataTime - 8 * 60 * 60)
// console.log(11111111,this.dataTime);
}
})
})
element.value = arr
})
console.log(this.topolodyData)
this.$forceUpdate()
},
},
// 页面销毁
onUnload() {
clearInterval(this.timer)
this.client.end()
},
// 下拉刷新
onPullDownRefresh() {
this.client.publish(`/zl/askDevData/${this.devId}`)
this.client.publish(`/zl/askTemperData/${this.devId}`)
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1000)
},
onLoad(options) {
this.pageOptions = options
this.userInfo = uni.getStorageSync(this.$cacheKey.userInfo)
this.devId = options.id
this.isPrimaryUser = options.isPrimaryUser
if (this.pageOptions.process == 2 || this.pageOptions.process == 3) {
this.content.splice(
0,
0,
{
iconPath: '/static/feedback.png',
text: '编辑',
},
{
iconPath: '/static/cancel.png',
text: '取消调试',
},
{
iconPath: '/static/confirm.png',
text: '完成调试',
},
)
} else {
if (this.isPrimaryUser == 1) {
this.content.splice(
0,
0,
{
iconPath: '/static/transfer.png',
text: '移交',
},
{
iconPath: '/static/feedback.png',
text: '编辑',
},
{
iconPath: '/static/delate.png',
text: '删除',
},
)
if (this.userInfo.authorities === 'app_vip_user') {
this.content.splice(3, 0, {
iconPath: '/static/share.png',
text: '分享',
})
}
}
if (this.userInfo.authorities !== 'tourist') {
this.content.splice(0, 0, {
iconPath: '/static/subordinate.png',
text: '用户',
})
}
}
this.$util.getDictData('Line_Position').then((res) => {
this.topolodyData = res.map((item) => {
switch (item.name) {
case '电网侧':
item.showKey = ['Apf_ThdA_Sys(%)']
break
case '负载侧':
item.showKey = ['Apf_ThdA_Load(%)']
break
case '输出侧':
item.showKey = ['Apf_RmsI_TolOut(A)']
break
case 'PCC公共点':
item.showKey = []
break
default:
break
}
return {
label: item.name,
linePostion: item.id,
lat: '',
lng: '',
showKey: item.showKey, //要展示的指标key
value: [],
}
})
})
this.init()
},
}
</script>
<style lang="scss">
.detail {
// background: $uni-theme-white;
.header-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: 100% 100%;
}
.header {
position: relative;
width: 375px;
margin: 0 auto;
overflow: hidden;
image {
image-rendering: -moz-crisp-edges;
image-rendering: -o-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: crisp-edges;
-ms-interpolation-mode: nearest-neighbor;
}
.point {
position: absolute;
color: #111;
z-index: 2;
text-align: center;
color: #111;
width: 110rpx;
font-size: 16rpx;
opacity: 0.8;
.grid-card-content-2,
.grid-card-content-1 {
font-size: 16rpx;
.item {
min-height: unset;
white-space: nowrap;
}
}
}
.module {
position: absolute;
bottom: 0;
right: 10rpx;
opacity: 0.8;
.grid-card-content-2,
.grid-card-content-6,
.grid-card-content-1 {
font-size: 16rpx;
.item {
min-height: unset;
}
}
}
}
.des {
padding: 20rpx 20rpx 0;
font-size: 28rpx;
color: #999;
}
.content {
box-sizing: border-box;
padding: 0 20rpx;
}
.detail-header {
position: sticky;
top: 0;
left: 0;
z-index: 2;
background: #f3f4f5;
}
}
/deep/ .uni-fab__circle--rightBottom {
right: 8px !important;
bottom: 8px !important;
}
/deep/ .uni-fab--rightBottom {
right: 8px !important;
bottom: 8px !important;
}
/deep/ .uni-fab__circle {
width: 40px;
height: 40px;
}
/deep/ .uni-fab__content--flexDirectionEnd {
width: 40px !important;
// height: 50px !important;
}
/deep/.uni-fab__item {
// height: 45px;
width: 40px !important;
}
/deep/.uni-fab__item--first {
height: 40px !important;
}
</style>