瀏覽代碼

feat(licenseManagement): 优化合同管理界面与变更记录功能

- 禁止通过点击遮罩或按ESC键关闭更换合同弹窗
- 重构变更记录弹窗布局,支持滚动查看历史记录
- 新增变更记录分组展示,按日期归类显示
- 添加审核拒绝原因展示字段
- 接入真实接口获取变更记录数据,替换原有模拟数据
- 更新状态映射逻辑,调整审核中与审核拒绝的状态码
- 样式优化:改进记录项展示效果及空状态提示
- 异常处理增强:网络请求失败时显示空状态而非报错
congxuesong 3 周之前
父節點
當前提交
078b1d1eda
共有 1 個文件被更改,包括 133 次插入114 次删除
  1. 133 114
      src/views/licenseManagement/contractManagement.vue

+ 133 - 114
src/views/licenseManagement/contractManagement.vue

@@ -32,7 +32,14 @@
     </div>
 
     <!-- 更换合同弹窗 -->
-    <el-dialog v-model="replaceDialogVisible" title="更换合同" width="860px" :before-close="handleReplaceDialogClose">
+    <el-dialog
+      v-model="replaceDialogVisible"
+      title="更换合同"
+      width="860px"
+      :before-close="handleReplaceDialogClose"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+    >
       <el-scrollbar height="400px" class="replace-upload-scrollbar">
         <div class="replace-upload-area" :class="{ 'upload-full': uploadedImageCount >= uploadMaxCount }">
           <el-upload
@@ -79,32 +86,39 @@
 
     <!-- 变更记录弹窗 -->
     <el-dialog v-model="changeRecordDialogVisible" title="变更记录" width="900px" :close-on-click-modal="false">
-      <div class="change-record-content">
-        <div class="record-date">
-          {{ currentRecordDate }}
-        </div>
-        <div class="record-items">
-          <div v-for="(item, index) in changeRecordList" :key="index" class="record-item">
-            <div class="record-status-badge" :class="getStatusClass(item.status)">
-              {{ getStatusName(item.status) }}
+      <el-scrollbar height="400px" class="change-record-scrollbar">
+        <div v-if="changeRecordList && changeRecordList.length > 0" class="change-record-content">
+          <div v-for="(item, index) in changeRecordList" :key="index" class="record-group">
+            <div class="record-date" v-if="item.date">
+              {{ item.date }}
             </div>
-            <el-image
-              :src="item.imgUrl"
-              fit="cover"
-              class="record-image"
-              :preview-src-list="changeRecordList.map(record => record.imgUrl)"
-              :initial-index="index"
-            >
-              <template #error>
-                <div class="image-slot">
-                  <el-icon><Picture /></el-icon>
+            <div class="record-items">
+              <div class="record-item">
+                <div class="record-status-badge" :class="getStatusClass(item.licenseExecuteStatus)">
+                  {{ item.licenseExecuteName }}
                 </div>
-              </template>
-            </el-image>
+                <el-image
+                  :src="item.imgUrl"
+                  fit="cover"
+                  class="record-image"
+                  :preview-src-list="changeRecordList.map(record => record.imgUrl)"
+                  :initial-index="index"
+                >
+                  <template #error>
+                    <div class="image-slot">
+                      <el-icon><Picture /></el-icon>
+                    </div>
+                  </template>
+                </el-image>
+              </div>
+            </div>
+            <div v-if="item.rejectionReason" class="rejection-reason">拒绝原因: {{ item.rejectionReason }}</div>
           </div>
         </div>
-        <div v-if="rejectionReason" class="rejection-reason">拒绝原因: {{ rejectionReason }}</div>
-      </div>
+        <div v-else class="empty-record">
+          <el-empty description="暂无变更记录" :image-size="100" />
+        </div>
+      </el-scrollbar>
       <template #footer>
         <div class="dialog-footer">
           <el-button type="primary" @click="changeRecordDialogVisible = false"> 关闭 </el-button>
@@ -120,29 +134,20 @@ import { ElMessage, ElMessageBox } from "element-plus";
 import { Plus, Picture } from "@element-plus/icons-vue";
 import type { UploadProps, UploadFile } from "element-plus";
 import { localGet } from "@/utils";
-import { getContractImages, uploadContractImage, submitContractReview } from "@/api/modules/licenseManagement";
-
-interface ChangeRecordItem {
-  id: string;
-  status: number; // 状态:0-审核中,1-审核通过,2-审核拒绝
-  imgUrl: string; // 图片URL
-  rejectionReason?: string;
-}
+import { getContractImages, uploadContractImage, submitContractReview, getChangeRecords } from "@/api/modules/licenseManagement";
 
 // 状态映射对象
 const statusMap: Record<number, { name: string; class: string }> = {
-  0: { name: "审核中", class: "status-pending" },
   1: { name: "审核通过", class: "status-success" },
-  2: { name: "审核拒绝", class: "status-failed" }
+  2: { name: "审核中", class: "status-pending" },
+  3: { name: "审核拒绝", class: "status-failed" }
 };
 
 const contractList = ref<any>([]);
 const replaceDialogVisible = ref(false);
 const changeRecordDialogVisible = ref(false);
 const fileList = ref<UploadFile[]>([]);
-const currentRecordDate = ref("2025.08.01 10:29");
-const changeRecordList = ref<ChangeRecordItem[]>([]);
-const rejectionReason = ref("");
+const changeRecordList = ref<any>([]);
 
 const id = localGet("createdId");
 
