From 4b96a3812c141f06c767467a7f9e41940b4e8d03 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 24 六月 2026 18:42:13 +0800
Subject: [PATCH] 1.客户,销售,供应商,采购增加总合同号 2.采购的产品增加库存状态回显 3.出厂改成创建时间倒序 4.开发总合同号管理页面

---
 src/views/procurementManagement/procurementLedger/index.vue  |  115 ++
 src/views/basicData/customerFile/index.vue                   |  411 +++++++++-
 doc/purchase_master_contract_linkage.md                      |  345 +++++++++
 src/api/basicData/customerContract.js                        |   52 +
 src/views/salesManagement/returnOrder/components/formDia.vue |    4 
 src/views/salesManagement/salesLedger/index.vue              |  132 +++
 src/views/salesManagement/salesQuotation/index.vue           |   54 +
 doc/customer_contract_record.md                              |  264 +++++++
 doc/sales_master_contract_linkage.md                         |  216 +++++
 src/api/basicData/supplierContract.js                        |   52 +
 src/views/basicData/supplierManage/components/HomeTab.vue    |  529 ++++++++++---
 11 files changed, 1,934 insertions(+), 240 deletions(-)

diff --git a/doc/customer_contract_record.md b/doc/customer_contract_record.md
new file mode 100644
index 0000000..1c6ca4a
--- /dev/null
+++ b/doc/customer_contract_record.md
@@ -0,0 +1,264 @@
+# 瀹㈡埛妗f绉佹捣-鍚堝悓璁板綍
+
+## 娑夊強椤甸潰
+
+- 瀹㈡埛妗f锛堢娴凤級璇︽儏椤�
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | /basic/customer-contract/list/{customerId} | 鏌ヨ鍚堝悓璁板綍鍒楄〃 |
+| GET | /basic/customer-contract/{id} | 鑾峰彇鍚堝悓璁板綍璇︽儏 |
+| POST | /basic/customer-contract | 鏂板鍚堝悓璁板綍 |
+| PUT | /basic/customer-contract | 淇敼鍚堝悓璁板綍 |
+| DELETE | /basic/customer-contract/{id} | 鍒犻櫎鍚堝悓璁板綍 |
+
+**娉ㄦ剰锛氶檮浠朵娇鐢ㄧ郴缁熼�氱敤鎺ュ彛 `/basic/storage-attachment` 绠$悊**
+
+### 璇锋眰鍙傛暟
+
+**鏂板/淇敼鍚堝悓璁板綍锛�**
+
+```json
+{
+    "id": null,  // 淇敼鏃跺繀浼�
+    "customerId": 1,  // 瀹㈡埛妗fID锛堝繀濉級
+    "masterContractNo": "HT202606240001",  // 鎬诲悎鍚屽彿锛堝繀濉級
+    "entryDate": "2026-06-24 10:30:00",  // 褰曞叆鏃ユ湡锛堝繀濉級
+    "entryPerson": "寮犱笁",  // 褰曞叆浜哄鍚�
+    "entryPersonId": 1,  // 褰曞叆浜篒D
+    "remark": "澶囨敞淇℃伅",  // 澶囨敞
+    "storageBlobDTOs": [  // 闄勪欢鍒楄〃
+        { "id": 1 },
+        { "id": 2 }
+    ]
+}
+```
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| id | Long | 鍚� | 淇敼鏃跺繀浼� |
+| customerId | Long | 鏄� | 瀹㈡埛妗fID |
+| masterContractNo | String | 鏄� | 鎬诲悎鍚屽彿 |
+| entryDate | String | 鏄� | 褰曞叆鏃ユ湡锛坹yyy-MM-dd HH:mm:ss锛� |
+| entryPerson | String | 鍚� | 褰曞叆浜哄鍚� |
+| entryPersonId | Long | 鍚� | 褰曞叆浜篒D |
+| remark | String | 鍚� | 澶囨敞 |
+| storageBlobDTOs | List | 鍚� | 闄勪欢ID鍒楄〃 |
+
+**鍝嶅簲锛�**
+
+```json
+{
+    "code": 200,
+    "msg": "鎿嶄綔鎴愬姛",
+    "data": {
+        "id": 1,
+        "customerId": 1,
+        "masterContractNo": "HT202606240001",
+        "entryDate": "2026-06-24 10:30:00",
+        "entryPerson": "寮犱笁",
+        "entryPersonId": 1,
+        "remark": "澶囨敞淇℃伅",
+        "fileList": [
+            {
+                "id": 1,
+                "name": "鍚堝悓鎵弿浠�.pdf",
+                "previewURL": "xxx",
+                "downloadURL": "xxx",
+                "byteSize": 102400,
+                "contentType": "application/pdf"
+            }
+        ]
+    }
+}
+```
+
+## 鍓嶇淇敼鐐�
+
+### 1. 鍦ㄥ鎴锋。妗堬紙绉佹捣锛夎鎯呴〉娣诲姞鍚堝悓璁板綍Tab
+
+```html
+<el-tabs v-model="activeTab">
+  <!-- 鍏朵粬Tab -->
+  <el-tab-pane label="鍚堝悓璁板綍" name="contract">
+    <el-button type="primary" size="small" @click="handleAddContract">鏂板鍚堝悓璁板綍</el-button>
+    <el-table :data="contractList" style="width: 100%; margin-top: 15px">
+      <el-table-column prop="masterContractNo" label="鎬诲悎鍚屽彿" />
+      <el-table-column prop="entryDate" label="褰曞叆鏃ユ湡" width="180" />
+      <el-table-column prop="entryPerson" label="褰曞叆浜�" width="120" />
+      <el-table-column prop="remark" label="澶囨敞" show-overflow-tooltip />
+      <el-table-column label="闄勪欢" width="100">
+        <template #default="{ row }">
+          <el-link type="primary" v-if="row.fileList && row.fileList.length" @click="handleViewFiles(row)">
+            鏌ョ湅({{ row.fileList.length }})
+          </el-link>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔" width="150">
+        <template #default="{ row }">
+          <el-button type="text" size="small" @click="handleEditContract(row)">缂栬緫</el-button>
+          <el-button type="text" size="small" style="color: #f56c6c" @click="handleDeleteContract(row)">鍒犻櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </el-tab-pane>
+</el-tabs>
+```
+
+### 2. 鏂板/缂栬緫鍚堝悓璁板綍寮圭獥锛堜娇鐢ㄧ郴缁熼�氱敤涓婁紶缁勪欢锛�
+
+```html
+<el-dialog :title="contractDialogTitle" v-model="contractDialogVisible" width="600px">
+  <el-form :model="contractForm" :rules="contractRules" ref="contractFormRef" label-width="100px">
+    <el-form-item label="鎬诲悎鍚屽彿" prop="masterContractNo">
+      <el-input v-model="contractForm.masterContractNo" placeholder="璇疯緭鍏ユ�诲悎鍚屽彿" />
+    </el-form-item>
+    <el-form-item label="褰曞叆鏃ユ湡" prop="entryDate">
+      <el-date-picker
+        v-model="contractForm.entryDate"
+        type="datetime"
+        placeholder="閫夋嫨鏃ユ湡鏃堕棿"
+        value-format="yyyy-MM-dd HH:mm:ss"
+        style="width: 100%"
+      />
+    </el-form-item>
+    <el-form-item label="褰曞叆浜�" prop="entryPerson">
+      <el-input v-model="contractForm.entryPerson" placeholder="璇疯緭鍏ュ綍鍏ヤ汉" />
+    </el-form-item>
+    <el-form-item label="闄勪欢">
+      <!-- 浣跨敤绯荤粺灏佽鐨勬枃浠朵笂浼犵粍浠� -->
+      <file-upload
+        v-model="contractForm.storageBlobDTOs"
+        :record-type="'customer_contract_record'"
+        :record-id="contractForm.id"
+      />
+    </el-form-item>
+    <el-form-item label="澶囨敞">
+      <el-input v-model="contractForm.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+    </el-form-item>
+  </el-form>
+  <template #footer>
+    <el-button @click="contractDialogVisible = false">鍙栨秷</el-button>
+    <el-button type="primary" @click="submitContractForm">纭畾</el-button>
+  </template>
+</el-dialog>
+```
+
+### 3. data 鏁版嵁
+
+```js
+data() {
+  return {
+    // 鍚堝悓璁板綍鐩稿叧
+    contractList: [],
+    contractDialogVisible: false,
+    contractDialogTitle: '',
+    contractForm: {
+      id: null,
+      customerId: null,
+      masterContractNo: '',
+      entryDate: '',
+      entryPerson: '',
+      entryPersonId: null,
+      remark: '',
+      storageBlobDTOs: []  // 闄勪欢鍒楄〃锛屼娇鐢ㄧ郴缁熼�氱敤鏍煎紡
+    },
+    contractRules: {
+      masterContractNo: [{ required: true, message: '璇疯緭鍏ユ�诲悎鍚屽彿', trigger: 'blur' }],
+      entryDate: [{ required: true, message: '璇烽�夋嫨褰曞叆鏃ユ湡', trigger: 'change' }]
+    }
+  }
+}
+```
+
+### 4. 鏂规硶
+
+```js
+methods: {
+  // 鑾峰彇鍚堝悓璁板綍鍒楄〃锛堝惈闄勪欢锛�
+  async getContractList(customerId) {
+    const res = await this.$http.get(`/basic/customer-contract/list/${customerId}`)
+    if (res.code === 200) {
+      this.contractList = res.data
+    }
+  },
+
+  // 鏂板鍚堝悓璁板綍
+  handleAddContract() {
+    this.contractDialogTitle = '鏂板鍚堝悓璁板綍'
+    this.contractForm = {
+      id: null,
+      customerId: this.customerId,
+      masterContractNo: '',
+      entryDate: '',
+      entryPerson: '',
+      entryPersonId: null,
+      remark: '',
+      storageBlobDTOs: []
+    }
+    this.contractDialogVisible = true
+  },
+
+  // 缂栬緫鍚堝悓璁板綍
+  async handleEditContract(row) {
+    this.contractDialogTitle = '缂栬緫鍚堝悓璁板綍'
+    const res = await this.$http.get(`/basic/customer-contract/${row.id}`)
+    if (res.code === 200) {
+      this.contractForm = {
+        ...res.data,
+        storageBlobDTOs: res.data.fileList ? res.data.fileList.map(f => ({ id: f.id })) : []
+      }
+      this.contractDialogVisible = true
+    }
+  },
+
+  // 鍒犻櫎鍚堝悓璁板綍
+  async handleDeleteContract(row) {
+    await this.$confirm('纭畾鍒犻櫎璇ュ悎鍚岃褰曞悧锛�', '鎻愮ず', { type: 'warning' })
+    const res = await this.$http.delete(`/basic/customer-contract/${row.id}`)
+    if (res.code === 200) {
+      this.$message.success('鍒犻櫎鎴愬姛')
+      this.getContractList(this.customerId)
+    }
+  },
+
+  // 鎻愪氦鍚堝悓璁板綍琛ㄥ崟
+  async submitContractForm() {
+    await this.$refs.contractFormRef.validate()
+    const res = await this.$http.post('/basic/customer-contract', this.contractForm)
+    if (res.code === 200) {
+      this.$message.success('鎿嶄綔鎴愬姛')
+      this.contractDialogVisible = false
+      this.getContractList(this.customerId)
+    }
+  },
+
+  // 鏌ョ湅闄勪欢鍒楄〃
+  handleViewFiles(row) {
+    // 寮圭獥灞曠ず闄勪欢鍒楄〃锛屼娇鐢� row.fileList
+  }
+}
+```
+
+## 鍚庣鏂囦欢娓呭崟
+
+| 鏂囦欢璺緞 | 璇存槑 |
+|---------|------|
+| `src/main/java/com/ruoyi/basic/pojo/CustomerContractRecord.java` | 瀹炰綋绫� |
+| `src/main/java/com/ruoyi/basic/dto/CustomerContractRecordDto.java` | DTO锛堝惈闄勪欢鍒楄〃锛� |
+| `src/main/java/com/ruoyi/basic/mapper/CustomerContractRecordMapper.java` | Mapper鎺ュ彛 |
+| `src/main/resources/mapper/basic/CustomerContractRecordMapper.xml` | Mapper XML |
+| `src/main/java/com/ruoyi/basic/service/CustomerContractRecordService.java` | Service鎺ュ彛 |
+| `src/main/java/com/ruoyi/basic/service/impl/CustomerContractRecordServiceImpl.java` | Service瀹炵幇 |
+| `src/main/java/com/ruoyi/basic/controller/CustomerContractRecordController.java` | Controller |
+| `src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java` | 娣诲姞鏋氫妇鍊� `CUSTOMER_CONTRACT_RECORD` |
+
+## 娉ㄦ剰浜嬮」
+
+- 闄勪欢涓婁紶浣跨敤绯荤粺灏佽鐨� `StorageAttachment` 鏈嶅姟锛岄�氳繃 `record_type='customer_contract_record'` 鍜� `record_id` 鍏宠仈
+- 鍓嶇鍙娇鐢ㄩ」鐩皝瑁呯殑 `file-upload` 缁勪欢锛岀粍浠跺唴閮ㄨ皟鐢� `/basic/storage-attachment` 鎺ュ彛
+- 鍒犻櫎鍚堝悓璁板綍鏃讹紝鍚庣浼氳嚜鍔ㄥ悓姝ュ垹闄ゅ叧鑱旂殑闄勪欢
+- 鏂板鍚堝悓璁板綍鎴愬姛鍚庯紝杩斿洖鐨� `data.id` 涓烘柊澧炶褰曠殑ID
\ No newline at end of file
diff --git a/doc/purchase_master_contract_linkage.md b/doc/purchase_master_contract_linkage.md
new file mode 100644
index 0000000..e1cbce8
--- /dev/null
+++ b/doc/purchase_master_contract_linkage.md
@@ -0,0 +1,345 @@
+# 閲囪喘鍙拌处-鎬诲悎鍚屽彿鑱斿姩閫夋嫨鍙婇檮浠舵煡鐪�
+
+## 娑夊強椤甸潰
+
+- 渚涘簲鍟嗙鐞嗚鎯呴〉锛堟柊澧炲悎鍚岃褰昑ab锛�
+- 閲囪喘鍙拌处鍒楄〃椤�/鏂板缂栬緫椤�
+
+## 鏂板API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | /basic/supplier-contract/list/{supplierId} | 鏌ヨ渚涘簲鍟嗗悎鍚岃褰曞垪琛紙渚涗笅鎷夐�夋嫨锛� |
+| GET | /basic/supplier-contract/{id} | 鑾峰彇渚涘簲鍟嗗悎鍚岃褰曡鎯� |
+| POST | /basic/supplier-contract | 鏂板渚涘簲鍟嗗悎鍚岃褰� |
+| PUT | /basic/supplier-contract | 淇敼渚涘簲鍟嗗悎鍚岃褰� |
+| DELETE | /basic/supplier-contract/{id} | 鍒犻櫎渚涘簲鍟嗗悎鍚岃褰� |
+| GET | /basic/supplier-contract/files/byContractNo?masterContractNo=xxx | 鏍规嵁鎬诲悎鍚屽彿鏌ヨ闄勪欢 |
+
+## 鍓嶇淇敼鐐�
+
+### 1. 渚涘簲鍟嗙鐞嗚鎯呴〉 - 娣诲姞鍚堝悓璁板綍Tab
+
+```html
+<el-tabs v-model="activeTab">
+  <!-- 鍩烘湰淇℃伅Tab -->
+  <el-tab-pane label="鍩烘湰淇℃伅" name="basic">...</el-tab-pane>
+  <!-- 鍚堝悓璁板綍Tab -->
+  <el-tab-pane label="鍚堝悓璁板綍" name="contract">
+    <el-button type="primary" size="small" @click="handleAddContract">鏂板鍚堝悓璁板綍</el-button>
+    <el-table :data="contractList" style="width: 100%; margin-top: 15px">
+      <el-table-column prop="masterContractNo" label="鎬诲悎鍚屽彿" />
+      <el-table-column prop="entryDate" label="褰曞叆鏃ユ湡" width="180" />
+      <el-table-column prop="entryPerson" label="褰曞叆浜�" width="120" />
+      <el-table-column prop="remark" label="澶囨敞" show-overflow-tooltip />
+      <el-table-column label="闄勪欢" width="100">
+        <template #default="{ row }">
+          <el-link type="primary" v-if="row.fileList && row.fileList.length" @click="handleViewFiles(row)">
+            鏌ョ湅({{ row.fileList.length }})
+          </el-link>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔" width="150">
+        <template #default="{ row }">
+          <el-button type="text" size="small" @click="handleEditContract(row)">缂栬緫</el-button>
+          <el-button type="text" size="small" style="color: #f56c6c" @click="handleDeleteContract(row)">鍒犻櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </el-tab-pane>
+</el-tabs>
+```
+
+### 2. 鏂板/缂栬緫鍚堝悓璁板綍寮圭獥
+
+```html
+<el-dialog :title="contractDialogTitle" v-model="contractDialogVisible" width="600px">
+  <el-form :model="contractForm" :rules="contractRules" ref="contractFormRef" label-width="100px">
+    <el-form-item label="鎬诲悎鍚屽彿" prop="masterContractNo">
+      <el-input v-model="contractForm.masterContractNo" placeholder="璇疯緭鍏ユ�诲悎鍚屽彿" />
+    </el-form-item>
+    <el-form-item label="褰曞叆鏃ユ湡" prop="entryDate">
+      <el-date-picker
+        v-model="contractForm.entryDate"
+        type="datetime"
+        placeholder="閫夋嫨鏃ユ湡鏃堕棿"
+        value-format="yyyy-MM-dd HH:mm:ss"
+        style="width: 100%"
+      />
+    </el-form-item>
+    <el-form-item label="褰曞叆浜�" prop="entryPerson">
+      <el-input v-model="contractForm.entryPerson" placeholder="璇疯緭鍏ュ綍鍏ヤ汉" />
+    </el-form-item>
+    <el-form-item label="闄勪欢">
+      <file-upload
+        v-model="contractForm.storageBlobDTOs"
+        :record-type="'supplier_contract_record'"
+        :record-id="contractForm.id"
+      />
+    </el-form-item>
+    <el-form-item label="澶囨敞">
+      <el-input v-model="contractForm.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+    </el-form-item>
+  </el-form>
+  <template #footer>
+    <el-button @click="contractDialogVisible = false">鍙栨秷</el-button>
+    <el-button type="primary" @click="submitContractForm">纭畾</el-button>
+  </template>
+</el-dialog>
+```
+
+### 3. 閲囪喘鍙拌处鏂板/缂栬緫椤� - 渚涘簲鍟嗛�夋嫨鍚庤仈鍔ㄦ�诲悎鍚屽彿
+
+```html
+<el-form-item label="渚涘簲鍟�" prop="supplierId">
+  <el-select
+    v-model="form.supplierId"
+    placeholder="璇烽�夋嫨渚涘簲鍟�"
+    filterable
+    @change="handleSupplierChange"
+  >
+    <el-option
+      v-for="item in supplierList"
+      :key="item.id"
+      :label="item.supplierName"
+      :value="item.id"
+    />
+  </el-select>
+</el-form-item>
+
+<el-form-item label="鎬诲悎鍚屽彿" prop="masterContractNo">
+  <el-select
+    v-model="form.masterContractNo"
+    placeholder="璇峰厛閫夋嫨渚涘簲鍟�"
+    filterable
+    clearable
+    :disabled="!form.supplierId"
+  >
+    <el-option
+      v-for="item in contractList"
+      :key="item.id"
+      :label="item.masterContractNo"
+      :value="item.masterContractNo"
+    />
+  </el-select>
+</el-form-item>
+```
+
+### 4. 閲囪喘鍙拌处鍒楄〃椤� - 鎿嶄綔鏍忔坊鍔犳煡鐪嬫�诲悎鍚岄檮浠舵寜閽�
+
+```html
+<el-table-column label="鎿嶄綔" width="200">
+  <template #default="{ row }">
+    <el-button type="text" size="small" @click="handleEdit(row)">缂栬緫</el-button>
+    <el-button type="text" size="small" @click="handleDelete(row)">鍒犻櫎</el-button>
+    <el-button
+      type="text"
+      size="small"
+      v-if="row.masterContractNo"
+      @click="handleViewContractFiles(row)"
+    >鏌ョ湅闄勪欢</el-button>
+  </template>
+</el-table-column>
+```
+
+### 5. 鎬诲悎鍚岄檮浠舵煡鐪嬪脊绐�
+
+```html
+<el-dialog title="鎬诲悎鍚岄檮浠�" v-model="contractFileDialogVisible" width="600px">
+  <el-table :data="contractFileList" v-if="contractFileList.length">
+    <el-table-column prop="name" label="鏂囦欢鍚嶇О" />
+    <el-table-column prop="byteSize" label="鏂囦欢澶у皬" width="120">
+      <template #default="{ row }">
+        {{ formatFileSize(row.byteSize) }}
+      </template>
+    </el-table-column>
+    <el-table-column label="鎿嶄綔" width="150">
+      <template #default="{ row }">
+        <el-link type="primary" :href="row.previewURL" target="_blank">棰勮</el-link>
+        <el-link type="primary" :href="row.downloadURL" style="margin-left: 10px">涓嬭浇</el-link>
+      </template>
+    </el-table-column>
+  </el-table>
+  <el-empty v-else description="鏆傛棤闄勪欢" />
+</el-dialog>
+```
+
+### 6. data 鏁版嵁
+
+```js
+data() {
+  return {
+    // 鍚堝悓鍒楄〃锛堜緵涓嬫媺閫夋嫨锛�
+    contractList: [],
+    // 鍚堝悓璁板綍鍒楄〃锛堜緵搴斿晢璇︽儏椤碉級
+    contractRecordList: [],
+    contractDialogVisible: false,
+    contractDialogTitle: '',
+    contractForm: {
+      id: null,
+      supplierId: null,
+      masterContractNo: '',
+      entryDate: '',
+      entryPerson: '',
+      entryPersonId: null,
+      remark: '',
+      storageBlobDTOs: []
+    },
+    contractRules: {
+      masterContractNo: [{ required: true, message: '璇疯緭鍏ユ�诲悎鍚屽彿', trigger: 'blur' }],
+      entryDate: [{ required: true, message: '璇烽�夋嫨褰曞叆鏃ユ湡', trigger: 'change' }]
+    },
+    // 鎬诲悎鍚岄檮浠跺脊绐�
+    contractFileDialogVisible: false,
+    contractFileList: []
+  }
+}
+```
+
+### 7. 鏂规硶
+
+```js
+methods: {
+  // 渚涘簲鍟嗛�夋嫨鍙樺寲鏃讹紝鍔犺浇璇ヤ緵搴斿晢鐨勫悎鍚屽垪琛�
+  async handleSupplierChange(supplierId) {
+    this.form.masterContractNo = ''  // 娓呯┖鎬诲悎鍚屽彿
+    this.contractList = []
+    if (!supplierId) return
+    
+    const res = await this.$http.get(`/basic/supplier-contract/list/${supplierId}`)
+    if (res.code === 200) {
+      this.contractList = res.data
+    }
+  },
+
+  // 鑾峰彇渚涘簲鍟嗗悎鍚岃褰曞垪琛紙渚涘簲鍟嗚鎯呴〉锛�
+  async getContractList(supplierId) {
+    const res = await this.$http.get(`/basic/supplier-contract/list/${supplierId}`)
+    if (res.code === 200) {
+      this.contractRecordList = res.data
+    }
+  },
+
+  // 鏂板鍚堝悓璁板綍
+  handleAddContract() {
+    this.contractDialogTitle = '鏂板鍚堝悓璁板綍'
+    this.contractForm = {
+      id: null,
+      supplierId: this.supplierId,
+      masterContractNo: '',
+      entryDate: '',
+      entryPerson: '',
+      entryPersonId: null,
+      remark: '',
+      storageBlobDTOs: []
+    }
+    this.contractDialogVisible = true
+  },
+
+  // 缂栬緫鍚堝悓璁板綍
+  async handleEditContract(row) {
+    this.contractDialogTitle = '缂栬緫鍚堝悓璁板綍'
+    const res = await this.$http.get(`/basic/supplier-contract/${row.id}`)
+    if (res.code === 200) {
+      this.contractForm = {
+        ...res.data,
+        storageBlobDTOs: res.data.fileList ? res.data.fileList.map(f => ({ id: f.id })) : []
+      }
+      this.contractDialogVisible = true
+    }
+  },
+
+  // 鍒犻櫎鍚堝悓璁板綍
+  async handleDeleteContract(row) {
+    await this.$confirm('纭畾鍒犻櫎璇ュ悎鍚岃褰曞悧锛�', '鎻愮ず', { type: 'warning' })
+    const res = await this.$http.delete(`/basic/supplier-contract/${row.id}`)
+    if (res.code === 200) {
+      this.$message.success('鍒犻櫎鎴愬姛')
+      this.getContractList(this.supplierId)
+    }
+  },
+
+  // 鎻愪氦鍚堝悓璁板綍琛ㄥ崟
+  async submitContractForm() {
+    await this.$refs.contractFormRef.validate()
+    const res = await this.$http.post('/basic/supplier-contract', this.contractForm)
+    if (res.code === 200) {
+      this.$message.success('鎿嶄綔鎴愬姛')
+      this.contractDialogVisible = false
+      this.getContractList(this.supplierId)
+    }
+  },
+
+  // 鏌ョ湅鎬诲悎鍚岄檮浠�
+  async handleViewContractFiles(row) {
+    if (!row.masterContractNo) {
+      this.$message.warning('璇ヨ褰曟病鏈夋�诲悎鍚屽彿')
+      return
+    }
+    const res = await this.$http.get('/basic/supplier-contract/files/byContractNo', {
+      params: { masterContractNo: row.masterContractNo }
+    })
+    if (res.code === 200) {
+      this.contractFileList = res.data || []
+      this.contractFileDialogVisible = true
+    }
+  },
+
+  // 鏍煎紡鍖栨枃浠跺ぇ灏�
+  formatFileSize(bytes) {
+    if (!bytes) return '0 B'
+    const k = 1024
+    const sizes = ['B', 'KB', 'MB', 'GB']
+    const i = Math.floor(Math.log(bytes) / Math.log(k))
+    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
+  },
+
+  // 鏌ョ湅闄勪欢鍒楄〃
+  handleViewFiles(row) {
+    this.contractFileList = row.fileList || []
+    this.contractFileDialogVisible = true
+  },
+
+  // 缂栬緫椤垫墦寮�鏃跺洖鏄炬�诲悎鍚屽彿
+  async handleEdit(row) {
+    this.form = { ...row }
+    this.dialogVisible = true
+    
+    // 鍔犺浇渚涘簲鍟嗙殑鍚堝悓鍒楄〃
+    if (row.supplierId) {
+      const res = await this.$http.get(`/basic/supplier-contract/list/${row.supplierId}`)
+      if (res.code === 200) {
+        this.contractList = res.data
+      }
+    }
+  }
+}
+```
+
+### 8. 閲囪喘鍙拌处鍒楄〃椤� - 琛ㄦ牸鏄剧ず鎬诲悎鍚屽彿鍒�
+
+```html
+<el-table-column prop="masterContractNo" label="鎬诲悎鍚屽彿" width="150" show-overflow-tooltip />
+```
+
+## 鍚庣鏂囦欢娓呭崟
+
+| 鏂囦欢璺緞 | 璇存槑 |
+|---------|------|
+| `src/main/java/com/ruoyi/basic/pojo/SupplierContractRecord.java` | 瀹炰綋绫� |
+| `src/main/java/com/ruoyi/basic/dto/SupplierContractRecordDto.java` | DTO |
+| `src/main/java/com/ruoyi/basic/mapper/SupplierContractRecordMapper.java` | Mapper鎺ュ彛 |
+| `src/main/resources/mapper/basic/SupplierContractRecordMapper.xml` | Mapper XML |
+| `src/main/java/com/ruoyi/basic/service/SupplierContractRecordService.java` | Service鎺ュ彛 |
+| `src/main/java/com/ruoyi/basic/service/impl/SupplierContractRecordServiceImpl.java` | Service瀹炵幇 |
+| `src/main/java/com/ruoyi/basic/controller/SupplierContractRecordController.java` | Controller |
+| `src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java` | 娣诲姞鏋氫妇鍊� `SUPPLIER_CONTRACT_RECORD` |
+
+## 娉ㄦ剰浜嬮」
+
+- 鎬诲悎鍚屽彿鏉ユ簮浜庝緵搴斿晢绠$悊鐨勫悎鍚岃褰曞瓙琛�
+- 閫夋嫨椤哄簭锛氬厛閫変緵搴斿晢 -> 鑷姩鍔犺浇璇ヤ緵搴斿晢鐨勫悎鍚屽垪琛� -> 鍐嶉�夋�诲悎鍚屽彿
+- 閲囪喘鍙拌处琛ㄥ凡鏈� `master_contract_no` 瀛楁锛屾棤闇�淇敼鏁版嵁搴�
+- 鏌ョ湅闄勪欢鎸夐挳浠呭湪鎬诲悎鍚屽彿瀛樺湪鏃舵樉绀�
+- 闄勪欢鍒楄〃閫氳繃鎬诲悎鍚屽彿鏌ヨ锛屾敮鎸侀瑙堝拰涓嬭浇
\ No newline at end of file
diff --git a/doc/sales_master_contract_linkage.md b/doc/sales_master_contract_linkage.md
new file mode 100644
index 0000000..4f3d914
--- /dev/null
+++ b/doc/sales_master_contract_linkage.md
@@ -0,0 +1,216 @@
+# 閿�鍞彴璐�/閿�鍞姤浠�-鎬诲悎鍚屽彿鑱斿姩閫夋嫨鍙婇檮浠舵煡鐪�
+
+## 娑夊強椤甸潰
+
+- 閿�鍞彴璐﹀垪琛ㄩ〉/鏂板缂栬緫椤�
+- 閿�鍞姤浠峰垪琛ㄩ〉/鏂板缂栬緫椤�
+
+## 鏂板API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | /basic/customer-contract/list/{customerId} | 鏌ヨ瀹㈡埛鍚堝悓璁板綍鍒楄〃锛堜緵涓嬫媺閫夋嫨锛� |
+| GET | /basic/customer-contract/files/byContractNo?masterContractNo=xxx | 鏍规嵁鎬诲悎鍚屽彿鏌ヨ闄勪欢 |
+
+## 鍓嶇淇敼鐐�
+
+### 1. 閿�鍞彴璐︽柊澧�/缂栬緫椤� - 瀹㈡埛閫夋嫨鍚庤仈鍔ㄦ�诲悎鍚屽彿
+
+```html
+<el-form-item label="瀹㈡埛鍚嶇О" prop="customerId">
+  <el-select
+    v-model="form.customerId"
+    placeholder="璇烽�夋嫨瀹㈡埛"
+    filterable
+    @change="handleCustomerChange"
+  >
+    <el-option
+      v-for="item in customerList"
+      :key="item.id"
+      :label="item.customerName"
+      :value="item.id"
+    />
+  </el-select>
+</el-form-item>
+
+<el-form-item label="鎬诲悎鍚屽彿" prop="masterContractNo">
+  <el-select
+    v-model="form.masterContractNo"
+    placeholder="璇峰厛閫夋嫨瀹㈡埛"
+    filterable
+    clearable
+    :disabled="!form.customerId"
+  >
+    <el-option
+      v-for="item in contractList"
+      :key="item.id"
+      :label="item.masterContractNo"
+      :value="item.masterContractNo"
+    />
+  </el-select>
+</el-form-item>
+```
+
+### 2. 閿�鍞姤浠锋柊澧�/缂栬緫椤� - 鍚屾牱娣诲姞瀹㈡埛鍜屾�诲悎鍚屽彿鑱斿姩
+
+```html
+<el-form-item label="瀹㈡埛鍚嶇О" prop="customerId">
+  <el-select
+    v-model="form.customerId"
+    placeholder="璇烽�夋嫨瀹㈡埛"
+    filterable
+    @change="handleCustomerChange"
+  >
+    <el-option
+      v-for="item in customerList"
+      :key="item.id"
+      :label="item.customerName"
+      :value="item.id"
+    />
+  </el-select>
+</el-form-item>
+
+<el-form-item label="鎬诲悎鍚屽彿" prop="masterContractNo">
+  <el-select
+    v-model="form.masterContractNo"
+    placeholder="璇峰厛閫夋嫨瀹㈡埛"
+    filterable
+    clearable
+    :disabled="!form.customerId"
+  >
+    <el-option
+      v-for="item in contractList"
+      :key="item.id"
+      :label="item.masterContractNo"
+      :value="item.masterContractNo"
+    />
+  </el-select>
+</el-form-item>
+```
+
+### 3. 閿�鍞彴璐﹀垪琛ㄩ〉 - 鎿嶄綔鏍忔坊鍔犳煡鐪嬫�诲悎鍚岄檮浠舵寜閽�
+
+```html
+<el-table-column label="鎿嶄綔" width="200">
+  <template #default="{ row }">
+    <el-button type="text" size="small" @click="handleEdit(row)">缂栬緫</el-button>
+    <el-button type="text" size="small" @click="handleDelete(row)">鍒犻櫎</el-button>
+    <el-button
+      type="text"
+      size="small"
+      v-if="row.masterContractNo"
+      @click="handleViewContractFiles(row)"
+    >鏌ョ湅闄勪欢</el-button>
+  </template>
+</el-table-column>
+```
+
+### 4. 鎬诲悎鍚岄檮浠舵煡鐪嬪脊绐�
+
+```html
+<el-dialog title="鎬诲悎鍚岄檮浠�" v-model="contractFileDialogVisible" width="600px">
+  <el-table :data="contractFileList" v-if="contractFileList.length">
+    <el-table-column prop="name" label="鏂囦欢鍚嶇О" />
+    <el-table-column prop="byteSize" label="鏂囦欢澶у皬" width="120">
+      <template #default="{ row }">
+        {{ formatFileSize(row.byteSize) }}
+      </template>
+    </el-table-column>
+    <el-table-column label="鎿嶄綔" width="150">
+      <template #default="{ row }">
+        <el-link type="primary" :href="row.previewURL" target="_blank">棰勮</el-link>
+        <el-link type="primary" :href="row.downloadURL" style="margin-left: 10px">涓嬭浇</el-link>
+      </template>
+    </el-table-column>
+  </el-table>
+  <el-empty v-else description="鏆傛棤闄勪欢" />
+</el-dialog>
+```
+
+### 5. data 鏁版嵁
+
+```js
+data() {
+  return {
+    // 鍚堝悓鍒楄〃锛堜緵涓嬫媺閫夋嫨锛�
+    contractList: [],
+    // 鎬诲悎鍚岄檮浠跺脊绐�
+    contractFileDialogVisible: false,
+    contractFileList: []
+  }
+}
+```
+
+### 6. 鏂规硶
+
+```js
+methods: {
+  // 瀹㈡埛閫夋嫨鍙樺寲鏃讹紝鍔犺浇璇ュ鎴风殑鍚堝悓鍒楄〃
+  async handleCustomerChange(customerId) {
+    this.form.masterContractNo = ''  // 娓呯┖鎬诲悎鍚屽彿
+    this.contractList = []
+    if (!customerId) return
+    
+    const res = await this.$http.get(`/basic/customer-contract/list/${customerId}`)
+    if (res.code === 200) {
+      this.contractList = res.data
+    }
+  },
+
+  // 鏌ョ湅鎬诲悎鍚岄檮浠�
+  async handleViewContractFiles(row) {
+    if (!row.masterContractNo) {
+      this.$message.warning('璇ヨ褰曟病鏈夋�诲悎鍚屽彿')
+      return
+    }
+    const res = await this.$http.get('/basic/customer-contract/files/byContractNo', {
+      params: { masterContractNo: row.masterContractNo }
+    })
+    if (res.code === 200) {
+      this.contractFileList = res.data || []
+      this.contractFileDialogVisible = true
+    }
+  },
+
+  // 鏍煎紡鍖栨枃浠跺ぇ灏�
+  formatFileSize(bytes) {
+    if (!bytes) return '0 B'
+    const k = 1024
+    const sizes = ['B', 'KB', 'MB', 'GB']
+    const i = Math.floor(Math.log(bytes) / Math.log(k))
+    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
+  }
+}
+```
+
+### 7. 閿�鍞彴璐�/鎶ヤ环鍒楄〃椤� - 琛ㄦ牸鏄剧ず鎬诲悎鍚屽彿鍒�
+
+```html
+<el-table-column prop="masterContractNo" label="鎬诲悎鍚屽彿" width="150" show-overflow-tooltip />
+```
+
+### 8. 缂栬緫椤垫墦寮�鏃跺洖鏄炬�诲悎鍚屽彿
+
+```js
+// 鎵撳紑缂栬緫寮圭獥鏃讹紝鍔犺浇瀹㈡埛鐨勫悎鍚屽垪琛ㄥ苟鍥炴樉
+async handleEdit(row) {
+  this.form = { ...row }
+  this.dialogVisible = true
+  
+  // 鍔犺浇瀹㈡埛鐨勫悎鍚屽垪琛�
+  if (row.customerId) {
+    const res = await this.$http.get(`/basic/customer-contract/list/${row.customerId}`)
+    if (res.code === 200) {
+      this.contractList = res.data
+    }
+  }
+}
+```
+
+## 娉ㄦ剰浜嬮」
+
+- 鎬诲悎鍚屽彿鏉ユ簮浜庡鎴锋。妗堢娴风殑鍚堝悓璁板綍瀛愯〃
+- 閫夋嫨椤哄簭锛氬厛閫夊鎴� -> 鑷姩鍔犺浇璇ュ鎴风殑鍚堝悓鍒楄〃 -> 鍐嶉�夋�诲悎鍚屽彿
+- 閿�鍞彴璐﹁〃宸叉湁 `master_contract_no` 瀛楁锛岄攢鍞姤浠疯〃闇�鏂板璇ュ瓧娈�
+- 鏌ョ湅闄勪欢鎸夐挳浠呭湪鎬诲悎鍚屽彿瀛樺湪鏃舵樉绀�
+- 闄勪欢鍒楄〃閫氳繃鎬诲悎鍚屽彿鏌ヨ锛屾敮鎸侀瑙堝拰涓嬭浇
\ No newline at end of file
diff --git a/src/api/basicData/customerContract.js b/src/api/basicData/customerContract.js
new file mode 100644
index 0000000..9bff94d
--- /dev/null
+++ b/src/api/basicData/customerContract.js
@@ -0,0 +1,52 @@
+import request from '@/utils/request'
+
+// 鏌ヨ鍚堝悓璁板綍鍒楄〃
+export function getCustomerContractList(customerId) {
+    return request({
+        url: `/basic/customer-contract/list/${customerId}`,
+        method: 'get'
+    })
+}
+
+// 鑾峰彇鍚堝悓璁板綍璇︽儏
+export function getCustomerContractById(id) {
+    return request({
+        url: `/basic/customer-contract/${id}`,
+        method: 'get'
+    })
+}
+
+// 鏂板鍚堝悓璁板綍
+export function addCustomerContract(data) {
+    return request({
+        url: '/basic/customer-contract',
+        method: 'post',
+        data: data
+    })
+}
+
+// 淇敼鍚堝悓璁板綍
+export function updateCustomerContract(data) {
+    return request({
+        url: '/basic/customer-contract',
+        method: 'put',
+        data: data
+    })
+}
+
+// 鍒犻櫎鍚堝悓璁板綍
+export function delCustomerContract(id) {
+    return request({
+        url: `/basic/customer-contract/${id}`,
+        method: 'delete'
+    })
+}
+
+// 鏍规嵁鎬诲悎鍚屽彿鏌ヨ闄勪欢
+export function getContractFilesByNo(masterContractNo) {
+    return request({
+        url: '/basic/customer-contract/files/byContractNo',
+        method: 'get',
+        params: { masterContractNo }
+    })
+}
diff --git a/src/api/basicData/supplierContract.js b/src/api/basicData/supplierContract.js
new file mode 100644
index 0000000..e3be8da
--- /dev/null
+++ b/src/api/basicData/supplierContract.js
@@ -0,0 +1,52 @@
+import request from '@/utils/request'
+
+// 鏌ヨ渚涘簲鍟嗗悎鍚岃褰曞垪琛�
+export function getSupplierContractList(supplierId) {
+    return request({
+        url: `/basic/supplier-contract/list/${supplierId}`,
+        method: 'get'
+    })
+}
+
+// 鑾峰彇渚涘簲鍟嗗悎鍚岃褰曡鎯�
+export function getSupplierContractById(id) {
+    return request({
+        url: `/basic/supplier-contract/${id}`,
+        method: 'get'
+    })
+}
+
+// 鏂板渚涘簲鍟嗗悎鍚岃褰�
+export function addSupplierContract(data) {
+    return request({
+        url: '/basic/supplier-contract',
+        method: 'post',
+        data: data
+    })
+}
+
+// 淇敼渚涘簲鍟嗗悎鍚岃褰�
+export function updateSupplierContract(data) {
+    return request({
+        url: '/basic/supplier-contract',
+        method: 'put',
+        data: data
+    })
+}
+
+// 鍒犻櫎渚涘簲鍟嗗悎鍚岃褰�
+export function delSupplierContract(id) {
+    return request({
+        url: `/basic/supplier-contract/${id}`,
+        method: 'delete'
+    })
+}
+
+// 鏍规嵁鎬诲悎鍚屽彿鏌ヨ闄勪欢
+export function getSupplierContractFilesByNo(masterContractNo) {
+    return request({
+        url: '/basic/supplier-contract/files/byContractNo',
+        method: 'get',
+        params: { masterContractNo }
+    })
+}
\ No newline at end of file
diff --git a/src/views/basicData/customerFile/index.vue b/src/views/basicData/customerFile/index.vue
index daf38b7..1b8fe52 100644
--- a/src/views/basicData/customerFile/index.vue
+++ b/src/views/basicData/customerFile/index.vue
@@ -357,70 +357,129 @@
           </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)" v-hasPermi="['basicData:customerFile:remove']">
