From f9d502b976ebb53281432faa56899015d49a87b6 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期三, 11 三月 2026 18:00:11 +0800
Subject: [PATCH] 能源管理、生产计划模块开发

---
 src/api/productionPlan/productionPlan.js                         |   60 +
 src/views/productionPlan/summaryByProduct/index.vue              |  290 ++++++
 src/api/energyManagement/energyType.js                           |   25 
 src/views/energyManagement/energyType/index2.vue                 |  128 ++
 src/views/productionPlan/productionPlan/components/PIMTable.vue  |   18 
 src/views/productionPlan/productionPlan/index.vue                | 1152 +++++++++++++++++++-------
 src/views/energyManagement/officeEnergyConsumption/index.vue     |  421 +++++++++
 src/views/energyManagement/productionEnergyConsumption/index.vue |  413 +++++++++
 8 files changed, 2,098 insertions(+), 409 deletions(-)

diff --git a/src/api/energyManagement/energyType.js b/src/api/energyManagement/energyType.js
index 43b8066..71fbc29 100644
--- a/src/api/energyManagement/energyType.js
+++ b/src/api/energyManagement/energyType.js
@@ -50,4 +50,29 @@
     url: "/energyConsumptionDetail/" + ids,
     method: "delete",
   });
+}
+
+// 鏌ヨ闄勪欢鍒楄〃
+export function fileListPage(query) {
+  return request({
+    url: "/energyConsumptionDetailFile/page",
+    method: "get",
+    params: query,
+  });
+}
+
+// 娣诲姞闄勪欢
+export function energyConsumptionDetailFileAdd(query) {
+    return request({
+        url: '/energyConsumptionDetailFile',
+        method: 'post',
+        data: query
+    })
+}
+// 鍒犻櫎闄勪欢
+export function energyConsumptionDetailFileDel(ids) {
+  return request({
+    url: `/energyConsumptionDetailFile/${ids}`,
+    method: 'delete',
+  })
 }
\ No newline at end of file
diff --git a/src/api/productionPlan/productionPlan.js b/src/api/productionPlan/productionPlan.js
index 2b08129..f064db9 100644
--- a/src/api/productionPlan/productionPlan.js
+++ b/src/api/productionPlan/productionPlan.js
@@ -9,3 +9,63 @@
     params: query,
   });
 }
+
+// 鎷夊彇鏁版嵁
+export function loadProdData(query) {
+  return request({
+    url: "/productionPlan/loadProdData",
+    method: "get",
+    params: query,
+  });
+}
+
+export function summaryByProductType(query) {
+  return request({
+    url: "/productionPlan/summaryByProductType",
+    method: "get",
+    params: query,
+  });
+}
+
+// 瀵煎嚭鐢熶骇璁″垝
+export function exportProductionPlan(bomId) {
+  return request({
+    url: "/productionPlan/export",
+    method: "post",
+    params: { bomId },
+    responseType: "blob",
+  });
+}
+
+// 鐢熶骇璁″垝-鏂板淇敼
+export function productionPlanAdd(query) {
+  return request({
+    url: "/productionPlan",
+    method: "post",
+    data: query,
+  });
+}
+export function productionPlanUpdate(query) {
+  return request({
+    url: "/productionPlan",
+    method: "put",
+    data: query,
+  });
+}
+
+// 鐢熶骇璁″垝-鍒犻櫎
+export function productionPlanDelete(data) {
+  return request({
+    url: "/productionPlan",
+    method: "delete",
+     data
+  });
+}
+// 鍚堝苟涓嬪彂
+export function productionPlanCombine(query) {
+  return request({
+    url: "/productionPlan/combine",
+    method: "post",
+    data: query,
+  });
+}
\ No newline at end of file
diff --git a/src/views/energyManagement/energyType/index2.vue b/src/views/energyManagement/energyType/index2.vue
index 88296a1..9d0b104 100644
--- a/src/views/energyManagement/energyType/index2.vue
+++ b/src/views/energyManagement/energyType/index2.vue
@@ -11,11 +11,11 @@
                      style="width: 120px;"
                      @change="handleQuery">
             <el-option label="姘�"
-                       value="gas" />
+                       value="姘�" />
             <el-option label="鐢�"
-                       value="electricity" />
+                       value="鐢�" />
             <el-option label="姘�"
-                       value="water" />
+                       value="姘�" />
           </el-select>
         </el-form-item>
         <el-form-item label="鑳芥簮鍚嶇О:">
@@ -495,7 +495,9 @@
   .search_form {
     display: flex;
     justify-content: space-between;
-    align-items: center;
+    align-items: flex-start;
+    flex-wrap: wrap;
+    gap: 16px;
     margin-bottom: 24px;
     padding: 20px;
     background-color: #ffffff;
@@ -505,6 +507,28 @@
 
     &:hover {
       box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
+    }
+
+    :deep(.el-form) {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 0;
+
+      .el-form-item {
+        margin-right: 16px;
+        margin-bottom: 0;
+
+        &:last-child {
+          margin-right: 0;
+        }
+      }
+    }
+
+    > div {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 8px;
+      flex-shrink: 0;
     }
   }
 
@@ -637,28 +661,83 @@
     font-size: 12px;
   }
 
