From 913e7cd145459ca10e80392819aa052454927103 Mon Sep 17 00:00:00 2001
From: ZN <zhang_12370@163.com>
Date: 星期三, 18 三月 2026 17:56:34 +0800
Subject: [PATCH] feat: 新增销售合同导出功能并增强多个模块交互

---
 src/views/basicData/customerFile/index.vue |  462 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 440 insertions(+), 22 deletions(-)

diff --git a/src/views/basicData/customerFile/index.vue b/src/views/basicData/customerFile/index.vue
index 1437beb..4e9f87a 100644
--- a/src/views/basicData/customerFile/index.vue
+++ b/src/views/basicData/customerFile/index.vue
@@ -112,6 +112,14 @@
         </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
+            <el-form-item label="寮�鎴烽摱琛岋細"
+                          prop="bankName">
+              <el-input v-model="form.bankName"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
             <el-form-item label="寮�鎴疯鍙凤細"
                           prop="bankCode">
               <el-input v-model="form.bankCode"
@@ -119,6 +127,8 @@
                         clearable />
             </el-form-item>
           </el-col>
+        </el-row>
+        <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="瀹㈡埛鍒嗙被锛�"
                           prop="customerType">
@@ -130,6 +140,47 @@
                 <el-option label="杩涢攢鍟嗗鎴�"
                            value="杩涢攢鍟嗗鎴�" />
               </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="娉曚汉"
+                          prop="corporation">
+              <el-input v-model="form.corporation"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="浠g悊浜�"
+                          prop="agent">
+              <el-select v-model="form.agent"
+                         placeholder="璇烽�夋嫨浠g悊浜�"
+                         clearable
+                         filterable
+                         :disabled="agentOptions.length === 0"
+                         :no-data-text="agentNoDataText">
+                <el-option v-for="(contact, index) in agentOptions"
+                           :key="getAgentOptionKey(contact, index)"
+                           :label="getAgentLabel(contact)"
+                           :value="contact.contactPhone"
+                           :disabled="!contact.contactPhone">
+                  <span>{{ contact.contactPerson || "-" }}</span>
+                  <span style="float: right; color: var(--el-text-color-secondary); font-size: 12px;">
+                    {{ contact.contactPhone || "" }}
+                  </span>
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="浼犵湡"
+                          prop="fax">
+              <el-input v-model="form.fax"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
             </el-form-item>
           </el-col>
         </el-row>
@@ -267,9 +318,9 @@
         <el-form-item label="鎻愰啋鏃堕棿锛�"
                       prop="reminderTime">
           <el-date-picker v-model="reminderForm.reminderTime"
-                          type="date"
-                          value-format="YYYY-MM-DD"
-                          format="YYYY-MM-DD"
+                          type="datetime"
+                          value-format="YYYY-MM-DD HH:mm:ss"
+                          format="YYYY-MM-DD HH:mm:ss"
                           placeholder="璇烽�夋嫨鎻愰啋鏃堕棿"
                           style="width: 100%" />
         </el-form-item>
@@ -369,7 +420,7 @@
     <!-- 瀹㈡埛璇︽儏瀵硅瘽妗� -->
     <el-dialog title="瀹㈡埛璇︽儏"
                v-model="detailDialogVisible"
-               width="800px"
+               width="1000px"
                @close="closeDetailDialog">
       <!-- 瀹㈡埛鍩烘湰淇℃伅 -->
       <div class="detail-section">
@@ -418,12 +469,7 @@
             </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>
@@ -481,14 +527,29 @@
                            label="璺熻繘鏂瑰紡"
                            width="100" />
           <el-table-column prop="followUpLevel"
-                           label="璺熻繘绋嬪害"
-                           width="120" />
+                           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
+                         size="small"
+                         @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">
@@ -519,12 +580,90 @@
         </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
+                           size="small"
+                           @click="downloadAttachment(row)">
+                  涓嬭浇
+                </el-button>
+                <el-button type="danger"
+                           link
+                           size="small"
+                           @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 } from "@element-plus/icons-vue";
+  import { onMounted, ref, reactive, getCurrentInstance, toRefs, computed, watch } from "vue";
+  import { Search, Paperclip, Upload } from "@element-plus/icons-vue";
   import {
     addCustomer,
     delCustomer,
@@ -534,6 +673,8 @@
     addCustomerFollow,
     updateCustomerFollow,
     delCustomerFollow,
+    addReturnVisit,
+    getReturnVisit,
   } from "@/api/basicData/customerFile.js";
   import { ElMessageBox } from "element-plus";
   import { userListNoPage } from "@/api/system/user.js";