-                鍒犻櫎
-              </el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-        <div v-if="negotiationRecords.length === 0"
-             class="no-records">
-          鏆傛棤娲借皥杩涘害璁板綍
-        </div>
-      </div>
+      <!-- Tab鍒囨崲鍖哄煙 -->
+      <el-tabs v-model="detailActiveTab" class="detail-tabs">
+        <!-- 娲借皥杩涘害璁板綍 -->
+        <el-tab-pane label="娲借皥杩涘害璁板綍" name="negotiation">
+          <div class="section-header">
+            <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>
+                  闄勪欢
+                </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)" v-hasPermi="['basicData:customerFile:remove']">
+                  鍒犻櫎
+                </el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div v-if="negotiationRecords.length === 0"
+               class="no-records">
+            鏆傛棤娲借皥杩涘害璁板綍
+          </div>
+        </el-tab-pane>
+        <!-- 鍚堝悓璁板綍 -->
+        <el-tab-pane label="鍚堝悓璁板綍" name="contract">
+          <div class="section-header">
+            <el-button type="primary"
+                       size="small"
+                       @click="handleAddContract">
+              鏂板鍚堝悓璁板綍
+            </el-button>
+          </div>
+          <el-table :data="contractList"
+                    border
+                    style="width: 100%">
+            <el-table-column prop="masterContractNo"
+                             label="鎬诲悎鍚屽彿"
+                             show-overflow-tooltip />
+            <el-table-column prop="entryDate"
+                             label="褰曞叆鏃ユ湡"
+                             width="180" />
+            <el-table-column prop="entryPerson"
+                             label="褰曞叆浜�"
+                             width="120" />
+            <el-table-column prop="remark"
+                             label="澶囨敞"
+                             show-overflow-tooltip />
+            <el-table-column label="闄勪欢"
+                             width="100"
+                             align="center">
+              <template #default="{ row }">
+                <el-link type="primary"
+                         v-if="row.fileList && row.fileList.length"
+                         @click="handleViewContractFiles(row)">
+                  鏌ョ湅({{ row.fileList.length }})
+                </el-link>
+                <span v-else>-</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="鎿嶄綔"
+                             width="150"
+                             align="center">
+              <template #default="{ row }">
+                <el-button type="primary"
+                           link
+                           @click="handleEditContract(row)">
+                  缂栬緫
+                </el-button>
+                <el-button type="danger"
+                           link
+                           @click="handleDeleteContract(row)" v-hasPermi="['basicData:customerFile:remove']">
+                  鍒犻櫎
+                </el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div v-if="contractList.length === 0"
+               class="no-records">
+            鏆傛棤鍚堝悓璁板綍
+          </div>
+        </el-tab-pane>
+      </el-tabs>
       <template #footer>
         <div class="dialog-footer">
           <el-button @click="closeDetailDialog">鍏抽棴</el-button>
