Просмотр исходного кода

维修、维保工单、工时、工分导出

HD_wangm 2 месяцев назад
Родитель
Сommit
f1cb82d9f7

+ 19 - 2
ygtx-gxt/src/main/java/com/ygtx/gxt/controller/GxtRepairOrderController.java

@@ -4,6 +4,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import javax.servlet.ServletOutputStream;
@@ -13,7 +14,8 @@ import javax.servlet.http.HttpServletResponse;
 import com.github.pagehelper.util.StringUtil;
 import com.ygtx.common.utils.DictUtils;
 import com.ygtx.common.utils.SecurityUtils;
-import com.ygtx.gxt.domain.GxtOrderData;
+import com.ygtx.gxt.domain.*;
+import com.ygtx.gxt.service.IGxtOrderScoreService;
 import com.ygtx.gxt.service.IGxtWorkOrderService;
 import com.ygtx.system.service.ISysConfigService;
 import com.ygtx.system.strategy.AutoCodeUtil;
@@ -26,7 +28,6 @@ import com.ygtx.common.annotation.Log;
 import com.ygtx.common.core.controller.BaseController;
 import com.ygtx.common.core.domain.AjaxResult;
 import com.ygtx.common.enums.BusinessType;
-import com.ygtx.gxt.domain.GxtRepairOrder;
 import com.ygtx.gxt.service.IGxtRepairOrderService;
 import com.ygtx.common.utils.poi.ExcelUtil;
 import com.ygtx.common.core.page.TableDataInfo;
@@ -57,6 +58,8 @@ public class GxtRepairOrderController extends BaseController
     private IGxtWorkOrderService gxtWorkOrderService;
     @Autowired
     private ISysConfigService configService;
+    @Autowired
+    private IGxtOrderScoreService gxtOrderScoreService;
 
     /**
      * 查询维修工单列表
@@ -105,6 +108,20 @@ public class GxtRepairOrderController extends BaseController
         return success(util.getExportFields());
     }
 
+    /**
+     * 获取维修工单数据列表
+     */
+//    @PreAuthorize("@ss.hasPermi('gxt:repairOrder:list')")
+    @GetMapping("/getOrderDataList")
+    @ApiOperation("获取维修工单数据列表")
+    public TableDataInfo getOrderDataList(GxtRepairOrder repairOrder, GxtWorkOrder workOrder) {
+
+//        repairOrder.setOrderType(1L);
+//        workOrder.setOrderType(1L);
+        List<OrderScoreInfo> list = gxtOrderScoreService.selectUnionExportOrderList(repairOrder, workOrder);
+        return getDataTable(list);
+    }
+
     /**
      * 获取维修工单详细信息
      */

+ 10 - 0
ygtx-gxt/src/main/java/com/ygtx/gxt/domain/GxtOrderData.java

@@ -223,6 +223,8 @@ public class GxtOrderData extends BaseEntity
 
     private Integer companyRepairRestartOrderNum;
 
+    private String workOrderStatus;
+
     public Integer getCompanyWorkOrderNum() {
         return companyWorkOrderNum;
     }
@@ -761,6 +763,14 @@ public class GxtOrderData extends BaseEntity
         this.companyRepairRestartOrderNum = companyRepairRestartOrderNum;
     }
 
