agri-app/pages/home/control/automatic.vue

1482 lines
47 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view>
<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, 0)">
<!-- 加@click.stop防止冒泡触发卡片点击的弹窗事件 -->
<uni-icons
:type="status[card.type] === 1 ? 'circle' : 'circle-filled'"
size="24"
color="#fff"
/>
</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>
参数设置完成请点击 <button @click="saveAutoTerm" style="margin-right: 10rpx" class="cu-btn bg-cyan shadow">保存</button>
</template>
<!--自动化设置-->
<view class="film-roller">
<tn-subsection :list="filmRollerList"
:current="current"
:bold="true"
@change="switchTab"
:height="80"></tn-subsection>
<swiper class="swiper" :disable-touch="true" :current="current" :acceleration="true"
@change="change" :style="{ height: swiperHeight + 'rpx' }">
<swiper-item v-for="(roller, key) in filmRollerList"
@touchmove.stop
:key="key">
<view >
<view class="param-setting">
<tn-button padding="0 20rpx" :plain="true" margin="10rpx 10rpx" @click="showSelect=true">
参考温度:{{ termList[current]['config'].refTemp }}
<uni-icons class="temp-dialog" type="down"/>
</tn-button>
<view @click="openParamDialog" class="param-dialog">
<tn-button padding="0 20rpx" :plain="true" margin="10rpx 10rpx" >
参数设置弹窗
</tn-button>
</view>
</view>
<!-- F8F7F8-->
<view class="term-content">
<uni-swipe-action :key="termList[key]['terms'].length">
<uni-swipe-action-item class="swiper-custom" v-for="(item, index) in termList[key].terms"
:key="item.id" :index="index" @click="onDeleteItem($event, item.id)" :right-options="options">
<view class="message">
<view class="message__left">
<view class="message__tag">温度控制</view>
<view class="message__content tn-text-ellipsis">第{{ index+1 }}段</view>
<!-- <tn-avatar shape="square" size="lg" src="https://resource.tuniaokj.com/images/avatar/xiaomai1.jpg"></tn-avatar>-->
</view>
<view class="message__middle" @click="changeTime('startTime',index)">
<view class="message__name">运行时间开始</view>
<view class="message__content tn-text-ellipsis">{{ item.startTime || '请选择时间' }}</view>
</view>
<view class="message__middle" @click="changeTime('endTime',index)">
<view class="message__name">运行时间终止</view>
<view class="message__content tn-text-ellipsis">{{ item.endTime || '请选择时间' }}</view>
</view>
<!-- <tn-button backgroundColor="#01BEFF" :plain="true" class="message__right" width="150rpx" height="100rpx"-->
<!-- :fontSize="40" shape="icon" margin="10rpx 10rpx">-->
<!-- 替换温度按钮 -->
<view
class="message__right"
@click.stop="openSlider('temp', index)"
>
<view class="message__name">温度</view>
<view class="message__content tn-text-ellipsis">{{ formatValueWithUnit(item.temp, '℃') }}</view>
</view>
<!-- 替换风口按钮 -->
<view
class="message__right"
@click.stop="openSlider('vent', index)"
>
<view class="message__name">风口</view>
<view class="message__content tn-text-ellipsis">{{ formatValueWithUnit(item.vent, 'cm') }}</view>
</view>
<!-- <view class="message__tag">阶段{{index+1}}</view>-->
</view>
</uni-swipe-action-item>
</uni-swipe-action>
</view>
<view class="copy-term" @click="copyTerm"
v-if="termList[key]['terms'].length > 0">
一键同步卷膜所有配置
</view>
<view class="add-term">
<uni-icons color="var(--cyan)" type="plus" size="40" @click="addTerm"/>
<text @click="addTerm">添加条件</text>
</view>
</view>
</swiper-item>
</swiper>
</view>
</uni-section>
<tn-picker mode="time" v-model="selectTime" :params="timeParams" @confirm="confirmTime"></tn-picker>
<tn-select title="请选择参考温度" :safeAreaInsetBottom="true" :searchShow="false" v-model="showSelect" mode="single" :list="tempList" @confirm="confirmTemp"></tn-select>
<uni-popup :mask-click="false" 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;text-align: left" 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>
<uni-popup :mask-click="false" ref="autoParam" mode="center">
<!-- 新增:修改运行时间的弹窗 -->
<view class="modal-container_">
<view class="modal-title">{{ `【${selectedText} - ${filmRollerList[current]}】风口校准`}}</view>
<uni-forms :model="rollerParam" ref="form" :labelWidth="130" >
<uni-forms-item label="当前卷膜:" prop="refTemp" v-if="false">
<uni-easyinput type="text" placeholderStyle="font-size: 25rpx;" v-model="rollerParam.imei"
disabled :inputBorder="false" :clearable="false"/>
</uni-forms-item>
<uni-forms-item label="当前卷膜:" prop="refTemp" v-if="false">
<uni-easyinput type="text" placeholderStyle="font-size: 25rpx;" v-model="rollerParam.roller"
disabled :inputBorder="false" :clearable="false"/>
</uni-forms-item>
<uni-forms-item label="参考温度:" prop="refTemp" v-if="false">
<uni-easyinput type="text" placeholderStyle="font-size: 25rpx;" v-model="rollerParam.refTemp"
disabled :inputBorder="false" :clearable="false"/>
</uni-forms-item>
<uni-forms-item label="计算风口长度:" prop="autoTotalLen" :labelWidth="124">
<uni-easyinput type="number" placeholderStyle="font-size: 25rpx;" v-model="rollerParam.autoTotalLen"
placeholder="风口校准完成自动写入" disabled
:inputBorder="false" :clearable="false">
<template #right>
<view>cm</view>
</template>
</uni-easyinput>
</uni-forms-item>
<view class="card-grid">
<view
class="control-card"
v-for="card in paramCard"
:key="card.type">
<view class="card-text">
<text class="card-main" >{{ card.name }}</text>
</view>
<view class="card-icon" :class="{ active: status[card.type] === 1 }"
@click.stop="handleCardClick(1 - status[card.type], card.type, 1)">
<!-- 加@click.stop防止冒泡触发卡片点击的弹窗事件 -->
<uni-icons
:type="status[card.type] === 1 ? 'circle' : 'circle-filled'"
size="24"
color="#fff"
/>
</view>
</view>
</view>
<uni-forms-item label="手动设置:" prop="manualTotalLen" :labelWidth="95">
<uni-easyinput type="number" placeholderStyle="font-size: 25rpx;" v-model="rollerParam.manualTotalLen"
placeholder="可在此处手动校准风口长度"
:inputBorder="false" :clearable="false">
<template #right>
<view>cm</view>
</template>
</uni-easyinput>
</uni-forms-item>
<uni-forms-item label="预留风口长度:" prop="reservedLen" :required="true" :labelWidth="135">
<uni-easyinput type="number" placeholderStyle="font-size: 25rpx;" v-model="rollerParam.reservedLen"
placeholder="请输入预留风口长度"
:inputBorder="false" :clearable="false">
<template #right>
<view>cm</view>
</template>
</uni-easyinput>
</uni-forms-item>
</uni-forms>
<view class="modal-btn-wrap">
<button class="modal-btn cancel" @click="closeParamDialog"></button>
<button class="modal-btn confirm" @click="setAutoParam"></button>
</view>
</view>
</uni-popup>
<uni-popup :mask-click="false" ref="sliderDialog" mode="center">
<view class="modal-container">
<view class="modal-title">{{ `${selectedText} - 自动化】条件设置` }}</view>
<!-- 核心修改给容器加 flex 布局样式 -->
<view class="modal-slider">
<text class="slider-label">{{ slider.title }}</text>
<slider
:value="slider.value"
:min="slider.min"
:max="slider.max"
:show-value="false"
class="slider-component"
@change="changeSlider"
/>
<input
class="modal-input"
align="center"
type="number"
v-model.number="slider.value"
/>
<text class="slider-unit">{{ slider.unit }}</text>
</view>
<view class="modal-btn-wrap">
<button class="modal-btn cancel" @click="closeSlider"></button>
<button class="modal-btn confirm" @click="confirmSlider"></button>
</view>
</view>
</uni-popup>
<tn-modal
v-model="showTip"
title="温馨提示"
content="卷膜参考温度设置存在重复,请确认是否保存?"
:button="[
{
text: '取消',
backgroundColor: '#dcdbdb',
fontColor: '#FFFFFF'
},
{
text: '确定',
backgroundColor: '#42b3ff',
fontColor: '#FFFFFF'
}
]"
:showCloseBtn="true"
:maskCloseable="false"
@click="showTips"
/>
<tn-fab
:btnList="content"
left="auto"
right="40"
bottom="100"
width="88"
height="88"
iconSize="64"
backgroundColor="#01BEFF"
fontColor="#FFFFFF"
icon="plusempty"
animationType="up"
@click="showData"
/>
<uni-popup ref="showHistoryData" type="dialog" mode="center">
<view class="modal-container popup">
<history-data ref="historyDataRef"></history-data>
</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, updateLimit} from "../../../api/system/assets/limit";
import store from "../../../store";
import {addRemark, updateRemark} from "../../../api/system/assets/remark";
import {generateUniqueId} from "../../../utils/agri";
import UniSection from "../../../components/uni-section/uni-section.vue";
import UniFormsItem from "../../../uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue";
import { checkTimeConflict } from "../../../utils/agri"
import {getAgriTerm, saveAgriTerm} from "../../../api/control/autoTerm";
import HistoryData from "../../data/data.vue";
export default {
options: {
styleIsolation: 'shared'
},
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 {}
}
},
ventTotalLen: {
type: Number,
default: 0
},
limitTimes: {
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
}
}
},
// 监听 current 变化,切换 tab 时也重新计算高度
current: {
handler() {
this.$nextTick(() => {
this.getSwiperHeight();
});
},
immediate: true
},
ventTotalLen: {
deep: true, // 监听对象内部属性变化(防止数据是对象时监听不到)
immediate: true, // 关键:进入组件就执行,不用等数据变化
handler(newVal) {
// 确保数据有值后执行方法
if (newVal) {
this.rollerParam.autoTotalLen = newVal;
}
}
}
},
components: {
UniFormsItem,
UniSection,
HistoryData,
UniPopup // 注册弹窗组件
},
mounted() {
this.refresh();
},
data() {
return {
fontStyle: '',
deviceCards: [
{type: 'jbk', name: '卷被开'},
{type: 'jbg', name: '卷被关'},
{type: 'jlk', name: '卷帘开'},
{type: 'jlg', name: '卷帘关'},
],
paramCard: [
{type: '', name: '卷膜开'},
{type: '', name: '卷膜关'}
],
// 优化:语义化变量名(替换原 hide: false
showStatusText: false,
showFlag: true,
remark: '',
// 新增:弹窗相关变量
currentCard: {}, // 当前点击的卡片信息
currentCardTime: '', // 当前卡片的运行时间
newLimitTime: 0, // 新的运行时间
message: {},
// 优化:声明响应式变量 connected
connected: false,
imei: '',
current: 0,
filmRollerList:["卷膜1", "卷膜2", "卷膜3"],
tempList: [
{
value: 'temp1',
label: '温度1'
},
{
value: 'temp2',
label: '温度2'
},
{
value: 'temp3',
label: '温度3'
},
{
value: 'temp4',
label: '温度4'
}
],
showSelect:false,
demo: {
config: [
{
refTemp: '请选择',
refTempCode: null,
autoTotalLen: null,
manualTotalLen: null,
reservedLen: null,
roller:'jm1'
},
{
refTemp: '请选择',
refTempCode: null,
autoTotalLen: null,
manualTotalLen: null,
reservedLen: null,
roller:'jm2'
},
{
refTemp: '请选择',
refTempCode: null,
autoTotalLen: null,
manualTotalLen: null,
reservedLen: null,
roller:'jm3'
}
],
terms: [
[/* 卷膜1的条件列表 */],
[/* 卷膜2的条件列表 */],
[/* 卷膜3的条件列表 */]
]
},
termList: [
{
terms: [],
config: {
refTemp: '请选择',
refTempCode: null,
autoTotalLen: null,
manualTotalLen: null,
reservedLen: null,
roller:'jm1'
}
},
{
terms: [],
config:{
refTemp: '请选择',
refTempCode: null,
autoTotalLen: null,
manualTotalLen: null,
reservedLen: null,
roller:'jm2'
}
},
{
terms: [],
config:{
refTemp: '请选择',
refTempCode: null,
autoTotalLen: null,
manualTotalLen: null,
reservedLen: null,
roller:'jm3'
}
}
],
options: [
{
text: '删除',
icon: 'delete',
style: {
backgroundColor: '#E83A30'
}
}
],
swiperHeight: 720, // 用于动态设置 swiper 高度
timeParams: {
year: false,
month: false,
day: false,
hour: true,
minute: true,
second: false
},
selectTime: false,
timeTag: null,
rollerIndex: null,
defaultTime: null,
maxTermLength: 5,
autoParam:false,
slider: {
title: '请选择',
min:0,
max:100,
value:0,
unit:'',
mode: null
},
rollerParam: {
imei: this.value,
roller: null,
refTemp: '请选择',
autoTotalLen: null,
manualTotalLen: null,
reservedLen: null,
ventTotalLen: null
},
showTip:false,
pattern: {
color: '#7A7E83',
backgroundColor: '#fff',
selectedColor: '#007AFF',
buttonColor: '#007AFF',
iconColor: '#fff'
},
content: [{
iconPath: '/static/image.png',
selectedIconPath: '/static/image-active.png',
text: '温度',
active: false
}]
};
},
methods: {
// 滑动切换卷膜
change(e) {
this.current =e.detail.current
},
// 切换卷膜
switchTab(e) {
this.current=e.index
},
// 左上角选择时间
confirmTemp(event) {
this.termList[this.current]['config'].refTemp = event[0]['label']
this.termList[this.current]['config'].refTempCode = event[0]['value']
},
// 下拉刷新以及进入组件渲染
refresh() {
this.getAgriTerm();
this.showFlag = !((store.getters && store.getters.name !== 'admin') && this.$auth.hasRole("test"))
},
getAgriTerm() {
getAgriTerm(this.imei).then(response => {
if (response.code === 200) {
this.termList = response.data;
}
if (this.termList && this.termList.length>0) {
return;
}
this.termList = [];
for (const [index,value] of this.filmRollerList.entries()) {
this.termList.push({
terms: [],
config: {
refTemp: '请选择',
refTempCode: null,
autoTotalLen: null,
manualTotalLen: null,
reservedLen: null,
roller: `jm${index+1}`,
imei: this.value
}
})
}
}).catch(err => {
}).finally(() => {
this.$nextTick(() => {
this.getSwiperHeight(); // 页面初始化时也计算一次
});
});
},
// --------------------共同逻辑------------------------------
// 修改运行时间以及设备备注弹窗
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, tag) {
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;
}
let content = `确定 ${status === 1 ? "运行" : "暂停"}${this.selectedText} - ${op}】设备?`;
let emitFunction = "publicMsg";
if (tag === 1) {
if (type.slice(-1) === 'k' && status === 1) {
content = `确定 ${status === 1 ? "运行" : "暂停"}${this.selectedText} - ${op}】之前已将该卷膜关到最低?`
}
emitFunction = "sendSettingMsg";
}
uni.showModal({
title: '操作提示:',
content: `${content}`,
cancelText: '取消',
confirmText: '确定',
success: (res) => {
if (res.confirm) {
// 组装消息
this.message = JSON.stringify({[`${type}1`]: status})
this.$emit(emitFunction, this.message);
//todo
// this.testAuto(type);
}
}
})
},
// 修改运行时间以及备注 点击关闭
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.$emit("getAgriLimit")
}
}
// 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('修改失败,请重试');
}
}
});
},
// --------------------共同逻辑------------------------------
// 添加条件更新窗口高度
updateSwiperHeight() {
if (this.termList[this.current]['terms'].length >= 3) {
// 加上 param-setting 和 add-term 的高度,以及一些间距
this.swiperHeight = this.swiperHeight + 160;
}
},
// 获取窗口高度
getSwiperHeight() {
this.swiperHeight = 720;
var length = this.termList[this.current]['terms'].length-2;
if (length>0) {
this.swiperHeight = this.swiperHeight + 160*length ;
}
},
// 一键复制卷膜所有配置到其他卷膜
copyTerm() {
var temps = this.termList[this.current]['terms'];
var config = this.termList[this.current]['config'];
if (temps.length <= 0) return;
uni.showModal({
title: '操作提示:',
content: '确定复制当前卷膜配置到其他卷膜?',
cancelText: '取消',
confirmText: '确定',
success: (res) => {
if (res.confirm) {
for (const [index,value] of this.filmRollerList.entries()) {
if (index === this.current) continue;
// this.termList[index]['terms'] = [ ...temps ];
// this.termList[index]['config']= { ...config };
this.termList[index]['terms'] = JSON.parse(JSON.stringify(temps));
this.termList[index]['config']= JSON.parse(JSON.stringify(config));
var terms = this.termList[index]['terms'];
terms.map(item => {
item.id = generateUniqueId();
item.roller = `jm${index+1}`;
})
this.termList[index]['config']['id']=null;
this.termList[index]['config']['roller']=`jm${index+1}`;
}
}
}
})
},
// 添加条件方法
addTerm() {
var temps = this.termList[this.current]['terms'];
if (temps.length >= this.maxTermLength) {
this.$modal.alert(`您设置的条件数量已超过上限!上限为${this.maxTermLength}条,请删除后重试!`,
"操作提示");
return;
}
const index = this.current + 1
this.termList[this.current]['terms'].push({
id: generateUniqueId(),
startTime: null,
endTime: null,
temp: 0,
vent: 0,
imei: this.value,
roller: `jm${index}`
});
this.$nextTick(() => {
this.updateSwiperHeight(); // 数据更新后,在下一个 tick 更新高度
});
},
// 格式化带单位的数值(通用方法,支持温度/风口等所有字段)
formatValueWithUnit(value, unit) {
// 容错如果value是null/undefined默认显示“选择”
if (!value || value === '选择') return '选择';
// 确保value是字符串/数字,避免拼接出错
return `${value}${unit}`;
},
// 删除条件
onDeleteItem(event, id) {
// this.rollerIndex = null;
const currentKey = this.current;
// 1. 先拿到当前页签的 terms 数组
const currentTerms = this.termList[currentKey]?.terms || [];
// 2. 过滤出要保留的项
const newTerms = currentTerms.filter(item => item.id !== id);
// 3. 只更新当前页签的 terms保持 termList 结构不变
this.$set(this.termList[currentKey], 'terms', newTerms);
// 4. 更新swiper高度可选
this.$nextTick(() => {
this.getSwiperHeight();
uni.$emit('uni-swipe-action-hide');
});
},
// 选择运行时间
changeTime(timeTag, timeIndex) {
this.timeTag = timeTag;
this.rollerIndex = timeIndex;
this.selectTime = true;
},
openParamDialog() {
var config = this.termList[this.current].config;
this.rollerParam = JSON.parse(JSON.stringify(config));
this.paramCard = [
{type: `${config.roller}k`, name: '卷膜开'},
{type: `${config.roller}g`, name: '卷膜关'}
]
this.$refs.autoParam.open();
},
// 卷膜参数设置
setAutoParam() {
// 2. 优化校验逻辑
const isValid = this.rollerParam.manualTotalLen > 0 || this.rollerParam.autoTotalLen > 0;
if (!isValid) {
this.$modal.alert(`计算风口总长和手动设置风口长度至少填写一个!`,
"操作提示");
return;
}
// 300/2.13
const isValidTotalLen = this.rollerParam.manualTotalLen > 300 || this.rollerParam.autoTotalLen > 300;
if (isValidTotalLen) {
this.$modal.alert(`风口长度设置不能大于300cm`,
"操作提示");
return;
}
if (!(this.rollerParam.reservedLen && this.rollerParam.reservedLen > 0)) {
this.$modal.alert(`请填写预留风口!`,
"操作提示");
return;
}
this.rollerParam.ventTotalLen = this.rollerParam.autoTotalLen;
if (this.rollerParam.manualTotalLen > 0) {
this.rollerParam.ventTotalLen = this.rollerParam.manualTotalLen;
}
this.termList[this.current]['config'] = {...this.rollerParam}
this.$refs.autoParam.close();
},
closeParamDialog() {
this.$refs.autoParam.close();
},
// 选择时间串口点击确定
confirmTime(time) {
this.termList[this.current]['terms'][this.rollerIndex][this.timeTag] = `${time.hour}:${time.minute}`
},
/**
* @param tag 参数
* @param index 条件
*/
openSlider(tag, index) {
var fillerTerm = this.termList[this.current]['terms'][index];
if (tag === 'temp') {
this.slider = {
title: '适宜温度',
min: 0,
max: 40,
unit: '℃',
value: fillerTerm.temp,
mode: tag
}
} else if (tag === 'vent') {
this.slider = {
title: '风口大小',
min: 0,
max: 30,
unit: 'cm',
value: fillerTerm.vent,
mode: tag
}
}
this.rollerIndex = index;
this.$refs.sliderDialog.open();
},
changeSlider(e) {
this.slider.value = e.detail.value
},
closeSlider() {
this.$refs.sliderDialog.close();
},
confirmSlider() {
const slideMode = this.slider.mode;
const value = this.slider.value;
if (slideMode === 'temp') {
this.termList[this.current]['terms'][this.rollerIndex].temp = value;
} else if (slideMode === 'vent') {
this.termList[this.current]['terms'][this.rollerIndex].vent = value;
}
this.$refs.sliderDialog.close();
},
// 保存自动化条件
saveAutoTerm() {
const termMap = this.termList;
const rollerList = [];
let showTips = null;
const checkRules = [
{
tip: '请设置自动化条件后重新保存重试!',
validate: (rollerParam, term) => rollerParam.refTemp === '请选择' && !(term && term.length > 0)
},
{
tip: '参考温度未设置,请点击相应页签左上角设置后重试!',
validate: (rollerParam, term) => rollerParam.refTemp === '请选择'
},
{
tip: '计算风口总长和手动设置风口长度至少填写一个!请填写后重试!',
validate: (rollerParam, term) => !((rollerParam.manualTotalLen && rollerParam.manualTotalLen > 0) ||
(rollerParam.autoTotalLen && rollerParam.autoTotalLen > 0))
},
{
tip: '风口长度设置不能大于300cm',
validate: (rollerParam, term) => (rollerParam.manualTotalLen && rollerParam.manualTotalLen > 300) ||
(rollerParam.autoTotalLen && rollerParam.autoTotalLen > 300)
},
{
tip: '预留风口长度未设置,请点击相应页签右上角设置后重试!',
validate: (rollerParam, term) => !(rollerParam.reservedLen && rollerParam.reservedLen > 0)
},
{
tip: '温度控制未设置,请设置后重新尝试!',
validate: (rollerParam, term) => !(term && term.length > 0)
},
{
tip: '温度控制设置条数超过上限!请调整后重试',
validate: (rollerParam, term) => term && term.length > 5
},
{
tip: '温度控制运行时间填写不完整,请检查填写后重新尝试!',
validate: (rollerParam, term) => term.some(item => !(item.startTime && item.startTime!=='请选择时间' &&
item.endTime && item.endTime !== '请选择时间'))
},
{
tip: '温度控制运行时间起不能大于止,请检查填写后重新尝试!',
validate: (rollerParam, term) => term.some(item => (item.startTime > item.endTime))
},
{
tip: '温度控制运行时间存在冲突,请检查填写后重新尝试!',
validate: (rollerParam, term) => {
// 调用时间冲突判断函数
const { isConflict } = checkTimeConflict(term);
return isConflict; // 有冲突 → 返回true校验失败
}
},
{
tip: '温度控制适宜温度填写不完整,请填写后重新尝试!',
validate: (rollerParam, term) => term.some(item => !(item.temp && item.temp>0))
},
{
tip: '温度控制风口开合大小填写不完整,请填写后重新尝试!',
validate: (rollerParam, term) => term.some(item => !(item.vent && item.vent>0))
}
];
for (const [_, rule] of checkRules.entries()) {
for (const [index, item] of this.filmRollerList.entries()) {
const term = termMap[index]['terms']; // 温度条件
const rollerParam = termMap[index]['config']; // 参数设置
const filmRoller = this.filmRollerList[index];
if (!rollerParam) {
this.$tn.message.toast(
'参数设置失败,请下拉刷新后重试!',
false, null, 'none', 3000)
return;
} else if (rule.validate(rollerParam, term)) {
rollerList.push(filmRoller);
showTips = rule.tip;
}
}
if (rollerList.length>0) {
this.$tn.message.toast(`${rollerList.join("")}${showTips}`);
return;
}
}
console.info(`要保存的条件:`,termMap)
const hasDuplicate = this.termList
.filter(item => item.config.refTempCode) // 过滤空值
.reduce(({ map, isDup }, item) => {
const code = item.config.refTempCode;
if (map.has(code)) isDup = true; // 已有该key标记为重复
else map.set(code, true); // 无则存入map
return { map, isDup };
}, { map: new Map(), isDup: false })
.isDup;
if (hasDuplicate) {
this.showTip = true;
} else {
uni.showModal({
title: '操作提示:',
content: '确定保存自动化条件?',
cancelText: '取消',
confirmText: '确定',
success: (res) => {
if (res.confirm) {
this.saveAgriTerm();
}
}
})
}
},
saveAgriTerm() {
saveAgriTerm(this.termList).then(response => {
if (response.code===200) {
this.$modal.msgSuccess("自动化条件保存成功!")
} else {
this.$modal.msgError("自动化条件保存失败!请重试或联系客服!")
}
})
},
showTips(event) {
this.showTip = false;
if (event.index===1) {
this.saveAgriTerm();
}
},
showData() {
this.$refs.showHistoryData.open();
this.$nextTick(() => {
this.$refs.historyDataRef.getAgriList()
})
}
}
};
</script>
<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;
}
.uni-view {
-webkit-flex: 1;
flex: 1;
height: 150rpx;
-webkit-justify-content: center;
justify-content: center;
-webkit-align-items: center;
align-items: center;
}
/deep/ .uni-section-header__slot-right {
color: green;
}
/* 新增:弹窗样式 */
.modal-container_ {
width: 600rpx;
background: #fff;
border-radius: 16rpx;
padding: 30rpx 20rpx;
}
.popup {
width: 700rpx;
}
.modal-container_{
padding: 30rpx 40rpx;
.uni-forms-item:nth-child(1) {
margin-bottom: 16rpx;
}
.card-grid {
padding: 20rpx 0;
gap: 30rpx;
}
}
/* 核心样式:让子元素水平排列 */
.modal-slider {
display: flex;
align-items: center; /* 垂直居中对齐 */
padding: 10rpx 20rpx; /* 增加内边距,避免拥挤 */
gap: 10rpx; /* 元素之间的间距 */
}
/* 标题样式:固定宽度,避免挤压滑块 */
.slider-label {
flex: 0 0 auto; /* 不拉伸、不收缩 */
font-size: 28rpx;
white-space: nowrap; /* 防止标题换行 */
}
/* 滑块样式:占满剩余空间 */
.slider-component {
flex: 1; /* 自动填充剩余宽度 */
}
/* 单位样式:固定宽度 */
.slider-unit {
flex: 0 0 auto;
font-size: 28rpx;
margin-left: 5rpx;
}
.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;
}
.film-roller {
padding: 10rpx 20rpx 20rpx 20rpx;
}
.swiper {
box-sizing: border-box;
border: 2rpx solid rgb(248, 241, 241);
border-top: 0;
border-bottom-left-radius: 10rpx;
border-bottom-right-radius: 10rpx;
box-shadow: 8rpx 5rpx 10rpx rgb(233, 231, 239);
padding-top: 10rpx;
}
.add-term {
display: flex;
align-items: center; /* 垂直居中 */
gap: 4px; /* 图标和文字之间的间距 */
justify-content: center; /* 内部元素水平居中(可选) */
//padding: 10px 20px;
font-size: 40rpx;
color: #42b3ff;
margin-top: 50rpx;
//border: 1px solid var(--cyan);
//border-radius: 8px;
//background-color: #fff;
}
.copy-term {
margin: 10rpx;
padding: 0 17px;
font-size: 16px;
height: 100rpx;
color: rgb(255, 255, 255);
background-color: rgb(1, 190, 255);
box-shadow: rgba(1, 190, 255, 0.063) 3px 3px 4px;
border-radius: 10rpx;
justify-content: center;
align-items: center;
display: flex;
}
.param-setting {
display: flex;
align-items: center;
font-size: 28rpx;
color: #0a0a0a;
.tn-btn-class {
border-radius: 15rpx;
}
.temp-dialog {
margin-left: 10rpx;
}
.param-dialog {
margin-left: auto; /* 自动填充左侧空间,将按钮推到最右侧 */
}
}
.term-content {
margin: 20rpx;
}
.swiper-custom {
box-shadow: 8rpx 5rpx 10rpx #D9D9D9 !important;
//border: 2rpx solid #D9D9D9;
margin-bottom: 20rpx;
background: #F8F7F8;
border-radius: 12rpx;
}
.message {
padding: 10rpx;
display: flex;
flex-direction: row;
align-items: center;
text-align: center;
/* #ifdef MP-WEIXIN */
box-shadow: 8rpx 5rpx 10rpx #D9D9D9 !important;
//border: 2rpx solid #D9D9D9;
margin-bottom: 20rpx;
background: #F8F7F8;
border-radius: 12rpx;
/* #endif */
}
.message__left {
flex:3;
.message__tag {
font-size: 25rpx;
margin-bottom: 8rpx;
color: #42b3ff;
}
}
.message__middle {
flex: 4;
.message__name {
margin-bottom: 8rpx;
}
}
.message__name {
font-size: 24rpx;
color: #838383;
}
.message__right {
flex: 2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 150rpx;
height: 100rpx;
margin: 10rpx;
border-radius: 50rpx;
background-color: #fff;
box-shadow: 0 2rpx 4rpx #D9D9D9;
transition: all 0.2s ease;
}
// 点击反馈效果
.message__right:active {
background-color: #f5f5f5;
transform: scale(0.98);
}
//.message__tag {
// font-size: 24rpx;
// color: #838383;
// text-align: center;
//}
.message__content {
font-size: 27rpx;
color: #020e15;
}
/deep/ .uni-slider-handle-wrapper {
height: 10rpx !important;
}
/deep/ .uni-forms-item {
border-bottom: 1rpx solid #E5E5E5;
.uni-forms-item__label {
font-size: 28rpx;
color: #0a0a0a;
}
}
/deep/.modal-container_ .uni-input-input {
text-align: right !important;
font-size: 28rpx;
}
/deep/ .uni-easyinput__content-input {
text-align: right !important;
}
/deep/ .uni-swipe_button {
height: 140rpx;
border-radius: 10rpx;
box-shadow: 8rpx 5rpx 10rpx #D9D9D9 !important
}
</style>