| | |
| | |
|
| | | <view class="card-box dynamic shadow">
|
| | | <view class="title-box margin-bottom-sm">
|
| | | <view style="width: 100vw;" class="left justify-between">
|
| | | <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 margin-right-lg">2024-07-14</text>
|
| | | <text class="text-gray text-sm">{{curDate}}</text>
|
| | | </view>
|
| | |
|
| | | </view>
|
| | |
|
| | | </view>
|
| | | <view class="flex flex-direction padding-xs">
|
| | |
|
| | |
|
| | | <!-- <view class="flex flex-direction padding-xs">
|
| | | <view class="flex">
|
| | | <view class="flex-sub flex flex-direction text-center">
|
| | | <text class="text-df">设备总数</text>
|
| | | <text class="text-bold text-sl margin-top-xs text-gray margin-top-sm">20
|
| | | <text class="text-bold text-sl margin-top-xs text-gray margin-top-sm">{{equCount}}
|
| | | <text class="text-gray text-sm margin-left-xs">台</text></text>
|
| | | </view> |
| | | |
| | | <view class="flex-sub flex flex-direction text-center"> |
| | | <text class="text-df">开机设备</text> |
| | | <text class="text-bold text-sl margin-top-xs text-gray margin-top-sm">10 |
| | | <text class="text-gray text-sm margin-left-xs">台</text></text> |
| | | </view> |
| | | |
| | | <view class="flex-sub flex flex-direction text-center"> |
| | | <text class="text-df">运行设备</text> |
| | | <text class="text-bold text-sl margin-top-xs text-cyan margin-top-sm">10 |
| | | <text class="text-gray text-sm margin-left-xs">台</text></text> |
| | | </view>
|
| | |
|
| | | <view class="flex-sub flex flex-direction text-center">
|
| | | <text class="text-df">开机设备</text>
|
| | | <text class="text-bold text-sl margin-top-xs text-gray margin-top-sm">{{onlineCount}}
|
| | | <text class="text-gray text-sm margin-left-xs">台</text></text>
|
| | | </view>
|
| | |
|
| | | <view class="flex-sub flex flex-direction text-center">
|
| | | <text class="text-df">运行设备</text>
|
| | | <text class="text-bold text-sl margin-top-xs text-cyan margin-top-sm">{{onlineCount}}
|
| | | <text class="text-gray text-sm margin-left-xs">台</text></text>
|
| | | </view>
|
| | | </view>
|
| | | <view class="margin-top">
|
| | |
| | |
|
| | | </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-green margin-top-sm">{{onlineCount}}
|
| | | <text class="text-gray text-sm margin-left-xs">台</text></text>
|
| | |
|
| | | </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>
|
| | | <text class="margin-lr-xs text-gray text-bold text-xl">{{equCount}}</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 text-bold text-xl">{{faultEquCount}}</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">0</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 text-bold text-xl">{{alarmEquCount}}</text>
|
| | | <text class="text-gray text-xs"></text>
|
| | |
|
| | | </view>
|
| | |
|
| | | </view>
|
| | |
|
| | | </view>
|
| | |
|
| | | </view>
|
| | |
|
| | |
|
| | | </view>
|
| | |
|
| | |
|
| | |
|
| | | </view>
|
| | |
|
| | |
|
| | | </template>
|
| | | |
| | | |
| | | |
| | | <u-skeleton |
| | | class=" " |
| | | 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)">
|
| | | <view class="title-box">
|
| | | <view class="left flex-sub">
|
| | | <uni-text class="cuIcon-titles text-blue"></uni-text>
|
| | | <view class="title text-cut">1#智能干燥机</view>
|
| | | <view class="flex title text-green text-sm">
|
| | | <u-tag size="mini" text="在线" type="success" plain plainFill></u-tag> |
| | | <u-tag class="margin-left-xs" size="mini" text="停机" type="error" plain plainFill></u-tag>
|
| | |
|
| | |
|
| | |
|
| | | <u-skeleton rows="20" :loading="loading" :title="false">
|
| | | <!-- 如果希望其他view跟着页面滚动,可以放在z-paging标签内 -->
|
| | | <view class="card-item-box dynamic shadow" v-for="(item,index) in dataList" :key="index"
|
| | | @click="itemClick(item)">
|
| | | <view class="title-box">
|
| | | <view class="left flex-sub">
|
| | | <uni-text class="cuIcon-titles text-blue"></uni-text>
|
| | | <view class="title text-cut">{{$lget(item,'name')}}</view>
|
| | | <view class="flex title text-green text-sm">
|
| | | <u-tag v-if="item.online" size="mini" text="在线" type="success" plain plainFill></u-tag>
|
| | | <u-tag v-else size="mini" text="离线" type="warning" plain plainFill></u-tag>
|
| | | <view style="width: 20rpx;height: 2rpx"></view>
|
| | | <template v-if="$lget(item,'realData.level')!= '--'">
|
| | | <u-tag size="mini" :text="$lget(item,'realData.level')" plain plainFill></u-tag>
|
| | | </template>
|
| | |
|
| | | </view>
|
| | | </view>
|
| | | <view class="right" style="min-width: 240rpx;">
|
| | | <!-- <u-badge :isDot="true" type="success"></u-badge> -->
|
| | | <!-- <view class="title text-gray text-sm">开机时间:</view> -->
|
| | | <view class="title text-gray text-sm">{{$lget(item,'realData.workorder')}}</view>
|
| | | </view>
|
| | | </view>
|
| | | <view class="right" style="min-width: 240rpx;">
|
| | | <!-- <u-badge :isDot="true" type="success"></u-badge> -->
|
| | | <view class="title text-gray text-sm">开机时间:</view>
|
| | | <view class="title text-gray text-sm">10:00:10</view>
|
| | | </view>
|
| | | </view>
|
| | |
|
| | | <view class="info-box">
|
| | | <view class="left flex-sub">
|
| | | <view class="title text-sm">烘干药材:</view>
|
| | | <view class="title text-sm">生地黄</view>
|
| | | <view class="info-box">
|
| | | <view class="left flex-sub">
|
| | | <view class="title text-sm">烘干药材:</view>
|
| | | <view class="title text-sm">{{$lget(item,'realData.name')}}</view>
|
| | | </view>
|
| | | <view class="right" style="min-width: 240rpx;">
|
| | | <view class="title text-sm">投料量:</view>
|
| | | <view class="title text-sm text-gray">{{$lget(item,'realData.weight1')}}筐</view>
|
| | | </view>
|
| | | </view>
|
| | | <view class="right" style="min-width: 240rpx;">
|
| | | <view class="title text-sm">烘干时间:</view>
|
| | | <view class="title text-sm">01:20:00</view>
|
| | | </view>
|
| | | </view>
|
| | |
|
| | | <view class="info-box">
|
| | | <view class="left flex-sub">
|
| | | <view class="title text-sm text-cut">报警信息:</view>
|
| | | <view class="flex title text-sm">
|
| | | <u-tag size="mini" text="温度传感器报警" type="warning" plain plainFill></u-tag>
|
| | | <u-tag size="mini" class="margin-left-xs" text="前门未关" type="warning" plain
|
| | | plainFill></u-tag>
|
| | | <u-tag size="mini" class="margin-left-xs" text="风箱低位报警" type="error" plain
|
| | | plainFill></u-tag>
|
| | | <view class="info-box">
|
| | | <view class="left flex-sub">
|
| | | <view class="title text-sm">已用时间:</view>
|
| | | <view class="title text-sm">{{$lget(item,'realData.time3')}}min</view>
|
| | | </view>
|
| | | <view class="right" style="min-width: 240rpx;">
|
| | | <view class="title text-sm">预测剩余:</view>
|
| | | <view class="title text-sm text-gray">{{$lget(item,'realData.ai_total_time')}}min</view>
|
| | | </view>
|
| | | </view>
|
| | |
|
| | | <view class="info-box">
|
| | | <view class="left flex-sub">
|
| | | <view class="title text-sm text-cut">报警信息:</view>
|
| | | <template v-if="item.errorData">
|
| | | <view class="title text-sm text-red">故障 {{item.errorData.length}}</view>
|
| | | </template>
|
| | |
|
| | | <template v-if="item.warnData">
|
| | | <view class="title text-sm text-yellow">告警 {{item.warnData.length}}</view>
|
| | | </template>
|
| | |
|
| | | </view>
|
| | | <view class="right" style="min-width: 240rpx;">
|
| | | <view class="title text-sm">更新时间:</view>
|
| | | <view class="title text-sm text-gray">{{ refreshTime}}</view>
|
| | | </view>
|
| | | </view>
|
| | |
|
| | | </view>
|
| | |
|
| | | </view>
|
| | |
|
| | | </u-skeleton> |
| | | </u-skeleton>
|
| | | </z-paging>
|
| | | </view>
|
| | | </template>
|
| | |
|
| | | <script>
|
| | | import dayjs from 'dayjs'
|
| | | export default {
|
| | | data() {
|
| | | return { |
| | | loading:true,
|
| | | return {
|
| | | loading: true,
|
| | | // v-model绑定的这个变量不要在分页请求结束中自己赋值!!!
|
| | | dataList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
| | | dataList: [],
|
| | | curDate: uni.$u.timeFormat(new Date(), 'yyyy-mm-dd'),
|
| | | isProcessing: false,
|
| | | // key->设备租户+code |
| | | dataMap: new Map(),
|
| | | // 更新时间
|
| | | refreshTime: '',
|
| | | alarmEquCount: 0,
|
| | | faultEquCount: 0,
|
| | | timer: null
|
| | |
|
| | | }
|
| | | },
|
| | | mounted() {
|
| | | this.startTimer()
|
| | | this.mqttData()
|
| | | },
|
| | | beforeDestroy() {
|
| | | this.stopTimer()
|
| | | uni.$off(this.$constant.MQTT_TOPIC_MESSAGE)
|
| | | },
|
| | | onTabItemTap: function(e) {
|
| | | getApp().globalData.selectTab = e.index
|
| | | },
|
| | | methods: {
|
| | | queryList(pageNo, pageSize) { |
| | | queryList(pageNo, pageSize) {
|
| | | this.loading = true;
|
| | | // 组件加载时会自动触发此方法,因此默认页面加载时会自动触发,无需手动调用
|
| | | // 这里的pageNo和pageSize会自动计算好,直接传给服务器即可
|
| | | // 模拟请求服务器获取分页数据,请替换成自己的网络请求
|
| | | // const params = {
|
| | | // pageNo: pageNo,
|
| | | // pageSize: pageSize,
|
| | | // }
|
| | | // this.$request.queryList(params).then(res => {
|
| | | // // 将请求的结果数组传递给z-paging
|
| | | this.$refs.paging.complete(this.dataList);
|
| | | // }).catch(res => {
|
| | | // // 如果请求失败写this.$refs.paging.complete(false);
|
| | | // // 注意,每次都需要在catch中写这句话很麻烦,z-paging提供了方案可以全局统一处理
|
| | | // // 在底层的网络请求抛出异常时,写uni.$emit('z-paging-error-emit');即可
|
| | | // this.$refs.paging.complete(false);
|
| | | // }) |
| | | setTimeout(() => { |
| | | this.loading = false |
| | | }, 1000)
|
| | | }, |
| | | itemClick(item){ |
| | | uni.navigateTo({ |
| | | url:"/pages/device/control" |
| | | }) |
| | | const params = {
|
| | | pageNo: pageNo,
|
| | | pageSize: pageSize,
|
| | | }
|
| | | this.$api.queryEquList(params).then((res) => {
|
| | | this.$refs.paging.complete(res.result.records);
|
| | | this.loading = false
|
| | | }).catch(res => {
|
| | | this.$refs.paging.complete(false);
|
| | | this.loading = false
|
| | | })
|
| | |
|
| | | },
|
| | | // 设备数据和mtqq实时数据合并
|
| | | mergeMqttRealData(mqttData) {
|
| | | const {
|
| | | tenantid,
|
| | | machineid
|
| | | } = mqttData;
|
| | | if (!tenantid || !machineid) return;
|
| | | const key = `${tenantid}_${machineid}`;
|
| | | const targetItem = this.dataList.find(item =>
|
| | | item.tenantId === tenantid && item.code === machineid
|
| | | );
|
| | | if (targetItem) {
|
| | | const updatedItem = {
|
| | | ...targetItem,
|
| | | realData: mqttData
|
| | | };
|
| | | this.$set(this.dataList, this.dataList.indexOf(targetItem), updatedItem);
|
| | |
|
| | | }
|
| | |
|
| | |
|
| | | },
|
| | | startTimer() {
|
| | | this.timer = setInterval(() => {
|
| | | this.queryRealFaultData();
|
| | | }, 3000);
|
| | | },
|
| | | stopTimer() {
|
| | | clearInterval(this.timer);
|
| | | },
|
| | | 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 => {
|
| | |
|
| | | })
|
| | | },
|
| | |
|
| | | itemClick(item) {
|
| | | uni.navigateTo({
|
| | | url: "/packageA/pages/device/control?code=" + item.code + "&name=" + item.name + "&clientId=" +
|
| | | item.clientId
|
| | | })
|
| | | },
|
| | | mqttData() {
|
| | | uni.$on(this.$constant.MQTT_TOPIC_MESSAGE, (data) => {
|
| | | try {
|
| | | // 1. 数据解析和验证
|
| | | const {
|
| | | data: wdata,
|
| | | topic
|
| | | } = this.parseAndValidateMqttData(data);
|
| | | if (!wdata || !topic) return;
|
| | |
|
| | | // 2. 准备主题常量
|
| | | const topics = this.prepareMqttTopics();
|
| | |
|
| | | // 3. 更新刷新时间
|
| | | this.refreshTime = dayjs().format('HH:mm:ss');
|
| | |
|
| | | // 4. 根据主题处理数据
|
| | | switch (topic) {
|
| | | case topics.updateEquStatu:
|
| | | this.handleEquipmentStatusUpdate(wdata);
|
| | | break;
|
| | | case topics.realData:
|
| | | this.mergeMqttRealData(wdata);
|
| | | break;
|
| | | case topics.realFaultTopic: |
| | | case topics.oneceFaultTopic:
|
| | | this.handleFaultData(wdata);
|
| | | break;
|
| | | }
|
| | | } catch (error) {
|
| | | console.error('MQTT数据处理错误:', error);
|
| | | }
|
| | | });
|
| | | },
|
| | |
|
| | | // 辅助方法:解析和验证MQTT数据
|
| | | parseAndValidateMqttData(data) {
|
| | | try {
|
| | | const json = JSON.parse(data);
|
| | | if (!json?.data || !json?.topic) return {
|
| | | data: null,
|
| | | topic: null
|
| | | };
|
| | | return {
|
| | | data: json.data,
|
| | | topic: json.topic
|
| | | };
|
| | | } catch (e) {
|
| | | return {
|
| | | data: null,
|
| | | topic: null
|
| | | };
|
| | | }
|
| | | },
|
| | |
|
| | | // 辅助方法:准备MQTT主题
|
| | | prepareMqttTopics() {
|
| | | return {
|
| | | updateEquStatu: this.$constant.SERVICE_BROADCAST_TENANT_UPDATE_EQU_STATU
|
| | | .replace('%s', this.tenantId),
|
| | | realData: this.$constant.SERVICE_BROADCAST_TENANT_REAL_DATA
|
| | | .replace('%s', this.tenantId),
|
| | | realFaultTopic: this.$constant.SERVICE_BROADCAST_TENANT_REAL_FAULT
|
| | | .replace('%s', this.tenantId),
|
| | | oneceFaultTopic: this.$constant.SERVICE_ONECE_TENANT_REAL_FAULT.replace('%s', this
|
| | | .deviceId)
|
| | | };
|
| | | },
|
| | |
|
| | | // 辅助方法:处理设备状态更新
|
| | | handleEquipmentStatusUpdate(wdata) {
|
| | | const {
|
| | | connected,
|
| | | tenantId,
|
| | | code
|
| | | } = wdata;
|
| | | const index = this.dataList.findIndex(item =>
|
| | | (item.tenantId + "") === tenantId && item.code === code
|
| | | );
|
| | |
|
| | | if (index === -1) return;
|
| | |
|
| | | // 更新在线状态
|
| | | this.$set(this.dataList[index], 'online', connected);
|
| | |
|
| | | // 如果离线,删除实时数据
|
| | | if (!connected && this.dataList[index]?.realData) {
|
| | | this.$delete(this.dataList[index], 'realData');
|
| | | }
|
| | | },
|
| | |
|
| | | // 辅助方法:处理故障数据
|
| | | handleFaultData(wdata) {
|
| | | if (!Array.isArray(wdata)) return;
|
| | |
|
| | | // 按设备分组故障数据
|
| | | const faultMap = wdata.reduce((map, item) => {
|
| | | const key = `${item.tenantId}_${item.equCode}`;
|
| | | map.get(key)?.push(item) || map.set(key, [item]);
|
| | | return map;
|
| | | }, new Map());
|
| | |
|
| | | // 更新每个设备的故障信息
|
| | | faultMap.forEach((faults, key) => {
|
| | | const [tenantId, equCode] = key.split("_");
|
| | | const targetItem = this.dataList.find(i =>
|
| | | i.tenantId == tenantId && i.code == equCode
|
| | | );
|
| | |
|
| | | if (targetItem) {
|
| | | const updatedItem = {
|
| | | ...targetItem,
|
| | | errorData: faults.filter(f => f.faultType === 1).map(n => n.faultName),
|
| | | warnData: faults.filter(f => f.faultType === 2).map(n => n.faultName),
|
| | | };
|
| | | this.$set(this.dataList, this.dataList.indexOf(targetItem), updatedItem);
|
| | | }
|
| | | });
|
| | |
|
| | | this.faultEquCount = this.dataList.filter(item => {
|
| | | return (
|
| | | item.errorData &&
|
| | | Array.isArray(item.errorData) &&
|
| | | item.errorData.length > 0
|
| | | );
|
| | | }).length || 0;
|
| | |
|
| | | this.alarmEquCount = this.dataList.filter(item => {
|
| | | return (
|
| | | item.warnData &&
|
| | | Array.isArray(item.warnData) &&
|
| | | item.warnData.length > 0
|
| | | );
|
| | | }).length || 0;
|
| | |
|
| | | }
|
| | |
|
| | | },onReady() { |
| | | |
| | | setTimeout(() => { |
| | | this.loading = false |
| | | }, 1000) |
| | |
|
| | |
|
| | | },
|
| | | onReady() {
|
| | |
|
| | | },
|
| | | computed: {
|
| | | equCount() {
|
| | | return this.dataList.length
|
| | | },
|
| | | onlineCount() {
|
| | | const list = this.dataList.filter(item => item.online)
|
| | | return list.length
|
| | | },
|
| | | tenantId() {
|
| | | const userinfo = uni.getStorageSync('userinfo');
|
| | | const tenantid = userinfo.loginTenantId
|
| | | return tenantid + "";
|
| | | },
|
| | | deviceId() {
|
| | | return uni.getStorageSync(this.$constant.DEVICE_ID);
|
| | | }
|
| | | }
|
| | | }
|
| | | </script>
|
| | |
| | | background-color: white;
|
| | | border-radius: 20rpx;
|
| | | font-family: Helvetica Neue, Helvetica, sans-serif;
|
| | | }
|
| | |
|
| | | .card-item-box {
|
| | | margin: 0rpx 20rpx 10rpx 20rpx;
|
| | | padding: 20rpx;
|
| | | box-sizing: border-box;
|
| | | background-color: white;
|
| | | border-radius: 20rpx;
|
| | | font-family: Helvetica Neue, Helvetica, sans-serif;
|
| | | }
|
| | |
|
| | | /* 第二个及之后的 item */
|
| | | .card-item-box:not(:first-child) {
|
| | | margin: 10rpx 20rpx;
|
| | | }
|
| | |
|
| | | .margin-left-sm {
|
| | | margin-left: 20rpx;
|
| | | }
|
| | |
|
| | |
|