+    public String getWorkOrderStatus() {
+        return workOrderStatus;
+    }
+
+    public void setWorkOrderStatus(String workOrderStatus) {
+        this.workOrderStatus = workOrderStatus;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 276 - 103
ygtx-gxt/src/main/java/com/ygtx/gxt/domain/OrderScoreInfo.java

@@ -1,6 +1,7 @@
 package com.ygtx.gxt.domain;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ygtx.common.annotation.Excel;
 
 import java.math.BigDecimal;
 import java.util.Date;
@@ -8,7 +9,7 @@ import java.util.List;
 
 /**
  * 工单评分信息DTO
- * 
+ *
  * @author lingming
  * @date 2025-11-12
  */
@@ -25,6 +26,10 @@ public class OrderScoreInfo {
     /** 工单状态 */
     private String workOrderStatus;
 
+    /** 结单方式(1自动结单,手动结单) */
+    @Excel(name = "结单方式", readConverterExp = "1=自动结单,2=手动结单")
+    private String finalizeMethod;
+
     /** 维保中心ID */
     private Long gxtCenterId;
 
@@ -40,26 +45,53 @@ public class OrderScoreInfo {
     /** 风电场 */
     private String pcsStationName;
 
+    /** 风机品牌 */
+    private String brand;
+
+    /** 风机型号 */
+    private String model;
+
     /** 风机设备ID */
     private Long pcsDeviceId;
 
     /** 风机编号 */
     private String pcsDeviceName;
 
-    /** 风机品牌 */
-    private String brand;
+    /** 故障代码 */
+    private String faultCode;
 
-    /** 风机型号 */
-    private String model;
+    /** 故障条文 */
+    private String faultBarcode;
 
-    /** 派单时间 */
+    /** 故障描述 */
+    private String faultDesc;
+
+    /** 真实故障原因 */
+    private String realFailureReason;
+
+    /** 发生时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
+    private Date occurTime;
+
+    /** 停机时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
+    private Date pauseTime;
+
+    /** 恢复运行时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
+    private Date restartTime;
+
+    /** 损失电量(单位kWh) */
+    private BigDecimal lostPower;
+
+    /** 下发时间 */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
     private Date assignTime;
 
-    /** 派单人ID */
+    /** 下发人ID */
     private Long assignUserId;
 
-    /** 派单人姓名 */
+    /** 下发人姓名 */
     private String assignUserName;
 
     /** 接单时间 */
@@ -72,6 +104,35 @@ public class OrderScoreInfo {
     /** 接单人姓名 */
     private String acceptUserName;
 
+    /** 复位方式(1远程复位,2就地复位) */
+    private String resetMethod;
+
+    /** MIS工单编码 */
+    @Excel(name = "MIS工单编码")
+    private String misOrderNo;
+
+    /** 工作票编号 */
+    @Excel(name = "工作票编号")
+    private String workPermitNum;
+
+    /** 工作负责人ID */
+    private Long teamLeaderId;
+
+    /** 工作负责人姓名 */
+    private String teamLeaderName;
+
+    /** 班组成员ID */
+    private Long workGroupMemberId;
+
+    /** 班组成员 */
+    private String workGroupMemberName;
+
+    /** 外委人员数 */
+    private Integer wwryNum;
+
+    /** 外来人员数 */
+    private Integer wlryNum;
+
     /** 实际开始时间 */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
     private Date realStartTime;
@@ -80,17 +141,72 @@ public class OrderScoreInfo {
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
     private Date realEndTime;
 
-    /** 班组组长ID */
-    private Long teamLeaderId;
+    // 工时构成相关字段
+    /** 下发时长 */
+    private Double issueHour;
 
-    /** 班组组长姓名 */
-    private String teamLeaderName;
+    /** 接单时长 */
+    private Double acceptHour;
 
-    /** 工作组成员ID */
-    private Long workGroupMemberId;
+    /** 准备时长 */
+    private Double prepareHour;
 
-    /** 工作组成员姓名 */
-    private String workGroupMemberName;
+    /** 挂起时长 */
+    private Double suspendHour;
+
+    /** 作业时长 */
+    private Double workHour;
+
+    /** 验收时长 */
+    private Double restartHour;
+
+    /** 停运时长 */
+    private Double downtimeHour;
+
+    /** 开工前挂起时长 */
+    private Double beforeStartSuspendHour;
+
+    /** 开工后挂起时长 */
+    private Double afterStartSuspendHour;
+
+    /** 检修类型(A类、B类、C类、D类) */
+    private String maintenanceType;
+
+    /** 维保类型(半年检、一年检、螺栓力矩校验) */
+    private String inspectionType;
+
+    /** 维保内容 */
+//    @Excel(name = "维保内容")
+    private String content;
+
+    /** 维修总结/维保总结 */
+    private String workSummary;
+
+    /** 额外工作总结 */
+    private String extraWork;
+
+    /** 工作部位 */
+    private String workArea;
+
+    /** 分项完成系数和 */
+    private BigDecimal itemCompletionFactorSum;
+
+    /** 完成系数累计 */
+    private BigDecimal itemCompletionFactor;
+
+    /** 自评时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
+    private Date selfRatingTime;
+
+    /** 复评时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
+    private Date reviewRatingTime;
+
+    /** 工作负责人分数 */
+    private Double teamLeaderScore;
+
+    /** 班组成员分数 */
+    private String workGroupMemberScore;
 
     /** 评分 */
     private Double score;
@@ -107,10 +223,6 @@ public class OrderScoreInfo {
     /** 修改理由 */
     private String modifyReason;
 
-    /** 重启时间 */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
-    private Date restartTime;
-
     /** 优先级 */
     private String priorityType;
 
@@ -121,15 +233,6 @@ public class OrderScoreInfo {
     /** 评分状态(to_self-待自评,to_re-待复评,to_confirm-待确认,to_final-待终评,to_archive-待归档,archived-已归档) */
     private String scoringStatus;
 
-    /** 维修总结/维保总结 */
-    private String workSummary;
-
-    /** 检修类型(A类、B类、C类、D类) */
-    private String maintenanceType;
-
-    /** 维保类型(半年检、一年检、螺栓力矩校验) */
-    private String inspectionType;
-
     // 新增的共同字段
     /** 计划开始时间 */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
@@ -142,16 +245,6 @@ public class OrderScoreInfo {
     /** 计划工时 */
     private Double planHour;
 
-    /** 故障代码 */
-    private String faultCode;
-
-    /** 故障描述 */
-    private String faultDesc;
-
-    /** 发生时间 */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
-    private Date occurTime;
-
     /** 挂起时间 */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
     private Date suspendTime;
@@ -159,10 +252,6 @@ public class OrderScoreInfo {
     /** 暂停原因 */
     private String pauseReason;
 
-    /** 暂停时间 */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
-    private Date pauseTime;
-
     /** 完成时间 */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
     private Date completeTime;
@@ -173,15 +262,9 @@ public class OrderScoreInfo {
     /** 检修人员列表 */
     private List<OrderScorePerson> scorePersonList;
 
-    /** 分项完成系数 */
-    private BigDecimal itemCompletionFactor;
-
-    /** 分项完成系数和 */
-    private BigDecimal itemCompletionFactorSum;
-    
     /** 复评次数 */
     private Integer reviewScoreNum;
-    
+
     /** 终评系数 */
     private BigDecimal finalCoefficient;
 
@@ -203,43 +286,13 @@ public class OrderScoreInfo {
 
     /** 处理时长 */
     private Double handleHour;
-    
-    // 工时构成相关字段
-    /** 下发时长 */
-    private Double issueHour;
-    
-    /** 接单时长 */
-    private Double acceptHour;
-    
-    /** 准备时长 */
-    private Double prepareHour;
-    
-    /** 作业时长 */
-    private Double workHour;
-    
-    /** 复运时长 */
-    private Double restartHour;
-    
-    /** 停运时长 */
-    private Double downtimeHour;
-    
-    /** 挂起时长 */
-    private Double suspendHour;
-    
-    /** 开工前挂起时长 */
-    private Double beforeStartSuspendHour;
-    
-    /** 开工后挂起时长 */
-    private Double afterStartSuspendHour;
+
 
     private String remark;
 
     /** 本人得分 */
     private BigDecimal myScore;
 
-    /** 额外工作总结 */
-    private String extraWork;
-    
     /** 评分退回理由 */
     private String scoreReturnReason;
 
@@ -656,19 +709,19 @@ public class OrderScoreInfo {
     public void setItemCompletionFactorSum(BigDecimal itemCompletionFactorSum) {
         this.itemCompletionFactorSum = itemCompletionFactorSum;
     }
-    
+
     public Integer getReviewScoreNum() {
         return reviewScoreNum;
     }
-    
+
     public void setReviewScoreNum(Integer reviewScoreNum) {
         this.reviewScoreNum = reviewScoreNum;
     }
-    
+
     public BigDecimal getFinalCoefficient() {
         return finalCoefficient;
     }
-    
+
     public void setFinalCoefficient(BigDecimal finalCoefficient) {
         this.finalCoefficient = finalCoefficient;
     }
@@ -730,59 +783,59 @@ public class OrderScoreInfo {
     public void setHandleHour(Double handleHour) {
         this.handleHour = handleHour;
     }
-    
+
     public Double getIssueHour() {
         return issueHour;
     }
-    
+
     public void setIssueHour(Double issueHour) {
         this.issueHour = issueHour;
     }
-    
+
     public Double getAcceptHour() {
         return acceptHour;
     }
-    
+
     public void setAcceptHour(Double acceptHour) {
         this.acceptHour = acceptHour;
     }
-    
+
     public Double getPrepareHour() {
         return prepareHour;
     }
-    
+
     public void setPrepareHour(Double prepareHour) {
         this.prepareHour = prepareHour;
     }
-    
+
     public Double getWorkHour() {
         return workHour;
     }
-    
+
     public void setWorkHour(Double workHour) {
         this.workHour = workHour;
     }
-    
+
     public Double getRestartHour() {
         return restartHour;
     }
-    
+
     public void setRestartHour(Double restartHour) {
         this.restartHour = restartHour;
     }
-    
+
     public Double getDowntimeHour() {
         return downtimeHour;
     }
-    
+
     public void setDowntimeHour(Double downtimeHour) {
         this.downtimeHour = downtimeHour;
     }
-    
+
     public Double getSuspendHour() {
         return suspendHour;
     }
-    
+
     public void setSuspendHour(Double suspendHour) {
         this.suspendHour = suspendHour;
     }
@@ -790,7 +843,7 @@ public class OrderScoreInfo {
     public Double getBeforeStartSuspendHour() {
         return beforeStartSuspendHour;
     }
-    
+
     public void setBeforeStartSuspendHour(Double beforeStartSuspendHour) {
         this.beforeStartSuspendHour = beforeStartSuspendHour;
     }
@@ -798,7 +851,7 @@ public class OrderScoreInfo {
     public Double getAfterStartSuspendHour() {
         return afterStartSuspendHour;
     }
-    
+
     public void setAfterStartSuspendHour(Double afterStartSuspendHour) {
         this.afterStartSuspendHour = afterStartSuspendHour;
     }
@@ -842,12 +895,132 @@ public class OrderScoreInfo {
     public void setOrderEntryType(String orderEntryType) {
         this.orderEntryType = orderEntryType;
     }
-    
+
     public Boolean getIsCurrentUserInOrder() {
         return isCurrentUserInOrder;
     }
-    
+
     public void setIsCurrentUserInOrder(Boolean isCurrentUserInOrder) {
         this.isCurrentUserInOrder = isCurrentUserInOrder;
     }
+
+    public String getFinalizeMethod() {
+        return finalizeMethod;
+    }
+
+    public void setFinalizeMethod(String finalizeMethod) {
+        this.finalizeMethod = finalizeMethod;
+    }
+
+    public String getFaultBarcode() {
+        return faultBarcode;
+    }
+
+    public void setFaultBarcode(String faultBarcode) {
+        this.faultBarcode = faultBarcode;
+    }
+
+    public String getRealFailureReason() {
+        return realFailureReason;
+    }
+
+    public void setRealFailureReason(String realFailureReason) {
+        this.realFailureReason = realFailureReason;
+    }
+
+    public BigDecimal getLostPower() {
+        return lostPower;
+    }
+
+    public void setLostPower(BigDecimal lostPower) {
+        this.lostPower = lostPower;
+    }
+
+    public String getResetMethod() {
+        return resetMethod;
+    }
+
+    public void setResetMethod(String resetMethod) {
+        this.resetMethod = resetMethod;
+    }
+
+    public String getMisOrderNo() {
+        return misOrderNo;
+    }
+
+    public void setMisOrderNo(String misOrderNo) {
+        this.misOrderNo = misOrderNo;
+    }
+
+    public String getWorkPermitNum() {
+        return workPermitNum;
+    }
+
+    public void setWorkPermitNum(String workPermitNum) {
+        this.workPermitNum = workPermitNum;
+    }
+
+    public Integer getWwryNum() {
+        return wwryNum;
+    }
+
+    public void setWwryNum(Integer wwryNum) {
+        this.wwryNum = wwryNum;
+    }
+
+    public Integer getWlryNum() {
+        return wlryNum;
+    }
+
+    public void setWlryNum(Integer wlryNum) {
+        this.wlryNum = wlryNum;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public String getWorkArea() {
+        return workArea;
+    }
+
+    public void setWorkArea(String workArea) {
+        this.workArea = workArea;
+    }
+
+    public Double getTeamLeaderScore() {
+        return teamLeaderScore;
+    }
+
+    public void setTeamLeaderScore(Double teamLeaderScore) {
+        this.teamLeaderScore = teamLeaderScore;
+    }
+
+    public String getWorkGroupMemberScore() {
+        return workGroupMemberScore;
+    }
+
+    public void setWorkGroupMemberScore(String workGroupMemberScore) {
+        this.workGroupMemberScore = workGroupMemberScore;
+    }
+
+    public Date getSelfRatingTime() {
+        return selfRatingTime;
+    }
+
+    public void setSelfRatingTime(Date selfRatingTime) {
+        this.selfRatingTime = selfRatingTime;
+    }
+
+    public Date getReviewRatingTime() {
+        return reviewRatingTime;
+    }
+
+    public void setReviewRatingTime(Date reviewRatingTime) {
+        this.reviewRatingTime = reviewRatingTime;
+    }
 }

+ 10 - 0
ygtx-gxt/src/main/java/com/ygtx/gxt/mapper/GxtOrderScoreMapper.java

@@ -48,4 +48,14 @@ public interface GxtOrderScoreMapper {
     public Map<String, Object> selectOrderScoreStatistics(@Param("repairOrder") GxtRepairOrder repairOrder,
                                                           @Param("workOrder") GxtWorkOrder workOrder,
                                                           @Param("month") String month);
+
+    /**
+     * 导出查询合并的工单列表(维修工单和维保工单)
+     *
+     * @param repairOrder 维修工单查询条件
+     * @param workOrder 维保工单查询条件
+     * @return 合并的工单列表
+     */
+    public List<OrderScoreInfo> selectUnionExportOrderList(@Param("repairOrder") GxtRepairOrder repairOrder,
+                                                     @Param("workOrder") GxtWorkOrder workOrder);
 }

+ 2 - 0
ygtx-gxt/src/main/java/com/ygtx/gxt/service/IGxtOrderScoreService.java

@@ -84,4 +84,6 @@ public interface IGxtOrderScoreService {
     public List<OrderScoreInfo> selectMobileUnionOrderList(GxtRepairOrder repairOrder, GxtWorkOrder workOrder, String keyword);
 
     public List<OrderScoreInfo> selectMobileUnionOrderList(Long userId, GxtRepairOrder repairOrder, GxtWorkOrder workOrder, String keyword);
+
+    public List<OrderScoreInfo> selectUnionExportOrderList(GxtRepairOrder repairOrder, GxtWorkOrder workOrder);
 }

+ 20 - 0
ygtx-gxt/src/main/java/com/ygtx/gxt/service/impl/GxtOrderScoreServiceImpl.java

@@ -1975,6 +1975,26 @@ public class GxtOrderScoreServiceImpl implements IGxtOrderScoreService {
         return gxtOrderScoreMapper.selectMobileUnionOrderList(repairOrder, workOrder,keyword);
     }
 
+    @Override
+    public List<OrderScoreInfo> selectUnionExportOrderList(GxtRepairOrder repairOrder, GxtWorkOrder workOrder) {
+        // 添加业务特定的数据权限过滤
+        addBusinessDataScopeFilter(repairOrder, workOrder);
+
+        // 设置创建人,用于数据权限过滤
+        if(!Constants.SUPER_ADMIN.equals(SecurityUtils.getUsername())){
+            if (repairOrder.getCreateBy() == null || repairOrder.getCreateBy().isEmpty()) {
+                repairOrder.setCreateBy(SecurityUtils.getUsername());
+            }
+
+            if (workOrder.getCreateBy() == null || workOrder.getCreateBy().isEmpty()) {
+                workOrder.setCreateBy(SecurityUtils.getUsername());
+            }
+        }
+        // 查询工单列表
+        List<OrderScoreInfo> orderList = gxtOrderScoreMapper.selectUnionExportOrderList(repairOrder, workOrder);
+        return orderList;
+    }
+
     private void addBusinessDataScopeFilter(SysUser currentUser, GxtRepairOrder repairOrder, GxtWorkOrder workOrder) {
         if (currentUser == null) {
             return;

+ 1 - 1
ygtx-gxt/src/main/java/com/ygtx/gxt/service/impl/GxtWorkOrderServiceImpl.java

@@ -1983,7 +1983,7 @@ public class GxtWorkOrderServiceImpl implements IGxtWorkOrderService
                 deptId = parentDept.getDeptId();
             }
         }
-        if (gxtOrderData!=null && (gxtOrderData.getCenter().equals("true") || gxtOrderData.getPcs().equals("true"))) {
+        if (null != gxtOrderData && (gxtOrderData.getCenter().equals("true") || gxtOrderData.getPcs().equals("true"))) {
             if (deptId!=null) {
                 gxtOrderData.setCenter(null);
                 gxtOrderData.setPcs("true");

+ 274 - 0
ygtx-gxt/src/main/resources/mapper/gxt/GxtOrderScoreMapper.xml

@@ -63,6 +63,30 @@
         <!-- 评分退回理由 -->
         <result property="scoreReturnReason"    column="score_return_reason"    />
         <result property="orderEntryType"    column="order_entry_type"    />
+
+        <result property="issueHour"    column="issueHour"    />
+        <result property="acceptHour"    column="acceptHour"    />
+        <result property="prepareHour"    column="prepareHour"    />
+        <result property="suspendHour"    column="suspendHour"    />
+        <result property="workHour"    column="workHour"    />
+        <result property="restartHour"    column="restartHour"    />
+        <result property="downtimeHour"    column="downtimeHour"    />
+        <result property="wlryNum"    column="wlry_num"    />
+        <result property="wwryNum"    column="wwry_num"    />
+        <result property="workPermitNum"    column="work_permit_num"    />
+        <result property="misOrderNo"    column="misOrderNo"    />
+        <result property="faultBarcode"    column="faultBarcode"    />
+        <result property="realFailureReason"    column="realFailureReason"    />
+        <result property="lostPower"    column="lostPower"    />
+        <result property="resetMethod"    column="resetMethod"    />
+        <result property="extraWork"    column="extraWork"    />
+        <result property="workArea"    column="workArea"    />
+        <result property="itemCompletionFactorSum"    column="itemCompletionFactorSum"    />
+        <result property="itemCompletionFactor"    column="itemCompletionFactor"    />
+        <result property="selfRatingTime"    column="selfRatingTime"    />
+        <result property="reviewRatingTime"    column="reviewRatingTime"    />
+        <result property="finalizeMethod"    column="finalizeMethod"    />
+        <result property="teamLeaderScore"    column="teamLeaderScore"    />
     </resultMap>
 
     <select id="selectUnionOrderList" resultMap="OrderScoreInfoResult">
@@ -486,4 +510,254 @@
             </where>
         ) t
     </select>
+
+    <select id="selectUnionExportOrderList" resultMap="OrderScoreInfoResult">
+        SELECT * FROM (
+        SELECT
+        1 as order_type,
+        work_order_project_no as work_order_project_no,
+        (SELECT dict.dict_label FROM sys_dict_data dict
+        WHERE dict.dict_type = 'gxt_work_order_status' AND dict.dict_value = t.work_order_status) as work_order_status,
+        gxt_center_id as gxt_center_id,
+        gxt_center as gxt_center,
+        pcs_station_id as pcs_station_id,
+        pcs_station_pid as pcs_station_pid,
+        pcs_station_name as pcs_station_name,
+        pcs_device_id as pcs_device_id,
+        pcs_device_name as pcs_device_name,
+        brand as brand,
+        model as model,
+        assign_time as assign_time,
+        assign_user_id as assign_user_id,
+        assign_user_name as assign_user_name,
+        accept_time as accept_time,
+        accept_user_id as accept_user_id,
+        accept_user_name as accept_user_name,
+        real_start_time as real_start_time,
+        real_end_time as real_end_time,
+        team_leader_id as team_leader_id,
+        team_leader_name as team_leader_name,
+        work_group_member_id as work_group_member_id,
+        work_group_member_name as work_group_member_name,
+        t.score as score,
+        (SELECT GROUP_CONCAT(CONCAT(p.nick_name, ':',
+            CASE 
+                WHEN p.score = ROUND(p.score, 0) THEN CAST(ROUND(p.score, 0) AS CHAR)
+                ELSE TRIM(TRAILING '.0' FROM CAST(ROUND(p.score, 1) AS CHAR))
+            END
+        ) SEPARATOR ',')
+         FROM gxt_repair_order_person p
+         WHERE p.order_id = t.id AND p.is_leader = 0 AND p.score IS NOT NULL) as workGroupMemberScore,
+        review_content as review_content,
+        suspend_reason as suspend_reason,
+        restart_time as restart_time,
+        priority_type as priority_type,
+        real_end_time as work_end_time,
+        scoring_status as scoring_status,
+        content as work_summary,
+        maintenance_type as maintenance_type,
+        null as inspection_type,
+        null as plan_start_time,
+        null as plan_end_time,
+        plan_hour as plan_hour,
+        fault_code as fault_code,
+        fault_desc as fault_desc,
+        occur_time as occur_time,
+        null as suspend_time,
+        null as pause_reason,
+        null as pause_time,
+        null as complete_time,
+        review_score_num as review_score_num,
+        final_coefficient as final_coefficient,
+        t.feedback_reason as feedback_reason,
+        t.confirm_status as confirm_status,
+        t.create_time as create_time,
+        t.update_time as update_time,
+        t.appeal_reason as appeal_reason,
+        t.score_return_reason as score_return_reason,
+        t.wwry_num as wwry_num,
+        t.wlry_num as wlry_num,
+        t.work_permit_num as work_permit_num,
+        t.mis_order_no as misOrderNo,
+        t.fault_barcode as faultBarcode,
+        t.real_failure_reason as realFailureReason,
+        t.lost_power as lostPower,
+        (SELECT dict.dict_label FROM sys_dict_data dict
+        WHERE dict.dict_type = 'gxt_reset_method' AND dict.dict_value = t.reset_method) as resetMethod,
+        extra_work as extraWork,
+        (SELECT GROUP_CONCAT(dict.dict_label SEPARATOR ',')
+         FROM sys_dict_data dict 
+         WHERE dict.dict_type = 'gxt_work_area' 
+         AND FIND_IN_SET(dict.dict_value, t.work_area)) as workArea,
+        null as itemCompletionFactorSum,
+        null as itemCompletionFactor,
+        f1.action_time as selfRatingTime,
+        f2.action_time as reviewRatingTime,
+        (SELECT dict.dict_label FROM sys_dict_data dict
+        WHERE dict.dict_type = 'gxt_finalize_method' AND dict.dict_value = t.finalize_method) as finalizeMethod,
+        p.score as teamLeaderScore,
+        ROUND(TIMESTAMPDIFF( SECOND, t.occur_time, t.assign_time ) / 3600,2) AS issueHour,
+        ROUND(( TIMESTAMPDIFF( SECOND, t.assign_time, t.accept_time ) - IFNULL(gq.total_night_auto_suspend_seconds, 0))/ 3600,2) AS acceptHour,
+        ROUND((TIMESTAMPDIFF( SECOND, t.accept_time, t.real_start_time ) - IFNULL(gq.total_before_start_suspend_seconds, 0)) / 3600,2) AS prepareHour,
+        ROUND(TIMESTAMPDIFF( SECOND, t.occur_time, t.restart_time ) / 3600,2) AS downtimeHour,
+        ROUND(TIMESTAMPDIFF( SECOND, t.real_end_time, t.restart_time ) / 3600,2) AS restartHour,
+        ROUND((TIMESTAMPDIFF( SECOND, t.real_start_time, t.real_end_time ) - IFNULL(gq.total_in_work_suspend_seconds, 0)) / 3600,2) AS workHour,
+        ROUND(CASE WHEN gq.order_id IS NOT NULL THEN (gq.total_before_start_suspend_seconds + gq.total_in_work_suspend_seconds + gq.total_night_auto_suspend_seconds)/3600 ELSE 0 END,2) AS suspendHour
+        FROM gxt_repair_order t
+        LEFT JOIN gxt_repair_order_flow_records_next gq ON gq.order_id = t.id
+        LEFT JOIN gxt_repair_order_person p ON p.order_id = t.id and p.is_leader = 1
+        LEFT JOIN gxt_repair_order_flow f1 ON f1.order_id = t.id and f1.action_type = 'selfRating'
+        LEFT JOIN gxt_repair_order_flow f2 ON f2.order_id = t.id and f2.action_type = 'reviewRating'
+
+        <where>
+            <if test="repairOrder.workOrderProjectNo != null and repairOrder.workOrderProjectNo != ''"> and work_order_project_no like concat('%', #{repairOrder.workOrderProjectNo}, '%')</if>
+            <if test="repairOrder.workOrderStatus != null and repairOrder.workOrderStatus != ''"> and work_order_status = #{repairOrder.workOrderStatus}</if>
+            <if test="repairOrder.gxtCenterId != null"> and gxt_center_id = #{repairOrder.gxtCenterId}</if>
+            <if test="repairOrder.gxtCenter != null and repairOrder.gxtCenter != ''"> and gxt_center = #{repairOrder.gxtCenter}</if>
+            <if test="repairOrder.pcsStationId != null"> and pcs_station_id = #{repairOrder.pcsStationId}</if>
+            <if test="repairOrder.pcsStationName != null and repairOrder.pcsStationName != ''"> and pcs_station_name = #{repairOrder.pcsStationName}</if>
+            <if test="repairOrder.pcsDeviceId != null"> and pcs_device_id = #{repairOrder.pcsDeviceId}</if>
+            <if test="repairOrder.pcsDeviceName != null and repairOrder.pcsDeviceName != ''"> and pcs_device_name like concat('%', #{repairOrder.pcsDeviceName}, '%')</if>
+            <if test="repairOrder.teamLeaderName != null and repairOrder.teamLeaderName != ''"> and team_leader_name like concat('%', #{repairOrder.teamLeaderName}, '%')</if>
+            <if test="repairOrder.scoringStatus != null and repairOrder.scoringStatus != ''"> and scoring_status in
+                <foreach collection="repairOrder.scoringStatus.split(',')" item="status" open="(" close=")" separator=",">
+                    #{status}
+                </foreach>
+            </if>
+            <if test="repairOrder.orderType != null"> and order_type = #{repairOrder.orderType}</if>
+            <if test="repairOrder.beginOccurTime != null and repairOrder.beginOccurTime != ''"> and date_format(occur_time,'%y%m%d') &gt;= date_format(#{repairOrder.beginOccurTime},'%y%m%d')</if>
+            <if test="repairOrder.endOccurTime != null and repairOrder.endOccurTime != ''"> and date_format(occur_time,'%y%m%d') &lt;= date_format(#{repairOrder.endOccurTime},'%y%m%d')</if>
+            <if test="repairOrder.repairMethod != null  and repairOrder.repairMethod != ''"> and repair_method = #{repairOrder.repairMethod}</if>
+            <if test="repairOrder.faultCode != null  and repairOrder.faultCode != ''"> and fault_code like concat('%', #{repairOrder.faultCode}, '%')</if>
+
+<!--            <if test="repairOrder.orderType != null and repairOrder.orderType != ''"> and 1 = #{repairOrder.orderType}</if>-->
+            AND work_order_status != 'invalid'
+            <!-- 业务特定数据权限过滤条件 -->
+            <if test="repairOrder.params.businessDataScope != null and repairOrder.params.businessDataScope != ''">
+                ${repairOrder.params.businessDataScope}
+            </if>
+        </where>
+
+        UNION ALL
+
+        SELECT
+        2 as order_type,
+        work_order_project_no as work_order_project_no,
+        (SELECT dict.dict_label FROM sys_dict_data dict
+        WHERE dict.dict_type = 'gxt_work_order_status' AND dict.dict_value = t.work_order_status) as work_order_status,
+        gxt_center_id as gxt_center_id,
+        gxt_center as gxt_center,
+        pcs_station_id as pcs_station_id,
+        pcs_station_pid as pcs_station_pid,
+        pcs_station_name as pcs_station_name,
+        pcs_device_id as pcs_device_id,
+        pcs_device_name as pcs_device_name,
+        brand as brand,
+        model as model,
+        assign_time as assign_time,
+        assign_user_id as assign_user_id,
+        assign_user_name as assign_user_name,
+        accept_time as accept_time,
+        accept_user_id as accept_user_id,
+        accept_user_name as accept_user_name,
+        real_start_time as real_start_time,
+        real_end_time as real_end_time,
+        team_leader_id as team_leader_id,
+        team_leader_name as team_leader_name,
+        work_group_member_id as work_group_member_id,
+        work_group_member_name as work_group_member_name,
+        t.score as score,
+        (SELECT GROUP_CONCAT(CONCAT(p.nick_name, ':',
+            CASE 
+                WHEN p.score = ROUND(p.score, 0) THEN CAST(ROUND(p.score, 0) AS CHAR)
+                ELSE TRIM(TRAILING '.0' FROM CAST(ROUND(p.score, 1) AS CHAR))
+            END
+        ) SEPARATOR ',')
+         FROM gxt_work_order_person p
+         WHERE p.order_id = t.id AND p.is_leader = 0 AND p.score IS NOT NULL) as workGroupMemberScore,
+        review_content as review_content,
+        suspend_reason as suspend_reason,
+        restart_time as restart_time,
+        priority_type as priority_type,
+        real_end_time as work_end_time,
+        scoring_status as scoring_status,
+        real_content as work_summary,
+        null as maintenance_type,
+        (SELECT dict.dict_label FROM sys_dict_data dict
+        WHERE dict.dict_type = 'gxt_inspection_type' AND dict.dict_value = t.inspection_type) as inspection_type,
+        plan_start_time as plan_start_time,
+        plan_end_time as plan_end_time,
+        plan_hour as plan_hour,
+        fault_code as fault_code,
+        fault_desc as fault_desc,
+        null as occur_time,
+        suspend_time as suspend_time,
+        pause_reason as pause_reason,
+        pause_time as pause_time,
+        complete_time as complete_time,
+        review_score_num as review_score_num,
+        final_coefficient as final_coefficient,
+        t.feedback_reason as feedback_reason,
+        t.confirm_status as confirm_status,
+        t.create_time as create_time,
+        t.update_time as update_time,
+        null as appeal_reason,
+        t.score_return_reason as score_return_reason,
+        t.wwry_num as wwry_num,
+        t.wlry_num as wlry_num,
+        t.work_permit_num as work_permit_num,
+        t.mis_no as misOrderNo,
+        null as faultBarcode,
+        null as realFailureReason,
+        lost_power as lostPower,
+        null as resetMethod,
+        null as extraWork,
+        null as workArea,
+        item_completion_factor_sum as itemCompletionFactorSum,
+        item_completion_factor as itemCompletionFactor,
+        f1.action_time as selfRatingTime,
+        f2.action_time as reviewRatingTime,
+        (SELECT dict.dict_label FROM sys_dict_data dict
+        WHERE dict.dict_type = 'gxt_finalize_method' AND dict.dict_value = t.finalize_method) as finalizeMethod,
+        p.score as teamLeaderScore,
+        ROUND(TIMESTAMPDIFF( SECOND, t.create_time, t.assign_time ) / 3600,2) AS issueHour,
+        ROUND(TIMESTAMPDIFF( SECOND, t.assign_time, t.accept_time ) / 3600,2) AS acceptHour,
+        ROUND((TIMESTAMPDIFF( SECOND, t.accept_time, t.real_start_time ) - IFNULL(gq.total_before_start_suspend_seconds, 0)) / 3600,2) AS prepareHour,
+        ROUND(TIMESTAMPDIFF( SECOND, COALESCE ( t.pause_time, t.real_start_time ), t.restart_time ) / 3600,2) AS downtimeHour,
+        ROUND(TIMESTAMPDIFF( SECOND, t.real_end_time, t.restart_time ) / 3600,2) AS restartHour,
+        ROUND(( TIMESTAMPDIFF( SECOND, t.real_start_time, t.real_end_time ) - IFNULL(gq.total_in_work_suspend_seconds, 0)) / 3600,2) AS workHour,
+        ROUND(CASE WHEN gq.order_id IS NOT NULL THEN (gq.total_before_start_suspend_seconds + gq.total_in_work_suspend_seconds + gq.total_night_auto_suspend_seconds)/3600 ELSE 0 END,2) AS suspendHour
+        FROM gxt_work_order t
+        LEFT JOIN gxt_work_order_flow_records_next gq ON gq.order_id = t.id
+        LEFT JOIN gxt_work_order_person p ON p.order_id = t.id and p.is_leader = 1
+        LEFT JOIN gxt_work_order_flow f1 ON f1.order_id = t.id and f1.action_type = 'selfRating'
+        LEFT JOIN gxt_work_order_flow f2 ON f2.order_id = t.id and f2.action_type = 'reviewRating'
+        <where>
+            <if test="workOrder.workOrderProjectNo != null and workOrder.workOrderProjectNo != ''"> and work_order_project_no like concat('%', #{workOrder.workOrderProjectNo}, '%')</if>
+            <if test="workOrder.workOrderStatus != null and workOrder.workOrderStatus != ''"> and work_order_status = #{workOrder.workOrderStatus}</if>
+            <if test="workOrder.gxtCenterId != null"> and gxt_center_id = #{workOrder.gxtCenterId}</if>
+            <if test="workOrder.gxtCenter != null and workOrder.gxtCenter != ''"> and gxt_center = #{workOrder.gxtCenter}</if>
+            <if test="workOrder.pcsStationId != null"> and pcs_station_id = #{workOrder.pcsStationId}</if>
+            <if test="workOrder.pcsStationName != null and workOrder.pcsStationName != ''"> and pcs_station_name = #{workOrder.pcsStationName}</if>
+            <if test="workOrder.pcsDeviceId != null"> and pcs_device_id = #{workOrder.pcsDeviceId}</if>
+            <if test="workOrder.pcsDeviceName != null and workOrder.pcsDeviceName != ''"> and pcs_device_name like concat('%', #{workOrder.pcsDeviceName}, '%')</if>
+            <if test="workOrder.teamLeaderName != null and workOrder.teamLeaderName != ''"> and team_leader_name like concat('%', #{workOrder.teamLeaderName}, '%')</if>
+            <if test="workOrder.scoringStatus != null and workOrder.scoringStatus != ''"> and scoring_status in
+                <foreach collection="workOrder.scoringStatus.split(',')" item="status" open="(" close=")" separator=",">
+                    #{status}
+                </foreach>
+            </if>
+            <if test="workOrder.orderType != null"> and order_type = #{workOrder.orderType}</if>
+            <if test="workOrder.beginCreateTime != null and workOrder.beginCreateTime != ''"> and date_format(t.create_time,'%y%m%d') &gt;= date_format(#{workOrder.beginCreateTime},'%y%m%d')</if>
+            <if test="workOrder.endCreateTime != null and workOrder.endCreateTime != ''"> and date_format(t.create_time,'%y%m%d') &lt;= date_format(#{workOrder.endCreateTime},'%y%m%d')</if>
+<!--            <if test="workOrder.orderType != null and workOrder.orderType != ''"> and 2 = #{workOrder.orderType}</if>-->
+            AND work_order_status != 'invalid'
+            <!-- 业务特定数据权限过滤条件 -->
+            <if test="workOrder.params.businessDataScope != null and workOrder.params.businessDataScope != ''">
+                ${workOrder.params.businessDataScope}
+            </if>
+        </where>
+        ) t
+        ORDER BY COALESCE(update_time, create_time) DESC, create_time DESC
+    </select>
 </mapper>

+ 9 - 0
ygtx-ui/src/api/gxt/repairOrder.js

@@ -203,3 +203,12 @@ export function getExportFields() {
     method: 'get'
   })
 }
+
+// 获取维修工单数据列表(使用ExportOrderInfo对象)
+export function getExportOrderDataList(query) {
+  return request({
+    url: '/gxt/repairOrder/getOrderDataList',
+    method: 'get',
+    params: query
+  })
+}

+ 193 - 5
ygtx-ui/src/views/gxt/gxtOrder/index.vue

@@ -161,6 +161,14 @@
           v-hasPermi="['gxt:maintenance:order:export']"
         >导出</el-button>
       </el-col>
+      <el-col :span="1.5">
+        <el-button
+            type="warning"
+            icon="Download"
+            @click="handleExport2"
+            v-hasPermi="['gxt:maintenance:order:export2']"
+        >数据流导出</el-button>
+      </el-col>
       <!-- <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> -->
     </el-row>
 
@@ -1281,12 +1289,12 @@ import {
 } from "@/api/gxt/gxtOrder"
 import {listUserData, listUserNoPermi, listLeader} from "@/api/system/user"
 import {getToken} from "@/utils/auth.js";
-import {getRepairOrder, listRepairOrder, startRepairOrder} from "@/api/gxt/repairOrder.js";
+import {getExportOrderDataList, getRepairOrder, listRepairOrder, startRepairOrder} from "@/api/gxt/repairOrder.js";
 import FinalizeDialog from '@/components/gxtOrder/finalize.vue'
 import {listEquipment, listMaintenanceCenters, listStationsByMaintenanceCenter} from "@/api/gxt/equipment.js";import MisInfoSelectSingle from "@/components/misInfoSelect/single.vue";
 import {genPostCode} from "@/api/system/autocode/rule.js";
 import {listAutoMisInfo, listMisInfo, listWorkPerson} from "@/api/gxt/misInfo.js";
-import {ElMessageBox} from "element-plus";
+import {ElLoading, ElMessageBox} from "element-plus";
 import EquipmentSelectSingle from "@/components/equipmentSelect/single.vue";
 import useUserStore from '@/store/modules/user'
 import SuspendDialog from "@/components/gxtOrder/suspend.vue";
@@ -1564,6 +1572,7 @@ const {
     "gxt_finalize_method"
 )
 const orderList = ref([])
+const orderDataList = ref([]) // 工单数据列表,从后端获取
 const open = ref(false)
 const loading = ref(true)
 const showSearch = ref(true)
@@ -1950,9 +1959,9 @@ const data = reactive({
     score: undefined  // 用于判断是否已评分
   },
   restartRules: {
-    lostPower: [
-      { required: true, message: "损失电量不能为空", trigger: "change" }
-    ],
+    // lostPower: [
+    //   { required: true, message: "损失电量不能为空", trigger: "change" }
+    // ],
     restartTime: [
       { required: true, message: "请选择恢复运行时间", trigger: "change" },
       {
@@ -3364,6 +3373,185 @@ async function handleRevoke(row) {
   }).catch(() => {})
 }
 
+let exportLoading
+/** 导出按钮操作 */
+async function handleExport2() {
+  await proxy.$modal.confirm('确定要导出维保工单数据吗?');
+  exportLoading=ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", }); // Vue3 开启加载
+  await getOrderDataList();
+}
+
+import ExcelJS from 'exceljs'
+import { saveAs } from 'file-saver'
+
+/** 导出所有字段 */
+async function exportAllFields() {
+  // 1. 定义表头映射(新增 width 字段,指定列宽;不指定则用默认值)
+  const headerFieldMap = [
+    { label: '工单编号', key: 'workOrderProjectNo', width: 20 }, // 自定义宽度
+    { label: '工单状态', key: 'workOrderStatus', width: 12 },
+    { label: '结单方式', key: 'finalizeMethod', width: 12 },
+    { label: '维保中心', key: 'gxtCenter', width: 20 },
+    { label: '风电场', key: 'pcsStationName', width: 20 },
+    { label: '风机品牌', key: 'brand', width: 12 },
+    { label: '风机机型', key: 'model', width: 15 },
+    { label: '风机编号', key: 'pcsDeviceName', width: 15 },
+    { label: '维保类型', key: 'inspectionType', width: 12 },
+    { label: '维保内容', key: 'workSummary', width: 30 }, // 长文本加宽
+    { label: '停机时间', key: 'pauseTime', width: 20 }, // 时间列加宽
+    { label: '恢复运行时间', key: 'restartTime', width: 20 },
+    { label: '损失电量', key: 'lostPower', width: 12 },
+    { label: '接单时间', key: 'acceptTime', width: 20 },
+    { label: '接单人', key: 'acceptUserName', width: 12 },
+    { label: 'MIS系统对应的工单号', key: 'misOrderNo', width: 20 },
+    { label: '工作票编号', key: 'workPermitNum', width: 20 }, // 红色字体列
+    { label: '工作负责人', key: 'teamLeaderName', width: 12 },
+    { label: '班组成员', key: 'workGroupMemberName', width: 25 }, // 多姓名加宽
+    { label: '外委人员数(人)', key: 'wwryNum', width: 10 },
+    { label: '外来人员数(人)', key: 'wlryNum', width: 10 },
+    { label: '开工打卡时间', key: 'realStartTime', width: 20 },
+    { label: '收工打卡时间', key: 'realEndTime', width: 20 },
+    { label: '挂起时长', key: 'suspendHour', width: 10 },
+    { label: '作业时长', key: 'workHour', width: 10 },
+    { label: '复运时长', key: 'restartHour', width: 10 },
+    { label: '停运时长', key: 'downtimeHour', width: 10 },
+    { label: '维保类型', key: 'inspectionType', width: 12 },
+    { label: '维保总结', key: 'workSummary', width: 30 }, // 长文本加宽
+    // { label: '额外工作总结', key: 'extraWork', width: 30 },
+    { label: '分项完成系数', key: 'itemCompletionFactorSum', width: 15 },
+    { label: '完成系数累计', key: 'itemCompletionFactor', width: 15 },
+    { label: '自评时间', key: 'selfRatingTime', width: 20 },
+    { label: '复评时间', key: 'reviewRatingTime', width: 20 },
+    { label: '工作负责人', key: 'teamLeaderScore', width: 12 },
+    { label: '班组成员', key: 'workGroupMemberScore', width: 12 },
+    { label: '汇总工分', key: 'score', width: 12 },
+  ];
+
+  // 2. 使用所有字段进行导出
+  const exportColumns = headerFieldMap;
+
+  // 3. 创建Excel工作簿和工作表
+  const workbook = new ExcelJS.Workbook();
+  const worksheet = workbook.addWorksheet('维保工单数据流');
+
+  // 4. 设置表头样式(模拟你示例的格式)
+  // 4.1 第一步:合并单元格(按你的需求分段合并)
+  const mergeConfigs = [
+    { range: 'A1:M1', title: '工单创建', color: '00b441' }, // 绿色
+    { range: 'N1:O1', title: '工单派发', color: 'ffbf00' }, // 黄色
+    { range: 'P1:W1', title: '工单执行', color: '00b441' }, // 绿色
+    { range: 'X1:AA1', title: '工时统计', color: 'ffbf00' }, // 黄色
+    { range: 'AB1:AJ1', title: '工分评价', color: '00b441' } // 绿色
+  ];
+  // 4.2 第二步:遍历配置,给每个合并单元格设置标题+样式
+  mergeConfigs.forEach(config => {
+    // 合并单元格
+    worksheet.mergeCells(config.range);
+    // 获取当前合并单元格的首个单元格(如A1、P1、U1)
+    const firstCell = worksheet.getCell(config.range.split(':')[0]);
+    // 设置标题文本
+    firstCell.value = config.title;
+    // 设置背景色
+    firstCell.fill = {
+      type: 'pattern',
+      pattern: 'solid',
+      fgColor: { argb: config.color }
+    };
+    // 设置字体(黑色粗体)
+    firstCell.font = {
+      color: { argb: '00000000' },
+      bold: true
+    };
+    // 设置居中对齐
+    firstCell.alignment = {
+      horizontal: 'center',
+      vertical: 'middle'
+    };
+    // 设置行高
+    worksheet.getRow(1).height = 25;
+  });
+
+  // 4.2 第二行:列标题(灰色背景+居中)
+  const headerRow = worksheet.getRow(2);
+  exportColumns.forEach((col, index) => {
+    const cell = headerRow.getCell(index + 1);
+    cell.value = col.label;
+    cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFC0C0C0' } }; // 灰色背景
+    cell.font = { bold: true };
+    cell.alignment = { horizontal: 'center', vertical: 'middle' };
+    cell.border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } };
+
+    // 给"工作票编号"设置红色字体(匹配你示例)
+    if (col.label === '工作票编号') {
+      cell.font = { bold: true, color: { argb: 'FFFF0000' } };
+    }
+  });
+  headerRow.height = 20;
+
+  // 5. 填充数据行
+  orderDataList.value.forEach((rowData, rowIndex) => {
+    const dataRow = worksheet.getRow(rowIndex + 3);
+    exportColumns.forEach((col, colIndex) => {
+      const cell = dataRow.getCell(colIndex + 1);
+      // 时间字段格式化(可选:解开注释启用)
+      if (['pauseTime', 'restartTime', 'acceptTime', 'realStartTime', 'realEndTime', 'selfRatingTime', 'reviewRatingTime'].includes(col.key)) {
+        cell.value = rowData[col.key] ? new Date(rowData[col.key]) : '-';
+        cell.numFmt = 'yyyy-mm-dd hh:mm:ss'; // 时间格式
+      } else {
+        cell.value = rowData[col.key]; // 空值显示为 "-",更友好
+      }
+      cell.alignment = { horizontal: 'center', vertical: 'middle' };
+      cell.border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } };
+    });
+    dataRow.height = 18;
+  });
+
+  // 6. 调整列宽(核心修改:按配置的width设置,无配置则用默认15)
+  exportColumns.forEach((col, index) => {
+    const column = worksheet.getColumn(index + 1);
+    // 优先使用自定义宽度,无则用默认15
+    column.width = col.width || 15;
+  });
+
+  // 7. 导出并下载Excel
+  const buffer = await workbook.xlsx.writeBuffer();
+  const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
+  saveAs(blob, `维保工单数据流_${new Date().getTime()}.xlsx`);
+
+  // 关闭对话框(如果有的话)
+  showExportFieldsDialog.value = false;
+}
+/** 获取工单数据列表 */
+function getOrderDataList() {
+  const orderDataParams = ref({
+    // pageNum: 1,
+    // pageSize: 10,
+    orderType: 2,
+    workOrderProjectNo: queryParams.value.workOrderProjectNo,
+    workOrderStatus: queryParams.value.workOrderStatus,
+    gxtCenterId: queryParams.value.gxtCenterId,
+    gxtCenter: queryParams.value.gxtCenter,
+    pcsStationId: queryParams.value.pcsStationId,
+    pcsStationName: queryParams.value.pcsStationName,
+    pcsDeviceId: queryParams.value.pcsDeviceId,
+    pcsDeviceName: queryParams.value.pcsDeviceName,
+    beginCreateTime: queryParams.value.beginCreateTime, // 添加开始时间参数
+    endCreateTime: queryParams.value.endCreateTime,    // 添加结束时间参数
+
+  })
+  // 调用后端接口获取工单数据列表(使用ExportOrderInfo对象)
+  getExportOrderDataList(orderDataParams.value).then(response => {
+    orderDataList.value = response.rows || [];
+    exportLoading.close()
+    // 数据获取成功,执行导出
+    exportAllFields();
+  }).catch(error => {
+    console.error("获取工单数据列表失败:", error);
+    orderDataList.value = [];
+    exportLoading.close()
+  });
+}
+
 getList()
 </script>
 <style scoped>

+ 3 - 3
ygtx-ui/src/views/gxt/orderMyTodo/index.vue

@@ -1768,9 +1768,9 @@ const restartRepairRules = ref({
 });
 
 const restartWorkRules = ref({
-  lostPower: [
-    { required: true, message: "损失电量不能为空", trigger: "change" }
-  ],
+  // lostPower: [
+  //   { required: true, message: "损失电量不能为空", trigger: "change" }
+  // ],
   restartTime: [
     { required: true, message: "请选择恢复运行时间", trigger: "change" },
     {

+ 193 - 2
ygtx-ui/src/views/gxt/repairOrder/index.vue

@@ -138,6 +138,14 @@
             v-hasPermi="['gxt:repairOrder:export']"
         >导出</el-button>
       </el-col>
+      <el-col :span="1.5">
+        <el-button
+            type="warning"
+            icon="Download"
+            @click="handleExport2"
+            v-hasPermi="['gxt:repairOrder:export2']"
+        >数据流导出</el-button>
+      </el-col>
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
@@ -1307,7 +1315,8 @@ import {
   revokeRepairOrder,
   invalidateRepairOrder,
   returnRepairOrder,
-  getExportFields
+  getExportFields,
+  getExportOrderDataList
 } from "@/api/gxt/repairOrder";
 import { listDept,getDept } from "@/api/system/dept";
 import { listFaultCodes } from "@/api/gxt/faultCodes"
@@ -1322,6 +1331,7 @@ import {genPostCode} from "@/api/system/autocode/rule.js";
 import MisInfoSelectSingle from "@/components/misInfoSelect/single.vue";
 import {listAutoMisInfo, listMisInfo, listWorkPerson} from "@/api/gxt/misInfo.js";
 import {getOrderList, listGxtOrder} from "@/api/gxt/gxtOrder.js";
+import {listOrderScore} from "@/api/gxt/orderScore.js";
 import useUserStore from '@/store/modules/user'
 import { useRoute } from 'vue-router'
 import {ref, watch} from 'vue'
@@ -1340,6 +1350,7 @@ const { gxt_maintenance_type, gxt_work_order_status, gxt_order_priority_type,gxt
 
 // 数据列表相关
 const repairOrderList = ref([])
+const orderDataList = ref([]) // 工单数据列表,从后端获取
 const occurTimeRange = ref([]) // 用于存储日期范围选择的值
 
 const openDialog = ref(false)
@@ -2868,6 +2879,39 @@ function handleDelete(row) {
   }).catch(() => {})
 }
 
+/** 获取工单数据列表 */
+function getOrderDataList() {
+  const orderDataParams = ref({
+    // pageNum: 1,
+    // pageSize: 10,
+    orderType: 1,
+    workOrderProjectNo: queryParams.value.workOrderProjectNo,
+    workOrderStatus: queryParams.value.workOrderStatus,
+    gxtCenterId: queryParams.value.gxtCenterId,
+    gxtCenter: queryParams.value.gxtCenter,
+    pcsStationId: queryParams.value.pcsStationId,
+    pcsStationName: queryParams.value.pcsStationName,
+    pcsDeviceId: queryParams.value.pcsDeviceId,
+    pcsDeviceName: queryParams.value.pcsDeviceName,
+    beginOccurTime: queryParams.value.beginOccurTime, // 添加开始时间参数
+    endOccurTime: queryParams.value.endOccurTime,    // 添加结束时间参数
+    faultCode: queryParams.value.faultCode,
+    repairMethod: queryParams.value.repairMethod
+
+  })
+  // 调用后端接口获取工单数据列表(使用ExportOrderInfo对象)
+  getExportOrderDataList(orderDataParams.value).then(response => {
+    orderDataList.value = response.rows || [];
+    exportLoading.close()
+    // 数据获取成功,执行导出
+    exportAllFields();
+  }).catch(error => {
+    console.error("获取工单数据列表失败:", error);
+    orderDataList.value = [];
+    exportLoading.close()
+  });
+}
+
 /** 导出按钮操作 */
 function handleExport() {
   // 显示字段选择对话框
@@ -4035,7 +4079,154 @@ function handleAutoGenChange(){
     form.value.workOrderProjectNo = response;
   });
 }
+let exportLoading
+/** 导出按钮操作 */
+async function handleExport2() {
+  await proxy.$modal.confirm('确定要导出维修工单数据吗?');
+  exportLoading=ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", }); // Vue3 开启加载
+  await getOrderDataList();
+}
+
+import ExcelJS from 'exceljs'
+import { saveAs } from 'file-saver'
+
+/** 导出所有字段 */
+async function exportAllFields() {
+  // 1. 定义表头映射(新增width字段,按字段类型适配列宽)
+  const headerFieldMap = [
+    { label: '工单编号', key: 'workOrderProjectNo', width: 20 },
+    { label: '工单状态', key: 'workOrderStatus', width: 12 },
+    { label: '结单方式', key: 'finalizeMethod', width: 12 },
+    { label: '维保中心', key: 'gxtCenter', width: 20 },
+    { label: '风电场', key: 'pcsStationName', width: 20 },
+    { label: '风机品牌', key: 'brand', width: 12 },
+    { label: '风机机型', key: 'model', width: 15 },
+    { label: '风机编号', key: 'pcsDeviceName', width: 15 },
+    { label: '故障代码', key: 'faultCode', width: 15 },
+    { label: '故障条文', key: 'faultBarcode', width: 20 },
+    { label: '故障描述', key: 'faultDesc', width: 30 }, // 长文本加宽
+    { label: '真实故障原因', key: 'realFailureReason', width: 30 }, // 长文本加宽
+    { label: '发生时间', key: 'occurTime', width: 20 }, // 时间列
+    { label: '恢复运行时间', key: 'restartTime', width: 20 }, // 时间列
+    { label: '损失电量', key: 'lostPower', width: 12 },
+    { label: '下发时间', key: 'assignTime', width: 20 }, // 时间列
+    { label: '下发人', key: 'assignUserName', width: 12 },
+    { label: '接单时间', key: 'acceptTime', width: 20 }, // 时间列
+    { label: '接单人', key: 'acceptUserName', width: 12 },
+    { label: '复位方式', key: 'resetMethod', width: 12 },
+    { label: 'MIS系统对应的工单号', key: 'misOrderNo', width: 20 },
+    { label: '工作票编号', key: 'workPermitNum', width: 20 }, // 红色字体列
+    { label: '工作负责人', key: 'teamLeaderName', width: 12 },
+    { label: '班组成员', key: 'workGroupMemberName', width: 25 }, // 多姓名加宽
+    { label: '外委人员数(人)', key: 'wwryNum', width: 10 },
+    { label: '外来人员数(人)', key: 'wlryNum', width: 10 },
+    { label: '开工打卡时间', key: 'realStartTime', width: 20 }, // 时间列
+    { label: '收工打卡时间', key: 'realEndTime', width: 20 }, // 时间列
+    { label: '下发时长', key: 'issueHour', width: 10 },
+    { label: '接单时长', key: 'acceptHour', width: 10 },
+    { label: '准备时长', key: 'prepareHour', width: 10 },
+    { label: '挂起时长', key: 'suspendHour', width: 10 },
+    { label: '作业时长', key: 'workHour', width: 10 },
+    { label: '验收时长', key: 'restartHour', width: 10 },
+    { label: '停运时长', key: 'downtimeHour', width: 10 },
+    { label: '检修类型', key: 'maintenanceType', width: 12 },
+    { label: '维修总结', key: 'workSummary', width: 30 }, // 长文本加宽
+    { label: '额外工作总结', key: 'extraWork', width: 30 }, // 长文本加宽
+    { label: '工作部位', key: 'workArea', width: 15 },
+    { label: '自评时间', key: 'selfRatingTime', width: 20 }, // 时间列
+    { label: '复评时间', key: 'reviewRatingTime', width: 20 }, // 时间列
+    { label: '工作负责人', key: 'teamLeaderScore', width: 12 },
+    { label: '班组成员', key: 'workGroupMemberScore', width: 12 },
+    { label: '汇总工分', key: 'score', width: 12 },
+  ];
 
+  // 2. 使用所有字段进行导出
+  const exportColumns = headerFieldMap;
+
+  // 3. 创建Excel工作簿和工作表
+  const workbook = new ExcelJS.Workbook();
+  const worksheet = workbook.addWorksheet('维修工单数据流');
+
+  // 4. 设置表头样式(适配新的列结构)
+  // 4.1 第一步:合并单元格(按新列数调整合并范围)
+  const mergeConfigs = [
+    { range: 'A1:O1', title: '工单创建', color: '00b441' }, // 绿色(A-O列:工单+故障基础信息)
+    { range: 'P1:T1', title: '工单派发', color: 'ffbf00' }, // 黄色(P-T列:派发/接单信息)
+    { range: 'U1:AB1', title: '工单执行', color: '00b441' }, // 绿色(U-AB列:执行/人员信息)
+    { range: 'AC1:AI1', title: '工时统计', color: 'ffbf00' }, // 黄色(AC-AI列:时长统计)
+    { range: 'AJ1:AR1', title: '工分评价', color: '00b441' } // 绿色(AJ-AR列:总结/评价)
+  ];
+  // 4.2 第二步:遍历配置,给每个合并单元格设置标题+样式
+  mergeConfigs.forEach(config => {
+    worksheet.mergeCells(config.range);
+    const firstCell = worksheet.getCell(config.range.split(':')[0]);
+    firstCell.value = config.title;
+    firstCell.fill = {
+      type: 'pattern',
+      pattern: 'solid',
+      fgColor: { argb: config.color }
+    };
+    firstCell.font = {
+      color: { argb: '00000000' },
+      bold: true
+    };
+    firstCell.alignment = {
+      horizontal: 'center',
+      vertical: 'middle'
+    };
+    worksheet.getRow(1).height = 25;
+  });
+
+  // 4.2 第二行:列标题(灰色背景+居中)
+  const headerRow = worksheet.getRow(2);
+  exportColumns.forEach((col, index) => {
+    const cell = headerRow.getCell(index + 1);
+    cell.value = col.label;
+    cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFC0C0C0' } };
+    cell.font = { bold: true };
+    cell.alignment = { horizontal: 'center', vertical: 'middle' };
+    cell.border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } };
+
+    // 给"工作票编号"设置红色字体
+    if (col.label === '工作票编号') {
+      cell.font = { bold: true, color: { argb: 'FFFF0000' } };
+    }
+  });
+  headerRow.height = 20;
+
+  // 5. 填充数据行(优化时间格式化+空值处理)
+  orderDataList.value.forEach((rowData, rowIndex) => {
+    const dataRow = worksheet.getRow(rowIndex + 3);
+    exportColumns.forEach((col, colIndex) => {
+      const cell = dataRow.getCell(colIndex + 1);
+      // 时间字段格式化(覆盖所有时间类字段)
+      const timeKeys = ['occurTime', 'restartTime', 'assignTime', 'acceptTime', 'realStartTime', 'realEndTime', 'selfRatingTime', 'reviewRatingTime'];
+      if (timeKeys.includes(col.key)) {
+        cell.value = rowData[col.key] ? new Date(rowData[col.key]) : '-';
+        cell.numFmt = 'yyyy-mm-dd hh:mm:ss'; // 统一时间格式
+      } else {
+        cell.value = rowData[col.key]; // 空值显示为"-",提升可读性
+      }
+      cell.alignment = { horizontal: 'center', vertical: 'middle' };
+      cell.border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } };
+    });
+    dataRow.height = 18;
+  });
+
+  // 6. 调整列宽(核心:按配置的width设置,无配置则默认15)
+  exportColumns.forEach((col, index) => {
+    const column = worksheet.getColumn(index + 1);
+    column.width = col.width || 15; // 优先用自定义宽度,否则默认15
+  });
+
+  // 7. 导出并下载Excel
+  const buffer = await workbook.xlsx.writeBuffer();
+  const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
+  saveAs(blob, `维修工单数据流_${new Date().getTime()}.xlsx`);
+
+  // 关闭对话框
+  showExportFieldsDialog.value = false;
+}
 </script>
 
 <style scoped>
@@ -4177,7 +4368,7 @@ function handleAutoGenChange(){
 }
 
 .quick-select-item:last-child {
-  //border-bottom: none;
+  /* border-bottom: none; */
 }
 
 .mis-no {