Quellcode durchsuchen

ly 可靠性 预防性维修

ly vor 2 Tagen
Ursprung
Commit
dd12939a66
29 geänderte Dateien mit 822 neuen und 142 gelöschten Zeilen
  1. 0 57
      master/src/main/java/com/ruoyi/framework/config/RedisConfig.java
  2. 1 1
      master/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
  3. 209 0
      master/src/main/java/com/ruoyi/framework/task/reliability/RelMaintPlanTask.java
  4. 72 0
      master/src/main/java/com/ruoyi/project/reliability/controller/TRelMaintPlanController.java
  5. 11 1
      master/src/main/java/com/ruoyi/project/reliability/controller/TRelMaintRecordController.java
  6. 12 0
      master/src/main/java/com/ruoyi/project/reliability/domain/TRelCompo.java
  7. 22 8
      master/src/main/java/com/ruoyi/project/reliability/mapper/TRelMaintPlanMapper.java
  8. 14 0
      master/src/main/java/com/ruoyi/project/reliability/service/ITRelMaintPlanService.java
  9. 149 1
      master/src/main/java/com/ruoyi/project/reliability/service/impl/MaintPlanGeneratorServiceImpl.java
  10. 42 0
      master/src/main/java/com/ruoyi/project/reliability/service/impl/TRelMaintPlanServiceImpl.java
  11. 46 3
      master/src/main/java/com/ruoyi/project/system/controller/SysAppVersionController.java
  12. 13 0
      master/src/main/java/com/ruoyi/project/system/domain/SysAppVersion.java
  13. 6 2
      master/src/main/java/com/ruoyi/project/system/mapper/SysAppVersionMapper.java
  14. 1 1
      master/src/main/java/com/ruoyi/project/system/service/ISysAppVersionService.java
  15. 4 3
      master/src/main/java/com/ruoyi/project/system/service/impl/SysAppVersionServiceImpl.java
  16. 3 2
      master/src/main/resources/application.yml
  17. 4 2
      master/src/main/resources/mybatis/reliability/TRelCompoMapper.xml
  18. 12 0
      master/src/main/resources/mybatis/reliability/TRelMaintPlanMapper.xml
  19. 33 4
      master/src/main/resources/mybatis/system/SysAppVersionMapper.xml
  20. 8 0
      ui/src/api/reliability/rel_maint_plan.js
  21. 3 2
      ui/src/api/system/appVersion.js
  22. 6 1
      ui/src/views/reliability/rel_compo/index.vue
  23. 10 3
      ui/src/views/reliability/rel_device/detail.vue
  24. 20 10
      ui/src/views/reliability/rel_maint_plan/MaintPlanDetailContent.vue
  25. 8 1
      ui/src/views/reliability/rel_maint_plan/MaintPlanForm.vue
  26. 50 2
      ui/src/views/reliability/rel_maint_plan/form.vue
  27. 13 9
      ui/src/views/reliability/rel_maint_record/index.vue
  28. 23 29
      ui/src/views/reliability/rel_maint_record/myRecord.vue
  29. 27 0
      ui/src/views/system/appVersion/index.vue

+ 0 - 57
master/src/main/java/com/ruoyi/framework/config/RedisConfig.java

@@ -1,26 +1,16 @@
 package com.ruoyi.framework.config;
 
-import io.lettuce.core.ClientOptions;
-import io.lettuce.core.SslOptions;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cache.annotation.CachingConfigurerSupport;
 import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
-import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
-import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
-import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
-import org.springframework.util.StringUtils;
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.PropertyAccessor;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-import java.io.File;
-import java.time.Duration;
-
 /**
  * redis配置
  *
@@ -30,53 +20,6 @@ import java.time.Duration;
 @EnableCaching
 public class RedisConfig extends CachingConfigurerSupport
 {
-    @Value("${spring.redis.host}")
-    private String host;
-
-    @Value("${spring.redis.port}")
-    private int port;
-
-    @Value("${spring.redis.password}")
-    private String password;
-
-    @Value("${spring.redis.database:0}")
-    private int database;
-
-    @Value("${spring.redis.timeout:10s}")
-    private Duration timeout;
-
-    // 阿里云 TLS 证书路径(JKS),如果为空则不启用 SSL
-    @Value("${spring.redis.ssl.trust-store:}")
-    private String trustStorePath;
-
-    /**
-     * 创建 Redis 连接工厂(支持阿里云 TLS)
-     */
-    @Bean
-    public RedisConnectionFactory redisConnectionFactory() {
-        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
-        config.setHostName(host);
-        config.setPort(port);
-        config.setPassword(password);
-        config.setDatabase(database);
-
-        LettuceClientConfiguration.LettuceClientConfigurationBuilder builder =
-            LettuceClientConfiguration.builder().commandTimeout(timeout);
-
-        // 如果配置了证书路径,启用 SSL(按照阿里云官方示例)
-        if (StringUtils.hasText(trustStorePath)) {
-            File trustStoreFile = new File(trustStorePath);
-            if (trustStoreFile.exists()) {
-                ClientOptions clientOptions = ClientOptions.builder().sslOptions(
-                    SslOptions.builder().jdkSslProvider().truststore(trustStoreFile).build()
-                ).build();
-                builder.clientOptions(clientOptions).useSsl();
-            }
-        }
-
-        return new LettuceConnectionFactory(config, builder.build());
-    }
-
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)

+ 1 - 1
master/src/main/java/com/ruoyi/framework/config/SecurityConfig.java

@@ -137,7 +137,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 .antMatchers("/safecheck/weekcheck/downloadWeekcheck").anonymous()
                 .antMatchers("/safecheck/weekcheck/downloadWeekcheckPdf").anonymous()
                 .antMatchers("/production/quality/export").anonymous()
-                .antMatchers("/system/appVersion/check").anonymous()
+                .antMatchers("/system/appVersion/check").permitAll()
                 // 除上面外的所有请求全部需要鉴权认证
                 .anyRequest().authenticated()
                 .and()

+ 209 - 0
master/src/main/java/com/ruoyi/framework/task/reliability/RelMaintPlanTask.java

