自动化

feasure
lld 2026-02-22 22:25:42 +08:00
parent b4cd127ff2
commit 8a6ef40cda
6 changed files with 1958 additions and 528 deletions

View File

@ -257,9 +257,6 @@ export default {
<style lang="scss"> <style lang="scss">
// //
//@import './tuniao-ui/index.scss'; @import './tuniao-ui/iconfont.css';
//@import './tuniao-ui/iconfont.css';
@import '@/static/scss/index.scss'; @import '@/static/scss/index.scss';
//@import "colorui/main.css";
//@import "colorui/icon.css";
</style> </style>

View File

@ -67,6 +67,19 @@
"navigationBarTitleText": "控制中心" "navigationBarTitleText": "控制中心"
} }
} }
// ,
// {
// "path": "control/automatic",
// "style": {
// "navigationBarTitleText": "自动控制"
// }
// },
// {
// "path": "control/manual",
// "style": {
// "navigationBarTitleText": "手动控制"
// }
// }
] ]
}, },
/* { /* {

File diff suppressed because it is too large Load Diff

View File

@ -8,191 +8,83 @@
</template> </template>
<view class="card shadow shadow-lg bg-white "> <view class="card shadow shadow-lg bg-white ">
<uni-section :title="`当前大棚:【${selectedText}】`" :subTitle="imei" titleFontSize="20px" type="line" > <uni-section :title="`当前大棚:【${selectedText}】`" :subTitle="imei" titleFontSize="20px" type="line" >
<!-- <view class="uni-px-5 uni-pb-5">--> <template v-slot:right>
<!-- <uni-data-select v-model="value" :localdata="range" @change="change"></uni-data-select>--> <view class="switch-row">
<!-- </view>--> <text class="modal-text">手动</text>
<tn-switch
v-model="currentMode"
:size="60"
/>
<text class="modal-text">自动</text>
</view>
</template>
</uni-section> </uni-section>
<uni-divider margin="10rpx 0"></uni-divider> <uni-divider margin="10rpx 0"></uni-divider>
<uni-section title="实时温湿度" titleFontSize="16px" type="line" v-if="value!== 1">
<template v-slot:right >
{{ temp }}
</template>
<view> <!-- 把解析好的完整数据传给子组件 -->
<!-- 优化温度卡片循环渲染 --> <auto-page
<view class="uni-flex_control uni-row" > v-if="currentMode === true"
<view value="0"
class="text uni-flex_control_one uni-view" />
v-for="item in sensorCards.temp" <manual-page
:key="item.key" ref="manualPage"
@click="openDataModal(item)" v-else-if="currentMode === false"
> :liveData="liveData"
<text class="data" :style="fontStyle"> :show="show"
{{ liveData[item.key] }} :status="status"
<text v-if="isEffectiveValue(liveData[item.key])" class="tempStyle"></text> :dtu_remark="dtu_remark"
</text> :selectedText="selectedText"
<text class="data" v-if="dtu_remark[item.key]">{{ dtu_remark[item.key] }}</text> :value="value"
<text class="data" v-else>{{ item.label }}</text> :agriId="agriId"
</view> @publicMsg="publishMessage"
</view> @getRemark="getRemarkByImei"
<!-- 优化湿度卡片循环渲染 -->
<view class="uni-flex_control uni-row" >
<view
class="text uni-flex_control_two uni-view"
v-for="item in sensorCards.humi"
:key="item.key"
@click="openDataModal(item)"
>
<text class="data" :style="fontStyle">
{{ liveData[item.key] }}
<text v-if="isEffectiveValue(liveData[item.key])" class="humiStyle"> %RH</text>
</text>
<text class="data" v-if="dtu_remark[item.key]">{{ dtu_remark[item.key] }}</text>
<text class="data" v-else>{{ item.label }}</text>
</view>
</view>
</view>
</uni-section>
<uni-section title="设备控制" titleFontSize="16px" type="line" v-if="value!== 1 && !['862538065276939','A','B','C'].includes(imei)">
<template v-slot:right >
{{ control }}
</template>
<!-- 优化设备卡片循环渲染 -->
<view class="card-grid">
<view
class="control-card"
v-for="card in deviceCards"
:key="card.type"
@click="openTimeModal(card)"
>
<view class="card-text">
<text class="card-main" v-if="dtu_remark[card.type]">{{ dtu_remark[card.type] }}</text>
<text class="card-main" v-else>{{ card.name }}</text>
<view class="card-sub-wrapper">
<text class="card-sub" v-if="showStatusText">{{ show[card.type] || '' }}</text>
<text class="limit-time">
运行时间{{ limitTimes[`${card.type}Limit`] && limitTimes[`${card.type}Limit`]!=='0' ? `${limitTimes[`${card.type}Limit`]} s` : '- -' }}
</text>
</view>
</view>
<view v-if="showFlag" class="card-icon" :class="{ active: status[card.type] === 1 }" @click.stop="handleCardClick(1 - status[card.type], card.type)">
<!-- @click.stop防止冒泡触发卡片点击的弹窗事件 -->
<uni-icons
:type="status[card.type] === 1 ? 'circle' : 'circle-filled'"
size="24"
color="#fff"
/> />
</view> </view>
</view>
</view>
</uni-section>
</view>
<uni-popup ref="inputNamelog" mode="center" >
<!-- 新增修改运行时间的弹窗 -->
<view class="modal-container">
<view class="modal-title">{{ `${selectedText} - 实时温湿度】别名设置` }}</view>
<view class="modal-input-wrap" >
<text class="modal-label">别名设置</text>
<uni-easyinput style="width: 100px" v-model="remark" placeholder="不填展示默认备注" />
</view>
<view class="modal-btn-wrap">
<button class="modal-btn cancel" @click="closeModalDataName"></button>
<button class="modal-btn confirm" @click="confirmModifyName"></button>
</view>
</view>
</uni-popup>
<uni-popup ref="inputDialog" mode="center" >
<!-- 新增修改运行时间的弹窗 -->
<view class="modal-container">
<view class="modal-title">{{ `${selectedText} - ${currentCard.name}】设置` }}</view>
<view class="modal-input-wrap">
<text class="modal-label">当前时间</text>
<text class="modal-current">{{ currentCardTime>0 ? `${currentCardTime}` : '未设置' }}</text>
</view>
<view class="modal-input-wrap" >
<text class="modal-label">修改后时间</text>
<input
class="modal-input"
type="number"
v-model.number="newLimitTime"
/>
<!-- <uni-number-box v-model="newLimitTime" />-->
<text class="modal-unit"></text>
</view>
<view class="modal-input-wrap" >
<text class="modal-label">别名设置</text>
<uni-easyinput style="width: 100px" v-model="remark" placeholder="不填展示默认备注" />
</view>
<view class="modal-btn-wrap">
<button class="modal-btn cancel" @click="close"></button>
<button class="modal-btn confirm" @click="confirmModifyTime"></button>
</view>
</view>
</uni-popup>
</z-paging> </z-paging>
</view> </view>
</template> </template>
<script> <script>
// //
import UniPopupDialog from "../../../uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue"; import ManualPage from "./manual.vue"
import AutoPage from "./automatic.vue"
const SENSOR_MAP = { const SENSOR_MAP = {
temp1: "201", temp2: "202", temp3: "203", temp4: "204", temp1: "201", temp2: "202", temp3: "203", temp4: "204",
humi1: "101", humi2: "102", humi3: "103", humi4: "104" humi1: "101", humi2: "102", humi3: "103", humi4: "104"
}; };
const MQTT_TOPIC_SUFFIX = { UP: "/+", DOWN: "/control" }; const MQTT_TOPIC_SUFFIX = { UP: "/+", DOWN: "/control" };
import UniDatetimePicker from "../../../uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue";
import UniPopup from "../../../uni_modules/uni-popup/components/uni-popup/uni-popup.vue"; //
import { findDtuDataByInfo } from "@/api/system/data";
import mqttUtil from '@/utils/mqtt'; import mqttUtil from '@/utils/mqtt';
import UniNumberBox from "../../../uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue";
import {addLimit, getAgriByImei, updateLimit} from "../../../api/system/assets/limit";
import {listAgri} from "../../../api/system/assets/agri"; import {listAgri} from "../../../api/system/assets/agri";
import {getNewSpecialData} from "../../../api/data/specialData"; import {getNewSpecialData} from "../../../api/data/specialData";
import store from "../../../store"; import store from "../../../store";
import {addRemark, getRemarkByImei, updateRemark} from "../../../api/system/assets/remark"; import {getRemarkByImei} from "../../../api/system/assets/remark";
import CustomRefresher from "../../../components/custom-refresher/custom-refresher.vue"; import CustomRefresher from "../../../components/custom-refresher/custom-refresher.vue";
import ZPaging from "../../../uni_modules/z-paging/components/z-paging/z-paging.vue"; import ZPaging from "../../../uni_modules/z-paging/components/z-paging/z-paging.vue";
import {findDtuDataByInfo} from "../../../api/system/data";
export default { export default {
dicts: ['sys_data_map'], dicts: ['sys_data_map'],
components: { components: {
ZPaging, ZPaging,
UniNumberBox,
UniPopupDialog,
UniDatetimePicker,
CustomRefresher, CustomRefresher,
UniPopup // ManualPage,
AutoPage,
}, },
data() { data() {
return { return {
showFlag:true, currentMode:false,
temp: "",
mqttConfig: { mqttConfig: {
subscribeTopic:'/listener', subscribeTopic:'/listener',
}, },
value: 1, value: 1,
selectedText: '', selectedText: '',
// hide: false
showStatusText: false,
control: '正在加载中...',
// range: [], // range: [],
agriId:'', agriId:'',
imei:'', imei:'',
publishTopic: '/control', publishTopic: '/control',
title:'',
message: {},
// connected // connected
connected: false, connected: false,
remark:'',
dtu_remark:{}, dtu_remark:{},
liveData: { liveData: {
temp1: '数据加载中...', temp1: '数据加载中...',
@ -202,37 +94,9 @@ export default {
humi1: '数据加载中...', humi1: '数据加载中...',
humi2: '数据加载中...', humi2: '数据加载中...',
humi3: '数据加载中...', humi3: '数据加载中...',
humi4: '数据加载中...' humi4: '数据加载中...',
temp: "正在加载中..."
}, },
// 湿1234湿1234
sensorCards: {
temp: [
{ label: '温度1', key: 'temp1' },
{ label: '温度2', key: 'temp2' },
{ label: '温度3', key: 'temp3' },
{ label: '温度4', key: 'temp4' }
],
humi: [
{ label: '湿度1', key: 'humi1' },
{ label: '湿度2', key: 'humi2' },
{ label: '湿度3', key: 'humi3' },
{ label: '湿度4', key: 'humi4' }
]
},
sensorCard:{},
//
deviceCards: [
{ type: 'jbk', name: '卷被开' },
{ type: 'jbg', name: '卷被关' },
{ type: 'jm1k', name: '卷膜1开' },
{ type: 'jm1g', name: '卷膜1关' },
{ type: 'jm2k', name: '卷膜2开' },
{ type: 'jm2g', name: '卷膜2关' },
{ type: 'jm3k', name: '卷膜3开' },
{ type: 'jm3g', name: '卷膜3关' },
{ type: 'jlk', name: '卷帘开' },
{ type: 'jlg', name: '卷帘关' },
],
// //
show: { show: {
jbk: "暂停", jbk: "暂停",
@ -246,19 +110,6 @@ export default {
jm3k: "暂停", jm3k: "暂停",
jm3g: "暂停" jm3g: "暂停"
}, },
//
limitTimes: {
jbkLimit: 0,
jbgLimit: 0,
jlkLimit: 0,
jlgLimit: 0,
jm1kLimit: 0,
jm1gLimit: 0,
jm2kLimit: 0,
jm2gLimit: 0,
jm3kLimit: 0,
jm3gLimit: 0
},
status: { status: {
jbk: 0, jbk: 0,
jbg: 0, jbg: 0,
@ -269,14 +120,10 @@ export default {
jm2k: 0, jm2k: 0,
jm2g: 0, jm2g: 0,
jm3k: 0, jm3k: 0,
jm3g: 0 jm3g: 0,
deviceTime:"正在加载中..."
}, },
testMsg:'由于线上为真实数据。任何操作均可影响线上功能,故仅作演示', testMsg:'由于线上为真实数据。任何操作均可影响线上功能,故仅作演示',
fontStyle: '',
//
currentCard: {}, //
currentCardTime: '', //
newLimitTime: 0, //
}; };
}, },
onLoad(option) { onLoad(option) {
@ -288,8 +135,6 @@ export default {
this.agriId = agriInfo.agriId; this.agriId = agriInfo.agriId;
this.change(this.value) this.change(this.value)
} }
this.title="";
// [k, g] // [k, g]
const mutexPairs = [ const mutexPairs = [
['jbk', 'jbg'], ['jbk', 'jbg'],
@ -311,7 +156,6 @@ export default {
mqttUtil.setOnMessageCallback(this.ackMessage); mqttUtil.setOnMessageCallback(this.ackMessage);
// //
this.connected = mqttUtil.getMqttState().isConnected; this.connected = mqttUtil.getMqttState().isConnected;
this.showFlag = !((store.getters && store.getters.name !== 'admin') && this.$auth.hasRole("test"))
}, },
onUnload() { onUnload() {
// MQTT // MQTT
@ -323,12 +167,13 @@ export default {
this.change(this.imei) this.change(this.imei)
mqttUtil.setOnMessageCallback(this.ackMessage); mqttUtil.setOnMessageCallback(this.ackMessage);
this.$refs.paging.complete(); this.$refs.paging.complete();
this.$refs.manualPage.refresh();
}, },
getNewSpecialData() { getNewSpecialData() {
getNewSpecialData().then(response => { getNewSpecialData().then(response => {
if (response.code === 200 && response.data) { if (response.code === 200 && response.data) {
this.makeSpecialData(response.data,false); this.makeSpecialData(response.data,false);
this.temp = "最后更新时间:"+response.data.time; this.liveData.temp = "最后更新时间:"+response.data.time;
this.fontStyle = 'font-size:16px;' this.fontStyle = 'font-size:16px;'
} }
}) })
@ -338,15 +183,11 @@ export default {
var clientId = mqttUtil.getMqttState().clientId; var clientId = mqttUtil.getMqttState().clientId;
this.connected = mqttUtil.getMqttState().isConnected; this.connected = mqttUtil.getMqttState().isConnected;
if ((e === 'A' || e==='B' || e==='C') if ((e === 'A' || e==='B' || e==='C')
&& store.getters && store.getters.name === 'admin' ) { && store.getters && store.getters.name === 'admin'
&& this.currentMode===false) {
this.getNewSpecialData(e); this.getNewSpecialData(e);
this.mqttConfig.subscribeTopic = `frontend/${clientId}/dtu/862538065276061`; this.mqttConfig.subscribeTopic = `frontend/${clientId}/dtu/862538065276061`;
} }
// const selectedItem = this.range.find(item => item.value === e);
// if (selectedItem) {
// this.selectedText = selectedItem.text; //
// this.agriId = selectedItem.agriId;
// this.title= this.selectedText;
if (e !== 'A' && e!=='B' && e!=='C') { if (e !== 'A' && e!=='B' && e!=='C') {
// 使MQTT // 使MQTT
this.publishTopic = `frontend/${clientId}${MQTT_TOPIC_SUFFIX.DOWN}/${this.imei}`; this.publishTopic = `frontend/${clientId}${MQTT_TOPIC_SUFFIX.DOWN}/${this.imei}`;
@ -359,24 +200,17 @@ export default {
Object.keys(response.data).forEach(key => { Object.keys(response.data).forEach(key => {
this.liveData[key] = response.data[key] || '已离线..'; this.liveData[key] = response.data[key] || '已离线..';
}); });
this.temp = "最后更新时间:"+response.data.time; this.liveData.temp = "最后更新时间:"+response.data.time;
this.fontStyle = 'font-size:16px;' this.fontStyle = 'font-size:16px;'
}) })
//
this.getAgriByImei();
// //
this.getRemarkByImei(); this.getRemarkByImei();
if (e!=="862538065276939"){ if (e!=="862538065276939"){
this.message=JSON.stringify({jbk: 0,read:true}) const message = JSON.stringify({jbk: 0,read:true})
this.publishMessage() this.publishMessage(message)
} }
} }
// } else {
// this.selectedText = ''; //
// this.title='';
// this.value=1;
// this.agriId = '';
// }
this.reset(); this.reset();
this.style=""; this.style="";
}, },
@ -387,16 +221,7 @@ export default {
} }
}); });
}, },
getAgriByImei() {
getAgriByImei(this.imei).then(response => {
if (response.code === 200) {
if (!response.data) {
return;
}
this.limitTimes = response.data;
}
})
},
getAgriList() { getAgriList() {
listAgri().then(response => { listAgri().then(response => {
if (response.code === 200) { if (response.code === 200) {
@ -469,106 +294,44 @@ export default {
|| "已离线..." || "已离线..."
]) ])
); );
this.liveData.temp = "最后更新时间:" + this.getCurrentTime();
}, },
reset() { reset() {
Object.keys(this.show).forEach(key => { Object.keys(this.show).forEach(key => {
this.show[key] = "暂停"; this.show[key] = "暂停";
}); });
Object.keys(this.status).forEach(key => { Object.keys(this.status).forEach(key => {
if (key === "deviceTime") {
this.status[key] = '正在加载中...';
return;
}
this.status[key] = 0; this.status[key] = 0;
}); });
Object.keys(this.liveData).forEach(key => { Object.keys(this.liveData).forEach(key => {
if (key==="temp") {
this.liveData[key] = '正在加载中...';
return;
}
this.liveData[key] = '数据加载中...'; this.liveData[key] = '数据加载中...';
}); });
Object.keys(this.limitTimes).forEach(key => {
this.limitTimes[key] = 0;
});
this.control = '正在加载中...';
this.message = {};
this.temp = '';
}, },
//
handleCardClick(status, type) {
const funcMsg = "该功能用来开启或暂停设备,按钮亮为开启,按钮暗为暂停设备"
if ((store.getters && store.getters.name !== 'admin')
&& this.$auth.hasRole("test")) {
// this.testFunction(`${this.testMsg}\n${funcMsg}`)
return;
}
//
//
const tipMap = {
'jbk': {opposite: 'jbg', name: '卷被关', op: '卷被开'},
'jbg': {opposite: 'jbk', name: '卷被开', op: '卷被关'},
'jlk': {opposite: 'jlg', name: '卷帘关', op: '卷帘开'},
'jlg': {opposite: 'jlk', name: '卷帘开', op: '卷帘关'},
'jm1k': {opposite: 'jm1g', name: '卷膜1关', op: '卷膜1开'},
'jm1g': {opposite: 'jm1k', name: '卷膜1开', op: '卷膜1关'},
'jm2k': {opposite: 'jm2g', name: '卷膜2关', op: '卷膜2开'},
'jm2g': {opposite: 'jm2k', name: '卷膜2开', op: '卷膜2关'},
'jm3k': {opposite: 'jm3g', name: '卷膜3关', op: '卷膜3开'},
'jm3g': {opposite: 'jm3k', name: '卷膜3开', op: '卷膜3关'}
};
// case
if (!tipMap[type]) return;
const {opposite, name, op} = tipMap[type];
//
if (status === 1 && this.status[opposite] === 1) {
this.$modal.msgError(`${this.selectedText}${name}在运行状态,不能运行${op}操作!`);
return;
}
// ========== ==========
this.connected = mqttUtil.getMqttState().isConnected;
if (!this.connected) {
this.$modal.msgError("设备连接异常");
return;
}
if (this.value === 1) {
this.$modal.msgError("设备控制失败!");
console.info("大棚选取失败!")
return;
}
uni.showModal({
title: '操作提示:',
content: '确定' + (status === 1 ? "运行" : "暂停") + '【' + this.selectedText + '】设备?',
cancelText: '取消',
confirmText: '确定',
success: (res) => {
if (res.confirm) {
//
this.message = JSON.stringify({[`${type}1`]: status})
//
this.publishMessage();
//todo
// this.testAuto(type);
}
}
})
},
// //
testAuto(type) { testAuto(type) {
this.$set(this.status, type, this.status[type] === 0 ? 1 : 0); this.$set(this.status, type, this.status[type] === 0 ? 1 : 0);
this.$set(this.show, type, this.status[type] === 0 ? "暂停" : "运行"); this.$set(this.show, type, this.status[type] === 0 ? "暂停" : "运行");
}, },
publishMessage() { publishMessage(message) {
if (!this.connected || !this.publishTopic || !this.message) { if (!this.connected || !this.publishTopic || !message) {
return return
} }
// MQTT // MQTT
const publishSuccess = mqttUtil.publishMqtt(this.publishTopic, this.message); const publishSuccess = mqttUtil.publishMqtt(this.publishTopic, message);
if (publishSuccess) { if (publishSuccess) {
this.addMessage(`【指令已发送】imei: ${this.publishTopic},指令: ${this.message}`); this.addMessage(`【指令已发送】imei: ${this.publishTopic},指令: ${message}`);
} else { } else {
this.addMessage(`发布失败:设备:[${this.publishTopic}]`) this.addMessage(`发布失败:设备:[${this.publishTopic}]`)
} }
this.message = {};
}, },
// //
@ -641,13 +404,12 @@ export default {
const value = msgData[key]; const value = msgData[key];
this.show[key] = value === 0 ? '暂停' : '运行'; this.show[key] = value === 0 ? '暂停' : '运行';
}); });
this.control = '最后更新时间:' + this.getCurrentTime(); this.status.deviceTime = '最后更新时间:' + this.getCurrentTime();
} }
if (this.currentMode) return;
const allKeysNumeric2 = Object.keys(msgData).every(key => /^\d+$/.test(key)); const allKeysNumeric2 = Object.keys(msgData).every(key => /^\d+$/.test(key));
if (Object.keys(msgData).length > 0 && allKeysNumeric2) { if (Object.keys(msgData).length > 0 && allKeysNumeric2) {
this.temp = "最后更新时间:" + this.getCurrentTime();
this.fontStyle = 'font-size:16px;' this.fontStyle = 'font-size:16px;'
this.makeSpecialData(msgData, true); this.makeSpecialData(msgData, true);
} }
} }
@ -674,220 +436,6 @@ export default {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}, },
testNumber(data) {
const reg = /^-?\d+(\.\d+)?$/;
return reg.test(String(data).trim());
},
// 湿
isEffectiveValue(value) {
return this.testNumber(value);
},
//
openTimeModal(card) {
if ((store.getters && store.getters.name !== 'admin')
&& this.$auth.hasRole("test")) {
return;
}
this.currentCard = card; //
this.currentCardTime = this.limitTimes[`${card.type}Limit`]; //
this.newLimitTime = this.currentCardTime; //
this.remark = this.dtu_remark[card.type] || card.name;
this.$refs.inputDialog.open()
},
openDataModal(sensorCard) {
if ((store.getters && store.getters.name !== 'admin')
&& this.$auth.hasRole("test")) {
return;
}
this.sensorCard = sensorCard;
this.remark=this.dtu_remark[sensorCard.key] || sensorCard.label;
this.$refs.inputNamelog.open()
},
confirmModifyName() {
const funcMsg = "该功能用来设置温湿度卡片别名,如若不填则展示默认备注"
if ((store.getters && store.getters.name !== 'admin')
&& this.$auth.hasRole("test")) {
// this.testFunction(`${this.testMsg}\n${funcMsg}`)
this.$refs?.inputNamelog?.close();
return;
}
// 1. sensorCardkey/label
let {
selectedText,
dtu_remark,
imei,
sensorCard: { key: sensorKey, label: sensorLabel },
remark
} = this;
remark = remark || sensorLabel;
// 2.
const isRemarkModified = remark !== (dtu_remark[sensorKey] || sensorLabel);
if (!isRemarkModified) {
//
this.$refs?.inputNamelog?.close();
return;
}
// 3. async/await
uni.showModal({
title: '温馨提示:',
content: `确定将${selectedText}-${sensorLabel}别名设置为:${remark}`, // remark
cancelText: '取消',
confirmText: '确定',
success: async (res) => {
// /
this.$refs?.inputNamelog?.close();
if (!res.confirm) return; //
try {
// 4. loading
// uni.showLoading({ title: '...' });
// 5.
this.$set(dtu_remark, sensorKey, remark); // sensorKey
// imei
if (!dtu_remark.imei) {
this.$set(dtu_remark, 'imei', imei);
}
// 6. await
const remarkRes = dtu_remark.id ? await updateRemark(dtu_remark) : await addRemark(dtu_remark);
// 7.
const isSuccess = remarkRes.code === 200;
this.$modal[isSuccess ? 'msgSuccess' : 'msgError'](
isSuccess ? '修改成功' : '修改失败'
);
// 8.
if (isSuccess) {
this.getRemarkByImei();
}
} catch (error) {
// /
console.error('别名修改失败:', error);
this.$modal.msgError('修改失败,请重试');
} finally {
// /loading
// uni.hideLoading();
}
}
});
},
closeModalDataName() {
this.$refs.inputNamelog.close()
},
close() {
this.$refs.inputDialog.close()
},
//
//
confirmModifyTime() {
const funcMsg = "该功能用来设置设备运行时间及设备别名,设备运行时间即开启设备后到达运行时间自行暂停设备;设备别名不填则展示默认设备名称"
if ((store.getters && store.getters.name !== 'admin')
&& this.$auth.hasRole("test")) {
// this.testFunction(`${this.testMsg}\n${funcMsg}`)
this.$refs?.inputDialog?.close();
return;
}
// 1. this.xxx
let {
newLimitTime, currentCardTime, selectedText, currentCard,
limitTimes, dtu_remark, imei, agriId, remark
} = this;
// 2.
/*if (!newLimitTime || newLimitTime < 1 || newLimitTime > 60) {
uni.showToast({ title: '请输入1-60的有效数字', icon: 'none' });
return;
}*/
// 3.
const isLimitTimeModified = newLimitTime !== currentCardTime; //
remark = remark || currentCard.name;
const isRemarkModified = (remark !== (dtu_remark[currentCard.type] || currentCard.name)); //
// 4.
if (!isLimitTimeModified && !isRemarkModified) {
// /
this.$refs?.inputDialog?.close();
return;
}
// 5.
let confirmContent = `确定修改${selectedText}-${currentCard.name}`;
const tips = [];
if (isLimitTimeModified) tips.push(`运行时间为:${newLimitTime}`);
if (isRemarkModified) {
tips.push(`别名设置为:${remark}`)
}
confirmContent += `\n${tips.map((tip, idx) => `${idx + 1}. ${tip}`).join('\n')}`;
// 6. async/await
uni.showModal({
title: '温馨提示:',
content: confirmContent,
cancelText: '取消',
confirmText: '确定',
success: async (res) => {
// /
this.$refs?.inputDialog?.close();
if (!res.confirm) return; //
let isAllSuccess = true; //
try {
// 7.
if (isLimitTimeModified) {
this.$set(limitTimes, `${currentCard.type}Limit`, newLimitTime || 0);
//
if (!limitTimes.imei) {
this.$set(limitTimes, 'imei', imei);
this.$set(limitTimes, 'agriName', selectedText);
this.$set(limitTimes, 'agriId', agriId);
}
// async/await
const limitRes = limitTimes.id
? await updateLimit(limitTimes)
: await addLimit(limitTimes);
if (limitRes.code !== 200) isAllSuccess = false;
//
if (!limitTimes.imei) this.getAgriByImei();
}
// 8.
if (isRemarkModified) {
this.$set(dtu_remark, currentCard.type, remark);
// imei
if (!dtu_remark.imei) {
this.$set(dtu_remark, 'imei', imei);
}
//
const remarkRes = dtu_remark.id
? await updateRemark(dtu_remark)
: await addRemark(dtu_remark);
if (remarkRes.code !== 200) isAllSuccess = false;
//
if (!dtu_remark.imei) this.getRemarkByImei();
}
// 9.
this.$modal[isAllSuccess ? 'msgSuccess' : 'msgError'](
isAllSuccess ? '修改成功' : '修改失败'
);
} catch (error) {
// /
console.error('修改失败:', error);
this.$modal.msgError('修改失败,请重试');
}
}
});
}
}, },
onHide() { onHide() {
mqttUtil.removeOnMessageCallback(); mqttUtil.removeOnMessageCallback();
@ -897,13 +445,10 @@ export default {
}, },
}; };
</script> </script>
<style>
@import "@/colorui/main.css";
// #ifdef mp-weixin
@import "/colorui/main.css";
// #endif
</style>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '@/tuniao-ui/index.scss';
@import "@/colorui/main.css";
@import "@/colorui/icon.css";
/deep/ .z-paging-content-fixed { /deep/ .z-paging-content-fixed {
padding: 20rpx !important; padding: 20rpx !important;
@ -1117,5 +662,14 @@ export default {
border-radius: 20rpx; border-radius: 20rpx;
overflow: hidden; overflow: hidden;
} }
.switch-row {
display: flex;
align-items: center; /* 垂直居中 */
gap: 8px; /* 元素之间的间距,可根据需要调整 */
}
.modal-text {
/* 确保文字和开关在视觉上对齐 */
line-height: 1;
}
</style> </style>

View File

@ -0,0 +1,778 @@
<template>
<view>
<uni-section title="实时温湿度" titleFontSize="16px" type="line" v-if="value!== '1'">
<template v-slot:right>
{{ liveData.temp }}
</template>
<view>
<!-- 优化温度卡片循环渲染 -->
<view class="uni-flex_control uni-row">
<view
class="text uni-flex_control_one uni-view"
v-for="item in sensorCards.temp"
:key="item.key"
@click="openDataModal(item)"
>
<text class="data" :style="fontStyle">
{{ liveData[item.key] }}
<text v-if=" (liveData[item.key])" class="tempStyle"></text>
</text>
<text class="data" v-if="dtu_remark[item.key]">{{ dtu_remark[item.key] }}</text>
<text class="data" v-else>{{ item.label }}</text>
</view>
</view>
<!-- 优化湿度卡片循环渲染 -->
<view class="uni-flex_control uni-row">
<view
class="text uni-flex_control_two uni-view"
v-for="item in sensorCards.humi"
:key="item.key"
@click="openDataModal(item)"
>
<text class="data" :style="fontStyle">
{{ liveData[item.key] }}
<text v-if="isEffectiveValue(liveData[item.key])" class="humiStyle"> %RH</text>
</text>
<text class="data" v-if="dtu_remark[item.key]">{{ dtu_remark[item.key] }}</text>
<text class="data" v-else>{{ item.label }}</text>
</view>
</view>
</view>
</uni-section>
<uni-section title="设备控制" titleFontSize="16px" type="line"
v-if="value!== 1 && !['862538065276939','A','B','C'].includes(value)">
<template v-slot:right>
{{ status.deviceTime }}
</template>
<!-- 优化设备卡片循环渲染 -->
<view class="card-grid">
<view
class="control-card"
v-for="card in deviceCards"
:key="card.type"
@click="openTimeModal(card)"
>
<view class="card-text">
<text class="card-main" v-if="dtu_remark[card.type]">{{ dtu_remark[card.type] }}</text>
<text class="card-main" v-else>{{ card.name }}</text>
<view class="card-sub-wrapper">
<text class="card-sub" v-if="showStatusText">{{ show[card.type] || '' }}</text>
<text class="limit-time">
运行时间{{
limitTimes[`${card.type}Limit`] && limitTimes[`${card.type}Limit`] !== '0' ? `${limitTimes[`${card.type}Limit`]} s` : '- -'
}}
</text>
</view>
</view>
<view v-if="showFlag" class="card-icon" :class="{ active: status[card.type] === 1 }"
@click.stop="handleCardClick(1 - status[card.type], card.type)">
<!-- @click.stop防止冒泡触发卡片点击的弹窗事件 -->
<uni-icons
:type="status[card.type] === 1 ? 'circle' : 'circle-filled'"
size="24"
color="#fff"
/>
</view>
</view>
</view>
</uni-section>
<uni-popup ref="inputNamelog" mode="center">
<!-- 新增修改运行时间的弹窗 -->
<view class="modal-container">
<view class="modal-title">{{ `${selectedText} - 实时温湿度】别名设置` }}</view>
<view class="modal-input-wrap">
<text class="modal-label">别名设置</text>
<uni-easyinput style="width: 100px" v-model="remark" placeholder="不填展示默认备注"/>
</view>
<view class="modal-btn-wrap">
<button class="modal-btn cancel" @click="closeModalDataName"></button>
<button class="modal-btn confirm" @click="confirmModifyName"></button>
</view>
</view>
</uni-popup>
<uni-popup ref="inputDialog" mode="center">
<!-- 新增修改运行时间的弹窗 -->
<view class="modal-container">
<view class="modal-title">{{ `${selectedText} - ${currentCard.name}】设置` }}</view>
<view class="modal-input-wrap">
<text class="modal-label">当前时间</text>
<text class="modal-current">{{ currentCardTime > 0 ? `${currentCardTime}` : '未设置' }}</text>
</view>
<view class="modal-input-wrap">
<text class="modal-label">修改后时间</text>
<input
class="modal-input"
type="number"
v-model.number="newLimitTime"
/>
<!-- <uni-number-box v-model="newLimitTime" />-->
<text class="modal-unit"></text>
</view>
<view class="modal-input-wrap">
<text class="modal-label">别名设置</text>
<uni-easyinput style="width: 100px" v-model="remark" placeholder="不填展示默认备注"/>
</view>
<view class="modal-btn-wrap">
<button class="modal-btn cancel" @click="close"></button>
<button class="modal-btn confirm" @click="confirmModifyTime"></button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
//
import UniPopup from "../../../uni_modules/uni-popup/components/uni-popup/uni-popup.vue"; //
import mqttUtil from '@/utils/mqtt';
import {addLimit, getAgriByImei, updateLimit} from "../../../api/system/assets/limit";
import store from "../../../store";
import {addRemark, updateRemark} from "../../../api/system/assets/remark";
export default {
dicts: ['sys_data_map'],
name: "manual",
props: {
//
value: {
type: String,
default: "1"
},
//
activeColor: {
type: String,
default: ''
},
dtu_remark: {
type: Object,
default() {
return {}
}
},
status: {
type: Object,
default() {
return {}
}
},
liveData: {
type: Object,
default() {
return {}
}
},
show: {
type: Object,
default() {
return {}
}
},
selectedText: {
type: String,
default: ""
},
agriId: {
type: String,
default: null
},
},
watch: {
value: {
deep: true, //
immediate: true, //
handler(newVal) {
//
if (newVal) {
this.imei = this.value
}
}
}
},
components: {
ZPaging,
UniNumberBox,
UniPopupDialog,
CustomRefresher,
UniPopup //
},
mounted() {
this.refresh()
},
data() {
return {
fontStyle: '',
sensorCards: {
temp: [
{label: '温度1', key: 'temp1'},
{label: '温度2', key: 'temp2'},
{label: '温度3', key: 'temp3'},
{label: '温度4', key: 'temp4'}
],
humi: [
{label: '湿度1', key: 'humi1'},
{label: '湿度2', key: 'humi2'},
{label: '湿度3', key: 'humi3'},
{label: '湿度4', key: 'humi4'}
]
},
deviceCards: [
{type: 'jbk', name: '卷被开'},
{type: 'jbg', name: '卷被关'},
{type: 'jm1k', name: '卷膜1开'},
{type: 'jm1g', name: '卷膜1关'},
{type: 'jm2k', name: '卷膜2开'},
{type: 'jm2g', name: '卷膜2关'},
{type: 'jm3k', name: '卷膜3开'},
{type: 'jm3g', name: '卷膜3关'},
{type: 'jlk', name: '卷帘开'},
{type: 'jlg', name: '卷帘关'},
],
// hide: false
showStatusText: false,
showFlag: true,
remark: '',
//
currentCard: {}, //
currentCardTime: '', //
newLimitTime: 0, //
message: {},
// connected
connected: false,
imei: '',
//
limitTimes: {
jbkLimit: 0,
jbgLimit: 0,
jlkLimit: 0,
jlgLimit: 0,
jm1kLimit: 0,
jm1gLimit: 0,
jm2kLimit: 0,
jm2gLimit: 0,
jm3kLimit: 0,
jm3gLimit: 0
},
// 湿1234湿1234
sensorCard:{},
};
},
methods: {
refresh() {
this.showFlag = !((store.getters && store.getters.name !== 'admin') && this.$auth.hasRole("test"))
this.getAgriByImei();
},
openDataModal(sensorCard) {
if ((store.getters && store.getters.name !== 'admin')
&& this.$auth.hasRole("test")) {
return;
}
this.sensorCard = sensorCard;
this.remark = this.dtu_remark[sensorCard.key] || sensorCard.label;
this.$refs.inputNamelog.open()
},
// 湿
isEffectiveValue(value) {
return this.testNumber(value);
},
testNumber(data) {
const reg = /^-?\d+(\.\d+)?$/;
return reg.test(String(data).trim());
},
//
openTimeModal(card) {
if ((store.getters && store.getters.name !== 'admin')
&& this.$auth.hasRole("test")) {
return;
}
this.currentCard = card; //
this.currentCardTime = this.limitTimes[`${card.type}Limit`]; //
this.newLimitTime = this.currentCardTime; //
this.remark = this.dtu_remark[card.type] || card.name;
this.$refs.inputDialog.open()
},
//
handleCardClick(status, type) {
const funcMsg = "该功能用来开启或暂停设备,按钮亮为开启,按钮暗为暂停设备"
if ((store.getters && store.getters.name !== 'admin')
&& this.$auth.hasRole("test")) {
// this.testFunction(`${this.testMsg}\n${funcMsg}`)
return;
}
//
//
const tipMap = {
'jbk': {opposite: 'jbg', name: '卷被关', op: '卷被开'},
'jbg': {opposite: 'jbk', name: '卷被开', op: '卷被关'},
'jlk': {opposite: 'jlg', name: '卷帘关', op: '卷帘开'},
'jlg': {opposite: 'jlk', name: '卷帘开', op: '卷帘关'},
'jm1k': {opposite: 'jm1g', name: '卷膜1关', op: '卷膜1开'},
'jm1g': {opposite: 'jm1k', name: '卷膜1开', op: '卷膜1关'},
'jm2k': {opposite: 'jm2g', name: '卷膜2关', op: '卷膜2开'},
'jm2g': {opposite: 'jm2k', name: '卷膜2开', op: '卷膜2关'},
'jm3k': {opposite: 'jm3g', name: '卷膜3关', op: '卷膜3开'},
'jm3g': {opposite: 'jm3k', name: '卷膜3开', op: '卷膜3关'}
};
// case
if (!tipMap[type]) return;
const {opposite, name, op} = tipMap[type];
//
if (status === 1 && this.status[opposite] === 1) {
this.$modal.msgError(`${this.selectedText}${name}在运行状态,不能运行${op}操作!`);
return;
}
// ========== ==========
this.connected = mqttUtil.getMqttState().isConnected;
if (!this.connected) {
this.$modal.msgError("设备连接异常");
return;
}
if (this.value === 1) {
this.$modal.msgError("设备控制失败!");
return;
}
uni.showModal({
title: '操作提示:',
content: '确定' + (status === 1 ? "运行" : "暂停") + '【' + this.selectedText + '】设备?',
cancelText: '取消',
confirmText: '确定',
success: (res) => {
if (res.confirm) {
//
this.message = JSON.stringify({[`${type}1`]: status})
//
this.$emit("publicMsg", this.message)
//todo
// this.testAuto(type);
}
}
})
},
closeModalDataName() {
this.$refs.inputNamelog.close()
},
close() {
this.$refs.inputDialog.close()
},
//
//
confirmModifyTime: function () {
const funcMsg = "该功能用来设置设备运行时间及设备别名,设备运行时间即开启设备后到达运行时间自行暂停设备;设备别名不填则展示默认设备名称"
if ((store.getters && store.getters.name !== 'admin')
&& this.$auth.hasRole("test")) {
// this.testFunction(`${this.testMsg}\n${funcMsg}`)
this.$refs?.inputDialog?.close();
return;
}
// 1. this.xxx
let {
newLimitTime, currentCardTime, selectedText, currentCard,
limitTimes, dtu_remark, imei, agriId, remark
} = this;
// 2.
/*if (!newLimitTime || newLimitTime < 1 || newLimitTime > 60) {
uni.showToast({ title: '请输入1-60的有效数字', icon: 'none' });
return;
}*/
// 3.
const isLimitTimeModified = newLimitTime !== currentCardTime; //
remark = remark || currentCard.name;
const isRemarkModified = (remark !== (dtu_remark[currentCard.type] || currentCard.name)); //
// 4.
if (!isLimitTimeModified && !isRemarkModified) {
// /
this.$refs?.inputDialog?.close();
return;
}
// 5.
let confirmContent = `确定修改${selectedText}-${currentCard.name}`;
const tips = [];
if (isLimitTimeModified) tips.push(`运行时间为:${newLimitTime}`);
if (isRemarkModified) {
tips.push(`别名设置为:${remark}`)
}
confirmContent += `\n${tips.map((tip, idx) => `${idx + 1}. ${tip}`).join('\n')}`;
// 6. async/await
uni.showModal({
title: '温馨提示:',
content: confirmContent,
cancelText: '取消',
confirmText: '确定',
success: async (res) => {
// /
this.$refs?.inputDialog?.close();
if (!res.confirm) return; //
let isAllSuccess = false; //
try {
// 7.
if (isLimitTimeModified) {
this.$set(limitTimes, `${currentCard.type}Limit`, newLimitTime || 0);
//
if (!limitTimes.imei) {
this.$set(limitTimes, 'imei', imei);
this.$set(limitTimes, 'agriName', selectedText);
this.$set(limitTimes, 'agriId', agriId);
}
// async/await
const limitRes = limitTimes.id
? await updateLimit(limitTimes)
: await addLimit(limitTimes);
if (limitRes.code === 200) {
isAllSuccess = true;
this.getAgriByImei();
}
}
// 8.
if (isRemarkModified) {
this.$set(dtu_remark, currentCard.type, remark);
// imei
if (!dtu_remark.imei) {
this.$set(dtu_remark, 'imei', imei);
}
//
const remarkRes = dtu_remark.id
? await updateRemark(dtu_remark)
: await addRemark(dtu_remark);
if (remarkRes.code === 200) {
this.$emit("getRemark")
isAllSuccess = true;
}
}
// 9.
this.$modal[isAllSuccess ? 'msgSuccess' : 'msgError'](
isAllSuccess ? '修改成功' : '修改失败'
);
} catch (error) {
// /
console.error('修改失败:', error);
this.$modal.msgError('修改失败,请重试');
}
}
});
},
confirmModifyName() {
const funcMsg = "该功能用来设置温湿度卡片别名,如若不填则展示默认备注"
if ((store.getters && store.getters.name !== 'admin')
&& this.$auth.hasRole("test")) {
// this.testFunction(`${this.testMsg}\n${funcMsg}`)
this.$refs?.inputNamelog?.close();
return;
}
// 1. sensorCardkey/label
let {
selectedText,
dtu_remark,
imei,
sensorCard: {key: sensorKey, label: sensorLabel},
remark
} = this;
remark = remark || sensorLabel;
// 2.
const isRemarkModified = remark !== (dtu_remark[sensorKey] || sensorLabel);
if (!isRemarkModified) {
//
this.$refs?.inputNamelog?.close();
return;
}
// 3. async/await
uni.showModal({
title: '温馨提示:',
content: `确定将${selectedText}-${sensorLabel}别名设置为:${remark}`, // remark
cancelText: '取消',
confirmText: '确定',
success: async (res) => {
// /
this.$refs?.inputNamelog?.close();
if (!res.confirm) return; //
try {
// 4. loading
// uni.showLoading({ title: '...' });
// 5.
this.$set(dtu_remark, sensorKey, remark); // sensorKey
// imei
if (!dtu_remark.imei) {
this.$set(dtu_remark, 'imei', imei);
}
// 6. await
const remarkRes = dtu_remark.id ? await updateRemark(dtu_remark) : await addRemark(dtu_remark);
// 7.
const isSuccess = remarkRes.code === 200;
this.$modal[isSuccess ? 'msgSuccess' : 'msgError'](
isSuccess ? '修改成功' : '修改失败'
);
// 8.
if (isSuccess) {
this.$emit("getRemark")
}
} catch (error) {
// /
console.error('别名修改失败:', error);
this.$modal.msgError('修改失败,请重试');
} finally {
// /loading
// uni.hideLoading();
}
}
});
},
getAgriByImei() {
getAgriByImei(this.imei).then(response => {
if (response.code === 200) {
if (!response.data) {
return;
}
this.limitTimes = response.data;
}
})
},
}
};
</script>
<style scoped>
</style>
<style lang="scss" scoped>
/* 2列栅格布局 */
.card-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20rpx;
padding: 20rpx;
}
/* 卡片样式 */
.control-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 2rpx 8rpx #bfbec1;
}
/* 卡片文字区域 */
.card-text {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.card-main {
font-size: 30rpx;
color: #333;
font-weight: 500;
}
.card-sub {
font-size: 24rpx;
color: #999;
}
/* 保留原有样式,仅修改/新增以下部分 */
.card-sub-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
gap: 10rpx;
/* 新增:给容器加最小宽度,确保所有卡片一致 */
min-width: 0;
}
.limit-time {
font-size: 24rpx;
color: #999;
/* 可选:加固定左边距,确保和暂停的间距统一 */
margin-left: 0rpx;
}
/* 卡片图标容器 */
.card-icon {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
background-color: #e5e5e5;
display: flex;
align-items: center;
justify-content: center;
}
/* 激活状态(运行) */
.card-icon.active {
background-color: #007aff;
}
.uni-px-5 {
padding-left: 20rpx;
padding-right: 20rpx;
}
.uni-pb-5 {
padding-bottom: 15rpx;
}
.text {
width: 50rpx;
margin: 10rpx 10rpx 8rpx 0;
padding: 0;
height: 70rpx;
line-height: 70rpx;
text-align: center;
font-size: 26rpx;
box-shadow: 0 2rpx 8rpx #bfbec1
}
.tempStyle, .humiStyle {
display: inline-block;
font-size: 12px;
}
.text:first-child {
margin-left: 10rpx;
}
.uni-view {
-webkit-flex: 1;
flex: 1;
height: 150rpx;
-webkit-justify-content: center;
justify-content: center;
-webkit-align-items: center;
align-items: center;
}
.data {
font-size: 13px;
display: inline-block;
line-height: 23px;
color: #3a3a3a;
}
.data:nth-child(even) {
font-size: 12px;
margin-top: 3px;
color: #9c9c9c;
}
/deep/ .uni-section-header__slot-right {
color: green;
}
/* 新增:弹窗样式 */
.modal-container {
width: 600rpx;
background: #fff;
border-radius: 16rpx;
padding: 30rpx 20rpx;
}
.modal-title {
font-size: 30rpx;
font-weight: 500;
text-align: center;
margin-bottom: 40rpx;
color: #333;
}
.modal-input-wrap {
display: flex;
align-items: center;
margin-bottom: 35rpx;
font-size: 26rpx;
margin-left: 20rpx;
}
.modal-label {
width: 160rpx;
color: #666;
}
.modal-current {
margin-left: 14rpx;
color: #333;
}
.modal-input {
height: 60rpx;
border: 1px solid #eee;
border-radius: 8rpx;
padding: 0 15rpx;
font-size: 26rpx;
width: 80rpx;
}
.modal-unit {
margin-left: 10rpx;
color: #666;
}
.modal-btn-wrap {
display: flex;
gap: 20rpx;
margin-top: 40rpx;
}
.modal-btn {
flex: 1;
height: 70rpx;
border-radius: 8rpx;
font-size: 26rpx;
}
.modal-btn.cancel {
background: #f5f5f5;
color: #666;
}
.modal-btn.confirm {
background: #007aff;
color: #fff;
}
.icon {
margin-left: 15rpx
}
/deep/ .is-input-border {
width: 340rpx;
}
.card {
border-radius: 20rpx;
overflow: hidden;
}
</style>

View File

@ -314,7 +314,7 @@ export default {
{ {
imei:item.imei, imei:item.imei,
agriName:item.agriName, agriName:item.agriName,
agriId:item.agriId agriId:item.id
} }
); );
this.$tab.navigateTo('/pages/home/control/index?agriInfo='+encodeURIComponent(agri)) this.$tab.navigateTo('/pages/home/control/index?agriInfo='+encodeURIComponent(agri))