From f4548acea8b1e364aa08bcbbb5c448bb88c73476 Mon Sep 17 00:00:00 2001
From: 张含笑 <zhx18749296735@163.com>
Date: Wed, 19 Nov 2025 19:19:40 +0800
Subject: [PATCH] feat:详情demo

---
 src/views/tickets/ticket.vue                             |  548 ++++++++++++++++++++++++++++++++++++-----------------
 src/views/tickets/ticketComponent/TicketDetailDialog.vue |   36 +++
 2 files changed, 402 insertions(+), 182 deletions(-)

diff --git a/src/views/tickets/ticket.vue b/src/views/tickets/ticket.vue
index 0ba858c..504a4b6 100644
--- a/src/views/tickets/ticket.vue
+++ b/src/views/tickets/ticket.vue
@@ -270,18 +270,18 @@
     </el-tabs>
 
     <!-- 新建工单对话框 -->
-      <create-ticket-dialog
-      v-model="dialogVisible"
-      :departments="departments"
-      :department-users="departmentUsers"
-      :types="types"
-      :all-algorithms="allAlgorithms"
-      :edit-data="editFormData"
-      :map-center="inputMapShowDefaultCenter"
-      @create-success="handleCreateSuccess"
-      @draft-success="handleDraftSuccess"
-      @create-error="handleCreateError"
-    />
+   <create-ticket-dialog
+  v-model="dialogVisible"
+  :departments="departments"
+  :department-users="departmentUsers"
+  :types="types"
+  :all-algorithms="allAlgorithms"
+  :edit-data="editFormData"
+  :map-center="inputMapShowDefaultCenter"
+  @create-success="handleCreateSuccess"
+  @draft-success="handleDraftSuccess"
+  @create-error="handleCreateError"
+/>
 
     <!-- 工单详情对话框 -->
 <ticket-detail-dialog
@@ -448,7 +448,13 @@
   data() {
     return {
       currentIndex: null, // 当前显示的数据索引
-
+  
+ 
+      approveLoading: false,
+      rejectLoading: false,
+      dispatchLoading: false,
+      completeLoading: false,
+      finalizeLoading: false,
       createoredit: '',
       activeTab: 'all',
       // tabs 只保留静态结构,不做权限判断
@@ -552,7 +558,7 @@
       },
       dialogVisible: false,
        editFormData: null, // 专门用于存储编辑数据的对象
-    
+        createoredit: '', // 可以删除这个字段,因为组件内部会判断
       detailVisible: false,
       currentDetail: {},
     
@@ -568,6 +574,8 @@
       dispatchDepartment: '', // 新增:派发部门
       dispatchHandler: '', // 新增:派发处理人
       dispatchDialogVisible: false, // 新增:派发对话框可见性
+  
+      stepInfos: [], // 新增:存储步骤信息
       fixedStatuses: ['2', '0', '3', '4'], // 固定的五个状态
       userNameToIdMap: {}, // 新增用户名到ID的映射
       workType: 0, // 新增:当前工单work_type
@@ -575,7 +583,7 @@
       reviewDialogVisible: false, // 新增:审核对话框可见性
       currentReviewImage: '', // 新增:当前审核图片
       currentImageIndex: 1, // 新增:当前图片索引
-
+      totalTime: '',
       isShowInfo: false,
 
       // 配置时间选择器默认配置
@@ -939,9 +947,21 @@
         });
       }
     },
+     // 上一项
+    handlePrevItem() {
+      this.currentIndex = Math.max(0, this.currentIndex - 1);
+      this.updateCurrentDetail();
+    },
+
+    // 下一项
+    handleNextItem() {
+      this.currentIndex = Math.min(this.tableData.length - 1, this.currentIndex + 1);
+      this.updateCurrentDetail();
+    },
  
     // 更新当前详情
-      updateCurrentDetail() {
+ 
+    updateCurrentDetail() {
       this.currentDetail = this.tableData[this.currentIndex];
       this.currentDetail.mediaUrl = this.currentDetail.photo_url;
       this.currentDetail.updatePhotoUrl = this.currentDetail.update_photo_url;
@@ -949,10 +969,12 @@
       this.currentDetail.showQR = false;
       this.currentDetail.latAndLon = _.round(this.currentDetail.location[0], 6) + ',' + _.round(this.currentDetail.location[1], 6);
     },
-     // 更新当前详情数据
+
+    // 更新当前详情数据
     handleCurrentDetailUpdate(updatedDetail) {
       this.currentDetail = updatedDetail;
     },
+
     // 受理并派发
     handleApproveAndDispatch(detail) {
       this.currentDetail = detail;
@@ -992,64 +1014,7 @@
         this.$message.error('地图加载失败,请检查网络或API Key配置');
       }
     },