@@ -0,0 +1,209 @@
+package com.ruoyi.framework.task.reliability;
+
+import com.ruoyi.common.jpush.JiGuangPushService;
+import com.ruoyi.common.sendEmail.IMailService;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.project.reliability.domain.TRelMaintPlan;
+import com.ruoyi.project.reliability.domain.TRelMaintRecord;
+import com.ruoyi.project.reliability.service.ITRelMaintPlanService;
+import com.ruoyi.project.reliability.service.ITRelMaintRecordService;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.service.ISysUserService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 可靠性模块 - 维修计划到期提醒定时任务
+ *
+ * 根据维修计划的计划结束时间(planEndTime)判断,距离结束还剩 3 天时,
+ * 给计划下相关维修记录的检查人或维修/更换责任人发送邮件提醒。
+ *
+ * 使用方式:在定时任务管理中配置 target 为 relMaintPlanTask.maintPlanDueReminder
+ */
+@Component("relMaintPlanTask")
+public class RelMaintPlanTask {
+
+    private static final Logger logger = LoggerFactory.getLogger(RelMaintPlanTask.class);
+
+    @Autowired
+    private ITRelMaintPlanService maintPlanService;
+
+    @Autowired
+    private ITRelMaintRecordService maintRecordService;
+
+    @Autowired
+    private ISysUserService sysUserService;
+
+    @Autowired
+    private IMailService mailService;
+
+    @Autowired
+    private JiGuangPushService jiGuangPushService;
+
+    /**
+     * 维修计划到期提醒任务入口
+     */
+    public void maintPlanDueReminder() {
+        try {
+            Date now = DateUtils.getNowDate();
+            logger.info("[RelMaintPlanTask] 开始执行维修计划到期提醒任务,当前时间: {}", now);
+
+            // 查询所有未完成的维修计划,后续按到期时间与当前时间的差值(天数)过滤
+            List<TRelMaintPlan> duePlans = maintPlanService.selectDueMaintPlanList(null, null);
+            if (duePlans == null || duePlans.isEmpty()) {
+                logger.info("[RelMaintPlanTask] 无未完成的维修计划,任务结束");
+                return;
+            }
+
+            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日");
+
+            for (TRelMaintPlan plan : duePlans) {
+                try {
+                    // 计划结束时间与当前时间相差天数 <= 3 才提醒(包含已过期 3 天内和即将到期 3 天内)
+                    Date endTime = plan.getPlanEndTime();
+                    if (endTime == null) {
+                        continue;
+                    }
+                    long diffMillis = endTime.getTime() - now.getTime();
+                    long diffDays = Math.abs(diffMillis / (24L * 60L * 60L * 1000L));
+                    if (diffDays > 3L) {
+                        continue;
+                    }
+
+                    Long planId = plan.getPlanId();
+                    if (planId == null) {
+                        continue;
+                    }
+
+                    // 查询该计划下的所有维修记录
+                    TRelMaintRecord query = new TRelMaintRecord();
+                    query.setPlanId(planId);
+                    List<TRelMaintRecord> records = maintRecordService.selectTRelMaintRecordList(query);
+                    if (records == null || records.isEmpty()) {
+                        continue;
+                    }
+
+                    // 收集所有检查人/维修责任人的 staffId(仅未完成记录:recordStatus = 1)
+                    Set<String> staffIds = new HashSet<>();
+                    boolean hasUnfinishedRecord = false;
+                    for (TRelMaintRecord record : records) {
+                        if (record == null || record.getRecordStatus() == null || record.getRecordStatus() != 1) {
+                            continue;
+                        }
+                        hasUnfinishedRecord = true;
+                        if (StringUtils.isNotEmpty(record.getInspector())) {
+                            staffIds.add(record.getInspector());
+                        }
+                        if (StringUtils.isNotEmpty(record.getResponsible())) {
+                            staffIds.add(record.getResponsible());
+                        }
+                    }
+
+                    if (!hasUnfinishedRecord || staffIds.isEmpty()) {
+                        continue;
+                    }
+
+                    List<String> emailList = new ArrayList<>();
+                    Set<String> aliasSet = new HashSet<>();
+                    for (String staffId : staffIds) {
+                        try {
+                            SysUser user = sysUserService.selectUserByStaffId(staffId);
+                            if (user != null) {
+                                if (StringUtils.isNotEmpty(user.getEmail())) {
+                                    emailList.add(user.getEmail());
+                                }
+                                if (user.getUserId() != null) {
+                                    aliasSet.add(String.valueOf(user.getUserId()));
+                                }
+                            }
+                        } catch (Exception e) {
+                            logger.warn("[RelMaintPlanTask] 根据 staffId 查询用户失败, staffId={}", staffId, e);
+                        }
+                    }
+
+                    if (emailList.isEmpty()) {
+                        continue;
+                    }
+
+                    // 组装邮件内容
+                    String planInfo = buildPlanInfo(plan, records, dateFormat);
+                    String subject = "维修计划即将过期提醒:" + safe(plan.getDevName()) + "-" + safe(plan.getDevTag());
+                    String html = buildHtmlContent(planInfo);
+
+                    String[] to = emailList.toArray(new String[0]);
+                    mailService.sendHtmlMail(to, subject, html);
+
+                    // 极光推送提醒,同步通知到移动端
+                    if (!aliasSet.isEmpty()) {
+                        String alert = "您有一条维修计划已过期或即将过期,请及时处理。设备:" +
+                                safe(plan.getDevName()) + " (" + safe(plan.getDevTag()) + ")";
+                        jiGuangPushService.send("维修计划即将过期提醒", alert, aliasSet.toArray(new String[0]));
+                    }
+
+                    logger.info("[RelMaintPlanTask] 已发送维修计划到期提醒邮件及JPush, planId={}, 收件人数量={}", planId, to.length);
+                } catch (Exception e) {
+                    logger.error("[RelMaintPlanTask] 处理单个维修计划到期提醒失败, planId=" + plan.getPlanId(), e);
+                }
+            }
+        } catch (Exception e) {
+            logger.error("[RelMaintPlanTask] 维修计划到期提醒任务执行失败", e);
+        }
+    }
+
+    private String buildPlanInfo(TRelMaintPlan plan, List<TRelMaintRecord> records, SimpleDateFormat dateFormat) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("设备:").append(safe(plan.getDevName())).append(" (").append(safe(plan.getDevTag())).append(")<br/>");
+        sb.append("计划开始时间:").append(plan.getPlanTime() != null ? dateFormat.format(plan.getPlanTime()) : "-").append("<br/>");
+        sb.append("计划结束时间:").append(plan.getPlanEndTime() != null ? dateFormat.format(plan.getPlanEndTime()) : "-").append("<br/>");
+        sb.append("责任人:").append(safe(plan.getResponsible())).append("<br/>");
+        sb.append("<br/>");
+        sb.append("该计划下包含的维修/检查任务如下:<br/>");
+        int index = 1;
+        for (TRelMaintRecord record : records) {
+            sb.append(index++).append(". 部件:").append(safe(record.getCompoName()))
+                    .append(",类型:").append(safe(record.getMaintType()))
+                    .append(",检查人:").append(safe(record.getInspector()))
+                    .append(",维修人:").append(safe(record.getResponsible()))
+                    .append("<br/>");
+        }
+        return sb.toString();
+    }
+
+    private String buildHtmlContent(String planInfo) {
+        String start = "<!DOCTYPE html><html><head><meta charset='utf-8'><title></title></head><body><div style='background-color:#ECECEC; padding: 35px;'>" +
+                "<table cellpadding='0' align='center' " +
+                "style='width: 600px; margin: 0 auto; text-align: left; position: relative; border-radius: 5px; font-size: 14px; font-family:微软雅黑, 黑体; line-height: 1.5; box-shadow: 0 0 5px #999; border-collapse: collapse; background:#fff;'>" +
+                "<tbody><tr><th valign='middle' style='height: 25px; line-height: 25px; padding: 15px 35px; border-bottom: 1px solid #42a3d3; background-color: #49bcff; border-radius: 5px 5px 0 0; color:#fff;'>" +
+                "化工装置管理系统 Chemical Plant Management System(CPMS)</th></tr>";
+
+        String end = "</tbody></table></div></body></html>";
+
+        String center = "<tr><td><div style=\"padding:25px 35px 40px; background-color:#fff;\"><h2 style=\"margin: 5px 0px; \">" +
+                "<font color=\"#333333\" style=\"line-height: 20px; \" size=\"4\">" +
+                "亲爱的 CPMS 用户:</font><br>" +
+                "<p>您有一条维修计划已过期或即将过期,请及时完成相关检查/维修任务。计划信息如下:<br>" +
+                planInfo +
+                "</p>" +
+                "<p align=\"right\">" + DateUtils.getDate() + "</p>" +
+                "<div style=\"width:700px;margin:0 auto;\">" +
+                "<div style=\"padding:10px 10px 0;border-top:1px solid #ccc;color:#747474;margin-bottom:20px;line-height:1.3em;font-size:12px;\">" +
+                "<p>此为系统邮件,请勿回复<br>This e-Mail is an automatic reminder sent by CPMS, please do not reply</p>" +
+                "</div></div></div></td></tr>";
+
+        return start + center + end;
+    }
+
+    private String safe(String val) {
+        return val == null ? "-" : val;
+    }
+}

+ 72 - 0
master/src/main/java/com/ruoyi/project/reliability/controller/TRelMaintPlanController.java

@@ -35,6 +35,9 @@ import com.ruoyi.framework.web.controller.BaseController;
 import com.ruoyi.framework.web.domain.AjaxResult;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.web.page.TableDataInfo;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.sendEmail.IMailService;
+import com.ruoyi.common.utils.DateUtils;
 
 /**
  * 设备维修计划Controller
@@ -54,6 +57,9 @@ public class TRelMaintPlanController extends BaseController
     @Autowired
     private ISysMessageService sysMessageService;
 
+    @Autowired
+    private IMailService mailService;
+
     @Autowired
     private SysUserMapper sysUserMapper;
 
@@ -330,6 +336,26 @@ public class TRelMaintPlanController extends BaseController
         return toAjax(tRelMaintPlanService.updateTRelMaintPlanWithSync(tRelMaintPlan));
     }
 
+    @PreAuthorize("@ss.hasPermi('reliability:rel_maint_plan:edit')")
+    @PostMapping("/saveOnly")
+    public AjaxResult saveOnly(@RequestBody TRelMaintPlan tRelMaintPlan)
+    {
+        try {
+            if (tRelMaintPlan == null || tRelMaintPlan.getPlanId() == null) {
+                return AjaxResult.error("计划ID不能为空");
+            }
+            if (tRelMaintPlan.getMaintComponents() == null) {
+                return AjaxResult.error("维修部件列表不能为空");
+            }
+
+            tRelMaintPlanService.syncMaintRecordsOnly(tRelMaintPlan.getPlanId(), tRelMaintPlan.getMaintComponents(), getUserId());
+            return AjaxResult.success("保存成功");
+        } catch (Exception e) {
+            logger.error("仅保存失败", e);
+            return AjaxResult.error("仅保存失败:" + e.getMessage());
+        }
+    }
+
     /**
      * 已通过的维修计划再次提交申请(重新启动审批流程)
      */
@@ -516,6 +542,12 @@ public class TRelMaintPlanController extends BaseController
                         message.setMsgContent("您的维修计划申请未通过审批");
                     }
                     sysMessageService.insertSysMessage(message);
+
+                    if (StringUtils.isNotEmpty(creater.getEmail())) {
+                        String subject = "维修计划审批结果通知";
+                        String html = buildMaintPlanApproveMailHtml(tRelMaintPlan, condition, devTask.getComment());
+                        mailService.sendHtmlMail(creater.getEmail(), subject, html);
+                    }
                 }
             } catch (Exception e) {
                 logger.error("发送系统消息失败", e);
@@ -618,4 +650,44 @@ public class TRelMaintPlanController extends BaseController
             logger.error("处理维修记录状态失败", e);
         }
     }
+
+    private String buildMaintPlanApproveMailHtml(TRelMaintPlan plan, String condition, String comment) {
+        String title = "1".equals(condition) ? "维修计划审批已通过" : "维修计划审批未通过";
+        String remark = StringUtils.isNotEmpty(comment) ? comment : "-";
+        String planTime = plan.getPlanTime() != null ? DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, plan.getPlanTime()) : "-";
+        String planEndTime = plan.getPlanEndTime() != null ? DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, plan.getPlanEndTime()) : "-";
+
+        String planInfo = "设备:" + safe(plan.getDevName()) + " (" + safe(plan.getDevTag()) + ")<br/>" +
+                "装置:" + safe(plan.getPlant()) + "<br/>" +
+                "计划开始时间:" + planTime + "<br/>" +
+                "计划结束时间:" + planEndTime + "<br/>" +
+                "审批结果:" + title + "<br/>" +
+                "审批意见:" + remark + "<br/>";
+
+        String start = "<!DOCTYPE html><html><head><meta charset='utf-8'><title></title></head><body><div style='background-color:#ECECEC; padding: 35px;'>" +
+                "<table cellpadding='0' align='center' " +
+                "style='width: 600px; margin: 0 auto; text-align: left; position: relative; border-radius: 5px; font-size: 14px; font-family:微软雅黑, 黑体; line-height: 1.5; box-shadow: 0 0 5px #999; border-collapse: collapse; background:#fff;'>" +
+                "<tbody><tr><th valign='middle' style='height: 25px; line-height: 25px; padding: 15px 35px; border-bottom: 1px solid #42a3d3; background-color: #49bcff; border-radius: 5px 5px 0 0; color:#fff;'>" +
+                "化工装置管理系统 Chemical Plant Management System(CPMS)</th></tr>";
+
+        String center = "<tr><td><div style=\"padding:25px 35px 40px; background-color:#fff;\"><h2 style=\"margin: 5px 0px; \">" +
+                "<font color=\"#333333\" style=\"line-height: 20px; \" size=\"4\">" +
+                "亲爱的 CPMS 用户:</font><br>" +
+                "<p>" + title + ",详情如下:<br>" +
+                planInfo +
+                "</p>" +
+                "<p align=\"right\">" + DateUtils.getDate() + "</p>" +
+                "<div style=\"width:700px;margin:0 auto;\">" +
+                "<div style=\"padding:10px 10px 0;border-top:1px solid #ccc;color:#747474;margin-bottom:20px;line-height:1.3em;font-size:12px;\">" +
+                "<p>此为系统邮件,请勿回复<br>This e-Mail is an automatic reminder sent by CPMS, please do not reply</p>" +
+                "</div></div></div></td></tr>";
+
+        String end = "</tbody></table></div></body></html>";
+
+        return start + center + end;
+    }
+
+    private String safe(String val) {
+        return val == null ? "-" : val;
+    }
 }

