From 868e08bf0513b2e6ad0360bc2000119a7e376548 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期五, 24 四月 2026 15:25:53 +0800
Subject: [PATCH] 产品管理: 增加导出产品数据功能,支持按大类选择导出

---
 src/views/basicData/product/index.vue |  158 ++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 140 insertions(+), 18 deletions(-)

diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index d71d7cb..d8ced28 100644
--- a/src/views/basicData/product/index.vue
+++ b/src/views/basicData/product/index.vue
@@ -17,6 +17,9 @@
           style="margin-left: 10px"
           >鏂板浜у搧澶х被</el-button
         >
+        <el-button type="success" style="margin-left: 10px" @click="openExportDia">
+          瀵煎嚭
+        </el-button>
       </div>
       <div ref="containerRef">
         <el-tree
@@ -25,18 +28,13 @@
           :data="list"
           @node-click="handleNodeClick"
           :expand-on-click-node="false"
-          default-expand-all
           :default-expanded-keys="expandedKeys"
-          :draggable="true"
           :filter-node-method="filterNode"
           :props="{ children: 'children', label: 'label' }"
           highlight-current
           node-key="id"
-          style="
-            height: calc(100vh - 190px);
-            overflow-y: scroll;
-            scrollbar-width: none;
-          "
+          class="product-tree-scroll"
+          style="height: calc(100vh - 190px); overflow-y: auto"
         >
           <template #default="{ node, data }">
             <div class="custom-tree-node">
@@ -45,7 +43,7 @@
                   <component :is="data.children && data.children.length > 0
                   ? node.expanded ? 'FolderOpened' : 'Folder' : 'Tickets'" />
                 </el-icon>
-                {{ data.label }}
+                <span class="tree-node-label">{{ data.label }}</span>
               </span>
               <div>
                 <el-button
@@ -55,7 +53,7 @@
                 >
                   缂栬緫
                 </el-button>
-                <el-button type="primary" link @click="openProDia('add', data)">
+                <el-button type="primary" link @click="openProDia('add', data)" :disabled="node.level >= 3">
                   娣诲姞浜у搧
                 </el-button>
                 <el-button
@@ -78,7 +76,7 @@
         <el-button type="primary" @click="openModelDia('add')">
           鏂板瑙勬牸鍨嬪彿
         </el-button>
-        <ImportExcel @uploadSuccess="getModelList" />
+        <ImportExcel :product-id="currentId" @uploadSuccess="getModelList" />
         <el-button
           type="danger"
           @click="handleDelete"
@@ -97,10 +95,9 @@
         @selection-change="handleSelectionChange"
         :tableLoading="tableLoading"
         @pagination="pagination"
-        :total="total"
       ></PIMTable>
     </div>
-    <el-dialog v-model="productDia" title="浜у搧" width="400px">
+    <el-dialog v-model="productDia" title="浜у搧" width="400px" @keydown.enter.prevent>
       <el-form
         :model="form"
         label-width="140px"
@@ -114,7 +111,10 @@
               <el-input
                 v-model="form.productName"
                 placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
+                maxlength="20"
+                show-word-limit
                 clearable
+                @keydown.enter.prevent
               />
             </el-form-item>
           </el-col>
@@ -132,6 +132,7 @@
       title="瑙勬牸鍨嬪彿"
       width="400px"
       @close="closeModelDia"
+      @keydown.enter.prevent
     >
       <el-form
         :model="modelForm"
@@ -147,6 +148,7 @@
                 v-model="modelForm.model"
                 placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
                 clearable
+                @keydown.enter.prevent
               />
             </el-form-item>
           </el-col>
@@ -158,6 +160,7 @@
                 v-model="modelForm.unit"
                 placeholder="璇疯緭鍏ュ崟浣�"
                 clearable
+                @keydown.enter.prevent
               />
             </el-form-item>
           </el-col>
@@ -167,6 +170,42 @@
         <div class="dialog-footer">
           <el-button type="primary" @click="submitModelForm">纭</el-button>
           <el-button @click="closeModelDia">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <el-dialog
+      v-model="exportDia"
+      title="瀵煎嚭浜у搧鏁版嵁"
+      width="420px"
+      @keydown.enter.prevent
+    >
+      <el-form label-position="top">
+        <el-form-item label="閫夋嫨浜у搧澶х被锛堝彲涓嶉�夛級">
+          <el-select
+            v-model="exportProductId"
+            clearable
+            filterable
+            placeholder="涓嶉�夊垯瀵煎嚭鍏ㄩ儴浜у搧"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="item in rootProductOptions"
+              :key="item.id"
+              :label="item.label"
+              :value="item.id"
+            />
+          </el-select>
+          <div class="export-tip">
+            宸查�夛細瀵煎嚭璇ュぇ绫诲強鍏跺瓙绫伙紱鏈�夛細瀵煎嚭鍏ㄩ儴
+          </div>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" :loading="exportLoading" :disabled="exportLoading" @click="handleExport">
+            纭瀵煎嚭
+          </el-button>
+          <el-button @click="closeExportDia">鍙栨秷</el-button>
         </div>
       </template>
     </el-dialog>
@@ -181,6 +220,7 @@
   addOrEditProductModel,
   delProduct,
   delProductModel,