-    async handleBatchApprove() {
-      try {
-        if (this.selections.length === 0) {
-          this.$message.warning('没有选中的工单');
-          return;
-        }
 
-        const currentItem = this.selections[this.currentImageIndex - 1];
-        if (!currentItem) {
-          this.$message.warning('当前工单数据无效');
-          return;
-        }
-
-        await this.$confirm('确认审核通过当前工单?', '提示', {
-          confirmButtonText: '确定',
-          cancelButtonText: '取消',
-          type: 'warning',
-        });
-
-        const data = {
-          id: currentItem.id,
-          status: currentItem.status,
-          isPass: 0,
-          eventNum: currentItem.orderNumber,
-          eventName: currentItem.orderName,
-        };
-
-        const response = await flowEvent(data);
-        if (response.data.code === 0) {
-          this.$message.success('工单审核通过');
-
-          // 创建新的数组而不是修改原数组
-          const newSelections = [...this.selections];
-          newSelections.splice(this.currentImageIndex - 1, 1);
-          this.selections = newSelections;
-
-          // 修正索引并更新图片
-          if (this.selections.length > 0) {
-            if (this.currentImageIndex > this.selections.length) {
-              this.currentImageIndex = this.selections.length;
-            }
-            this.updateCurrentReviewImage();
-          } else {
-            this.reviewDialogVisible = false;
-            this.currentImageIndex = 1;
-            this.currentReviewImage = '';
-          }
-
-          // 刷新表格数据
-          this.fetchTableData();
-        } else {
-          throw new Error(response.data.msg || '审核失败');
-        }
-      } catch (error) {
-        if (error === 'cancel') return;
-        this.$message.error(error.message || '审核失败,请稍后重试');
-      }
-    },
     async fetchDropdownData() {
       try {
         const response = await getTicketInfo();
@@ -1334,25 +1299,92 @@
       }
       return `${location[0].toFixed(6)}, ${location[1].toFixed(6)}`;
     },
-    handleViewDetail(row) {
+
+    async handleViewDetail(row) {
+      // 找到当前行在tableData中的索引
       this.currentIndex = this.tableData.findIndex(item => item.id === row.id);
+      // 先设置workType,直接从row读取
       this.workType = row.work_type !== undefined ? Number(row.work_type) : 0;
-      
+
+      // 重置上传组件的文件列表
+      this.$nextTick(() => {
+        if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) {
+          this.$refs.MapContainer.initAddEntity('point', this.currentDetail.location);
+        }
+      });
+
       const detailData = {
         ...row,
         processingDetail: row.processing_details || '',
         mediaUrl: row.photo_url || row.video_url || '',
         updatePhotoUrl: row.update_photo_url || '',
         photos: [],
-        job_name: row.job_name || '',
-        showQR: false,
-        latAndLon: _.round(row.location[0], 6) + ',' + _.round(row.location[1], 6),
+        job_name: row.job_name || '', // 新增
       };
 
-      this.currentDetail = detailData;
+      let stepArr = [];
+      try {
+        const stepResponse = await getStepInfo(row.orderNumber);
+        const steps = Array.isArray(stepResponse.data.data)
+          ? stepResponse.data.data
+          : stepResponse.data.data?.steps || [];
+        const finishedStep = steps.find(s => String(s.status) === '4');
+        this.totalTime = finishedStep && finishedStep.total_time ? finishedStep.total_time : '';
+        if (this.activeTab !== 'myTickets') {
+          this.stepInfos = steps.map(step => ({
+            status: String(step.status),
+            name: step.name,
+            time: step.time,
+            create_time: step.create_time,
+          }));
+        } else {
+          const statusArr = this.workType === 1 ? ['3', '4'] : this.fixedStatuses;
+          this.stepInfos = statusArr.map(status => {
+            const step = steps.find(s => String(s.status) === String(status));
+            return {
+              status,
+              name: step ? step.name : '',
+              time: step ? step.time : null,
+              create_time: step ? step.create_time : null,
+            };
+          });
+          console.log('this.stepInfos', this.stepInfos);
+          
+        }
+        this.currentDetail.status = row.status;
+      } catch (error) {
+        if (this.activeTab === 'myTickets') {
+          const statusArr = this.workType === 1 ? ['3', '4'] : this.fixedStatuses;
+          this.stepInfos = statusArr.map(status => ({
+            status,
+            name: status === row.status ? row.handler || '未分配' : '未处理',
+            time: status === row.status ? row.startTime || '未知时间' : null,
+          }));
+        } else {
+          this.stepInfos = [];
+        }
+      }
+      console.log(detailData, 'detailDatadetailDatadetailData');
+      this.currentDetail = {
+        ...detailData,
+        // address: null,
+        showQR: false,
+        latAndLon: _.round(detailData.location[0], 6) + ',' + _.round(detailData.location[1], 6),
+      };
+
+      console.log('this.currentDetail', this.currentDetail);
+
       this.detailVisible = true;
+
+
+
+      this.$nextTick(() => {
+        if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) {
+          this.$refs.MapContainer.initAddEntity('point', this.currentDetail.location);
+        }
+      });
     },