+ 11 - 1
master/src/main/java/com/ruoyi/project/reliability/controller/TRelMaintRecordController.java

@@ -164,7 +164,17 @@ public class TRelMaintRecordController extends BaseController
         }
         String memoReason = tRelMaintRecord.getMemoReason();
         Date memoTime = tRelMaintRecord.getMemoTime();
-        
+        String remarks = tRelMaintRecord.getRemarks();
+
+        if (cannotMaintOrReplace != null && cannotMaintOrReplace
+            && memoReason != null && !memoReason.isEmpty()) {
+            if (remarks == null || remarks.isEmpty()) {
+                tRelMaintRecord.setRemarks(memoReason);
+            } else if (!remarks.contains(memoReason)) {
+                tRelMaintRecord.setRemarks(remarks + ";" + memoReason);
+            }
+        }
+
         int result = tRelMaintRecordService.updateTRelMaintRecord(tRelMaintRecord);
         
         // 如果是检查任务(maintType = "1")且选择了需要维修/更换,创建新的维修或更换记录

+ 12 - 0
master/src/main/java/com/ruoyi/project/reliability/domain/TRelCompo.java

@@ -149,6 +149,8 @@ public class TRelCompo extends BaseEntity
     /** 设备区域负责人(不存储到数据库,通过设备关联查询) */
     private String areaResponsible;
 
+    private Integer hasMemo;
+
     public void setCompoId(Long compoId)
     {
         this.compoId = compoId;
@@ -448,6 +450,16 @@ public class TRelCompo extends BaseEntity
         return areaResponsible;
     }
 
+    public void setHasMemo(Integer hasMemo)
+    {
+        this.hasMemo = hasMemo;
+    }
+
+    public Integer getHasMemo()
+    {
+        return hasMemo;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 22 - 8
master/src/main/java/com/ruoyi/project/reliability/mapper/TRelMaintPlanMapper.java

@@ -1,20 +1,24 @@
 package com.ruoyi.project.reliability.mapper;
 
+import java.util.Date;
 import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
 import com.ruoyi.framework.aspectj.lang.annotation.DataScope;
 import com.ruoyi.project.reliability.domain.TRelMaintPlan;
 
 /**
  * 设备维修计划Mapper接口
- * 
+ *
  * @author ly
  * @date 2025-10-28
  */
-public interface TRelMaintPlanMapper 
+public interface TRelMaintPlanMapper
 {
     /**
      * 查询设备维修计划
-     * 
+     *
      * @param planId 设备维修计划ID
      * @return 设备维修计划
      */
@@ -22,7 +26,7 @@ public interface TRelMaintPlanMapper
 
     /**
      * 查询设备维修计划列表
-     * 
+     *
      * @param tRelMaintPlan 设备维修计划
      * @return 设备维修计划集合
      */
@@ -31,7 +35,7 @@ public interface TRelMaintPlanMapper
 
     /**
      * 新增设备维修计划
-     * 
+     *
      * @param tRelMaintPlan 设备维修计划
      * @return 结果
      */
@@ -39,7 +43,7 @@ public interface TRelMaintPlanMapper
 
     /**
      * 修改设备维修计划
-     * 
+     *
      * @param tRelMaintPlan 设备维修计划
      * @return 结果
      */
@@ -47,7 +51,7 @@ public interface TRelMaintPlanMapper
 
     /**
      * 删除设备维修计划
-     * 
+     *
      * @param planId 设备维修计划ID
      * @return 结果
      */
@@ -55,9 +59,19 @@ public interface TRelMaintPlanMapper
 
     /**
      * 批量删除设备维修计划
-     * 
+     *
      * @param planIds 需要删除的数据ID
      * @return 结果
      */
     public int deleteTRelMaintPlanByIds(Long[] planIds);
+
+    /**
+     * 查询在指定结束日期区间内且未完成的维修计划(用于到期提醒)
+     *
+     * @param startDate 区间开始日期(含)
+     * @param endDate   区间结束日期(不含)
+     * @return 维修计划集合
+     */
+    public List<TRelMaintPlan> selectDueMaintPlanList(@Param("startDate") Date startDate,
+                                                      @Param("endDate") Date endDate);
 }

+ 14 - 0
master/src/main/java/com/ruoyi/project/reliability/service/ITRelMaintPlanService.java

@@ -1,7 +1,10 @@
 package com.ruoyi.project.reliability.service;
 
+import java.util.Date;
 import java.util.List;
+
 import com.ruoyi.project.reliability.domain.TRelMaintPlan;
+import com.ruoyi.project.reliability.domain.TRelMaintRecord;
 
 /**
  * 设备维修计划Service接口
@@ -66,4 +69,15 @@ public interface ITRelMaintPlanService
      * @return 结果
      */
     public int deleteTRelMaintPlanById(Long planId);
+
+    void syncMaintRecordsOnly(Long planId, List<TRelMaintRecord> maintComponents, Long operatorUserId);
+
+    /**
+     * 查询在指定结束日期区间内且未完成的维修计划(用于到期提醒)
+     *
+     * @param startDate 区间开始日期(含)
+     * @param endDate   区间结束日期(不含)
+     * @return 维修计划集合
+     */
+    List<TRelMaintPlan> selectDueMaintPlanList(Date startDate, Date endDate);
 }

+ 149 - 1
master/src/main/java/com/ruoyi/project/reliability/service/impl/MaintPlanGeneratorServiceImpl.java

@@ -8,11 +8,13 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import com.ruoyi.project.reliability.domain.TRelCompo;
 import com.ruoyi.project.reliability.domain.TRelDevice;
+import com.ruoyi.project.reliability.domain.TRelMaintMemo;
 import com.ruoyi.project.reliability.domain.TRelMaintPlan;
 import com.ruoyi.project.reliability.domain.TRelMaintRecord;
 import com.ruoyi.project.reliability.service.IMaintPlanGeneratorService;
 import com.ruoyi.project.reliability.service.ITRelCompoService;
 import com.ruoyi.project.reliability.service.ITRelDeviceService;
+import com.ruoyi.project.reliability.service.ITRelMaintMemoService;
 import com.ruoyi.project.reliability.service.ITRelMaintPlanService;
 import com.ruoyi.project.reliability.service.ITRelMaintRecordService;
 import com.ruoyi.project.reliability.utils.MaintFrequencyUtils;
@@ -51,11 +53,17 @@ public class MaintPlanGeneratorServiceImpl implements IMaintPlanGeneratorService
     @Autowired
     private ITRelMaintRecordService maintRecordService;
 
