From 1464889b1eaab139e7bf85b0d4a46ce7364a0e6e Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 20 四月 2026 10:49:11 +0800
Subject: [PATCH] Merge branch 'dev_New' into dev_西宁_湟水峡

---
 src/views/productionManagement/processRoute/index.vue                              |    1 
 src/views/basicData/product/ProductSelectDialog.vue                                |   27 
 src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue   |  152 +-
 src/api/productionManagement/workOrder.js                                          |   11 
 src/views/basicData/product/index.vue                                              |   17 
 src/views/productionManagement/processRoute/processRouteItem/index.vue             |   15 
 src/api/basicData/customerFile.js                                                  |  104 +
 src/store/modules/permission.js                                                    |   13 
 src/views/basicData/customerFileOpenSea/index.vue                                  | 1794 +++++++++++++++++++++++++++++++++++++++
 src/views/salesManagement/salesLedger/index.vue                                    |   96 +
 src/views/salesManagement/salesQuotation/index.vue                                 |   32 
 src/views/productionManagement/workOrderManagement/index.vue                       |   12 
 src/views/productionManagement/productStructure/Detail/index.vue                   |   72 
 src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue |  105 --
 src/views/procurementManagement/procurementLedger/index.vue                        |    6 
 src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue |  109 +
 src/views/basicData/customerFile/index.vue                                         |  102 +-
 src/views/salesManagement/returnOrder/components/formDia.vue                       |   19 
 src/views/productionManagement/productionOrder/index.vue                           |   23 
 src/api/basicData/productModel.js                                                  |    8 
 src/views/procurementManagement/purchaseReturnOrder/New.vue                        |    1 
 21 files changed, 2,327 insertions(+), 392 deletions(-)

diff --git a/src/api/basicData/customerFile.js b/src/api/basicData/customerFile.js
index 6a5f049..7b6d7c6 100644
--- a/src/api/basicData/customerFile.js
+++ b/src/api/basicData/customerFile.js
@@ -1,7 +1,5 @@
-// 瀹㈡埛妗f椤甸潰鎺ュ彛
 import request from '@/utils/request'
 
-// 鍒嗛〉鏌ヨ
 export function listCustomer(query) {
     return request({
         url: '/basic/customer/list',
@@ -9,14 +7,76 @@
         params: query
     })
 }
-// 鏌ヨ瀹㈡埛妗f璇︾粏
+
+// 瀹㈡埛妗f绉佹捣鏌ヨ
+export function listCustomerPrivatePool(query) {
+    return request({
+        url: '/customerPrivatePool/listPage',
+        method: 'get',
+        params: query
+    })
+}
+
+export function addCustomerPrivatePool(data) {
+    return request({
+        url: '/customerPrivatePool/add',
+        method: 'post',
+        data: data
+    })
+}
+
+export function addCustomerPrivate(data) {
+    return request({
+        url: '/customerPrivate/add',
+        method: 'post',
+        data: data
+    })
+}
+
+export function delCustomerPrivate(ids) {
+    return request({
+        url: '/customerPrivate/delete',
+        method: 'delete',
+        data: ids
+    })
+}
+
+export function delCustomerPrivatePool(id) {
+    return request({
+        url: '/customerPrivatePool/delete/' + id,
+        method: 'delete',
+    })
+}
+
+export function shareCustomer(data) {
+    return request({
+        url: '/customerPrivatePool/together',
+        method: 'post',
+        data: data
+    })
+}
+
 export function getCustomer(id) {
     return request({
         url: '/basic/customer/' + id,
         method: 'get'
     })
 }
-// 鏂板瀹㈡埛妗f
+
+export function getCustomerPrivatePoolById(id) {
+    return request({
+        url: '/customerPrivatePool/getbyId/' + id,
+        method: 'get'
+    })
+}
+
+export function getCustomerPrivatePoolInfo(id) {
+    return request({
+        url: '/customerPrivatePool/info/' + id,
+        method: 'get'
+    })
+}
+
 export function addCustomer(data) {
     return request({
         url: '/basic/customer/addCustomer',
@@ -24,7 +84,7 @@
         data: data
     })
 }
-// 淇敼瀹㈡埛妗f
+
 export function updateCustomer(data) {
     return request({
         url: '/basic/customer/updateCustomer',
@@ -32,7 +92,15 @@
         data: data
     })
 }
-// 瀵煎嚭瀹㈡埛妗f
+
+export function updateCustomerPrivatePool(data) {
+    return request({
+        url: '/customerPrivatePool/update',
+        method: 'put',
+        data: data
+    })
+}
+
 export function exportCustomer(query) {
     return request({
         url: '/basic/customer/export',
@@ -41,7 +109,7 @@
         responseType: 'blob'
     })
 }
-// 鍒犻櫎瀹㈡埛妗f
+
 export function delCustomer(ids) {
     return request({
         url: '/basic/customer/delCustomer',
@@ -50,8 +118,6 @@
     })
 }
 
-
-// 鏂板瀹㈡埛璺熻繘
 export function addCustomerFollow(data) {
     return request({
         url: '/basic/customer-follow/add',
@@ -60,23 +126,21 @@
     })
 }
 
-// 淇敼瀹㈡埛璺熻繘
 export function updateCustomerFollow(data) {
-  return request({
-    url: '/basic/customer-follow/edit',
-    method: 'put',
-    data: data,
-  })
+    return request({
+        url: '/basic/customer-follow/edit',
+        method: 'put',
+        data: data,
+    })
 }
-// 鍒犻櫎瀹㈡埛璺熻繘
+
 export function delCustomerFollow(id) {
     return request({
-        url: '/basic/customer-follow/'+id,
+        url: '/basic/customer-follow/' + id,
         method: 'delete',
     })
 }
 
-// 鍥炶鎻愰啋-鏂板/鏇存柊
 export function addReturnVisit(data) {
     return request({
         url: '/basic/customer-follow/return-visit',
@@ -84,10 +148,10 @@
         data: data
     })
 }
-// 鑾峰彇鍥炶鎻愰啋璇︽儏
+
 export function getReturnVisit(id) {
     return request({
         url: '/basic/customer-follow/return-visit/' + id,
         method: 'get'
     })
-}
\ No newline at end of file
+}
diff --git a/src/api/basicData/productModel.js b/src/api/basicData/productModel.js
index f048f9e..8acc2ef 100644
--- a/src/api/basicData/productModel.js
+++ b/src/api/basicData/productModel.js
@@ -6,4 +6,12 @@
         method: 'get',
         params: query
     })
+}
+
+export function productModelListByUrl(url, query) {
+    return request({
+        url,
+        method: 'get',
+        params: query
+    })
 }
\ No newline at end of file
diff --git a/src/api/productionManagement/workOrder.js b/src/api/productionManagement/workOrder.js
index d2a8095..cdc97cd 100644
--- a/src/api/productionManagement/workOrder.js
+++ b/src/api/productionManagement/workOrder.js
@@ -37,7 +37,7 @@
 // 宸ュ崟-褰撳墠宸ュ簭鐗╂枡鍙拌处
 export function listWorkOrderMaterialLedger(query) {
   return request({
-    url: "/productWorkOrder/material/list",
+    url: "/productOrderMaterial/reportMaterials",
     method: "get",
     params: query,
   });
@@ -69,3 +69,12 @@
     params: query,
   });
 }
+
+// 宸ュ崟-棰嗙敤锛堟彁浜ゅ疄闄呴鐢ㄦ暟閲忥級
+export function pickWorkOrderMaterial(data) {
+  return request({
+    url: "/productWorkOrder/material/pick",
+    method: "post",
+    data,
+  });
+}
diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js
index 6a16d9d..911a0f4 100644
--- a/src/store/modules/permission.js
+++ b/src/store/modules/permission.js
@@ -1,5 +1,5 @@
 import auth from '@/plugins/auth'
-import router, { constantRoutes, dynamicRoutes } from '@/router'
+import router, { constantRoutes, dynamicRoutes, financialRoutes } from '@/router'
 import { getRouters } from '@/api/menu'
 import Layout from '@/layout/index'
 import ParentView from '@/components/ParentView'
@@ -45,9 +45,14 @@
             const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
             asyncRoutes.forEach(route => { router.addRoute(route) })
             this.setRoutes(rewriteRoutes)
-            this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
-            this.setDefaultRoutes(sidebarRoutes)
-            this.setTopbarRoutes(defaultRoutes)
+            // 灏嗚储鍔$鐞嗚矾鐢卞悎骞跺埌渚ц竟鏍�
+            this.setSidebarRouters(constantRoutes.concat(sidebarRoutes).concat(financialRoutes))
+            this.setDefaultRoutes(sidebarRoutes.concat(financialRoutes))
+            this.setTopbarRoutes(defaultRoutes.concat(financialRoutes))
+            // 娣诲姞璐㈠姟绠$悊妯″潡璺敱
+            financialRoutes.forEach(route => {
+              router.addRoute(route)
+            })
             resolve(rewriteRoutes)
           })
         })