-    // 详情成功回调
+ // 详情成功回调
     handleDetailSuccess() {
       this.fetchTableData();
     },
@@ -1361,20 +1393,6 @@
     handleDetailError(error) {
       console.error('工单操作失败:', error);
     },
-   // 上一项
-    handlePrevItem() {
-      this.currentIndex = Math.max(0, this.currentIndex - 1);
-      this.updateCurrentDetail();
-    },
-
-    // 下一项
-    handleNextItem() {
-      this.currentIndex = Math.min(this.tableData.length - 1, this.currentIndex + 1);
-      this.updateCurrentDetail();
-    },
-
-
-
     getStepHandler(status) {
       const step = this.stepInfos.find(step => step.status === status);
       return step ? step.name : '';
@@ -1486,8 +1504,57 @@
         工单状态: this.mapStatus(Number(item.status || 0)),
       };
     },
-  
-   
+    async approveTicket() {
+      if (this.approveLoading) return;
+      this.approveLoading = true;
+      try {
+        const data = {
+          id: this.currentDetail.id,
+          status: this.currentDetail.status,
+          isPass: 0, // 0 表示通过
+          eventNum: this.currentDetail.orderNumber,
+        };
+
+        const file = this.currentDetail.file || null; // 如果没有文件,则为 null
+
+        const response = await flowEvent(data, file);
+        if (response.data.code === 0) {
+          this.$message.success('工单已通过');
+          this.detailVisible = false;
+          this.fetchTableData();
+        } else {
+          throw new Error(response.data.msg || '操作失败');
+        }
+      } catch (error) {
+        this.$message.error(error.message || '操作失败,请稍后重试');
+      } finally {
+        this.approveLoading = false;
+      }
+    },
+    async rejectTicket() {
+      if (this.rejectLoading) return;
+      this.rejectLoading = true;
+      try {
+        const data = {
+          id: this.currentDetail.id,
+          status: this.currentDetail.status,
+          isPass: 1, // 1 表示不通过
+        };
+
+        const response = await flowEvent(data);
+        if (response.data.code === 0) {
+          this.$message.success('工单未通过');
+          this.detailVisible = false;
+          this.fetchTableData();
+        } else {
+          throw new Error(response.data.msg || '操作失败');
+        }
+      } catch (error) {
+        this.$message.error(error.message || '操作失败,请稍后重试');
+      } finally {
+        this.rejectLoading = false;
+      }
+    },
     async submitProcessing() {
       if (this.currentDetail.status !== 3) {
         this.$message.warning('只有处理中状态的工单可以提交处理详情');
@@ -1520,7 +1587,7 @@
     markAsCompleted() {
       this.$message.success('工单已标记为完成');
     },
-  
+
     async approveAndDispatch() {
       if (this.dispatchLoading) return;
 
@@ -1554,10 +1621,6 @@
       // 可以在这里处理错误,或者保持空实现
       console.error('派发失败:', error);
     },
-
-
-
-
     hasProcessingBtnPermission() {
       // undefined 或 false 都返回 false,只有 true 返回 true
       //  console.log('权限检查:', this.permission.tickets_processing_btn)
@@ -1573,8 +1636,6 @@
       // console.log('权限检查:', this.permission)
       return this.permission && this.permission.tickets_review_btn === true;
     },
-
-   
     async finalizeTicket() {
       if (this.finalizeLoading) return;
       this.finalizeLoading = true;
@@ -1669,11 +1730,6 @@
     // 添加全选方法
     handleSelectAll(val) {
       this.$refs.avueCrud.toggleSelection(val);
-    },
-
-    // 添加单行选择方法
-    handleSelect(selection, row) {
-      console.log('选中行变化:', selection, row);
     },
 
     // 如果需要手动选中某些行
@@ -2057,7 +2113,10 @@
   margin-bottom: 32px;
 }
 
-
+.tableCss {
+  width: 100%;
+  margin-bottom: 10px;
+}
 
 .step-timer {
   position: absolute;
@@ -2070,7 +2129,12 @@
   font-size: 12px;
 }
 
-
+.event-total-time {
+  text-align: center;
+  color: #666;
+  font-size: 15px;
+  margin-bottom: 12px;
+}
 
 .action-bar {
   margin-bottom: 16px;
@@ -2102,7 +2166,103 @@
   margin-top: 12px;
 }
 
+.create-ticket-form {
+  padding: 20px 10px;
 
+  .form-section {
+    background-color: #fff;
+    border-radius: 4px;
+
+    .el-row {
+      margin-bottom: 16px;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
+
+  .location-wrapper {
+    width: 100%; // 修改为100%以适应父容器
+    height: auto; // 修改为auto以自适应内容
+
+    .map-button {
+      width: 100%; // 让按钮填满容器宽度
+      height: 36px; // 与其他输入框保持一致的高度
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      i {
+        margin-right: 4px;
+      }
+    }
+
+    .location-text {
+      margin-top: 8px;
+      padding: 8px 12px;
+      background-color: #f5f7fa;
+      border-radius: 4px;
+      color: #606266;
+      font-size: 13px;
+      line-height: 1.4;
+    }
+  }
+
+  .upload-wrapper {
+    .uploader {
+      :deep(.el-upload--picture-card) {
+        width: 120px;
+        height: 100px;
+        line-height: 128px;
+      }
+
+      :deep(.el-upload-list__item) {
+        width: 120px;
+        height: 120px;
+      }
+    }
+
+    .upload-tip {
+      font-size: 12px;
+      color: #909399;
+      line-height: 1.4;
+      margin-top: 8px;
+    }
+  }
+
+  .el-form-item {
+    margin-bottom: 18px;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  :deep(.el-form-item__label) {
+    font-weight: 500;
+    color: #606266;
+  }
+
+  // :deep(.el-input__inner) {
+  //   height: 36px;
+  //   line-height: 36px;
+  // }
+
+  :deep(.el-textarea__inner) {
+    padding: 8px 12px;
+  }
+
+  // :deep(.el-input__inner),
+  // :deep(.el-select),
+  // :deep(.el-select .el-input) {
+  //   width: 100%; // 确保所有输入框和选择框宽度一致
+  // }
+
+  .full-width {
+    width: 100%;
+  }
+}
 
 :deep(.el-dialog__body) {
   border-top: 0.1rem solid #f0f0f0;
@@ -2244,65 +2404,65 @@
   }
 }
 
-// .status-flow {
-//   margin-bottom: 20px;
+.status-flow {
+  margin-bottom: 20px;
 
-//   .custom-steps {
-//     .el-step__description {
-//       position: relative;
-//       margin: 0;
-//       padding: 0;
-//     }
+  .custom-steps {
+    .el-step__description {
+      position: relative;
+      margin: 0;
+      padding: 0;
+    }
 
-//     // 添加发起任务步骤的特殊样式
-//     .init-step-info {
-//       display: flex;
-//       flex-direction: column;
-//       align-items: center;
-//       text-align: center;
-//       width: 130px;
+    // 添加发起任务步骤的特殊样式
+    .init-step-info {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      text-align: center;
+      width: 130px;
 
-//       .creator-name {
-//         font-size: 14px;
-//         font-weight: bold;
-//         color: #303133;
-//         margin-bottom: 4px;
-//       }
+      .creator-name {
+        font-size: 14px;
+        font-weight: bold;
+        color: #303133;
+        margin-bottom: 4px;
+      }
 
-//       .create-time {
-//         font-size: 10px;
-//         color: #909399;
-//       }
-//     }
+      .create-time {
+        font-size: 10px;
+        color: #909399;
+      }
+    }
 
-//     // 保持其他步骤的原有样式
-//     .step-info {
-//       display: flex;
-//       margin: 0;
-//       padding: 0;
-//       justify-content: space-between;
-//       align-items: center;
-//       width: 200px;
+    // 保持其他步骤的原有样式
+    .step-info {
+      display: flex;
+      margin: 0;
+      padding: 0;
+      justify-content: space-between;
+      align-items: center;
+      width: 200px;
 
-//       .process-time {
-//         font-size: 10px;
-//         color: #909399;
-//         text-align: left;
-//         margin-left: -50%;
-//         padding-left: 0%;
-//         flex: 1;
-//       }
+      .process-time {
+        font-size: 10px;
+        color: #909399;
+        text-align: left;
+        margin-left: -50%;
+        padding-left: 0%;
+        flex: 1;
+      }
 
-//       .handler-name {
-//         font-size: 14px;
-//         font-weight: bold;
-//         color: #303133;
-//         text-align: left;
-//         flex: 1;
-//       }
-//     }
-//   }
-// }
+      .handler-name {
+        font-size: 14px;
+        font-weight: bold;
+        color: #303133;
+        text-align: left;
+        flex: 1;
+      }
+    }
+  }
+}
 
 .basic-info {
   margin-bottom: 20px;
@@ -2336,7 +2496,11 @@
   opacity: 0.3 !important;
 }
 
-
+.PopUpTableScrolls {
+  max-height: 600px;
+  overflow-y: scroll;
+  overflow-x: hidden;
+}
 
 .media-box {
   width: 100%;
@@ -2421,22 +2585,7 @@
   word-break: break-word;
 }
 
-.readonly-processing-detail {
-  background-color: #f5f7fa;
-  padding: 12px;
-  border-radius: 4px;
-  min-height: 30px;
-  color: #606266;
-  line-height: 1.5;
-  display: block;
-  text-align: left;
-  margin-bottom: 5px;
 
-  &:first-child {
-    font-weight: bold;
-    margin-bottom: 4px;
-  }
-}
 
 // 添加删除按钮样式
 .danger-button {
@@ -2447,9 +2596,39 @@
   color: #f78989;
 }
 
+.custom-steps-container {
+  width: 100%;
+  margin: 10px 0;
+}
 
+.steps-titles {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 14px;
+  position: relative;
+}
 
+.step-title {
+  text-align: center;
+  flex: 1;
+  font-size: 14px;
+  color: #999;
+  position: relative;
+  padding-bottom: 5px;
+}
 
+.step-title.active {
+  color: #409eff;
+  font-weight: bold;
+}
+
+.event-title-center {
+  text-align: center;
+  font-size: 20px;
+  font-weight: bold;
+  margin-bottom: 5px;
+  color: #333;
+}
 
 .custom-steps {
   margin-top: -20px;
@@ -2460,7 +2639,18 @@
   }
 }
 
+.step-description {
+  font-size: 14px;
+  color: #666;
+  line-height: 1.5;
+  display: block;
+  text-align: center;
 
+  &:first-child {
+    font-weight: bold;
+    margin-bottom: 4px;
+  }
+}
 
 // 覆盖其他相关样式
 .status-flow {
diff --git a/src/views/tickets/ticketComponent/TicketDetailDialog.vue b/src/views/tickets/ticketComponent/TicketDetailDialog.vue
index 9a6a302..dc1acca 100644
--- a/src/views/tickets/ticketComponent/TicketDetailDialog.vue
+++ b/src/views/tickets/ticketComponent/TicketDetailDialog.vue
@@ -585,6 +585,8 @@
 
     getActiveStep() {
       const arr = this.stepStatusList;
+      console.log('stepStatusList',this.stepStatusList);
+      
       const index = arr.indexOf(String(this.currentDetail.status));
       return index !== -1 ? index + 2 : 1;
     },
@@ -754,14 +756,26 @@
   }
 };
 </script>
-
-<style lang="scss" scoped>
+<style lang="scss">
 .custom-dialog {
-  max-height: 96vh;
+  max-height: 95vh;
 
   :deep(.el-dialog__body) {
     border-top: 0.1rem solid #f0f0f0;
   }
+}
+</style>
+<style lang="scss" scoped>
+
+.step-timer {
+  position: absolute;
+  right: 80%;
+  top: 50%;
+  transform: translateY(-50%);
+  width: 100px;
+  margin-left: 4px;
+  color: #666;
+  font-size: 12px;
 }
 .detail-container {
   padding: 0 20px;
@@ -1061,4 +1075,20 @@
   pointer-events: none;
   opacity: 0.3 !important;
 }
+.readonly-processing-detail {
+  background-color: #f5f7fa;
+  padding: 12px;
+  border-radius: 4px;
+  min-height: 30px;
+  color: #606266;
+  line-height: 1.5;
+  display: block;
+  text-align: left;
+  margin-bottom: 5px;
+
+  &:first-child {
+    font-weight: bold;
+    margin-bottom: 4px;
+  }
+}
 </style>
\ No newline at end of file

--
Gitblit v1.9.3