@@ -503,6 +562,79 @@
         </div>
       </template>
     </el-dialog>
+    <!-- 鍚堝悓璁板綍鏂板/缂栬緫寮圭獥 -->
+    <el-dialog :title="contractDialogTitle"
+               v-model="contractDialogVisible"
+               width="600px"
+               @close="closeContractDialog">
+      <el-form :model="contractForm"
+               :rules="contractRules"
+               ref="contractFormRef"
+               label-width="100px">
+        <el-form-item label="鎬诲悎鍚屽彿" prop="masterContractNo">
+          <el-input v-model="contractForm.masterContractNo"
+                    placeholder="璇疯緭鍏ユ�诲悎鍚屽彿" />
+        </el-form-item>
+        <el-form-item label="褰曞叆鏃ユ湡" prop="entryDate">
+          <el-date-picker v-model="contractForm.entryDate"
+                          type="datetime"
+                          placeholder="閫夋嫨鏃ユ湡鏃堕棿"
+                          value-format="YYYY-MM-DD HH:mm:ss"
+                          style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="褰曞叆浜�" prop="entryPerson">
+          <el-input v-model="contractForm.entryPerson"
+                    placeholder="璇疯緭鍏ュ綍鍏ヤ汉" />
+        </el-form-item>
+        <el-form-item label="闄勪欢">
+          <FileUpload v-model:file-list="contractForm.storageBlobDTOs" />
+        </el-form-item>
+        <el-form-item label="澶囨敞">
+          <el-input v-model="contractForm.remark"
+                    type="textarea"
+                    :rows="3"
+                    placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="contractDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary" @click="submitContractForm">纭畾</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 鍚堝悓闄勪欢鏌ョ湅寮圭獥 -->
+    <el-dialog title="闄勪欢鍒楄〃"
+               v-model="contractFileDialogVisible"
+               width="600px">
+      <el-table :data="currentContractFiles"
+                border
+                style="width: 100%">
+        <el-table-column prop="name"
+                         label="鏂囦欢鍚�"
+                         show-overflow-tooltip />
+        <el-table-column label="鎿嶄綔"
+                         width="120"
+                         align="center">
+          <template #default="{ row }">
+            <el-button type="primary"
+                       link
+                       @click="downloadContractFile(row)">
+              涓嬭浇
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div v-if="!currentContractFiles || currentContractFiles.length === 0"
+           class="no-records">
+        鏆傛棤闄勪欢
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="contractFileDialogVisible = false">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -516,11 +648,19 @@
     addReturnVisit,
     getReturnVisit,
   } from "@/api/basicData/customerFile.js";
