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, // 瀹㈡埛妗fID锛堝繀濉級
+ "masterContractNo": "HT202606240001", // 鎬诲悎鍚屽彿锛堝繀濉級
+ "entryDate": "2026-06-24 10:30:00", // 褰曞叆鏃ユ湡锛堝繀濉級
+ "entryPerson": "寮犱笁", // 褰曞叆浜哄鍚�
+ "entryPersonId": 1, // 褰曞叆浜篒D
+ "remark": "澶囨敞淇℃伅", // 澶囨敞
+ "storageBlobDTOs": [ // 闄勪欢鍒楄〃
+ { "id": 1 },
+ { "id": 2 }
+ ]
+}
+```
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| id | Long | 鍚� | 淇敼鏃跺繀浼� |
+| customerId | Long | 鏄� | 瀹㈡埛妗fID |
+| 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