Commit ec6ffe29 authored by wanghao's avatar wanghao

1 上料必须是 绑定状态。

2 解绑时 必须解绑 异常的后才能 一键解绑 托盘状态。
3 上传MES 后 先不删除历史数据。
4  历史界面 导出功能。 根据  托盘号,设备号
5 增加一个任务 去执行 继电器状态,脉冲状态,模组状态,SIM卡状态,网络状态 这几个状态的 单独读 以及 写,科强说网络需要监控两次。
6 继电器状态,脉冲状态,模组状态,SIM卡状态,网络状态 涉及到的 校验 以及记录都放开了,待联调。
parent 3d775b4d
......@@ -23,4 +23,9 @@ public class RoboticArmConstans {
* 老化流程第三阶段执行时间(单位:分钟)
*/
public static final String AGING_STAGE3_TIME = "agingStage3Time";
/**
* 老化流程第四阶段执行时间(单位:分钟)
*/
public static final String AGING_STAGE4_TIME = "agingStage4Time";
}
......@@ -22,14 +22,14 @@ public class PalletDeviceUploadHistory extends BaseEntity
private Long id;
/** 托盘id */
@Excel(name = "托盘id")
private Long trayId;
/** 托盘编号 */
@Excel(name = "托盘编号")
private String trayCode;
/** 绑定的设备编号 */
@Excel(name = "绑定的设备编号")
@Excel(name = "设备编号")
private String deviceCode;
/** 行 */
......@@ -59,7 +59,7 @@ public class PalletDeviceUploadHistory extends BaseEntity
private Date unbindingTime;
/** 0-预热;1-正常;3-传感器故障;4-报警;5-通讯故障 */
@Excel(name = "0-预热;1-正常;3-传感器故障;4-报警;5-通讯故障")
@Excel(name = "状态")
private String status;
/** 设置-年 */
......@@ -91,7 +91,7 @@ public class PalletDeviceUploadHistory extends BaseEntity
private String adjustmentZeroAd;
/** 合格;不合格 */
@Excel(name = "合格;不合格")
@Excel(name = "调零状态")
private String zeroStatus;
/** 标定AD */
......@@ -99,7 +99,7 @@ public class PalletDeviceUploadHistory extends BaseEntity
private String calibrationAd;
/** 合格;不合格 */
@Excel(name = "合格;不合格")
@Excel(name = "标定状态0-不合格;1-合格")
private String calibrationStatus;
/** 浓度值 */
......@@ -115,48 +115,56 @@ public class PalletDeviceUploadHistory extends BaseEntity
private Integer realTimeAd;
/** 实时AD状态;0-异常;1-正常 */
@Excel(name = "实时AD状态;0-异常;1-正常")
@Excel(name = "实时AD状态;0-异常;1-正常 ")
private String realTimeAdStatus;
/**
* 传感器校准浓度
*/
@Excel(name = "校准浓度")
private BigDecimal calibrationConcentration;
/**
* 传感器校准状态 4-正常,其他都是异常
* 0-预热;1-正常;3-传感器故障;4-报警;5-通讯故障; 只有是4的时候显示正常,其他的都是异常
*/
@Excel(name = "校准浓度状态;0-预热;1-正常;3-传感器故障;4-报警;5-通讯故障; 只有是4的时候显示正常")
private String calibrationConcentrationStatus;
/**
* 写自检状态 空 是没写 0-失败;1-成功
*/
@Excel(name = "写自检状态;0-失败;1-成功")
private Integer writeSelfCheckStatus;
/**
* 继电器状态 0:初始 1:动作
*/
@Excel(name = "继电器状态;0:初始 1:动作")
private Integer relayStatus;
/**
* 脉冲状态 0:初始 1:动作
*/
@Excel(name = "脉冲状态;0:初始 1:动作")
private Integer pulseStatus;
/**
* 模块状态 0:异常 1:正常
*/
@Excel(name = "模块状态;0:异常 1:正常")
private Integer moduleStatus;
/**
* SIM卡状态 0:异常 1:正常
*/
@Excel(name = "SIM卡状态;0:异常 1:正常")
private Integer simCardStatus;
/**
* 网络状态 0:异常 1:正常
*/
@Excel(name = "网络状态;0:异常 1:正常")
private Integer networkStatus;
public void setId(Long id)
{
......
......@@ -137,14 +137,16 @@ public class CalibrationResultEventHandler {
uploadMesResultHistoryService.insertUploadMesResultHistory(uploadMesResultHistory);
if(StringUtils.isNotBlank(result)) {
JSONObject jsonObject = JSON.parseObject(result);
if(jsonObject.getInteger("code") != 200) {
String data = jsonObject.getString("data");
if(StringUtils.isNotBlank(data)) {
processPalletDeviceUploadHistory(palletDeviceBindings,data);
} else {
// if(jsonObject.getInteger("code") != 200) {
// String data = jsonObject.getString("data");
// if(StringUtils.isNotBlank(data)) {
// processPalletDeviceUploadHistory(palletDeviceBindings,data);
// } else {
// directProcessPaalletDeviceUploadHistory(palletDeviceBindings);
// }
// }
// 20251210 领导说 先 保存所有历史数据
directProcessPaalletDeviceUploadHistory(palletDeviceBindings);
}
}
} else {
directProcessPaalletDeviceUploadHistory(palletDeviceBindings);
}
......
......@@ -501,9 +501,10 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
}
// 20251104 上料 不需要看托盘状态了
// if(!"0".equals(tTrayInfo.getfStatus())) {
// throw new RuntimeException("托盘未解绑,请联系管理员");
// }
// 20251210 上料 又需要看托盘状态了
if(!"4".equals(tTrayInfo.getfStatus())) {
throw new RuntimeException("托盘状态异常,请联系管理员");
}
TStoreyInfo tStoreyInfo = storeyInfoMapper.selectNearestFreeStorey();
if(tStoreyInfo != null) {
......
package com.zehong.system.task;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
......@@ -22,7 +21,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
......@@ -35,8 +33,8 @@ import java.util.stream.Collectors;
* @description 老化最终执行任务
*/
@Component
public class FinalExecutionJob implements Job {
private static final Logger log = LoggerFactory.getLogger(FinalExecutionJob.class);
public class AgingStageFourProcessJob implements Job {
private static final Logger log = LoggerFactory.getLogger(AgingStageFourProcessJob.class);
// -------------------------- 常量配置(统一管理,避免魔法值)--------------------------
// 超时控制:必须小于Cron周期(假设Cron为5分钟,这里留1分钟缓冲)
......
......@@ -31,8 +31,8 @@ import java.util.stream.Collectors;
*/
@Component
@DisallowConcurrentExecution // 禁止同一任务并行执行(必须保留)
public class DeviceCommunicationJob implements Job {
private static final Logger log = LoggerFactory.getLogger(DeviceCommunicationJob.class);
public class AgingStageOneProcessJob implements Job {
private static final Logger log = LoggerFactory.getLogger(AgingStageOneProcessJob.class);
// -------------------------- 常量配置(统一管理,避免魔法值)--------------------------
// 超时控制:必须小于Cron周期(假设Cron为5分钟,这里留1分钟缓冲)
......@@ -157,16 +157,18 @@ public class DeviceCommunicationJob implements Job {
// 3. 更新浓度值
binding.setConcentration(String.valueOf(result[0]));
// 4. 条件写入时间
if (result[1] == 1 || result[1] == 3 || result[1] == 4) {
// 重用之前的master连接进行写操作
master = Modbus4jUtils.createModbusMaster(ip, port);
// 4. 条件写入时间
if (result[1] == 1 || result[1] == 3 || result[1] == 4) {
writeCurrentTimeToDevice(master, deviceId, binding);
}
// 5. 写入自检让设备开始自检,跟 上面的状态没关系
writeSelfCheckStatus(master, deviceId, binding);
binding.setWriteTimeStatus("1");
// 5. 更新数据库
// 6. 更新数据库
palletDeviceBindingMapper.updatePalletDeviceBinding(binding);
log.debug("设备{}处理完成: ip={}, port={}, status={}", deviceId, ip, port, result[1]);
......@@ -214,6 +216,20 @@ public class DeviceCommunicationJob implements Job {
}
}
/**
* 写入自检状态 让设备开始自检
*/
private void writeSelfCheckStatus(ModbusMaster master, int deviceId,
PalletDeviceBinding binding){
// 20251206 写完时间写自检,写自检就在时间后边写就行,不管时间写不写成功
try {
Modbus4jUtils.writeRegister(master, deviceId, 15, (short) 1);
binding.setWriteSelfCheckStatus(1);
} catch (Exception e) {
binding.setWriteSelfCheckStatus(0);
}
}
/**
* 写入当前时间到设备
*/
......@@ -251,15 +267,6 @@ public class DeviceCommunicationJob implements Job {
log.error("设备{}时间写入异常", deviceId, e);
recordAlarmByBinding(binding, "设备时间写入异常: " + e.getMessage());
}
// 20251206 写完时间写自检,写自检就在时间后边写就行,不管时间写不写成功
try {
Modbus4jUtils.writeRegister(master, deviceId, 15, (short) 1);
binding.setWriteSelfCheckStatus(1);
} catch (Exception e) {
binding.setWriteSelfCheckStatus(0);
}
}
// -------------------------- 辅助方法(日志/告警)--------------------------
......
......@@ -36,8 +36,8 @@ import java.util.stream.Collectors;
*/
@Component
@DisallowConcurrentExecution // 禁止同一任务并行执行(必须保留)
public class PrepareFinalExecutionJob implements Job {
private static final Logger log = LoggerFactory.getLogger(DeviceCommunicationJob.class);
public class AgingStageThreeProcessJob implements Job {
private static final Logger log = LoggerFactory.getLogger(AgingStageThreeProcessJob.class);
// -------------------------- 常量配置(统一管理,避免魔法值)--------------------------
// 超时控制:必须小于Cron周期(假设Cron为5分钟,这里留1分钟缓冲)
......@@ -190,6 +190,23 @@ public class PrepareFinalExecutionJob implements Job {
binding.setStatus(result[1] +"");
}
// 处理 继电器状态,脉冲状态,模组状态,SIM卡状态,网络状态
if(result[10] == 1){
binding.setRelayStatus(1);
}
if(result[11] == 1){
binding.setPulseStatus(1);
}
if(result[12] == 1){
binding.setModuleStatus(1);
}
if(result[13] == 1){
binding.setSimCardStatus(1);
}
if(result[14] == 1){
binding.setNetworkStatus(1);
}
// 5. 更新数据库
palletDeviceBindingMapper.updatePalletDeviceBinding(binding);
......
......@@ -9,7 +9,6 @@ import com.zehong.system.task.DeviceCommJob.DeviceComm501Device2Job;
import com.zehong.system.task.DeviceCommJob.DeviceComm501Device3Job;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.utils.Key;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -19,9 +18,7 @@ import javax.annotation.Resource;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author lenovo
......@@ -74,6 +71,9 @@ public class DeviceTaskScheduler {
// 3. 创建5分钟开始读写时间的job
createHourlyCommunicationJob(fStoreyId);
// 4. 增加一个阶段 去处理 脉冲状态,模组状态,SIM卡状态,网络状态
createTworPortCommJobs(fStoreyId);
// 4. 创建71小时 执行的任务
prepareFinalExecutionJob(fStoreyId);
// 5. 创建72小时 最终任务
......@@ -223,6 +223,54 @@ public class DeviceTaskScheduler {
port, jobId, delayMin, nextFireTime);
}
/**
* 创建第二阶段任务
* @param fStoreyId 设备ID
*/
private void createTworPortCommJobs(Long fStoreyId) throws SchedulerException {
String jobId = "TWO_" + fStoreyId;
JobKey jobKey = new JobKey(jobId, JOB_GROUP);
TriggerKey triggerKey = new TriggerKey(jobId + "_TRIGGER", TRIGGER_GROUP);
// 先删除旧的触发器和任务(彻底清理)
if (scheduler.checkExists(triggerKey)) {
scheduler.unscheduleJob(triggerKey);
}
if (scheduler.checkExists(jobKey)) {
scheduler.deleteJob(jobKey);
}
JobDetail job = JobBuilder.newJob(AgingStageOneProcessJob.class)
.withIdentity(jobKey)
.usingJobData("fStoreyId", fStoreyId.toString())
.storeDurably(false)
.build();
String s = sysConfigService.directSelectConfigByKey(RoboticArmConstans.AGING_STAGE2_TIME);
int delayMin = 0;
if(StringUtils.isNotBlank(s)) {
delayMin = Integer.parseInt(s);
}
if(delayMin == 0) {
delayMin = 10;
}
Date executeTime = Date.from(Instant.now().plus(delayMin, ChronoUnit.MINUTES));
// 关键修复:使用StartAt而不是StartNow
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.forJob(jobKey)
.withDescription("设备" + fStoreyId + "第二阶段触发器,触发时间是:" + executeTime)
.startAt(executeTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withMisfireHandlingInstructionFireNow() // 错过立即执行
.withRepeatCount(0)) // 仅一次
.build();
Date nextFireTime = scheduler.scheduleJob(job, trigger);
log.info("通信任务[{}]创建成功,下次执行:{}", jobId, nextFireTime);
}
/**
* 1. 创建每5分钟执行的通信任务(核心优化:简化调度逻辑、调整Misfire策略)
*/
......@@ -239,7 +287,7 @@ public class DeviceTaskScheduler {
scheduler.deleteJob(jobKey);
}
JobDetail job = JobBuilder.newJob(DeviceCommunicationJob.class)
JobDetail job = JobBuilder.newJob(AgingStageOneProcessJob.class)
.withIdentity(jobKey)
.usingJobData("fStoreyId", fStoreyId.toString())
.storeDurably(false)
......@@ -259,20 +307,12 @@ public class DeviceTaskScheduler {
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.forJob(jobKey)
.withDescription("设备" + fStoreyId + "最终任务触发器,触发时间是:" + executeTime)
.withDescription("设备" + fStoreyId + "第一阶段触发器,触发时间是:" + executeTime)
.startAt(executeTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withMisfireHandlingInstructionFireNow() // 错过立即执行
.withRepeatCount(0)) // 仅一次
.build();
// CronTrigger trigger = TriggerBuilder.newTrigger()
// .withIdentity(triggerKey)
// .forJob(jobKey)
// .withDescription("设备" + fStoreyId + "写时间任务,触发时间是:" + executeTime)
// .withSchedule(CronScheduleBuilder.cronSchedule("0 0/3 * * * ?") // 改为3分钟
// .withMisfireHandlingInstructionDoNothing())
// .startAt(new Date()) // 显式设置开始时间:cite[1]
// .build();
Date nextFireTime = scheduler.scheduleJob(job, trigger);
log.info("通信任务[{}]创建成功,下次执行:{}", jobId, nextFireTime);
......@@ -295,19 +335,19 @@ public class DeviceTaskScheduler {
scheduler.deleteJob(jobKey);
}
JobDetail job = JobBuilder.newJob(PrepareFinalExecutionJob.class)
JobDetail job = JobBuilder.newJob(AgingStageThreeProcessJob.class)
.withIdentity(jobKey)
.usingJobData("fStoreyId", fStoreyId.toString())
.storeDurably(false)
.build();
String s = sysConfigService.directSelectConfigByKey(RoboticArmConstans.AGING_STAGE2_TIME);
String s = sysConfigService.directSelectConfigByKey(RoboticArmConstans.AGING_STAGE3_TIME);
int delayMin = 0;
if(StringUtils.isNotBlank(s)) {
delayMin = Integer.parseInt(s);
}
if(delayMin == 0) {
delayMin = 10;
delayMin = 15;
}
Date executeTime = Date.from(Instant.now().plus(delayMin, ChronoUnit.MINUTES));
......@@ -315,7 +355,7 @@ public class DeviceTaskScheduler {
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.forJob(jobKey)
.withDescription("设备" + fStoreyId + "最终任务触发器,触发时间是:" + executeTime)
.withDescription("设备" + fStoreyId + "第三阶段触发器,触发时间是:" + executeTime)
.startAt(executeTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withMisfireHandlingInstructionFireNow() // 错过立即执行
......@@ -327,14 +367,14 @@ public class DeviceTaskScheduler {
}
/**
* 2. 创建15分钟后执行的最终任务(保持原逻辑,优化超时)
* 2. 创建20分钟后执行的最终任务(保持原逻辑,优化超时)
*/
private void createFinalExecutionJob(Long fStoreyId, String fPowerOutageIp, Integer fPowerOutagePort) throws SchedulerException {
String jobId = "FINAL_" + fStoreyId;
JobKey jobKey = new JobKey(jobId, JOB_GROUP);
TriggerKey triggerKey = new TriggerKey(jobId + "_TRIGGER", TRIGGER_GROUP);
JobDetail job = JobBuilder.newJob(FinalExecutionJob.class)
JobDetail job = JobBuilder.newJob(AgingStageFourProcessJob.class)
.withIdentity(jobKey)
.withDescription("设备" + fStoreyId + "最终执行任务(仅一次)")
.usingJobData("fStoreyId", fStoreyId.toString())
......@@ -344,13 +384,13 @@ public class DeviceTaskScheduler {
.requestRecovery(true)
.build();
String s = sysConfigService.directSelectConfigByKey(RoboticArmConstans.AGING_STAGE3_TIME);
String s = sysConfigService.directSelectConfigByKey(RoboticArmConstans.AGING_STAGE4_TIME);
int delayMin = 0;
if(StringUtils.isNotBlank(s)) {
delayMin = Integer.parseInt(s);
}
if(delayMin == 0) {
delayMin = 15;
delayMin = 20;
}
Date executeTime = Date.from(Instant.now().plus(delayMin, ChronoUnit.MINUTES));
SimpleTrigger trigger = TriggerBuilder.newTrigger()
......
......@@ -84,7 +84,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectPalletDeviceUploadHistoryList" parameterType="PalletDeviceUploadHistory" resultMap="PalletDeviceUploadHistoryResult">
<include refid="selectPalletDeviceUploadHistoryVo"/>
<where>
<if test="deviceCode != null and deviceCode != ''"> and palDeviceBinding.f_device_code = #{deviceCode}</if>
<if test="deviceCode != null and deviceCode != ''"> and palDeviceBinding.f_device_code like concat('%',#{deviceCode},'%') </if>
<if test="trayCode != null and trayCode != ''"> and trayInfo.f_tray_code like concat('%',#{trayCode},'%') </if>
</where>
</select>
......
......@@ -39,7 +39,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectAgingStageTime" resultMap="SysConfigResult">
<include refid="selectConfigVo"/>
where config_key in ('agingStage1Time','agingStage2Time','agingStage3Time')
where config_key in ('agingStage1Time','agingStage2Time','agingStage3Time','agingStage4Time')
</select>
<select id="selectConfigList" parameterType="SysConfig" resultMap="SysConfigResult">
<include refid="selectConfigVo"/>
......
......@@ -10,6 +10,15 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="托盘编号" prop="trayCode">
<el-input
v-model="queryParams.trayCode"
placeholder="请输入托盘编号"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
......@@ -322,6 +331,7 @@ export default {
pageSize: 10,
trayId: null,
deviceCode: null,
trayCode: null,
row: null,
col: null,
index: null,
......@@ -435,10 +445,6 @@ export default {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
//单个设备重新上传
handleSingleUpload(row) {
},
/** 新增按钮操作 */
handleAdd() {
......
......@@ -34,10 +34,11 @@
</div>
<!-- 设备扫描区域 -->
<!-- 调整点1: 修改扫描区域的显示条件 -->
<div
class="scan-section"
style="align-items: center;"
v-show="trayStatus !== '1' && trayStatus !== '2'"
v-show="trayStatus !== '1' && trayStatus !== '2' && !(trayStatus === '3' && abnormalCount === 0)"
>
<input
type="text"
......@@ -46,12 +47,18 @@
v-model="deviceInput"
@keyup.enter="addDevice"
ref="deviceInput"
:disabled="trayStatus === '1'"
:disabled="trayStatus === '1' || (trayStatus === '3' && abnormalCount === 0)"
>
<div class="status-bar">
<i class="fas fa-info-circle"></i>
<!-- 调整点2: 修改状态栏显示逻辑 -->
<span v-if="trayStatus === '0'">已绑定设备: <span class="filled-count">{{ filledCount }}</span>/72</span>
<span v-if="trayStatus === '3'">待处理异常设备: <span class="filled-count">{{ abnormalCount }}</span>/{{ initialAbnormalCount }}</span>
<span v-if="trayStatus === '3' && abnormalCount > 0">
待处理异常设备: <span class="filled-count">{{ abnormalCount }}</span>/{{ initialAbnormalCount }}
</span>
<span v-if="trayStatus === '3' && abnormalCount === 0">
所有设备正常,可进行一键解绑
</span>
</div>
</div>
</div>
......@@ -111,9 +118,9 @@
<i class="fas fa-paper-plane"></i>
{{ '提交绑定' }}
</button>
<!-- 一键解绑按钮 - 仅status=3时显示 -->
<!-- 调整点3: 修改一键解绑按钮显示条件 -->
<button
v-if="trayStatus === '3'"
v-if="trayStatus === '3' && abnormalCount === 0"
class="unbind-btn"
@click="unbindAll"
>
......@@ -256,6 +263,10 @@ export default {
// 计算是否需要显示操作按钮
showControlButtons() {
// 当托盘状态为"老化中"(1)或"老化完成"(2)时,不显示操作按钮
// 调整点4: 在状态3时,只有当异常设备数量为0时才显示操作按钮(一键解绑)
if (this.trayStatus === '3') {
return this.abnormalCount === 0;
}
return this.trayStatus !== '1' && this.trayStatus !== '2';
},
// 计算已填充的设备数量
......@@ -300,7 +311,7 @@ export default {
// 扫描输入框提示语
scanPlaceholder() {
return this.trayStatus === '3'
? '解绑设备条码...'
? '扫描异常设备条码进行解绑...'
: '扫描设备条码...';
}
},
......@@ -353,7 +364,9 @@ export default {
(device.realTimeStatus != null && device.realTimeStatus === '0') ||
(device.calibrationConcentrationStatus != null && device.calibrationConcentrationStatus !== '4') ||
(device.writeTimeStatus != null && device.writeTimeStatus === '0') ||
(device.runTimeStatus != null && device.runTimeStatus === '0');
(device.runTimeStatus != null && device.runTimeStatus === '0') ||
device.relayStatus === 0 || device.pulseStatus === 0 ||
device.moduleStatus === 0 || device.simCardStatus === 0 || device.networkStatus === 0;
} else {
// 非标定完成状态(0,4,1,2等),只检查设备状态是否为 0 或 5
// 其他状态字段在绑定阶段可能为 null,不应该视为错误
......@@ -467,6 +480,15 @@ export default {
);
if (deviceIndex !== -1) {
// 调整点5: 在标定完成状态下,只有异常设备才允许解绑
const device = this.devices[deviceIndex];
if (!this.isDeviceError(device)) {
// 正常设备不允许解绑
this.$message.warning(`设备 ${this.deviceInput} 状态正常,不能解绑`);
this.deviceInput = '';
return;
}
// 找到设备,直接解绑(不弹确认对话框)
this.executeUnbind(deviceIndex);
} else {
......@@ -586,6 +608,11 @@ export default {
// 如果是异常设备,减少异常计数(使用完整的异常判断逻辑)
if (wasErrorDevice) {
this.abnormalCount--;
// 调整点6: 当异常设备全部解绑完成后,输入框会自动变为readonly(通过disabled属性控制)
if (this.abnormalCount === 0) {
this.$message.success('所有异常设备已解绑完成,可进行一键解绑');
}
}
this.$message.success(`设备 ${device.deviceCode} 解绑成功`);
......
......@@ -68,6 +68,23 @@
<div class="form-tips">必须大于等于第二阶段时间5分钟及以上,最大4320分钟</div>
</el-form-item>
<!-- 第四阶段 - 新增字段 -->
<el-form-item
label="老化流程第四阶段执行时间(单位:分钟)"
prop="agingStage4Time"
>
<el-input-number
v-model="form.agingStage4Time"
:min="Math.max(20, form.agingStage3Time + 5)"
:max="4325"
placeholder="请输入第四阶段执行时间"
controls-position="right"
style="width: 300px;"
@change="validateStage4"
/>
<div class="form-tips">必须大于等于第三阶段时间5分钟及以上,最大4325分钟</div>
</el-form-item>
<!-- 操作按钮 -->
<el-form-item>
<el-button
......@@ -130,14 +147,28 @@ export default {
}
};
// 第四阶段验证规则 - 新增验证规则
const validateStage4 = (rule, value, callback) => {
if (value === null || value === undefined || value === '') {
callback(new Error("第四阶段时间不能为空"));
} else if (value < this.form.agingStage3Time + 5) {
callback(new Error("第四阶段时间必须大于等于第三阶段时间5分钟及以上"));
} else if (value > 4325) {
callback(new Error("第四阶段时间不能超过4325分钟"));
} else {
callback();
}
};
return {
// 表单数据
// 表单数据 - 添加第四阶段字段
form: {
agingStage1Time: null,
agingStage2Time: null,
agingStage3Time: null,
agingStage4Time: null, // 新增第四阶段字段
},
// 验证规则
// 验证规则 - 添加第四阶段验证规则
rules: {
agingStage1Time: [
{ required: true, validator: validateStage1, trigger: ["blur", "change"] }
......@@ -148,14 +179,18 @@ export default {
agingStage3Time: [
{ required: true, validator: validateStage3, trigger: ["blur", "change"] }
],
agingStage4Time: [ // 新增第四阶段验证规则
{ required: true, validator: validateStage4, trigger: ["blur", "change"] }
],
},
// 加载状态
loading: false,
// 配置键名
// 配置键名 - 添加第四阶段键名
configKeys: {
stage1: "agingStage1Time",
stage2: "agingStage2Time",
stage3: "agingStage3Time",
stage4: "agingStage4Time", // 新增第四阶段键名
},
};
},
......@@ -170,7 +205,7 @@ export default {
const response = await getAgingStageTime();
if (response.code === 200 && response.data) {
// 假设返回的数据格式为数组,包含个配置项
// 假设返回的数据格式为数组,包含个配置项
const configs = response.data;
configs.forEach(item => {
switch (item.configKey) {
......@@ -183,6 +218,9 @@ export default {
case this.configKeys.stage3:
this.form.agingStage3Time = parseInt(item.configValue) || 15;
break;
case this.configKeys.stage4: // 新增第四阶段数据处理
this.form.agingStage4Time = parseInt(item.configValue) || 20;
break;
}
});
}
......@@ -201,7 +239,7 @@ export default {
try {
this.loading = true;
// 构建请求数据
// 构建请求数据 - 添加第四阶段数据
const requestData = [
{
configKey: this.configKeys.stage1,
......@@ -215,6 +253,10 @@ export default {
configKey: this.configKeys.stage3,
configValue: this.form.agingStage3Time.toString(),
},
{
configKey: this.configKeys.stage4, // 新增第四阶段数据
configValue: this.form.agingStage4Time.toString(),
},
];
const response = await updateAgingStageTime(requestData);
......@@ -246,19 +288,27 @@ export default {
// 第一阶段变化时触发验证
validateStage1() {
// 重新验证第二阶段和第三阶段
// 重新验证第二、三、四阶段
this.$refs.formRef.validateField("agingStage2Time");
this.$refs.formRef.validateField("agingStage3Time");
this.$refs.formRef.validateField("agingStage4Time");
},
// 第二阶段变化时触发验证
validateStage2() {
// 重新验证第三阶段
// 重新验证第三、四阶段
this.$refs.formRef.validateField("agingStage3Time");
this.$refs.formRef.validateField("agingStage4Time");
},
// 第三阶段变化时触发验证
validateStage3() {
// 重新验证第四阶段
this.$refs.formRef.validateField("agingStage4Time");
},
// 第四阶段变化时触发验证 - 新增方法
validateStage4() {
// 不需要重新验证其他阶段
},
......@@ -270,6 +320,11 @@ export default {
// 计算第三阶段的最小值
getStage3Min() {
return Math.max(15, (this.form.agingStage2Time || 10) + 5);
},
// 计算第四阶段的最小值 - 新增方法
getStage4Min() {
return Math.max(20, (this.form.agingStage3Time || 15) + 5);
}
},
watch: {
......@@ -296,6 +351,18 @@ export default {
}
},
immediate: true
},
'form.agingStage3Time': {
handler(newVal) {
// 当第三阶段变化时,确保第四阶段不小于第三阶段+5 - 新增监听
if (newVal !== null && this.form.agingStage4Time !== null) {
const minStage4 = newVal + 5;
if (this.form.agingStage4Time < minStage4) {
this.form.agingStage4Time = minStage4;
}
}
},
immediate: true
}
}
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment