1.客户,销售,供应商,采购增加总合同号
2.采购的产品增加库存状态回显
3.出厂改成创建时间倒序
4.开发总合同号管理页面
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # å®¢æ·æ¡£æ¡ç§æµ·-ååè®°å½ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - å®¢æ·æ¡£æ¡ï¼ç§æµ·ï¼è¯¦æ
页 |
| | | |
| | | ## 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, // å®¢æ·æ¡£æ¡IDï¼å¿
å¡«ï¼ |
| | | "masterContractNo": "HT202606240001", // æ»ååå·ï¼å¿
å¡«ï¼ |
| | | "entryDate": "2026-06-24 10:30:00", // å½å
¥æ¥æï¼å¿
å¡«ï¼ |
| | | "entryPerson": "å¼ ä¸", // å½å
¥äººå§å |
| | | "entryPersonId": 1, // å½å
¥äººID |
| | | "remark": "夿³¨ä¿¡æ¯", // 夿³¨ |
| | | "storageBlobDTOs": [ // éä»¶å表 |
| | | { "id": 1 }, |
| | | { "id": 2 } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | id | Long | å¦ | ä¿®æ¹æ¶å¿
ä¼ | |
| | | | customerId | Long | æ¯ | å®¢æ·æ¡£æ¡ID | |
| | | | masterContractNo | String | æ¯ | æ»ååå· | |
| | | | entryDate | String | æ¯ | å½å
¥æ¥æï¼yyyy-MM-dd HH:mm:ssï¼ | |
| | | | entryPerson | String | å¦ | å½å
¥äººå§å | |
| | | | entryPersonId | Long | å¦ | å½å
¥äººID | |
| | | | 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 |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # éè´å°è´¦-æ»ååå·èå¨éæ©åéä»¶æ¥ç |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - ä¾åºå管ç详æ
é¡µï¼æ°å¢ååè®°å½Tabï¼ |
| | | - éè´å°è´¦å表页/æ°å¢ç¼è¾é¡µ |
| | | |
| | | ## æ°å¢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` åæ®µï¼æ éä¿®æ¹æ°æ®åº |
| | | - æ¥çéä»¶æé®ä»
卿»ååå·å卿¶æ¾ç¤º |
| | | - éä»¶å表éè¿æ»ååå·æ¥è¯¢ï¼æ¯æé¢è§åä¸è½½ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # éå®å°è´¦/é宿¥ä»·-æ»ååå·èå¨éæ©åéä»¶æ¥ç |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - éå®å°è´¦å表页/æ°å¢ç¼è¾é¡µ |
| | | - é宿¥ä»·å表页/æ°å¢ç¼è¾é¡µ |
| | | |
| | | ## æ°å¢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` åæ®µï¼é宿¥ä»·è¡¨éæ°å¢è¯¥å段 |
| | | - æ¥çéä»¶æé®ä»
卿»ååå·å卿¶æ¾ç¤º |
| | | - éä»¶å表éè¿æ»ååå·æ¥è¯¢ï¼æ¯æé¢è§åä¸è½½ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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 } |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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 } |
| | | }) |
| | | } |
| | |
| | | </el-row> |
| | | </div> |
| | | </div> |
| | | <!-- Tab忢åºå --> |
| | | <el-tabs v-model="detailActiveTab" class="detail-tabs"> |
| | | <!-- æ´½è°è¿åº¦è®°å½ --> |
| | | <div class="detail-section"> |
| | | <el-tab-pane label="æ´½è°è¿åº¦è®°å½" name="negotiation"> |
| | | <div class="section-header"> |
| | | <h3 class="section-title">æ´½è°è¿åº¦è®°å½</h3> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="openNegotiationDialog(detailForm)"> |
| | |
| | | <Paperclip /> |
| | | </el-icon> |
| | | éä»¶ |
| | | <!-- {{ row.fileList && row.fileList.length > 0 ? row.fileList.length : 'ä¸ä¼ ' }} --> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | 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> |
| | |
| | | </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> |
| | | |
| | |
| | | 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(); |
| | | |
| | |
| | | |
| | | // 详æ
ç¸å
³ |
| | | const detailDialogVisible = ref(false); |
| | | const detailActiveTab = ref("negotiation"); |
| | | const detailForm = reactive({ |
| | | customerName: "", |
| | | customerType: "", |
| | |
| | | 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); |
| | |
| | | |
| | | // æå¼è¯¦æ
å¼¹çª |
| | | 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 = []; |
| | | }; |
| | | |
| | | // ä¿®æ¹æ´½è°è®°å½ |
| | |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .detail-tabs { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .attachment-section { |
| | | .upload-area { |
| | | margin-bottom: 20px; |
| | |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | | <el-tabs v-model="supplierActiveTab"> |
| | | <!-- åºæ¬ä¿¡æ¯ Tab --> |
| | | <el-tab-pane label="åºæ¬ä¿¡æ¯" name="basic"> |
| | | <el-form |
| | | :model="form" |
| | | label-width="140px" |
| | |
| | | </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> |
| | | |
| | | <!-- ä¾åºå导å
¥å¯¹è¯æ¡ --> |
| | |
| | | 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") |
| | | ); |
| | |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | |
| | | // ä¾åºå详æ
Tabç¸å
³ |
| | | 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); |
| | |
| | | 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) => { |
| | |
| | | show-overflow-tooltip /> |
| | | <el-table-column fixed="right" |
| | | label="æä½" |
| | | width="120" |
| | | width="200" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | |
| | | <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> |
| | |
| | | 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="éè´ååå·ï¼" |
| | |
| | | <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" |
| | |
| | | </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"> |
| | |
| | | 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> |
| | | |
| | |
| | | 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); |
| | |
| | | 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"; |
| | | |
| | |
| | | å¾
å
¥åº: "info", // å¾
å
¥åº - ç°è² |
| | | å
¥åºä¸: "warning", // å
¥åºä¸ - æ©è² |
| | | å®å
¨å
¥åº: "success", // å®å
¨å
¥åº - ç»¿è² |
| | | 已驳å: "danger", // 已驳å - çº¢è² |
| | | }; |
| | | return typeMap[status] || ""; |
| | | }; |
| | |
| | | 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("å è½½æ°æ®å¤±è´¥"); |
| | |
| | | } |
| | | }; |
| | | |
| | | // ä¾åºåéæ©ååæ¶ï¼å 载该ä¾åºåçååå表 |
| | | 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(); |
| | |
| | | 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([ |
| | |
| | | </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 |
| | |
| | | </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> |
| | |
| | | <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"> |
| | |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | filterable |
| | | @change="handleCustomerChange" |
| | | > |
| | | <el-option |
| | | v-for="item in customerOption" |
| | |
| | | </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" |
| | |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¾è®¢æ¥æï¼" prop="executionDate"> |
| | | <el-date-picker |
| | |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼"> |
| | | <el-select |
| | |
| | | </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 |
| | |
| | | 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" |
| | |
| | | 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") |
| | |
| | | const total = ref(0); |
| | | const fileList = ref([]); |
| | | const deliveryFileList = ref([]); |
| | | // ååå表ï¼ä¾ä¸æéæ©ï¼ |
| | | const contractList = ref([]); |
| | | // æ»ååéä»¶å¼¹çª |
| | | const contractFileDialogVisible = ref(false); |
| | | const contractFileList = ref([]); |
| | | |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const operationType = ref(""); |
| | |
| | | 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() |
| | |
| | | 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(); |
| | |
| | | > |
| | | <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" /> |
| | |
| | | <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> |
| | |
| | | <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> |
| | |
| | | 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) |
| | |
| | | const userList = ref([]) |
| | | const productOptions = ref([]); |
| | | const modelOptions = ref([]); |
| | | const contractList = ref([]); |
| | | const pagination = reactive({ |
| | | total: 3, |
| | | currentPage: 1, |
| | |
| | | paymentMethod: '', |
| | | status: 'è稿', |
| | | remark: '', |
| | | masterContractNo: '', |
| | | products: [], |
| | | subtotal: 0, |
| | | freight: 0, |
| | |
| | | // 计ç®å±æ§ |
| | | const filteredList = computed(() => { |
| | | let list = quotationList.value |
| | | console.log(list) |
| | | return list |
| | | }) |
| | | |
| | |
| | | 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) => { |
| | |
| | | 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 => ({ |
| | |
| | | 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 以便æ éæ©å¨åæ¾ |
| | |
| | | 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 = () => { |
| | |
| | | customer: item.customer || '', |
| | | customerId: item.customerId || undefined, |
| | | salesperson: item.salesperson || '', |
| | | masterContractNo: item.masterContractNo || '', |
| | | quotationDate: item.quotationDate || '', |
| | | validDate: item.validDate || '', |
| | | paymentMethod: item.paymentMethod || '', |