@@ -201,25 +206,22 @@ const handleReplace = () => {
 
 const handleViewChangeRecord = async () => {
   try {
-    // TODO: 调用API获取变更记录
-    // const response = await getChangeRecords();
-    // if (response.code === 200) {
-    //   changeRecordList.value = response.data.records;
-    //   currentRecordDate.value = response.data.date;
-    //   rejectionReason.value = response.data.rejectionReason || "";
-    // }
-    // 模拟数据(假数据)
-    changeRecordList.value = [
-      { id: "1", status: 0, imgUrl: "https://picsum.photos/150/150?random=1" },
-      { id: "2", status: 1, imgUrl: "https://picsum.photos/150/150?random=2" },
-      { id: "3", status: 0, imgUrl: "https://picsum.photos/150/150?random=3" },
-      { id: "4", status: 2, imgUrl: "https://picsum.photos/150/150?random=4" },
-      { id: "5", status: 1, imgUrl: "https://picsum.photos/150/150?random=5" }
-    ];
-    rejectionReason.value = "";
+    const params = {
+      storeId: localGet("createdId")
+    };
+    const res: any = await getChangeRecords(params);
+    if (res.code === 200) {
+      changeRecordList.value = res.data;
+    } else {
+      // 请求失败时清空数据
+      changeRecordList.value = [];
+    }
     changeRecordDialogVisible.value = true;
   } catch (error) {
     ElMessage.error("获取变更记录失败");
+    // 发生错误时清空数据并显示空状态
+    changeRecordList.value = [];
+    changeRecordDialogVisible.value = true;
   }
 };
 
@@ -697,75 +699,92 @@ const getStatusName = (status: number) => {
     color: #8c939d;
   }
 }
-.change-record-content {
-  padding: 20px 0;
-  .record-date {
-    margin-bottom: 20px;
-    font-size: 16px;
-    font-weight: 500;
-    color: var(--el-text-color-primary);
-  }
-  .record-items {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 15px;
-    margin-bottom: 20px;
+.change-record-scrollbar {
+  :deep(.el-scrollbar__wrap) {
+    overflow-x: hidden;
   }
-  .record-item {
-    position: relative;
-    width: 150px;
-    height: 150px;
-    overflow: hidden;
-    border-radius: 8px;
-    .record-status-badge {
-      position: absolute;
-      right: 0;
-      bottom: 0;
-      left: 0;
-      z-index: 1;
-      padding: 4px 8px;
-      font-size: 12px;
+}
+.change-record-content {
+  .record-group {
+    padding: 20px;
+    margin-bottom: 30px;
+    background-color: var(--el-fill-color-lighter);
+    &:last-child {
+      margin-bottom: 0;
+    }
+    .record-date {
+      margin-bottom: 15px;
+      font-size: 16px;
       font-weight: 500;
-      text-align: center;
-      border-radius: 0 0 8px 8px;
-      &.status-pending {
-        color: #e6a23c;
-        background-color: rgb(253 246 236 / 95%);
-        border-top: 1px solid #e6a23c;
-      }
-      &.status-success {
-        color: #67c23a;
-        background-color: rgb(240 249 255 / 95%);
-        border-top: 1px solid #67c23a;
-      }
-      &.status-failed {
-        color: #f56c6c;
-        background-color: rgb(254 240 240 / 95%);
-        border-top: 1px solid #f56c6c;
-      }
+      color: var(--el-text-color-primary);
     }
-    .record-image {
-      width: 100%;
-      height: 100%;
-      .image-slot {
-        display: flex;
-        align-items: center;
-        justify-content: center;
+    .record-items {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 15px;
+    }
+    .record-item {
+      position: relative;
+      width: 150px;
+      height: 150px;
+      overflow: hidden;
+      border-radius: 8px;
+      .record-status-badge {
+        position: absolute;
+        right: 0;
+        bottom: 0;
+        left: 0;
+        z-index: 1;
+        padding: 4px 8px;
+        font-size: 12px;
+        font-weight: 500;
+        text-align: center;
+        border-radius: 0 0 8px 8px;
+        &.status-pending {
+          color: #e6a23c;
+          background-color: rgb(253 246 236 / 90%);
+          border-top: 1px solid #e6a23c;
+        }
+        &.status-success {
+          color: #67c23a;
+          background-color: rgb(240 249 255 / 90%);
+          border-top: 1px solid #67c23a;
+        }
+        &.status-failed {
+          color: #f56c6c;
+          background-color: rgb(254 240 240 / 90%);
+          border-top: 1px solid #f56c6c;
+        }
+      }
+      .record-image {
         width: 100%;
         height: 100%;
-        font-size: 30px;
-        color: var(--el-text-color-placeholder);
-        background: var(--el-fill-color-light);
+        .image-slot {
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          width: 100%;
+          height: 100%;
+          font-size: 30px;
+          color: var(--el-text-color-placeholder);
+          background: var(--el-fill-color-light);
+        }
       }
     }
+    .rejection-reason {
+      margin-top: 15px;
+      font-size: 14px;
+      font-weight: 500;
+      color: var(--el-text-color-regular);
+      border-radius: 8px;
+    }
   }
-  .rejection-reason {
-    padding: 15px;
-    margin-top: 20px;
-    font-size: 14px;
-    color: var(--el-text-color-regular);
-    background-color: var(--el-fill-color-lighter);
-    border-radius: 8px;
-  }
+}
+.empty-record {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  min-height: 300px;
+  padding: 40px 20px;
 }
 </style>