+  import {
+    getCustomerContractList,
+    getCustomerContractById,
+    addCustomerContract,
+    updateCustomerContract,
+    delCustomerContract,
+  } from "@/api/basicData/customerContract.js";
   import {listCustomer, getCustomer, addCustomer, updateCustomer, delCustomer, backCustomer} from "@/api/basicData/customer.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";
+  import FileUpload from "@/components/AttachmentUpload/file/index.vue";
   const { proxy } = getCurrentInstance();
   const userStore = useUserStore();
 
@@ -570,6 +710,7 @@
 
   // 璇︽儏鐩稿叧
   const detailDialogVisible = ref(false);
+  const detailActiveTab = ref("negotiation");
   const detailForm = reactive({
     customerName: "",
     customerType: "",
@@ -579,6 +720,32 @@
     maintenanceTime: "",
   });
   const negotiationRecords = ref([]);
+
+  // 鍚堝悓璁板綍鐩稿叧
+  const contractList = ref([]);
+  const contractDialogVisible = ref(false);
+  const contractDialogTitle = ref("");
+  const contractFormRef = ref();
+  const contractForm = reactive({
+    id: null,
+    customerId: null,
+    masterContractNo: "",
+    entryDate: "",
+    entryPerson: "",
+    entryPersonId: null,
+    remark: "",
+    storageBlobDTOs: [],
+  });
+  const contractRules = {
+    masterContractNo: [
+      { required: true, message: "璇疯緭鍏ユ�诲悎鍚屽彿", trigger: "blur" },
+    ],
+    entryDate: [
+      { required: true, message: "璇烽�夋嫨褰曞叆鏃ユ湡", trigger: "change" },
+    ],
+  };
+  const contractFileDialogVisible = ref(false);
+  const currentContractFiles = ref([]);
 
   // 闄勪欢鐩稿叧
   const attachmentDialogVisible = ref(false);
@@ -1087,20 +1254,132 @@
 
   // 鎵撳紑璇︽儏寮圭獥
   const openDetailDialog = row => {
+    detailActiveTab.value = "negotiation";
     getCustomer(row.id).then(res => {
       // 濉厖瀹㈡埛鍩烘湰淇℃伅
       Object.assign(detailForm, res.data);
+      detailForm.id = row.id; // 淇濆瓨瀹㈡埛ID
 
       // 鑾峰彇娲借皥杩涘害璁板綍
       negotiationRecords.value = res.data.followUpList || [];
 
       detailDialogVisible.value = true;
     });
+
+    // 鑾峰彇鍚堝悓璁板綍鍒楄〃
+    getContractList(row.id);
+  };
+
+  // 鑾峰彇鍚堝悓璁板綍鍒楄〃
+  const getContractList = customerId => {
+    getCustomerContractList(customerId).then(res => {
+      if (res.code === 200) {
+        contractList.value = res.data || [];
+      }
+    });
+  };
+
+  // 鏂板鍚堝悓璁板綍
+  const handleAddContract = () => {
+    contractDialogTitle.value = "鏂板鍚堝悓璁板綍";
+    contractForm.id = null;
+    contractForm.customerId = detailForm.id;
+    contractForm.masterContractNo = "";
+    contractForm.entryDate = "";
+    contractForm.entryPerson = userStore.nickName;
+    contractForm.entryPersonId = userStore.id;
+    contractForm.remark = "";
+    contractForm.storageBlobDTOs = [];
+    contractDialogVisible.value = true;
+  };
+
+  // 缂栬緫鍚堝悓璁板綍
+  const handleEditContract = row => {
+    contractDialogTitle.value = "缂栬緫鍚堝悓璁板綍";
+    getCustomerContractById(row.id).then(res => {
+      if (res.code === 200) {
+        contractForm.id = res.data.id;
+        contractForm.customerId = res.data.customerId;
+        contractForm.masterContractNo = res.data.masterContractNo;
+        contractForm.entryDate = res.data.entryDate;
+        contractForm.entryPerson = res.data.entryPerson;
+        contractForm.entryPersonId = res.data.entryPersonId;
+        contractForm.remark = res.data.remark;
+        contractForm.storageBlobDTOs = res.data.fileList || [];
+        contractDialogVisible.value = true;
+      }
+    });
+  };
+
+  // 鍒犻櫎鍚堝悓璁板綍
+  const handleDeleteContract = row => {
+    ElMessageBox.confirm("纭畾鍒犻櫎璇ュ悎鍚岃褰曞悧锛�", "鎻愮ず", {
+      type: "warning",
+    }).then(() => {
+      delCustomerContract(row.id).then(res => {
+        if (res.code === 200) {
+          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+          getContractList(detailForm.id);
+        }
+      });
+    }).catch(() => {
+      proxy.$modal.msg("宸插彇娑�");
+    });
+  };
+
+  // 鎻愪氦鍚堝悓璁板綍琛ㄥ崟
+  const submitContractForm = () => {
+    proxy.$refs.contractFormRef.validate(valid => {
+      if (valid) {
+        const submitData = {
+          id: contractForm.id,
+          customerId: contractForm.customerId,
+          masterContractNo: contractForm.masterContractNo,
+          entryDate: contractForm.entryDate,
+          entryPerson: contractForm.entryPerson,
+          entryPersonId: contractForm.entryPersonId,
+          remark: contractForm.remark,
+          storageBlobDTOs: contractForm.storageBlobDTOs.map(f => ({ id: f.id })),
+        };
+
+        const request = contractForm.id ? updateCustomerContract : addCustomerContract;
+        request(submitData).then(res => {
+          if (res.code === 200) {
+            proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+            contractDialogVisible.value = false;
+            getContractList(detailForm.id);
+          }
+        });
+      }
+    });
+  };
+
+  // 鍏抽棴鍚堝悓璁板綍寮圭獥
+  const closeContractDialog = () => {
+    proxy.resetForm("contractFormRef");
+    contractDialogVisible.value = false;
+  };
+
+  // 鏌ョ湅鍚堝悓闄勪欢
+  const handleViewContractFiles = row => {
+    currentContractFiles.value = row.fileList || [];
+    contractFileDialogVisible.value = true;
+  };
+
+  // 涓嬭浇鍚堝悓闄勪欢
+  const downloadContractFile = row => {
+    if (row.downloadURL || row.url) {
+      proxy.$download.byUrl(row.downloadURL || row.url, row.name || row.originalFilename);
+    } else {
+      proxy.$modal.msgError("涓嬭浇閾炬帴涓嶅瓨鍦�");
+    }
   };
 
   // 鍏抽棴璇︽儏寮圭獥
   const closeDetailDialog = () => {
     detailDialogVisible.value = false;
+    detailActiveTab.value = "negotiation";
+    contractList.value = [];
   };
 
   // 淇敼娲借皥璁板綍
@@ -1356,6 +1635,10 @@
     font-size: 14px;
   }
 