+  exportProductData,
   modelListPage,
   productTreeList,
 } from "@/api/basicData/product.js";
@@ -192,14 +232,18 @@
 
 const productDia = ref(false);
 const modelDia = ref(false);
+const exportDia = ref(false);
+const exportLoading = ref(false);
 const modelOperationType = ref("");
 const search = ref("");
 const currentId = ref("");
 const currentParentId = ref("");
+const exportProductId = ref("");
 const operationType = ref("");
 const treeLoad = ref(false);
 const list = ref([]);
 const expandedKeys = ref([]);
+const rootProductOptions = ref([]);
 const tableColumn = ref([
   {
     label: "瑙勬牸鍨嬪彿",
@@ -227,18 +271,21 @@
 const tableData = ref([]);
 const tableLoading = ref(false);
 const isShowButton = ref(false);
-const total = ref(0);
 const selectedRows = ref([]);
 const page = reactive({
   current: 1,
   size: 10,
+  total: 0,
 });
 const data = reactive({
   form: {
     productName: "",
   },
   rules: {
-    productName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    productName: [
+      { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+      { max: 20, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃20涓瓧绗�", trigger: "blur" },
+    ],
   },
   modelForm: {
     model: "",
@@ -256,6 +303,7 @@
   productTreeList()
     .then((res) => {
       list.value = res;
+      rootProductOptions.value = Array.isArray(res) ? res : [];
       list.value.forEach((a) => {
         expandedKeys.value.push(a.label);
       });
@@ -316,6 +364,7 @@
   proxy.$refs.formRef.resetFields();
   productDia.value = false;
 };
+
 // 鍒犻櫎浜у搧
 const remove = (node, data) => {
   let ids = [];
@@ -388,7 +437,7 @@
   }).then((res) => {
     console.log("res", res);
     tableData.value = res.records;
-    total.value = res.total;
+    page.total = res.total;
     tableLoading.value = false;
   });
 };
@@ -419,6 +468,42 @@
     })
     .catch(() => {
       proxy.$modal.msg("宸插彇娑�");
+    });
+};
+const openExportDia = () => {
+  exportDia.value = true;
+};
+const closeExportDia = () => {
+  exportDia.value = false;
+};
+// 瀵煎嚭浜у搧锛堝彲鎸夊ぇ绫伙紝鍚瓙绫伙級
+const handleExport = () => {
+  if (exportLoading.value) return;
+  exportLoading.value = true;
+  const params = exportProductId.value ? { productId: exportProductId.value } : {};
+  exportProductData(params)
+    .then((blobData) => {
+      const blob =
+        blobData instanceof Blob
+          ? blobData
+          : new Blob([blobData], {
+              type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+            });
+      const url = window.URL.createObjectURL(blob);
+      const link = document.createElement("a");
+      link.href = url;
+      link.download = "浜у搧鏁版嵁.xlsx";
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+      window.URL.revokeObjectURL(url);
+      closeExportDia();
+    })
+    .catch(() => {
+      proxy.$modal.msgError("瀵煎嚭澶辫触锛岃绋嶅悗閲嶈瘯");
+    })
+    .finally(() => {
+      exportLoading.value = false;
     });
 };
 // 璋冪敤tree杩囨护鏂规硶 涓枃鑻辫繃婊�
@@ -465,18 +550,21 @@
   display: flex;
 }
 .left {
-  width: 380px;
+  width: 450px;
+  min-width: 450px;
   padding: 16px;
   background: #ffffff;
 }
 .right {
-  width: calc(100% - 380px);
+  flex: 1;
+  min-width: 0;
   padding: 16px;
   margin-left: 20px;
   background: #ffffff;
 }
 .custom-tree-node {
   flex: 1;
+  min-width: 0;
   display: flex;
   align-items: center;
   justify-content: space-between;
@@ -484,13 +572,47 @@
   padding-right: 8px;
 }
 .tree-node-content {
+  flex: 1;
+  min-width: 0;
   display: flex;
-  align-items: center; /* 鍨傜洿灞呬腑 */
+  align-items: center;
   height: 100%;
+  overflow: hidden;
+}
+.tree-node-content .orange-icon {
+  flex-shrink: 0;
+}
+.tree-node-label {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
 }
 .orange-icon {
   color: orange;
   font-size: 18px;
   margin-right: 8px; /* 鍥炬爣涓庢枃瀛椾箣闂村姞鐐归棿璺� */
 }
+.product-tree-scroll {
+  scrollbar-width: thin;
+  scrollbar-color: #c0c4cc #f5f7fa;
+}
+.product-tree-scroll::-webkit-scrollbar {
+  width: 8px;
+}
+.product-tree-scroll::-webkit-scrollbar-track {
+  background: #f5f7fa;
+  border-radius: 4px;
+}
+.product-tree-scroll::-webkit-scrollbar-thumb {
+  background: #c0c4cc;
+  border-radius: 4px;
+}
+.product-tree-scroll::-webkit-scrollbar-thumb:hover {
+  background: #909399;
+}
+.export-tip {
+  margin-top: 8px;
+  font-size: 12px;
+  color: #909399;
+}
 </style>

--
Gitblit v1.9.3