@@ -545,6 +686,7 @@
   // 鍥炶鎻愰啋鐩稿叧
   const reminderDialogVisible = ref(false);
   const reminderFormRef = ref();
+  const currentCustomerId = ref();
   const reminderForm = reactive({
     customerName: "",
     reminderSwitch: false,
@@ -594,6 +736,8 @@
     companyPhone: "",
     companyAddress: "",
     basicBankAccount: "",
+    corporation: "",
+    fax: "",
     bankAccount: "",
     bankCode: "",
     contactPerson: "",
@@ -602,6 +746,22 @@
     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([
     {
@@ -656,6 +816,16 @@
     {
       label: "寮�鎴疯鍙�",
       prop: "bankCode",
+      width: 220,
+    },
+    {
+      label: "娉曚汉浠h〃",
+      prop: "corporation",
+      width: 220,
+    },
+    {
+      label: "浼犵湡",
+      prop: "fax",
       width: 220,
     },
     {
@@ -744,6 +914,8 @@
       maintenanceTime: "",
       basicBankAccount: "",
       bankAccount: "",
+      fax: "",
+      corporation: "",
       bankCode: "",
       customerType: "",
     },
@@ -762,6 +934,9 @@
       ],
       basicBankAccount: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
       bankAccount: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      corporation: [{ required: true, message: "璇疯緭鍏ユ硶浜轰唬琛�", trigger: "blur" }],
+      agent: [{ required: true, message: "璇烽�夋嫨浠g悊浜�", trigger: "change" }],
+      bankName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
       bankCode: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
       customerType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
     },
@@ -822,6 +997,39 @@
     },
   });
   const { searchForm, form, rules } = toRefs(data);
+
+  const agentOptions = computed(() => {
+    const list = formYYs.value?.contactList || [];
+    return list.filter(item => item && (item.contactPerson || item.contactPhone));
+  });
+
+  const agentNoDataText = computed(() => {
+    if (agentOptions.value.length === 0) return "璇峰厛鏂板鑱旂郴浜�";
+    return "鏃犲尮閰嶈仈绯讳汉";
+  });
+
+  const getAgentLabel = contact => {
+    const person = (contact?.contactPerson || "").trim();
+    const phone = (contact?.contactPhone || "").trim();
+    if (person && phone) return `${person}锛�${phone}锛塦;
+    return person || phone || "-";
+  };
+
+  const getAgentOptionKey = (contact, index) => {
+    return contact?.contactPhone || contact?.contactPerson || index;
+  };
+
+  watch(
+    () => agentOptions.value.map(item => item.contactPhone),
+    phones => {
+      const val = form.value?.agent;
+      if (!val) return;
+      if (!phones.includes(val)) {
+        form.value.agent = "";
+      }
+    }
+  );
+
   const addNewContact = () => {
     formYYs.value.contactList.push({
       contactPerson: "",
@@ -1003,10 +1211,25 @@
 
   // 鎵撳紑鍥炶鎻愰啋寮圭獥
   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;
   };
@@ -1016,20 +1239,48 @@
     proxy.resetForm("reminderFormRef");
     reminderDialogVisible.value = false;
   };
+  const submitvalue = ref({});
 
   // 鎻愪氦鍥炶鎻愰啋
   const submitReminderForm = () => {
+    console.log("鎻愪氦鍥炶鎻愰啋鏁版嵁:", userStore.id, userStore);
     proxy.$refs.reminderFormRef.validate(valid => {
       if (valid) {
-        // 杩欓噷鍋囪涓�涓帴鍙f潵鎻愪氦鍥炶鎻愰啋鏁版嵁
-        // 瀹為檯椤圭洰涓渶瑕佹牴鎹悗绔帴鍙h繘琛岃皟鏁�
-        console.log("鎻愪氦鍥炶鎻愰啋鏁版嵁:", reminderForm);
+        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,
+          };
+        }
 
-        // 妯℃嫙鎺ュ彛璋冪敤
-        setTimeout(() => {
-          proxy.$modal.msgSuccess("鍥炶鎻愰啋璁剧疆鎴愬姛");
-          closeReminderDialog();
-        }, 1000);
+        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("璁剧疆澶辫触");
+          });
       }
     });
   };
@@ -1188,6 +1439,143 @@
       });
   };
 
+  // 鎵撳紑闄勪欢寮圭獥
+  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();
@@ -1255,4 +1643,34 @@
     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>

--
Gitblit v1.9.3