+  .detail-tabs {
+    margin-top: 20px;
+  }
+
   .attachment-section {
     .upload-area {
       margin-bottom: 20px;
diff --git a/src/views/basicData/supplierManage/components/HomeTab.vue b/src/views/basicData/supplierManage/components/HomeTab.vue
index 71b1b8a..eea719d 100644
--- a/src/views/basicData/supplierManage/components/HomeTab.vue
+++ b/src/views/basicData/supplierManage/components/HomeTab.vue
@@ -44,155 +44,259 @@
         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="supplierName">
-              <el-input
-                  v-model="form.supplierName"
-                  placeholder="璇疯緭鍏�"
-                  clearable
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item
-                label="绾崇◣浜鸿瘑鍒彿锛�"
-                prop="taxpayerIdentificationNum"
-            >
-              <el-input
-                  v-model="form.taxpayerIdentificationNum"
-                  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="bankAccountName">
-              <el-input
-                  v-model="form.bankAccountName"
-                  placeholder="璇疯緭鍏�"
-                  clearable
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="璐﹀彿锛�" prop="bankAccountNum">
-              <el-input
-                  v-model="form.bankAccountNum"
-                  placeholder="璇疯緭鍏�"
-                  clearable
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="鑱旂郴浜猴細" prop="contactUserName">
-              <el-input
-                  v-model="form.contactUserName"
-                  placeholder="璇疯緭鍏�"
-                  clearable
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="鑱旂郴鐢佃瘽锛�" prop="contactUserPhone">
-              <el-input
-                  v-model="form.contactUserPhone"
-                  placeholder="璇疯緭鍏�"
-                  clearable
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="缁存姢浜猴細" prop="maintainUserId">
-              <el-select
-                  v-model="form.maintainUserId"
-                  placeholder="璇烽�夋嫨"
-                  clearable
-                  disabled
-              >
-                <el-option
-                    v-for="item in userList"
-                    :key="item.nickName"
-                    :label="item.nickName"
-                    :value="item.userId"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="缁存姢鏃堕棿锛�" prop="maintainTime">
-              <el-date-picker
-                  style="width: 100%"
-                  v-model="form.maintainTime"
-                  value-format="YYYY-MM-DD"
-                  format="YYYY-MM-DD"
-                  type="date"
-                  placeholder="璇烽�夋嫨"
-                  clearable
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="渚涘簲鍟嗙被鍨嬶細" prop="supplierType">
-              <el-select v-model="form.supplierType" placeholder="璇烽�夋嫨" clearable>
-                <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="isWhite">
-              <el-select v-model="form.isWhite" placeholder="璇烽�夋嫨" clearable>
-                <el-option label="鏄�" :value="0" />
-                <el-option label="鍚�" :value="1" />
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
-      </el-form>
+      <el-tabs v-model="supplierActiveTab">
+        <!-- 鍩烘湰淇℃伅 Tab -->
+        <el-tab-pane label="鍩烘湰淇℃伅" name="basic">
+          <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="supplierName">
+                  <el-input
+                      v-model="form.supplierName"
+                      placeholder="璇疯緭鍏�"
+                      clearable
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item
+                    label="绾崇◣浜鸿瘑鍒彿锛�"
+                    prop="taxpayerIdentificationNum"
+                >
+                  <el-input
+                      v-model="form.taxpayerIdentificationNum"
+                      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="bankAccountName">
+                  <el-input
+                      v-model="form.bankAccountName"
+                      placeholder="璇疯緭鍏�"
+                      clearable
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="璐﹀彿锛�" prop="bankAccountNum">
+                  <el-input
+                      v-model="form.bankAccountNum"
+                      placeholder="璇疯緭鍏�"
+                      clearable
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="30">
+              <el-col :span="12">
+                <el-form-item label="鑱旂郴浜猴細" prop="contactUserName">
+                  <el-input
+                      v-model="form.contactUserName"
+                      placeholder="璇疯緭鍏�"
+                      clearable
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="鑱旂郴鐢佃瘽锛�" prop="contactUserPhone">
+                  <el-input
+                      v-model="form.contactUserPhone"
+                      placeholder="璇疯緭鍏�"
+                      clearable
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="30">
+              <el-col :span="12">
+                <el-form-item label="缁存姢浜猴細" prop="maintainUserId">
+                  <el-select
+                      v-model="form.maintainUserId"
+                      placeholder="璇烽�夋嫨"
+                      clearable
+                      disabled
+                  >
+                    <el-option
+                        v-for="item in userList"
+                        :key="item.nickName"
+                        :label="item.nickName"
+                        :value="item.userId"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="缁存姢鏃堕棿锛�" prop="maintainTime">
+                  <el-date-picker
+                      style="width: 100%"
+                      v-model="form.maintainTime"
+                      value-format="YYYY-MM-DD"
+                      format="YYYY-MM-DD"
+                      type="date"
+                      placeholder="璇烽�夋嫨"
+                      clearable
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="30">
+              <el-col :span="12">
+                <el-form-item label="渚涘簲鍟嗙被鍨嬶細" prop="supplierType">
+                  <el-select v-model="form.supplierType" placeholder="璇烽�夋嫨" clearable>
+                    <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="isWhite">
+                  <el-select v-model="form.isWhite" placeholder="璇烽�夋嫨" clearable>
+                    <el-option label="鏄�" :value="0" />
+                    <el-option label="鍚�" :value="1" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-tab-pane>
+
+        <!-- 鍚堝悓璁板綍 Tab -->
+        <el-tab-pane label="鍚堝悓璁板綍" name="contract" :disabled="operationType === 'add' || !currentSupplierId">
+          <div style="margin-bottom: 15px;">
+            <el-button type="primary" size="small" @click="handleAddContract">鏂板鍚堝悓璁板綍</el-button>
+          </div>
+          <el-table :data="contractRecordList" border style="width: 100%">
+            <el-table-column prop="masterContractNo" label="鎬诲悎鍚屽彿" show-overflow-tooltip />
+            <el-table-column prop="entryDate" label="褰曞叆鏃ユ湡" width="180" />
+            <el-table-column prop="entryPerson" label="褰曞叆浜�" width="120" />
+            <el-table-column prop="remark" label="澶囨敞" show-overflow-tooltip />
+            <el-table-column label="闄勪欢" width="100">
+              <template #default="{ row }">
+                <el-link type="primary" v-if="row.fileList && row.fileList.length" @click="handleViewContractFiles(row)">
+                  鏌ョ湅({{ row.fileList.length }})
+                </el-link>
+                <span v-else>-</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="鎿嶄綔" width="150">
+              <template #default="{ row }">
+                <el-button link type="primary" @click="handleEditContract(row)">缂栬緫</el-button>
+                <el-button link type="danger" @click="handleDeleteContract(row)">鍒犻櫎</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-tab-pane>
+      </el-tabs>
       <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="contractDialogVisible"
+        :title="contractDialogTitle"
+        width="600px"
+        :close-on-click-modal="false"
+    >
+      <el-form
+          :model="contractForm"
+          :rules="contractRules"
+          ref="contractFormRef"
+          label-width="100px"
+      >
+        <el-form-item label="鎬诲悎鍚屽彿" prop="masterContractNo">
+          <el-input v-model="contractForm.masterContractNo" placeholder="璇疯緭鍏ユ�诲悎鍚屽彿" clearable />
+        </el-form-item>
+        <el-form-item label="褰曞叆鏃ユ湡" prop="entryDate">
+          <el-date-picker
+              v-model="contractForm.entryDate"
+              type="datetime"
+              placeholder="閫夋嫨鏃ユ湡鏃堕棿"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              style="width: 100%"
+              clearable
+          />
+        </el-form-item>
+        <el-form-item label="褰曞叆浜�" prop="entryPerson">
+          <el-input v-model="contractForm.entryPerson" placeholder="璇疯緭鍏ュ綍鍏ヤ汉" clearable />
+        </el-form-item>
+        <el-form-item label="闄勪欢">
+          <FileUpload
+              v-model="contractForm.storageBlobDTOs"
+              :record-type="'supplier_contract_record'"
+              :record-id="contractForm.id"
+          />
+        </el-form-item>
+        <el-form-item label="澶囨敞">
+          <el-input v-model="contractForm.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitContractForm">纭</el-button>
+          <el-button @click="contractDialogVisible = false">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <!-- 鍚堝悓闄勪欢鏌ョ湅寮圭獥 -->
+    <el-dialog
+        v-model="contractFileDialogVisible"
+        title="鍚堝悓闄勪欢"
+        width="700px"
+        :close-on-click-modal="false"
+    >
+      <el-table :data="contractFileList" border v-if="contractFileList.length">
+        <el-table-column prop="name" label="鏂囦欢鍚嶇О" show-overflow-tooltip />
+        <el-table-column prop="byteSize" label="鏂囦欢澶у皬" width="120">
+          <template #default="{ row }">
+            {{ formatFileSize(row.byteSize) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" width="150">
+          <template #default="{ row }">
+            <el-link type="primary" :href="row.previewURL" target="_blank">棰勮</el-link>
+            <el-link type="primary" :href="row.downloadURL" style="margin-left: 10px">涓嬭浇</el-link>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-empty v-else description="鏆傛棤闄勪欢" />
     </el-dialog>
 
     <!-- 渚涘簲鍟嗗鍏ュ璇濇 -->
@@ -256,8 +360,16 @@
   listSupplier,
   updateSupplier,
 } from "@/api/basicData/supplierManageFile.js";
+import {
+  getSupplierContractList,
+  getSupplierContractById,
+  addSupplierContract,
+  updateSupplierContract,
+  delSupplierContract,
+} from "@/api/basicData/supplierContract.js";
 import useUserStore from "@/store/modules/user";
 import { getToken } from "@/utils/auth.js";
+import FileUpload from "@/components/AttachmentUpload/file/index.vue";
 const FileList = defineAsyncComponent(() =>
     import("@/components/Dialog/FileList.vue")
 );
@@ -355,6 +467,34 @@
   size: 100,
   total: 0,
 });
+
+// 渚涘簲鍟嗚鎯匱ab鐩稿叧
+const supplierActiveTab = ref('basic');
+const currentSupplierId = ref(null);
+const contractRecordList = ref([]);
+
+// 鍚堝悓璁板綍寮圭獥鐩稿叧
+const contractDialogVisible = ref(false);
+const contractDialogTitle = ref('');
+const contractFormRef = ref();
+const contractForm = reactive({
+  id: null,
+  supplierId: null,
+  masterContractNo: '',
+  entryDate: '',
+  entryPerson: '',
+  entryPersonId: null,
+  remark: '',
+  storageBlobDTOs: []
+});
+const contractRules = {
+  masterContractNo: [{ required: true, message: '璇疯緭鍏ユ�诲悎鍚屽彿', trigger: 'blur' }],
+  entryDate: [{ required: true, message: '璇烽�夋嫨褰曞叆鏃ユ湡', trigger: 'change' }]
+};
+
+// 鍚堝悓闄勪欢寮圭獥鐩稿叧
+const contractFileDialogVisible = ref(false);
+const contractFileList = ref([]);
 // 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
 const operationType = ref("");
 const dialogFormVisible = ref(false);
@@ -472,21 +612,122 @@
   selectedRows.value = selection;
 };
 // 鎵撳紑寮规