+    @Autowired
+    private ITRelMaintMemoService maintMemoService;
+
     @Override
     @Transactional
     public int generateAllMaintPlans(int monthsAhead, int mergeThresholdDays) {
         logger.info("开始生成所有设备的维修计划,提前{}个月,合并阈值{}天", monthsAhead, mergeThresholdDays);
 
+        // 为避免重复生成:先清理历史“计划中/未开始(9)”的旧计划及其下的旧记录(逻辑删除)
+        cleanupOldPlannedPlans(null);
+
         // 获取所有设备
         List<TRelDevice> devices = deviceService.selectTRelDeviceList(new TRelDevice());
         if (devices == null || devices.isEmpty()) {
@@ -80,6 +88,9 @@ public class MaintPlanGeneratorServiceImpl implements IMaintPlanGeneratorService
             return 0;
         }
 
+        // 按设备生成时同样需要先清理该设备历史“计划中/未开始(9)”的旧计划与记录,避免重复堆积
+        cleanupOldPlannedPlans(devTag);
+
         // 1. 获取设备信息
         TRelDevice deviceQuery = new TRelDevice();
         deviceQuery.setDevTag(devTag);
@@ -110,6 +121,19 @@ public class MaintPlanGeneratorServiceImpl implements IMaintPlanGeneratorService
         // Key: 年月(如 "2025-06"), Value: 该月需要维护的部件任务列表
         Map<String, List<CompoMaintTask>> monthlyTasks = new TreeMap<>();
 
+        // 备忘录纳入规则:若某部件存在备忘录(t_rel_maint_memo),也需要强制纳入本次生成
+        // 将备忘录按 compoId 分组,后续在遍历部件时追加“备忘录任务”到 monthlyTasks
+        List<TRelMaintMemo> memoList = getMemoListForDevice(devTag);
+        Map<String, List<TRelMaintMemo>> memoMap = new HashMap<>();
+        if (memoList != null && !memoList.isEmpty()) {
+            for (TRelMaintMemo memo : memoList) {
+                if (memo == null || memo.getCompoId() == null || memo.getCompoId().trim().isEmpty()) {
+                    continue;
+                }
+                memoMap.computeIfAbsent(memo.getCompoId().trim(), k -> new ArrayList<>()).add(memo);
+            }
+        }
+
         for (TRelCompo compo : compoList) {
             CompoMaintTask task = calculateOptimalTask(compo, now, endDate, mergeThresholdDays);
             if (task != null) {
@@ -117,6 +141,21 @@ public class MaintPlanGeneratorServiceImpl implements IMaintPlanGeneratorService
                 String monthKey = getMonthKey(task.maintDate);
                 monthlyTasks.computeIfAbsent(monthKey, k -> new ArrayList<>()).add(task);
             }
+
+            if (compo != null && compo.getCompoId() != null) {
+                List<TRelMaintMemo> compoMemoList = memoMap.get(compo.getCompoId().toString());
+                if (compoMemoList != null && !compoMemoList.isEmpty()) {
+                    // 将同一部件的备忘录逐条转换为“备忘录任务”并纳入生成
+                    // 日期优先使用 memoTime;若 memoTime 早于当前日期,则按当前日期落计划
+                    for (TRelMaintMemo memo : compoMemoList) {
+                        CompoMaintTask memoTask = buildTaskFromMemo(compo, memo, now, endDate);
+                        if (memoTask != null) {
+                            String monthKey = getMonthKey(memoTask.maintDate);
+                            monthlyTasks.computeIfAbsent(monthKey, k -> new ArrayList<>()).add(memoTask);
+                        }
+                    }
+                }
+            }
         }
 
         if (monthlyTasks.isEmpty()) {
@@ -142,7 +181,18 @@ public class MaintPlanGeneratorServiceImpl implements IMaintPlanGeneratorService
             // 不设置计划结束时间
             plan.setApprovalStatus(STATUS_PLANNED);  // 9-未开始
             plan.setCompletionStatus(STATUS_PLANNED); // 9-计划中
-            plan.setRemarks("自动生成的" + monthKey + "维修计划");
+
+            // 若该月计划中包含“备忘录任务”,在计划备注中追加来源标识,便于客户识别
+            boolean hasMemoTask = false;
+            if (tasks != null && !tasks.isEmpty()) {
+                for (CompoMaintTask t : tasks) {
+                    if (t != null && t.fromMemo) {
+                        hasMemoTask = true;
+                        break;
+                    }
+                }
+            }
+            plan.setRemarks(hasMemoTask ? ("自动生成的" + monthKey + "维修计划(从备忘录添加)") : ("自动生成的" + monthKey + "维修计划"));
 
             maintPlanService.insertTRelMaintPlan(plan);
             Long planId = plan.getPlanId();
@@ -160,6 +210,98 @@ public class MaintPlanGeneratorServiceImpl implements IMaintPlanGeneratorService
         return planCount;
     }
 
+    private int cleanupOldPlannedPlans(String devTag) {
+        // 清理范围:只清理“计划中/未开始(9)”的自动计划数据
+        // 原则:先删记录再删计划,避免残留关联数据
+        TRelMaintPlan planQuery = new TRelMaintPlan();
+        planQuery.setApprovalStatus(STATUS_PLANNED);
+        planQuery.setCompletionStatus(STATUS_PLANNED);
+        if (devTag != null && !devTag.trim().isEmpty()) {
+            planQuery.setDevTag(devTag);
+        }
+
+        List<TRelMaintPlan> oldPlanList = maintPlanService.selectTRelMaintPlanList(planQuery);
+        if (oldPlanList == null || oldPlanList.isEmpty()) {
+            return 0;
+        }
+
+        for (TRelMaintPlan oldPlan : oldPlanList) {
+            if (oldPlan == null || oldPlan.getPlanId() == null) {
+                continue;
+            }
+            Long planId = oldPlan.getPlanId();
+
+            List<TRelMaintRecord> recordList = maintRecordService.selectTRelMaintRecordByPlanId(planId);
+            if (recordList != null && !recordList.isEmpty()) {
+                for (TRelMaintRecord record : recordList) {
+                    if (record != null && record.getRecordId() != null) {
+                        maintRecordService.deleteTRelMaintRecordById(record.getRecordId());
+                    }
+                }
+            }
+
+            maintPlanService.deleteTRelMaintPlanById(planId);
+        }
+
+        logger.info("已清理旧的计划中数据,devTag={}, planCount={}", devTag, oldPlanList.size());
+        return oldPlanList.size();
+    }
+
+    private List<TRelMaintMemo> getMemoListForDevice(String devTag) {
+        // 备忘录按设备查询(devTag),再在内存中按 compoId 分组
+        if (devTag == null || devTag.trim().isEmpty()) {
+            return Collections.emptyList();
+        }
+        TRelMaintMemo memoQuery = new TRelMaintMemo();
+        memoQuery.setDevTag(devTag);
+        return maintMemoService.selectTRelMaintMemoList(memoQuery);
+    }
+
+    private CompoMaintTask buildTaskFromMemo(TRelCompo compo, TRelMaintMemo memo, Date now, Date endDate) {
+        // 将备忘录转成可参与生成的任务:使用 memoTime 作为任务日期(为空则使用当前日期)
+        if (compo == null || memo == null) {
+            return null;
+        }
+
+        Date memoTime = memo.getMemoTime();
+        if (memoTime != null && memoTime.after(endDate)) {
+            return null;
+        }
+
+        String maintType = memo.getMaintType();
+        if (maintType == null || maintType.trim().isEmpty()) {
+            // 兜底:防止客户未填维修类型导致生成记录 maintType 为空
+            maintType = "2";
+        }
+        String maintContent = memo.getMaintContent();
+        if (maintContent == null || maintContent.trim().isEmpty()) {
+            maintContent = memo.getMemoReason();
+        }
+        String responsible = memo.getResponsible();
+        if (responsible == null || responsible.trim().isEmpty()) {
+            if ("1".equals(maintType)) {
+                responsible = compo.getInspector();
+            } else {
+                responsible = compo.getFixer();
+            }
+        }
+
+        Date taskDate = memoTime != null ? memoTime : now;
+        // 若备忘录时间早于当前日期,为避免计划开始时间落在过去,这里按当前日期落计划
+        if (taskDate.before(now)) {
+            taskDate = now;
+        }
+
+        CompoMaintTask task = new CompoMaintTask();
+        task.compo = compo;
+        task.maintType = maintType;
+        task.maintDate = taskDate;
+        task.maintContent = maintContent;
+        task.responsible = responsible;
+        task.fromMemo = true;
+        return task;
+    }
+
     /**
      * 计算部件的最优维护任务
      *
@@ -373,6 +515,11 @@ public class MaintPlanGeneratorServiceImpl implements IMaintPlanGeneratorService
         record.setResponsible(task.responsible);
         record.setRecordStatus(RECORD_STATUS_PLANNED);  // 9-计划中
 
+        if (task.fromMemo) {
+            // 记录备注用于在列表/导出中识别该条记录来源于备忘录
+            record.setRemarks("从备忘录添加");
+        }
+
         // 根据维修类型设置检查人
         if ("1".equals(task.maintType)) {
             record.setInspector(task.compo.getInspector());
@@ -391,5 +538,6 @@ public class MaintPlanGeneratorServiceImpl implements IMaintPlanGeneratorService
         Date maintDate;
         String maintContent;
         String responsible;
+        boolean fromMemo;
     }
 }

+ 42 - 0
master/src/main/java/com/ruoyi/project/reliability/service/impl/TRelMaintPlanServiceImpl.java

@@ -148,6 +148,35 @@ public class TRelMaintPlanServiceImpl implements ITRelMaintPlanService
         return tRelMaintPlanMapper.updateTRelMaintPlan(tRelMaintPlan);
     }
 
+    @Override
+    public void syncMaintRecordsOnly(Long planId, List<TRelMaintRecord> maintComponents, Long operatorUserId)
+    {
+        if (planId == null) {
+            throw new IllegalArgumentException("计划ID不能为空");
+        }
+        if (maintComponents == null) {
+            throw new IllegalArgumentException("维修部件列表不能为空");
+        }
+
+        TRelMaintPlan existing = tRelMaintPlanMapper.selectTRelMaintPlanById(planId);
+        if (existing == null) {
+            throw new IllegalArgumentException("维修计划不存在");
+        }
+
+        boolean isPlanned = "9".equals(existing.getApprovalStatus()) || "9".equals(existing.getCompletionStatus());
+        if (!isPlanned) {
+            throw new IllegalStateException("仅计划中(9)的维修计划允许仅保存");
+        }
+
+        TRelMaintPlan planForSync = new TRelMaintPlan();
+        planForSync.setPlant(existing.getPlant());
+        planForSync.setDevName(existing.getDevName());
+        planForSync.setDevTag(existing.getDevTag());
+        planForSync.setCreaterCode(operatorUserId != null ? String.valueOf(operatorUserId) : existing.getCreaterCode());
+
+        syncMaintRecords(planId, maintComponents, planForSync);
+    }
+
     /**
      * 同步维修记录:根据maintComponents对比实际的record,进行删除、新增或修改
      * @param planId 计划ID
@@ -258,4 +287,17 @@ public class TRelMaintPlanServiceImpl implements ITRelMaintPlanService
     {
         return tRelMaintPlanMapper.deleteTRelMaintPlanById(planId);
     }
+
+    /**
+     * 查询在指定结束日期区间内且未完成的维修计划(用于到期提醒)
+     *
+     * @param startDate 区间开始日期(含)
+     * @param endDate   区间结束日期(不含)
+     * @return 维修计划集合
+     */
+    @Override
+    public List<TRelMaintPlan> selectDueMaintPlanList(Date startDate, Date endDate)
+    {
+        return tRelMaintPlanMapper.selectDueMaintPlanList(startDate, endDate);
+    }
 }

+ 46 - 3
master/src/main/java/com/ruoyi/project/system/controller/SysAppVersionController.java

@@ -3,6 +3,7 @@ package com.ruoyi.project.system.controller;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
+import java.net.URI;
 import javax.servlet.http.HttpServletRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -19,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.framework.aspectj.lang.annotation.Log;
 import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
 import com.ruoyi.framework.web.controller.BaseController;
@@ -44,6 +46,11 @@ public class SysAppVersionController extends BaseController
     /** 版本号正则:支持 x.x.x 格式 */
     private static final String VERSION_PATTERN = "^\\d+(\\.\\d+){0,2}$";
 
+    private static final String NET_TYPE_INTRANET = "intranet";
+    private static final String NET_TYPE_EXTRANET = "extranet";
+
+    private static final String INTRANET_HOST = "cpms.basf-ypc.net.cn";
+
     @Autowired
     private ISysAppVersionService appVersionService;
 
@@ -55,14 +62,15 @@ public class SysAppVersionController extends BaseController
             @RequestParam(value = "version", required = false) String version,
             @RequestParam(value = "appVersion", required = false) String appVersion,
             @RequestParam(value = "platform", required = false) String platform,
+            @RequestParam(value = "baseUrl", required = false) String baseUrl,
             HttpServletRequest request)
     {
         // 生成请求追踪ID,便于日志排查
         String traceId = UUID.randomUUID().toString().substring(0, 8);
         String clientIp = getClientIp(request);
         
-        log.info("[{}] APP版本检查请求 - IP:{}, version:{}, appVersion:{}, platform:{}", 
-                traceId, clientIp, version, appVersion, platform);
+        log.info("[{}] APP版本检查请求 - IP:{}, version:{}, appVersion:{}, platform:{}, baseUrl:{}", 
+                traceId, clientIp, version, appVersion, platform, baseUrl);
         
         try {
             // 参数校验
@@ -98,8 +106,10 @@ public class SysAppVersionController extends BaseController
                 }
             }
             