-  @media (max-width: 768px) {
-    .app-container {
-      padding: 16px;
+  @media (max-width: 1200px) {
+    .search_form {
+      :deep(.el-form) {
+        .el-form-item {
+          margin-right: 12px;
+          margin-bottom: 8px;
+        }
+      }
     }
+  }
 
+  @media (max-width: 992px) {
     .search_form {
       flex-direction: column;
-      align-items: flex-start;
+      align-items: stretch;
       gap: 12px;
 
-      .el-form {
+      :deep(.el-form) {
         width: 100%;
 
         .el-form-item {
-          width: 100%;
+          margin-right: 12px;
+          margin-bottom: 8px;
+          flex: 1;
+          min-width: 200px;
+
+          &:last-child {
+            margin-right: 12px;
+          }
         }
       }
 
       > div {
         width: 100%;
-        display: flex;
-        gap: 12px;
+        justify-content: flex-end;
+      }
+    }
+  }
+
+  @media (max-width: 768px) {
+    .app-container {
+      padding: 12px;
+    }
+
+    .search_form {
+      padding: 16px;
+      gap: 12px;
+
+      :deep(.el-form) {
+        flex-direction: column;
+        width: 100%;
+
+        .el-form-item {
+          width: 100%;
+          margin-right: 0;
+          margin-bottom: 12px;
+
+          &:last-child {
+            margin-right: 0;
+            margin-bottom: 0;
+          }
+
+          .el-form-item__content {
+            width: 100%;
+
+            .el-input,
+            .el-select,
+            .el-date-editor {
+              width: 100% !important;
+            }
+          }
+        }
+      }
+
+      > div {
+        width: 100%;
+        justify-content: stretch;
 
         .el-button {
           flex: 1;
@@ -666,17 +745,36 @@
       }
     }
 
+    .table_list {
+      height: calc(100vh - 300px);
+    }
+
     :deep(.el-table) {
       th,
       td {
-        padding: 10px 0;
+        padding: 8px 0;
         font-size: 12px;
       }
     }
 
     :deep(.el-dialog) {
-      width: 90% !important;
-      margin: 20px auto !important;
+      width: 95% !important;
+      margin: 10px auto !important;
+    }
+  }
+
+  @media (max-width: 480px) {
+    .search_form {
+      padding: 12px;
+
+      > div {
+        flex-direction: column;
+        gap: 8px;
+
+        .el-button {
+          width: 100%;
+        }
+      }
     }
   }
   .consumption-value {
diff --git a/src/views/energyManagement/officeEnergyConsumption/index.vue b/src/views/energyManagement/officeEnergyConsumption/index.vue
index c3fbf82..0def5d5 100644
--- a/src/views/energyManagement/officeEnergyConsumption/index.vue
+++ b/src/views/energyManagement/officeEnergyConsumption/index.vue
@@ -11,14 +11,21 @@
                      style="width: 140px;"
                      @change="handleQuery">
             <el-option label="姘�"
-                       value="water" />
+                       value="姘�" />
             <el-option label="鐢�"
-                       value="electricity" />
+                       value="鐢�" />
             <el-option label="姘�"
-                       value="gas" />
+                       value="姘�" />
           </el-select>
         </el-form-item>
-        <!-- <el-form-item label="鏃ユ湡鑼冨洿:">
+        <el-form-item label="鑳芥簮鍚嶇О:">
+          <el-input v-model="searchForm.energyName"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    style="width: 160px;"
+                    @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item label="鏃ユ湡鑼冨洿:">
           <el-date-picker v-model="searchForm.dateRange"
                           type="daterange"
                           range-separator="鑷�"
@@ -27,7 +34,7 @@
                           value-format="YYYY-MM-DD"
                           style="width: 240px;"
                           @change="handleQuery" />
-        </el-form-item> -->
+        </el-form-item>
         <el-form-item>
           <el-button type="primary"
                      @click="handleQuery">鎼滅储</el-button>
@@ -35,10 +42,12 @@
         </el-form-item>
       </el-form>
       <div>
-        <!-- <el-button type="primary"
-                   @click="handleAdd">鏂板</el-button> -->
+        <el-button type="primary"
+                   @click="handleAdd">鏂板</el-button>
         <el-button type="success"
                    @click="handleExport">瀵煎嚭</el-button>
+        <el-button type="warning"
+                   @click="handleImport">瀵煎叆</el-button>
       </div>
     </div>
     <!-- 鏁版嵁琛ㄦ牸 -->
@@ -102,8 +111,8 @@
                          label="澶囨敞"
                          min-width="150"
                          show-overflow-tooltip />
-        <!-- <el-table-column label="鎿嶄綔"
-                         width="180"
+        <el-table-column label="鎿嶄綔"
+                         width="220"
                          align="center"
                          fixed="right">
           <template #default="scope">
@@ -113,8 +122,11 @@
             <el-button type="danger"
                        link
                        @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+            <el-button type="info"
+                       link
+                       @click="downLoadFile(scope.row)">闄勪欢</el-button>
           </template>
-        </el-table-column> -->
+        </el-table-column>
       </el-table>
       <div class="pagination-container">
         <el-pagination v-model:current-page="page.current"
@@ -199,6 +211,28 @@
         </span>
       </template>
     </el-dialog>
+    <!-- 闄勪欢鍒楄〃寮圭獥 -->
+    <FileListDialog ref="fileListRef"
+                    v-model="fileListDialogVisible"
+                    :show-upload-button="true"
+                    :show-delete-button="true"
+                    :is-show-pagination="true"
+                    :page="filePagination"
+                    :upload-method="handleUpload"
+                    :delete-method="handleFileDelete"
+                    @pagination="paginationSearch"
+                    title="闄勪欢鍒楄〃" />
+    <ImportDialog ref="importDialogRef"
+                  v-model="importDialogVisible"
+                  title="瀵煎叆鏄庣粏"
+                  :action="importAction"
+                  :headers="importHeaders"
+                  :auto-upload="false"
+                  :on-success="handleImportSuccess"
+                  :on-error="handleImportError"
+                  @confirm="handleImportConfirm"
+                  @download-template="handleDownloadTemplate"
+                  @close="handleImportClose" />
   </div>
 </template>
 
@@ -206,11 +240,18 @@
   import { ref, reactive, onMounted, getCurrentInstance } from "vue";
   import { ElMessage, ElMessageBox } from "element-plus";
   import { Watermelon, Lightning } from "@element-plus/icons-vue";
+  import ImportDialog from "@/components/Dialog/ImportDialog.vue";
+  import { getToken } from "@/utils/auth";
+  import FileListDialog from "@/components/Dialog/FileListDialog.vue";
+  import request from "@/utils/request";
   import {
     energyConsumptionDetailListPage,
     energyConsumptionDetailAdd,
     energyConsumptionDetailDelete,
     energyTypeListPage,
+    fileListPage,
+    energyConsumptionDetailFileAdd,
+    energyConsumptionDetailFileDel,
   } from "@/api/energyManagement/energyType";
 
   // 鎼滅储琛ㄥ崟
@@ -247,6 +288,25 @@
   const formRef = ref(null);
   const isEdit = ref(false);
   const currentId = ref(null);
+
+  // 闄勪欢鐩稿叧
+  const fileListRef = ref(null);
+  const fileListDialogVisible = ref(false);
+  const currentFileRow = ref(null);
+  const filePagination = ref({
+    current: 1,
+    size: 10,
+    total: 0,
+  });
+
+  // 瀵煎叆鐩稿叧
+  const importDialogRef = ref(null);
+  const importDialogVisible = ref(false);
+  const importAction =
+    import.meta.env.VITE_APP_BASE_API + "/energyConsumptionDetail/importData";
+  const importHeaders = ref({
+    Authorization: `Bearer ${getToken()}`,
+  });
 
   // 琛ㄥ崟鏁版嵁
   const form = reactive({
@@ -314,15 +374,16 @@
       current: page.current,
       size: page.size,
       type: "鍔炲叕",
-      // energyType: searchForm.energyType,
-      // startDate:
-      //   searchForm.dateRange && searchForm.dateRange.length === 2
-      //     ? searchForm.dateRange[0]
-      //     : null,
-      // endDate:
-      //   searchForm.dateRange && searchForm.dateRange.length === 2
-      //     ? searchForm.dateRange[1]
-      //     : null,
+      energyTyep: searchForm.energyType,
+      energyName: searchForm.energyName,
+      startDate:
+        searchForm.dateRange && searchForm.dateRange.length === 2
+          ? searchForm.dateRange[0]
+          : null,
+      endDate:
+        searchForm.dateRange && searchForm.dateRange.length === 2
+          ? searchForm.dateRange[1]
+          : null,
     };
     energyConsumptionDetailListPage(params)
       .then(res => {
@@ -358,6 +419,7 @@
   // 閲嶇疆
   const handleReset = () => {
     searchForm.energyType = "";
+    searchForm.energyName = "";
     searchForm.dateRange = [];
     page.current = 1;
     handleQuery();
@@ -478,6 +540,205 @@
     });
   };
 
+  // 涓嬭浇鏂囦欢
+  const downLoadFile = row => {
+    currentFileRow.value = row;
+    fileListPage({
+      energyConsumptionDetailId: row.id,
+      current: filePagination.value.current,
+      size: filePagination.value.size,
+    }).then(res => {
+      if (fileListRef.value) {
+        fileListRef.value.open(res.data.records);
+      }
+      filePagination.value.total = res.data.total || 0;
+    });
+  };
+
+  // 涓婁紶闄勪欢
+  const handleUpload = async () => {
+    if (!currentFileRow.value) {
+      ElMessage.warning("璇峰厛閫夋嫨鏁版嵁");
+      return null;
+    }
+
+    return new Promise(resolve => {
+      // 鍒涘缓涓�涓殣钘忕殑鏂囦欢杈撳叆鍏冪礌
+      const input = document.createElement("input");
+      input.type = "file";
+      input.style.display = "none";
+      input.onchange = async e => {
+        const file = e.target.files[0];
+        if (!file) {
+          resolve(null);
+          return;
+        }
+
+        try {
+          // 浣跨敤 FormData 涓婁紶鏂囦欢
+          const formData = new FormData();
+          formData.append("file", file);
+
+          const uploadRes = await request({
+            url: "/file/upload",
+            method: "post",
+            data: formData,
+            headers: {
+              "Content-Type": "multipart/form-data",
+              Authorization: `Bearer ${getToken()}`,
+            },
+          });
+
+          if (uploadRes.code === 200) {
+            // 淇濆瓨闄勪欢淇℃伅
+            const fileData = {
+              energyConsumptionDetailId: currentFileRow.value.id,
+              name: uploadRes.data.originalName || file.name,
+              url: uploadRes.data.tempPath || uploadRes.data.url,
+            };
+
+            const saveRes = await energyConsumptionDetailFileAdd(fileData);
+            if (saveRes.code === 200) {
+              proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
+              // 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
+              const listRes = await fileListPage({
+                energyConsumptionDetailId: currentFileRow.value.id,
+                current: filePagination.value.current,
+                size: filePagination.value.size,
+              });
+              if (listRes.code === 200 && fileListRef.value) {
+                const fileList = (listRes.data?.records || []).map(item => ({
+                  name: item.name,
+                  url: item.url,
+                  id: item.id,
+                  ...item,
+                }));
+                fileListRef.value.setList(fileList);
+                filePagination.value.total = listRes.data?.total || 0;
+              }
+              // 杩斿洖鏂版枃浠朵俊鎭�
+              resolve({
+                name: fileData.name,
+                url: fileData.url,
+                id: Date.now(),
+              });
+            } else {
+              ElMessage.error(uploadRes.message || "鏂囦欢涓婁紶澶辫触");
+              resolve(null);
+            }
+          }
+        } catch (error) {
+          ElMessage.error("鏂囦欢涓婁紶澶辫触");
+          resolve(null);
+        } finally {
+          document.body.removeChild(input);
+        }
+      };
+
+      document.body.appendChild(input);
+      input.click();
+    });
+  };
+
+  // 鍒嗛〉鎼滅储
+  const paginationSearch = async (page, size) => {
+    filePagination.value.current = page;
+    filePagination.value.size = size;
+    const listRes = await fileListPage({
+      energyConsumptionDetailId: currentFileRow.value.id,
+      current: filePagination.value.current,
+      size: filePagination.value.size,
+    });
+    if (listRes.code === 200) {
+      const fileList = (listRes.data?.records || []).map(item => ({
+        name: item.name,
+        url: item.url,
+        id: item.id,
+        ...item,
+      }));
+
+      fileListRef.value.setList(fileList);
+      filePagination.value.total = listRes.data?.total || 0;
+    }
+  };
+
+  // 鍒犻櫎闄勪欢
+  const handleFileDelete = async row => {
+    try {
+      const res = await energyConsumptionDetailFileDel([row.id]);
+      if (res.code === 200) {
+        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+        // 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
+        if (currentFileRow.value && fileListRef.value) {
+          const listRes = await fileListPage({
+            energyConsumptionDetailId: currentFileRow.value.id,
+            current: filePagination.value.current,
+            size: filePagination.value.size,
+          });
+          if (listRes.code === 200) {
+            const fileList = (listRes.data?.records || []).map(item => ({
+              name: item.name,
+              url: item.url,
+              id: item.id,
+              ...item,
+            }));
+            fileListRef.value.setList(fileList);
+            filePagination.value.total = listRes.data?.total || 0;
+          }
+        }
+        return true; // 杩斿洖 true 琛ㄧず鍒犻櫎鎴愬姛锛岀粍浠朵細鏇存柊鍒楄〃
+      } else {
+        proxy.$modal.msgError(res.msg || "鍒犻櫎澶辫触");
+        return false;
+      }
+    } catch (error) {
+      ElMessage.error("鍒犻櫎澶辫触");
+      return false;
+    }
+  };
+
+  // 瀵煎叆
+  const handleImport = () => {
+    importDialogVisible.value = true;
+  };
+
+  // 瀵煎叆鎴愬姛
+  const handleImportSuccess = response => {
+    if (response.code === 200) {
+      ElMessage.success("瀵煎叆鎴愬姛");
+      importDialogVisible.value = false;
+      handleQuery();
+    } else {
+      ElMessage.error(response.message || "瀵煎叆澶辫触");
+    }
+  };
+
+  // 瀵煎叆澶辫触
+  const handleImportError = error => {
+    ElMessage.error("瀵煎叆澶辫触锛岃妫�鏌ユ枃浠舵牸寮忔槸鍚︽纭�");
+  };
+
+  // 纭瀵煎叆
+  const handleImportConfirm = () => {
+    if (importDialogRef.value) {
+      importDialogRef.value.submit();
+    }
+  };
+
+  // 涓嬭浇妯℃澘
+  const handleDownloadTemplate = () => {
+    proxy.download(
+      "/energyConsumptionDetail/downloadTemplate",
+      {},
+      "鐢熶骇鑳借�楀鍏ユā鏉�.xlsx"
+    );
+  };
+
+  // 鍏抽棴瀵煎叆寮圭獥
+  const handleImportClose = () => {
+    importDialogVisible.value = false;
+  };
+
   onMounted(() => {
     getEnergyTypeList();
     handleQuery();
@@ -494,7 +755,9 @@
   .search_form {
     display: flex;
     justify-content: space-between;
-    align-items: center;
+    align-items: flex-start;
+    flex-wrap: wrap;
+    gap: 16px;
     margin-bottom: 24px;
     padding: 20px;
     background-color: #ffffff;
@@ -504,6 +767,28 @@
 
     &:hover {
       box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
+    }
+
+    :deep(.el-form) {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 0;
+
+      .el-form-item {
+        margin-right: 16px;
+        margin-bottom: 0;
+
+        &:last-child {
+          margin-right: 0;
+        }
+      }
+    }
+
+    > div {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 8px;
+      flex-shrink: 0;
     }
   }
 
@@ -636,28 +921,83 @@
     font-size: 12px;
   }
 
-  @media (max-width: 768px) {
-    .app-container {
-      padding: 16px;
+  @media (max-width: 1200px) {
+    .search_form {
+      :deep(.el-form) {
+        .el-form-item {
+          margin-right: 12px;
+          margin-bottom: 8px;
+        }
+      }
     }
+  }
 
+  @media (max-width: 992px) {
     .search_form {
       flex-direction: column;
-      align-items: flex-start;
+      align-items: stretch;
       gap: 12px;
 
-      .el-form {
+      :deep(.el-form) {
         width: 100%;
 
         .el-form-item {
-          width: 100%;
+          margin-right: 12px;
+          margin-bottom: 8px;
+          flex: 1;
+          min-width: 200px;
+
+          &:last-child {
+            margin-right: 12px;
+          }
         }
       }
 
       > div {
         width: 100%;
-        display: flex;
-        gap: 12px;
+        justify-content: flex-end;
+      }
+    }
+  }
+
+  @media (max-width: 768px) {
+    .app-container {
+      padding: 12px;
+    }
+
+    .search_form {
+      padding: 16px;
+      gap: 12px;
+
+      :deep(.el-form) {
+        flex-direction: column;
+        width: 100%;
+
+        .el-form-item {
+          width: 100%;
+          margin-right: 0;
+          margin-bottom: 12px;
+
+          &:last-child {
+            margin-right: 0;
+            margin-bottom: 0;
+          }
+
+          .el-form-item__content {
+            width: 100%;
+
+            .el-input,
+            .el-select,
+            .el-date-editor {
+              width: 100% !important;
+            }
+          }
+        }
+      }
+
+      > div {
+        width: 100%;
+        justify-content: stretch;
 
         .el-button {
           flex: 1;
@@ -665,17 +1005,36 @@
       }
     }
 
+    .table_list {
+      height: calc(100vh - 300px);
+    }
+
     :deep(.el-table) {
       th,
       td {
-        padding: 10px 0;
+        padding: 8px 0;
         font-size: 12px;
       }
     }
 
     :deep(.el-dialog) {
-      width: 90% !important;
-      margin: 20px auto !important;
+      width: 95% !important;
+      margin: 10px auto !important;
+    }
+  }
+
+  @media (max-width: 480px) {
+    .search_form {
+      padding: 12px;
+
+      > div {
+        flex-direction: column;
+        gap: 8px;
+
+        .el-button {
+          width: 100%;
+        }
+      }
     }
   }
   .consumption-value {
diff --git a/src/views/energyManagement/productionEnergyConsumption/index.vue b/src/views/energyManagement/productionEnergyConsumption/index.vue
index 7a69ec2..cf8784f 100644
--- a/src/views/energyManagement/productionEnergyConsumption/index.vue
+++ b/src/views/energyManagement/productionEnergyConsumption/index.vue
@@ -11,14 +11,21 @@
                      style="width: 140px;"
                      @change="handleQuery">
             <el-option label="姘�"
-                       value="water" />
+                       value="姘�" />
             <el-option label="鐢�"
-                       value="electricity" />
+                       value="鐢�" />
             <el-option label="姘�"
-                       value="gas" />
+                       value="姘�" />
           </el-select>
         </el-form-item>
-        <!-- <el-form-item label="鏃ユ湡鑼冨洿:">
+        <el-form-item label="鑳芥簮鍚嶇О:">
+          <el-input v-model="searchForm.energyName"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    style="width: 160px;"
+                    @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item label="鏃ユ湡鑼冨洿:">
           <el-date-picker v-model="searchForm.dateRange"
                           type="daterange"
                           range-separator="鑷�"
@@ -27,7 +34,7 @@
                           value-format="YYYY-MM-DD"
                           style="width: 240px;"
                           @change="handleQuery" />
-        </el-form-item> -->
+        </el-form-item>
         <el-form-item>
           <el-button type="primary"
                      @click="handleQuery">鎼滅储</el-button>
@@ -39,6 +46,8 @@
                    @click="handleAdd">鏂板</el-button>
         <el-button type="success"
                    @click="handleExport">瀵煎嚭</el-button>
+        <el-button type="warning"
+                   @click="handleImport">瀵煎叆</el-button>
       </div>
     </div>
     <!-- 鏁版嵁琛ㄦ牸 -->
@@ -103,7 +112,7 @@
                          min-width="150"
                          show-overflow-tooltip />
         <el-table-column label="鎿嶄綔"
-                         width="180"
+                         width="220"
                          align="center"
                          fixed="right">
           <template #default="scope">
@@ -113,6 +122,9 @@
             <el-button type="danger"
                        link
                        @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+            <el-button type="info"
+                       link
+                       @click="downLoadFile(scope.row)">闄勪欢</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -199,6 +211,28 @@
         </span>
       </template>
     </el-dialog>
+    <!-- 闄勪欢鍒楄〃寮圭獥 -->
+    <FileListDialog ref="fileListRef"
+                    v-model="fileListDialogVisible"
+                    :show-upload-button="true"
+                    :show-delete-button="true"
+                    :is-show-pagination="true"
+                    :page="filePagination"
+                    :upload-method="handleUpload"
+                    :delete-method="handleFileDelete"
+                    @pagination="paginationSearch"
+                    title="闄勪欢鍒楄〃" />
+    <ImportDialog ref="importDialogRef"
+                  v-model="importDialogVisible"
+                  title="瀵煎叆鏄庣粏"
+                  :action="importAction"
+                  :headers="importHeaders"
+                  :auto-upload="false"
+                  :on-success="handleImportSuccess"
+                  :on-error="handleImportError"
+                  @confirm="handleImportConfirm"
+                  @download-template="handleDownloadTemplate"
+                  @close="handleImportClose" />
   </div>
 </template>
 
@@ -206,11 +240,18 @@
   import { ref, reactive, onMounted, getCurrentInstance } from "vue";
   import { ElMessage, ElMessageBox } from "element-plus";
   import { Watermelon, Lightning } from "@element-plus/icons-vue";
+  import ImportDialog from "@/components/Dialog/ImportDialog.vue";
+  import { getToken } from "@/utils/auth";
+  import FileListDialog from "@/components/Dialog/FileListDialog.vue";
+  import request from "@/utils/request";
   import {
     energyConsumptionDetailListPage,
     energyConsumptionDetailAdd,
     energyConsumptionDetailDelete,
     energyTypeListPage,
+    fileListPage,
+    energyConsumptionDetailFileAdd,
+    energyConsumptionDetailFileDel,
   } from "@/api/energyManagement/energyType";
 
   // 鎼滅储琛ㄥ崟
@@ -247,6 +288,25 @@
   const formRef = ref(null);
   const isEdit = ref(false);
   const currentId = ref(null);
+
+  // 闄勪欢鐩稿叧
+  const fileListRef = ref(null);
+  const fileListDialogVisible = ref(false);
+  const currentFileRow = ref(null);
+  const filePagination = ref({
+    current: 1,
+    size: 10,
+    total: 0,
+  });
+
+  // 瀵煎叆鐩稿叧
+  const importDialogRef = ref(null);
+  const importDialogVisible = ref(false);
+  const importAction =
+    import.meta.env.VITE_APP_BASE_API + "/energyConsumptionDetail/importData";
+  const importHeaders = ref({
+    Authorization: `Bearer ${getToken()}`,
+  });
 
   // 琛ㄥ崟鏁版嵁
   const form = reactive({
@@ -314,15 +374,16 @@
       current: page.current,
       size: page.size,
       type: "鐢熶骇",
-      // energyType: searchForm.energyType,
-      // startDate:
-      //   searchForm.dateRange && searchForm.dateRange.length === 2
-      //     ? searchForm.dateRange[0]
-      //     : null,
-      // endDate:
-      //   searchForm.dateRange && searchForm.dateRange.length === 2
-      //     ? searchForm.dateRange[1]
-      //     : null,
+      energyTyep: searchForm.energyType,
+      energyName: searchForm.energyName,
+      startDate:
+        searchForm.dateRange && searchForm.dateRange.length === 2
+          ? searchForm.dateRange[0]
+          : null,
+      endDate:
+        searchForm.dateRange && searchForm.dateRange.length === 2
+          ? searchForm.dateRange[1]
+          : null,
     };
     energyConsumptionDetailListPage(params)
       .then(res => {
@@ -358,6 +419,7 @@
   // 閲嶇疆
   const handleReset = () => {
     searchForm.energyType = "";
+    searchForm.energyName = "";
     searchForm.dateRange = [];
     page.current = 1;
     handleQuery();
@@ -478,6 +540,205 @@
     });
   };
 
+  // 涓嬭浇鏂囦欢
+  const downLoadFile = row => {
+    currentFileRow.value = row;
+    fileListPage({
+      energyConsumptionDetailId: row.id,
+      current: filePagination.value.current,
+      size: filePagination.value.size,
+    }).then(res => {
+      if (fileListRef.value) {
+        fileListRef.value.open(res.data.records);
+      }
+      filePagination.value.total = res.data.total || 0;
+    });
+  };
+
+  // 涓婁紶闄勪欢
+  const handleUpload = async () => {
+    if (!currentFileRow.value) {
+      ElMessage.warning("璇峰厛閫夋嫨鏁版嵁");
+      return null;
+    }
+
+    return new Promise(resolve => {
+      // 鍒涘缓涓�涓殣钘忕殑鏂囦欢杈撳叆鍏冪礌
+      const input = document.createElement("input");
+      input.type = "file";
+      input.style.display = "none";
+      input.onchange = async e => {
+        const file = e.target.files[0];
+        if (!file) {
+          resolve(null);
+          return;
+        }
+
+        try {
+          // 浣跨敤 FormData 涓婁紶鏂囦欢
+          const formData = new FormData();
+          formData.append("file", file);
+
+          const uploadRes = await request({
+            url: "/file/upload",
+            method: "post",
+            data: formData,
+            headers: {
+              "Content-Type": "multipart/form-data",
+              Authorization: `Bearer ${getToken()}`,
+            },
+          });
+
+          if (uploadRes.code === 200) {
+            // 淇濆瓨闄勪欢淇℃伅
+            const fileData = {
+              energyConsumptionDetailId: currentFileRow.value.id,
+              name: uploadRes.data.originalName || file.name,
+              url: uploadRes.data.tempPath || uploadRes.data.url,
+            };
+
+            const saveRes = await energyConsumptionDetailFileAdd(fileData);
+            if (saveRes.code === 200) {
+              proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
+              // 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
+              const listRes = await fileListPage({
+                energyConsumptionDetailId: currentFileRow.value.id,
+                current: filePagination.value.current,
+                size: filePagination.value.size,
+              });
+              if (listRes.code === 200 && fileListRef.value) {
+                const fileList = (listRes.data?.records || []).map(item => ({
+                  name: item.name,
+                  url: item.url,
+                  id: item.id,
+                  ...item,
+                }));
+                fileListRef.value.setList(fileList);
+                filePagination.value.total = listRes.data?.total || 0;
+              }
+              // 杩斿洖鏂版枃浠朵俊鎭�
+              resolve({
+                name: fileData.name,
+                url: fileData.url,
+                id: Date.now(),
+              });
+            } else {
+              ElMessage.error(uploadRes.message || "鏂囦欢涓婁紶澶辫触");
+              resolve(null);
+            }
+          }
+        } catch (error) {
+          ElMessage.error("鏂囦欢涓婁紶澶辫触");
+          resolve(null);
+        } finally {
+          document.body.removeChild(input);
+        }
+      };
+
+      document.body.appendChild(input);
+      input.click();
+    });
+  };
+
+  // 鍒嗛〉鎼滅储
+  const paginationSearch = async (page, size) => {
+    filePagination.value.current = page;
+    filePagination.value.size = size;
+    const listRes = await fileListPage({
+      energyConsumptionDetailId: currentFileRow.value.id,
+      current: filePagination.value.current,
+      size: filePagination.value.size,
+    });
+    if (listRes.code === 200) {
+      const fileList = (listRes.data?.records || []).map(item => ({
+        name: item.name,
+        url: item.url,
+        id: item.id,
+        ...item,
+      }));
+
+      fileListRef.value.setList(fileList);
+      filePagination.value.total = listRes.data?.total || 0;
+    }
+  };
+
+  // 鍒犻櫎闄勪欢
+  const handleFileDelete = async row => {
+    try {
+      const res = await energyConsumptionDetailFileDel([row.id]);
+      if (res.code === 200) {
+        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+        // 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
+        if (currentFileRow.value && fileListRef.value) {
+          const listRes = await fileListPage({
+            energyConsumptionDetailId: currentFileRow.value.id,
+            current: filePagination.value.current,
+            size: filePagination.value.size,
+          });
+          if (listRes.code === 200) {
+            const fileList = (listRes.data?.records || []).map(item => ({
+              name: item.name,
+              url: item.url,
+              id: item.id,
+              ...item,
+            }));
+            fileListRef.value.setList(fileList);
+            filePagination.value.total = listRes.data?.total || 0;
+          }
+        }
+        return true; // 杩斿洖 true 琛ㄧず鍒犻櫎鎴愬姛锛岀粍浠朵細鏇存柊鍒楄〃
+      } else {
+        proxy.$modal.msgError(res.msg || "鍒犻櫎澶辫触");
+        return false;
+      }
+    } catch (error) {
+      ElMessage.error("鍒犻櫎澶辫触");
+      return false;
+    }
+  };
+
+  // 瀵煎叆
+  const handleImport = () => {
+    importDialogVisible.value = true;
+  };
+
+  // 瀵煎叆鎴愬姛
+  const handleImportSuccess = response => {
+    if (response.code === 200) {
+      ElMessage.success("瀵煎叆鎴愬姛");
+      importDialogVisible.value = false;
+      handleQuery();
+    } else {
+      ElMessage.error(response.message || "瀵煎叆澶辫触");
+    }
+  };
+
+  // 瀵煎叆澶辫触
+  const handleImportError = error => {
+    ElMessage.error("瀵煎叆澶辫触锛岃妫�鏌ユ枃浠舵牸寮忔槸鍚︽纭�");
+  };
+
+  // 纭瀵煎叆
+  const handleImportConfirm = () => {
+    if (importDialogRef.value) {
+      importDialogRef.value.submit();
+    }
+  };
+
+  // 涓嬭浇妯℃澘
+  const handleDownloadTemplate = () => {
+    proxy.download(
+      "/energyConsumptionDetail/downloadTemplate",
+      {},
+      "鐢熶骇鑳借�楀鍏ユā鏉�.xlsx"
+    );
+  };
+
+  // 鍏抽棴瀵煎叆寮圭獥
+  const handleImportClose = () => {
+    importDialogVisible.value = false;
+  };
+
   onMounted(() => {
     getEnergyTypeList();
     handleQuery();
@@ -494,7 +755,9 @@
   .search_form {
     display: flex;
     justify-content: space-between;
-    align-items: center;
+    align-items: flex-start;
+    flex-wrap: wrap;
+    gap: 16px;
     margin-bottom: 24px;
     padding: 20px;
     background-color: #ffffff;
@@ -504,6 +767,28 @@
 
     &:hover {
       box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
+    }
+
+    :deep(.el-form) {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 0;
+
+      .el-form-item {
+        margin-right: 16px;
+        margin-bottom: 0;
+
+        &:last-child {
+          margin-right: 0;
+        }
+      }
+    }
+
+    > div {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 8px;
+      flex-shrink: 0;
     }
   }
 
@@ -636,28 +921,83 @@
     font-size: 12px;
   }
 
-  @media (max-width: 768px) {
-    .app-container {
-      padding: 16px;
+  @media (max-width: 1200px) {
+    .search_form {
+      :deep(.el-form) {
+        .el-form-item {
+          margin-right: 12px;
+          margin-bottom: 8px;
+        }
+      }
     }
+  }
 
+  @media (max-width: 992px) {
     .search_form {
       flex-direction: column;
-      align-items: flex-start;
+      align-items: stretch;
       gap: 12px;
 
-      .el-form {
+      :deep(.el-form) {
         width: 100%;
 
         .el-form-item {
-          width: 100%;
+          margin-right: 12px;
+          margin-bottom: 8px;
+          flex: 1;
+          min-width: 200px;
+
+          &:last-child {
+            margin-right: 12px;
+          }
         }
       }
 
       > div {
         width: 100%;
-        display: flex;
-        gap: 12px;
+        justify-content: flex-end;
+      }
+    }
+  }
+
+  @media (max-width: 768px) {
+    .app-container {
+      padding: 12px;
+    }
+
+    .search_form {
+      padding: 16px;
+      gap: 12px;
+
+      :deep(.el-form) {
+        flex-direction: column;
+        width: 100%;
+
+        .el-form-item {
+          width: 100%;
+          margin-right: 0;
+          margin-bottom: 12px;
+
+          &:last-child {
+            margin-right: 0;
+            margin-bottom: 0;
+          }
+
+          .el-form-item__content {
+            width: 100%;
+
+            .el-input,
+            .el-select,
+            .el-date-editor {
+              width: 100% !important;
+            }
+          }
+        }
+      }
+
+      > div {
+        width: 100%;
+        justify-content: stretch;
 
         .el-button {
           flex: 1;
@@ -665,17 +1005,36 @@
       }
     }
 
+    .table_list {
+      height: calc(100vh - 300px);
+    }
+
     :deep(.el-table) {
       th,
       td {
-        padding: 10px 0;
+        padding: 8px 0;
         font-size: 12px;
       }
     }
 
     :deep(.el-dialog) {
-      width: 90% !important;
-      margin: 20px auto !important;
+      width: 95% !important;
+      margin: 10px auto !important;
+    }
+  }
+
+  @media (max-width: 480px) {
+    .search_form {
+      padding: 12px;
+
+      > div {
+        flex-direction: column;
+        gap: 8px;
+
+        .el-button {
+          width: 100%;
+        }
+      }
     }
   }
   .consumption-value {
diff --git a/src/views/productionPlan/productionPlan/components/PIMTable.vue b/src/views/productionPlan/productionPlan/components/PIMTable.vue
index c0f7616..9431002 100644
--- a/src/views/productionPlan/productionPlan/components/PIMTable.vue
+++ b/src/views/productionPlan/productionPlan/components/PIMTable.vue
@@ -43,7 +43,8 @@
                      :align="item.align"
                      :sortable="!!item.sortable"
                      :type="item.type"
-                     :width="item.width">
+                     :width="item.width"
+                     :class-name="item.className || ''">
       <template #header="scope">
         <div class="pim-table-header-cell">
           <div class="pim-table-header-title">
@@ -65,24 +66,28 @@
       </template>
       <template #default="scope">
         <!-- 鎻掓Ы -->
-        <div v-if="item.dataType == 'slot'">
+        <div v-if="item.dataType == 'slot'"
+             :class="item.className || ''">
           <slot v-if="item.slot"
                 :index="scope.$index"
                 :name="item.slot"
                 :row="scope.row" />
         </div>
         <!-- 杩涘害鏉� -->
-        <div v-else-if="item.dataType == 'progress'">
+        <div v-else-if="item.dataType == 'progress'"
+             :class="item.className || ''">
           <el-progress :percentage="Number(scope.row[item.prop])" />
         </div>
         <!-- 鍥剧墖 -->
-        <div v-else-if="item.dataType == 'image'">
+        <div v-else-if="item.dataType == 'image'"
+             :class="item.className || ''">
           <img :src="javaApi + '/img/' + scope.row[item.prop]"
                alt=""
                style="width: 40px; height: 40px; margin-top: 10px" />
         </div>
         <!-- tag -->
-        <div v-else-if="item.dataType == 'tag'">
+        <div v-else-if="item.dataType == 'tag'"
+             :class="item.className || ''">
           <el-tag v-if="
               typeof dataTypeFn(scope.row[item.prop], item.formatData) ===
               'string'
@@ -112,6 +117,7 @@
         </div>
         <!-- 鎸夐挳 -->
         <div v-else-if="item.dataType == 'action'"
+             :class="item.className || ''"
              @click.stop>
           <template v-for="(o, key) in item.operation"
                     :key="key">
@@ -172,6 +178,7 @@
         </div>
         <!-- 鍙偣鍑荤殑鏂囧瓧 -->
         <div v-else-if="item.dataType == 'link'"
+             :class="item.className || ''"
              class="cell link"
              style="width: 100%"
              @click="goLink(scope.row, item.linkMethod)">
@@ -180,6 +187,7 @@
         <!-- 榛樿绾睍绀烘暟鎹� -->
         <div v-else
              class="cell"
+             :class="item.className || ''"
              style="width: 100%">
           <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
           <span v-else>{{
diff --git a/src/views/productionPlan/productionPlan/index.vue b/src/views/productionPlan/productionPlan/index.vue
index 7c475ce..db2ab5e 100644
--- a/src/views/productionPlan/productionPlan/index.vue
+++ b/src/views/productionPlan/productionPlan/index.vue
@@ -3,46 +3,69 @@
     <div class="search_form">
       <el-form :model="searchForm"
                :inline="true">
-        <el-form-item label="杞﹂棿:">
-          <el-select v-model="searchForm.workshop"
-                     placeholder="璇烽�夋嫨"
-                     clearable
-                     style="width: 160px;"
-                     @change="handleQuery">
-            <el-option label="杞﹂棿1"
-                       value="1" />
-            <el-option label="杞﹂棿2"
-                       value="2" />
-            <el-option label="杞﹂棿3"
-                       value="3" />
-          </el-select>
+        <el-form-item label="瀹㈡埛鍚嶇О:">
+          <el-input v-model="searchForm.customerName"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    style="width: 160px;"
+                    @keyup.enter="handleQuery" />
         </el-form-item>
-        <el-form-item label="鐘舵��:">
-          <el-select v-model="searchForm.status"
-                     placeholder="璇烽�夋嫨"
-                     clearable
-                     style="width: 160px;"
-                     @change="handleQuery">
-            <el-option label="寰呭鐞�"
-                       value="pending" />
-            <el-option label="杩涜涓�"
-                       value="processing" />
-            <el-option label="宸插畬鎴�"
-                       value="completed" />
-          </el-select>
+        <el-form-item label="浜у搧鍚嶇О:">
+          <el-input v-model="searchForm.productName"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    style="width: 160px;"
+                    @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item label="浜у搧瑙勬牸:">
+          <el-input v-model="searchForm.productSpec"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    style="width: 160px;"
+                    @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item label="鐗╂枡缂栫爜:">
+          <el-input v-model="searchForm.materialCode"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    style="width: 160px;"
+                    @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item label="鐢宠鍗曠紪鍙�:">
+          <el-input v-model="searchForm.applyNo"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    style="width: 160px;"
+                    @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item label="璁″垝鏃ユ湡鑼冨洿:">
+          <el-date-picker v-model="searchForm.dateRange"
+                          type="daterange"
+                          range-separator="鑷�"
+                          start-placeholder="寮�濮嬫棩鏈�"
+                          end-placeholder="缁撴潫鏃ユ湡"
+                          value-format="YYYY-MM-DD"
+                          style="width: 240px;"
+                          @change="handleQuery" />
         </el-form-item>
         <el-form-item>
           <el-button type="primary"
                      @click="handleQuery">鎼滅储</el-button>
+          <el-button type="info"
+                     @click="handleReset">閲嶇疆</el-button>
+          <el-button type="primary"
+                     @click="handleAdd">鏂板</el-button>
+          <el-button type="warning"
+                     @click="getLoadProdData">鎷夊彇鏁版嵁</el-button>
+          <el-button type="warning"
+                     @click="handleMerge">鍚堝苟涓嬪彂</el-button>
+          <el-button type="warning"
+                     @click="handleImport">瀵煎叆</el-button>
+          <el-button type="warning"
+                     @click="handleExport">瀵煎嚭</el-button>
         </el-form-item>
       </el-form>
       <div>
-        <el-button type="primary"
-                   @click="handleMerge">鍜屽苟涓嬪彂</el-button>
-        <el-button type="info"
-                   @click="showCategorySummaryDialog = true">浜у搧绫诲埆姹囨��</el-button>
-        <!-- <el-button type="danger"
-                   @click="handleDelete">鍒犻櫎</el-button> -->
       </div>
     </div>
     <div class="table_list">
@@ -61,23 +84,50 @@
     <!-- 鍚堝苟涓嬪彂寮圭獥 -->
     <el-dialog v-model="isShowNewModal"
                title="鍚堝苟涓嬪彂"
-               width="500px">
+               width="600px">
       <el-form :model="mergeForm"
                label-width="120px">
-        <el-form-item label="搴忓垪鍙�">
-          <el-input v-model="mergeForm.serialNo"
-                    disabled />
+        <el-row :gutter="20">
+          <el-col :span="10">
+            <el-form-item label="鐗╂枡缂栫爜">
+              <div class="info-display">{{ mergeForm.materialCode || '-' }}</div>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="浜у搧鍚嶇О">
+              <el-tag class="info-display">{{ mergeForm.productName || '-' }}</el-tag>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="10">
+            <el-form-item label="浜у搧瑙勬牸">
+              <div class="info-display">{{ mergeForm.productSpec || '-' }}</div>
+            </el-form-item>
+          </el-col>
+          <el-col :span="10">
+            <el-form-item label="闀�*瀹�*楂�">
+              <div class="info-display">{{ mergeForm.length || '-' }}*{{ mergeForm.width || '-' }}*{{ mergeForm.height || '-' }}</div>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="璁″垝瀹屾垚鏃堕棿">
+          <el-date-picker v-model="mergeForm.planCompleteTime"
+                          type="date"
+                          value-format="YYYY-MM-DD"
+                          style="width: 100%" />
         </el-form-item>
-        <el-form-item label="鐢熶骇璁″垝鏁伴噺">
-          <el-input-number v-model="mergeForm.totalquantity"
-                           :min="1"
-                           :step="1"
+        <el-form-item label="鐢熶骇鏂规暟">
+          <el-input-number v-model="mergeForm.totalAssignedQuantity"
+                           :min="0"
+                           :max="sumAssignedQuantity"
+                           @change="onBlur"
                            style="width: 100%" />
         </el-form-item>
-        <el-form-item label="澶囨敞">
+        <!-- <el-form-item label="澶囨敞">
           <el-input v-model="mergeForm.remark"
                     type="textarea" />
-        </el-form-item>
+        </el-form-item> -->
       </el-form>
       <template #footer>
         <span class="dialog-footer">
@@ -87,35 +137,14 @@
         </span>
       </template>
     </el-dialog>
-    <!-- 浜у搧绫诲埆姹囨�诲脊绐� -->
-    <el-dialog v-model="showCategorySummaryDialog"
-               title="浜у搧绫诲埆姹囨�荤粺璁�"
-               width="400px">
-      <el-table :data="categorySummary"
-                border
-                style="width: 100%">
-        <el-table-column prop="materialCategory"
-                         label="浜у搧绫诲埆"
-                         align="center"
-                         width="150" />
-        <el-table-column prop="totalquantity"
-                         label="鎬诲埗閫犳暟閲�"
-                         align="center" />
-      </el-table>
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="showCategorySummaryDialog = false">鍏抽棴</el-button>
-        </span>
-      </template>
-    </el-dialog>
     <!-- 杩借釜杩涘害寮圭獥 -->
     <el-dialog v-model="showTrackProgressDialog"
-               :title="`杩借釜杩涘害 - ${trackProgressForm.serialNo || ''}`"
+               :title="`杩借釜杩涘害 - ${trackProgressForm.materialCode || ''}`"
                width="600px">
       <el-form :model="trackProgressForm"
                label-width="120px">
-        <el-form-item label="搴忓垪鍙�">
-          <el-input v-model="trackProgressForm.serialNo"
+        <el-form-item label="鐗╂枡缂栫爜">
+          <el-input v-model="trackProgressForm.materialCode"
                     disabled />
         </el-form-item>
         <el-form-item label="褰撳墠鐘舵��">
@@ -174,140 +203,295 @@
         </span>
       </template>
     </el-dialog>
+    <!-- 瀵煎叆寮圭獥 -->
+    <ImportDialog ref="importDialogRef"
+                  v-model="importDialogVisible"
+                  title="瀵煎叆鐢熶骇璁″垝"
+                  :action="importAction"
+                  :headers="importHeaders"
+                  :auto-upload="false"
+                  :on-success="handleImportSuccess"
+                  :on-error="handleImportError"
+                  @confirm="handleImportConfirm"
+                  @download-template="handleDownloadTemplate"
+                  @close="handleImportClose" />
+    <!-- 鏂板/缂栬緫寮圭獥 -->
+    <el-dialog v-model="dialogVisible"
+               :title="operationType === 'add' ? '鏂板鐢熶骇璁″垝' : '缂栬緫鐢熶骇璁″垝'"
+               width="600px">
+      <el-form ref="formRef"
+               :model="form"
+               :rules="rules"
+               label-width="120px">
+        <el-form-item label="鐢宠鍗曠紪鍙�"
+                      prop="applyNo">
+          <el-input v-model="form.applyNo"
+                    placeholder="璇疯緭鍏ョ敵璇峰崟缂栧彿" />
+        </el-form-item>
+        <el-form-item label="瀹㈡埛鍚嶇О"
+                      prop="customerName">
+          <el-input v-model="form.customerName"
+                    placeholder="璇疯緭鍏ュ鎴峰悕绉�" />
+        </el-form-item>
+        <el-form-item label="浜у搧鍚嶇О"
+                      prop="productName">
+          <el-input v-model="form.productName"
+                    placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�" />
+        </el-form-item>
+        <el-form-item label="浜у搧瑙勬牸"
+                      prop="productSpec">
+          <el-input v-model="form.productSpec"
+                    placeholder="璇疯緭鍏ヤ骇鍝佽鏍�" />
+        </el-form-item>
+        <el-form-item label="鐗╂枡缂栫爜"
+                      prop="materialCode">
+          <el-input v-model="form.materialCode"
+                    placeholder="璇疯緭鍏ョ墿鏂欑紪鐮�" />
+        </el-form-item>
+        <el-form-item label="鍧楁暟"
+                      prop="quantity">
+          <el-input-number v-model="form.quantity"
+                           :min="0"
+                           placeholder="璇疯緭鍏ュ潡鏁�" />
+        </el-form-item>
+        <el-form-item label="鏂规暟"
+                      prop="volume">
+          <el-input-number v-model="form.volume"
+                           :min="0"
+                           placeholder="璇疯緭鍏ユ柟鏁�" />
+        </el-form-item>
+        <el-form-item label="闀�"
+                      prop="length">
+          <el-input-number v-model="form.length"
+                           :min="0"
+                           placeholder="璇疯緭鍏ラ暱搴�" />
+        </el-form-item>
+        <el-form-item label="瀹�"
+                      prop="width">
+          <el-input-number v-model="form.width"
+                           :min="0"
+                           placeholder="璇疯緭鍏ュ搴�" />
+        </el-form-item>
+        <el-form-item label="楂�"
+                      prop="height">
+          <el-input-number v-model="form.height"
+                           :min="0"
+                           placeholder="璇疯緭鍏ラ珮搴�" />
+        </el-form-item>
+        <el-form-item label="璁″垝寮�濮嬫棩鏈�"
+                      prop="startDate">
+          <el-date-picker v-model="form.startDate"
+                          type="date"
+                          value-format="YYYY-MM-DD"
+                          placeholder="璇烽�夋嫨璁″垝寮�濮嬫棩鏈�" />
+        </el-form-item>
+        <el-form-item label="璁″垝缁撴潫鏃ユ湡"
+                      prop="endDate">
+          <el-date-picker v-model="form.endDate"
+                          type="date"
+                          value-format="YYYY-MM-DD"
+                          placeholder="璇烽�夋嫨璁″垝缁撴潫鏃ユ湡" />
+        </el-form-item>
+        <el-form-item label="寮哄害"
+                      prop="strength">
+          <el-input v-model="form.strength"
+                    placeholder="璇疯緭鍏ュ己搴�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞 1"
+                      prop="remarkOne">
+          <el-input v-model="form.remarkOne"
+                    placeholder="璇疯緭鍏ュ娉� 1" />
+        </el-form-item>
+        <el-form-item label="澶囨敞 2"
+                      prop="remarkTwo">
+          <el-input v-model="form.remarkTwo"
+                    placeholder="璇疯緭鍏ュ娉� 2" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary"
+                     @click="handleSubmit">纭畾</el-button>
+        </span>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-  import { onMounted, ref } from "vue";
+  import { onMounted, ref, reactive, getCurrentInstance } from "vue";
   import { ElMessage } from "element-plus";
   import dayjs from "dayjs";
-  import { productionPlanListPage } from "@/api/productionPlan/productionPlan.js";
+  import ImportDialog from "@/components/Dialog/ImportDialog.vue";
+  import { getToken } from "@/utils/auth";
+  import {
+    productionPlanListPage,
+    loadProdData,
+    exportProductionPlan,
+    productionPlanAdd,
+    productionPlanUpdate,
+    productionPlanDelete,
+    productionPlanCombine,
+  } from "@/api/productionPlan/productionPlan.js";
   import PIMTable from "./components/PIMTable.vue";
+
+  const { proxy } = getCurrentInstance();
 
   const tableColumn = ref([
     {
-      label: "鏉ユ簮",
-      prop: "source",
-      width: "100px",
+      label: "鐢宠鍗曠紪鍙�",
+      prop: "applyNo",
+      width: "150px",
+      className: "code-cell",
     },
     {
-      label: "鐘舵��",
-      prop: "status",
-      width: "80px",
-    },
-    {
-      label: "瀹℃牳鐘舵��",
-      prop: "auditStatus",
-      width: "100px",
-    },
-    {
-      label: "璁㈠崟鍙�",
-      prop: "orderNo",
-      width: "120px",
-    },
-    {
-      label: "搴忓垪鍙�",
-      prop: "serialNo",
-      width: "140px",
-    },
-    {
-      label: "闆朵欢鍙�",
-      prop: "partNo",
-      width: "120px",
-    },
-    {
-      label: "闆朵欢",
-      prop: "partName",
-      width: "120px",
-    },
-    {
-      label: "浜у搧绫诲埆",
-      prop: "materialCategory",
-      width: "100px",
-    },
-    {
-      label: "宸ヨ壓鏂囦欢鍙�",
-      prop: "processFileNo",
-      width: "140px",
-    },
-    {
-      label: "閿�鍞暟閲�",
-      prop: "salesQuantity",
-      width: "100px",
-      align: "right",
-    },
-    {
-      label: "鍒堕�犳暟閲�",
-      prop: "quantity",
-      width: "100px",
-      align: "right",
-    },
-    {
-      label: "闆朵欢鍗曚綅",
-      prop: "partUnit",
-      width: "80px",
-    },
-    {
-      label: "涓昏鍒掗渶姹傛棩鏈�",
-      prop: "mainPlanDemandDate",
-      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
-      width: "140px",
-    },
-    {
-      label: "鎵胯鏃ユ湡",
-      prop: "commitmentDate",
-      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
-      width: "120px",
-    },
-    {
-      label: "鍒堕�犲睘鎬�",
-      prop: "manufactureProperty",
-      width: "100px",
-    },
-    {
-      label: "澶囨敞",
-      prop: "remark",
+      label: "瀹㈡埛鍚嶇О",
+      prop: "customerName",
       width: "150px",
     },
     {
-      label: "鏇存柊鏃堕棿",
-      prop: "updateTime",
-      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
-      width: "120px",
-    },
-    {
-      label: "鏇存柊浜�",
-      prop: "updateBy",
+      label: "浜у搧鍚嶇О",
+      prop: "productName",
       width: "100px",
+      dataType: "tag",
+      formatType: params => {
+        const typeMap = {
+          鏉挎潗: "primary",
+          鐮屽潡: "info",
+        };
+        return typeMap[params] || "info";
+      },
     },
     {
-      label: "鍒涘缓鏃堕棿",
-      prop: "createTime",
-      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
-      width: "120px",
+      label: "浜у搧瑙勬牸",
+      prop: "productSpec",
+      width: "150px",
+      className: "spec-cell",
     },
     {
-      label: "鍒涘缓浜�",
-      prop: "createBy",
-      width: "100px",
+      label: "鐗╂枡缂栫爜",
+      prop: "materialCode",
+      width: "150px",
+      className: "code-cell",
     },
+    {
+      label: "鍧楁暟",
+      prop: "quantity",
+      className: "quantity-cell",
+    },
+    {
+      label: "鏂规暟",
+      prop: "volume",
+      width: "150px",
+      className: "volume-cell",
+    },
+    {
+      label: "宸蹭笅鍙戞柟鏁�",
+      prop: "assignedQuantity",
+      width: "150px",
+      className: "spec-cell",
+    },
+    {
+      label: "闀�",
+      prop: "length",
+      className: "dimension-cell",
+    },
+    {
+      label: "瀹�",
+      prop: "width",
+      className: "dimension-cell",
+    },
+    {
+      label: "楂�",
+      prop: "height",
+      className: "dimension-cell",
+    },
+    {
+      label: "娴佹按鍙�",
+      prop: "serialNo",
+      width: "150px",
+      className: "code-cell",
+    },
+    {
+      label: "璁″垝寮�濮嬫棩鏈�",
+      prop: "startDate",
+      width: "150px",
+      className: "date-cell",
+      formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
+    },
+    {
+      label: "璁″垝缁撴潫鏃ユ湡",
+      prop: "endDate",
+      width: "150px",
+      className: "date-cell",
+      formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
+    },
+    {
+      label: "寮哄害",
+      prop: "strength",
+    },
+    // {
+    //   label: "鏁版嵁鏉ユ簮",
+    //   width: "100px",
+    //   prop: "dataSourceType",
+    //   formatData: cell => (cell == 1 ? "鍚屾" : "鎵嬪姩"),
+    // },
+    {
+      label: "澶囨敞 1",
+      prop: "remarkOne",
+    },
+    {
+      label: "澶囨敞 2",
+      prop: "remarkTwo",
+    },
+
     {
       dataType: "action",
       label: "鎿嶄綔",
       align: "center",
       fixed: "right",
-      width: 200,
+      width: 300,
       operation: [
+        {
+          name: "缂栬緫",
+          type: "primary",
+          link: true,
+          clickFun: row => {
+            handleEdit(row);
+          },
+        },
+        {
+          name: "鍒犻櫎",
+          type: "danger",
+          link: true,
+          clickFun: row => {
+            handleDelete(row);
+          },
+        },
         {
           name: "涓嬪彂",
           type: "text",
+          disabled: row => {
+            // 璁$畻鍓╀綑鏂规暟
+            const remainingVolume =
+              (row.volume || 0) - (row.assignedQuantity || 0);
+            // 濡傛灉鍓╀綑鏂规暟灏忎簬绛変簬0锛岀姝㈤�夋嫨
+            return remainingVolume <= 0;
+          },
           clickFun: row => {
             // 鍗曠嫭涓嬪彂鎿嶄綔
             // 璁剧疆琛ㄥ崟鏁版嵁
-            mergeForm.serialNo = row.serialNo;
-            mergeForm.totalquantity = row.quantity;
-            mergeForm.remark = "";
-
+            mergeForm.materialCode = row.materialCode;
+            mergeForm.productName = row.productName || "";
+            mergeForm.productSpec = row.productSpec || "";
+            mergeForm.length = row.length || 0;
+            mergeForm.width = row.width || 0;
+            mergeForm.height = row.height || 0;
+            mergeForm.totalAssignedQuantity =
+              Number(row.volume) - Number(row.assignedQuantity) || 0;
+            mergeForm.planCompleteTime = row.planCompleteTime || "";
+            sumAssignedQuantity.value = mergeForm.totalAssignedQuantity;
             // 鎵撳紑寮圭獥
             isShowNewModal.value = true;
           },
@@ -340,26 +524,74 @@
   const isShowNewModal = ref(false);
   // 鍚堝苟涓嬪彂琛ㄥ崟鏁版嵁
   const mergeForm = reactive({
-    serialNo: "",
-    totalquantity: 0,
-    remark: "",
+    materialCode: "",
+    productName: "",
+    productSpec: "",
+    length: 0,
+    width: 0,
+    height: 0,
+    totalAssignedQuantity: 0,
+    planCompleteTime: "",
   });
 
   // 杩借釜杩涘害寮圭獥鎺у埗
   const showTrackProgressDialog = ref(false);
   // 杩借釜杩涘害琛ㄥ崟鏁版嵁
   const trackProgressForm = reactive({
-    serialNo: "",
+    materialCode: "",
     currentStatus: "",
     completionRate: 0,
     progressDetails: [],
     remark: "",
   });
 
+  // 瀵煎叆鐩稿叧
+  const importDialogRef = ref(null);
+  const importDialogVisible = ref(false);
+  const importAction =
+    import.meta.env.VITE_APP_BASE_API + "/productionPlan/import";
+  const importHeaders = ref({
+    Authorization: `Bearer ${getToken()}`,
+  });
+
+  // 鏂板/缂栬緫鐩稿叧
+  const dialogVisible = ref(false);
+  const operationType = ref("add"); // add | edit
+  const formRef = ref(null);
+  const form = reactive({
+    id: undefined,
+    applyNo: "",
+    customerName: "",
+    productName: "",
+    productSpec: "",
+    materialCode: "",
+    quantity: 0,
+    volume: 0,
+    length: 0,
+    width: 0,
+    height: 0,
+    startDate: "",
+    endDate: "",
+    strength: "",
+    remarkOne: "",
+    remarkTwo: "",
+  });
+  const rules = reactive({
+    applyNo: [{ required: true, message: "璇疯緭鍏ョ敵璇峰崟缂栧彿", trigger: "blur" }],
+    customerName: [
+      { required: true, message: "璇疯緭鍏ュ鎴峰悕绉�", trigger: "blur" },
+    ],
+    productName: [{ required: true, message: "璇疯緭鍏ヤ骇鍝佸悕绉�", trigger: "blur" }],
+    productSpec: [{ required: true, message: "璇疯緭鍏ヤ骇鍝佽鏍�", trigger: "blur" }],
+    materialCode: [
+      { required: true, message: "璇疯緭鍏ョ墿鏂欑紪鐮�", trigger: "blur" },
+    ],
+  });
+
   // 澶勭悊杩借釜杩涘害鎸夐挳鐐瑰嚮
   const handleTrackProgress = row => {
     // 璁剧疆琛ㄥ崟鏁版嵁
-    trackProgressForm.serialNo = row.serialNo;
+    trackProgressForm.materialCode = row.materialCode;
     trackProgressForm.currentStatus = row.status;
 
     // 鐢熸垚妯℃嫙杩涘害鏁版嵁
@@ -373,6 +605,10 @@
 
     // 鎵撳紑寮圭獥
     showTrackProgressDialog.value = true;
+  };
+  const onBlur = value => {
+    // 闄愬埗鍥涗綅灏忔暟
+    mergeForm.totalAssignedQuantity = Number(value.toFixed(4));
   };
 
   // 鐢熸垚妯℃嫙杩涘害璇︽儏鏁版嵁
@@ -449,10 +685,11 @@
   const data = reactive({
     searchForm: {
       customerName: "",
-      salesContractNo: "",
-      projectName: "",
-      materialCategory: "",
-      specificationModel: "",
+      productName: "",
+      productSpec: "",
+      materialCode: "",
+      applyNo: "",
+      dateRange: [],
     },
   });
   const { searchForm } = toRefs(data);
@@ -460,6 +697,20 @@
   // 鏌ヨ鍒楄〃
   /** 鎼滅储鎸夐挳鎿嶄綔 */
   const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+
+  /** 閲嶇疆鎸夐挳鎿嶄綔 */
+  const handleReset = () => {
+    Object.assign(searchForm.value, {
+      customerName: "",
+      productName: "",
+      productSpec: "",
+      materialCode: "",
+      applyNo: "",
+      dateRange: [],
+    });
     page.current = 1;
     getList();
   };
@@ -474,14 +725,15 @@
 
     // 閬嶅巻琛ㄦ牸鏁版嵁锛屾寜浜у搧绫诲埆姹囨��
     tableData.value.forEach(row => {
-      const category = row.materialCategory;
+      const category = row.materialCode;
       if (!summary[category]) {
         summary[category] = {
-          materialCategory: category,
-          totalquantity: 0,
+          materialCode: category,
+          totalAssignedQuantity: 0,
         };
       }
-      summary[category].totalquantity += row.quantity;
+      summary[category].totalAssignedQuantity +=
+        Number(row.volume) - Number(row.assignedQuantity);
     });
 
     // 杞崲涓烘暟缁勬牸寮�
@@ -490,130 +742,11 @@
 
   const getList = () => {
     tableLoading.value = true;
-    // 鏋勯�犱竴涓柊鐨勫璞★紝涓嶅寘鍚玡ntryDate瀛楁
+    // 鏋勯�犳悳绱㈠弬鏁�
     const params = { ...searchForm.value, ...page };
-    params.entryDate = undefined;
-    // tableData.value = [
-    //   {
-    //     id: 1,
-    //     source: "閿�鍞鍗�",
-    //     status: "寰呭鐞�",
-    //     auditStatus: "宸插鏍�",
-    //     orderNo: "SO20260301001",
-    //     serialNo: "PP20260301001",
-    //     partNo: "P001",
-    //     partName: "闆朵欢A",
-    //     materialCategory: "绫诲埆1",
-    //     processFileNo: "PF20260301001",
-    //     salesQuantity: 100,
-    //     quantity: 105,
-    //     partUnit: "涓�",
-    //     mainPlanDemandDate: "2026-03-15",
-    //     commitmentDate: "2026-03-10",
-    //     manufactureProperty: "甯歌",
-    //     remark: "",
-    //     updateTime: "2026-03-01",
-    //     updateBy: "admin",
-    //     createTime: "2026-03-01",
-    //     createBy: "admin",
-    //   },
-    //   {
-    //     id: 2,
-    //     source: "閿�鍞鍗�",
-    //     status: "寰呭鐞�",
-    //     auditStatus: "宸插鏍�",
-    //     orderNo: "SO20260301002",
-    //     serialNo: "PP20260301001",
-    //     partNo: "P002",
-    //     partName: "闆朵欢B",
-    //     materialCategory: "绫诲埆1",
-    //     processFileNo: "PF20260301002",
-    //     salesQuantity: 200,
-    //     quantity: 210,
-    //     partUnit: "涓�",
-    //     mainPlanDemandDate: "2026-03-15",
-    //     commitmentDate: "2026-03-10",
-    //     manufactureProperty: "甯歌",
-    //     remark: "",
-    //     updateTime: "2026-03-01",
-    //     updateBy: "admin",
-    //     createTime: "2026-03-01",
-    //     createBy: "admin",
-    //   },
-    //   {
-    //     id: 3,
-    //     source: "閿�鍞鍗�",
-    //     status: "杩涜涓�",
-    //     auditStatus: "宸插鏍�",
-    //     orderNo: "SO20260301003",
-    //     serialNo: "PP20260301002",
-    //     partNo: "P003",
-    //     partName: "闆朵欢C",
-    //     materialCategory: "绫诲埆2",
-    //     processFileNo: "PF20260301003",
-    //     salesQuantity: 150,
-    //     quantity: 155,
-    //     partUnit: "涓�",
-    //     mainPlanDemandDate: "2026-03-20",
-    //     commitmentDate: "2026-03-15",
-    //     manufactureProperty: "甯歌",
-    //     remark: "",
-    //     updateTime: "2026-03-01",
-    //     updateBy: "admin",
-    //     createTime: "2026-03-01",
-    //     createBy: "admin",
-    //   },
-    //   {
-    //     id: 4,
-    //     source: "閿�鍞鍗�",
-    //     status: "杩涜涓�",
-    //     auditStatus: "宸插鏍�",
-    //     orderNo: "SO20260301004",
-    //     serialNo: "PP20260301002",
-    //     partNo: "P004",
-    //     partName: "闆朵欢D",
-    //     materialCategory: "绫诲埆2",
-    //     processFileNo: "PF20260301004",
-    //     salesQuantity: 300,
-    //     quantity: 315,
-    //     partUnit: "涓�",
-    //     mainPlanDemandDate: "2026-03-20",
-    //     commitmentDate: "2026-03-15",
-    //     manufactureProperty: "甯歌",
-    //     remark: "",
-    //     updateTime: "2026-03-01",
-    //     updateBy: "admin",
-    //     createTime: "2026-03-01",
-    //     createBy: "admin",
-    //   },
-    //   {
-    //     id: 5,
-    //     source: "閿�鍞鍗�",
-    //     status: "宸插畬鎴�",
-    //     auditStatus: "宸插鏍�",
-    //     orderNo: "SO20260301005",
-    //     serialNo: "PP20260301003",
-    //     partNo: "P005",
-    //     partName: "闆朵欢E",
-    //     materialCategory: "绫诲埆3",
-    //     processFileNo: "PF20260301005",
-    //     salesQuantity: 250,
-    //     quantity: 260,
-    //     partUnit: "涓�",
-    //     mainPlanDemandDate: "2026-03-10",
-    //     commitmentDate: "2026-03-05",
-    //     manufactureProperty: "甯歌",
-    //     remark: "",
-    //     updateTime: "2026-03-01",
-    //     updateBy: "admin",
-    //     createTime: "2026-03-01",
-    //     createBy: "admin",
-    //   },
-    // ];
-    // tableLoading.value = false;
-    // page.total = tableData.value.length;
-    // // 璁$畻浜у搧绫诲埆姹囨�荤粺璁�
-    // calculateCategorySummary();
+    params.startDate = params.dateRange ? params.dateRange[0] : "";
+    params.endDate = params.dateRange ? params.dateRange[1] : "";
+    delete params.dateRange;
     productionPlanListPage(params)
       .then(res => {
         tableLoading.value = false;
@@ -636,7 +769,7 @@
     selectedRows.value = selection;
     // 濡傛灉鏈夐�変腑鐨勮锛岃褰曠涓�涓�変腑琛岀殑搴忓垪鍙�
     if (selection.length > 0) {
-      selectedserialNo.value = selection[0].serialNo;
+      selectedserialNo.value = selection[0].materialCode;
     } else {
       // 濡傛灉娌℃湁閫変腑鐨勮锛屾竻绌哄簭鍒楀彿
       selectedserialNo.value = "";
@@ -645,30 +778,54 @@
 
   // 鍒ゆ柇琛屾槸鍚﹀彲閫夋嫨
   const isSelectable = row => {
+    // 璁$畻鍓╀綑鏂规暟
+    const remainingVolume = (row.volume || 0) - (row.assignedQuantity || 0);
+    // 濡傛灉鍓╀綑鏂规暟灏忎簬绛変簬0锛岀姝㈤�夋嫨
+    if (remainingVolume <= 0) {
+      return false;
+    }
     // 濡傛灉娌℃湁閫変腑鐨勮锛屾墍鏈夎閮藉彲閫夋嫨
     if (!selectedserialNo.value) {
       return true;
     }
     // 濡傛灉鏈夐�変腑鐨勮锛屽彧鏈夊簭鍒楀彿鐩稿悓鐨勮鎵嶅彲閫夋嫨
-    return row.serialNo === selectedserialNo.value;
+    return row.materialCode === selectedserialNo.value;
   };
-
+  // 鎷夊彇鏁版嵁鎸夐挳鎿嶄綔
+  const getLoadProdData = () => {
+    loadProdData()
+      .then(res => {})
+      .catch(() => {});
+  };
+  const sumAssignedQuantity = ref(0);
   // 澶勭悊鍚堝苟涓嬪彂鎸夐挳鐐瑰嚮
   const handleMerge = () => {
     if (selectedRows.value.length === 0) {
       ElMessage.warning("璇烽�夋嫨瑕佸悎骞朵笅鍙戠殑鐢熶骇璁″垝");
       return;
     }
-
+    console.log(selectedRows.value);
     // 璁$畻鎬诲埗閫犳暟閲�
-    const totalQuantity = selectedRows.value.reduce((sum, row) => {
-      return sum + row.quantity;
+    const totalAssignedQuantity = selectedRows.value.reduce((sum, row) => {
+      return (
+        sum +
+        (row.volume == null
+          ? 0
+          : Number(row.volume) - Number(row.assignedQuantity))
+      );
     }, 0);
-
+    sumAssignedQuantity.value = totalAssignedQuantity;
+    console.log(totalAssignedQuantity);
     // 璁剧疆琛ㄥ崟鏁版嵁
-    mergeForm.serialNo = selectedserialNo.value;
-    mergeForm.totalquantity = totalQuantity;
-    mergeForm.remark = "";
+    const firstRow = selectedRows.value[0];
+    mergeForm.materialCode = selectedserialNo.value;
+    mergeForm.productName = firstRow.productName || "";
+    mergeForm.productSpec = firstRow.productSpec || "";
+    mergeForm.length = firstRow.length || 0;
+    mergeForm.width = firstRow.width || 0;
+    mergeForm.height = firstRow.height || 0;
+    mergeForm.totalAssignedQuantity = totalAssignedQuantity;
+    mergeForm.planCompleteTime = firstRow.planCompleteTime || "";
 
     // 鎵撳紑寮圭獥
     isShowNewModal.value = true;
@@ -676,10 +833,219 @@
 
   // 澶勭悊鍚堝苟涓嬪彂鎻愪氦
   const handleMergeSubmit = () => {
-    // 杩欓噷鍙互娣诲姞涓嬪彂閫昏緫
-    ElMessage.success("鍚堝苟涓嬪彂鎴愬姛");
-    isShowNewModal.value = false;
+    console.log(sumAssignedQuantity.value, "sumAssignedQuantity");
+    // 璁$畻褰撳墠閫変腑琛岀殑鎬绘柟鏁�
+    const totalVolume = selectedRows.value.reduce((sum, row) => {
+      return sum + (Number(row.volume) - Number(row.assignedQuantity) || 0);
+    }, 0);
+
+    // 楠岃瘉totalAssignedQuantity涓嶈兘澶т簬鎬绘柟鏁�
+    if (mergeForm.totalAssignedQuantity > sumAssignedQuantity.value) {
+      ElMessage.error("鐢熶骇鏂规暟涓嶈兘澶т簬褰撳墠璁$畻鐨勬�诲��");
+      return;
+    }
+
+    mergeForm.ids = selectedRows.value.map(row => row.id);
+    console.log(mergeForm, "mergeForm");
+    productionPlanCombine(mergeForm)
+      .then(res => {
+        if (res.code === 200) {
+          ElMessage.success("鍚堝苟涓嬪彂鎴愬姛");
+          isShowNewModal.value = false;
+          // 鍙互閫夋嫨鍒锋柊鍒楄〃鎴栧叾浠栨搷浣�
+        } else {
+          ElMessage.error(res.message || "鍚堝苟涓嬪彂澶辫触");
+        }
+      })
+      .catch(err => {
+        console.error("鍚堝苟涓嬪彂寮傚父锛�", err);
+        ElMessage.error("绯荤粺寮傚父锛屽悎骞朵笅鍙戝け璐�");
+      });
     // 鍙互閫夋嫨鍒锋柊鍒楄〃鎴栧叾浠栨搷浣�
+  };
+
+  // 瀵煎叆
+  const handleImport = () => {
+    importDialogVisible.value = true;
+  };
+
+  // 瀵煎嚭
+  const handleExport = () => {
+    const fileName = `鐢熶骇璁″垝.xlsx`;
+    exportProductionPlan()
+      .then(res => {
+        // 杩斿洖鐨勬暟鎹槸鍚︿负绌�
+        if (!res) {
+          proxy.$modal.msgError("瀵煎嚭澶辫触锛岃繑鍥炴暟鎹负绌�");
+          return;
+        }
+
+        const blob = new Blob([res], {
+          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+        });
+        const downloadElement = document.createElement("a");
+        const href = window.URL.createObjectURL(blob);
+
+        downloadElement.style.display = "none";
+        downloadElement.href = href;
+        downloadElement.download = fileName;
+
+        document.body.appendChild(downloadElement);
+        downloadElement.click();
+
+        document.body.removeChild(downloadElement);
+        window.URL.revokeObjectURL(href);
+
+        proxy.$modal.msgSuccess("瀵煎嚭鎴愬姛");
+      })
+      .catch(err => {
+        console.error("瀵煎嚭寮傚父锛�", err);
+        proxy.$modal.msgError("绯荤粺寮傚父锛屽鍑哄け璐�");
+      });
+  };
+
+  // 瀵煎叆鎴愬姛
+  const handleImportSuccess = response => {
+    if (response.code === 200) {
+      ElMessage.success("瀵煎叆鎴愬姛");
+      importDialogVisible.value = false;
+      getList();
+    } else {
+      ElMessage.error(response.message || "瀵煎叆澶辫触");
+    }
+  };
+
+  // 瀵煎叆澶辫触
+  const handleImportError = error => {
+    ElMessage.error("瀵煎叆澶辫触锛岃妫�鏌ユ枃浠舵牸寮忔槸鍚︽纭�");
+  };
+
+  // 纭瀵煎叆
+  const handleImportConfirm = () => {
+    if (importDialogRef.value) {
+      importDialogRef.value.submit();
+    }
+  };
+
+  // 涓嬭浇妯℃澘
+  const handleDownloadTemplate = () => {
+    proxy.download(
+      "/productionPlan/downloadTemplate",
+      {},
+      "鐢熶骇璁″垝瀵煎叆妯℃澘.xlsx"
+    );
+  };
+
+  // 鍏抽棴瀵煎叆寮圭獥
+  const handleImportClose = () => {
+    importDialogVisible.value = false;
+  };
+
+  // 鏂板
+  const handleAdd = () => {
+    operationType.value = "add";
+    Object.assign(form, {
+      applyNo: "",
+      customerName: "",
+      productName: "",
+      productSpec: "",
+      materialCode: "",
+      quantity: 0,
+      volume: 0,
+      length: 0,
+      width: 0,
+      height: 0,
+      startDate: "",
+      endDate: "",
+      strength: "",
+      remarkOne: "",
+      remarkTwo: "",
+    });
+    dialogVisible.value = true;
+  };
+
+  // 缂栬緫
+  const handleEdit = row => {
+    operationType.value = "edit";
+    Object.assign(form, {
+      id: row.id,
+      applyNo: row.applyNo || "",
+      customerName: row.customerName || "",
+      productName: row.productName || "",
+      productSpec: row.productSpec || "",
+      materialCode: row.materialCode || "",
+      quantity: row.quantity || 0,
+      volume: row.volume || 0,
+      length: row.length || 0,
+      width: row.width || 0,
+      height: row.height || 0,
+      startDate: row.startDate || "",
+      endDate: row.endDate || "",
+      strength: row.strength || "",
+      remarkOne: row.remarkOne || "",
+      remarkTwo: row.remarkTwo || "",
+    });
+    dialogVisible.value = true;
+  };
+
+  // 鍒犻櫎
+  const handleDelete = row => {
+    proxy.$modal
+      .confirm("纭鍒犻櫎璇ョ敓浜ц鍒掞紵", "鎻愮ず", {
+        confirmButtonText: "纭",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      })
+      .then(() => {
+        productionPlanDelete([row.id])
+          .then(() => {
+            proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+            getList();
+          })
+          .catch(() => {
+            proxy.$modal.msgError("鍒犻櫎澶辫触");
+          });
+      })
+      .catch(() => {});
+  };
+
+  // 鎻愪氦琛ㄥ崟
+  const handleSubmit = () => {
+    formRef.value.validate(valid => {
+      if (valid) {
+        const payload = { ...form };
+        if (operationType.value === "add") {
+          productionPlanAdd(payload)
+            .then(() => {
+              proxy.$modal.msgSuccess(
+                operationType.value === "add" ? "鏂板鎴愬姛" : "淇敼鎴愬姛"
+              );
+              dialogVisible.value = false;
+              getList();
+            })
+            .catch(() => {
+              proxy.$modal.msgError(
+                operationType.value === "add" ? "鏂板澶辫触" : "淇敼澶辫触"
+              );
+            });
+        }
+        if (operationType.value === "edit") {
+          productionPlanUpdate(payload)
+            .then(() => {
+              proxy.$modal.msgSuccess(
+                operationType.value === "add" ? "鏂板鎴愬姛" : "淇敼鎴愬姛"
+              );
+              dialogVisible.value = false;
+              getList();
+            })
+            .catch(() => {
+              proxy.$modal.msgError(
+                operationType.value === "add" ? "鏂板澶辫触" : "淇敼澶辫触"
+              );
+            });
+        }
+      }
+    });
   };
 
   onMounted(() => {
@@ -723,16 +1089,19 @@
     border: none;
     border-radius: 6px;
     overflow: hidden;
+    box-shadow: 0 4px 16px rgba(102, 126, 234, 0.1);
 
     .el-table__header-wrapper {
-      background-color: #fafafa;
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 
       th {
-        background-color: #fafafa;
+        background: transparent;
         font-weight: 600;
-        color: #303133;
-        border-bottom: 1px solid #ebeef5;
-        padding: 14px 0;
+        color: #ffffff;
+        border-bottom: none;
+        padding: 16px 0;
+        font-size: 14px;
+        letter-spacing: 0.5px;
       }
     }
 
@@ -741,23 +1110,144 @@
         transition: all 0.3s ease;
 
         &:hover {
-          background-color: #f5f7fa;
+          background: linear-gradient(
+            90deg,
+            rgba(102, 126, 234, 0.05) 0%,
+            rgba(118, 75, 162, 0.05) 100%
+          );
+          transform: scale(1.002);
+          box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1);
         }
 
         td {
-          border-bottom: 1px solid #ebeef5;
-          padding: 12px 0;
+          border-bottom: 1px solid #f0f0f0;
+          padding: 14px 0;
+          color: #303133;
+          font-size: 13px;
         }
       }
 
       tr.current-row {
-        background-color: #ecf5ff;
+        background: linear-gradient(
+          90deg,
+          rgba(102, 126, 234, 0.08) 0%,
+          rgba(118, 75, 162, 0.08) 100%
+        );
+      }
+
+      // 鏁板�煎瓧娈垫牱寮�
+      .quantity-cell,
+      .volume-cell,
+      .dimension-cell {
+        font-weight: 600;
+        color: #409eff;
+        font-family: "Courier New", monospace;
+        font-size: 14px;
+        text-shadow: 0 1px 2px rgba(64, 158, 255, 0.2);
+      }
+
+      // 瑙勬牸瀛楁鏍峰紡
+      .spec-cell {
+        color: #67c23a;
+        font-weight: 500;
+
+        padding: 4px 8px;
+        border-radius: 4px;
+      }
+
+      // 缂栫爜瀛楁鏍峰紡
+      .code-cell {
+        color: #e6a23c;
+        font-family: "Courier New", monospace;
+        font-weight: 500;
+        padding: 4px 8px;
+        border-radius: 4px;
+      }
+
+      // 鏃ユ湡瀛楁鏍峰紡
+      .date-cell {
+        color: #909399;
+        font-size: 12px;
+        font-style: italic;
+      }
+
+      // 鐘舵�佹爣绛炬牱寮�
+      .status-tag {
+        &.pending {
+          background: linear-gradient(135deg, #ffeaa7 0%, #fdcb6e 100%);
+          color: #d63031;
+          padding: 4px 12px;
+          border-radius: 12px;
+          font-weight: 500;
+          box-shadow: 0 2px 4px rgba(253, 203, 110, 0.3);
+        }
+
+        &.processing {
+          background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%);
+          color: #ffffff;
+          padding: 4px 12px;
+          border-radius: 12px;
+          font-weight: 500;
+          box-shadow: 0 2px 4px rgba(9, 132, 227, 0.3);
+        }
+
+        &.completed {
+          background: linear-gradient(135deg, #55efc4 0%, #00b894 100%);
+          color: #ffffff;
+          padding: 4px 12px;
+          border-radius: 12px;
+          font-weight: 500;
+          box-shadow: 0 2px 4px rgba(0, 184, 148, 0.3);
+        }
       }
     }
 
     .el-table__empty-block {
-      padding: 40px 0;
+      padding: 60px 0;
+      background-color: #fafafa;
     }
+  }
+
+  // 鎿嶄綔鎸夐挳鏍峰紡
+  :deep(.el-table .cell .el-button--text) {
+    padding: 6px 10px;
+    border-radius: 4px;
+    transition: all 0.3s ease;
+    font-weight: 500;
+
+    &:hover {
+      background-color: rgba(64, 158, 255, 0.1);
+      transform: translateY(-1px);
+      box-shadow: 0 2px 4px rgba(64, 158, 255, 0.2);
+    }
+
+    &:nth-of-type(1) {
+      color: #409eff;
+      background: linear-gradient(
+        135deg,
+        rgba(64, 158, 255, 0.1) 0%,
+        rgba(64, 158, 255, 0.05) 100%
+      );
+    }
+
+    &:nth-of-type(2) {
+      color: #67c23a;
+      background: linear-gradient(
+        135deg,
+        rgba(103, 194, 58, 0.1) 0%,
+        rgba(103, 194, 58, 0.05) 100%
+      );
+    }
+  }
+
+  // 淇℃伅灞曠ず鏍峰紡
+  .info-display {
+    border-radius: 6px;
+    color: #303133;
+    font-size: 14px;
+    min-height: 32px;
+    display: flex;
+    align-items: center;
   }
 
   .pagination-container {
@@ -891,9 +1381,9 @@
     color: #909399;
     margin-left: 4px;
   }
-  .search_form {
-    :deep(.el-form-item) {
-      margin-bottom: 0px !important;
-    }
-  }
+  // .search_form {
+  //   :deep(.el-form-item) {
+  //     margin-bottom: 0px !important;
+  //   }
+  // }
 </style>
diff --git a/src/views/productionPlan/summaryByProduct/index.vue b/src/views/productionPlan/summaryByProduct/index.vue
new file mode 100644
index 0000000..62b8010
--- /dev/null
+++ b/src/views/productionPlan/summaryByProduct/index.vue
@@ -0,0 +1,290 @@
+<template>
+  <div class="app-container">
+    <div class="table_list">
+      <PIMTable rowKey="materialCode"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                height="calc(100vh - 200px)"
+                :tableLoading="tableLoading"
+                :isSelection="false"
+                @pagination="pagination">
+      </PIMTable>
+    </div>
+  </div>
+</template>
+
+<script setup>
+  import { onMounted, ref, reactive } from "vue";
+  import { summaryByProductType } from "@/api/productionPlan/productionPlan.js";
+  import PIMTable from "../productionPlan/components/PIMTable.vue";
+
+  const tableColumn = ref([
+    {
+      label: "鐗╂枡缂栫爜",
+      prop: "materialCode",
+      className: "code-cell",
+    },
+    {
+      label: "浜у搧鍚嶇О",
+      prop: "productName",
+      dataType: "tag",
+      formatType: params => {
+        const typeMap = {
+          鏉挎潗: "primary",
+          鐮屽潡: "info",
+        };
+        return typeMap[params] || "info";
+      },
+    },
+    {
+      label: "浜у搧瑙勬牸",
+      prop: "productSpec",
+      className: "spec-cell",
+    },
+    {
+      label: "闀�",
+      prop: "length",
+      className: "dimension-cell",
+    },
+    {
+      label: "瀹�",
+      prop: "width",
+      className: "dimension-cell",
+    },
+    {
+      label: "楂�",
+      prop: "height",
+      className: "dimension-cell",
+    },
+    {
+      label: "鍧楁暟",
+      prop: "quantity",
+      className: "quantity-cell",
+    },
+    {
+      label: "鏂规暟",
+      prop: "volume",
+      className: "volume-cell",
+    },
+  ]);
+  const tableData = ref([]);
+  const tableLoading = ref(false);
+  const page = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
+
+  // 鎼滅储琛ㄥ崟
+  const searchForm = reactive({
+    productName: "",
+    productSpec: "",
+  });
+
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
+
+  const getList = () => {
+    tableLoading.value = true;
+    // 鏋勯�犱竴涓柊鐨勫璞★紝涓嶅寘鍚玡ntryDate瀛楁
+    const params = { ...searchForm, ...page };
+    params.entryDate = undefined;
+    summaryByProductType(params)
+      .then(res => {
+        tableLoading.value = false;
+
+        tableData.value = res.data;
+        page.total = res.total;
+      })
+      .catch(() => {
+        tableLoading.value = false;
+      });
+  };
+
+  onMounted(() => {
+    getList();
+  });
+</script>
+
+<style scoped lang="scss">
+  .app-container {
+    padding: 24px;
+    background-color: #f0f2f5;
+    min-height: calc(100vh - 48px);
+  }
+
+  .search_form {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 24px;
+    padding: 20px;
+    background-color: #ffffff;
+    border-radius: 6px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+    transition: all 0.3s ease;
+
+    &:hover {
+      box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
+    }
+  }
+
+  .table_list {
+    background-color: #ffffff;
+    border-radius: 6px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+    overflow: hidden;
+    height: calc(100vh - 150px);
+  }
+
+  :deep(.el-table) {
+    border: none;
+    border-radius: 6px;
+    overflow: hidden;
+    box-shadow: 0 4px 16px rgba(102, 126, 234, 0.1);
+
+    .el-table__header-wrapper {
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+
+      th {
+        background: transparent;
+        font-weight: 600;
+        color: #ffffff;
+        border-bottom: none;
+        padding: 16px 0;
+        font-size: 14px;
+        letter-spacing: 0.5px;
+      }
+    }
+
+    .el-table__body-wrapper {
+      tr {
+        transition: all 0.3s ease;
+
+        &:hover {
+          background: linear-gradient(
+            90deg,
+            rgba(102, 126, 234, 0.05) 0%,
+            rgba(118, 75, 162, 0.05) 100%
+          );
+          transform: scale(1.002);
+          box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1);
+        }
+
+        td {
+          border-bottom: 1px solid #f0f0f0;
+          padding: 14px 0;
+          color: #303133;
+          font-size: 13px;
+        }
+      }
+
+      tr.current-row {
+        background: linear-gradient(
+          90deg,
+          rgba(102, 126, 234, 0.08) 0%,
+          rgba(118, 75, 162, 0.08) 100%
+        );
+      }
+
+      // 鏁板�煎瓧娈垫牱寮�
+      .quantity-cell,
+      .volume-cell,
+      .dimension-cell {
+        font-weight: 600;
+        color: #409eff;
+        font-family: "Courier New", monospace;
+        font-size: 14px;
+        text-shadow: 0 1px 2px rgba(64, 158, 255, 0.2);
+      }
+
+      // 瑙勬牸瀛楁鏍峰紡
+      .spec-cell {
+        color: #67c23a;
+        font-weight: 500;
+        padding: 4px 8px;
+        border-radius: 4px;
+      }
+
+      // 缂栫爜瀛楁鏍峰紡
+      .code-cell {
+        color: #e6a23c;
+        font-family: "Courier New", monospace;
+        font-weight: 500;
+        padding: 4px 8px;
+        border-radius: 4px;
+      }
+
+      // 鏃ユ湡瀛楁鏍峰紡
+      .date-cell {
+        color: #909399;
+        font-size: 12px;
+        font-style: italic;
+      }
+    }
+
+    .el-table__empty-block {
+      padding: 60px 0;
+      background-color: #fafafa;
+    }
+  }
+
+  .pagination-container {
+    display: flex;
+    justify-content: flex-end;
+    padding: 16px 20px;
+    background-color: #ffffff;
+    border-top: 1px solid #ebeef5;
+    border-radius: 0 0 12px 12px;
+  }
+
+  :deep(.el-button) {
+    transition: all 0.3s ease;
+
+    &:hover {
+      transform: translateY(-1px);
+    }
+  }
+
+  @media (max-width: 768px) {
+    .app-container {
+      padding: 16px;
+    }
+
+    .search_form {
+      flex-direction: column;
+      align-items: flex-start;
+      gap: 12px;
+
+      .el-form {
+        width: 100%;
+
+        .el-form-item {
+          width: 100%;
+        }
+      }
+
+      .el-button {
+        margin-right: 12px;
+      }
+    }
+
+    :deep(.el-table) {
+      th,
+      td {
+        padding: 10px 0;
+        font-size: 12px;
+      }
+    }
+  }
+</style>

--
Gitblit v1.9.3