-const openForm = (type, row) => {
+const openForm = async (type, row) => {
   operationType.value = type;
   form.value = {};
   form.value.maintainUserId = userStore.id;
   form.value.maintainTime = getCurrentDate();
+  supplierActiveTab.value = 'basic';
+  currentSupplierId.value = null;
+  contractRecordList.value = [];
+
   userListNoPage().then((res) => {
     userList.value = res.data;
   });
+
   if (type === "edit") {
+    currentSupplierId.value = row.id;
     getSupplier(row.id).then((res) => {
       form.value = { ...res.data };
     });
+    // 鍔犺浇鍚堝悓璁板綍鍒楄〃
+    loadContractList(row.id);
   }
   dialogFormVisible.value = true;
 };
+
+// 鍔犺浇鍚堝悓璁板綍鍒楄〃
+const loadContractList = async (supplierId) => {
+  try {
+    const res = await getSupplierContractList(supplierId);
+    if (res.code === 200) {
+      contractRecordList.value = res.data || [];
+    }
+  } catch (error) {
+    console.error('鑾峰彇鍚堝悓璁板綍鍒楄〃澶辫触:', error);
+  }
+};
+
+// 鏂板鍚堝悓璁板綍
+const handleAddContract = () => {
+  contractDialogTitle.value = '鏂板鍚堝悓璁板綍';
+  Object.assign(contractForm, {
+    id: null,
+    supplierId: currentSupplierId.value,
+    masterContractNo: '',
+    entryDate: '',
+    entryPerson: userStore.nickName,
+    entryPersonId: userStore.id,
+    remark: '',
+    storageBlobDTOs: []
+  });
+  contractDialogVisible.value = true;
+};
+
+// 缂栬緫鍚堝悓璁板綍
+const handleEditContract = async (row) => {
+  contractDialogTitle.value = '缂栬緫鍚堝悓璁板綍';
+  try {
+    const res = await getSupplierContractById(row.id);
+    if (res.code === 200) {
+      Object.assign(contractForm, {
+        ...res.data,
+        storageBlobDTOs: res.data.fileList ? res.data.fileList.map(f => ({ id: f.id })) : []
+      });
+      contractDialogVisible.value = true;
+    }
+  } catch (error) {
+    console.error('鑾峰彇鍚堝悓璁板綍璇︽儏澶辫触:', error);
+  }
+};
+
+// 鍒犻櫎鍚堝悓璁板綍
+const handleDeleteContract = async (row) => {
+  try {
+    await ElMessageBox.confirm('纭畾鍒犻櫎璇ュ悎鍚岃褰曞悧锛�', '鎻愮ず', { type: 'warning' });
+    const res = await delSupplierContract(row.id);
+    if (res.code === 200) {
+      proxy.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+      loadContractList(currentSupplierId.value);
+    }
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('鍒犻櫎鍚堝悓璁板綍澶辫触:', error);
+    }
+  }
+};
+
+// 鎻愪氦鍚堝悓璁板綍琛ㄥ崟
+const submitContractForm = async () => {
+  try {
+    await contractFormRef.value.validate();
+    const api = contractForm.id ? updateSupplierContract : addSupplierContract;
+    const res = await api(contractForm);
+    if (res.code === 200) {
+      proxy.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+      contractDialogVisible.value = false;
+      loadContractList(currentSupplierId.value);
+    }
+  } catch (error) {
+    console.error('鎻愪氦鍚堝悓璁板綍澶辫触:', error);
+  }
+};
+
+// 鏌ョ湅鍚堝悓闄勪欢
+const handleViewContractFiles = (row) => {
+  contractFileList.value = row.fileList || [];
+  contractFileDialogVisible.value = true;
+};
+
+// 鏍煎紡鍖栨枃浠跺ぇ灏�
+const formatFileSize = (bytes) => {
+  if (!bytes) return '0 B';
+  const k = 1024;
+  const sizes = ['B', 'KB', 'MB', 'GB'];
+  const i = Math.floor(Math.log(bytes) / Math.log(k));
+  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
+};
+
 // 鎻愪氦琛ㄥ崟
 const submitForm = () => {
   proxy.$refs["formRef"].validate((valid) => {
diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index a65df53..83089cf 100644
--- a/src/views/procurementManagement/procurementLedger/index.vue
+++ b/src/views/procurementManagement/procurementLedger/index.vue
@@ -278,7 +278,7 @@
                          show-overflow-tooltip />
         <el-table-column fixed="right"
                          label="鎿嶄綔"
-                         width="120"
+                         width="200"
                          align="center">
           <template #default="scope">
             <el-button link
@@ -288,6 +288,11 @@
             <el-button link
                        type="primary"
                        @click="openFileDialog(scope.row)">闄勪欢</el-button>
+            <el-button link
+                       type="primary"
+                       v-if="scope.row.masterContractNo"
+                       @click="openContractFileDialog(scope.row)">鎬诲悎鍚岄檮浠�
+            </el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -310,15 +315,6 @@
                label-position="top"
                :rules="rules"
                ref="formRef">
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="鎬诲悎鍚屽彿锛�">
-              <el-input v-model="form.masterContractNo"
-                        placeholder="璇疯緭鍏�"
-                        clearable />
-            </el-form-item>
-          </el-col>
-        </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="閲囪喘鍚堝悓鍙凤細"
@@ -351,7 +347,8 @@
               <el-select v-model="form.supplierId"
                          placeholder="璇烽�夋嫨"
                          filterable
-                         clearable>
+                         clearable
+                         @change="handleSupplierChange">
                 <el-option v-for="item in supplierList"
                            :key="item.id"
                            :label="item.supplierName"
@@ -360,6 +357,22 @@
               </el-select>
             </el-form-item>
           </el-col>
+          <el-col :span="12">
+            <el-form-item label="鎬诲悎鍚屽彿锛�">
+              <el-select v-model="form.masterContractNo"
+                         placeholder="璇峰厛閫夋嫨渚涘簲鍟�"
+                         filterable
+                         clearable
+                         :disabled="!form.supplierId">
+                <el-option v-for="item in contractList"
+                           :key="item.id"
+                           :label="item.masterContractNo"
+                           :value="item.masterContractNo" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="椤圭洰鍚嶇О"
                           prop="projectName">
@@ -857,6 +870,28 @@
               v-model:visible="fileListDialogVisible"
               record-type="purchase_ledger"
               :record-id="recordId" />
+    <!-- 鎬诲悎鍚岄檮浠跺脊绐� -->
+    <el-dialog
+      v-model="contractFileDialogVisible"
+      title="鎬诲悎鍚岄檮浠�"
+      width="700px"
+      :close-on-click-modal="false">
+      <el-table :data="contractFileList" border v-if="contractFileList.length">
+        <el-table-column prop="name" label="鏂囦欢鍚嶇О" show-overflow-tooltip />
+        <el-table-column prop="byteSize" label="鏂囦欢澶у皬" width="120">
+          <template #default="{ row }">
+            {{ formatFileSize(row.byteSize) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" width="150">
+          <template #default="{ row }">
+            <el-link type="primary" :href="row.previewURL" target="_blank">棰勮</el-link>
+            <el-link type="primary" :href="row.downloadURL" style="margin-left: 10px">涓嬭浇</el-link>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-empty v-else description="鏆傛棤闄勪欢" />
+    </el-dialog>
   </div>
 </template>
 
@@ -914,6 +949,9 @@
   const sparePartsOptions = ref([]);
   const salesContractList = ref([]);
   const supplierList = ref([]);
+  const contractList = ref([]); // 渚涘簲鍟嗗悎鍚屽垪琛�
+  const contractFileDialogVisible = ref(false);
+  const contractFileList = ref([]);
   const tableLoading = ref(false);
   const recordId = ref();
   const fileListDialogVisible = ref(false);
@@ -926,6 +964,7 @@
   import useUserStore from "@/store/modules/user";
   import { modelList, productTreeList } from "@/api/basicData/product.js";
   import { getSparePartsList } from "@/api/equipmentManagement/spareParts.js";
+  import { getSupplierContractList, getSupplierContractFilesByNo } from "@/api/basicData/supplierContract.js";
   import dayjs from "dayjs";
   import FileUpload from "@/components/AttachmentUpload/file/index.vue";
 
@@ -966,6 +1005,7 @@
       寰呭叆搴�: "info", // 寰呭叆搴� - 鐏拌壊
       鍏ュ簱涓�: "warning", // 鍏ュ簱涓� - 姗欒壊
       瀹屽叏鍏ュ簱: "success", // 瀹屽叏鍏ュ簱 - 缁胯壊
+      宸查┏鍥�: "danger", // 宸查┏鍥� - 绾㈣壊
     };
     return typeMap[status] || "";
   };
@@ -1509,6 +1549,17 @@
             type: 2,
           });
           productData.value = productRes.data || [];
+          // 缂栬緫鏃跺姞杞戒緵搴斿晢鐨勫悎鍚屽垪琛ㄤ互渚垮洖鏄炬�诲悎鍚屽彿
+          if (purchaseRes.supplierId) {
+            try {
+              const contractRes = await getSupplierContractList(purchaseRes.supplierId);
+              if (contractRes.code === 200) {
+                contractList.value = contractRes.data || [];
+              }
+            } catch (error) {
+              console.error('鑾峰彇渚涘簲鍟嗗悎鍚屽垪琛ㄥけ璐�:', error);
+            }
+          }
         } catch (error) {
           console.error("鍔犺浇閲囪喘鍙拌处鏁版嵁澶辫触:", error);
           proxy.$modal.msgError("鍔犺浇鏁版嵁澶辫触");
@@ -2200,6 +2251,48 @@
     }
   };
 
