zhuguifei
2024-12-25 576d28de6be2d75bfd5a213874dd8d06657635b5
1.添加实时报警和历史报警 2.添加云服务和局域网模式切换界面
已修改19个文件
已添加4个文件
2998 ■■■■■ 文件已修改
App.vue 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/api.js 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/constant.js 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/loadshget.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/request/fly.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components/drop-down-menu/index.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manifest.json 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mqtt/MqttView.vue 164 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages.json 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/charts/charts.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/device/control.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/login/login.vue 89 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/login/switchMode.vue 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabBar/components/hisWarning.vue 599 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabBar/components/realWarning.vue 455 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabBar/device.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabBar/formula.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabBar/general.vue 299 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabBar/me.vue 113 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabBar/warning.vue 840 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
readMe.txt 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
store/index.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
App.vue
@@ -2,11 +2,16 @@
    import Vue from 'vue'
    export default {
        //全局变量(同步数据可用全局变量,异步数据使用vuex)
        globalData: {
            token: ""
            token: "",
            //当前选择tab
            selectTab: 0,
            //当前网络模式(cloud-云服务、lan-局域网)
            mode: "",
        },
        onLaunch: function() {
            //此处为unipush2.0的推送方法
@@ -27,19 +32,17 @@
            //推送仅支持Android ios原生平台
            // #ifdef APP-PLUS
            console.log('APP-PLUS:')
            // æ­¤å¤„为unipush1.0的推送方法
            plus.push.getClientInfoAsync((info) => {
                let cid = info["clientid"];
                console.log('客户端推送标识:', cid)
                this.$store.dispatch('setCid', cid); // ä½¿ç”¨ action æ›´æ–° cid
                this.$store.dispatch('setClientPushId', cid); // ä½¿ç”¨ action æ›´æ–° cid
            });
            // #endif
            uni.onPushMessage((res) => {
                console.log("收到推送消息:", res) //监听推送消息
            })
            uni.getSystemInfo({
                success: function(e) {
@@ -96,12 +99,7 @@
                if (this.$mqttTool.client) {
                    console.log(this.$mqttTool.client.connected)
                }
                // if(this.$mqttTool.client){
                //     this.$mqttTool.end().then(res =>{
                //         console.log('终止:')
                //     })
                // }
            }
        },
common/api.js
@@ -2,14 +2,14 @@
    fly
} from '@/common/request/fly.js'
//配置请求基地址
fly.config.baseURL = "http://192.168.12.20:9999"
const url = uni.getStorageSync('baseurl');
if (url) {
    fly.config.baseURL = url + "/herb"
}
const mqttBaseUrl = "223.241.221.20";
const emqxBaseUrl = "http://"+mqttBaseUrl +":18083/api/v5"
fly.config.baseURL = "http://192.168.12.47:9999"
const ip = uni.getStorageSync('lan_ip');
if (ip) {
    fly.config.baseURL = "http://" + ip + ":9999"
}
const mqttBaseUrl = "192.168.2.3";
const emqxBaseUrl = "http://" + mqttBaseUrl + ":18083/api/v5"
// æµ‹è¯•
function test(params) {
@@ -37,11 +37,17 @@
function getOrderList(params) {
    fly.config.loading = true
    return fly.get('/dry/dryOrder/list', params);
}
//设备列表
function queryEquList(params){
    fly.config.loading = true
    return fly.get('/mobile/equ/list', params);
}
//设备列表
function queryEquList(params) {
    fly.config.loading = true
    return fly.get('/mobile/equ/list', params);
}
//仅用作下拉刷新,无实际意义
function querySampleList(params) {
    fly.config.loading = true
    return fly.get('/mobile/sample/list', params);
}
//
@@ -53,20 +59,32 @@
function sendCommand(params) {
    fly.config.loading = true
    return fly.post('/dry/real/sendCommand', params);
}
//emqx æŽ¥å£
}
//历史故障,查询最近数据
function queryHisFaultList(params) {
    fly.config.loading = true
    return fly.get('/mobile/fault/list', params);
}
//历史故障,查询图表需要数据
function queryHisFaultChartList(params) {
    fly.config.loading = true
    return fly.get('/mobile/fault/chart', params);
}
//emqx æŽ¥å£
function emqxClients() {
    fly.config.loading = true
    return fly.get(emqxBaseUrl + "/clients", {
        auth: {
          username: "a3dc6758f6abc41a",
          password: "FvBwJcmqhAuQ0aBg4FLl5gQA9A9BE64bpl0oJTg9A6I0bK"
        },
        headers: {
      'Content-Type': 'application/json',
    },
    return fly.get(emqxBaseUrl + "/clients", {
        auth: {
            username: "a3dc6758f6abc41a",
            password: "FvBwJcmqhAuQ0aBg4FLl5gQA9A9BE64bpl0oJTg9A6I0bK"
        },
        headers: {
            'Content-Type': 'application/json',
        },
    });
}
@@ -77,11 +95,14 @@
    login,
    getRealTimeData,
    getAnalyList,
    getOrderList,
    getOrderList,
    queryEquList,
    querySampleList,
    queryHisFaultList,
    queryHisFaultChartList,
    getMonth,
    sendCommand,
    emqxClients,
    sendCommand,
    emqxClients,
    mqttBaseUrl
}
common/constant.js
@@ -2,6 +2,9 @@
const MOBILE_UP = "mobile/up"
//请求查询设备状态
const MOBILE_QUERY_EQU_STATU = MOBILE_UP + "/query/equ/statu"
//请求查询设备实时故障
const MOBILE_REQ_EQU_REAL_FAULT = MOBILE_UP + "/req/real/fault"
//发送操作指令
const MOBILE_REQ_EQU_CMD = MOBILE_UP + "/req/equ/cmd"
@@ -27,7 +30,10 @@
//广播前缀
const SERVICE_BROADCAST = "service/broadcast"
//  //服务端向各租户客户端发送实时故障广播
const SERVICE_BROADCAST_TENANT_REAL_FAULT = SERVICE_BROADCAST + "/real/fault/%s"
const SERVICE_BROADCAST_TENANT_REAL_FAULT = SERVICE_BROADCAST + "/real/fault/%s"
const SERVICE_ONECE_TENANT_REAL_FAULT = "service/onece" + "/real/fault/%s"
@@ -40,12 +46,16 @@
export default {
    MOBILE_QUERY_EQU_STATU,
    MOBILE_REQ_EQU_REAL_FAULT,
    SERVICE_RES_EQU_STATU,
    SERVICE_DOWN,
    MOBILE_REQ_EQU_CMD,
    SERVICE_RES_EQU_CMD,
    SERVICE_BROADCAST_TENANT_REAL_FAULT,
    SERVICE_BROADCAST_TENANT_REAL_FAULT,
    SERVICE_ONECE_TENANT_REAL_FAULT,
    
    MQTT_TOPIC_MESSAGE,
common/loadshget.js
@@ -1,9 +1,9 @@
import get from 'lodash.get'
export default function lget(data, item) {
    if(!data || data == null) return "--";
    if(!data || data == null ) return "--";
    if(data & (!item || item == null)) return data;
    let res  =  get(data, item)
    return res == null ? "--" :res
    return (res == null || res == "")  ? "--" :res
}
common/request/fly.js
@@ -1,5 +1,6 @@
var Fly = require("./wx.umd.min")
var fly = new Fly
var fly = new Fly
const log = false
fly.config.loading = true;
@@ -7,7 +8,6 @@
fly.config.timeout=10000;
//添加拦截器
fly.interceptors.request.use((config, promise) => {
    console.info(fly.config.loading)
    if (fly.config.loading) {
        uni.showLoading({
            title: '加载中'
@@ -24,9 +24,11 @@
    if (userinfo) {
        config.headers["X-Tenant-Id"] = userinfo.loginTenantId;
    }
    console.log('========================================== ')
    console.log('==    è¯·æ±‚数据:' + JSON.stringify(config))
    console.log('=========================================== ')
    if(log){
        console.log('========================================== ')
        console.log('==    è¯·æ±‚数据:' + JSON.stringify(config))
        console.log('=========================================== ')
    }
    return config;
})
@@ -34,10 +36,12 @@
fly.interceptors.response.use(
    (response) => {
        console.log('========================================')
        console.log('==    å“åº”数据:' + JSON.stringify(response.request.url))
        console.log('==    ' + JSON.stringify(response.data))
        console.log('======================================== ')
        if(log){
            console.log('========================================')
            console.log('==    å“åº”数据:' + JSON.stringify(response.request.url))
            console.log('==    ' + JSON.stringify(response.data))
            console.log('======================================== ')
        }
        //只将请求结果的data字段返回
components/drop-down-menu/index.vue
@@ -1,7 +1,10 @@
<template>
    <view class="dropdown">
        <view class="item">
            <view class="item-title">设备</view>
            <view class="item-title" style="display: flex;justify-content: space-between;">
                <text>设备</text>
                <text @click="closeMenu"><u-icon name="close"></u-icon></text>
            </view>
            <view class="item-box">
                <view v-for="(item,index) in menuList" :key="item.code" :class="['item-menu',item.ck?'active':'']"
                    @click="changeMenu(index)">{{item.name}}
@@ -196,6 +199,9 @@
                    }
                }
                this.selectData.endTime = val;
            },
            closeMenu(){
                this.$emit('closeMenu')
            }
        }
    }
main.js
@@ -33,15 +33,13 @@
import mqttTool from './lib/mqttTool.js'
Vue.prototype.$mqttTool = mqttTool
const EventBus = new Vue();
Vue.prototype.$eventBus = EventBus
 
//全局监控定时器(在页面使用局部定时器会出现无法关闭问题)
Vue.prototype.$monitorTimer = null
Vue.config.productionTip = false
App.mpType = 'app'
manifest.json
@@ -1,5 +1,5 @@
{
    "name" : "智能中草药干燥机",
    "name" : "兰浦智能干燥",
    "appid" : "__UNI__1E83F66",
    "description" : "",
    "versionName" : "1.0.0",
@@ -54,11 +54,11 @@
                    "unipush" : {
                        "icons" : {
                            "small" : {
                                "ldpi" : "static/push/push_small_18.png",
                                "mdpi" : "static/push/push_small_24.png",
                                "hdpi" : "static/push/push_small_36.png",
                                "xhdpi" : "static/push/push_small_48.png",
                                "xxhdpi" : "static/push/push_small_72.png"
                                "ldpi" : "static/logo.png",
                                "mdpi" : "static/logo.png",
                                "hdpi" : "static/logo.png",
                                "xhdpi" : "static/logo.png",
                                "xxhdpi" : "static/logo.png"
                            }
                        }
                    }
mqtt/MqttView.vue
@@ -4,7 +4,6 @@
</template>
<script>
    export default {
        name: 'MqttView',
        data() {
@@ -13,92 +12,105 @@
            }
        },
        onReady() {
        },
        methods: {
            initMqtt(){
            initMqtt() {
                uni.getSystemInfo({
                    success: (res) => {
                        let deviceId = res.deviceId
                        if (!deviceId) {
                            deviceId = 'mobile-' + this.tenantId + '-' + Date.parse(new Date())
                        }
                        uni.setStorageSync(this.$constant.DEVICE_ID, 'mobile-' + this.tenantId + '-' + res.deviceId);
                        uni.setStorageSync(this.$constant.DEVICE_ID, 'mobile-' + this.tenantId + '-' + res
                            .deviceId);
                    }
                })
                    this.startConnect();
                this.startConnect();
            },
             /* è¿žæŽ¥MQTT */
             async startConnect() {
                 var _this = this
                 const account = uni.getStorageSync('account');
                 const deviceid = uni.getStorageSync(this.$constant.DEVICE_ID);
                 if (!account) {
                     return false
                 }
                 let opts = {
                     // #ifdef H5
                     url: 'ws://' + this.$api.mqttBaseUrl + ':8083/mqtt',
                     // #endif
                     // #ifdef APP-PLUS
                     url: 'wx://' + this.$api.mqttBaseUrl + ':8083/mqtt',
                     // #endif
                     clientId: deviceid,
                     username: account.username,
                     password: account.password
                 }
                 if (!this.$mqttTool.client) {
                     var client = await this.$mqttTool.connect(opts);
                 }
                 //订阅查询设备状态返回数据
                 this.$mqttTool.subscribe({
                     topic: this.$constant.SERVICE_DOWN + '/' + deviceid + '/#',
                     qos: 0
                 }).then(res => {
                     console.error(res)
                 })
                 //订阅设备故障广播(广播不在乎客户端id,发送给租户下所有在线的设备)
                 this.$mqttTool.subscribe({
                     topic: this.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT.replace('%s', this.tenantId),
                     qos: 0
                 }).then(res => {
                     console.error(res)
                 })
                 //订阅发送指令返回结果
                 // this.$mqttTool.subscribe({
                 //     topic: this.$constant.SERVICE_RES_EQU_CMD,
                 //     qos: 0
                 // }).then(res => {
                 //     console.error(res)
                 // })
                 // if (!client) {
                 //     return false
                 // }
                let that = this
                 client.on('connect', function(res) {
                     console.error('连接成功')
                 })
                 client.on('reconnect', function(res) {
                     console.error('重新连接')
                 })
                 client.on('error', function(res) {
                     console.info('连接错误')
                 })
                 client.on('close', function(res) {
                     console.error('关闭成功')
                 })
                 client.on('message', function(topic, message, buffer) {
                    that.$eventBus.$emit(that.$constant.MQTT_TOPIC_MESSAGE, message);
                     console.info(message)
                 })
             },
            /* è¿žæŽ¥MQTT */
            async startConnect() {
                var _this = this
                const account = uni.getStorageSync('account');
                const deviceid = uni.getStorageSync(this.$constant.DEVICE_ID);
                if (!account) {
                    return false
                }
                let opts = {
                    // #ifdef H5
                    url: 'ws://' + this.$api.mqttBaseUrl + ':8083/mqtt',
                    // #endif
                    // #ifdef APP-PLUS
                    url: 'wx://' + this.$api.mqttBaseUrl + ':8083/mqtt',
                    // #endif
                    clientId: deviceid,
                    username: account.username,
                    password: account.password
                }
                if (!this.$mqttTool.client) {
                    this.$mqttTool.client = await this.$mqttTool.connect(opts);
                }
                //订阅查询设备状态返回数据
                this.$mqttTool.subscribe({
                    topic: this.$constant.SERVICE_DOWN + '/' + deviceid + '/#',
                    qos: 0
                }).then(res => {
                    console.error(res)
                })
                //订阅设备故障广播(广播不在乎客户端id,发送给租户下所有在线的设备)
                this.$mqttTool.subscribe({
                    topic: this.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT.replace('%s', this.tenantId),
                    qos: 0
                }).then(res => {
                    console.error(res)
                })
                //订阅设备实时故障(主动请求只发给请求设备)
                this.$mqttTool.subscribe({
                    topic: this.$constant.SERVICE_ONECE_TENANT_REAL_FAULT.replace('%s', deviceid),
                    qos: 0
                }).then(res => {
                    console.error(res)
                })
                //订阅发送指令返回结果
                // this.$mqttTool.subscribe({
                //     topic: this.$constant.SERVICE_RES_EQU_CMD,
                //     qos: 0
                // }).then(res => {
                //     console.error(res)
                // })
                // if (!client) {
                //     return false
                // }
                let that = this
                let client = this.$mqttTool.client
                client.on('connect', function(res) {
                    console.error('连接成功')
                })
                client.on('reconnect', function(res) {
                    console.error('重新连接')
                })
                client.on('error', function(res) {
                    console.info('连接错误')
                })
                client.on('close', function(res) {
                    console.error('关闭成功')
                })
                client.on('message', function(topic, message, buffer) {
                    uni.$emit(that.$constant.MQTT_TOPIC_MESSAGE, message);
                    console.error("收到message(总):"+topic)
                })
            },
        },
        computed: {
            tenantId() {
pages.json
@@ -113,38 +113,44 @@
                }
            }
        },
        {
            "path" : "pages/tabBar/device",
            "style" :
            {
                "navigationStyle": "custom",
                "app-plus": {
                    "titleNView": false
                }
            }
        },
        {
            "path" : "pages/tabBar/warning",
            "style" :
            {
            "path": "pages/tabBar/device",
            "style": {
                "navigationStyle": "custom",
                "app-plus": {
                    "titleNView": false
                }
            }
        },
        {
            "path" : "pages/device/control",
            "style" :
            {
                "navigationStyle": "custom",
                "app-plus": {
                    "titleNView": false
                }
            }
        },
        {
            "path": "pages/tabBar/warning",
            "style": {
                "navigationStyle": "custom",
                "app-plus": {
                    "titleNView": false
                }
            }
        },
        {
            "path": "pages/device/control",
            "style": {
                "navigationStyle": "custom",
                "app-plus": {
                    "titleNView": false
                }
            }
        },
        {
            "path": "pages/login/switchMode",
            "style": {
                "navigationStyle": "custom",
                "app-plus": {
                    "titleNView": false
                }
            }
        }
    ],
    "globalStyle": {
        "navigationBarTextStyle": "white",
@@ -167,17 +173,17 @@
            "pagePath": "pages/tabBar/device",
            "iconPath": "static/tabBar/shop.png",
            "selectedIconPath": "static/tabBar/shop_cur.png",
            "text": "监控"
            "text": "机台监控"
        }, {
            "pagePath": "pages/tabBar/warning",
            "iconPath": "static/tabBar/analy.png",
            "selectedIconPath": "static/tabBar/analy_cur.png",
            "text": "报警"
            "text": "报警通知"
        }, {
            "pagePath": "pages/tabBar/formula",
            "iconPath": "static/tabBar/order.png",
            "selectedIconPath": "static/tabBar/order_cur.png",
            "text": "配方"
            "text": "干燥配方"
        }, {
            "pagePath": "pages/tabBar/me",
            "iconPath": "static/tabBar/me.png",
pages/charts/charts.vue
@@ -113,7 +113,7 @@
    export default {
        data() {
            return {
                text: '智能中草药干燥设备配套系统',
                text: '兰浦智能干燥',
                cWidth: '',
                cHeight: '',
                pixelRatio: 1,
pages/device/control.vue
@@ -291,12 +291,8 @@
            // })
            let _this = this
            this.$mqttTool.client.on('message', function(topic, message, buffer) {
                console.error("解析数据")
                const msg = JSON.parse(message);
                console.error(topic)
                console.error(msg)
                console.error(msg.success)
                if (topic == _this.$constant.SERVICE_RES_EQU_STATU.replace('equ', _this.deviceId)) {
                    if (msg.success) {
                        _this.connected = msg.connected
pages/login/login.vue
@@ -2,7 +2,7 @@
<template>
    <view class="contaier">
        <view class="top-bg">
            <view class="text-white text-bold text-xxxl">智能中草药干燥设备配套系统</view>
            <view class="text-white text-bold text-xxxl">兰浦<text @longpress="showModal">智能</text>干燥</view>
            <view class="margin-top-xs text-white">欢迎使用,请先登录</view>
        </view>
        <u-form labelPosition="left" :model="model" ref="form">
@@ -22,17 +22,27 @@
            <view class="padding margin-top-xs">
                <button @click="submit" class="cu-btn block round bg-login-zl margin-tb-sm lg">立即登录</button>
                <view class="text-gray flex justify-between padding-lr-sm">
                    <text>注册账号</text>
                    <text>忘记密码</text>
                    <!-- <text @click="switchMode">局域网模式</text> -->
                    <!-- <text>忘记密码</text> -->
                </view>
            </view>
        </u-form>
        <u-modal :show="show" title="提示" @confirm="handleSwitchMode">
            <view class="slot-content">
                <u-input v-model="pass" style="width: 100%;" placeholder="请输入管理员密码" border="surround" password
                    clearable></u-input>
            </view>
        </u-modal>
    </view>
</template>
<script>
    export default {
        data() {
            return {
                show: false,
                pass: "",
                model: {
                    username: "tongjitang",
                    password: "123456"
@@ -54,52 +64,56 @@
            };
        },
        onLoad() {
            const account = uni.getStorageSync('account');
            if (account) {
                this.model = account
            }
            let _that = this
            // uni-app客户端获取push客户端标记
            // uni.getPushClientId({
            //     success: (res) => {
            //         let push_clientid = res.cid
            //         _that.model.username  = push_clientid
            //         console.log('客户端推送标识:', push_clientid)
            //     },
            //     fail(err) {
            //         _that.model.username  = err
            //         console.log(err)
            //     }
            // })
            // uni.onPushMessage((res) => {
            //     _that.model.username  = JSON.stringify(res)
            //     console.log("收到推送消息:", res) //监听推送消息
            // })
        },
        mounted() {
            setTimeout(() => {
                this.model.username = this.clientPushId
            }, 1000)
        },
        computed: {
            cid() {
                return this.$store.getters.getCid; // ä½¿ç”¨ getter èŽ·å– cid
            clientPushId() {
                return this.$store.getters.getClientPushId;
            },
        },
        methods: {
            showModal() {
                this.pass = ""
                this.show = true
            },
            handleSwitchMode() {
                this.show = false
                if (this.pass === 'lanbaoit') {
                    this.switchMode()
                }
            },
            switchMode() {
                uni.navigateTo({
                    url: "/pages/login/switchMode"
                })
            },
            forget() {
                uni.navigateTo({
                    url: "/pages/tabBar/demo"
                })
            },
            submit() {
                if (this.model.username.startsWith("http")) {
                    uni.setStorageSync('baseurl', this.model.username);
                    this.model.username = null
                    uni.$u.toast('配置baseUrl成功,请退出程序重新启动~')
                    return false;
                const mode = uni.getStorageSync('mode');
                //首次登录没有配置,默认为云服务模式
                if (!mode) {
                    uni.setStorageSync('mode', 'cloud');
                }
                const curMode = uni.getStorageSync('mode');
                getApp().globalData.mode = curMode;
                this.$refs.form.validate().then(res => {
                    this.login()
@@ -111,20 +125,15 @@
            login() {
                this.$api.login(this.model).then((res) => {
                    if (res.success) {
                        console.log('request success', res)
                        uni.showToast({
                            title: '登录成功',
                            icon: 'success',
                            mask: true
                        });
                        //缓存信息
                        uni.setStorageSync('account', this.model);
                        uni.setStorageSync('userinfo', res.result.userInfo);
                        uni.setStorageSync('token', res.result.token);
                        uni.switchTab({
                            url: '/pages/tabBar/general'
@@ -143,9 +152,7 @@
                    console.log('request fail', err);
                })
            },
            onReset(e) {
                console.log(e)
            }
        },
        onReady() {
            //onReady ä¸ºuni-app支持的生命周期之一
pages/login/switchMode.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,169 @@
<template>
    <view class="app">
        <cu-custom bgColor="bg-gradual-blue" :isBack="true">
            <block slot="content">切换模式</block>
        </cu-custom>
        <view class="cu-bar bg-white margin-top">
            <view class="action sub-title">
                <text class="text-xl text-bold text-blue text-shadow">当前模式</text>
                <text class="text-ABC text-blue">current</text>
            </view>
        </view>
        <u-form labelPosition="left" ref="form">
            <view class="padding-lr info-box">
                <view class="cover-view" @tap.catch="showSelect">
                </view>
                <view style="position: relative;">
                    <u-form-item label="模式" prop="username" borderBottom>
                        <u-input v-model="sModel" border="none" suffixIcon="arrow-right" readonly></u-input>
                    </u-form-item>
                </view>
                <u-form-item label="地址" prop="password" borderBottom v-show="showIp">
                    <u-input v-model="ip" placeholder="请输入IP地址,如:192.168.1.1" border="none" clearable>
                    </u-input>
                </u-form-item>
                <view class="submitBtn" @click="saveMode">保存</view>
                <view style="height: 20rpx;background-color: white"></view>
            </view>
        </u-form>
        <u-action-sheet :closeOnClickAction="true" @close="show=false" @select="selectMode" :safeAreaInsetBottom="true"
            :actions="switchList" title="选择模式" cancelText="取消" :show="show"></u-action-sheet>
    </view>
</template>
<script>
    import {
        vShow
    } from "vue";
    export default {
        data() {
            return {
                switchList: [{
                    name: '云服务',
                    index: 0
                }, {
                    name: '局域网',
                    index: 1
                }],
                show: false,
                sModel: '云服务',
                ip: '',
                showIp: false,
                selectModeIndex: 0,
            };
        },
        mounted() {
            const mode = uni.getStorageSync('mode');
            const lan_ip = uni.getStorageSync('lan_ip');
            if (mode) {
                if (mode == 'cloud') {
                    this.sModel = '云服务'
                    this.showIp = false
                } else if (mode == 'lan') {
                    this.sModel = '局域网'
                    this.showIp = true
                }
                if (this.sModel == '局域网') {
                    this.ip = lan_ip
                }
            }
        },
        methods: {
            showSelect() {
                this.show = true
            },
            selectMode(item) {
                this.sModel = item.name
                console.error(item.index)
                this.showIp = item.index === 1
                this.selectModeIndex = item.index
            },
            saveMode() {
                if (!this.ip | !this.isValidIPv4(this.ip)) {
                    uni.$u.toast('请输入正确IP地址~')
                    return false
                }
                if (this.selectModeIndex === 0) {
                    uni.removeStorageSync('lan_ip')
                    uni.setStorageSync('mode', 'cloud');
                } else if (this.selectModeIndex === 1) {
                    uni.setStorageSync('mode', 'lan');
                    uni.setStorageSync('lan_ip', this.ip);
                }
                uni.$u.toast('配置成功,请完全退出程序后重新启动~')
            },
            isValidIPv4(ip) {
                const ipv4Regex =
                    /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
                return ipv4Regex.test(ip);
            }
        }
    }
</script>
<style lang="scss">
    .cover-view {
        position: absolute;
        /* å¿…须是相对定位或绝对定位 */
        z-index: 10;
        /* ç¡®ä¿ z-index å€¼é«˜äºŽ u-input */
        width: 100%;
        height: 80rpx;
        background-color: transparent;
    }
    .info-box {
        position: relative;
        background-color: white;
    }
    .distinguishBox {
        padding: 5rpx 30rpx 20rpx;
        background: white;
        view {
            margin-bottom: 20rpx;
        }
    }
    .disLabel {
        text-align-last: justify;
        text-align: justify;
        text-justify: distribute-all-lines;
        min-width: 142rpx;
        display: inline-block;
        margin-right: 5rpx;
    }
    .submitBtn {
        width: 90%;
        height: 90rpx;
        background: #007aec;
        line-height: 90rpx;
        margin: 50rpx auto;
        text-align: center;
        font-size: 34rpx;
        color: #fff;
        border-radius: 12rpx;
    }
</style>
pages/tabBar/components/hisWarning.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,599 @@
<template>
    <view>
        <view class="card-box center dynamic shadow">
            <view class="title-box margin-bottom-sm">
                <view style="width: 100vw;" class="left justify-between">
                    <view class="flex align-center">
                        <uni-text class="cuIcon-titles text-blue"></uni-text>
                        <view class="title">最近报警</view>
                    </view>
                    <view>
                        <text class="text-blue text-sm">更多></text>
                    </view>
                </view>
            </view>
            <view class="borderTop">
                <view class="h-table">
                    <view class="h-tr h-tr-2 ">
                        <view class="h-td">机台</view>
                        <view class="h-td">故障</view>
                        <view class="h-td">时间</view>
                        <view class="h-td">类型</view>
                    </view>
                    <view class="h-tr h-tr-2" v-for="(item,index) in faultList">
                        <view class="h-td">{{item.equName}}</view>
                        <view class="h-td text-ellipsis">{{item.faultName}}</view>
                        <view class="h-td text-ellipsis">{{item.faultTimeStr}}</view>
                        <view class="h-td">{{item.faultType===1?'故障':'告警'}}</view>
                    </view>
                </view>
            </view>
        </view>
        <view class="card-box center dynamic shadow">
            <view class="title-box margin-bottom-sm">
                <view style="width: 100vw;" class="left justify-between">
                    <view class="flex align-center">
                        <uni-text class="cuIcon-titles text-blue"></uni-text>
                        <view class="title">报警统计</view>
                    </view>
                    <view>
                        <text class="text-gray text-sm"></text>
                    </view>
                </view>
            </view>
            <view class="chartsMain" style="height: 400rpx;">
                <!-- <qiun-data-charts type="mount" :opts="opts" :ontouch='true' :animation="true" :chartData="mountData" /> -->
                <qiun-data-charts canvasId="chat1"  type="column" :errorMessage="errorMessage1"
                    :opts="opts" :ontouch='true' :animation="true" :chartData="statisData" />
            </view>
        </view>
        <view class="card-box center dynamic shadow">
            <view class="title-box margin-bottom-sm">
                <view style="width: 100vw;" class="left justify-between">
                    <view class="flex align-center">
                        <uni-text class="cuIcon-titles text-blue"></uni-text>
                        <view class="title">报警对比</view>
                    </view>
                    <view>
                        <text class="text-gray text-sm"></text>
                    </view>
                </view>
            </view>
            <view class="chartsMain">
                <qiun-data-charts canvasId="chat2" type="pie"   :errorMessage="errorMessage2"
                    :opts="optsPie" :ontouch='true' :animation="true" :chartData="pieData" />
            </view>
        </view>
        <view class="card-box center dynamic shadow">
            <view class="title-box margin-bottom-sm">
                <view style="width: 100vw;" class="left justify-between">
                    <view class="flex align-center">
                        <uni-text class="cuIcon-titles text-blue"></uni-text>
                        <view class="title">报警频率</view>
                    </view>
                    <view>
                        <text class="text-gray text-sm"></text>
                    </view>
                </view>
            </view>
            <view class="chartsMain" style="height: 500rpx;">
                <!-- <qiun-data-charts type="mount" :opts="{extra:{mount:{type:'mount',widthRatio:1.5}}}"
                :chartData="Mount" /> -->
                <qiun-data-charts canvasId="chat3" type="word"  :errorMessage="errorMessage3"
                    :chartData="wordData" />
            </view>
        </view>
        <view class="card-box bot dynamic shadow">
            <view class="title-box margin-bottom-sm">
                <view style="width: 100vw;" class="left justify-between">
                    <view class="flex align-center">
                        <uni-text class="cuIcon-titles text-blue"></uni-text>
                        <view class="title">报警分析</view>
                    </view>
                    <view>
                        <text class="text-gray text-sm"></text>
                    </view>
                </view>
            </view>
            <view class="chartsMain" style="height: 500rpx;">
                <qiun-data-charts canvasId="chat4" type="line"   :opts="optsStep"
                    :errorMessage="errorMessage4" :ontouch='true' :animation="true" :chartData="statisData" />
                <!-- <qiun-data-charts type="radar" :opts="optsRadar" :chartData="dataRadar" /> -->
            </view>
        </view>
    </view>
</template>
<script>
    import dayjs from 'dayjs'
    export default {
        data() {
            return {
                "PieA": {
                    "series": [{
                        "data": [
                        ]
                    }]
                },
                errorMessage1: "",
                errorMessage2: "",
                errorMessage3: "",
                errorMessage4: "",
                faultList: [],
                pieData: {
                    "series": [{
                        "data": []
                    }]
                },
                statisData: {
                    series: [],
                    categories: []
                },
                wordData: {
                    series: []
                },
                optsStep: {
                    enableScroll: true,
                    xAxis: {
                        marginTop: 10,
                        itemCount: 8,
                        rotateLabel: true
                    },
                    extra: {
                        line: {
                            type: 'step'
                        }
                    }
                },
                opts: {
                    enableScroll: true,
                    xAxis: {
                        marginTop: 10,
                        itemCount: 8,
                        rotateLabel: true
                    },
                    legend: {
                        show: false
                    },
                    yAxis: {
                        data: [{
                            min: 0
                        }]
                    },
                    extra: {
                        column: {
                            width: 12,
                            seriesGap: 5,
                            barBorderRadius: [6, 6, 6, 6]
                        }
                    }
                },
                optsPie: {
                    padding: [5, 5, 5, 5],
                    legend: {
                        show: false
                    },
                    fontSize: 12,
                    extra: {
                        pie: {
                            activeOpacity: 0.5,
                            activeRadius: 10,
                            offsetAngle: 0,
                            labelWidth: 1,
                            border: true,
                            borderWidth: 3,
                            borderColor: "#FFFFFF",
                            linearType: "custom"
                        }
                    }
                },
            }
        },
        mounted() {
            setTimeout(() => {
                this.init()
            }, 300)
        },
        methods: {
            init() {
                // èŽ·å–N天前的0点0分0秒
                let daysAgo = dayjs().subtract(3, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss');
                // èŽ·å–ä»Šå¤©çš„23点59分59秒
                let today = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss');
                let params = {
                    column: 'createTime',
                    order: 'desc',
                    startTime_begin: daysAgo,
                    startTime_end: today,
                    pageNo: 1,
                    pageSize: 3
                }
                this.$api.queryHisFaultList(params).then((res) => {
                    this.faultList = res.result.records
                }).catch(res => {
                })
                let chartParam = {
                    startTime_begin: daysAgo,
                    startTime_end: today,
                }
                this.statisData = {}
                this.pieData = {}
                this.wordData = {}
                this.$api.queryHisFaultChartList(chartParam).then((res) => {
                    // æŸ±çж图
                    const statis = {
                        series: [],
                        categories: []
                    };
                    //饼图
                    const pieData = {
                        series: [{
                            data: []
                        }]
                    };
                    //词云图
                    const wordData = {
                        series: []
                    };
                    this.$emit('handleData', res)
                    // æ£€æŸ¥ res æ˜¯å¦ä¸º null æˆ–者不是一个数组
                    if (!Array.isArray(res) || res === null || res.length === 0) {
                        this.errorMessage1 = "暂无数据"+ dayjs().format('HH:mm:ss');
                        this.errorMessage2 = "暂无数据"+ dayjs().format('HH:mm:ss');
                        this.errorMessage3 = "暂无数据"+ dayjs().format('HH:mm:ss');
                        this.errorMessage4 = "暂无数据"+ dayjs().format('HH:mm:ss');
                        return;
                    }
                   console.error(res)
                    // å•次遍历 res,同时寻找最大 ecount å¹¶æž„建 categories、data å’Œ pieData
                    const result = res.reduce((acc, item) => {
                        if (item.faultType === 1) {
                            acc.categories.push(item.faultName);
                            const value = item.ecount;
                            acc.data.push({
                                value,
                                color: undefined // å…ˆä¸è®¾å®šé¢œè‰²
                            });
                        }
                        // æ›´æ–°æœ€å¤§ ecount
                        if (item.ecount > acc.maxEcount) {
                            acc.maxEcount = item.ecount;
                        }
                        return acc;
                    }, {
                        maxEcount: 0,
                        categories: [],
                        data: []
                    });
                    // ç¬¬äºŒæ¬¡éåކ data æ•°ç»„,为所有最大 ecount è®¾ç½®é¢œè‰²
                    result.data.forEach((item, index) => {
                        if (item.value === result.maxEcount) {
                            result.data[index].color = '#f04864';
                        }
                    });
                    // å°†å¤„理结果赋值给 statis
                    statis.series.push({
                        name: "故障",
                        data: result.data.map(item => 'color' in item ? item : item.value)
                    });
                    statis.categories = result.categories;
                    res.filter(item => item.faultType == 1).forEach(item => {
                        // æž„建 pieData
                        pieData.series[0].data.push({
                            name: item.faultName,
                            value: item.ecount,
                            labelText: item.faultName + ":" + item.ecount + "次"
                        });
                    })
                    // è®¾ç½®åˆ°ç»„件属性
                    this.statisData = statis;
                    this.pieData = pieData;
                    if (this.statisData.series.length === 0) {
                        this.errorMessage1 = "暂无数据"+ dayjs().format('HH:mm:ss');
                        this.errorMessage4 = "暂无数据"+ dayjs().format('HH:mm:ss');
                    }
                    if (this.pieData.series[0].data.length === 0) {
                        this.errorMessage2 = "暂无数据"+ dayjs().format('HH:mm:ss');
                    }
                    const cdata = res.filter(item => item.faultType === 1).map(item => ({
                        faultName: item.faultName,
                        ecount: item.ecount
                    }));
                    console.error(cdata)
                    const wordCloudData = this.generateWordCloudData(cdata)
                    wordData.series = wordCloudData
                    this.wordData = wordData
                    console.error(this.wordData)
                    if (this.wordData.series.length === 0) {
                        this.errorMessage3 = "暂无数据"+ dayjs().format('HH:mm:ss');
                    }
                }).catch(res => {
                })
            },
            generateWordCloudData(data) {
                // å®šä¹‰å­—体大小范围
                const minFontSize = 10;
                const maxFontSize = 20;
                // å¦‚果数据为空或不是数组,则返回空数组
                if (!Array.isArray(data) || data.length === 0) {
                    return [];
                }
                // æ‰¾åˆ°æœ€å¤§å’Œæœ€å°çš„æ•…障次数
                let minCount = Infinity;
                let maxCount = -Infinity;
                data.forEach(item => {
                    if (item.ecount < minCount) minCount = item.ecount;
                    if (item.ecount > maxCount) maxCount = item.ecount;
                });
                // å¦‚果所有故障次数都相同,则直接为每个条目设置相同的字体大小
                if (minCount === maxCount) {
                    return data.map(item => ({
                        name: item.faultName,
                        textSize: maxFontSize
                    }));
                }
                // è®¡ç®—每个条目的字体大小
                const wordCloudData = data.map(item => {
                    const fontSize = ((maxFontSize - minFontSize) * (item.ecount - minCount)) / (maxCount -
                        minCount) + minFontSize;
                    return {
                        name: item.faultName,
                        textSize: Math.round(fontSize) // å¯é€‰ï¼šå››èˆäº”入到最接近的整数
                    };
                });
                return wordCloudData;
            },
        }
    }
</script>
<style lang="scss" scoped>
    @import "components/table/helang-table";
    .app {}
    .card-box {
        margin: 20rpx;
        padding: 20rpx;
        box-sizing: border-box;
        background-color: white;
        border-radius: 20rpx;
        font-family: Helvetica Neue, Helvetica, sans-serif;
    }
    .text-ellipsis {
        white-space: nowrap;
        /* é˜²æ­¢æ–‡æœ¬æ¢è¡Œ */
        overflow: hidden;
        /* è¶…出部分隐藏 */
        text-overflow: ellipsis;
        /* è¶…出部分用省略号表示 */
        max-width: 100%;
        /* è®¾ç½®æœ€å¤§å®½åº¦ */
    }
    .top {
        margin: 0 20rpx;
        border-radius: 0;
        border-top-left-radius: 20rpx;
        border-top-right-radius: 20rpx;
        border-bot-left-radius: 0;
        border-bot-right-radius: 0;
    }
    .center {
        margin: 0 20rpx;
        border-radius: 0;
    }
    .bot {
        margin: 0 20rpx 20rpx 20rpx;
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        border-bot-left-radius: 20rpx;
        border-bot-right-radius: 20rpx;
    }
    .title-box {
        display: flex;
        flex-direction: row;
        align-items: center;
        .left {
            display: flex;
            align-items: center;
            .title {
                margin: 0 10rpx;
                font-weight: bold;
            }
        }
        .right {
            display: flex;
            align-items: center;
            .title {
                margin: 0 10rpx;
                font-weight: bold;
            }
        }
    }
    .info-box {
        margin-top: 20rpx;
        display: flex;
        flex-direction: row;
        align-items: center;
        .left {
            display: flex;
            align-items: center;
            .title {
                margin: 0 10rpx;
            }
        }
        .right {
            display: flex;
            align-items: center;
            .title {
                margin: 0 10rpx;
            }
        }
    }
    .chartsMain {
        width: 100%;
        height: 320rpx;
        padding-top: 15rpx;
        background: #fff;
        margin-bottom: 24rpx;
        border-top: 2rpx solid #f2f2f2;
        .charts {
            width: 50%;
            height: 450rpx;
            box-sizing: border-box;
        }
    }
    .tab-box {
        display: flex;
        justify-content: center;
        /* å¼ºåˆ¶æ°´å¹³å±…中 */
    }
    .swiper {
        height: 2116rpx;
    }
    .swiper-item-view {
        height: 2116rpx;
    }
    .picBox {
        margin: 0 20rpx;
        background-color: white;
        image {
            border-radius: 8rpx;
            width: 100%;
        }
    }
    .borderTop {
        border-top: 2rpx solid #f2f2f2;
        padding-top: 20rpx;
    }
    // å¼¹å‡ºå±‚背景遮罩start
    .dropdown-mask {
        background: rgba(0, 0, 0, 0.5);
    }
    .lock-page {
        height: 100vh;
        width: 100vw;
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 998;
    }
    // å¼¹å‡ºå±‚背景遮罩end
</style>
pages/tabBar/components/realWarning.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,455 @@
<template>
    <view>
        <view class="card-box center dynamic shadow">
            <view class="title-box margin-bottom-sm">
                <view style="width: 100vw;" class="left justify-between">
                    <view class="flex align-center">
                        <uni-text class="cuIcon-titles text-blue"></uni-text>
                        <view class="title">设备 </view>
                    </view>
                    <view>
                        <!-- <text class="text-blue text-sm">更多></text> -->
                    </view>
                </view>
            </view>
            <view class="fault-box">
                <image style="width: 100%;border-radius: 20rpx; " src="../../../static/image/ganzaoji-x.png"
                    mode="aspectFit">
                </image>
                <view class="fault-inner">
                    <view  class="fault-info"   v-for="(item,index) in realFaults" :style="getFaultItemStyle(item)">
                        <view :class="['fault-animal', item.type === 1 ? 'fault-marker' : 'warn-marker']"></view>
                        <view :class="['fault-tag', item.type === 1 ? 'fault-text' : 'warn-text']">{{item.name}}</view>
                    </view>
                </view>
            </view>
        </view>
        <view class="card-box center dynamic shadow">
            <view class="title-box margin-bottom-sm">
                <view style="width: 100vw;" class="left justify-between">
                    <view class="flex align-center">
                        <uni-text class="cuIcon-titles text-blue"></uni-text>
                        <view class="title">实时报警 </view>
                    </view>
                    <view>
                        <text class="text-blue text-sm">{{refreshTime}}</text>
                    </view>
                </view>
            </view>
            <view class="borderTop" v-for="(item,index) in     dataList">
                <view style="height: 180rpx;display: flex;align-items: center;">
                    <view>
                        <image style="width: 80rpx;height: 80rpx;margin:20rpx;border-radius: 20rpx; "
                            src="../../../static/image/pic_gz.jpg"></image>
                    </view>
                    <view
                        style="display: flex;flex: 1;flex-direction: column; justify-content: space-between;  ;height: 100%;padding: 20rpx;">
                        <view class="text-bold">
                            {{$lget(item,'faultName')}}
                        </view>
                        <view class="text-gray text-sm">
                            è®¾å¤‡:{{$lget(item,'equName')}}
                        </view>
                        <view class="text-gray text-sm">
                            ç±»åž‹:{{checkFaultType($lget(item,'faultType'))}}
                        </view>
                        <view class="text-gray text-sm">
                            æ•…障时间:{{coverTime($lget(item,'startTime'))}}
                        </view>
                    </view>
                    <view style="width: 100rpx;"></view>
                </view>
            </view>
        </view>
    </view>
</template>
<script>
    import dayjs from 'dayjs'
    export default {
        data() {
            return {
                dataList: [],
                //实时报警数据更新时间(一般10s)
                refreshTime: '',
                realFaults:[],
                allFaults: [{ id: 1, name: '设备急停(总)', type: 1, position: {top: 70,left: 230},show: false},
                 { id: 2, name: '左前急停', type: 1, position: {top: 100,left: 250},show: false},
                 { id: 3, name: '出料急停', type: 1, position: {top: 130,left: 230},show: false},
                 { id: 4, name: '面板急停', type: 1, position: {top: 160,left: 250},show: false},
                 { id: 5, name: '风箱升超时', type: 1, position: {top: 190,left: 230},show: false},
                 { id: 6, name: '风箱降超时', type: 1, position: {top: 220,left: 250},show: false},
                 { id: 7, name: '风机过流', type: 1, position: {top: 250,left: 230},show: false},
                 { id: 8, name: '滚筒升超时', type: 1, position: {top: 280,left: 250},show: false},
                 { id: 9, name: '滚筒降超时', type: 1, position: {top: 310,left: 230},show: false},
                 { id: 10, name: '滚筒电机过流', type: 1, position: {top: 340,left: 250},show: false},
                 { id: 11, name: '温度失控', type: 1, position: {top: 370,left: 230},show: false},
                 { id: 12, name: '左前门报警', type: 2, position: {top: 100,left: 0},show: false},
                 { id: 13, name: '左后门报警', type: 2, position: {top: 130,left: 20},show: false},
                 { id: 14, name: '右前门报警', type: 2, position: {top: 160,left: 0},show: false},
                 { id: 15, name: '右后门报警', type: 2, position: {top: 190,left: 20},show: false},
                 { id: 16, name: '滚筒不在高位', type: 2, position: {top: 220,left: 0},show: false},
                 { id: 17, name: '风箱不在高位', type: 2, position: {top: 250,left: 20},show: false},
                 { id: 18, name: '风箱不在低位', type: 2, position: {top: 280,left: 0},show: false},
                 { id: 19, name: '加热位传感器报警', type: 2, position: {top: 310,left: 20},show: false},
                 { id: 20, name: '左前风箱高位', type: 2, position: {top: 340,left: 0},show: false},
                 { id: 21, name: '左前风箱低位', type: 2, position: {top: 370,left: 20},show: false},
                 { id: 22, name: '左后风箱高位', type: 2, position: {top: 100,left: 430},show: false},
                 { id: 23, name: '左后风箱低位', type: 2, position: {top: 130,left: 450},show: false},
                 { id: 24, name: '右前风箱高位', type: 2, position: {top: 160,left: 430},show: false},
                 { id: 25, name: '右前风箱低位', type: 2, position: {top: 190,left: 450},show: false},
                 { id: 26, name: '右后风箱高位', type: 2, position: {top: 220,left: 430},show: false},
                 { id: 27, name: '右后风箱低位', type: 2, position: {top: 250,left: 450},show: false},
                 { id: 28, name: '左前滚筒低位', type: 2, position: {top: 280,left: 430},show: false},
                 { id: 29, name: '左后滚筒低位', type: 2, position: {top: 310,left: 450},show: false},
                 { id: 30, name: '右前滚筒低位', type: 2, position: {top: 340,left: 430},show: false},
                 { id: 31, name: '右后滚筒低位', type: 2, position: {top: 370,left: 450},show: false},
                  ],
            }
        },
        mounted() {
            this.mqttData()
        },
        methods: {
            mqttData() {
                //处理实时报警数据
                uni.$on(this.$constant.MQTT_TOPIC_MESSAGE, (data) => {
                    let json = JSON.parse(data);
                    if (json == null || json.data == null || json.topic == null) return false
                    let wdata = json.data
                    let topic = json.topic
                    const realFaultTopic = this.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT.replace('%s', this
                        .tenantId)
                    const oneceFaultTopic = this.$constant.SERVICE_ONECE_TENANT_REAL_FAULT.replace('%s', this
                        .deviceId)
                    switch (topic) {
                        //topic å®žæ—¶æŠ¥è­¦
                        case realFaultTopic:
                        case oneceFaultTopic:
                            if (wdata.length === 0) return false;
                            this.dataList = wdata
                            this.refreshTime = "更新时间:" + dayjs().format('HH:mm:ss');
                            const realFaultNames =  this.dataList.map(item => item.faultName);
                            const realFaults =    this.allFaults.filter(item=>
                                                realFaultNames.some(name =>
                                                name.startsWith(item.name)
                                                ))
                            this.realFaults = realFaults
                            //处理数据统计
                            this.$emit('handleData', this.dataList)
                            break
                    }
                });
            },
            getFaultItemStyle(item) {
                return {
                    position: 'absolute',
                    top: `${item.position.top}rpx`,
                    left: `${item.position.left}rpx`,
                    fontSize: '24rpx',
                };
            },
            checkFaultType(type) {
                if (type === 1) {
                    return "故障";
                } else if (type === 2) {
                    return "报警";
                }
            },
            coverTime(timestamp) {
                if (timestamp) {
                    return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')
                }
            },
        },
        computed: {
            tenantId() {
                const userinfo = uni.getStorageSync('userinfo');
                const tenantid = userinfo.loginTenantId
                return tenantid + "";
            },
            deviceId() {
                return uni.getStorageSync(this.$constant.DEVICE_ID);
            }
        },
    }
</script>
<style lang="scss" scoped>
    @import "components/table/helang-table";
    .app {}
    .card-box {
        margin: 20rpx;
        padding: 20rpx;
        box-sizing: border-box;
        background-color: white;
        border-radius: 20rpx;
        font-family: Helvetica Neue, Helvetica, sans-serif;
    }
    .top {
        margin: 0 20rpx;
        border-radius: 0;
        border-top-left-radius: 20rpx;
        border-top-right-radius: 20rpx;
        border-bot-left-radius: 0;
        border-bot-right-radius: 0;
    }
    .center {
        margin: 0 20rpx;
        border-radius: 0;
    }
    .bot {
        margin: 0 20rpx 20rpx 20rpx;
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        border-bot-left-radius: 20rpx;
        border-bot-right-radius: 20rpx;
    }
    .title-box {
        display: flex;
        flex-direction: row;
        align-items: center;
        .left {
            display: flex;
            align-items: center;
            .title {
                margin: 0 10rpx;
                font-weight: bold;
            }
        }
        .right {
            display: flex;
            align-items: center;
            .title {
                margin: 0 10rpx;
                font-weight: bold;
            }
        }
    }
    .info-box {
        margin-top: 20rpx;
        display: flex;
        flex-direction: row;
        align-items: center;
        .left {
            display: flex;
            align-items: center;
            .title {
                margin: 0 10rpx;
            }
        }
        .right {
            display: flex;
            align-items: center;
            .title {
                margin: 0 10rpx;
            }
        }
    }
    .chartsMain {
        width: 100%;
        height: 320rpx;
        padding-top: 15rpx;
        background: #fff;
        margin-bottom: 24rpx;
        border-top: 2rpx solid #f2f2f2;
        .charts {
            width: 50%;
            height: 450rpx;
            box-sizing: border-box;
        }
    }
    .tab-box {
        display: flex;
        justify-content: center;
        /* å¼ºåˆ¶æ°´å¹³å±…中 */
    }
    .swiper {
        height: 2116rpx;
    }
    .swiper-item-view {
        height: 2116rpx;
    }
    .fault-box {
        width: 100%;
        height: 460rpx;
        position: relative;
        .fault-inner {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            padding: 20rpx 0;
            background: transparent;
            overflow: hidden;
            .fault-info {
                position: absolute;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            .fault-tag {
                display: flex;
                align-items: center;
                margin-left: 6rpx;
                height: 28rpx;
                padding: 0 10rpx;
                background: gray;
                border-radius: 5rpx;
                font-size: 22rpx;
                font-weight: bold;
                color: red;
                 white-space: nowrap; /* ä¸æ¢è¡Œ */
                   overflow: hidden; /* è¶…出部分隐藏 */
                   text-overflow: ellipsis; /* è¶…出部分用省略号表示 */
            }
            .fault-marker{
                width: 20rpx;
                height: 20rpx;
                background: red;
                border-radius: 50%;
            }
            .warn-marker{
                width: 24rpx;
                height: 24rpx;
                background: orange;
                border-radius: 50%;
            }
            .fault-text{
                color: red;
            }
            .warn-text{
                color: orange;
            }
            .fault-animal {
                -webkit-animation: scaleout 2s infinite ease-in-out;
                animation: scaleout 2s infinite ease-in-out;
            }
            @-webkit-keyframes scaleout {
                0% {
                    -webkit-transform: scale(1);
                }
                100% {
                    -webkit-transform: scale(1.1);
                    opacity: 0;
                }
            }
            @keyframes scaleout {
                0% {
                    transform: scale(1);
                    -webkit-transform: scale(1);
                }
                100% {
                    transform: scale(1.1);
                    -webkit-transform: scale(1.1);
                    opacity: 0;
                }
            }
        }
    }
    .picBox {
        margin: 0 20rpx;
        background-color: white;
        image {
            border-radius: 8rpx;
            width: 100%;
        }
    }
    .borderTop {
        border-top: 2rpx solid #f2f2f2;
        padding-top: 20rpx;
    }
    // å¼¹å‡ºå±‚背景遮罩start
    .dropdown-mask {
        background: rgba(0, 0, 0, 0.5);
    }
    .lock-page {
        height: 100vh;
        width: 100vw;
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 998;
    }
    // å¼¹å‡ºå±‚背景遮罩end
</style>
pages/tabBar/device.vue
@@ -62,7 +62,7 @@
            <u-skeleton class=" " rows="20" :loading="loading" :title="false">
            <u-skeleton   rows="20" :loading="loading" :title="false">
                <!-- å¦‚果希望其他view跟着页面滚动,可以放在z-paging标签内 -->
                <view class="card-box dynamic shadow" v-for="(item,index) in dataList" :key="index"
                    @click="itemClick(item)">
@@ -132,6 +132,9 @@
                dataList: [],
                curDate:uni.$u.timeFormat(new Date(), 'yyyy-mm-dd'),
            }
        },
        onTabItemTap : function(e) {
        getApp().globalData.selectTab = e.index
        },
        methods: {
            queryList(pageNo, pageSize) {
@@ -156,7 +159,7 @@
                })
            },
            itemClick(item) {
            itemClick(item) {
                uni.navigateTo({
                    url: "/pages/device/control?code="+item.code+"&clientId="+item.clientId
                })
pages/tabBar/formula.vue
@@ -54,6 +54,9 @@
    computed: {
    },
    onTabItemTap : function(e) {
    getApp().globalData.selectTab = e.index
    },
    methods: {
      showItem(item){
        console.info(item)
pages/tabBar/general.vue
@@ -1,76 +1,10 @@
<template>
    <view class="app">
        <!--     <view class="cu-custom custom-bar" :style="[{height:CustomBar + 'px'}]">
            <view class="select-equ-box">
                <u-icon name="setting" color="rgb(255,255,255)" :size="20"></u-icon>
                <text style="margin-left: 10rpx;" @click="show">1号干燥机</text>
                <u-icon name="arrow-right" color="rgb(255,255,255)" :size="12"></u-icon>
            </view>
        </view>
         <view class="blank" :style="[{height:CustomBar + 'px'}]">
         </view>
         -->
        <cu-custom  bgColor="bg-gradual-blue" :isBack="false">
            <block slot="content">智能中草药</block>
        <cu-custom bgColor="bg-gradual-blue" :isBack="false">
            <block slot="content">兰浦智能干燥</block>
        </cu-custom>
        <mqtt-view  ref="mqttView"></mqtt-view>
        <u-toast ref="uToast"></u-toast>
        <!--         <view class="card-box dynamic shadow cu-list menu">
            <view class="title-box">
                <view class="left">
                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                    <view class="title">统计</view>
                </view>
            </view>
            <view class="cu-item" style="padding: 0;">
                <view class="content">
                    <image src='/static/me/icon/bianqian.png' class='png' mode='aspectFit'></image>
                    <text class="text-lg">烘干总量</text>
                </view>
                <view class="action">
                    <view class="cu-tag radius bg-blue light">
                        1200kg
                    </view>
                </view>
            </view>
            <view class="cu-item" style="padding: 0;">
                <view class="content">
                    <image src='/static/me/icon/bianqian.png' class='png' mode='aspectFit'></image>
                    <text class="text-lg">烘干时长</text>
                </view>
                <view class="action">
                    <view class="cu-tag radius bg-blue light">
                        300分钟
                    </view>
                </view>
            </view>
            <view class="bxBox">
                <image src="/static/me/icon/bianqian.png" class="png" style="width: 48upx;
    height: 48upx;" mode="aspectFit"></image>
                <view style="padding-left: 10rpx;" class="text-lg">烘干药材</view>
            </view>
            <view class='padding-bottom-sm flex flex-wrap bg-white' style="justify-content: flex-end;">
                <view class="cu-tag margin-right-xs bg-blue light radius">
                    å½“å½’
                </view>
                <view class="cu-tag bg-purple light radius">
                    äººå‚
                </view>
                <view class="cu-tag bg-brown light radius">
                    é¾™é¡»è‰
                </view>
            </view>
        </view> -->
        <view class="card-box dynamic shadow">
            <view class="title-box margin-bottom-sm">
                <view class="left justify-between">
@@ -165,76 +99,82 @@
        <view class="card-box dynamic shadow">
        <view class="card-box dynamic shadow" >
            <view class="title-box">
                <view class="left">
                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                    <view class="title">明细</view>
                </view>
            </view>
            <view class="data-item" v-for="(item, index) in herbList" :key="item.id" style="padding: 0 10rpx;">
                <view class="flex justify-between text-lg align-center padding-tb-xs">
                    <view class="flex">
                        <text>中草药</text>
                        <text>丨</text>
                        <text @click="showLongName(showValue(item.herbName))"
                            class="text-cut flex-twice">{{showValue(item.herbName)}}</text>
                        <!-- <view class="margin-lr-sm">
            <u-skeleton rows="20" :loading="loading" :title="false">
                <view class="data-item" v-for="(item, index) in herbList" :key="item.id" style="padding: 0 10rpx;">
                    <view class="flex justify-between text-lg align-center padding-tb-xs">
                        <view class="flex">
                            <text>中草药</text>
                            <text>丨</text>
                            <text @click="showLongName(showValue(item.herbName))"
                                class="text-cut flex-twice">{{showValue(item.herbName)}}</text>
                            <!-- <view class="margin-lr-sm">
                            <u-tag text="报警一次" plain size="mini" type="warning"></u-tag>
                        </view> -->
                        <view class="margin-lr-sm">
                            <u-tag text="正常" plain size="mini" type="success"></u-tag>
                            <view class="margin-lr-sm">
                                <u-tag text="正常" plain size="mini" type="success"></u-tag>
                            </view>
                        </view>
                        <view style="width:160rpx;" class="flex align-center text-xs ">
                            <text>进度:</text>
                            <view style="width: 100rpx;">
                                <u-line-progress :percentage="calcProgress(item.dryTime,item.et)"
                                    height="10"></u-line-progress>
                            </view>
                        </view>
                    </view>
                    <view style="width:160rpx;" class="flex align-center text-xs ">
                        <text>进度:</text>
                        <view style="width: 100rpx;">
                            <u-line-progress :percentage="calcProgress(item.dryTime,item.et)"
                                height="10"></u-line-progress>
                    <view style="display: flex;flex-direction: row;height: 120rpx;">
                        <view class="image">
                            <image :src="dataList[0].goodsUrl" mode="aspectFill"></image>
                        </view>
                        <view class="flex flex-direction margin-left-xl margin-left-sm justify-center">
                            <view>
                                <text class="text-xxl text-green">{{showValue(item.target)}}%</text>
                                <text
                                    class="text-xs margin-top-xs text-grey margin-left-xs text-line-through">{{showValue(item.initial)}}%</text>
                            </view>
                            <text class="text-xs margin-top-xs text-grey">含水率</text>
                        </view>
                        <view class="flex flex-direction margin-left-xl margin-left-10p justify-center">
                            <view>
                                <text class="text-xxl">{{showValue(item.yield )}}kg</text>
                                <text
                                    class="text-xs margin-top-xs text-grey margin-left-xs text-line-through">{{showValue(item.originWeight)}}kg</text>
                            </view>
                            <view class="text-xs margin-top-xs text-grey">
                                <text>{{showValue(item.feed)}}筐</text>
                                <text style="margin: 0 10rpx;"> l </text>
                                <text>{{showValue(item.windTemp)}}℃</text>
                                <text style="margin: 0 10rpx;"> l </text>
                                <text>{{showValue(item.dryTime)}}min</text>
                            </view>
                        </view>
                    </view>
                    <u-gap height="10"></u-gap>
                    <view class="text-grey text-sm flex  justify-between">
                        <text>{{showValue(item.code)}}</text>
                        <text>{{showValue(item.orderTime)}}</text>
                    </view>
                    <u-line color="#f1f1f1" margin="15rpx 0 15rpx 0"></u-line>
                </view>
                <view style="display: flex;flex-direction: row;height: 120rpx;">
                    <view class="image">
                        <image :src="dataList[0].goodsUrl" mode="aspectFill"></image>
                    </view>
                    <view class="flex flex-direction margin-left-xl margin-left-sm justify-center">
                        <view>
                            <text class="text-xxl text-green">{{showValue(item.target)}}%</text>
                            <text
                                class="text-xs margin-top-xs text-grey margin-left-xs text-line-through">{{showValue(item.initial)}}%</text>
                        </view>
                        <text class="text-xs margin-top-xs text-grey">含水率</text>
                    </view>
                    <view class="flex flex-direction margin-left-xl margin-left-10p justify-center">
                        <view>
                            <text class="text-xxl">{{showValue(item.yield )}}kg</text>
                            <text
                                class="text-xs margin-top-xs text-grey margin-left-xs text-line-through">{{showValue(item.originWeight)}}kg</text>
                        </view>
                        <view class="text-xs margin-top-xs text-grey">
                            <text>{{showValue(item.feed)}}筐</text>
                            <text style="margin: 0 10rpx;"> l </text>
                            <text>{{showValue(item.windTemp)}}℃</text>
                            <text style="margin: 0 10rpx;"> l </text>
                            <text>{{showValue(item.dryTime)}}min</text>
                        </view>
                    </view>
                </view>
                <u-gap height="10"></u-gap>
                <view class="text-grey text-sm flex  justify-between">
                    <text>{{showValue(item.code)}}</text>
                    <text>{{showValue(item.orderTime)}}</text>
                </view>
                <u-line color="#f1f1f1" margin="15rpx 0 15rpx 0"></u-line>
            </view>
            </u-skeleton>
        </view>
        <mqtt-view ref="mqttView"></mqtt-view>
        <!-- 
        <view class="page-box" v-show="false">
            <view class="order" v-for="(res, index) in dataList" :key="res.id">
@@ -278,15 +218,15 @@
                </view>
            </view>
        </view> -->
    </view>
</template>
<script>
    import get from 'lodash.get'
    export default {
        data() {
            return {
                list: [{
@@ -302,6 +242,7 @@
                }],
                StatusBar: this.StatusBar,
                CustomBar: this.CustomBar,
                loading: true,
                percent: 50,
                activeColor: '#0081ff',
                monthList: [],
@@ -357,33 +298,44 @@
            }
        },
        onShow() {
            console.info('onShow')
            uni.showTabBarRedDot({
                index: 2 // æ˜¾ç¤ºç¬¬2个tabbar项(索引从0开始)的红点
            });
        },
        mounted() {
        },
        onReady() {
            //接收报警信息,数据预处理(未打开tab无法接收数据)
            uni.$on(this.$constant.MQTT_TOPIC_MESSAGE, (data) => {
                let json = JSON.parse(data);
                if (json == null || json.data == null || json.topic == null) {
                    return false
                }
                //在报警页面时不显示红点
                if (getApp().globalData.selectTab === 2) {
                    uni.hideTabBarRedDot({
                        index: 2
                    });
                    return false
                }
                let wdata = json.data
                let topic = json.topic
                switch (topic) {
                    //topic å®žæ—¶æŠ¥è­¦
                    case this.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT.replace('%s', this.tenantId):
                        if (wdata.length > 0) {
                            uni.showTabBarRedDot({
                                index: 2 // æ˜¾ç¤ºç¬¬2个tabbar项(索引从0开始)的红点
                            });
                        }
                        break
                }
            });
            this.$refs.mqttView.initMqtt()
            // uni.getSystemInfo({
            //     success: (res) => {
            //         let deviceId = res.deviceId
            //         if (!deviceId) {
            //             deviceId = 'mobile-' + this.tenantId + '-' + Date.parse(new Date())
            //         }
            //         uni.setStorageSync(this.$constant.DEVICE_ID, 'mobile-' + this.tenantId + '-' + res.deviceId);
            //     }
            // })
            // this.startConnect();
            let month = uni.$u.timeFormat(new Date(), 'yyyy-mm')
            this.getMonth(month)
@@ -393,16 +345,17 @@
        },
        onLoad() {
        onLoad() {},
        onTabItemTap: function(e) {
            getApp().globalData.selectTab = e.index
        },
        methods: {
            /* è¿žæŽ¥MQTT */
            async startConnect() {
                var _this = this
                const account = uni.getStorageSync('account');
                const deviceid = uni.getStorageSync(this.$constant.DEVICE_ID);
                if (!account) {
@@ -415,7 +368,7 @@
                    // #ifdef APP-PLUS
                    url: 'wx://' + this.$api.mqttBaseUrl + ':8083/mqtt',
                    // #endif
                    clientId: deviceid,
                    clientId: this.deviceId,
                    username: account.username,
                    password: account.password
                }
@@ -429,10 +382,18 @@
                }).then(res => {
                    console.error(res)
                })
                //订阅设备故障广播(广播不在乎客户端id,发送给租户下所有在线的设备)
                //订阅设备实时故障广播(广播不在乎客户端id,发送给租户下所有在线的设备)
                this.$mqttTool.subscribe({
                    topic: this.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT.replace('%s', this.tenantId),
                    qos: 0
                }).then(res => {
                    console.error(res)
                })
                //订阅设备实时故障(主动请求只发给请求设备)
                this.$mqttTool.subscribe({
                    topic: this.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT.replace('%s', this.deviceId),
                    qos: 0
                }).then(res => {
                    console.error(res)
@@ -477,17 +438,14 @@
            },
            dayChange(dayInfo) { // ç‚¹å‡»æ—¥æœŸ
                this.model.date = dayInfo.date
                console.log("点击日期", JSON.parse(JSON.stringify(dayInfo)));
                this.getOrderList(dayInfo.date, dayInfo.date);
            },
            monthChange(monthInfo) { // åˆ‡æ¢æœˆä»½
                console.log("切换月份", JSON.parse(JSON.stringify(monthInfo)));
                let m = monthInfo.month
                if (m < 10) {
                    m = '0' + m
                }
                let month = monthInfo.year + '-' + m
                console.info(month)
                this.getMonth(month)
            },
            showValue(value) {
@@ -495,10 +453,6 @@
                    return "--"
                }
                return value
            },
            show(e) {
                console.info(this.CustomBar)
            },
            goCharts(e) {
                uni.navigateTo({
@@ -510,7 +464,11 @@
                    "orderTime_begin": date1 + " 00:00:00",
                    "orderTime_end": date2 + " 23:59:59",
                }
                this.loading = true;
                this.$api.getOrderList(params).then((res) => {
                    setTimeout(()=>{
                        this.loading = false
                    },200)
                    if (res.success) {
                        this.herbList = res.result.records
@@ -533,8 +491,6 @@
                        this.model.batch = this.herbList.length
                        this.model.weight = weight
                        this.model.dryTime = dryTime
                        console.info(this.model)
                    }
                })
            },
@@ -597,20 +553,29 @@
        },
        computed:{
            tenantId(){
        computed: {
            tenantId() {
                const userinfo = uni.getStorageSync('userinfo');
                const tenantid = userinfo.loginTenantId
                return   tenantid;
            }
                return tenantid;
            },
            deviceId() {
                return uni.getStorageSync(this.$constant.DEVICE_ID);
            },
        }
    }
</script>
<style lang="scss" scoped>
    .app {}
    .app {
        width: 100%;
        max-height: 100vh;
        overflow: hidden;
    }
    .custom-bar {
        background-image: linear-gradient(to right, #0381FF, #0381FF);
pages/tabBar/me.vue
@@ -32,7 +32,7 @@
                <view class='text-center'>
                    <view class="cu-avatar2 round xl margin-right-sm shadow-blur-lg bg-img open-data"
                        style="overflow: hidden;">
                        <image  style="width: 100%; height: 100%;" src="../../static/me/icon/zhuti.png"></image>
                        <image style="width: 100%; height: 100%;" src="../../static/me/icon/zhuti.png"></image>
                    </view>
                    <view class="padding text-blue text-xl text-bold">
                        ä½ å¥½
@@ -95,7 +95,7 @@
                    </view>
                </view>
                <view class="cu-item">
                    <view class='content'>
                        <image src='../../static/me/icon/dengta.png' class='png' mode='aspectFit'></image>
@@ -123,28 +123,28 @@
                        <image src='../../static/me/icon/jiankong.png' class='png' mode='aspectFit'></image>
                        <text class='text-lg margin-sm'>实时监控</text>
                    </button>
                </view>
                <view class="cu-item">
                    <button class='content cu-btn' @click="itemClick('control')">
                        <image src='../../static/me/icon/diannao.png' class='png' mode='aspectFit'></image>
                        <text class='text-lg margin-sm'>远程控制</text>
                    </button>
                </view> -->
                </view>
                
                <view class="cu-item">
                    <button class='content cu-btn' @click="itemClick('control')">
                        <image src='../../static/me/icon/diannao.png' class='png' mode='aspectFit'></image>
                        <text class='text-lg margin-sm'>远程控制</text>
                    </button>
                </view> -->
                <view class="cu-item">
                    <button class='content cu-btn' @click="itemClick('analy')">
                        <image src='../../static/me/icon/diannao.png' class='png' mode='aspectFit'></image>
                        <text class='text-lg margin-sm'>分析</text>
                    </button>
                </view>
                <view class="cu-item">
                    <button class='content cu-btn' @click="exit">
                        <image src='../../static/me/icon/exit.png' class='png' mode='aspectFit'></image>
                        <text class='text-lg margin-sm'>退出登录</text>
                    </button>
                </view>
                <view class="cu-item">
                    <button class='content cu-btn' @click="exit">
                        <image src='../../static/me/icon/exit.png' class='png' mode='aspectFit'></image>
                        <text class='text-lg margin-sm'>退出登录</text>
                    </button>
                </view>
            </view>
@@ -166,8 +166,8 @@
                // CustomBar: this.CustomBar,
                spaceShow: true,
                modalName: null,
                picName: '流星之夜',
                userinfo:{},
                picName: '流星之夜',
                userinfo: {},
                pic: [{
                    link: 'data:image/jpeg;base64,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gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAj5YYJggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ5wAEIigAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFogAAAAAAAAAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxywBAgAAAAAAAAAAAFrAAAAA4QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABW4IKwAAAAAAAAAAQ9CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8IAAAQwAAAAAAAAygAHwAAAAAAAAAAAAADBTTTTTTTTTTTTTTTTTTTTTTSSAAAAAAAAAgAAABPQAAAAAAAAMggoAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAJQAAAAAAAAcMccYggAAAAAAAABDDDDDDDDDDDDDDDDDJSDDDDDDDDDDDDDDDDCDDDDDCLTDDDDDDDDDDDDAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAEKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAIQQQQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAUYAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAEMMOQQQQQQQQQQQQQQMMMMMMMMMMMMMMMMMMMMMMMMMMMIIQQQQQQQQQQYQAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAABAAJQQQRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQQQQQQQQQQQQQQQQQWTQgAAAAAAAnwgAAAAAKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAGeQAAAAAAA8wgAAABBYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEKQQQQQQQQQQQQQQQTAAEYwAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAwQAAAAAAAADDDDDORDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDGTAAwQAAAAAAAFAAAAAAAAAAAAAAAAJQAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAJQAAAAAAAAAAAAAAAAAAAAAAAAMMMAeUcMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNQAAAAAAAAAAAAAJJfygAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAJE5ZQSwQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQAAAAAAAAAAAAAAIQANJ6CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8QAAAFZPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGwgAAEsQFygAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2wAABIAAAESgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIwAAACgAAAAiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0jDDDCbDTTTx2iCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBHHaCgAgAAAAE0csssoQgggkQQgsgAAAAAAAAAAAAAAAAAAADQgggggggggggggggggjjvPvgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEHDjjnAAAAAAAAAAAAAAAABKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/EACcRAAMAAQIGAQQDAAAAAAAAAAABESExUBAgMEFRYWBAgJGhcMHw/9oACAEDAQE/EPtcc3owQYt+BURQns7wNmqHg0049+SbcRPJj28h+aGzR7elcY1H1YrnRE9sT+xqi4M1aDbRadXLCSrlaLUpi2pO0QheKMl09EQks5Hlgam4qycrSajE5LpRFOKKXccD5ilHSwCZ7D2DNW4ptOoST0HoPQNo2br++qEIQhCEIQhCfXQhCEIQhCE+KUpS7BSlKUpSlKUpSlKUvB+p3GyVa6Phc71RIavvX2sI8o35uPxP76ObDO9K3n+CaP8Auw/gF4sar+F7smE+BJNuIknwNGEU8GqBVj4AlG7xaMpuIak1N9k5SLMalS2+C1lnjTkTuxtvgnGJN6SrG6+V4xyLKm9PCnKsK8zyrvDNYNnY9R6j1Ce5Q62RkZGTgsMbJke1KXtCaJPwJqkYxzargmenNWit8taK8leSvJXkrZWivJXkr8/SpNlFFFFFFFFFFFFFFFFFFFCXX6Pf+me/9Pj3eXakiIIIIIIIIIIIIIIIIIIIIIIII+gZIgggj7Av/8QALBEAAwAABAUCBgIDAAAAAAAAAAERITFBYRAgMFBRgaFAYHBx0fCAkLHB4f/aAAgBAgEBPxD+LngTReR3wh6/5plu8rx+V8hO1LKHjqZsV4W/LxG/NIXXCxjR9+Y0QqGz0IGi5GOswexO3oQmKmJdVFb50x2sk24hBUMpTg1HeAtVeHVeZNW5c1QU8yLtWQMby4Z5ifG6eWBdgrFmljTTj7ip/FyoSIKTT6KERNHHeXcUR6+ZK3ukqjH6kj9SQ6rXuLGqE1ozZXv+TZXv+TZXv+RvWg9rX+qhMydGsqr471jMDIr0cfD/AKd6q4fQiTa+i+f5IZPT5CYySErQwvUamD+QXRqaIcog1eDyKvm4Tv1nXkhzV8asci35gxD9Y004++JUafb93/zl1A6DQvloeU3vlnU4/wBxFtFksuRDdZIY1fCylmGXerIWYcixGinkwr9O9PvdeX748ubCn17xE4yGx1pm+N8b4ceKiLawooojRGy08VgxpCPtTm1xsVsPI1QhEnB9zmTayGzzZWVlZWJlkzeN43jeGzNiZZM3jfN74VMyyyyyyyyyyyyyyyyyy+NNke6Nv3Rt+6JF4KTeOj61KUpSlKUpSlKUpSlKUvPFRkkkkkkkkkkkkkkkkkkkkkkkjWddjpZkkkj+Cl9cP//EAC4QAAIBAgQEBgMBAAMBAAAAAAABEWHwITFxkUFR0fFAUGCBocEQIDCxcJDhoP/aAAgBAQABPxD/AOVdpUHjieRn9MvrGmmS6OzT18m2x2gzhDFpz/4Dwwk9ZcH7DhpSU5G82vFL8zdYGFh6rxRLU/U4OCP/AICWWl65Jy/hRttmltuW/wAJNkklk0mccF/gOKS8EavoE/FnnYkJo257mxum4/2AUpoQliU1zT9fIzVLc2XQ/DEJm2QT2+38xLSglDG5t/pnQvTcOqHde7bHkug7ES2LU9evOFEsU4FQbty865UaTGKLPi1EcKUC6t8URp4ps5/ZMSwKGP1DJpq88nxh6hQXRmBuPbMSETJLNPLxTloCE24Z8BlVlI9LpxCG23mIgrQhKa0ENCQoSShJfs0moaTRg/8AN30CBVGHlyib1ITOUjWD9PubtyRLySNNrU2UNe3ilJJIObMlsDcy/m7y1NKYsUKvJcV4yDWObR6dSap0rZy8YlEGIcuEwh4tijK2q7o0JNUaVJisYl+JTgpFj7L+jzTlBg0CaEhgc1/i9LLe9KZC1ODW1gpicovAV0LES+zIgWjKT+iwydMaUwjFDExTmHxU+KYdmtCpKm8RpKRrBprP+aHLikId8u9bakpnDDFDWObT6VYmT9mYzjnV1H/ubMFipOBOpvN6v8UaC4VXJ1RxBtBGvp8odREk255Gq5Px05dByaGKQOcMgKl3/ETTTTTTTTK2GE5ssnWPlQx9EQvBelGtmsZVOsMBsMPGI+EZFLlxh1a2QhaFCX6NGMsGhUCee+T+UJpM2K08NDRpojOJqSzrJ+L2whwxKPCOea1ExGZ7htKHxGWadH6khAqnlzLNNYGoWGOjYCja3h/BmDQCDhKE25j9mxlWwxsqSuDaERA8BN7D4MMzeBx3Xmq+DUlHIYbbySWLYlEkMXPEbzAhE5Qxk/OVqlbYMyKLYQucISjFr6ianDqfkzht2NlxC5P+Q0mmmsIEG8PBpzfupeqENwQSvBO5iqUxRh8203+qyAwqYuUPBilLiw7iXAhYyhupcSRK9Qo/lr2iW+EOWwwfms/n+aIZnvICS9BU85hPgoJS1mNE94/ZIBClLaND2aMMsm0J+NUtwkJ2sWkQGoahqGoahqGoahqGoahqGoahqGoahqGoahqGoahqGoahqGoahqGoahqGoahqGoahqGoahqGoahqGoNksEEbQngkNmD3FNYomZfYIk83wT+Ge6y/iylIUtt4JDlAwrJYQcLJoW5rIoWnglG+Aj54qjHRAmTY3Jr9PXr02KIKoDy1HV6RmaXgPxyVgYvzJLFx4eDwpScreLzTFCp6Jyxy9mL/1h3wd8HfB3wd8HfA//WEG6OC9mH22OhOzEy+Iz4+b/G8zzNfSjJrTDzJtKT4Ibl+YwyNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNCNB0ygTVi4KIXJCaa8ha3i0URRFEURRFEURRFEURRD54zDgkRoRoRoRoRoRoRoRoRoRoRoRoRoRoRoRoRoRoRoRoRoRoRoRoRoRoRp4d/Xk7nzw4oTTSaya8exOD1foB/XlDN+7x7bbbr6Af15QjWp+PaaZV9AP68nc6hYcxJJQso8e54Gq9MNWcguSKAoCgKAoCgKAoCgKAoCgFyWxQggggggggggggggggggggggggggggggggbnikygKAoCgKAoCgKAoCgKAoCgKAoCgKAoCgKAoBvWSGPTn5+lMebEko9Dqm6p5/8r0Q/P02nNRUy9DrgnFr0AmaWYm5CySySySySySySyTCMn5y2kpbQ1ThJsskskblCSQ22ltz5Q/ryJrE3ivOGuJ4Ly1/XkWdp5xn6+Wv68iVpPzgrWvHy3NL9km3gioKgqCoKgqCoKgqCoKgqCoKgqCoKgqCoKgqCoKgqCoKgc3OBChJJLDzhcTmjGp5SVBUFQVBUFQVBUFQVBUFQVBUFQVBUFQVBUFQNNZprxaOIJWEvVbSahomprJ+KRSdPVqS3imUlT1a0N4pmhBKyn6rbSS2TVGS8Lkl+ybXEriuK4riuK4rhKecillZ8VJBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAlOMw+aK4riuK4riuK4riuK4riuK4riuK4riuK4riuK4riuK4riuK4riuK4riuK4bbzbf4UO7TEcExE7JTPGU8zIQ3gSlt6eCf1/dmlq489dp6PJ3GqiYGzTMUzM57XIabIImzScUsMBzxREw1j4Bu4nBnVZQzMTwzusv++Xr57n+TrW07kLWLZhEkbwhJufgjOpIUyfJ9fAyT9phsRyIrmswkKef92uUYLz1pNlzQ02JrydTzbUCVanD5Gly8CpSbMNNmjGRsw02S/ox4SE3FBKcttiSShLz5K4oaTgxbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbA2SwTYRtCfs8WGA88nPjkpYqJeokxceH7Z72D8dk6+pHg2v6572D8cnFBiaaTn1E2lJ8ENy/1Rj4BzzE4fPj2LDxQueiiKIoiiKIoiiKIoiiKIoiiKIoiiKIoiiE08nJubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5uNpLFwURRFEURRFEURRFEURRFEURRFEURRFEURRFEUQ1rNDMOS8S/ryFm0sLU9/M0zceA225b8xf15E7gp5m7gqeZP68iVyU8zVwdPMn9eQpmhIWpb+Zpi48BG0J5wrgSQk5qWUQ+TDtmdYtCxhIXonOh5tx9jlFEURRFEURRFEURRFEURRFEURRFEURRFEURRCSWSNzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3NxpPNSURRFEURRFEURRFEURRFEURRFEURRFEURRFEURRFEURRDYsFDGOT8YiU+b/AAh48wxJFi9htZlu5IfAfCj4Xqekz5PxnxvwyVOSzPPzCK6wEJJcEKEy8Fi+aSqZgo+x7C5w9TZ3jFw2eguFGSUvhhD0zY47HPHksvyx4/zSeU1BXpIlxTWHqVbSR6+MbSUzEcRYElosMj6kKy0ViuS4lWCCFNk01Ka4mQz6SS1kji2L+DRMm8eAdGOXqJMzfcotktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktktkYySQ34x92mlksTHHhORhMmwaUURJusZVAhqJNccBOA+5LQWasBMbnyYx5ubmQQwrWFyH+5ozjaDGfDjYziRYTlMZ+3H8xS/lwl7mfrtnbUhpZt8FrOA6g4Cz5TovygRAoITDWT1/LYAXiLFTniSnw4v8c55uQkQxNNSmnKa/E5ZspEizFyzZLGmmuM49jJ+u8RJxvLp/wCv4JxJQjWDXFD6bf8At255D3rb9noTGTOqC5uzCSS6BZ/aIZg3MSeafFb4euXiKXQ/SEuqOhKqzhLmX/FNVfbOCULFOjUpkGIZeJTfFievkiU4cP5Q3ejUU2XIwNif8FRFFhNkMEvW6Ab5rJJLEbUwuYU8Pd5kY6MKybn/ABUHpgtAHNiPwxUqlFhCwekNxGC0CeW4UMhhi5pcgt2xY+WoE8Cz+Q4rFVPHwPW7QDSjpzGKoS0zDq0pzceT/FDEZhLZvyL3EfKCvFvyfJZDbbbbbbeL/R5ppmn+p5AvusSyaawfrVE9xYST4CXYTObMWx0WPkuC4L+MyiXNDyy/qX7NJqGsB5A01h5Nn969aNsMSJS23CSGhaKeVmeTUthNppNJvFT/ABcR4CsIuLm1ghXdchkklh+6GRKRzRmGYvjj1Zr7XrNz9PidXc2+BJJCESwS4L+KJMJbhJtKW8lvgPKTUZJ4Lov4o11xobDlDoJpoxNNSmn6xSo3GF5uyPce+oBcHwSolh/LADP04a0/1/Ja94klNNYjTrbQCnnk9Y4DcpyPmVkX8sqXon/gmIvp4aXm3H3PH+bk1Jbi4xeiYCjmiw3NWZo/V74clQueCqKwIQfxS0lhklxJQoLPcL3zf1yLA+R8asjr6YdjeCErhOp2gYylD3hqVz8CmMbUpLTa3Jc2QDbolOLoMkxg84u66nbJ2ydsnbJ2ydsnbJ2ydsiBYUupf7MVLEhJI60vstL7LS+y0vstL7LS+y0vstL7FhZilNpTX4gqOaCe0lpfZCjofNrxkRc8i6RDgvTOoqvxwkltvcZU3i0U9pFV+OEktt7+j3VJEsW2IMmJzbgYcMTm0i6oj4kyXkv1aNNNDoP1Z9sOnNV1/mpJNJGlLVovH6O1DtQ7UO1DtQ7UO1DtQ7UO1BoNm2Nt85gsj6LI+iyPosj6LI+iyPosj6LI+iyPoxF5MBNkjtQzURODDDFqSyPosj6LI+htMYLaayxjAzgpOMMMWpHthgtprJzGD9HvcrxoS5vi/wAvU5c0LQWFVP2XByP9UKaymBoZcfb0k9fuMSFjJqipvXQqb10Km9dCpvXQqb10Km9dCpvXQqb10Km9dCpvXQqb10Km9dCpvXQqb10Km9dCpvXQqb10Km9dCpvXQqb10Km9dCpvXQqb10Km9dCpvXQqb10Km9dCpvXQqb10Km9dCpvXQqb10Km9dCpvXQqb10Km9dBu3Lv9y3vLe8t7xLh20huMyHNsQ5tiHNsQ5tiHNsNwzcD5hvi5/ozuJ0+Cy6ekkJPFrDzF0nvlHjUyUmLBCztLO0s7SztLO0s7SztLO0s7SztLO0s7SztLO0s7SztLO0s7SztLO0s7SztLO0s7SztLO0s7SztLO0s7SztLO0s7SztLO0s7SztLO0s7SztLO0s7SztGkkS5qX0IbkxYP+ibQxpriJKUpihtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1KG19ShtfUobX1GeLDksvGu2c2D24CGLhLZ8pYtIIlwWP8FZpprBxinz8jdvhk01rx9NNn0mWKfESnKdNPBw6iUkhZFl7F5eheXoXl6F5eheXoXl6F5eg4rwQ3BoLCdSwnUsJ1LCdSwnUsJ1LCdSwnUsJ1LCdSwnUsJ1LCdSwnUsJ1LCdSwnUsJ1LCdSwnUsJ1LCdSwnUsJ1LCdSwnUsJ1LCdSwnUsJ1LCdSwnUsJ1GUTRObQllLmW+b/AO3H/9k=',
                    name: '春天'
@@ -190,7 +190,7 @@
                    title: 'theme',
                    name: '清爽夏日',
                    color: ''
                },  {
                }, {
                    title: 'theme',
                    name: '幽兰星空',
                    color: ''
@@ -200,12 +200,12 @@
                    color: ''
                }]
            }
        },
        onReady() {
             const userinfo =  uni.getStorageSync('userinfo');
            if(userinfo){
                this.userinfo  = userinfo
            }
        },
        onReady() {
            const userinfo = uni.getStorageSync('userinfo');
            if (userinfo) {
                this.userinfo = userinfo
            }
        },
        watch: {
            topBackGroupImageIndex(val) {
@@ -217,38 +217,45 @@
                }
            }
        },
        mounted() {
        mounted() {
        },
        methods: {
            itemClick(mode){
                if(mode == 'control'){
                    uni.navigateTo({
                        url:"/pages/me/control"
                    })
                }else if(mode == 'video'){
                    uni.navigateTo({
                        url:"/pages/me/video"
                    })
                }else if(mode == 'analy'){
                    uni.navigateTo({
                        url:"/pages/me/analy"
                    })
                }else if(mode == 'other'){
                    uni.navigateTo({
                        url:"/pages/tabBar/monitor"
                    })
                }
        onTabItemTap: function(e) {
            getApp().globalData.selectTab = e.index
        },
        methods: {
            itemClick(mode) {
                if (mode == 'control') {
                    uni.navigateTo({
                        url: "/pages/me/control"
                    })
                } else if (mode == 'video') {
                    uni.navigateTo({
                        url: "/pages/me/video"
                    })
                } else if (mode == 'analy') {
                    uni.navigateTo({
                        url: "/pages/me/analy"
                    })
                } else if (mode == 'other') {
                    uni.navigateTo({
                        url: "/pages/tabBar/monitor"
                    })
                }
            },
            exit() {
                this.show = true
            },
            confirm() {
                this.show = false
                uni.removeStorageSync('token');
                uni.removeStorageSync('userinfo');
                uni.reLaunch({
                    url: '/pages/login/login'
                this.show = false
                uni.removeStorageSync('token');
                uni.removeStorageSync('userinfo');
                uni.reLaunch({
                    url: '/pages/login/login'
                });
                this.$mqttTool.end().then(res => {
                    console.error(res)
                })
            },
            cancel() {
                this.show = false
@@ -679,4 +686,4 @@
            -moz-transform: scale(1) rotate(0) translate3d(-300rpx, 300rpx, 0);
        }
    }
</style>
</style>
pages/tabBar/warning.vue
@@ -1,353 +1,152 @@
<template>
    <view class="app">
        <cu-custom bgColor="bg-gradual-blue" :isBack="false" :isRight="true" @rightclick="rclick">
            <block slot="content">报警</block>
        <z-paging ref="paging" v-model="dataList" show-refresher-update-time @query="queryList">
            <!-- éœ€è¦å›ºå®šåœ¨é¡¶éƒ¨ä¸æ»šåŠ¨çš„view放在slot="top"的view中,如果需要跟着滚动,则不要设置slot="top" -->
            <template #top>
                <cu-custom bgColor="bg-gradual-blue" :isBack="false" :isRight="true" @rightclick="rclick">
                    <block slot="content">报警</block>
        </cu-custom>
        <u-notice-bar text="12:20:10 1车间GM001机台电机过流(电气)报警" mode="closable"></u-notice-bar>
                </cu-custom>
        <!--弹出层start-->
        <view style="width: 100%; position: absolute;z-index: 1000;top:300rpx;padding: 20rpx;" @touchmove.prevent >
            <dropdown-menu v-show="filterMenuShow"@reset='resetMenu'  @change='changeMenu' :list='equList' ref='dropdownMenuRef'/>
        </view>
        <view class="lock-page dropdown-mask" @touchmove.prevent @click.stop="1==1" v-if="filterMenuShow"></view>
        <!--弹出层end-->
        <view class="card-box dynamic shadow">
            <view class="title-box margin-bottom-sm">
                <view style="width: 100vw;" class="left justify-between">
                    <view class="flex align-center">
                        <uni-text class="cuIcon-titles text-blue"></uni-text>
                        <view class="title">总览</view>
                    </view>
                    <view>
                        <text class="text-gray text-sm">2024-10-01 è‡³ 2024-12-01</text>
                    </view>
                <!--弹出层start-->
                <view style="width: 100%; position: absolute;z-index: 1000;top:300rpx;padding: 20rpx;"
                    @touchmove.prevent>
                    <dropdown-menu v-show="filterMenuShow" @closeMenu="filterMenuShow = false " @reset='resetMenu' @change='changeMenu' :list='equList'
                        ref='dropdownMenuRef' />
                </view>
                <view class="lock-page dropdown-mask" @touchmove.prevent v-if="filterMenuShow"></view>
            </view>
            <view class="flex flex-direction padding-xs">
                <view class="flex">
                    <view class="flex-sub flex flex-direction">
                        <text class="text-df">报警总数</text>
                        <text class="text-bold text-sl margin-top-xs text-red margin-top-sm">10
                            <text class="text-gray text-sm margin-left-xs">次</text></text>
                <!--弹出层end-->
                    </view>
                    <view class="flex-twice flex flex-direction justify-between">
                        <view class="flex-sub flex">
                            <view class="flex flex-direction flex-sub">
                                <text class="text-gray text-xs">报警信息</text>
                                <text class="text-black">
                                    <text class="text-black">机械故障:</text>
                                    <text class="margin-lr-xs text-orange">0</text>
                                    <text class="text-gray text-xs">次</text>
                                </text>
                <view class="card-box dynamic shadow">
                    <view class="title-box margin-bottom-sm">
                        <view style="width: 100vw;" class="left justify-between">
                            <view class="flex align-center">
                                <uni-text class="cuIcon-titles text-blue"></uni-text>
                                <view class="title">总览</view>
                            </view>
                            <view class="flex flex-direction flex-sub">
                                <text class="text-white text-xs">电气故障</text>
                                <text class="text-black">
                                    <text class="text-black">电气故障:</text>
                                    <text class="margin-lr-xs text-orange">1</text>
                                    <text class="text-gray text-xs">次</text>
                                </text>
                            </view>
                        </view>
                        <view class="flex-sub flex margin-top-sm">
                            <view class="flex flex-direction flex-sub">
                                <text class="text-gray text-xs">报警信息</text>
                                <text>
                                    <text class="text-black">通信故障:</text>
                                    <text class="margin-lr-xs text-orange">4</text>
                                    <text class="text-gray text-xs">次</text>
                                </text>
                            </view>
                            <view class="flex flex-direction flex-sub">
                                <text class="text-white text-xs">其他故障</text>
                                <text class="text-cyan">
                                    <text class="text-black">其他故障:</text>
                                    <text class="margin-lr-xs text-orange">5</text>
                                    <text class="text-gray text-xs">次</text>
                                </text>
                            <view>
                                <text class="text-gray text-sm">{{curDate}}</text>
                            </view>
                        </view>
                    </view>
                    <view class="flex flex-direction padding-xs">
                        <view class="flex">
                            <view class="flex-sub flex flex-direction">
                                <text class="text-df">报警总数</text>
                                <text class="text-bold text-sl margin-top-xs text-red margin-top-sm">{{count}}
                                    <text class="text-gray text-sm margin-left-xs">次</text></text>
                </view>
                <view class="margin-top">
                    <u-scroll-list>
                            </view>
                            <view class="flex-twice flex flex-direction justify-between">
                                <view class="flex-sub flex">
                                    <view class="flex flex-direction flex-sub">
                                        <text class="text-gray text-xs">报警信息</text>
                    </u-scroll-list>
                                        <text class="text-black">故障次数:</text>
                                        <text class="margin-lr-xs text-red text-bold text-xl">{{faultCount}}</text>
                                        <text class="text-gray text-xs"></text>
                                    </view>
                                    <view class="flex flex-direction flex-sub">
                                        <text class="text-gray text-xs">机台信息</text>
                                        <text class="text-black">故障机台:</text>
                                        <text class="margin-lr-xs text-red">{{faultEqus}}</text>
                                        <text class="text-gray text-xs"></text>
                                    </view>
                                </view>
                                <view class="flex-sub flex">
                                    <view class="flex flex-direction flex-sub"  >
                                        <text class="text-white text-xs">报警信息</text>
                                        <text class="text-black">告警次数:</text>
                                        <text class="margin-lr-xs text-orange text-bold text-xl">{{alarmCount}}</text>
                                        <text class="text-gray text-xs"></text>
                                    </view>
                                    <view class="flex flex-direction flex-sub">
                                        <text class="text-white text-xs">机台信息</text>
                                        <text class="text-black">告警机台:</text>
                                        <text class="margin-lr-xs text-orange">{{alarmEqus}}</text>
                                        <text class="text-gray text-xs"></text>
                                    </view>
                                </view>
                            </view>
                        </view>
                        <view>
                            <u-scroll-list>
                            </u-scroll-list>
                        </view>
                    </view>
                </view>
            </view>
            </template>
        </view>
        <u-sticky>
            <view class="tab-box card-box top dynamic shadow">
                <z-tabs ref="tabs" :active-style="{'font-size':'30rpx','font-weight':'bold'}" :current="tabCurrent"
                    :list="tabList" @change="tabsChange">
                    <!-- è‡ªå®šä¹‰å³ä¾§æ’æ§½ -->
                    <!-- <template v-slot:right>
                        <u-icon name="setting"  ></u-icon>
                    </template> -->
                <u-tabs :list="tabList" @click="tabsChange"></u-tabs>
                <!-- <z-tabs ref="tabs" :active-style="{'font-size':'30rpx','font-weight':'bold'}" :current="tabCurrent"
                        :list="tabList" @change="tabsChange"> -->
                <!-- è‡ªå®šä¹‰å³ä¾§æ’æ§½ -->
                <!-- <template v-slot:right>
                            <u-icon name="setting"  ></u-icon>
                        </template> -->
                </z-tabs>
            </view>
        </u-sticky>
        <!-- å¯é€šè¿‡è®¾ç½®bar-animate-mode="worm"开启毛毛虫模式-->
        <swiper :style="'height:' + swiperItemHeight[tabCurrent] + 'rpx;'" :current="tabCurrent"
            @transition="swiperTransition" @animationfinish="swiperAnimationfinish">
            <swiper-item :key="0">
                <view :style="'height:' + swiperItemHeight[0] + 'rpx;'">
                    <view class="card-box center dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">设备 </view>
                                </view>
                                <view>
                                    <!-- <text class="text-blue text-sm">更多></text> -->
                                </view>
                            </view>
                        </view>
                        <view class="borderTop">
                            <image style="width: 100%;border-radius: 20rpx; " src="../../static/image/pic_gz.jpg"
                                mode="aspectFit"></image>
                        </view>
                    </view>
                    <view class="card-box center dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">实时报警 </view>
                                </view>
                                <view>
                                    <!-- <text class="text-blue text-sm">更多></text> -->
                                </view>
                            </view>
                        </view>
                        <view class="borderTop"  v-for="i in 10">
                            <view style="height: 180rpx;display: flex;align-items: center;">
                                <view>
                                    <image style="width: 80rpx;height: 80rpx;margin:20rpx;border-radius: 20rpx; "
                                        src="../../static/image/pic_gz.jpg"></image>
                                </view>
                                <view
                                    style="display: flex;flex: 1;flex-direction: column; justify-content: space-between;  ;height: 100%;padding: 20rpx;">
                                    <view class="text-bold">
                                        æ»šç­’电机过流报警
                                    </view>
                                    <view class="text-gray text-sm">
                                        è®¾å¤‡:1号干燥机
                                    </view>
                                    <view class="text-gray text-sm">
                                        æè¿°:同济堂1号滚筒电机过流报警,需要xxxx处理,东方航空的恢复很快的回复
                                    </view>
                                </view>
                                <view style="width: 100rpx;">{{i}}</view>
                            </view>
                        </view>
                    </view>
                    <view class="card-box bot dynamic shadow">
                        <view style="height: 20rpx;"></view>
                    </view>
                </view>
            </swiper-item>
            <swiper-item :key="1">
                <view :style="'height:' + swiperItemHeight[1] + 'rpx;'">
                    <view class="card-box center dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">最近报警</view>
                                </view>
                                <view>
                                    <text class="text-blue text-sm">更多></text>
                                </view>
                            </view>
                        </view>
            <!-- å¦‚果希望其他view跟着页面滚动,可以放在z-paging标签内 -->
            <real-warning ref="realWarining" v-if="tabCurrent === 0" @handleData="realHandleData"></real-warning>
            <his-warning ref="hisWarining" v-else @handleData="hisHandleData"></his-warning>
                        <view class="borderTop">
                            <view class="h-table">
                                <view class="h-tr h-tr-2 ">
                                    <view class="h-td">车间</view>
                                    <view class="h-td">机台</view>
                                    <view class="h-td">时间</view>
                                    <view class="h-td">类型</view>
                                    <view class="h-td">等级</view>
                                </view>
                                <view class="h-tr h-tr-2">
                                    <view class="h-td">001</view>
                                    <view class="h-td">GM001</view>
                                    <view class="h-td">12:00:00</view>
                                    <view class="h-td">机械</view>
                                    <view class="h-td">高</view>
                                </view>
                                <view class="h-tr h-tr-2">
                                    <view class="h-td">001</view>
                                    <view class="h-td">GM001</view>
                                    <view class="h-td">12:00:00</view>
                                    <view class="h-td">通信</view>
                                    <view class="h-td">中</view>
                                </view>
                                <view class="h-tr h-tr-2">
                                    <view class="h-td">001</view>
                                    <view class="h-td">GM001</view>
                                    <view class="h-td">12:00:00</view>
                                    <view class="h-td">电气</view>
                                    <view class="h-td">高</view>
                                </view>
                            </view>
                        </view>
                    </view>
                    <view class="card-box center dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">报警统计</view>
                                </view>
                                <view>
                                    <text class="text-gray text-sm"></text>
                                </view>
                            </view>
                        </view>
                        <view class="chartsMain">
                            <qiun-data-charts type="mount" :opts="opts" :animation="true" :chartData="Mount" />
                        </view>
                    </view>
                    <view class="card-box center dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">报警对比</view>
                                </view>
                                <view>
                                    <text class="text-gray text-sm"></text>
                                </view>
                            </view>
                        </view>
                        <view class="chartsMain">
                            <qiun-data-charts type="pie" :opts="optsPie" :chartData="PieA" />
                        </view>
                    </view>
                    <view class="card-box center dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">报警频率</view>
                                </view>
                                <view>
                                    <text class="text-gray text-sm"></text>
                                </view>
                            </view>
                        </view>
                        <view class="chartsMain">
                            <!-- <qiun-data-charts type="mount" :opts="{extra:{mount:{type:'mount',widthRatio:1.5}}}"
                    :chartData="Mount" /> -->
                            <qiun-data-charts type="word" :chartData="Word" />
                        </view>
                    </view>
                    <view class="card-box bot dynamic shadow">
                        <view class="title-box margin-bottom-sm">
                            <view style="width: 100vw;" class="left justify-between">
                                <view class="flex align-center">
                                    <uni-text class="cuIcon-titles text-blue"></uni-text>
                                    <view class="title">报警分析</view>
                                </view>
                                <view>
                                    <text class="text-gray text-sm"></text>
                                </view>
                            </view>
                        </view>
                        <view class="chartsMain">
                            <qiun-data-charts type="radar" :opts="optsRadar" :chartData="dataRadar" />
                        </view>
                    </view>
                </view>
            </swiper-item>
        </swiper>
        </z-paging>
    </view>
</template>
<script>
    import dropdownMenu from '@/components/drop-down-menu/index.vue'
    import realWarning from './components/realWarning.vue'
    import hisWarning from './components/hisWarning.vue'
    import dayjs from 'dayjs'
    export default {
        components: {
            dropdownMenu
            dropdownMenu,
            realWarning,
            hisWarning
        },
        data() {
            return {
                loading: true,
                // v-model绑定的这个变量不要在分页请求结束中自己赋值!!!
                dataList: [],
                curDate: dayjs().format('YYYY-MM-DD'),
                tabList: [{
                    name: '实时报警',
                    // badge: {
                    //     count: 6
                    // }
                }, {
                    name: '报警统计',
                }],
                equList: [{
                        code: 1,
                        name: '1#干燥机'
@@ -355,237 +154,207 @@
                    {
                        code: 2,
                        name: '2#干燥机'
                    }
                    }
                ],
                model: {},
                //筛选框
                filterMenuShow: false,
                //当前选中tab
                tabCurrent: 0,
                swiperItemHeight: [2800, 2140],
                tabList: [{
                    name: '实时报警',
                    badge: {
                        count: 6
                    }
                }, {
                    name: '报警统计',
                }],
                PieA: {
                    "series": [{
                        "data": [{
                            "name": "机械",
                            "value": 50,
                            "labelText": "机械:50次"
                        }, {
                            "name": "电气",
                            "value": 30,
                            "labelText": "电气:30次"
                        }, {
                            "name": "通信",
                            "value": 20,
                            "labelText": "通信:20次"
                        }, {
                            "name": "其他",
                            "value": 18,
                            "labelText": "其他:18次"
                        }]
                    }]
                },
                dataRadar: {
                    categories: ["机械", "电气", "通信", "其他"],
                    series: [{
                        name: "报警占比",
                        data: [99, 30, 18, 73]
                    }]
                },
                Mount: {
                    "series": [{
                        "data": [{
                            "name": "机械",
                            "value": 82
                        }, {
                            "name": "电气",
                            "value": 63
                        }, {
                            "name": "通信",
                            "value": 86
                        }, {
                            "name": "其他",
                            "value": 65
                        }]
                    }]
                },
                Word: {
                    "series": [{
                        "name": "风机过流",
                        "textSize": 25
                    }, {
                        "name": "后门未关闭",
                        "textSize": 20
                    }, {
                        "name": "风箱升异常",
                        "textSize": 20
                    }, {
                        "name": "电机过流",
                        "textSize": 20
                    }, {
                        "name": "加热位传感器",
                        "textSize": 20
                    }, {
                        "name": "加热时间异常",
                        "textSize": 20
                    }, {
                        "name": "滚筒升异常",
                        "textSize": 20
                    }, {
                        "name": "风箱不在加热位",
                        "textSize": 20
                    }, {
                        "name": "滚筒不在加热位",
                        "textSize": 10
                    }]
                },
                opts: {
                    color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4",
                        "#ea7ccc"
                    ],
                    padding: [15, 15, 0, 5],
                    enableScroll: false,
                    legend: {
                        show: true
                    },
                    xAxis: {
                        disableGrid: true
                    },
                    yAxis: {
                        gridColor: "rgba(230,230,230,0.6)",
                        data: [
                            {
                                min: 0
                            }
                        ]
                    },
                    extra: {
                        mount: {
                            type: "bar",
                            widthRatio: 0.3,
                            borderWidth: 0,
                            barBorderRadius: [
                                50,
                                50,
                                50,
                                50
                            ],
                            linearType: "custom"
                        }
                    }
                },
                optsPie: {
                    color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4",
                        "#ea7ccc"
                    ],
                    padding: [5, 5, 5, 5],
                    enableScroll: false,
                    extra: {
                        pie: {
                            activeOpacity: 0.5,
                            activeRadius: 10,
                            offsetAngle: 0,
                            labelWidth: 1,
                            border: true,
                            borderWidth: 3,
                            borderColor: "#FFFFFF",
                            linearType: "custom"
                        }
                    }
                },
                optsRadar: {
                    color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4",
                        "#ea7ccc"
                    ],
                    padding: [5, 5, 5, 5],
                    dataLabel: true,
                    dataPointShape: false,
                    enableScroll: false,
                    legend: {
                        show: false,
                        position: "right",
                        lineHeight: 25
                    },
                    extra: {
                        radar: {
                            gridType: "circle",
                            gridColor: "#CCCCCC",
                            gridCount: 3,
                            opacity: 1,
                            max: 100,
                            labelShow: true,
                            linearType: "custom",
                            border: false
                        }
                    }
                },
                //实时报警数据更新时间(一般10s)
                refreshTime: '',
                //报警总数
                count: 0,
                //告警总数
                alarmCount: 0,
                //故障总数
                faultCount: 0,
                //故障机台
                faultEqus: "",
                //告警机台
                alarmEqus: "",
            }
        },
        computed: {
        onTabItemTap: function(e) {
            getApp().globalData.selectTab = e.index
        },
        created() {
        },
        beforeDestroy() {
        onShow() {
            // //实时故障
            // if(this.tabCurrent === 0){
            //     this.$nextTick(()=>{
            //         this.$refs.realWarining.queryRealFaultData()
            //     })
            // }
            //实时故障
            if (this.tabCurrent === 0) {
                this.queryRealFaultData()
            }
        },
        onReady() {
        },
        methods: {
            mqttTopicMessage(){
            //处理消息逻辑
            switch(topic){
                case _this.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT.replace('%s', that.tenantId):
                   console.error(that.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT.replace('%s', that.tenantId))
                   console.error("收到广播")
                break
            }
            },
            changeMenu(selectData){
                console.info(selectData)
                this.filterMenuShow = false
            },
            resetMenu(selectData){
                console.info(selectData)
            },
            rclick() {
                this.filterMenuShow=!this.filterMenuShow
            queryList(pageNo, pageSize) {
                this.loading = true;
                // ç»„件加载时会自动触发此方法,因此默认页面加载时会自动触发,无需手动调用
                // è¿™é‡Œçš„pageNo和pageSize会自动计算好,直接传给服务器即可
                // æ¨¡æ‹Ÿè¯·æ±‚服务器获取分页数据,请替换成自己的网络请求
                const params = {
                    pageNo: pageNo,
                    pageSize: pageSize,
                }
                //
                this.$api.querySampleList(params).then((res) => {
                    //     // å°†è¯·æ±‚的结果数组传递给z-paging
                    //实时故障
                    if (this.tabCurrent === 0) {
                        this.queryRealFaultData()
                    }else if(this.tabCurrent === 1){
                        this.$refs.hisWarining.init();
                    }
                    this.$refs.paging.complete(res.result.records);
                    this.loading = false
                }).catch(res => {
                    // å¦‚果请求失败写this.$refs.paging.complete(false);
                    // æ³¨æ„ï¼Œæ¯æ¬¡éƒ½éœ€è¦åœ¨catch中写这句话很麻烦,z-paging提供了方案可以全局统一处理
                    // åœ¨åº•层的网络请求抛出异常时,写uni.$emit('z-paging-error-emit');即可
                    this.$refs.paging.complete(false);
                    this.loading = false
                })
            },
            //tabs通知swiper切换
            tabsChange(index) {
                this.tabCurrent = index;
            tabsChange(item) {
                this.tabCurrent = item.index;
                //重置故障统计数据
                this.count = 0;
                this.alarmCount = 0;
                this.faultCount = 0;
                this.faultEqus = "";
                this.alarmEqus = "";
                //实时故障
                if (this.tabCurrent === 0) {
                    // this.$nextTick(()=>{
                    //     this.$refs.realWarining.queryRealFaultData()
                    // })
                    this.queryRealFaultData()
                    this.curDate = dayjs().format('YYYY-MM-DD');
                }else if(this.tabCurrent === 1){
                    this.curDate = dayjs().subtract(3, 'day').format('YYYY-MM-DD') +" - "+ dayjs().format('YYYY-MM-DD');
                }
            },
            //swiper滑动中
            swiperTransition(e) {
                this.$refs.tabs.setDx(e.detail.dx);
            realHandleData(data) {
                if (!data) return false
                this.count = data.length
                this.faultCount = data.filter(item => item.faultType === 1).length;
                this.alarmCount = data.filter(item => item.faultType === 2).length;
                const faultEquList = [...new Set(
                    data
                    .filter(item => item.faultType === 1) // æ ¹æ®æ•…障类型过滤
                    .map(item => {
                        const match = item.equName.match(/(\d+#)/);
                        return match ? match[0] : null; // å¦‚果匹配成功,则返回匹配的编号;否则返回null
                    })
                    .filter(Boolean) // å†æ¬¡è¿‡æ»¤æŽ‰å¯èƒ½å­˜åœ¨çš„null值
                )];
                if (faultEquList) {
                    this.faultEqus = faultEquList.join(',');
                }
                const alarmEquList = [...new Set(
                    data
                    .filter(item => item.faultType === 2) // æ ¹æ®æ•…障类型过滤
                    .map(item => {
                        const match = item.equName.match(/(\d+#)/);
                        return match ? match[0] : null; // å¦‚果匹配成功,则返回匹配的编号;否则返回null
                    })
                    .filter(Boolean) // å†æ¬¡è¿‡æ»¤æŽ‰å¯èƒ½å­˜åœ¨çš„null值
                )];
                if (alarmEquList) {
                    this.alarmEqus = alarmEquList.join(',')
                }
            },
            hisHandleData(data){
               if (!data) return false
               this.count = data.reduce((sum, item) => sum + item.ecount, 0);
               this.faultCount = data.filter(item => item.faultType === 1).reduce((sum, item) => sum + item.ecount, 0);
               this.alarmCount = data.filter(item => item.faultType === 2).reduce((sum, item) => sum + item.ecount, 0);
            },
            //swiper滑动结束
            swiperAnimationfinish(e) {
                this.tabCurrent = e.detail.current;
                this.$refs.tabs.unlockDx();
            changeMenu(selectData) {
                console.info(selectData)
                this.filterMenuShow = false
            },
            resetMenu(selectData) {
                console.info(selectData)
            },
            rclick() {
                this.filterMenuShow = !this.filterMenuShow
                if (this.filterMenuShow) {
                    uni.pageScrollTo({
                        scrollTop: 0,
                        duration: 300 // æ»šåŠ¨åŠ¨ç”»æŒç»­æ—¶é—´ï¼Œå•ä½ä¸ºæ¯«ç§’
                    });
                }
            },
        }
            queryRealFaultData() {
                //发送数据
                const message = {
                    req: this.deviceId,
                    tenantId: this.tenantId,
                    timeStamp: new Date(),
                }
                let opts = {
                    topic: this.$constant.MOBILE_REQ_EQU_REAL_FAULT,
                    message: JSON.stringify(message),
                }
                this.$mqttTool.publish(opts).then(res => {
                    //console.error(res)
                })
            },
        },
        onReady() {
        },
        computed: {
            tenantId() {
                const userinfo = uni.getStorageSync('userinfo');
                const tenantid = userinfo.loginTenantId
                return tenantid + "";
            },
            deviceId() {
                return uni.getStorageSync(this.$constant.DEVICE_ID);
            },
        },
    }
</script>
@@ -593,8 +362,13 @@
    @import "components/table/helang-table";
    .app {
        position: relative;
        width: 100%;
        max-height: 100vh;
        overflow: hidden;
    }
    .card-box {
        margin: 20rpx;
@@ -741,6 +515,7 @@
    .dropdown-mask {
        background: rgba(0, 0, 0, 0.5);
    }
    .lock-page {
        height: 100vh;
        width: 100vw;
@@ -751,5 +526,6 @@
        bottom: 0;
        z-index: 998;
    }
    // å¼¹å‡ºå±‚背景遮罩end
</style>
readMe.txt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
注意事项
一、《云服务》和《局域网》不同模式下部分接口有所不同
1.
store/index.js
@@ -4,23 +4,27 @@
 
const store = new Vuex.Store({
  state: {
    // å®šä¹‰cid默认值
    cid: null
    // è®¾å¤‡å”¯ä¸€æ ‡è¯†ï¼ŒæŽ¨é€ä½¿ç”¨
    clientPushId: null,
  },
  mutations: {
    setCid(state, cid) {
          state.cid = cid;
    setClientPushId(state, clientPushId) {
          state.clientPushId = clientPushId;
    },
  },
  actions: {
    setCid({ commit }, cid) {
         commit('setCid', cid);
    setClientPushId({ commit }, clientPushId) {
         commit('setClientPushId', clientPushId);
       },
  },
  getters: {
   getCid(state) {
         return state.cid;
       }
   getClientPushId(state) {
         return state.clientPushId;
       },
  }
});