+            String netType = resolveNetType(baseUrl, request);
+
             // 执行版本检查
-            Map<String, Object> result = appVersionService.checkVersion(version, appVersion, platform);
+            Map<String, Object> result = appVersionService.checkVersion(version, appVersion, platform, netType);
             
             boolean needUpdate = (boolean) result.getOrDefault("needUpdate", false);
             log.info("[{}] APP版本检查完成 - needUpdate:{}, latestVersion:{}", 
@@ -143,6 +153,39 @@ public class SysAppVersionController extends BaseController
         return ip;
     }
 
+    private String resolveNetType(String baseUrl, HttpServletRequest request)
+    {
+        String host = null;
+        if (StringUtils.isEmpty(baseUrl))
+        {
+            return NET_TYPE_EXTRANET;
+        }
+
+        try
+        {
+            String url = baseUrl.trim();
+            if (!url.startsWith("http://") && !url.startsWith("https://"))
+            {
+                url = "http://" + url;
+            }
+            URI uri = new URI(url);
+            host = uri.getHost();
+        }
+        catch (Exception e)
+        {
+            log.warn("APP版本检查 baseUrl解析失败: {}", baseUrl);
+            return NET_TYPE_EXTRANET;
+        }
+
+        if (StringUtils.isEmpty(host))
+        {
+            return NET_TYPE_EXTRANET;
+        }
+
+        host = host.toLowerCase();
+        return INTRANET_HOST.equals(host) ? NET_TYPE_INTRANET : NET_TYPE_EXTRANET;
+    }
+
     /**
      * 获取APP版本列表
      */

+ 13 - 0
master/src/main/java/com/ruoyi/project/system/domain/SysAppVersion.java

@@ -39,6 +39,8 @@ public class SysAppVersion extends BaseEntity
     /** 下载地址 */
     private String downloadUrl;
 
+    private String netType;
+
     /** 是否强制更新(0否 1是) */
     private String forceUpdate;
 
@@ -134,6 +136,16 @@ public class SysAppVersion extends BaseEntity
         this.downloadUrl = downloadUrl;
     }
 