+  // 渚涘簲鍟嗛�夋嫨鍙樺寲鏃讹紝鍔犺浇璇ヤ緵搴斿晢鐨勫悎鍚屽垪琛�
+  const handleSupplierChange = async (supplierId) => {
+    form.value.masterContractNo = '';
+    contractList.value = [];
+    if (!supplierId) return;
+
+    try {
+      const res = await getSupplierContractList(supplierId);
+      if (res.code === 200) {
+        contractList.value = res.data || [];
+      }
+    } catch (error) {
+      console.error('鑾峰彇渚涘簲鍟嗗悎鍚屽垪琛ㄥけ璐�:', error);
+    }
+  };
+
+  // 鏌ョ湅鎬诲悎鍚岄檮浠�
+  const openContractFileDialog = async (row) => {
+    if (!row.masterContractNo) {
+      proxy.$modal.msgWarning('璇ヨ褰曟病鏈夋�诲悎鍚屽彿');
+      return;
+    }
+    try {
+      const res = await getSupplierContractFilesByNo(row.masterContractNo);
+      if (res.code === 200) {
+        contractFileList.value = res.data || [];
+        contractFileDialogVisible.value = true;
+      }
+    } catch (error) {
+      console.error('鑾峰彇鍚堝悓闄勪欢澶辫触:', error);
+    }
+  };
+
+  // 鏍煎紡鍖栨枃浠跺ぇ灏�
+  const formatFileSize = (bytes) => {
+    if (!bytes) return '0 B';
+    const k = 1024;
+    const sizes = ['B', 'KB', 'MB', 'GB'];
+    const i = Math.floor(Math.log(bytes) / Math.log(k));
+    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
+  };
+
   onMounted(() => {
     getList();
     getTemplateList();
diff --git a/src/views/salesManagement/returnOrder/components/formDia.vue b/src/views/salesManagement/returnOrder/components/formDia.vue
index 1f59c96..a5eb0e9 100644
--- a/src/views/salesManagement/returnOrder/components/formDia.vue
+++ b/src/views/salesManagement/returnOrder/components/formDia.vue
@@ -274,8 +274,8 @@
 const calcAlreadyReturned = (row) => {
   const total = Number(row?.stockOutNum ?? row?.totalQuantity ?? row?.totalReturnNum ?? 0);
   const un = Number(row?.unQuantity ?? 0);
-  if (!Number.isFinite(total) || !Number.isFinite(un)) return 0;
-  return Math.max(total - un, 0);
+  if (!Number.isFinite(total) || !Number.isFinite(un)) return '0.00';
+  return Math.max(total - un, 0).toFixed(2);
 };
 
 const tableColumn = ref([
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index 6a5520b..bbeef59 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -284,7 +284,7 @@
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column fixed="right" label="鎿嶄綔" width="220" align="center">
+        <el-table-column fixed="right" label="鎿嶄綔" width="280" align="center">
           <template #default="scope">
 						<el-button
 							link
@@ -297,6 +297,13 @@
             </el-button>
             <el-button link type="primary" @click="openFileDialog(scope.row)"
               >闄勪欢
+            </el-button>
+            <el-button
+              link
+              type="primary"
+              v-if="scope.row.masterContractNo"
+              @click="openContractFileDialog(scope.row)"
+              >鎬诲悎鍚岄檮浠�
             </el-button>
           </template>
         </el-table-column>
@@ -338,18 +345,6 @@
             <el-button type="primary" plain @click="openQuotationDialog">
               浠庨攢鍞姤浠峰鍏�
             </el-button>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="鎬诲悎鍚屽彿锛�">
-              <el-input
-                v-model="form.masterContractNo"
-                placeholder="璇疯緭鍏�"
-                clearable
-                :disabled="operationType === 'view'"
-              />
-            </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="30">
@@ -409,6 +404,7 @@
                 clearable
                 :disabled="operationType === 'view'"
                 filterable
+                @change="handleCustomerChange"
               >
                 <el-option
                   v-for="item in customerOption"
@@ -424,6 +420,26 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
+            <el-form-item label="鎬诲悎鍚屽彿锛�">
+              <el-select
+                v-model="form.masterContractNo"
+                placeholder="璇峰厛閫夋嫨瀹㈡埛"
+                clearable
+                filterable
+                :disabled="operationType === 'view' || !form.customerId"
+              >
+                <el-option
+                  v-for="item in contractList"
+                  :key="item.id"
+                  :label="item.masterContractNo"
+                  :value="item.masterContractNo"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
             <el-form-item label="椤圭洰鍚嶇О锛�" prop="projectName">
               <el-input
                 v-model="form.projectName"
@@ -433,8 +449,6 @@
               />
             </el-form-item>
           </el-col>
-        </el-row>
-        <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="绛捐鏃ユ湡锛�" prop="executionDate">
               <el-date-picker
@@ -449,6 +463,8 @@
               />
             </el-form-item>
           </el-col>
+        </el-row>
+        <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="浠樻鏂瑰紡">
               <el-select
@@ -466,8 +482,6 @@
               </el-select>
             </el-form-item>
           </el-col>
-        </el-row>
-        <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="閲囪喘鍚堝悓鍙凤細" prop="purchaseContractNumber">
               <el-select
@@ -947,6 +961,29 @@
       record-type="sales_ledger"
       :record-id="recordId"
     />
+    <!-- 鎬诲悎鍚岄檮浠跺脊绐� -->
+    <el-dialog
+      v-model="contractFileDialogVisible"
+      title="鎬诲悎鍚岄檮浠�"
+      width="700px"
+      :close-on-click-modal="false"
+    >
+      <el-table :data="contractFileList" border v-if="contractFileList.length">
+        <el-table-column prop="name" label="鏂囦欢鍚嶇О" show-overflow-tooltip />
+        <el-table-column prop="byteSize" label="鏂囦欢澶у皬" width="120">
+          <template #default="{ row }">
+            {{ formatFileSize(row.byteSize) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" width="150">
+          <template #default="{ row }">
+            <el-link type="primary" :href="row.previewURL" target="_blank">棰勮</el-link>
+            <el-link type="primary" :href="row.downloadURL" style="margin-left: 10px">涓嬭浇</el-link>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-empty v-else description="鏆傛棤闄勪欢" />
+    </el-dialog>
     <!-- 鎵撳嵃棰勮寮圭獥 -->
     <el-dialog
       v-model="printPreviewVisible"
@@ -1266,6 +1303,7 @@
 import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
 import { getCurrentDate } from "@/utils/index.js";
 import { listCustomer } from "@/api/basicData/customer.js";
+import { getCustomerContractList, getContractFilesByNo } from "@/api/basicData/customerContract.js";
 
 const FileList = defineAsyncComponent(() =>
   import("@/components/Dialog/FileList.vue")
@@ -1293,6 +1331,11 @@
 const total = ref(0);
 const fileList = ref([]);
 const deliveryFileList = ref([]);
+// 鍚堝悓鍒楄〃锛堜緵涓嬫媺閫夋嫨锛�
+const contractList = ref([]);
+// 鎬诲悎鍚岄檮浠跺脊绐�
+const contractFileDialogVisible = ref(false);
+const contractFileList = ref([]);
 
 // 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
 const operationType = ref("");
@@ -1824,13 +1867,24 @@
     form.value.autoGenerateContractNo = true;
   } else {
     currentId.value = row.id;
-    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
+    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(async (res) => {
       form.value = { ...res };
       form.value.entryPerson = Number(res.entryPerson);
       productData.value = form.value.productData;
       fileList.value = form.value.storageBlobVOs;
       // 缂栬緫鏃惰缃嚜鍔ㄧ敓鎴愪负false锛屽厑璁告墜鍔ㄤ慨鏀�
       form.value.autoGenerateContractNo = false;
+      // 缂栬緫鏃跺姞杞藉鎴风殑鍚堝悓鍒楄〃浠ヤ究鍥炴樉鎬诲悎鍚屽彿
+      if (res.customerId) {
+        try {
+          const contractRes = await getCustomerContractList(res.customerId);
+          if (contractRes.code === 200) {
+            contractList.value = contractRes.data || [];
+          }
+        } catch (error) {
+          console.error('鑾峰彇瀹㈡埛鍚堝悓鍒楄〃澶辫触:', error);
+        }
+      }
     });
   }
   // let userAll = await userStore.getInfo()
@@ -3064,6 +3118,48 @@
   return statusMap[returnStatus] || 'info';
 };
 
+// 瀹㈡埛閫夋嫨鍙樺寲鏃讹紝鍔犺浇璇ュ鎴风殑鍚堝悓鍒楄〃
+const handleCustomerChange = async (customerId) => {
+  form.value.masterContractNo = '';
+  contractList.value = [];
+  if (!customerId) return;
+
+  try {
+    const res = await getCustomerContractList(customerId);
+    if (res.code === 200) {
+      contractList.value = res.data || [];
+    }
+  } catch (error) {
+    console.error('鑾峰彇瀹㈡埛鍚堝悓鍒楄〃澶辫触:', error);
+  }
+};
+
+// 鏌ョ湅鎬诲悎鍚岄檮浠�
+const openContractFileDialog = async (row) => {
+  if (!row.masterContractNo) {
+    proxy.$modal.msgWarning('璇ヨ褰曟病鏈夋�诲悎鍚屽彿');
+    return;
+  }
+  try {
+    const res = await getContractFilesByNo(row.masterContractNo);
+    if (res.code === 200) {
+      contractFileList.value = res.data || [];
+      contractFileDialogVisible.value = true;
+    }
+  } catch (error) {
+    console.error('鑾峰彇鍚堝悓闄勪欢澶辫触:', error);
+  }
+};
+
+// 鏍煎紡鍖栨枃浠跺ぇ灏�
+const formatFileSize = (bytes) => {
+  if (!bytes) return '0 B';
+  const k = 1024;
+  const sizes = ['B', 'KB', 'MB', 'GB'];
+  const i = Math.floor(Math.log(bytes) / Math.log(k));
+  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
+};
+
 onMounted(() => {
   searchForm.salesContractNo = route.query.salesContractNo;
   getList();
diff --git a/src/views/salesManagement/salesQuotation/index.vue b/src/views/salesManagement/salesQuotation/index.vue
index 5c47115..9dece6c 100644
--- a/src/views/salesManagement/salesQuotation/index.vue
+++ b/src/views/salesManagement/salesQuotation/index.vue
@@ -52,6 +52,7 @@
       >
 				<el-table-column align="center" label="搴忓彿" type="index" width="60" />
         <el-table-column prop="quotationNo" label="鎶ヤ环鍗曞彿" />
+        <el-table-column prop="masterContractNo" label="鎬诲悎鍚屽彿" width="150" show-overflow-tooltip />
         <el-table-column prop="customer" label="瀹㈡埛鍚嶇О" />
         <el-table-column prop="salesperson" label="涓氬姟鍛�" width="100" />
         <el-table-column prop="quotationDate" label="鎶ヤ环鏃ユ湡" width="120" />
@@ -103,11 +104,20 @@
             <el-row :gutter="24">
               <el-col :span="12">
                 <el-form-item label="瀹㈡埛鍚嶇О" prop="customerId">
-                  <el-select v-model="form.customerId" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%" clearable filterable>
+                  <el-select v-model="form.customerId" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%" clearable filterable @change="handleCustomerChange">
                     <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="鎬诲悎鍚屽彿">
+                  <el-select v-model="form.masterContractNo" placeholder="璇峰厛閫夋嫨瀹㈡埛" style="width: 100%" clearable filterable :disabled="!form.customerId">
+                    <el-option v-for="item in contractList" :key="item.id" :label="item.masterContractNo" :value="item.masterContractNo"></el-option>
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="24">
               <el-col :span="12">
                 <el-form-item label="涓氬姟鍛�" prop="salesperson">
                   <el-select v-model="form.salesperson" placeholder="璇烽�夋嫨涓氬姟鍛�" style="width: 100%" clearable filterable>
@@ -264,6 +274,7 @@
     <el-dialog v-model="viewDialogVisible" title="鎶ヤ环璇︽儏" width="800px">
       <el-descriptions :column="2" border>
         <el-descriptions-item label="鎶ヤ环鍗曞彿">{{ currentQuotation.quotationNo }}</el-descriptions-item>
+        <el-descriptions-item label="鎬诲悎鍚屽彿">{{ currentQuotation.masterContractNo || '--' }}</el-descriptions-item>
         <el-descriptions-item label="瀹㈡埛鍚嶇О">{{ currentQuotation.customer }}</el-descriptions-item>
         <el-descriptions-item label="涓氬姟鍛�">{{ currentQuotation.salesperson }}</el-descriptions-item>
         <el-descriptions-item label="鎶ヤ环鏃ユ湡">{{ currentQuotation.quotationDate }}</el-descriptions-item>
@@ -309,6 +320,7 @@
 import {modelList, productTreeList} from "@/api/basicData/product.js";
 import {listCustomer} from "@/api/basicData/customer.js";
 import { userListNoPage } from "@/api/system/user.js";
+import { getCustomerContractList } from "@/api/basicData/customerContract.js";
 
 // 鍝嶅簲寮忔暟鎹�
 const loading = ref(false)
@@ -322,6 +334,7 @@
 const userList = ref([])
 const productOptions = ref([]);
 const modelOptions  = ref([]);
+const contractList = ref([]);
 const pagination = reactive({
   total: 3,
   currentPage: 1,
@@ -341,6 +354,7 @@
   paymentMethod: '',
   status: '鑽夌',
   remark: '',
+  masterContractNo: '',
   products: [],
   subtotal: 0,
   freight: 0,
@@ -381,6 +395,7 @@
 // 璁$畻灞炴��
 const filteredList = computed(() => {
   let list = quotationList.value
+  console.log(list)
   return list
 })
 
@@ -419,6 +434,23 @@
     customerOption.value = res.data.records;
   });
 }
+
+// 瀹㈡埛閫夋嫨鍙樺寲鏃讹紝鍔犺浇璇ュ鎴风殑鍚堝悓鍒楄〃
+const handleCustomerChange = async (customerId) => {
+  form.masterContractNo = '';
+  contractList.value = [];
+  if (!customerId) return;
+
+  try {
+    const res = await getCustomerContractList(customerId);
+    if (res.code === 200) {
+      contractList.value = res.data || [];
+    }
+  } catch (error) {
+    console.error('鑾峰彇瀹㈡埛鍚堝悓鍒楄〃澶辫触:', error);
+  }
+};
+
 const getProductOptions = () => {
 	// 杩斿洖 Promise锛屼究浜庣紪杈戞椂 await 纭繚鑳藉弽鏄�
 	return productTreeList().then((res) => {
@@ -521,6 +553,7 @@
     quotationDate: row.quotationDate || '',
     validDate: row.validDate || '',
     paymentMethod: row.paymentMethod || '',
+    masterContractNo: row.masterContractNo || '',
     status: row.status || '',
     remark: row.remark || '',
     products: row.products ? row.products.map(product => ({
@@ -557,6 +590,22 @@
   form.paymentMethod = row.paymentMethod || ''
   form.status = row.status || '鑽夌'
   form.remark = row.remark || ''
+
+  // 缂栬緫鏃跺姞杞藉鎴风殑鍚堝悓鍒楄〃浠ヤ究鍥炴樉鎬诲悎鍚屽彿
+  if (row.customerId) {
+    try {
+      const contractRes = await getCustomerContractList(row.customerId);
+      if (contractRes.code === 200) {
+        contractList.value = contractRes.data || [];
+      }
+    } catch (error) {
+      console.error('鑾峰彇瀹㈡埛鍚堝悓鍒楄〃澶辫触:', error);
+    }
+  }
+
+  // 鍏煎鍚庣杩斿洖鐨� masterContractNo 鎴� master_contract_no锛屾斁鍦ㄥ悎鍚屽垪琛ㄥ姞杞藉悗璁剧疆
+  form.masterContractNo = row.masterContractNo || row.master_contract_no || ''
+
   form.products = row.products ? await Promise.all(row.products.map(async (product) => {
     const productName = product.product || product.productName || ''
     // 浼樺厛鐢� productId锛涘鏋滃彧鏈夊悕绉帮紝灏濊瘯鍙嶆煡 id 浠ヤ究鏍戦�夋嫨鍣ㄥ弽鏄�
@@ -634,11 +683,13 @@
   form.paymentMethod = ''
   form.status = '鑽夌'
   form.remark = ''
+  form.masterContractNo = ''
   form.products = []
   form.subtotal = 0
   form.freight = 0
   form.otherFee = 0
   form.totalAmount = 0
+  contractList.value = []
 }
 
 const addProduct = () => {
@@ -749,6 +800,7 @@
         customer: item.customer || '',
         customerId: item.customerId || undefined,
         salesperson: item.salesperson || '',
+        masterContractNo: item.masterContractNo || '',
         quotationDate: item.quotationDate || '',
         validDate: item.validDate || '',
         paymentMethod: item.paymentMethod || '',

--
Gitblit v1.9.3