diff --git a/src/views/basicData/customerFile/index.vue b/src/views/basicData/customerFile/index.vue
index 79b1616..d0f829c 100644
--- a/src/views/basicData/customerFile/index.vue
+++ b/src/views/basicData/customerFile/index.vue
@@ -612,11 +612,13 @@
   import { onMounted, ref, reactive, getCurrentInstance, toRefs } from "vue";
   import { Search, Paperclip, Upload } from "@element-plus/icons-vue";
   import {
-    addCustomer,
-    delCustomer,
+    addCustomerPrivate,
+    delCustomerPrivate,
     getCustomer,
-    listCustomer,
-    updateCustomer,
+    getCustomerPrivatePoolById,
+    getCustomerPrivatePoolInfo,
+    listCustomerPrivatePool,
+    updateCustomerPrivatePool,
     addCustomerFollow,
     updateCustomerFollow,
     delCustomerFollow,
@@ -654,7 +656,7 @@
   const negotiationFormRef = ref();
   const negotiationForm = reactive({
     customerName: "",
-    customerId: "",
+		customerPrivatePoolId: "",
     followUpMethod: "",
     followUpLevel: "",
     followUpTime: "",
@@ -726,7 +728,7 @@
     },
     {
       label: "鍦板潃鍙婅仈绯绘柟寮�",
-      prop: "addressPhone",
+      prop: "companyAddress",
       width: 250,
     },
     {
@@ -766,6 +768,24 @@
     {
       label: "缁存姢浜�",
       prop: "maintainer",
+    },
+    {
+      label: "瀹㈡埛鏉ユ簮",
+      prop: "type",
+      dataType: "tag",
+      width: 100,
+      formatData: value => {
+        if (value === 1 || value === "1") {
+          return "鍏捣";
+        }
+        return "绉佹捣";
+      },
+      formatType: value => {
+        if (value === 1 || value === "1") {
+          return "warning";
+        }
+        return "success";
+      },
     },
     {
       label: "缁存姢鏃堕棿",
@@ -881,7 +901,7 @@
     // 璁剧疆涓婁紶鐨勮姹傚ご閮�
     headers: { Authorization: "Bearer " + getToken() },
     // 涓婁紶鐨勫湴鍧�
-    url: import.meta.env.VITE_APP_BASE_API + "/basic/customer/importData",
+    url: import.meta.env.VITE_APP_BASE_API + "/customerPrivate/importData",
     // 鏂囦欢涓婁紶鍓嶇殑鍥炶皟
     beforeUpload: file => {
       console.log("鏂囦欢鍗冲皢涓婁紶", file);
@@ -952,10 +972,10 @@
   };
   const getList = () => {
     tableLoading.value = true;
-    listCustomer({ ...searchForm.value, ...page }).then(res => {
+    listCustomerPrivatePool({ ...searchForm.value, ...page }).then(res => {
       tableLoading.value = false;
-      tableData.value = res.records;
-      page.total = res.total;
+      tableData.value = res.data.records;
+      page.total = res.data.total;
     });
   };
   // 琛ㄦ牸閫夋嫨鏁版嵁
@@ -974,7 +994,7 @@
   }
   /** 涓嬭浇妯℃澘 */
   function importTemplate() {
-    proxy.download("/basic/customer/downloadTemplate", {}, "瀹㈡埛瀵煎叆妯℃澘.xlsx");
+    proxy.download("/customerPrivate/downloadTemplate", {}, "瀹㈡埛瀵煎叆妯℃澘.xlsx");
   }
   // 鎵撳紑寮规
   const openForm = (type, row) => {
@@ -992,7 +1012,7 @@
       userList.value = res.data;
     });
     if (type === "edit") {
-      getCustomer(row.id).then(res => {
+      getCustomerPrivatePoolById(row.id).then(res => {
         form.value = { ...res.data };
         formYYs.value.contactList = res.data.contactPerson
           .split(",")
@@ -1029,7 +1049,7 @@
     form.value.contactPhone = formYYs.value.contactList
       .map(item => item.contactPhone)
       .join(",");
-    addCustomer(form.value).then(res => {
+    addCustomerPrivate(form.value).then(res => {
       proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
       closeDia();
       getList();
@@ -1043,7 +1063,7 @@
     form.value.contactPhone = formYYs.value.contactList
       .map(item => item.contactPhone)
       .join(",");
-    updateCustomer(form.value).then(res => {
+    updateCustomerPrivatePool(form.value).then(res => {
       proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
       closeDia();
       getList();
@@ -1062,7 +1082,7 @@
       type: "warning",
     })
       .then(() => {
-        proxy.download("/basic/customer/export", {}, "瀹㈡埛妗f.xlsx");
+        proxy.download("/customerPrivate/export", {}, "瀹㈡埛妗f.xlsx");
       })
       .catch(() => {
         proxy.$modal.msg("宸插彇娑�");
@@ -1072,12 +1092,11 @@
   const handleDelete = () => {
     let ids = [];
     if (selectedRows.value.length > 0) {
-      // 妫�鏌ユ槸鍚︽湁浠栦汉缁存姢鐨勬暟鎹�
       const unauthorizedData = selectedRows.value.filter(
-        item => item.maintainer !== userStore.nickName
+        item => item.type === 1 || item.type === "1"
       );
       if (unauthorizedData.length > 0) {
-        proxy.$modal.msgWarning("涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�");
+        proxy.$modal.msgWarning("鍏捣鍒嗛厤鐨勫鎴蜂笉鑳藉垹闄�");
         return;
       }
       ids = selectedRows.value.map(item => item.id);
@@ -1092,8 +1111,8 @@
     })
       .then(() => {
         tableLoading.value = true;
-        delCustomer(ids)
-          .then(res => {
+        delCustomerPrivate(ids)
+          .then(() => {
             proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
             getList();
           })
@@ -1146,7 +1165,8 @@
         if (reminderForm.id) {
           submitvalue.value = {
             id: reminderForm.id,
-            customerId: currentCustomerId.value,
+						customerPrivatePoolId: reminderForm.id,
+						customerPrivatePoolId: currentCustomerId.value,
             isEnabled: reminderForm.reminderSwitch ? 1 : 0,
             content: reminderForm.reminderContent,
             reminderTime: reminderForm.reminderTime,
@@ -1154,15 +1174,13 @@
           };
         } else {
           submitvalue.value = {
-            customerId: currentCustomerId.value,
+						customerPrivatePoolId: currentCustomerId.value,
             isEnabled: reminderForm.reminderSwitch ? 1 : 0,
             content: reminderForm.reminderContent,
             reminderTime: reminderForm.reminderTime,
             remindUserId: userStore.id,
           };
         }
-
-        console.log("鎻愪氦鍥炶鎻愰啋鏁版嵁:", submitvalue.value);
 
         // 璋冪敤鎺ュ彛
         addReturnVisit(submitvalue.value)
@@ -1185,20 +1203,12 @@
   // 鎵撳紑娲借皥杩涘害寮圭獥
   const openNegotiationDialog = row => {
     negotiationForm.customerName = row.customerName;
-    negotiationForm.customerId = row.id;
+    negotiationForm.customerPrivatePoolId = row.id;
     negotiationForm.followUpMethod = "";
     negotiationForm.followUpLevel = "";
     negotiationForm.followUpTime = "";
     negotiationForm.followerUserName = userStore.nickName; // 榛樿褰撳墠鐧诲綍浜�
     negotiationForm.content = "";
-    // {
-    // 	"customerId": 152,
-    // 	"followUpMethod": "鐢佃瘽娌熼��",
-    // 	"followUpLevel": "娌℃湁鎰忓悜",
-    // 	"followUpTime": "2026-03-04T15:30:00",
-    // 	"followerUserName": "绠$悊鍛樿处鍙�",
-    // 	"content": "111"
-    // }
     negotiationDialogVisible.value = true;
   };
 
@@ -1220,26 +1230,9 @@
 
         if (isEdit) {
           // 淇敼鎿嶄綔
-          console.log("淇敼娲借皥杩涘害鏁版嵁:", negotiationForm);
-          // 杩欓噷鍙互璋冪敤鏇存柊鎺ュ彛
-          // 瀹為檯椤圭洰涓渶瑕佹牴鎹悗绔帴鍙h繘琛岃皟鏁�
-          // 绀轰緥锛歶pdateCustomerFollow(negotiationForm).then(res => {
-          //   // 鏇存柊鏈湴鏁版嵁
-          //   const index = negotiationForm.editIndex;
-          //   negotiationRecords.value[index] = {
-          //     followUpTime: negotiationForm.followUpTime,
-          //     followUpMethod: negotiationForm.followUpMethod,
-          //     followUpLevel: negotiationForm.followUpLevel,
-          //     followerUserName: negotiationForm.followerUserName,
-          //     content: negotiationForm.content,
-          //     id: negotiationForm.id,
-          //   };
-          //   proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          //   closeNegotiationDialog();
-          // });
           updateCustomerFollow(negotiationForm).then(res => {
             // 鏇存柊鏈湴鏁版嵁
-            getCustomer(negotiationForm.customerId).then(res => {
+            getCustomer(negotiationForm.customerPrivatePoolId).then(res => {
               // 鏇存柊鏈湴鏁版嵁
               negotiationRecords.value = res.data.followUpList || [];
             });
@@ -1271,8 +1264,7 @@
 
   // 鎵撳紑璇︽儏寮圭獥
   const openDetailDialog = row => {
-    // 璋冪敤getCustomer鎺ュ彛鑾峰彇瀹㈡埛璇︽儏
-    getCustomer(row.id).then(res => {
+    getCustomerPrivatePoolInfo(row.id).then(res => {
       // 濉厖瀹㈡埛鍩烘湰淇℃伅
       Object.assign(detailForm, res.data);
 
@@ -1293,7 +1285,7 @@
     // 灏嗗綋鍓嶈褰曟暟鎹~鍏呭埌琛ㄥ崟
     Object.assign(negotiationForm, {
       customerName: row.customerName,
-      customerId: row.customerId,
+			customerPrivatePoolId: row.customerPrivatePoolId,
       followUpMethod: row.followUpMethod,
       followUpLevel: row.followUpLevel,
       followUpTime: row.followUpTime,
@@ -1321,7 +1313,7 @@
         // });
         delCustomerFollow(row.id).then(() => {
           // 鍒犻櫎鎴愬姛鍚庢洿鏂版湰鍦版暟鎹�
-          getCustomer(row.customerId).then(res => {
+          getCustomer(row.customerPrivatePoolId).then(res => {
             // 鏇存柊鏈湴鏁版嵁
             negotiationRecords.value = res.data.followUpList || [];
           });
diff --git a/src/views/basicData/customerFileOpenSea/index.vue b/src/views/basicData/customerFileOpenSea/index.vue
new file mode 100644
index 0000000..71cc051
--- /dev/null
+++ b/src/views/basicData/customerFileOpenSea/index.vue
@@ -0,0 +1,1794 @@
+<template>
+  <div class="app-container">
+    <div class="search_form">
+      <div>
+        <span class="search_title">瀹㈡埛鍚嶇О锛�</span>
+        <el-input v-model="searchForm.customerName"
+                  style="width: 240px;margin-right: 10px"
+                  placeholder="璇疯緭鍏�"
+                  @change="handleQuery"
+                  clearable
+                  :prefix-icon="Search" />
+        <span class="search_title">瀹㈡埛鍒嗙被锛�</span>
+        <el-select v-model="searchForm.customerType"
+                   placeholder="璇烽�夋嫨"
+                   style="width: 240px"
+                   clearable
+                   @change="handleQuery">
+          <el-option label="闆跺敭瀹㈡埛"
+                     value="闆跺敭瀹㈡埛" />
+          <el-option label="杩涢攢鍟嗗鎴�"
+                     value="杩涢攢鍟嗗鎴�" />
+        </el-select>
+        <el-button type="primary"
+                   @click="handleQuery"
+                   style="margin-left: 10px">鎼滅储</el-button>
+      </div>
+      <div>
+        <el-button type="primary"
+                   @click="openForm('add')">鏂板瀹㈡埛</el-button>
+        <el-button @click="handleOut">瀵煎嚭</el-button>
+        <el-button type="info"
+                   plain
+                   icon="Upload"
+                   @click="handleImport">瀵煎叆</el-button>
+        <el-button type="danger"
+                   plain
+                   @click="handleDelete">鍒犻櫎</el-button>
+      </div>
+    </div>
+    <div class="table_list">
+      <PIMTable rowKey="id"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                :isSelection="true"
+                @selection-change="handleSelectionChange"
+                :tableLoading="tableLoading"
+                @pagination="pagination"></PIMTable>
+    </div>
+    <el-dialog v-model="dialogFormVisible"
+               :title="operationType === 'add' ? '鏂板瀹㈡埛淇℃伅' : '缂栬緫瀹㈡埛淇℃伅'"
+               width="70%"
+               @close="closeDia">
+      <el-form :model="form"
+               label-width="140px"
+               label-position="top"
+               :rules="rules"
+               ref="formRef">
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="瀹㈡埛鍚嶇О锛�"
+                          prop="customerName">
+              <el-input v-model="form.customerName"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="绾崇◣浜鸿瘑鍒彿锛�"
+                          prop="taxpayerIdentificationNumber">
+              <el-input v-model="form.taxpayerIdentificationNumber"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鍏徃鍦板潃锛�"
+                          prop="companyAddress">
+              <el-input v-model="form.companyAddress"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍏徃鐢佃瘽锛�"
+                          prop="companyPhone">
+              <el-input v-model="form.companyPhone"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="閾惰鍩烘湰鎴凤細"
+                          prop="basicBankAccount">
+              <el-input v-model="form.basicBankAccount"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="閾惰璐﹀彿锛�"
+                          prop="bankAccount">
+              <el-input v-model="form.bankAccount"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="寮�鎴疯鍙凤細"
+                          prop="bankCode">
+              <el-input v-model="form.bankCode"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="瀹㈡埛鍒嗙被锛�"
+                          prop="customerType">
+              <el-select v-model="form.customerType"
+                         placeholder="璇烽�夋嫨"
+                         clearable>
+                <el-option label="闆跺敭瀹㈡埛"
+                           value="闆跺敭瀹㈡埛" />
+                <el-option label="杩涢攢鍟嗗鎴�"
+                           value="杩涢攢鍟嗗鎴�" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30"
+                v-for="(contact, index) in formYYs.contactList"
+                :key="index">
+          <el-col :span="12">
+            <el-form-item label="鑱旂郴浜猴細"
+                          prop="contactPerson">
+              <el-input v-model="contact.contactPerson"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鑱旂郴鐢佃瘽锛�"
+                          prop="contactPhone">
+              <div style="display: flex; align-items: center;width: 100%;">
+                <el-input v-model="contact.contactPhone"
+                          placeholder="璇疯緭鍏�"
+                          clearable />
+                <el-button @click="removeContact(index)"
+                           type="danger"
+                           circle
+                           style="margin-left: 5px;">
+                  <el-icon>
+                    <Close />
+                  </el-icon>
+                </el-button>
+              </div>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-button @click="addNewContact"
+                   style="margin-bottom: 10px;">+ 鏂板鑱旂郴浜�</el-button>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="缁存姢浜猴細"
+                          prop="maintainer">
+              <el-select v-model="form.maintainer"
+                         placeholder="璇烽�夋嫨"
+                         clearable
+                         disabled>
+                <el-option v-for="item in userList"
+                           :key="item.nickName"
+                           :label="item.nickName"
+                           :value="item.nickName" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="缁存姢鏃堕棿锛�"
+                          prop="maintenanceTime">
+              <el-date-picker style="width: 100%"
+                              v-model="form.maintenanceTime"
+                              value-format="YYYY-MM-DD"
+                              format="YYYY-MM-DD"
+                              type="date"
+                              placeholder="璇烽�夋嫨"
+                              clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="submitForm">纭</el-button>
+          <el-button @click="closeDia">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <el-dialog v-model="assignDialogVisible"
+               title="鍒嗛厤瀹㈡埛"
+               width="500px"
+               @close="closeAssignDialog">
+      <el-form :model="assignForm"
+               :rules="assignRules"
+               ref="assignFormRef"
+               label-width="100px">
+        <el-form-item label="瀹㈡埛鍚嶇О">
+          <el-input v-model="assignForm.customerName"
+                    disabled />
+        </el-form-item>
+        <el-form-item label="鍒嗛厤浜哄憳"
+                      prop="boundId">
+          <el-select v-model="assignForm.boundId"
+                     placeholder="璇烽�夋嫨鍒嗛厤浜哄憳"
+                     style="width: 100%"
+                     filterable>
+            <el-option v-for="item in userList"
+                       :key="item.userId || item.nickName"
+                       :label="item.nickName"
+                       :value="item.userId" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="submitAssignForm">纭</el-button>
+          <el-button @click="closeAssignDialog">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <el-dialog v-model="shareDialogVisible"
+               title="鍏变韩瀹㈡埛"
+               width="500px"
+               @close="closeShareDialog">
+      <el-form :model="shareForm"
+               :rules="shareRules"
+               ref="shareFormRef"
+               label-width="100px">
+        <el-form-item label="瀹㈡埛鍚嶇О">
+          <el-input v-model="shareForm.customerName"
+                    disabled />
+        </el-form-item>
+        <el-form-item label="鍏变韩浜哄憳"
+                      prop="boundIds">
+          <el-select v-model="shareForm.boundIds"
+                     placeholder="璇烽�夋嫨鍏变韩浜哄憳"
+                     style="width: 100%"
+                     filterable
+                     multiple
+                     collapse-tags
+                     collapse-tags-tooltip>
+            <el-option v-for="item in userList"
+                       :key="item.userId || item.nickName"
+                       :label="item.nickName"
+                       :value="item.userId" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="submitShareForm">纭</el-button>
+          <el-button @click="closeShareDialog">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 鐢ㄦ埛瀵煎叆瀵硅瘽妗� -->
+    <el-dialog :title="upload.title"
+               v-model="upload.open"
+               width="400px"
+               append-to-body>
+      <el-upload ref="uploadRef"
+                 :limit="1"
+                 accept=".xlsx, .xls"
+                 :headers="upload.headers"
+                 :action="upload.url + '?updateSupport=' + upload.updateSupport"
+                 :disabled="upload.isUploading"
+                 :before-upload="upload.beforeUpload"
+                 :on-progress="upload.onProgress"
+                 :on-success="upload.onSuccess"
+                 :on-error="upload.onError"
+                 :on-change="upload.onChange"
+                 :auto-upload="false"
+                 drag>
+        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+        <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+        <template #tip>
+          <div class="el-upload__tip text-center">
+            <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
+            <el-link type="primary"
+                     :underline="false"
+                     style="font-size: 12px; vertical-align: baseline"
+                     @click="importTemplate">涓嬭浇妯℃澘</el-link>
+          </div>
+        </template>
+      </el-upload>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="submitFileForm">纭� 瀹�</el-button>
+          <el-button @click="upload.open = false">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 鍥炶鎻愰啋瀵硅瘽妗� -->
+    <el-dialog title="鍥炶鎻愰啋"
+               v-model="reminderDialogVisible"
+               width="500px"
+               @close="closeReminderDialog">
+      <el-form :model="reminderForm"
+               label-width="100px"
+               :rules="reminderRules"
+               ref="reminderFormRef">
+        <el-form-item label="瀹㈡埛鍚嶇О锛�">
+          <el-input v-model="reminderForm.customerName"
+                    disabled />
+        </el-form-item>
+        <el-form-item label="鎻愰啋寮�鍏筹細">
+          <el-switch v-model="reminderForm.reminderSwitch" />
+        </el-form-item>
+        <el-form-item label="鎻愰啋鍐呭锛�"
+                      prop="reminderContent">
+          <el-input v-model="reminderForm.reminderContent"
+                    type="textarea"
+                    :maxlength="100"
+                    show-word-limit
+                    placeholder="璇疯緭鍏ユ彁閱掑唴瀹�" />
+        </el-form-item>
+        <el-form-item label="鎻愰啋鏃堕棿锛�"
+                      prop="reminderTime">
+          <el-date-picker v-model="reminderForm.reminderTime"
+                          type="datetime"
+                          value-format="YYYY-MM-DD HH:mm:ss"
+                          format="YYYY-MM-DD HH:mm:ss"
+                          placeholder="璇烽�夋嫨鎻愰啋鏃堕棿"
+                          style="width: 100%" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitReminderForm">纭</el-button>
+          <el-button @click="closeReminderDialog">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 娣诲姞/淇敼娲借皥杩涘害瀵硅瘽妗� -->
+    <el-dialog :title="negotiationForm.editIndex !== undefined ? '淇敼杩涘害' : '娣诲姞杩涘害'"
+               v-model="negotiationDialogVisible"
+               width="600px"
+               @close="closeNegotiationDialog">
+      <el-form :model="negotiationForm"
+               label-width="100px"
+               :rules="negotiationRules"
+               ref="negotiationFormRef">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="璺熻繘鏂瑰紡锛�"
+                          prop="followUpMethod">
+              <el-select v-model="negotiationForm.followUpMethod"
+                         placeholder="璇烽�夋嫨"
+                         style="width: 100%">
+                <el-option label="鐢佃瘽"
+                           value="鐢佃瘽" />
+                <el-option label="閭欢"
+                           value="閭欢" />
+                <el-option label="涓婇棬"
+                           value="涓婇棬" />
+                <el-option label="寰俊"
+                           value="寰俊" />
+                <el-option label="鍏朵粬"
+                           value="鍏朵粬" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璺熻繘绋嬪害锛�"
+                          prop="followUpLevel">
+              <el-select v-model="negotiationForm.followUpLevel"
+                         placeholder="璇烽�夋嫨"
+                         style="width: 100%">
+                <el-option label="娼滃湪瀹㈡埛"
+                           value="娼滃湪瀹㈡埛" />
+                <el-option label="鍒濇鎷滆"
+                           value="鍒濇鎷滆" />
+                <el-option label="澶氭鎷滆"
+                           value="澶氭鎷滆" />
+                <el-option label="鎰忓悜瀹㈡埛"
+                           value="鎰忓悜瀹㈡埛" />
+                <el-option label="宸茬绾﹀鎴�"
+                           value="宸茬绾﹀鎴�" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="璺熻繘鏃堕棿锛�"
+                          prop="followUpTime">
+              <el-date-picker v-model="negotiationForm.followUpTime"
+                              type="datetime"
+                              value-format="YYYY-MM-DD HH:mm:ss"
+                              format="YYYY-MM-DD HH:mm:ss"
+                              placeholder="璇烽�夋嫨"
+                              style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璺熻繘浜猴細">
+              <el-input v-model="negotiationForm.followerUserName"
+                        disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="鍐呭锛�"
+                      prop="content">
+          <el-input v-model="negotiationForm.content"
+                    type="textarea"
+                    :rows="4"
+                    placeholder="璇疯緭鍏�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitNegotiationForm">纭</el-button>
+          <el-button @click="closeNegotiationDialog">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 瀹㈡埛璇︽儏瀵硅瘽妗� -->
+    <el-dialog title="瀹㈡埛璇︽儏"
+               v-model="detailDialogVisible"
+               width="1000px"
+               @close="closeDetailDialog">
+      <!-- 瀹㈡埛鍩烘湰淇℃伅 -->
+      <div class="detail-section">
+        <h3 class="section-title">瀹㈡埛鍩烘湰淇℃伅</h3>
+        <div class="info-display">
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">瀹㈡埛鍚嶇О锛�</span>
+                <span class="info-value">{{ detailForm.customerName }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">瀹㈡埛鍒嗙被锛�</span>
+                <span class="info-value">{{ detailForm.customerType }}</span>
+              </div>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">绾崇◣浜鸿瘑鍒彿锛�</span>
+                <span class="info-value">{{ detailForm.taxpayerIdentificationNumber }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">鍏徃鐢佃瘽锛�</span>
+                <span class="info-value">{{ detailForm.companyPhone }}</span>
+              </div>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">鍏徃鍦板潃锛�</span>
+                <span class="info-value">{{ detailForm.companyAddress }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">閾惰鍩烘湰鎴凤細</span>
+                <span class="info-value">{{ detailForm.basicBankAccount }}</span>
+              </div>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">閾惰璐﹀彿锛�</span>
+                <span class="info-value">{{ detailForm.bankAccount }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">寮�鎴疯鍙凤細</span>
+                <span class="info-value">{{ detailForm.bankCode }}</span>
+              </div>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">鑱旂郴浜猴細</span>
+                <span class="info-value">{{ detailForm.contactPerson }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">鑱旂郴鐢佃瘽锛�</span>
+                <span class="info-value">{{ detailForm.contactPhone }}</span>
+              </div>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">缁存姢浜猴細</span>
+                <span class="info-value">{{ detailForm.maintainer }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">缁存姢鏃堕棿锛�</span>
+                <span class="info-value">{{ detailForm.maintenanceTime }}</span>
+              </div>
+            </el-col>
+          </el-row>
+        </div>
+      </div>
+      <!-- 娲借皥杩涘害璁板綍 -->
+      <div class="detail-section">
+        <div class="section-header">
+          <h3 class="section-title">娲借皥杩涘害璁板綍</h3>
+          <el-button type="primary"
+                     size="small"
+                     @click="openNegotiationDialog(detailForm)">
+            娣诲姞杩涘害
+          </el-button>
+        </div>
+        <el-table :data="negotiationRecords"
+                  border
+                  style="width: 100%">
+          <el-table-column prop="followUpTime"
+                           label="璺熻繘鏃堕棿"
+                           width="160" />
+          <el-table-column prop="followUpMethod"
+                           label="璺熻繘鏂瑰紡"
+                           width="100" />
+          <el-table-column prop="followUpLevel"
+                           label="璺熻繘绋嬪害" />
+          <el-table-column prop="followerUserName"
+                           label="璺熻繘浜�"
+                           width="100" />
+          <el-table-column prop="content"
+                           label="鍐呭"
+                           show-overflow-tooltip />
+          <el-table-column label="闄勪欢"
+                           width="100"
+                           align="center">
+            <template #default="{ row }">
+              <el-button type="info"
+                         link
+                         @click="openAttachmentDialog(row)">
+                <el-icon>
+                  <Paperclip />
+                </el-icon>
+                闄勪欢
+                <!-- {{ row.fileList && row.fileList.length > 0 ? row.fileList.length : '涓婁紶' }} -->
+              </el-button>
+            </template>
+          </el-table-column>
+          <el-table-column label="鎿嶄綔"
+                           width="150"
+                           align="center">
+            <template #default="{ row, $index }">
+              <el-button type="primary"
+                         link
+                         @click="editNegotiationRecord(row, $index)">
+                淇敼
+              </el-button>
+              <el-button type="danger"
+                         link
+                         @click="deleteNegotiationRecord(row, $index)">
+                鍒犻櫎
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div v-if="negotiationRecords.length === 0"
+             class="no-records">
+          鏆傛棤娲借皥杩涘害璁板綍
+        </div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeDetailDialog">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 闄勪欢涓婁紶寮圭獥 -->
+    <el-dialog title="闄勪欢绠$悊"
+               v-model="attachmentDialogVisible"
+               width="600px"
+               @close="closeAttachmentDialog">
+      <div class="attachment-section">
+        <div class="upload-area">
+          <el-upload ref="attachmentUploadRef"
+                     :action="getAttachmentUploadUrl()"
+                     :headers="attachmentUploadHeaders"
+                     :file-list="currentAttachmentList"
+                     :on-success="handleAttachmentSuccess"
+                     :on-error="handleAttachmentError"
+                     :on-remove="handleAttachmentRemove"
+                     :before-upload="beforeAttachmentUpload"
+                     multiple
+                     :limit="10"
+                     name="files">
+            <el-button type="primary">
+              <el-icon>
+                <Upload />
+              </el-icon>
+              涓婁紶闄勪欢
+            </el-button>
+            <template #tip>
+              <div class="el-upload__tip">
+                鏀寔涓婁紶鍥剧墖銆佹枃妗g瓑鏂囦欢锛屽崟涓枃浠朵笉瓒呰繃50MB
+              </div>
+            </template>
+          </el-upload>
+        </div>
+        <div v-if="currentAttachmentList.length > 0"
+             class="attachment-list">
+          <h4>宸蹭笂浼犻檮浠讹細</h4>
+          <el-table :data="currentAttachmentList"
+                    border
+                    size="small">
+            <el-table-column prop="name"
+                             label="鏂囦欢鍚�"
+                             show-overflow-tooltip />
+            <el-table-column prop="size"
+                             label="澶у皬"
+                             width="100">
+              <template #default="{ row }">
+                {{ formatFileSize(row.size) }}
+              </template>
+            </el-table-column>
+            <el-table-column label="鎿嶄綔"
+                             width="120"
+                             align="center">
+              <template #default="{ row, $index }">
+                <el-button type="primary"
+                           link
+                           @click="downloadAttachment(row)">
+                  涓嬭浇
+                </el-button>
+                <el-button type="danger"
+                           link
+                           @click="deleteAttachment(row, $index)">
+                  鍒犻櫎
+                </el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        <div v-else
+             class="no-attachment">
+          鏆傛棤闄勪欢
+        </div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeAttachmentDialog">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+  import { onMounted, ref, reactive, getCurrentInstance, toRefs } from "vue";
+  import { Search, Paperclip, Upload } from "@element-plus/icons-vue";
+  import {
+    addCustomer,
+    addCustomerPrivatePool,
+    delCustomerPrivatePool,
+    delCustomer,
+    getCustomer,
+    shareCustomer,
+    listCustomer,
+    updateCustomer,
+    addCustomerFollow,
+    updateCustomerFollow,
+    delCustomerFollow,
+    addReturnVisit,
+    getReturnVisit,
+  } from "@/api/basicData/customerFile.js";
+  import { ElMessageBox } from "element-plus";
+  import { userListNoPage } from "@/api/system/user.js";
+  import useUserStore from "@/store/modules/user";
+  import { getToken } from "@/utils/auth.js";
+  const { proxy } = getCurrentInstance();
+  const userStore = useUserStore();
+  const assignDialogVisible = ref(false);
+  const assignFormRef = ref();
+  const assignForm = reactive({
+    id: undefined,
+    customerName: "",
+    boundId: undefined,
+  });
+  const assignRules = {
+    boundId: [{ required: true, message: "璇烽�夋嫨鍒嗛厤浜哄憳", trigger: "change" }],
+  };
+  const shareDialogVisible = ref(false);
+  const shareFormRef = ref();
+  const shareForm = reactive({
+    id: undefined,
+    customerName: "",
+    boundIds: [],
+  });
+  const shareRules = {
+    boundIds: [{ required: true, message: "璇烽�夋嫨鍏变韩浜哄憳", trigger: "change" }],
+  };
+
+  // 鍥炶鎻愰啋鐩稿叧
+  const reminderDialogVisible = ref(false);
+  const reminderFormRef = ref();
+  const currentCustomerId = ref();
+  const reminderForm = reactive({
+    customerName: "",
+    reminderSwitch: false,
+    reminderContent: "",
+    reminderTime: "",
+  });
+  const reminderRules = {
+    reminderContent: [
+      { required: true, message: "璇疯緭鍏ユ彁閱掑唴瀹�", trigger: "blur" },
+    ],
+    reminderTime: [
+      { required: true, message: "璇烽�夋嫨鎻愰啋鏃堕棿", trigger: "change" },
+    ],
+  };
+
+  // 娲借皥杩涘害鐩稿叧
+  const negotiationDialogVisible = ref(false);
+  const negotiationFormRef = ref();
+  const negotiationForm = reactive({
+    customerName: "",
+    customerId: "",
+    followUpMethod: "",
+    followUpLevel: "",
+    followUpTime: "",
+    followerUserName: "",
+    content: "",
+  });
+  const negotiationRules = {
+    followUpMethod: [
+      { required: true, message: "璇烽�夋嫨璺熻繘鏂瑰紡", trigger: "change" },
+    ],
+    followUpLevel: [
+      { required: true, message: "璇烽�夋嫨璺熻繘绋嬪害", trigger: "change" },
+    ],
+    followUpTime: [
+      { required: true, message: "璇烽�夋嫨璺熻繘鏃堕棿", trigger: "change" },
+    ],
+    content: [{ required: true, message: "璇疯緭鍏ュ唴瀹�", trigger: "blur" }],
+  };
+
+  // 璇︽儏鐩稿叧
+  const detailDialogVisible = ref(false);
+  const detailForm = reactive({
+    customerName: "",
+    customerType: "",
+    taxpayerIdentificationNumber: "",
+    companyPhone: "",
+    companyAddress: "",
+    basicBankAccount: "",
+    bankAccount: "",
+    bankCode: "",
+    contactPerson: "",
+    contactPhone: "",
+    maintainer: "",
+    maintenanceTime: "",
+  });
+  const negotiationRecords = ref([]);
+
+  // 闄勪欢鐩稿叧
+  const attachmentDialogVisible = ref(false);
+  const attachmentUploadRef = ref();
+  const currentAttachmentList = ref([]);
+  const currentFollowRecord = ref({});
+  const attachmentUploadHeaders = { Authorization: "Bearer " + getToken() };
+
+  // 鍔ㄦ�佹瀯寤轰笂浼燯RL
+  const getAttachmentUploadUrl = () => {
+    const baseUrl =
+      import.meta.env.VITE_APP_BASE_API + "/basic/customer-follow/upload";
+    return currentFollowRecord.value.id
+      ? `${baseUrl}/${currentFollowRecord.value.id}`
+      : baseUrl;
+  };
+
+  const tableColumn = ref([
+    {
+      label: "瀹㈡埛鍒嗙被",
+      prop: "customerType",
+      width: 120,
+    },
+    {
+      label: "瀹㈡埛鍚嶇О",
+      prop: "customerName",
+      width: 220,
+    },
+    {
+      label: "绾崇◣浜鸿瘑鍒爜",
+      prop: "taxpayerIdentificationNumber",
+      width: 220,
+    },
+    {
+      label: "鍦板潃鍙婅仈绯绘柟寮�",
+      prop: "addressPhone",
+      width: 250,
+    },
+    {
+      label: "鑱旂郴浜�",
+      prop: "contactPerson",
+    },
+    {
+      label: "鑱旂郴鐢佃瘽",
+      prop: "contactPhone",
+      width: 150,
+    },
+    // {
+    //   label: "璺熻繘杩涘害",
+    //   prop: "followUpLevel",
+    //   width: 120,
+    // },
+    // {
+    //   label: "璺熻繘鏃堕棿",
+    //   prop: "followUpTime",
+    //   width: 120,
+    // },
+    {
+      label: "閾惰鍩烘湰鎴�",
+      prop: "basicBankAccount",
+      width: 220,
+    },
+    {
+      label: "閾惰璐﹀彿",
+      prop: "bankAccount",
+      width: 220,
+    },
+    {
+      label: "寮�鎴疯鍙�",
+      prop: "bankCode",
+      width: 220,
+    },
+    {
+      label: "缁存姢浜�",
+      prop: "maintainer",
+    },
+		{
+			label: "缁存姢鏃堕棿",
+			prop: "maintenanceTime",
+			width: 100,
+		},
+    {
+      label: "棰嗙敤浜�",
+      prop: "usageUserName",
+      width: 120,
+			fixed: "right",
+    },
+    {
+      label: "棰嗙敤鐘舵��",
+      prop: "usageStatus",
+      dataType: "tag",
+      width: 100,
+			fixed: "right",
+      formatData: value => {
+        if (value === 1 || value === "1") {
+          return "宸查鐢�";
+        }
+        return "鏈鐢�";
+      },
+      formatType: value => {
+        if (value === 1 || value === "1") {
+          return "success";
+        }
+        return "info";
+      },
+    },
+		{
+			label: "鍏变韩浜�",
+			prop: "togetherUserNames",
+			width: 120,
+			fixed: "right",
+		},
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+      width: 200,
+      operation: [
+        {
+          name: "鍒嗛厤",
+          type: "text",
+          showHide: row => row.usageStatus != 1,
+          clickFun: row => {
+            openAssignDialog(row);
+          },
+        },
+        {
+          name: "鍥炴敹",
+          type: "text",
+          showHide: row => row.usageStatus == 1,
+          clickFun: row => {
+            recycleCustomer(row);
+          },
+        },
+				{
+					name: "鍏变韩",
+					type: "text",
+					showHide: row => row.usageStatus == 1,
+					clickFun: row => {
+						openShareDialog(row);
+					},
+				},
+				{
+					name: "缂栬緫",
+					type: "text",
+					clickFun: row => {
+						openForm("edit", row);
+					},
+				},
+        // {
+        //   name: "璇︽儏",
+        //   type: "text",
+        //   clickFun: row => {
+        //     openDetailDialog(row);
+        //   },
+        // },
+      ],
+    },
+  ]);
+  const tableData = ref([]);
+  const selectedRows = ref([]);
+  const userList = ref([]);
+  const tableLoading = ref(false);
+  const page = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
+  const total = ref(0);
+
+  // 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
+  const operationType = ref("");
+  const dialogFormVisible = ref(false);
+  const formYYs = ref({
+    // 鍏朵粬瀛楁...
+    contactList: [
+      {
+        contactPerson: "",
+        contactPhone: "",
+      },
+    ],
+  });
+  const data = reactive({
+    searchForm: {
+      customerName: "",
+      customerType: "",
+    },
+    form: {
+      customerName: "",
+      taxpayerIdentificationNumber: "",
+      companyAddress: "",
+      companyPhone: "",
+      contactPerson: "",
+      contactPhone: "",
+      maintainer: "",
+      maintenanceTime: "",
+      basicBankAccount: "",
+      bankAccount: "",
+      bankCode: "",
+      customerType: "",
+    },
+    rules: {
+      customerName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      taxpayerIdentificationNumber: [
+        { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+      ],
+      companyAddress: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      companyPhone: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      // contactPerson: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      // contactPhone: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      maintainer: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
+      maintenanceTime: [
+        { required: false, message: "璇烽�夋嫨", trigger: "change" },
+      ],
+      basicBankAccount: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      bankAccount: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      bankCode: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      customerType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    },
+  });
+  const upload = reactive({
+    // 鏄惁鏄剧ず寮瑰嚭灞傦紙瀹㈡埛瀵煎叆锛�
+    open: false,
+    // 寮瑰嚭灞傛爣棰橈紙瀹㈡埛瀵煎叆锛�
+    title: "",
+    // 鏄惁绂佺敤涓婁紶
+    isUploading: false,
+    // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+    headers: { Authorization: "Bearer " + getToken() },
+    // 涓婁紶鐨勫湴鍧�
+    url: import.meta.env.VITE_APP_BASE_API + "/basic/customer/importData",
+    // 鏂囦欢涓婁紶鍓嶇殑鍥炶皟
+    beforeUpload: file => {
+      console.log("鏂囦欢鍗冲皢涓婁紶", file);
+      // 鍙互鍦ㄦ澶勫仛鏂囦欢绫诲瀷鎴栧ぇ灏忔牎楠�
+      const isValid =
+        file.type ===
+          "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
+        file.name.endsWith(".xlsx") ||
+        file.name.endsWith(".xls");
+      if (!isValid) {
+        proxy.$modal.msgError("鍙兘涓婁紶 Excel 鏂囦欢");
+      }
+      return isValid;
+    },
+    // 鏂囦欢鐘舵�佹敼鍙樻椂鐨勫洖璋�
+    onChange: (file, fileList) => {
+      console.log("鏂囦欢鐘舵�佹敼鍙�", file, fileList);
+    },
+    // 鏂囦欢涓婁紶鎴愬姛鏃剁殑鍥炶皟
+    onSuccess: (response, file, fileList) => {
+      console.log("涓婁紶鎴愬姛", response, file, fileList);
+      upload.isUploading = false;
+      if (response.code === 200) {
+        proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
+        upload.open = false;
+        proxy.$refs["uploadRef"].clearFiles();
+        getList();
+      } else if (response.code === 500) {
+        proxy.$modal.msgError(response.msg);
+      } else {
+        proxy.$modal.msgWarning(response.msg);
+      }
+    },
+    // 鏂囦欢涓婁紶澶辫触鏃剁殑鍥炶皟
+    onError: (error, file, fileList) => {
+      console.error("涓婁紶澶辫触", error, file, fileList);
+      upload.isUploading = false;
+      proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触");
+    },
+    // 鏂囦欢涓婁紶杩涘害鍥炶皟
+    onProgress: (event, file, fileList) => {
+      console.log("涓婁紶涓�...", event.percent);
+    },
+  });
+  const { searchForm, form, rules } = toRefs(data);
+  const addNewContact = () => {
+    formYYs.value.contactList.push({
+      contactPerson: "",
+      contactPhone: "",
+    });
+  };
+
+  const removeContact = index => {
+    if (formYYs.value.contactList.length > 1) {
+      formYYs.value.contactList.splice(index, 1);
+    }
+  };
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
+  const getList = () => {
+    tableLoading.value = true;
+    const { total, ...queryPage } = page;
+    listCustomer({ ...searchForm.value, ...queryPage }).then(res => {
+      tableLoading.value = false;
+      tableData.value = res.data.records;
+      page.total = res.data.total;
+    });
+  };
+  // 琛ㄦ牸閫夋嫨鏁版嵁
+  const handleSelectionChange = selection => {
+    selectedRows.value = selection;
+  };
+  /** 鎻愪氦涓婁紶鏂囦欢 */
+  function submitFileForm() {
+    upload.isUploading = true;
+    proxy.$refs["uploadRef"].submit();
+  }
+  /** 瀵煎叆鎸夐挳鎿嶄綔 */
+  function handleImport() {
+    upload.title = "瀹㈡埛瀵煎叆";
+    upload.open = true;
+  }
+  /** 涓嬭浇妯℃澘 */
+  function importTemplate() {
+    proxy.download("/basic/customer/downloadTemplate", {}, "瀹㈡埛瀵煎叆妯℃澘.xlsx");
+  }
+  // 鎵撳紑寮规
+  const openForm = (type, row) => {
+    operationType.value = type;
+    form.value = {};
+    form.value.maintainer = userStore.nickName;
+    formYYs.value.contactList = [
+      {
+        contactPerson: "",
+        contactPhone: "",
+      },
+    ];
+    form.value.maintenanceTime = getCurrentDate();
+    userListNoPage().then(res => {
+      userList.value = res.data;
+    });
+    if (type === "edit") {
+      getCustomer(row.id).then(res => {
+        form.value = { ...res.data };
+        formYYs.value.contactList = res.data.contactPerson
+          .split(",")
+          .map((item, index) => {
+            return {
+              contactPerson: item,
+              contactPhone: res.data.contactPhone.split(",")[index],
+            };
+          });
+      });
+    }
+    dialogFormVisible.value = true;
+  };
+  // 鎻愪氦琛ㄥ崟
+  const submitForm = () => {
+    proxy.$refs["formRef"].validate(valid => {
+      if (valid) {
+        if (operationType.value === "edit") {
+          submitEdit();
+        } else {
+          submitAdd();
+        }
+      }
+    });
+  };
+  // 鎻愪氦鏂板
+  const submitAdd = () => {
+    if (formYYs.value.contactList.length < 1) {
+      return proxy.$modal.msgWarning("璇疯嚦灏戞坊鍔犱竴涓仈绯讳汉");
+    }
+    form.value.contactPerson = formYYs.value.contactList
+      .map(item => item.contactPerson)
+      .join(",");
+    form.value.contactPhone = formYYs.value.contactList
+      .map(item => item.contactPhone)
+      .join(",");
+    addCustomer(form.value).then(res => {
+      proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+      closeDia();
+      getList();
+    });
+  };
+  // 鎻愪氦淇敼
+  const submitEdit = () => {
+    form.value.contactPerson = formYYs.value.contactList
+      .map(item => item.contactPerson)
+      .join(",");
+    form.value.contactPhone = formYYs.value.contactList
+      .map(item => item.contactPhone)
+      .join(",");
+    updateCustomer(form.value).then(res => {
+      proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+      closeDia();
+      getList();
+    });
+  };
+  // 鍏抽棴寮规
+  const closeDia = () => {
+    proxy.resetForm("formRef");
+    dialogFormVisible.value = false;
+  };
+  const ensureUserList = () => {
+    if (userList.value.length) {
+      return Promise.resolve();
+    }
+    return userListNoPage().then(res => {
+      userList.value = res.data || [];
+    });
+  };
+  const openAssignDialog = row => {
+    assignForm.id = row.id;
+    assignForm.customerName = row.customerName;
+    assignForm.boundId = undefined;
+    ensureUserList().then(() => {
+      assignDialogVisible.value = true;
+    });
+  };
+  const closeAssignDialog = () => {
+    proxy.resetForm("assignFormRef");
+    assignForm.id = undefined;
+    assignForm.customerName = "";
+    assignForm.boundId = undefined;
+    assignDialogVisible.value = false;
+  };
+  const openShareDialog = row => {
+    shareForm.id = row.id;
+    shareForm.customerName = row.customerName;
+    shareForm.boundIds = [];
+    ensureUserList().then(() => {
+      shareDialogVisible.value = true;
+    });
+  };
+  const closeShareDialog = () => {
+    proxy.resetForm("shareFormRef");
+    shareForm.id = undefined;
+    shareForm.customerName = "";
+    shareForm.boundIds = [];
+    shareDialogVisible.value = false;
+  };
+  const submitAssignForm = () => {
+    proxy.$refs.assignFormRef.validate(valid => {
+      if (!valid) {
+        return;
+      }
+      addCustomerPrivatePool({
+        customerId: assignForm.id,
+        boundId: assignForm.boundId,
+      }).then(() => {
+        proxy.$modal.msgSuccess("鍒嗛厤鎴愬姛");
+        closeAssignDialog();
+        getList();
+      });
+    });
+  };
+  const submitShareForm = () => {
+    proxy.$refs.shareFormRef.validate(valid => {
+      if (!valid) {
+        return;
+      }
+      shareCustomer({
+        customerId: shareForm.id,
+        boundIds: shareForm.boundIds,
+      }).then(() => {
+        proxy.$modal.msgSuccess("鍏变韩鎴愬姛");
+        closeShareDialog();
+        getList();
+      });
+    });
+  };
+  const recycleCustomer = row => {
+    ElMessageBox.confirm("纭鍥炴敹瀹㈡埛鈥�" + row.customerName + "鈥濆悧锛�", "鍥炴敹鎻愮ず", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        return delCustomerPrivatePool(row.id);
+      })
+      .then(() => {
+        proxy.$modal.msgSuccess("鍥炴敹鎴愬姛");
+        getList();
+      })
+      .catch(error => {
+        if (error === "cancel" || error === "close") {
+          proxy.$modal.msg("宸插彇娑�");
+        }
+      });
+  };
+  // 瀵煎嚭
+  const handleOut = () => {
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        proxy.download("/basic/customer/export", {}, "瀹㈡埛妗f.xlsx");
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+  };
+  // 鍒犻櫎
+  const handleDelete = () => {
+    let ids = [];
+    if (selectedRows.value.length > 0) {
+      // 妫�鏌ユ槸鍚︽湁浠栦汉缁存姢鐨勬暟鎹�
+      const unauthorizedData = selectedRows.value.filter(
+        item => item.maintainer !== userStore.nickName
+      );
+      if (unauthorizedData.length > 0) {
+        proxy.$modal.msgWarning("涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�");
+        return;
+      }
+      ids = selectedRows.value.map(item => item.id);
+    } else {
+      proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+      return;
+    }
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        tableLoading.value = true;
+        delCustomer(ids)
+          .then(res => {
+            proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+            getList();
+          })
+          .finally(() => {
+            tableLoading.value = false;
+          });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+  };
+
+  // 鎵撳紑鍥炶鎻愰啋寮圭獥
+  const openReminderDialog = row => {
+    currentCustomerId.value = row.id;
+    reminderForm.customerName = row.customerName;
+    reminderForm.reminderSwitch = false;
+    reminderForm.reminderContent = "";
+    reminderForm.reminderTime = "";
+
+    // 灏濊瘯鑾峰彇宸叉湁鐨勫洖璁挎彁閱�
+    getReturnVisit(row.id)
+      .then(res => {
+        if (res.code === 200 && res.data) {
+          reminderForm.reminderSwitch = res.data.isEnabled === 1;
+          reminderForm.reminderContent = res.data.content;
+          reminderForm.reminderTime = res.data.reminderTime;
+          reminderForm.id = res.data.id;
+        }
+      })
+      .catch(error => {
+        console.error("鑾峰彇鍥炶鎻愰啋澶辫触:", error);
+      });
+
+    reminderDialogVisible.value = true;
+  };
+
+  // 鍏抽棴鍥炶鎻愰啋寮圭獥
+  const closeReminderDialog = () => {
+    proxy.resetForm("reminderFormRef");
+    reminderDialogVisible.value = false;
+  };
+  const submitvalue = ref({});
+
+  // 鎻愪氦鍥炶鎻愰啋
+  const submitReminderForm = () => {
+    console.log("鎻愪氦鍥炶鎻愰啋鏁版嵁:", userStore.id, userStore);
+    proxy.$refs.reminderFormRef.validate(valid => {
+      if (valid) {
+        if (reminderForm.id) {
+          submitvalue.value = {
+            id: reminderForm.id,
+            customerId: currentCustomerId.value,
+            isEnabled: reminderForm.reminderSwitch ? 1 : 0,
+            content: reminderForm.reminderContent,
+            reminderTime: reminderForm.reminderTime,
+            remindUserId: userStore.id,
+          };
+        } else {
+          submitvalue.value = {
+            customerId: currentCustomerId.value,
+            isEnabled: reminderForm.reminderSwitch ? 1 : 0,
+            content: reminderForm.reminderContent,
+            reminderTime: reminderForm.reminderTime,
+            remindUserId: userStore.id,
+          };
+        }
+
+        console.log("鎻愪氦鍥炶鎻愰啋鏁版嵁:", submitvalue.value);
+
+        // 璋冪敤鎺ュ彛
+        addReturnVisit(submitvalue.value)
+          .then(res => {
+            if (res.code === 200) {
+              proxy.$modal.msgSuccess("鍥炶鎻愰啋璁剧疆鎴愬姛");
+              closeReminderDialog();
+            } else {
+              proxy.$modal.msgError(res.msg || "璁剧疆澶辫触");
+            }
+          })
+          .catch(error => {
+            console.error("璁剧疆鍥炶鎻愰啋澶辫触:", error);
+            proxy.$modal.msgError("璁剧疆澶辫触");
+          });
+      }
+    });
+  };
+
+  // 鎵撳紑娲借皥杩涘害寮圭獥
+  const openNegotiationDialog = row => {
+    negotiationForm.customerName = row.customerName;
+    negotiationForm.customerId = row.id;
+    negotiationForm.followUpMethod = "";
+    negotiationForm.followUpLevel = "";
+    negotiationForm.followUpTime = "";
+    negotiationForm.followerUserName = userStore.nickName; // 榛樿褰撳墠鐧诲綍浜�
+    negotiationForm.content = "";
+    // {
+    // 	"customerId": 152,
+    // 	"followUpMethod": "鐢佃瘽娌熼��",
+    // 	"followUpLevel": "娌℃湁鎰忓悜",
+    // 	"followUpTime": "2026-03-04T15:30:00",
+    // 	"followerUserName": "绠$悊鍛樿处鍙�",
+    // 	"content": "111"
+    // }
+    negotiationDialogVisible.value = true;
+  };
+
+  // 鍏抽棴娲借皥杩涘害寮圭獥
+  const closeNegotiationDialog = () => {
+    proxy.resetForm("negotiationFormRef");
+    // 娓呴櫎缂栬緫鐘舵��
+    delete negotiationForm.editIndex;
+    delete negotiationForm.id;
+    negotiationDialogVisible.value = false;
+  };
+
+  // 鎻愪氦娲借皥杩涘害
+  const submitNegotiationForm = () => {
+    proxy.$refs.negotiationFormRef.validate(valid => {
+      if (valid) {
+        // 鍒ゆ柇鏄柊澧炶繕鏄慨鏀�
+        const isEdit = negotiationForm.editIndex !== undefined;
+
+        if (isEdit) {
+          // 淇敼鎿嶄綔
+          console.log("淇敼娲借皥杩涘害鏁版嵁:", negotiationForm);
+          // 杩欓噷鍙互璋冪敤鏇存柊鎺ュ彛
+          // 瀹為檯椤圭洰涓渶瑕佹牴鎹悗绔帴鍙h繘琛岃皟鏁�
+          // 绀轰緥锛歶pdateCustomerFollow(negotiationForm).then(res => {
+          //   // 鏇存柊鏈湴鏁版嵁
+          //   const index = negotiationForm.editIndex;
+          //   negotiationRecords.value[index] = {
+          //     followUpTime: negotiationForm.followUpTime,
+          //     followUpMethod: negotiationForm.followUpMethod,
+          //     followUpLevel: negotiationForm.followUpLevel,
+          //     followerUserName: negotiationForm.followerUserName,
+          //     content: negotiationForm.content,
+          //     id: negotiationForm.id,
+          //   };
+          //   proxy.$modal.msgSuccess("淇敼鎴愬姛");
+          //   closeNegotiationDialog();
+          // });
+          updateCustomerFollow(negotiationForm).then(res => {
+            // 鏇存柊鏈湴鏁版嵁
+            getCustomer(negotiationForm.customerId).then(res => {
+              // 鏇存柊鏈湴鏁版嵁
+              negotiationRecords.value = res.data.followUpList || [];
+            });
+          });
+          proxy.$modal.msgSuccess("淇敼鎴愬姛");
+          closeNegotiationDialog();
+        } else {
+          // 鏂板鎿嶄綔
+          console.log("鎻愪氦娲借皥杩涘害鏁版嵁:", negotiationForm);
+          addCustomerFollow(negotiationForm).then(res => {
+            // 娣诲姞鎴愬姛鍚庢洿鏂拌鎯呴〉闈㈢殑杩涘害璁板綍
+            const newRecord = {
+              followUpTime: negotiationForm.followUpTime,
+              followUpMethod: negotiationForm.followUpMethod,
+              followUpLevel: negotiationForm.followUpLevel,
+              followerUserName: negotiationForm.followerUserName,
+              content: negotiationForm.content,
+            };
+            negotiationRecords.value.unshift(newRecord);
+
+            proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+            closeNegotiationDialog();
+            getList();
+          });
+        }
+      }
+    });
+  };
+
+  // 鎵撳紑璇︽儏寮圭獥
+  const openDetailDialog = row => {
+    // 璋冪敤getCustomer鎺ュ彛鑾峰彇瀹㈡埛璇︽儏
+    getCustomer(row.id).then(res => {
+      // 濉厖瀹㈡埛鍩烘湰淇℃伅
+      Object.assign(detailForm, res.data);
+
+      // 鑾峰彇娲借皥杩涘害璁板綍
+      negotiationRecords.value = res.data.followUpList || [];
+
+      detailDialogVisible.value = true;
+    });
+  };
+
+  // 鍏抽棴璇︽儏寮圭獥
+  const closeDetailDialog = () => {
+    detailDialogVisible.value = false;
+  };
+
+  // 淇敼娲借皥璁板綍
+  const editNegotiationRecord = (row, index) => {
+    // 灏嗗綋鍓嶈褰曟暟鎹~鍏呭埌琛ㄥ崟
+    Object.assign(negotiationForm, {
+      customerName: row.customerName,
+      customerId: row.customerId,
+      followUpMethod: row.followUpMethod,
+      followUpLevel: row.followUpLevel,
+      followUpTime: row.followUpTime,
+      followerUserName: row.followerUserName,
+      content: row.content,
+      id: row.id, // 璁板綍ID鐢ㄤ簬鏇存柊
+      editIndex: index, // 璁板綍绱㈠紩鐢ㄤ簬鏈湴鏇存柊
+    });
+    negotiationDialogVisible.value = true;
+  };
+
+  // 鍒犻櫎娲借皥璁板綍
+  const deleteNegotiationRecord = (row, index) => {
+    ElMessageBox.confirm("纭畾瑕佸垹闄よ繖鏉℃唇璋堣褰曞悧锛�", "鍒犻櫎鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        // 杩欓噷鍙互璋冪敤鍒犻櫎鎺ュ彛
+        // 瀹為檯椤圭洰涓渶瑕佹牴鎹悗绔帴鍙h繘琛岃皟鏁�
+        // 绀轰緥锛歞eleteCustomerFollow(row.id).then(() => {
+        //   negotiationRecords.value.splice(index, 1);
+        //   proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+        // });
+        delCustomerFollow(row.id).then(() => {
+          // 鍒犻櫎鎴愬姛鍚庢洿鏂版湰鍦版暟鎹�
+          getCustomer(row.customerId).then(res => {
+            // 鏇存柊鏈湴鏁版嵁
+            negotiationRecords.value = res.data.followUpList || [];
+          });
+          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+        });
+        // 鏈湴鍒犻櫎锛堟ā鎷燂級
+        negotiationRecords.value.splice(index, 1);
+        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑堝垹闄�");
+      });
+  };
+
+  // 鎵撳紑闄勪欢寮圭獥
+  const openAttachmentDialog = row => {
+    currentFollowRecord.value = row;
+    // 杞崲涓虹鍚圗lement Plus fileList鏍煎紡鐨勬暟缁�
+    currentAttachmentList.value = (row.fileList || []).map((file, index) => ({
+      name: file.fileName,
+      url: file.fileUrl,
+      size: file.fileSize,
+      id: file.id,
+      uid: file.id || index,
+      status: "success",
+    }));
+
+    attachmentDialogVisible.value = true;
+  };
+
+  // 鍏抽棴闄勪欢寮圭獥
+  const closeAttachmentDialog = () => {
+    attachmentDialogVisible.value = false;
+    currentFollowRecord.value = {};
+    currentAttachmentList.value = [];
+  };
+
+  // 闄勪欢涓婁紶鎴愬姛
+  const handleAttachmentSuccess = (response, file, fileList) => {
+    if (response.code === 200) {
+      proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+      // 鏇存柊褰撳墠璁板綍鐨勯檮浠跺垪琛�
+      currentAttachmentList.value = fileList.map(item => ({
+        name: item.name,
+        size: item.size,
+        url: item.response?.data?.url || item.url,
+        id: item.response?.data?.id,
+        uid: item.uid,
+        status: "success",
+      }));
+      // 鏇存柊鍘熻褰曚腑鐨刦iles瀛楁
+      if (currentFollowRecord.value) {
+        currentFollowRecord.value.files = [...currentAttachmentList.value];
+      }
+    } else {
+      proxy.$modal.msgError(response.msg || "涓婁紶澶辫触");
+    }
+  };
+
+  // 闄勪欢涓婁紶澶辫触
+  const handleAttachmentError = (error, file, fileList) => {
+    console.error("涓婁紶澶辫触:", error);
+    proxy.$modal.msgError("涓婁紶澶辫触");
+  };
+
+  // 闄勪欢绉婚櫎
+  const handleAttachmentRemove = (file, fileList) => {
+    currentAttachmentList.value = fileList;
+    // 鏇存柊鍘熻褰曚腑鐨刦iles瀛楁
+    if (currentFollowRecord.value) {
+      currentFollowRecord.value.files = [...fileList];
+    }
+  };
+
+  // 闄勪欢涓婁紶鍓嶆牎楠�
+  const beforeAttachmentUpload = file => {
+    const maxSize = 50 * 1024 * 1024; // 50MB
+    if (file.size > maxSize) {
+      proxy.$modal.msgError("鏂囦欢澶у皬涓嶈兘瓒呰繃50MB");
+      return false;
+    }
+    return true;
+  };
+
+  // 鏍煎紡鍖栨枃浠跺ぇ灏�
+  const formatFileSize = size => {
+    if (size < 1024) {
+      return size + " B";
+    } else if (size < 1024 * 1024) {
+      return (size / 1024).toFixed(2) + " KB";
+    } else {
+      return (size / (1024 * 1024)).toFixed(2) + " MB";
+    }
+  };
+
+  // 涓嬭浇闄勪欢
+  const downloadAttachment = row => {
+    if (row.url) {
+      // proxy.download(row.url, {}, row.name);
+      proxy.$download.name(row.url);
+    } else {
+      proxy.$modal.msgError("涓嬭浇閾炬帴涓嶅瓨鍦�");
+    }
+  };
+
+  // 鍒犻櫎闄勪欢
+  const deleteAttachment = (row, index) => {
+    ElMessageBox.confirm("纭畾瑕佸垹闄よ繖涓檮浠跺悧锛�", "鍒犻櫎鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        // 璋冪敤鍚庣鎺ュ彛鍒犻櫎闄勪欢
+        const deleteUrl =
+          import.meta.env.VITE_APP_BASE_API +
+          "/basic/customer-follow/file/" +
+          row.id;
+        fetch(deleteUrl, {
+          method: "DELETE",
+          headers: {
+            Authorization: "Bearer " + getToken(),
+            "Content-Type": "application/json",
+          },
+        })
+          .then(response => response.json())
+          .then(res => {
+            if (res.code === 200) {
+              // 鍒犻櫎鎴愬姛鍚庢洿鏂版湰鍦版枃浠跺垪琛�
+              currentAttachmentList.value.splice(index, 1);
+              // 鏇存柊鍘熻褰曚腑鐨刦iles瀛楁
+              if (currentFollowRecord.value) {
+                currentFollowRecord.value.files = [
+                  ...currentAttachmentList.value,
+                ];
+              }
+              proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+            } else {
+              proxy.$modal.msgError(res.msg || "鍒犻櫎澶辫触");
+            }
+          })
+          .catch(error => {
+            console.error("鍒犻櫎闄勪欢澶辫触:", error);
+            proxy.$modal.msgError("鍒犻櫎澶辫触");
+          });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑堝垹闄�");
+      });
+  };
+
+  // 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD
+  function getCurrentDate() {
+    const today = new Date();
+    const year = today.getFullYear();
+    const month = String(today.getMonth() + 1).padStart(2, "0"); // 鏈堜唤浠�0寮�濮�
+    const day = String(today.getDate()).padStart(2, "0");
+    return `${year}-${month}-${day}`;
+  }
+
+  onMounted(() => {
+    getList();
+  });
+</script>
+
+<style scoped lang="scss">
+  .detail-section {
+    margin-bottom: 20px;
+    padding: 15px;
+    background-color: #f9f9f9;
+    border-radius: 4px;
+  }
+
+  .section-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 15px;
+  }
+
+  .section-title {
+    font-size: 16px;
+    font-weight: bold;
+    margin: 0 0 15px 0;
+    color: #333;
+  }
+
+  .info-display {
+    background-color: #fff;
+    padding: 15px;
+    border-radius: 4px;
+  }
+
+  .info-item {
+    margin-bottom: 12px;
+    display: flex;
+    align-items: flex-start;
+  }
+
+  .info-label {
+    width: 120px;
+    font-weight: 500;
+    color: #606266;
+    margin-right: 10px;
+  }
+
+  .info-value {
+    flex: 1;
+    color: #303133;
+    word-break: break-word;
+  }
+
+  .no-records {
+    text-align: center;
+    padding: 30px;
+    color: #999;
+    font-size: 14px;
+  }
+
+  .attachment-section {
+    .upload-area {
+      margin-bottom: 20px;
+      padding: 20px;
+      background-color: #f9f9f9;
+      border-radius: 4px;
+      border: 1px dashed #d9d9d9;
+
+      .el-upload__tip {
+        margin-top: 10px;
+        color: #909399;
+      }
+    }
+
+    .attachment-list {
+      h4 {
+        margin: 0 0 10px 0;
+        font-size: 14px;
+        color: #606266;
+      }
+    }
+
+    .no-attachment {
+      text-align: center;
+      padding: 40px;
+      color: #909399;
+      font-size: 14px;
+    }
+  }
+</style>
diff --git a/src/views/basicData/product/ProductSelectDialog.vue b/src/views/basicData/product/ProductSelectDialog.vue
index c10d29e..ad27baa 100644
--- a/src/views/basicData/product/ProductSelectDialog.vue
+++ b/src/views/basicData/product/ProductSelectDialog.vue
@@ -1,12 +1,12 @@
 <template>
   <el-dialog v-model="visible" title="閫夋嫨浜у搧" width="900px" destroy-on-close :close-on-click-modal="false">
     <el-form :inline="true" :model="query" class="mb-2">
-      <el-form-item label="浜у搧澶х被">
-        <el-input v-model="query.productName" placeholder="杈撳叆浜у搧澶х被" clearable @keyup.enter="onSearch" />
+      <el-form-item label="浜у搧鍚嶇О">
+        <el-input v-model="query.productName" placeholder="杈撳叆浜у搧鍚嶇О" clearable @keyup.enter="onSearch" />
       </el-form-item>
 
-      <el-form-item label="鍨嬪彿鍚嶇О">
-        <el-input v-model="query.model" placeholder="杈撳叆鍨嬪彿鍚嶇О" clearable @keyup.enter="onSearch" />
+      <el-form-item label="浜у搧鍨嬪彿">
+        <el-input v-model="query.model" placeholder="杈撳叆浜у搧鍨嬪彿" clearable @keyup.enter="onSearch" />
       </el-form-item>
 
       <el-form-item>
@@ -20,8 +20,8 @@
       @selection-change="handleSelectionChange" @select="handleSelect">
       <el-table-column type="selection" width="55" />
       <el-table-column type="index" label="搴忓彿" width="60" />
-      <el-table-column prop="productName" label="浜у搧澶х被" min-width="160" />
-      <el-table-column prop="model" label="鍨嬪彿鍚嶇О" min-width="200" />
+      <el-table-column prop="productName" label="浜у搧鍚嶇О" min-width="160" />
+      <el-table-column prop="model" label="浜у搧鍨嬪彿" min-width="200" />
       <el-table-column prop="unit" label="鍗曚綅" min-width="160" />
     </el-table>
 
@@ -43,7 +43,7 @@
 <script setup lang="ts">
 import { computed, onMounted, reactive, ref, watch, nextTick } from "vue";
 import { ElMessage } from "element-plus";
-import { productModelList } from '@/api/basicData/productModel'
+import { productModelList, productModelListByUrl } from '@/api/basicData/productModel'
 
 export type ProductRow = {
   id: number;
@@ -56,6 +56,7 @@
   modelValue: boolean;
   single?: boolean; // 鏄惁鍙兘閫夋嫨涓�涓紝榛樿false锛堝彲閫夋嫨澶氫釜锛�
   topProductParentId?: number; // 涓�绾т骇鍝乮d
+  requestUrl?: string; // 鑷畾涔夋煡璇㈡帴鍙�
 }>();
 
 const emit = defineEmits(['update:modelValue', 'confirm']);
@@ -155,15 +156,19 @@
   loading.value = true;
   try {
     multipleSelection.value = []; // 缈婚〉/鎼滅储鍚庢竻绌洪�夋嫨鏇寸鍚堥鏈�
-    const res: any = await productModelList({
+    const params = {
       productName: query.productName.trim(),
       model: query.model.trim(),
       current: page.pageNum,
       size: page.pageSize,
       topProductParentId: props.topProductParentId,
-    });
-    tableData.value = res.records;
-    total.value = res.total;
+    };
+    const res: any = props.requestUrl
+      ? await productModelListByUrl(props.requestUrl, params)
+      : await productModelList(params);
+    const records = res?.records || res?.data?.records || res?.data || [];
+    tableData.value = Array.isArray(records) ? records : [];
+    total.value = Number(res?.total ?? res?.data?.total ?? tableData.value.length);
   } finally {
     loading.value = false;
   }
diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index 3b86b37..10b51bf 100644
--- a/src/views/basicData/product/index.vue
+++ b/src/views/basicData/product/index.vue
@@ -12,6 +12,7 @@
           prefix-icon="Search"
         />
         <el-button
+          v-if="false"
           type="primary"
           @click="openProDia('addOne')"
           style="margin-left: 10px"
@@ -49,6 +50,7 @@
                 <el-button
                   type="primary"
                   link
+                  :disabled="isTopLevelNode(data, node)"
                   @click="openProDia('edit', data)"
                 >
                   缂栬緫
@@ -61,6 +63,7 @@
                   style="margin-left: 4px"
                   type="danger"
                   link
+                  :disabled="isTopLevelNode(data, node)"
                   @click="remove(node, data)"
                 >
                   鍒犻櫎
@@ -375,8 +378,18 @@
 const searchFilter = () => {
   proxy.$refs.tree.filter(search.value);
 };
+const isTopLevelNode = (data, node) => {
+  if (node?.level !== undefined) {
+    return node.level === 1;
+  }
+  return [null, undefined, "", 0, "0"].includes(data?.parentId);
+};
 // 鎵撳紑浜у搧寮规
 const openProDia = (type, data) => {
+  if (data && type === "edit" && isTopLevelNode(data)) {
+    proxy.$modal.msgWarning("涓�绾ц妭鐐逛笉鑳界紪杈戞垨鍒犻櫎");
+    return;
+  }
   operationType.value = type;
   productDia.value = true;
   form.value.productName = "";
@@ -425,6 +438,10 @@
 
 // 鍒犻櫎浜у搧
 const remove = (node, data) => {
+  if (isTopLevelNode(data, node)) {
+    proxy.$modal.msgWarning("涓�绾ц妭鐐逛笉鑳界紪杈戞垨鍒犻櫎");
+    return;
+  }
   let ids = [];
   ids.push(data.id);
   ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index 8a293fc..0543286 100644
--- a/src/views/procurementManagement/procurementLedger/index.vue
+++ b/src/views/procurementManagement/procurementLedger/index.vue
@@ -546,6 +546,7 @@
               <el-tree-select v-model="productForm.productId"
                               placeholder="璇烽�夋嫨"
                               clearable
+                              filterable
                               check-strictly
                               @change="getModels"
                               :data="productOptions"
@@ -560,6 +561,7 @@
                           prop="productModelId">
               <el-select v-model="productForm.productModelId"
                          placeholder="璇烽�夋嫨"
+                         filterable
                          clearable
                          @change="getProductModel">
                 <el-option v-for="item in modelOptions"
@@ -1685,12 +1687,12 @@
   const handleDelete = () => {
     let ids = [];
     if (selectedRows.value.length > 0) {
-      ids = selectedRows.value.map(item => item.id);
+      ids = selectedRows.value.filter(item => item.salesLedgerId === null).map(item => item.id);
     } else {
       proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
       return;
     }
-    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎", {
       confirmButtonText: "纭",
       cancelButtonText: "鍙栨秷",
       type: "warning",
diff --git a/src/views/procurementManagement/purchaseReturnOrder/New.vue b/src/views/procurementManagement/purchaseReturnOrder/New.vue
index 17e36dc..dffdeea 100644
--- a/src/views/procurementManagement/purchaseReturnOrder/New.vue
+++ b/src/views/procurementManagement/purchaseReturnOrder/New.vue
@@ -70,6 +70,7 @@
               v-model="formState.supplierId"
               placeholder="璇烽�夋嫨渚涘簲鍟�"
               style="width: 240px"
+							filterable
               @focus="fetchSupplierOptions"
               @change="handleChangeSupplierId"
           >
diff --git a/src/views/productionManagement/processRoute/index.vue b/src/views/productionManagement/processRoute/index.vue
index 0d1bb14..553dbe2 100644
--- a/src/views/productionManagement/processRoute/index.vue
+++ b/src/views/productionManagement/processRoute/index.vue
@@ -171,6 +171,7 @@
     path: '/productionManagement/processRouteItem',
     query: {
       id: row.id,
+      bomId: row.bomId,
       processRouteCode: row.processRouteCode || '',
       productName: row.productName || '',
       model: row.model || '',
diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
index aca3550..b673a81 100644
--- a/src/views/productionManagement/processRoute/processRouteItem/index.vue
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -47,7 +47,13 @@
         </div>
       </div>
     </el-card>
-    
+    <div class="section-title" style="margin-bottom: 10px;">浜у搧缁撴瀯</div>
+    <ProductStructureDetail
+      class="product-structure-panel"
+      style="margin-bottom: 20px;"
+      embedded
+      :bom-id="route.query.bomId"
+    />
     <!-- 琛ㄦ牸瑙嗗浘 -->
     <div v-if="viewMode === 'table'" class="section-header">
       <div class="section-title">宸ヨ壓璺嚎椤圭洰鍒楄〃</div>
@@ -231,7 +237,7 @@
 </template>
 
 <script setup>
-import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick } from "vue";
+import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick, defineAsyncComponent } from "vue";
 import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
 import { findProcessRouteItemList, addOrUpdateProcessRouteItem, sortProcessRouteItem, batchDeleteProcessRouteItem } from "@/api/productionManagement/processRouteItem.js";
 import { findProductProcessRouteItemList, deleteRouteItem, addRouteItem, addOrUpdateProductProcessRouteItem, sortRouteItem } from "@/api/productionManagement/productProcessRoute.js";
@@ -242,6 +248,7 @@
 
 const route = useRoute()
 const { proxy } = getCurrentInstance() || {};
+const ProductStructureDetail = defineAsyncComponent(() => import("@/views/productionManagement/productStructure/Detail/index.vue"));
 
 const routeId = computed(() => route.query.id);
 const orderId = computed(() => route.query.orderId);
@@ -841,6 +848,10 @@
   align-items: center;
 }
 
+.product-structure-panel {
+  margin: 12px 0 20px;
+}
+
 /* 宸ヨ壓璺嚎淇℃伅鍗$墖鏍峰紡 */
 .route-info-card {
   margin-bottom: 20px;
diff --git a/src/views/productionManagement/productStructure/Detail/index.vue b/src/views/productionManagement/productStructure/Detail/index.vue
index 6734830..832c121 100644
--- a/src/views/productionManagement/productStructure/Detail/index.vue
+++ b/src/views/productionManagement/productStructure/Detail/index.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="app-container">
-    <PageHeader content="浜у搧缁撴瀯璇︽儏">
+  <div :class="embedded ? 'embedded-container' : 'app-container'">
+    <PageHeader v-if="!embedded" content="浜у搧缁撴瀯璇︽儏">
       <template #right-button>
         <el-button v-if="!dataValue.isEdit && !isOrderPage"
                    type="primary"
@@ -119,7 +119,7 @@
                   </el-form-item>
                 </template>
               </el-table-column>
-              <el-table-column label="鎿嶄綔"
+              <el-table-column v-if="!embedded" label="鎿嶄綔"
                                fixed="right"
                                width="200">
                 <template #default="{ row, $index }">
@@ -174,6 +174,18 @@
   const ProductSelectDialog = defineAsyncComponent(
     () => import("@/views/basicData/product/ProductSelectDialog.vue")
   );
+  const props = defineProps({
+    embedded: {
+      type: Boolean,
+      default: false,
+    },
+    // 鏄惧紡鎸囧畾BOM涓婚敭锛堢敤浜庡祵鍏ュ埌鈥滃伐鑹鸿矾绾块」鐩�濈瓑椤甸潰鏃讹紝璺敱 query.id 涓嶆槸 bomId 鐨勬儏鍐碉級
+    bomId: {
+      type: [String, Number],
+      default: undefined,
+    },
+  });
+  const embedded = computed(() => props.embedded);
   const emit = defineEmits(["update:router"]);
   const form = ref();
 
@@ -181,7 +193,8 @@
   const router = useRouter();
   const routeId = computed({
     get() {
-      return route.query.id;
+      // 浼樺厛浣跨敤澶栭儴浼犲叆鐨� bomId锛屽叾娆′娇鐢ㄨ矾鐢辩殑 bomId锛屾渶鍚庡洖閫�鍒拌矾鐢辩殑 id锛堝吋瀹瑰師椤甸潰锛�
+      return props.bomId ?? route.query.bomId ?? route.query.id;
     },
 
     set(val) {
@@ -227,29 +240,27 @@
   };
 
   const fetchData = async () => {
-    if (isOrderPage.value) {
-      // 璁㈠崟鎯呭喌锛氫娇鐢ㄨ鍗曠殑浜у搧缁撴瀯鎺ュ彛
-      const { data } = await listProcessBom({ orderId: routeOrderId.value });
-      dataValue.dataList = (data as any) || [];
-    } else {
-      // 闈炶鍗曟儏鍐碉細浣跨敤鍘熸潵鐨勬帴鍙�
-      const { data } = await queryList(routeId.value);
-      dataValue.dataList = (data as any) || [];
-      // 涓烘墍鏈夐」鍙婂叾瀛愰」璁剧疆name灞炴��
-      const setNameRecursively = (items: any[]) => {
-        items.forEach((item: any) => {
-          item.tempId = item.id;
-          item.processName =
-            dataValue.processOptions.find(option => option.id === item.processId)
-              ?.name || "";
-          if (item.children && item.children.length > 0) {
-            setNameRecursively(item.children);
-          }
-        });
-      };
-      setNameRecursively(dataValue.dataList);
-      console.log(dataValue.dataList, "dataValue.dataList");
+    const setNameRecursively = (items: any[]) => {
+      items.forEach((item: any) => {
+        item.tempId = item.tempId || item.id || new Date().getTime() + Math.random();
+        item.processName =
+          dataValue.processOptions.find(option => option.id === item.processId)?.name || item.processName || "";
+        if (item.children && item.children.length > 0) {
+          setNameRecursively(item.children);
+        }
+      });
+    };
+
+    // 缁熶竴浣跨敤 BOM 鏌ヨ浜у搧缁撴瀯锛�/productStructure/listBybomId/{bomId}
+    // 璇存槑锛氳鍗曢〉涔熶細浠庤矾鐢�/鐖剁粍浠跺甫鍏� bomId锛坮oute.query.bomId 鎴� props.bomId锛�
+    const bomId = routeId.value;
+    if (!bomId) {
+      dataValue.dataList = [];
+      return;
     }
+    const { data } = await queryList(bomId);
+    dataValue.dataList = (data as any) || [];
+    setNameRecursively(dataValue.dataList);
   };
 
   const fetchProcessOptions = async () => {
@@ -518,4 +529,11 @@
     await fetchProcessOptions();
     await fetchData();
   });
-</script>
\ No newline at end of file
+</script>
+
+<style scoped>
+.embedded-container {
+  padding: 0;
+  margin: 0;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue b/src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
index f93199a..9c50fc8 100644
--- a/src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
+++ b/src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
@@ -36,8 +36,9 @@
     <el-dialog v-model="supplementRecordDialogVisible" title="琛ユ枡璁板綍" width="800px">
       <el-table v-loading="supplementRecordLoading" :data="supplementRecordTableData" border row-key="id">
         <el-table-column label="琛ユ枡鏁伴噺" prop="supplementQty" min-width="120" />
-        <el-table-column label="琛ユ枡鏃堕棿" prop="supplementTime" min-width="180" />
-        <el-table-column label="澶囨敞" prop="remark" min-width="200" />
+        <el-table-column label="琛ユ枡浜�" prop="supplementUserName" min-width="120" />
+        <el-table-column label="琛ユ枡鏃ユ湡" prop="supplementTime" min-width="160" />
+        <el-table-column label="琛ユ枡鍘熷洜" prop="supplementReason" min-width="200" />
       </el-table>
       <template #footer>
         <span class="dialog-footer">
@@ -54,29 +55,6 @@
         <el-table-column label="閫�鏂欐眹鎬绘暟閲�" prop="returnQtyTotal" min-width="140" />
       </el-table>
 
-      <el-card class="approver-card" shadow="never">
-        <template #header>
-          <div class="card-header-wrapper">
-            <span class="card-title">瀹℃壒浜洪�夋嫨</span>
-            <el-button type="primary" size="small" @click="addApproverNode">鏂板鑺傜偣</el-button>
-          </div>
-        </template>
-        <div class="approver-nodes-container">
-          <div v-for="(node, index) in approverNodes" :key="node.id" class="approver-node-item">
-            <div class="approver-node-label">
-              <span class="node-step">{{ index + 1 }}</span>
-              <span class="node-text">瀹℃壒浜�</span>
-            </div>
-            <el-select v-model="node.userId" placeholder="閫夋嫨浜哄憳" class="approver-select" clearable>
-              <el-option v-for="user in userList" :key="user.userId" :label="user.nickName" :value="user.userId" />
-            </el-select>
-            <el-button v-if="approverNodes.length > 1" type="danger" size="small" @click="removeApproverNode(index)">
-              鍒犻櫎
-            </el-button>
-          </div>
-        </div>
-      </el-card>
-
       <template #footer>
         <span class="dialog-footer">
           <el-button type="primary" :loading="materialReturnConfirming" @click="handleReturnConfirm">纭鎻愪氦</el-button>
@@ -91,7 +69,6 @@
 import { computed, ref, watch } from "vue";
 import { ElMessage } from "element-plus";
 import { listMaterialPickingDetail, listMaterialSupplementRecord, confirmMaterialReturn } from "@/api/productionManagement/productionOrder.js";
-import { userListNoPageByTenantId } from "@/api/system/user.js";
 
 const props = defineProps({
   modelValue: { type: Boolean, default: false },
@@ -112,10 +89,10 @@
 const supplementRecordTableData = ref([]);
 const returnSummaryDialogVisible = ref(false);
 const returnSummaryList = ref([]);
-const userList = ref([]);
-const approverNodes = ref([{ id: Date.now(), userId: undefined }]);
+const calcReturnQty = item =>
+  Number(item.pickQty || 0) + Number(item.supplementQty || 0) - Number(item.actualQty || 0);
 const canOpenReturnSummary = computed(() =>
-  materialDetailTableData.value.some(item => Number(item.returnQty || 0) > 0)
+  materialDetailTableData.value.some(item => calcReturnQty(item) > 0)
 );
 
 const loadDetailList = async () => {
@@ -159,6 +136,8 @@
 const buildReturnSummary = () => {
   const map = new Map();
   materialDetailTableData.value.forEach(item => {
+    const returnQty = calcReturnQty(item);
+    if (returnQty <= 0) return;
     const key = `${item.materialModelId || ""}_${item.materialName || ""}_${item.materialModel || ""}_${item.unit || ""}`;
     const old = map.get(key) || {
       summaryKey: key,
@@ -167,52 +146,28 @@
       unit: item.unit || "",
       returnQtyTotal: 0,
     };
-    old.returnQtyTotal += Number(item.returnQty || 0);
+    old.returnQtyTotal += returnQty;
     map.set(key, old);
   });
   return Array.from(map.values());
 };
 
-const loadUserList = async () => {
-  if (userList.value.length > 0) return;
-  const res = await userListNoPageByTenantId();
-  userList.value = res.data || [];
-};
-
 const openReturnSummaryDialog = async () => {
   if (!canOpenReturnSummary.value) {
-    ElMessage.warning("閫�鏂欐暟閲忓ぇ浜�0鏃舵墠鑳介��鏂欑‘璁�");
+    ElMessage.warning("閫�鏂欐暟閲�=棰嗙敤鏁伴噺+琛ユ枡鏁伴噺-瀹為檯鏁伴噺锛屼笖闇�澶т簬0");
     return;
   }
   returnSummaryList.value = buildReturnSummary();
-  approverNodes.value = [{ id: Date.now(), userId: undefined }];
-  await loadUserList();
   returnSummaryDialogVisible.value = true;
-};
-
-const addApproverNode = () => {
-  approverNodes.value.push({ id: Date.now() + Math.random(), userId: undefined });
-};
-
-const removeApproverNode = index => {
-  approverNodes.value.splice(index, 1);
 };
 
 const handleReturnConfirm = async () => {
   if (!props.orderRow?.id) return;
-  const approverList = approverNodes.value
-    .filter(item => item.userId)
-    .map((item, index) => ({ userId: item.userId, sort: index + 1 }));
-  if (approverList.length === 0) {
-    ElMessage.warning("璇疯嚦灏戦�夋嫨涓�浣嶅鎵逛汉");
-    return;
-  }
   materialReturnConfirming.value = true;
   try {
     await confirmMaterialReturn({
       orderId: props.orderRow.id,
       returnSummaryList: returnSummaryList.value,
-      approverList,
     });
     returnSummaryDialogVisible.value = false;
     dialogVisible.value = false;
@@ -223,42 +178,4 @@
 };
 </script>
 
-<style scoped lang="scss">
-.approver-card {
-  margin-top: 12px;
-}
-.card-header-wrapper {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-}
-.approver-nodes-container {
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-}
-.approver-node-item {
-  display: flex;
-  gap: 8px;
-  align-items: center;
-}
-.approver-node-label {
-  display: flex;
-  gap: 4px;
-  min-width: 88px;
-  align-items: center;
-}
-.node-step {
-  width: 20px;
-  height: 20px;
-  line-height: 20px;
-  text-align: center;
-  border-radius: 50%;
-  background: #409eff;
-  color: #fff;
-  font-size: 12px;
-}
-.approver-select {
-  flex: 1;
-}
-</style>
+<style scoped lang="scss"></style>
diff --git a/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue b/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
index f9db112..9e1a852 100644
--- a/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
+++ b/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
@@ -7,21 +7,24 @@
       <el-table v-loading="materialTableLoading" :data="materialTableData" border row-key="tempId">
         <el-table-column label="宸ュ簭鍚嶇О" min-width="180">
           <template #default="{ row }">
+            <span v-if="row.bom === true">{{ row.processName || "-" }}</span>
             <el-select
-              v-model="row.processId"
+              v-else
+              v-model="row.processName"
               placeholder="璇烽�夋嫨宸ュ簭"
               clearable
               filterable
               style="width: 100%;"
-              @change="val => handleProcessChange(row, val)"
+              @change="val => handleProcessNameChange(row, val)"
             >
-              <el-option v-for="item in processOptions" :key="item.id" :label="item.name" :value="item.id" />
+              <el-option v-for="item in processOptions" :key="item.id" :label="item.name" :value="item.name" />
             </el-select>
           </template>
         </el-table-column>
         <el-table-column label="鍘熸枡鍚嶇О" min-width="160">
           <template #default="{ row }">
-            <el-button type="primary" link @click="openMaterialProductSelect(row)">
+            <span v-if="row.bom === true">{{ row.materialName || "-" }}</span>
+            <el-button v-else type="primary" link @click="openMaterialProductSelect(row)">
               {{ row.materialName || "閫夋嫨鍘熸枡" }}
             </el-button>
           </template>
@@ -33,7 +36,9 @@
         </el-table-column>
         <el-table-column label="闇�姹傛暟閲�" min-width="120">
           <template #default="{ row }">
+            <span v-if="row.bom === true">{{ row.requiredQty ?? "-" }}</span>
             <el-input-number
+              v-else
               v-model="row.requiredQty"
               :min="0"
               :precision="3"
@@ -62,8 +67,8 @@
           </template>
         </el-table-column>
         <el-table-column label="鎿嶄綔" width="90" fixed="right">
-          <template #default="{ $index }">
-            <el-button type="danger" link @click="handleDeleteMaterialRow($index)">鍒犻櫎</el-button>
+          <template #default="{ $index, row }">
+            <el-button v-if="row.bom !== true" type="danger" link @click="handleDeleteMaterialRow($index)">鍒犻櫎</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -79,15 +84,21 @@
       v-model="materialProductDialogVisible"
       @confirm="handleMaterialProductConfirm"
       single
+      request-url="/stockInventory/rawMaterials"
     />
   </div>
 </template>
 
 <script setup>
 import { computed, ref, watch } from "vue";
+import { ElMessage } from "element-plus";
 import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-import { processList } from "@/api/productionManagement/productionProcess.js";
-import { listMaterialPickingLedger, saveMaterialPickingLedger } from "@/api/productionManagement/productionOrder.js";
+import { findProductProcessRouteItemList } from "@/api/productionManagement/productProcessRoute.js";
+import {
+  listMaterialPickingDetail,
+  listMaterialPickingLedger,
+  saveMaterialPickingLedger,
+} from "@/api/productionManagement/productionOrder.js";
 
 const props = defineProps({
   modelValue: { type: Boolean, default: false },
@@ -112,7 +123,9 @@
   tempId: row.id || `temp_${++materialTempId}`,
   id: row.id,
   processId: row.processId,
+  productProcessId: row.productProcessId || row.processId,
   processName: row.processName || "",
+  bom: row.bom === true,
   materialModelId: row.materialModelId,
   materialName: row.materialName || "",
   materialModel: row.materialModel || "",
@@ -122,9 +135,23 @@
 });
 
 const getProcessOptions = async () => {
-  if (processOptions.value.length > 0) return;
-  const res = await processList({});
-  processOptions.value = res.data || [];
+  if (!props.orderRow?.id) return;
+  const res = await findProductProcessRouteItemList({ orderId: props.orderRow.id });
+  const routeList = Array.isArray(res?.data) ? res.data : res?.data?.records || [];
+  const processMap = new Map();
+  routeList.forEach(item => {
+    const processId = item.processId;
+    const processName = item.processName;
+    if (!processId || !processName) return;
+    const key = `${processId}_${processName}`;
+    if (!processMap.has(key)) {
+      processMap.set(key, {
+        id: processId,
+        name: processName,
+      });
+    }
+  });
+  processOptions.value = Array.from(processMap.values());
 };
 
 const loadMaterialData = async () => {
@@ -133,8 +160,19 @@
   materialTableData.value = [];
   await getProcessOptions();
   try {
-    const res = await listMaterialPickingLedger({ orderId: props.orderRow.id });
-    materialTableData.value = (res.data || []).map(item => createMaterialRow(item));
+    const detailRes = await listMaterialPickingDetail({ orderId: props.orderRow.id });
+    const detailList = Array.isArray(detailRes?.data)
+      ? detailRes.data
+      : detailRes?.data?.records || [];
+    if (detailList.length > 0) {
+      materialTableData.value = detailList.map(item => createMaterialRow(item));
+      return;
+    }
+    const ledgerRes = await listMaterialPickingLedger({ orderId: props.orderRow.id });
+    const ledgerList = Array.isArray(ledgerRes?.data)
+      ? ledgerRes.data
+      : ledgerRes?.data?.records || [];
+    materialTableData.value = ledgerList.map(item => createMaterialRow(item));
   } finally {
     materialTableLoading.value = false;
   }
@@ -162,9 +200,9 @@
   materialTableData.value.splice(index, 1);
 };
 
-const handleProcessChange = (row, processId) => {
-  const process = processOptions.value.find(item => item.id === processId);
-  row.processName = process?.name || "";
+const handleProcessNameChange = (row, processName) => {
+  const process = processOptions.value.find(item => item.name === processName);
+  row.productProcessId = process?.id;
 };
 
 const handleRequiredQtyChange = (row, val) => {
@@ -186,37 +224,56 @@
   if (index < 0 || !materialTableData.value[index]) return;
   const product = products[0];
   const row = materialTableData.value[index];
-  row.materialModelId = product.id;
-  row.materialName = product.productName || "";
-  row.materialModel = product.model || "";
-  row.unit = product.unit || "";
+  row.materialModelId = product.materialModelId || product.modelId || product.id;
+  row.materialName = product.materialName || product.productName || product.name || "";
+  row.materialModel = product.materialModel || product.model || "";
+  row.unit = product.unit || product.measureUnit || "";
   currentMaterialSelectRowIndex.value = -1;
   materialProductDialogVisible.value = false;
 };
 
 const validateMaterialRows = () => {
-  if (materialTableData.value.length === 0) return false;
-  return !materialTableData.value.find(
+  if (materialTableData.value.length === 0) {
+    return { valid: false, message: "璇峰厛鏂板棰嗘枡鏁版嵁" };
+  }
+  const invalidNewRow = materialTableData.value.find(
+    item => item.bom !== true && (!item.processName || !item.materialName)
+  );
+  if (invalidNewRow) {
+    return { valid: false, message: "鏂板琛岀殑宸ュ簭鍚嶇О鍜屽師鏂欏悕绉颁负蹇呭~椤�" };
+  }
+  const invalidRow = materialTableData.value.find(
     item =>
-      !item.processId ||
-      !item.materialModelId ||
+      !item.processName ||
+      !item.materialName ||
       item.requiredQty === null ||
       item.requiredQty === undefined ||
       item.pickQty === null ||
       item.pickQty === undefined
   );
+  if (invalidRow) {
+    return { valid: false, message: "璇峰畬鍠勫伐搴忋�佸師鏂欏拰鏁伴噺鍚庡啀淇濆瓨" };
+  }
+  return { valid: true, message: "" };
 };
 
 const handleMaterialSave = async () => {
-  if (!props.orderRow?.id || !validateMaterialRows()) return;
+  if (!props.orderRow?.id) return;
+  const validateResult = validateMaterialRows();
+  if (!validateResult.valid) {
+    ElMessage.warning(validateResult.message);
+    return;
+  }
   materialSaving.value = true;
   try {
     await saveMaterialPickingLedger({
       orderId: props.orderRow.id,
       items: materialTableData.value.map(item => ({
         id: item.id,
-        processId: item.processId,
+        processId: item.processName,
+        productProcessId: item.productProcessId,
         processName: item.processName,
+        bom: item.bom === true,
         materialModelId: item.materialModelId,
         materialName: item.materialName,
         materialModel: item.materialModel,
diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index 8d4a8aa..667688e 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -48,7 +48,7 @@
                      @click="handleQuery">鎼滅储</el-button>
         </el-form-item>
       </el-form>
-      <div>
+      <div class="action-buttons">
         <el-button type="primary" @click="isShowNewModal = true">鏂板</el-button>
         <el-button type="danger" @click="handleDelete">鍒犻櫎</el-button>
         <el-button @click="handleOut">瀵煎嚭</el-button>
@@ -224,13 +224,13 @@
             openBindRouteDialog(row);
           },
         },
-        {
-          name: "浜у搧缁撴瀯",
-          type: "text",
-          clickFun: row => {
-            showProductStructure(row);
-          },
-        },
+        // {
+        //   name: "浜у搧缁撴瀯",
+        //   type: "text",
+        //   clickFun: row => {
+        //     showProductStructure(row);
+        //   },
+        // },
         {
           name: "棰嗘枡",
           type: "text",
@@ -421,6 +421,7 @@
         path: "/productionManagement/processRouteItem",
         query: {
           id: data.id,
+          bomId: data.bomId,
           processRouteCode: data.processRouteCode || "",
           productName: data.productName || "",
           model: data.model || "",
@@ -504,6 +505,12 @@
   align-items: start;
 }
 
+.action-buttons {
+  display: flex;
+  flex-wrap: nowrap;
+  gap: 8px;
+}
+
 :deep(.yellow) {
   background-color: #FAF0DE;
 }
diff --git a/src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue b/src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue
index e5eee56..575d888 100644
--- a/src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue
+++ b/src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue
@@ -11,18 +11,33 @@
         <el-table-column label="鍘熸枡鍚嶇О" prop="materialName" min-width="140" />
         <el-table-column label="鍘熸枡鍨嬪彿" prop="materialModel" min-width="140" />
         <el-table-column label="璁¢噺鍗曚綅" prop="unit" min-width="100" />
-        <el-table-column label="棰嗙敤鏁伴噺" prop="pickQty" min-width="100" />
+        <el-table-column label="绾胯竟浠撴暟閲�" prop="pickQty" min-width="100" />
         <el-table-column label="琛ユ枡鏁伴噺" prop="supplementQty" min-width="100" />
-        <el-table-column label="閫�鏂欐暟閲�" prop="returnQty" min-width="100" />
-        <el-table-column label="瀹為檯鏁伴噺" prop="actualQty" min-width="100" />
-        <el-table-column label="鎿嶄綔" align="center" fixed="right" width="220">
+        <el-table-column label="瀹為檯鏁伴噺" min-width="140">
+          <template #default="{ row }">
+            <el-input-number
+              v-model="row.actualQty"
+              :min="0"
+              :precision="3"
+              :step="1"
+              controls-position="right"
+              style="width: 100%;"
+            />
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" align="center" fixed="right" width="180">
           <template #default="{ row }">
             <el-button type="primary" link @click="openSupplementDialog(row)">琛ユ枡</el-button>
-            <el-button type="warning" link @click="openReturnDialog(row)">閫�鏂�</el-button>
             <el-button type="info" link @click="openSupplementRecordDialog(row)">琛ユ枡璁板綍</el-button>
           </template>
         </el-table-column>
       </el-table>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" :loading="pickSubmitting" @click="handleSubmitPick">棰嗙敤</el-button>
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
     </el-dialog>
 
     <FormDialog
@@ -60,31 +75,6 @@
       </template>
     </FormDialog>
 
-    <FormDialog
-      v-model="returnDialogVisible"
-      title="閫�鏂�"
-      width="500px"
-      @confirm="handleSubmitReturn"
-    >
-      <el-form ref="returnFormRef" :model="returnForm" :rules="returnRules" label-width="120px">
-        <el-form-item label="閫�鏂欐暟閲�" prop="returnQty">
-          <el-input-number
-            v-model="returnForm.returnQty"
-            :min="0.001"
-            :precision="3"
-            :step="1"
-            style="width: 100%;"
-          />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button type="primary" :loading="returnSubmitting" @click="handleSubmitReturn">纭畾</el-button>
-          <el-button @click="returnDialogVisible = false">鍙栨秷</el-button>
-        </span>
-      </template>
-    </FormDialog>
-
     <el-dialog v-model="supplementRecordDialogVisible" title="琛ユ枡璁板綍" width="900px">
       <el-table v-loading="supplementRecordLoading" :data="supplementRecordTableData" border row-key="id">
         <el-table-column label="琛ユ枡鏁伴噺" prop="supplementQty" min-width="100" />
@@ -108,8 +98,8 @@
 import {
   listWorkOrderMaterialLedger,
   addWorkOrderMaterialSupplement,
-  addWorkOrderMaterialReturn,
   listWorkOrderMaterialSupplementRecord,
+  pickWorkOrderMaterial,
 } from "@/api/productionManagement/workOrder.js";
 
 const props = defineProps({
@@ -134,6 +124,7 @@
 const materialTableData = ref([]);
 const currentMaterialRow = ref(null);
 const currentMaterialOrderRow = ref(null);
+const pickSubmitting = ref(false);
 
 const supplementDialogVisible = ref(false);
 const supplementSubmitting = ref(false);
@@ -141,13 +132,6 @@
 const supplementForm = reactive({
   supplementQty: null,
   supplementReason: "",
-});
-
-const returnDialogVisible = ref(false);
-const returnSubmitting = ref(false);
-const returnFormRef = ref(null);
-const returnForm = reactive({
-  returnQty: null,
 });
 
 const supplementRecordDialogVisible = ref(false);
@@ -158,10 +142,6 @@
   supplementQty: [{ required: true, message: "璇疯緭鍏ヨˉ鏂欐暟閲�", trigger: "blur" }],
   supplementReason: [{ required: true, message: "璇疯緭鍏ヨˉ鏂欏師鍥�", trigger: "blur" }],
 };
-const returnRules = {
-  returnQty: [{ required: true, message: "璇疯緭鍏ラ��鏂欐暟閲�", trigger: "blur" }],
-};
-
 const loadMaterialTable = async row => {
   if (!row?.id) return;
   currentMaterialOrderRow.value = row;
@@ -234,49 +214,6 @@
   });
 };
 
-const openReturnDialog = row => {
-  currentMaterialRow.value = row;
-  returnForm.returnQty = null;
-  returnDialogVisible.value = true;
-  nextTick(() => {
-    returnFormRef.value?.clearValidate();
-  });
-};
-
-const handleSubmitReturn = () => {
-  returnFormRef.value?.validate(async valid => {
-    if (!valid || !currentMaterialRow.value?.id) {
-      ElMessage.warning("缂哄皯鐗╂枡鏄庣粏ID");
-      return;
-    }
-    const returnQty = Number(returnForm.returnQty);
-    const minQty =
-      Number(currentMaterialRow.value.pickQty || 0) +
-      Number(currentMaterialRow.value.supplementQty || 0);
-    if (returnQty < minQty) {
-      ElMessage.warning(`閫�鏂欐暟閲忎笉鑳戒綆浜庨鐢ㄦ暟閲�+琛ユ枡鏁伴噺锛�${minQty}锛塦);
-      return;
-    }
-    returnSubmitting.value = true;
-    try {
-      await addWorkOrderMaterialReturn({
-        materialLedgerId: currentMaterialRow.value.id,
-        returnQty,
-        workOrderId: currentMaterialOrderRow.value?.id,
-      });
-      returnDialogVisible.value = false;
-      await loadMaterialTable(currentMaterialOrderRow.value);
-      ElMessage.success("閫�鏂欐垚鍔�");
-      emit("refresh");
-    } catch (e) {
-      console.error("閫�鏂欏け璐�", e);
-      ElMessage.error("閫�鏂欏け璐�");
-    } finally {
-      returnSubmitting.value = false;
-    }
-  });
-};
-
 const openSupplementRecordDialog = async row => {
   supplementRecordDialogVisible.value = true;
   supplementRecordLoading.value = true;
@@ -293,4 +230,49 @@
     supplementRecordLoading.value = false;
   }
 };
+
+const validatePickRows = () => {
+  if (materialTableData.value.length === 0) {
+    return { valid: false, message: "鏆傛棤鍙鐢ㄧ墿鏂�" };
+  }
+  const invalidRow = materialTableData.value.find(item => item.actualQty === null || item.actualQty === undefined || item.actualQty === "");
+  if (invalidRow) {
+    return { valid: false, message: "璇峰~鍐欏疄闄呮暟閲忓悗鍐嶉鐢�" };
+  }
+  const exceedRow = materialTableData.value.find(item => {
+    const maxQty = Number(item.pickQty || 0) + Number(item.supplementQty || 0);
+    return Number(item.actualQty || 0) > maxQty;
+  });
+  if (exceedRow) {
+    return { valid: false, message: "瀹為檯鏁伴噺涓嶈兘澶т簬棰嗙敤鏁伴噺+琛ユ枡鏁伴噺" };
+  }
+  return { valid: true, message: "" };
+};
+
+const handleSubmitPick = async () => {
+  if (!currentMaterialOrderRow.value?.id) return;
+  const validateResult = validatePickRows();
+  if (!validateResult.valid) {
+    ElMessage.warning(validateResult.message);
+    return;
+  }
+  pickSubmitting.value = true;
+  try {
+    await pickWorkOrderMaterial({
+      workOrderId: currentMaterialOrderRow.value.id,
+      items: materialTableData.value.map(item => ({
+        materialLedgerId: item.id,
+        actualQty: Number(item.actualQty || 0),
+      })),
+    });
+    ElMessage.success("棰嗙敤鎴愬姛");
+    await loadMaterialTable(currentMaterialOrderRow.value);
+    emit("refresh");
+  } catch (e) {
+    console.error("棰嗙敤澶辫触", e);
+    ElMessage.error("棰嗙敤澶辫触");
+  } finally {
+    pickSubmitting.value = false;
+  }
+};
 </script>
diff --git a/src/views/productionManagement/workOrderManagement/index.vue b/src/views/productionManagement/workOrderManagement/index.vue
index 9d18813..48f8839 100644
--- a/src/views/productionManagement/workOrderManagement/index.vue
+++ b/src/views/productionManagement/workOrderManagement/index.vue
@@ -289,17 +289,17 @@
           },
         },
         {
+          name: "鐗╂枡",
+          clickFun: row => {
+            openMaterialDialog(row);
+          },
+        },
+        {
           name: "鎶ュ伐",
           clickFun: row => {
             showReportDialog(row);
           },
           disabled: row => row.planQuantity <= 0,
-        },
-        {
-          name: "鐗╂枡",
-          clickFun: row => {
-            openMaterialDialog(row);
-          },
         },
       ],
     },
diff --git a/src/views/salesManagement/returnOrder/components/formDia.vue b/src/views/salesManagement/returnOrder/components/formDia.vue
index 9f1bb9a..27bbb0d 100644
--- a/src/views/salesManagement/returnOrder/components/formDia.vue
+++ b/src/views/salesManagement/returnOrder/components/formDia.vue
@@ -168,10 +168,10 @@
 <script setup>
 import { reactive, ref, toRefs, getCurrentInstance } from "vue";
 import { returnManagementAdd, returnManagementUpdate, returnManagementGetByShippingId, getSalesLedger, returnManagementGetById } from "@/api/salesManagement/returnOrder.js";
-import { getAllCustomerList } from "@/api/customerService/index.js";
 import useUserStore from "@/store/modules/user.js";
 import { userListNoPageByTenantId } from "@/api/system/user.js";
 import { listProject } from "@/api/oaSystem/projectManagement.js";
+import {listCustomerPrivatePool} from "@/api/basicData/customerFile.js";
 
 const { proxy } = getCurrentInstance();
 const emit = defineEmits(['close'])
@@ -354,15 +354,14 @@
 };
 
 const initCustomers = async () => {
-  const res = await getAllCustomerList({});
-  if (res?.records) {
-    customerNameOptions.value = res.records.map(item => ({
-      label: item.customerName,
-      value: item.customerName, // Keep value as name if needed for other logic, but request says customerId
-      id: item.id,
-      code: item.customerCode
-    }));
-  }
+	listCustomerPrivatePool({current: -1,size:-1}).then((res) => {
+		customerNameOptions.value = res.data.records.map(item => ({
+			label: item.customerName,
+			value: item.customerName, // Keep value as name if needed for other logic, but request says customerId
+			id: item.id,
+			code: item.customerCode
+		}));
+	});
 };
 
 const initUsers = async () => {
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index ca19f29..7c9ebf8 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -42,7 +42,7 @@
         <el-table-column align="center" type="selection" width="55" fixed="left"/>
         <el-table-column type="expand" width="60" fixed="left">
           <template #default="props">
-            <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable">
+            <el-table :data="props.row.children" border show-summary :summary-method="(param) => summarizeChildrenTable(param, props.row)">
               <el-table-column align="center" label="搴忓彿" type="index"/>
               <el-table-column label="浜у搧澶х被" prop="productCategory" />
               <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
@@ -89,9 +89,9 @@
               </el-table-column>
               <el-table-column label="鏁伴噺" prop="quantity" />
               <el-table-column label="绋庣巼(%)" prop="taxRate" />
-              <el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
-              <el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
-              <el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
+              <el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" :formatter="sensitiveAmountFormatter" />
+              <el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="sensitiveAmountFormatter" />
+              <el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="sensitiveAmountFormatter" />
             <!--鎿嶄綔-->
               <el-table-column Width="60px" label="鎿嶄綔" align="center">
                 <template #default="scope">
@@ -122,7 +122,7 @@
         <el-table-column label="澶囨敞" prop="remarks" width="200" show-overflow-tooltip />
         <el-table-column fixed="right" label="鎿嶄綔" width="130" align="center">
           <template #default="scope">
-            <el-button link type="primary" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit || scope.row.hasProductionRecord">缂栬緫</el-button>
+            <el-button link type="primary" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit || scope.row.hasProductionRecord || !canEditLedger(scope.row)">缂栬緫</el-button>
             <el-button link type="primary" @click="downLoadFile(scope.row)">闄勪欢</el-button>
           </template>
         </el-table-column>
@@ -149,7 +149,7 @@
           </el-col>
           <el-col :span="12">
             <el-form-item label="涓氬姟鍛橈細" prop="salesman">
-              <el-select v-model="form.salesman" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'view'">
+              <el-select v-model="form.salesman" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'view'" filterable>
                 <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                   :value="item.nickName" />
               </el-select>
@@ -159,7 +159,7 @@
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="瀹㈡埛鍚嶇О锛�" prop="customerId">
-              <el-select v-model="form.customerId" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'view'">
+              <el-select v-model="form.customerId" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'view'" filterable>
                 <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
                   {{
                     item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
@@ -343,11 +343,8 @@
 				<el-row :gutter="30">
 					<el-col :span="24">
 						<el-form-item label="浜у搧澶х被锛�" prop="productCategory">
-							<!-- <el-select v-model="productForm.productCategory" placeholder="璇烽�夋嫨" clearable>
-								<el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
-							</el-select> -->
-							<el-tree-select v-model="productForm.productCategory" placeholder="璇烽�夋嫨" clearable check-strictly
-															@change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
+							<el-tree-select v-model="productForm.productCategory" placeholder="璇烽�夋嫨" clearable filterable check-strictly
+							                @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
 						</el-form-item>
 					</el-col>
 				</el-row>
@@ -677,6 +674,7 @@
 import useFormData from "@/hooks/useFormData.js";
 import dayjs from "dayjs";
 import { getCurrentDate } from "@/utils/index.js";
+import {listCustomerPrivatePool} from "@/api/basicData/customerFile.js";
 
 const userStore = useUserStore();
 const { proxy } = getCurrentInstance();
@@ -926,7 +924,49 @@
 	});
 };
 const formattedNumber = (row, column, cellValue) => {
+	if (cellValue === undefined || cellValue === null || cellValue === "") {
+		return "0.00";
+	}
 	return parseFloat(cellValue).toFixed(2);
+};
+const findLedgerRecordByRow = (row) => {
+	if (!row) return null;
+	if (
+		row.maintainer !== undefined ||
+		row.maintainerName !== undefined ||
+		row.entryPerson !== undefined ||
+		row.entryPersonName !== undefined
+	) {
+		return row;
+	}
+	if (row.salesLedgerId !== undefined && row.salesLedgerId !== null) {
+		return tableData.value.find((item) => String(item.id) === String(row.salesLedgerId)) || null;
+	}
+	return null;
+};
+const isCurrentUserMaintainer = (row) => {
+	const ledgerRecord = findLedgerRecordByRow(row);
+	if (!ledgerRecord) return true;
+	const currentUserId = String(userStore.id ?? "");
+	const currentNickName = String(userStore.nickName ?? "").trim();
+	const maintainerId = ledgerRecord.maintainerId ?? ledgerRecord.entryPerson;
+	const maintainerName =
+		ledgerRecord.maintainerName ?? ledgerRecord.maintainer ?? ledgerRecord.entryPersonName;
+	if (maintainerId !== undefined && maintainerId !== null && String(maintainerId) !== "") {
+		return String(maintainerId) === currentUserId;
+	}
+	if (maintainerName !== undefined && maintainerName !== null && String(maintainerName).trim() !== "") {
+		return String(maintainerName).trim() === currentNickName;
+	}
+	return true;
+};
+const canEditLedger = (row) => isCurrentUserMaintainer(row);
+const canDeleteLedger = (row) => isCurrentUserMaintainer(row);
+const sensitiveAmountFormatter = (row, column, cellValue) => {
+	if (!isCurrentUserMaintainer(row)) {
+		return "*****";
+	}
+	return formattedNumber(row, column, cellValue);
 };
 // 鑾峰彇tree瀛愭暟鎹�
 const getModels = (value) => {
@@ -1041,7 +1081,19 @@
 	]);
 };
 // 瀛愯〃鍚堣鏂规硶
-const summarizeChildrenTable = (param) => {
+const summarizeChildrenTable = (param, parentRow) => {
+	if (!isCurrentUserMaintainer(parentRow)) {
+		const { columns } = param;
+		return columns.map((column, index) => {
+			if (index === 0) {
+				return "鍚堣";
+			}
+			if (["taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice"].includes(column.property)) {
+				return "*****";
+			}
+			return "";
+		});
+	}
 	return proxy.summarizeTable(param, [
 		"taxInclusiveUnitPrice",
 		"taxInclusiveTotalPrice",
@@ -1050,14 +1102,18 @@
 };
 // 鎵撳紑寮规
 const openForm = async (type, row) => {
+	if (type === "edit" && row && !canEditLedger(row)) {
+		proxy.$modal.msgWarning("褰撳墠绯荤粺鐧诲綍浜轰笉鏄淮鎶や汉锛屼笉鑳界紪杈戞暟鎹�");
+		return;
+	}
 	operationType.value = type;
 	form.value = {};
 	productData.value = [];
 	selectedQuotation.value = null;
 	let userLists = await userListNoPage();
 	userList.value = userLists.data;
-	customerList().then((res) => {
-		customerOption.value = res;
+	listCustomerPrivatePool({current: -1,size:-1}).then((res) => {
+		customerOption.value = res.data.records;
 	});
 	form.value.entryPerson = userStore.id;
 	if (type === "add") {
@@ -1093,8 +1149,9 @@
 	// 鍏堢‘淇濆鎴峰垪琛ㄥ凡鍔犺浇锛屼究浜庡悗缁洖濉� customerId
 	if (!customerOption.value || customerOption.value.length === 0) {
 		try {
-			const res = await customerList();
-			customerOption.value = res;
+			listCustomerPrivatePool({current: -1,size:-1}).then((res) => {
+				customerOption.value = res.data.records;
+			});
 		} catch (e) {
 			// ignore锛屽厑璁哥敤鎴峰悗缁墜鍔ㄩ�夋嫨瀹㈡埛
 		}
@@ -1433,6 +1490,11 @@
 		proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
 		return;
 	}
+	const unauthorizedRows = selectedRows.value.filter((row) => !canDeleteLedger(row));
+	if (unauthorizedRows.length > 0) {
+		proxy.$modal.msgWarning("褰撳墠鐧诲綍鐢ㄦ埛涓嶆槸褰曞叆浜猴紝涓嶈兘鍒犻櫎璇ユ暟鎹�");
+		return;
+	}
 	const ids = selectedRows.value.map((item) => item.id);
 
 	// 妫�鏌ユ槸鍚︽湁宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曪紝鑻ユ湁鍒欎笉鍏佽鍒犻櫎
diff --git a/src/views/salesManagement/salesQuotation/index.vue b/src/views/salesManagement/salesQuotation/index.vue
index 5574bce..01174d7 100644
--- a/src/views/salesManagement/salesQuotation/index.vue
+++ b/src/views/salesManagement/salesQuotation/index.vue
@@ -103,18 +103,14 @@
             <el-row :gutter="24">
               <el-col :span="12">
                 <el-form-item label="瀹㈡埛鍚嶇О" prop="customer">
-                  <el-select v-model="form.customer" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%" @change="handleCustomerChange" clearable>
-                    <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName">
-                      {{
-                        item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
-                      }}
-                    </el-option>
+                  <el-select v-model="form.customer" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%" clearable filterable>
+                    <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id"></el-option>
                   </el-select>
                 </el-form-item>
               </el-col>
               <el-col :span="12">
                 <el-form-item label="涓氬姟鍛�" prop="salesperson">
-                  <el-select v-model="form.salesperson" placeholder="璇烽�夋嫨涓氬姟鍛�" style="width: 100%" clearable>
+                  <el-select v-model="form.salesperson" placeholder="璇烽�夋嫨涓氬姟鍛�" style="width: 100%" clearable filterable>
                     <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                       :value="item.nickName" />
                   </el-select>
@@ -190,6 +186,7 @@
                         v-model="node.userId"
                         placeholder="閫夋嫨浜哄憳"
                         class="approver-select"
+												filterable
                         clearable
                       >
                         <el-option
@@ -363,6 +360,7 @@
 import {userListNoPage} from "@/api/system/user.js";
 import {customerList} from "@/api/salesManagement/salesLedger.js";
 import {modelList, productTreeList} from "@/api/basicData/product.js";
+import {listCustomerPrivatePool} from "@/api/basicData/customerFile.js";
 
 // 鍝嶅簲寮忔暟鎹�
 const loading = ref(false)
@@ -492,13 +490,8 @@
     userName: item.userName || ''
   }));
 	getProductOptions();
-	customerList().then((res) => {
-		// 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
-		customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
-      id: item.id,
-      customerName: item.customerName || '',
-      taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || ''
-    }))
+	listCustomerPrivatePool({current: -1,size:-1}).then((res) => {
+		customerOption.value = res.data.records;
 	});
 }
 const getProductOptions = () => {
@@ -783,10 +776,6 @@
   form.totalAmount = form.subtotal + form.freight + form.otherFee - form.discountAmount
 }
 
-const handleCustomerChange = () => {
-  // 鍙互鏍规嵁瀹㈡埛淇℃伅鑷姩濉厖涓�浜涢粯璁ゅ��
-}
-
 const handleSubmit = () => {
   formRef.value.validate((valid) => {
     if (valid) {
@@ -889,12 +878,7 @@
     }
   })
 	customerList().then((res) => {
-		// 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
-		customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
-      id: item.id,
-      customerName: item.customerName || '',
-      taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || ''
-    }))
+		customerOption.value = res;
 	});
 }
 

--
Gitblit v1.9.3