+    public String getNetType()
+    {
+        return netType;
+    }
+
+    public void setNetType(String netType)
+    {
+        this.netType = netType;
+    }
+
     public String getForceUpdate()
     {
         return forceUpdate;
@@ -165,6 +177,7 @@ public class SysAppVersion extends BaseEntity
             .append("title", getTitle())
             .append("content", getContent())
             .append("downloadUrl", getDownloadUrl())
+            .append("netType", getNetType())
             .append("forceUpdate", getForceUpdate())
             .append("status", getStatus())
             .append("createBy", getCreateBy())

+ 6 - 2
master/src/main/java/com/ruoyi/project/system/mapper/SysAppVersionMapper.java

@@ -1,6 +1,7 @@
 package com.ruoyi.project.system.mapper;
 
 import java.util.List;
+import org.apache.ibatis.annotations.Param;
 import com.ruoyi.project.system.domain.SysAppVersion;
 
 /**
@@ -39,17 +40,20 @@ public interface SysAppVersionMapper
      *
      * @param appVersion 用户原生包版本
      * @param platform 平台
+     * @param netType 网络类型
      * @return APP版本信息
      */
-    public SysAppVersion selectLatestCompatibleWgt(String appVersion, String platform);
+    public SysAppVersion selectLatestCompatibleWgt(@Param("appVersion") String appVersion, @Param("platform") String platform,
+            @Param("netType") String netType);
 
     /**
      * 查询最新的整包更新版本(apk/ipa)
      *
      * @param platform 平台
+     * @param netType 网络类型
      * @return APP版本信息
      */
-    public SysAppVersion selectLatestApk(String platform);
+    public SysAppVersion selectLatestApk(@Param("platform") String platform, @Param("netType") String netType);
 
     /**
      * 新增APP版本

+ 1 - 1
master/src/main/java/com/ruoyi/project/system/service/ISysAppVersionService.java

@@ -35,7 +35,7 @@ public interface ISysAppVersionService
      * @param platform 平台
      * @return 版本检查结果
      */
-    public Map<String, Object> checkVersion(String version, String appVersion, String platform);
+    public Map<String, Object> checkVersion(String version, String appVersion, String platform, String netType);
 
     /**
      * 新增APP版本

+ 4 - 3
master/src/main/java/com/ruoyi/project/system/service/impl/SysAppVersionServiceImpl.java

@@ -51,16 +51,17 @@ public class SysAppVersionServiceImpl implements ISysAppVersionService
      * @param version 当前wgt版本
      * @param appVersion 原生包版本
      * @param platform 平台
+     * @param netType 网络类型
      * @return 版本检查结果
      */
     @Override
-    public Map<String, Object> checkVersion(String version, String appVersion, String platform)
+    public Map<String, Object> checkVersion(String version, String appVersion, String platform, String netType)
     {
         Map<String, Object> result = new HashMap<>();
         result.put("needUpdate", false);
 
         // 1. 先检查是否需要整包更新(原生包版本落后)
-        SysAppVersion latestApk = appVersionMapper.selectLatestApk(platform);
+        SysAppVersion latestApk = appVersionMapper.selectLatestApk(platform, netType);
         if (latestApk != null && StringUtils.isNotEmpty(appVersion))
         {
             // 用户原生包版本 < 最新整包版本,需要整包更新
@@ -79,7 +80,7 @@ public class SysAppVersionServiceImpl implements ISysAppVersionService
         }
 
         // 2. 原生包版本兼容,检查是否有wgt热更新
-        SysAppVersion latestWgt = appVersionMapper.selectLatestCompatibleWgt(appVersion, platform);
+        SysAppVersion latestWgt = appVersionMapper.selectLatestCompatibleWgt(appVersion, platform, netType);
         if (latestWgt != null)
         {
             // 用户wgt版本 < 最新兼容wgt版本,需要wgt热更新

+ 3 - 2
master/src/main/resources/application.yml

@@ -89,10 +89,11 @@ spring:
     # 端口,默认为6379
     port: 6379
     database: 1
+#    host: r-uf6enp06djfg3ut8e4pd.redis.rds.aliyuncs.com
+#    password: "r-uf6enp06djfg3ut8e4:Ssy666666"
+#    password: Ssy666666
 #    host: r-uf6jl950xjqofogjxi.redis.rds.aliyuncs.com
-#    # 密码
 #    password: "r-uf6jl950xjqofogjxi:kT6GShHXt3UvSEduX9K4"
-
 #    ssl:
 #      trust-store: /home/appadmin/ApsaraDB-CA-Chain.jks
     # 连接超时时间

+ 4 - 2
master/src/main/resources/mybatis/reliability/TRelCompoMapper.xml

@@ -37,13 +37,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="deptName" column="dept_name" />
         <result property="inspectorName" column="inspector_name" />
         <result property="fixerName" column="fixer_name" />
+        <result property="hasMemo" column="has_memo" />
     </resultMap>
 
     <sql id="selectTRelCompoVo">
-        select d.compo_id, d.plant, d.dev_loc, d.dev_tag, d.compo_name, d.compo_model, d.thickness_pt, d.compo_desc, d.compo_photo, d.inspector, d.fixer, d.insp_freq, d.insp_content, d.last_insp_date, d.fix_freq, d.fix_content, d.last_fix_date, d.replace_freq, d.replace_content, d.last_replace_date, d.next_maint_date, d.next_maint_type, d.del_flag, d.creater_code, d.createdate, d.updater_code, d.updatedate, d.dept_id, d.remarks, s.dept_name, st1.name as inspector_name, st2.name as fixer_name from t_rel_compo d
+        select d.compo_id, d.plant, d.dev_loc, d.dev_tag, d.compo_name, d.compo_model, d.thickness_pt, d.compo_desc, d.compo_photo, d.inspector, d.fixer, d.insp_freq, d.insp_content, d.last_insp_date, d.fix_freq, d.fix_content, d.last_fix_date, d.replace_freq, d.replace_content, d.last_replace_date, d.next_maint_date, d.next_maint_type, d.del_flag, d.creater_code, d.createdate, d.updater_code, d.updatedate, d.dept_id, d.remarks, s.dept_name, st1.name as inspector_name, st2.name as fixer_name, case when m.compo_id is not null then 1 else 0 end as has_memo from t_rel_compo d
       left join sys_dept s on s.dept_id = d.dept_id
       left join t_staffmgr st1 on st1.staffid = d.inspector
       left join t_staffmgr st2 on st2.staffid = d.fixer
+      left join (select distinct trim(compo_id) as compo_id from t_rel_maint_memo where del_flag = 0) m on m.compo_id = to_char(d.compo_id)
     </sql>
 
     <select id="selectTRelCompoList" parameterType="TRelCompo" resultMap="TRelCompoResult">
@@ -84,7 +86,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <select id="selectTRelCompoById" parameterType="Long" resultMap="TRelCompoResult">
         <include refid="selectTRelCompoVo"/>
-        where compo_id = #{compoId}
+        where d.compo_id = #{compoId}
     </select>
 
     <!-- 根据设备位号查询部件列表 -->

+ 12 - 0
master/src/main/resources/mybatis/reliability/TRelMaintPlanMapper.xml

@@ -142,4 +142,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </foreach>
     </update>
 
+    <!-- 查询审批已通过且未完成的维修计划(用于到期提醒),具体时间范围在任务中按时间差过滤 -->
+    <select id="selectDueMaintPlanList" resultMap="TRelMaintPlanResult">
+        <include refid="selectTRelMaintPlanVo"/>
+        <where>
+            and d.del_flag = 0
+            <!-- 审批已通过 -->
+            and d.approval_status = '1'
+            <!-- 未完成 -->
+            and d.completion_status = '0'
+        </where>
+    </select>
+
 </mapper>

+ 33 - 4
master/src/main/resources/mybatis/system/SysAppVersionMapper.xml

@@ -13,6 +13,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="title"        column="title"         />
         <result property="content"      column="content"       />
         <result property="downloadUrl"  column="download_url"  />
+        <result property="netType"      column="net_type"      />
         <result property="forceUpdate"  column="force_update"  />
         <result property="status"       column="status"        />
         <result property="createBy"     column="create_by"     />
@@ -24,7 +25,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <sql id="selectAppVersionVo">
         select version_id, version, app_version, platform, update_type, title, content,
-               download_url, force_update, status, create_by, create_time, update_by, update_time, remark
+               download_url, net_type, force_update, status, create_by, create_time, update_by, update_time, remark
         from sys_app_version
     </sql>
 
@@ -45,6 +46,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="updateType != null and updateType != ''">
                 AND update_type = #{updateType}
             </if>
+            <if test="netType != null and netType != ''">
+                AND net_type = #{netType}
+            </if>
             <if test="status != null and status != ''">
                 AND status = #{status}
             </if>
@@ -69,18 +73,40 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             and update_type = 'wgt'
             and (platform = #{platform} or platform = 'all')
             and (app_version is null or app_version = '' or app_version = #{appVersion})
-            order by create_time desc
+            <if test="netType != null and netType != ''">
+                and net_type = #{netType}
+            </if>
+            order by
+            <choose>
+                <when test="netType != null and netType != ''">
+                    create_time desc
+                </when>
+                <otherwise>
+                    create_time desc
+                </otherwise>
+            </choose>
         ) where rownum = 1
     </select>
 
     <!-- 查询最新的整包更新版本 -->
-    <select id="selectLatestApk" parameterType="String" resultMap="SysAppVersionResult">
+    <select id="selectLatestApk" resultMap="SysAppVersionResult">
         select * from (
             <include refid="selectAppVersionVo"/>
             where status = '0'
             and update_type = 'apk'
             and (platform = #{platform} or platform = 'all')
-            order by create_time desc
+            <if test="netType != null and netType != ''">
+                and net_type = #{netType}
+            </if>
+            order by
+            <choose>
+                <when test="netType != null and netType != ''">
+                    create_time desc
+                </when>
+                <otherwise>
+                    create_time desc
+                </otherwise>
+            </choose>
         ) where rownum = 1
     </select>
 
@@ -97,6 +123,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="title != null and title != ''">title,</if>
             <if test="content != null and content != ''">content,</if>
             <if test="downloadUrl != null and downloadUrl != ''">download_url,</if>
+            <if test="netType != null and netType != ''">net_type,</if>
             <if test="forceUpdate != null and forceUpdate != ''">force_update,</if>
             <if test="status != null and status != ''">status,</if>
             <if test="remark != null and remark != ''">remark,</if>
@@ -111,6 +138,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="title != null and title != ''">#{title},</if>
             <if test="content != null and content != ''">#{content},</if>
             <if test="downloadUrl != null and downloadUrl != ''">#{downloadUrl},</if>
+            <if test="netType != null and netType != ''">#{netType},</if>
             <if test="forceUpdate != null and forceUpdate != ''">#{forceUpdate},</if>
             <if test="status != null and status != ''">#{status},</if>
             <if test="remark != null and remark != ''">#{remark},</if>
@@ -129,6 +157,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="title != null">title = #{title},</if>
             <if test="content != null">content = #{content},</if>
             <if test="downloadUrl != null and downloadUrl != ''">download_url = #{downloadUrl},</if>
+            <if test="netType != null and netType != ''">net_type = #{netType},</if>
             <if test="forceUpdate != null and forceUpdate != ''">force_update = #{forceUpdate},</if>
             <if test="status != null and status != ''">status = #{status},</if>
             <if test="remark != null">remark = #{remark},</if>

+ 8 - 0
ui/src/api/reliability/rel_maint_plan.js

@@ -79,6 +79,14 @@ export function resubmitApprove(data) {
   })
 }
 
+export function saveOnlyRel_maint_plan(data) {
+  return request({
+    url: '/reliability/rel_maint_plan/saveOnly',
+    method: 'post',
+    data: data
+  })
+}
+
 // 自动生成所有设备的维修计划
 export function generateMaintPlans(monthsAhead, mergeThresholdDays) {
   return request({

+ 3 - 2
ui/src/api/system/appVersion.js

@@ -44,14 +44,15 @@ export function delAppVersion(versionId) {
 }
 
 // 检查APP版本更新
-export function checkAppVersion(version, appVersion, platform) {
+export function checkAppVersion(version, appVersion, platform, baseUrl) {
   return request({
     url: '/system/appVersion/check',
     method: 'get',
     params: {
       version: version,
       appVersion: appVersion,
-      platform: platform
+      platform: platform,
+      baseUrl: baseUrl
     }
   })
 }

+ 6 - 1
ui/src/views/reliability/rel_compo/index.vue

@@ -126,7 +126,12 @@
       <el-table-column label="装置" align="center" prop="plant" width="120" :show-overflow-tooltip="true"/>
       <el-table-column label="位置" align="center" prop="devLoc" width="120" :show-overflow-tooltip="true"/>
       <el-table-column label="设备位号" align="center" prop="devTag" width="150" :show-overflow-tooltip="true"/>
-      <el-table-column label="部件名称" align="center" prop="compoName" width="150" :show-overflow-tooltip="true"/>
+      <el-table-column label="部件名称" align="center" prop="compoName" width="150" :show-overflow-tooltip="true">
+        <template slot-scope="scope">
+          <span>{{ scope.row.compoName }}</span>
+          <el-tag v-if="scope.row.hasMemo" type="warning" size="mini" style="margin-left: 6px;">备</el-tag>
+        </template>
+      </el-table-column>
       <el-table-column label="部件型号" align="center" prop="compoModel" width="150" :show-overflow-tooltip="true"/>
       <el-table-column label="测厚点" align="center" prop="thicknessPt" width="120" :show-overflow-tooltip="true"/>
       <el-table-column label="部件描述" align="center" prop="compoDesc" width="180" :show-overflow-tooltip="true"/>

+ 10 - 3
ui/src/views/reliability/rel_device/detail.vue

@@ -73,6 +73,7 @@
               <el-table-column label="部件名称" align="center" prop="compoName" width="120" :show-overflow-tooltip="true">
                 <template slot-scope="scope">
                   <el-button type="text" @click="handleCompoClick(scope.row)">{{ scope.row.compoName }}</el-button>
+                  <el-tag v-if="scope.row.hasMemo" type="warning" size="mini" style="margin-left: 6px;">备</el-tag>
                 </template>
               </el-table-column>
               <el-table-column label="检查频率" align="center" prop="inspFreq" width="110" :show-overflow-tooltip="true">
@@ -161,7 +162,11 @@
     </div>
 
     <!-- 部件详情对话框 -->
-    <el-dialog :title="selectedCompo ? selectedCompo.compoName : '部件信息'" :visible.sync="compoDialogVisible" width="900px" append-to-body class="compo-detail-dialog">
+    <el-dialog :visible.sync="compoDialogVisible" width="900px" append-to-body class="compo-detail-dialog">
+      <template slot="title">
+        <span>{{ selectedCompo ? selectedCompo.compoName : '部件信息' }}</span>
+        <el-tag v-if="selectedCompo && selectedCompo.hasMemo" type="warning" size="mini" style="margin-left: 6px;">备</el-tag>
+      </template>
       <div v-if="selectedCompo" class="compo-detail-content">
         <!-- 部件图片 -->
         <div v-if="selectedCompo.compoPhoto" class="compo-photo-section">
@@ -430,7 +435,8 @@ export default {
         '-1': 'danger',    // 删除申请中
         '0': 'info',       // 新增申请中
         '1': 'warning',    // 待完成
-        '2': 'success'     // 已完成
+        '2': 'success',    // 已完成
+        '3': 'warning'     // 已延期
       };
       return statusMap[status] || 'info';
     },
@@ -442,7 +448,8 @@ export default {
         '-1': '删除申请中',
         '0': '新增申请中',
         '1': '待完成',
-        '2': '已完成'
+        '2': '已完成',
+        '3': '已延期'
       };
       return statusMap[status] || '-';
     },

+ 20 - 10
ui/src/views/reliability/rel_maint_plan/MaintPlanDetailContent.vue

@@ -72,7 +72,7 @@
         style="margin-top: 10px"
         v-loading="recordsLoading">
         <el-table-column type="index" label="序号" width="60" align="center" />
-        <el-table-column label="部件名称" prop="compoName" width="150" />
+        <el-table-column label="部件名称" align="center" prop="compoName" width="150" />
         <el-table-column label="维修类型" width="80" align="center">
           <template slot-scope="scope">
             <span v-if="scope.row.maintType === '1'">检查</span>
@@ -101,29 +101,37 @@
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="检查内容" prop="inspectContent" :show-overflow-tooltip="true" />
-        <el-table-column label="维修内容" prop="maintContent" :show-overflow-tooltip="true" />
-        <el-table-column label="维修结果" prop="maintResult" width="100" :show-overflow-tooltip="true" />
-        <el-table-column label="维修时间" width="120" align="center">
+        <el-table-column label="检查/维修时间" width="120" align="center">
           <template slot-scope="scope">
-            <span v-if="scope.row.maintTime">{{ parseTime(scope.row.maintTime, '{y}-{m}-{d}') }}</span>
+            <span v-if="scope.row.inspectTime || scope.row.maintTime">
+              <span v-if="scope.row.inspectTime">
+                {{ parseTime(scope.row.inspectTime, '{y}-{m}-{d}') }}
+              </span>
+              <span v-if="scope.row.inspectTime && scope.row.maintTime"> / </span>
+              <span v-if="scope.row.maintTime">
+                {{ parseTime(scope.row.maintTime, '{y}-{m}-{d}') }}
+              </span>
+            </span>
             <span v-else>-</span>
           </template>
         </el-table-column>
-        <el-table-column label="维修费用" prop="maintCost" width="100" align="right">
+        <el-table-column label="检查内容" prop="inspectContent" align="center" :show-overflow-tooltip="true" />
+        <el-table-column label="维修内容" prop="maintContent" align="center" :show-overflow-tooltip="true" />
+        <el-table-column label="维修结果" prop="maintResult"  align="center" width="100" :show-overflow-tooltip="true" />
+        <el-table-column label="维修费用k" align="center" prop="maintCost" width="100" >
           <template slot-scope="scope">
             <span v-if="scope.row.maintCost">{{ scope.row.maintCost }}</span>
             <span v-else>-</span>
           </template>
         </el-table-column>
-        <el-table-column label="维修时长" prop="maintDuration" width="100" align="center">
+        <el-table-column label="维修时长h" align="center" prop="maintDuration" width="100" >
           <template slot-scope="scope">
             <span v-if="scope.row.maintDuration">{{ scope.row.maintDuration }}</span>
             <span v-else>-</span>
           </template>
         </el-table-column>
-        <el-table-column label="工艺损失" prop="processLoss" :show-overflow-tooltip="true" />
-        <el-table-column label="备注" prop="remarks" :show-overflow-tooltip="true" />
+        <el-table-column label="工艺损失k" align="center" prop="processLoss" width="100" :show-overflow-tooltip="true" />
+        <el-table-column label="备注" align="center" prop="remarks" :show-overflow-tooltip="true" />
       </el-table>
       <div v-if="maintRecords.length === 0 && !recordsLoading" style="text-align: center; padding: 20px; color: #909399;">
         暂无维修记录
@@ -212,6 +220,7 @@ export default {
       if (status === 0 || status === '0') return '新增申请中';
       if (status === 1 || status === '1') return '待完成';
       if (status === 2 || status === '2') return '已完成';
+      if (status === 3 || status === '3') return '已延期';
       return status != null ? status : '-';
     },
     /** 获取维修状态标签类型 */
@@ -220,6 +229,7 @@ export default {
       if (status === 0 || status === '0') return 'warning';
       if (status === 1 || status === '1') return 'info';
       if (status === 2 || status === '2') return 'success';
+      if (status === 3 || status === '3') return 'warning';
       return '';
     },
     /** 查看备忘录 */

+ 8 - 1
ui/src/views/reliability/rel_maint_plan/MaintPlanForm.vue

@@ -140,7 +140,12 @@
           :height="500"
           style="margin-top: 10px">
           <el-table-column type="index" label="序号" width="60" align="center" />
-          <el-table-column label="部件名称" prop="compoName" width="150" :show-overflow-tooltip="true" />
+          <el-table-column label="部件名称" prop="compoName" width="150" :show-overflow-tooltip="true">
+            <template slot-scope="scope">
+              <span>{{ scope.row.compoName }}</span>
+              <el-tag v-if="scope.row.hasMemo" type="warning" size="mini" style="margin-left: 6px;">备</el-tag>
+            </template>
+          </el-table-column>
           <el-table-column label="检查频率/日期" width="120" align="center">
             <template slot-scope="scope">
               <div style="line-height: 1.2;">
@@ -657,6 +662,7 @@ export default {
       if (status === 0 || status === '0') return '新增申请中';
       if (status === 1 || status === '1') return '待完成';
       if (status === 2 || status === '2') return '已完成';
+      if (status === 3 || status === '3') return '已延期';
       return status != null ? status : '-';
     },
     /** 获取维修状态标签类型 */
@@ -665,6 +671,7 @@ export default {
       if (status === 0 || status === '0') return 'warning';
       if (status === 1 || status === '1') return 'info';
       if (status === 2 || status === '2') return 'success';
+      if (status === 3 || status === '3') return 'warning';
       return '';
     },
     /** 提交表单 */

+ 50 - 2
ui/src/views/reliability/rel_maint_plan/form.vue

@@ -7,6 +7,7 @@
       </div>
       <div class="header-right">
         <el-button icon="el-icon-arrow-left" @click="goBack">返回</el-button>
+        <el-button v-if="formData.planId && isPlannedStatus" @click="handleSaveOnly" :loading="savingOnly">仅保存</el-button>
         <el-button type="primary" @click="handleSubmit" :loading="submitting">
           {{ isPlannedStatus ? '提交申请' : (formData.planId && formData.approvalStatus === '1' ? '再次提交申请' : '确 定') }}
         </el-button>
@@ -158,7 +159,12 @@
                 {{ (compoCurrentPage - 1) * compoPageSize + scope.$index + 1 }}
               </template>
             </el-table-column>
-            <el-table-column label="部件名称" prop="compoName" :show-overflow-tooltip="true" />
+            <el-table-column label="部件名称" prop="compoName" :show-overflow-tooltip="true">
+              <template slot-scope="scope">
+                <span>{{ scope.row.compoName }}</span>
+                <el-tag v-if="scope.row.hasMemo" type="warning" size="mini" style="margin-left: 6px;">备</el-tag>
+              </template>
+            </el-table-column>
             <el-table-column label="检查频率/日期" width="120" align="center">
               <template slot-scope="scope">
                 <div style="line-height: 1.2;">
@@ -258,7 +264,7 @@
 import { listRel_device, getRel_device } from "@/api/reliability/rel_device";
 import { listRel_compo } from "@/api/reliability/rel_compo";
 import { listStaffmgrAll } from "@/api/plant/staffmgr";
-import { getRel_maint_plan, submitApprove, resubmitApprove } from "@/api/reliability/rel_maint_plan";
+import { getRel_maint_plan, submitApprove, resubmitApprove, saveOnlyRel_maint_plan } from "@/api/reliability/rel_maint_plan";
 import { parseTime as formatTime } from "@/utils/ruoyi";
 
 export default {
@@ -266,6 +272,7 @@ export default {
   data() {
     return {
       submitting: false,
+      savingOnly: false,
       formData: {},
       rules: {
         devId: [
@@ -381,6 +388,45 @@ export default {
         }
       });
     },
+
+    handleSaveOnly() {
+      if (this.savingOnly) {
+        return;
+      }
+      if (!this.formData.planId) {
+        this.$message.error("计划ID不能为空");
+        return;
+      }
+      if (!this.isPlannedStatus) {
+        this.$message.error("仅计划中状态允许仅保存");
+        return;
+      }
+
+      this.savingOnly = true;
+
+      if (this.compoList.length > 0) {
+        const maintComponents = this.compoList
+          .filter(compo => compo.needMaint)
+          .map(compo => ({
+            compoId: compo.compoId,
+            compoName: compo.compoName,
+            maintType: compo.maintType,
+            responsible: compo.maintResponsible
+          }));
+        this.formData.maintComponents = maintComponents;
+      } else {
+        this.formData.maintComponents = [];
+      }
+
+      saveOnlyRel_maint_plan({
+        planId: this.formData.planId,
+        maintComponents: this.formData.maintComponents
+      }).then(() => {
+        this.$message.success("保存成功");
+      }).finally(() => {
+        this.savingOnly = false;
+      });
+    },
     /** 查询人员列表 */
     getStaffList() {
       return listStaffmgrAll().then(response => {
@@ -569,6 +615,7 @@ export default {
       if (status === 0 || status === '0') return '新增申请中';
       if (status === 1 || status === '1') return '待完成';
       if (status === 2 || status === '2') return '已完成';
+      if (status === 3 || status === '3') return '已延期';
       return status != null ? status : '-';
     },
     /** 获取维修状态标签类型 */
@@ -577,6 +624,7 @@ export default {
       if (status === 0 || status === '0') return 'warning';
       if (status === 1 || status === '1') return 'info';
       if (status === 2 || status === '2') return 'success';
+      if (status === 3 || status === '3') return 'warning';
       return '';
     },
     /** 提交表单 */

+ 13 - 9
ui/src/views/reliability/rel_maint_record/index.vue

@@ -51,6 +51,7 @@
           <el-option label="新增申请中" value="0" />
           <el-option label="待完成" value="1" />
           <el-option label="已完成" value="2" />
+          <el-option label="已延期" value="3" />
         </el-select>
       </el-form-item>
 
@@ -166,9 +167,9 @@
           <span>{{ parseTime(scope.row.maintTime, '{y}-{m}-{d}') }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="维修费用" align="center" prop="maintCost" :show-overflow-tooltip="true"/>
-      <el-table-column label="维修时长" align="center" prop="maintDuration" :show-overflow-tooltip="true"/>
-      <el-table-column label="工艺损失" align="center" prop="processLoss" :show-overflow-tooltip="true"/>
+      <el-table-column label="维修费用(k)" align="center" prop="maintCost" :show-overflow-tooltip="true"/>
+      <el-table-column label="维修时长(h)" align="center" prop="maintDuration" :show-overflow-tooltip="true"/>
+      <el-table-column label="工艺损失(k)" align="center" prop="processLoss" :show-overflow-tooltip="true"/>
       <el-table-column label="照片" align="center" width="80">
         <template slot-scope="scope">
           <el-button
@@ -254,6 +255,7 @@
             <el-option label="新增申请中" value="0" />
             <el-option label="待完成" value="1" />
             <el-option label="已完成" value="2" />
+            <el-option label="已延期" value="3" />
           </el-select>
         </el-form-item>
         <el-form-item label="维修部门" prop="maintDept">
@@ -290,14 +292,14 @@
             placeholder="选择维修时间">
           </el-date-picker>
         </el-form-item>
-        <el-form-item label="维修费用" prop="maintCost">
-          <el-input v-model="form.maintCost" placeholder="请输入维修费用" />
+        <el-form-item label="维修费用(k)" prop="maintCost">
+          <el-input v-model="form.maintCost" placeholder="请输入维修费用(k)" />
         </el-form-item>
-        <el-form-item label="维修时长" prop="maintDuration">
-          <el-input v-model="form.maintDuration" placeholder="请输入维修时长" />
+        <el-form-item label="维修时长(h)" prop="maintDuration">
+          <el-input v-model="form.maintDuration" placeholder="请输入维修时长(h)" />
         </el-form-item>
-        <el-form-item label="工艺损失" prop="processLoss">
-          <el-input v-model="form.processLoss" placeholder="请输入工艺损失" />
+        <el-form-item label="工艺损失(k)" prop="processLoss">
+          <el-input v-model="form.processLoss" placeholder="请输入工艺损失(k)" />
         </el-form-item>
         <el-form-item label="附件" prop="attachments">
           <el-input v-model="form.attachments" placeholder="请输入附件" />
@@ -601,6 +603,7 @@ export default {
       if (status === 0 || status === '0') return '新增申请中';
       if (status === 1 || status === '1') return '待完成';
       if (status === 2 || status === '2') return '已完成';
+      if (status === 3 || status === '3') return '已延期';
       return status != null ? status : '-';
     },
     /** 获取维修状态标签类型 */
@@ -609,6 +612,7 @@ export default {
       if (status === 0 || status === '0') return 'warning';
       if (status === 1 || status === '1') return 'info';
       if (status === 2 || status === '2') return 'success';
+      if (status === 3 || status === '3') return 'warning';
       return '';
     },
     /** 查看备忘录 */

+ 23 - 29
ui/src/views/reliability/rel_maint_record/myRecord.vue

@@ -1,10 +1,7 @@
 <template>
   <div class="app-container my-record-page">
     <!-- 搜索表单 -->
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px" class="search-form">
-      <el-form-item label="装置" prop="plant">
-        <el-input v-model="queryParams.plant" placeholder="请输入装置" clearable size="small" @keyup.enter.native="handleQuery"/>
-      </el-form-item>
+    <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="80px" class="search-form">
       <el-form-item label="设备名称" prop="devName">
         <el-input v-model="queryParams.devName" placeholder="请输入设备名称" clearable size="small" @keyup.enter.native="handleQuery"/>
       </el-form-item>
@@ -27,10 +24,6 @@
       </el-form-item>
     </el-form>
 
-    <el-row :gutter="10" class="mb5">
-      <right-toolbar :showSearch.sync="showSearch" @queryTable="handleQuery"></right-toolbar>
-    </el-row>
-
     <!-- Tab布局 -->
     <el-tabs v-model="activeTab" type="border-card" @tab-click="handleTabClick">
       <!-- 未完成 Tab -->
@@ -106,9 +99,9 @@
       <el-table-column label="维修内容" align="center" prop="maintContent" :show-overflow-tooltip="true"/>
       <el-table-column label="维修结果" align="center" prop="maintResult" :show-overflow-tooltip="true"/>
 
-      <el-table-column label="维修费用" align="center" prop="maintCost" :show-overflow-tooltip="true"/>
-      <el-table-column label="维修时长" align="center" prop="maintDuration" :show-overflow-tooltip="true"/>
-      <el-table-column label="工艺损失" align="center" prop="processLoss" :show-overflow-tooltip="true"/>
+      <el-table-column label="维修费用(k)" align="center" prop="maintCost" :show-overflow-tooltip="true"/>
+      <el-table-column label="维修时长(h)" align="center" prop="maintDuration" :show-overflow-tooltip="true"/>
+      <el-table-column label="工艺损失(k)" align="center" prop="processLoss" :show-overflow-tooltip="true"/>
       <el-table-column label="照片" align="center" width="80">
         <template slot-scope="scope">
           <el-button
@@ -175,9 +168,15 @@
                 <span>{{ getStaffNameById(scope.row.responsible) || '-' }}</span>
               </template>
             </el-table-column>
-            <el-table-column label="维修状态" align="center" width="80">
+            <el-table-column label="维修状态" align="center" width="150">
               <template slot-scope="scope">
-                <el-tag type="success" size="small">已完成</el-tag>
+                <el-tag v-if="scope.row.recordStatus === 9 || scope.row.recordStatus === '9'" type="" size="small">计划中</el-tag>
+                <el-tag v-else-if="scope.row.recordStatus === -1 || scope.row.recordStatus === '-1'" type="danger" size="small">删除申请中</el-tag>
+                <el-tag v-else-if="scope.row.recordStatus === 0 || scope.row.recordStatus === '0'" type="warning" size="small">新增申请中</el-tag>
+                <el-tag v-else-if="scope.row.recordStatus === 1 || scope.row.recordStatus === '1'" type="info" size="small">待完成</el-tag>
+                <el-tag v-else-if="scope.row.recordStatus === 2 || scope.row.recordStatus === '2'" type="success" size="small">已完成</el-tag>
+                <el-tag v-else-if="scope.row.recordStatus === 3 || scope.row.recordStatus === '3'" type="warning" size="small">已延期</el-tag>
+                <span v-else>{{ scope.row.recordStatus || '-' }}</span>
               </template>
             </el-table-column>
             <el-table-column label="维修计划" align="center" width="100">
@@ -417,38 +416,38 @@
                 </el-form-item>
               </el-col>
               <el-col :span="12">
-                <el-form-item label="维修费用" prop="maintCost">
+                <el-form-item label="维修费用(k)" prop="maintCost">
                   <el-input-number
                     v-model="processForm.maintCost"
                     :min="0"
                     :precision="1"
                     :step="0.1"
                     style="width: 100%"
-                    placeholder="请输入维修费用" />
+                    placeholder="请输入维修费用(k)" />
                 </el-form-item>
               </el-col>
             </el-row>
             <el-row :gutter="20">
               <el-col :span="12">
-                <el-form-item label="维修时长" prop="maintDuration">
+                <el-form-item label="维修时长(h)" prop="maintDuration">
                   <el-input-number
                     v-model="processForm.maintDuration"
                     :min="0"
                     :precision="1"
                     :step="1"
                     style="width: 100%"
-                    placeholder="请输入维修时长(小时)" />
+                    placeholder="请输入维修时长(h)" />
                 </el-form-item>
               </el-col>
               <el-col :span="12">
-                <el-form-item label="工艺损失" prop="processLoss">
+                <el-form-item label="工艺损失(k)" prop="processLoss">
                   <el-input-number
                     v-model="processForm.processLoss"
                     :min="0"
                     :precision="1"
                     :step="0.1"
                     style="width: 100%"
-                    placeholder="请输入工艺损失" />
+                    placeholder="请输入工艺损失(k)" />
                 </el-form-item>
               </el-col>
             </el-row>
@@ -589,8 +588,6 @@ export default {
       activeTab: 'incomplete',
       // 表格高度
       tableHeight: 400,
-      // 显示搜索条件
-      showSearch: false,
 
       // 未完成数据
       incompleteList: [],
@@ -666,7 +663,6 @@ export default {
         },
       // 搜索参数
       queryParams: {
-        plant: null,
         devName: null,
         devTag: null,
         compoName: null,
@@ -682,11 +678,6 @@ export default {
     };
   },
   watch: {
-    showSearch() {
-      this.$nextTick(() => {
-        this.tableHeight = document.body.clientHeight - (this.showSearch ? 280 : 220);
-      });
-    },
     // 监听是否需要维修/更换,自动填充负责人
     'processForm.needMaintOrReplace'(newVal) {
       if (newVal && this.processForm.compo && this.processForm.compo.fixer) {
@@ -789,6 +780,7 @@ export default {
       if (status === 0 || status === '0') return '新增申请中';
       if (status === 1 || status === '1') return '待完成';
       if (status === 2 || status === '2') return '已完成';
+      if (status === 3 || status === '3') return '已延期';
       return status != null ? status : '-';
     },
     /** 查看备忘录 */
@@ -1217,9 +1209,11 @@ export default {
 <style scoped lang="scss">
 .my-record-page {
   .search-form {
-    padding: 5px 0;
+    padding: 10px 12px;
     background-color: #fff;
-    margin-bottom: 5px;
+    margin-bottom: 8px;
+    border-radius: 4px;
+    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
   }
 
   .mb5 {

+ 27 - 0
ui/src/views/system/appVersion/index.vue

@@ -23,6 +23,12 @@
           <el-option :label="$t('整包更新')" value="apk" />
         </el-select>
       </el-form-item>
+      <el-form-item :label="$t('网络类型')" prop="netType">
+        <el-select v-model="queryParams.netType" :placeholder="$t('请选择') + $t('网络类型')" clearable size="small">
+          <el-option :label="$t('内网')" value="intranet" />
+          <el-option :label="$t('外网')" value="extranet" />
+        </el-select>
+      </el-form-item>
       <el-form-item :label="$t('状态')" prop="status">
         <el-select v-model="queryParams.status" :placeholder="$t('请选择') + $t('状态')" clearable size="small">
           <el-option :label="$t('正常')" value="0" />
@@ -80,6 +86,13 @@
           <el-tag v-else type="info">{{ $t('全部') }}</el-tag>
         </template>
       </el-table-column>
+      <el-table-column :label="$t('网络类型')" align="center" prop="netType" width="100">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.netType === 'intranet'" type="success">{{ $t('内网') }}</el-tag>
+          <el-tag v-else-if="scope.row.netType === 'extranet'" type="warning">{{ $t('外网') }}</el-tag>
+          <el-tag v-else type="info">{{ $t('通用') }}</el-tag>
+        </template>
+      </el-table-column>
       <el-table-column :label="$t('更新类型')" align="center" prop="updateType" width="100">
         <template slot-scope="scope">
           <el-tag v-if="scope.row.updateType === 'wgt'" type="primary">{{ $t('热更新') }}</el-tag>
@@ -156,6 +169,12 @@
             <el-option :label="$t('全部平台')" value="all" />
           </el-select>
         </el-form-item>
+        <el-form-item :label="$t('网络类型')" prop="netType">
+          <el-select v-model="form.netType" :placeholder="$t('请选择') + $t('网络类型')" style="width: 100%">
+            <el-option :label="$t('内网')" value="intranet" />
+            <el-option :label="$t('外网')" value="extranet" />
+          </el-select>
+        </el-form-item>
         <el-form-item :label="$t('更新类型')" prop="updateType">
           <el-select v-model="form.updateType" :placeholder="$t('请选择') + $t('更新类型')" style="width: 100%">
             <el-option :label="$t('热更新') + ' (wgt)'" value="wgt" />
@@ -251,6 +270,7 @@ export default {
         version: undefined,
         platform: undefined,
         updateType: undefined,
+        netType: undefined,
         status: undefined
       },
       // 表单参数
@@ -263,6 +283,9 @@ export default {
         platform: [
           { required: true, message: this.$t('平台') + this.$t('不能为空'), trigger: "change" }
         ],
+        netType: [
+          { required: true, message: this.$t('网络类型') + this.$t('不能为空'), trigger: "change" }
+        ],
         updateType: [
           { required: true, message: this.$t('更新类型') + this.$t('不能为空'), trigger: "change" }
         ],
@@ -297,6 +320,7 @@ export default {
         version: undefined,
         appVersion: undefined,
         platform: "android",
+        netType: "extranet",
         updateType: "wgt",
         title: undefined,
         content: undefined,
@@ -335,6 +359,9 @@ export default {
       const versionId = row.versionId || this.ids[0];
       getAppVersion(versionId).then(response => {
         this.form = response.data;
+        if (!this.form.netType) {
+          this.form.netType = "extranet";
+        }
         this.open = true;
         this.title = this.$t('修改') + " APP " + this.$t('版本');
       });