1、报价系统中,需将付款方式改为选择项(现金、电汇、微信、支付宝等),不能做成手工输入;4、退货单未明确退货规则;5、客户往来信息过于简单,只做总结性信息,无多维度明细信息;
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # 客æ·å¾æ¥å¤ç»´åº¦æç»åè½å端èè°ææ¡£ |
| | | |
| | | > ä¼å客æ·å¾æ¥åè½ï¼æ°å¢å¤ç»´åº¦æç»æ¥è¯¢æ¥å£ï¼æ¯æäº§åæç»ååè´§æç»ç»´åº¦ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - è¥é管ç / 客æ·å¾æ¥ - 客æ·å¾æ¥å表页 |
| | | - è¥é管ç / 客æ·å¾æ¥ - 客æ·å¾æ¥è¯¦æ
é¡µï¼æ°å¢ï¼ |
| | | - è¥é管ç / 客æ·å¾æ¥ - 产åæç»Tabï¼æ°å¢ï¼ |
| | | - è¥é管ç / 客æ·å¾æ¥ - åè´§æç»Tabï¼æ°å¢ï¼ |
| | | |
| | | ## API |
| | | |
| | | ### 1. 客æ·å¾æ¥å表ï¼åææ¥å£ï¼æªåæ´ï¼ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /metricStatistics/customewTransactions | 客æ·å¾æ¥å表 | |
| | | |
| | | **请æ±åæ°ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | pageNum | Long | å¦ | 页ç ï¼é»è®¤1 | |
| | | | pageSize | Long | å¦ | æ¯é¡µæ¡æ°ï¼é»è®¤10 | |
| | | | customerName | String | å¦ | 客æ·åç§°ï¼æ¨¡ç³æç´¢ï¼ | |
| | | |
| | | **ååºå段ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | customerId | Long | 客æ·ID | |
| | | | customerName | String | 客æ·åç§° | |
| | | | contractAmounts | BigDecimal | ååæ»éé¢ | |
| | | | receiptPaymentAmount | BigDecimal | æ¶æ¬¾éé¢ | |
| | | | receiptableAmount | BigDecimal | åºæ¶éé¢ | |
| | | |
| | | --- |
| | | |
| | | ### 2. 客æ·å¾æ¥ç»è®¡æ±æ»ï¼æ°å¢ï¼ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /metricStatistics/customerTransactionsSummary | 客æ·å¾æ¥ç»è®¡æ±æ» | |
| | | |
| | | **请æ±åæ°ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | customerId | Long | æ¯ | 客æ·ID | |
| | | |
| | | **ååºå段ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | customerId | Long | 客æ·ID | |
| | | | customerName | String | 客æ·åç§° | |
| | | | contractAmounts | BigDecimal | ååæ»éé¢ | |
| | | | contractCount | Integer | ååæ°é | |
| | | | productCount | Integer | 产åç§ç±»æ° | |
| | | | shippedAmounts | BigDecimal | åè´§æ»éé¢ | |
| | | | shippedQuantity | BigDecimal | åè´§æ»æ°é | |
| | | | receivedAmounts | BigDecimal | æ¶æ¬¾éé¢ | |
| | | | receivableAmounts | BigDecimal | åºæ¶éé¢ | |
| | | | returnAmounts | BigDecimal | éè´§éé¢ | |
| | | | unshippedAmounts | BigDecimal | æªåè´§éé¢ | |
| | | | receivedRate | BigDecimal | æ¶æ¬¾ç(%) | |
| | | | shippedRate | BigDecimal | åè´§ç(%) | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "msg": "æä½æå", |
| | | "data": { |
| | | "customerId": 1, |
| | | "customerName": "客æ·A", |
| | | "contractAmounts": 100000.00, |
| | | "contractCount": 5, |
| | | "productCount": 12, |
| | | "shippedAmounts": 80000.00, |
| | | "shippedQuantity": 500, |
| | | "receivedAmounts": 60000.00, |
| | | "receivableAmounts": 20000.00, |
| | | "returnAmounts": 5000.00, |
| | | "unshippedAmounts": 20000.00, |
| | | "receivedRate": 75.00, |
| | | "shippedRate": 80.00 |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ### 3. 客æ·å¾æ¥äº§åæç»ï¼æ°å¢ï¼ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /metricStatistics/customerTransactionsProducts | 客æ·å¾æ¥äº§åæç» | |
| | | |
| | | **请æ±åæ°ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | customerId | Long | æ¯ | 客æ·ID | |
| | | | salesLedgerId | Long | å¦ | éå®å°è´¦IDï¼å¯éï¼ç¨äºçéæååï¼ | |
| | | | pageNum | Long | å¦ | 页ç ï¼é»è®¤1 | |
| | | | pageSize | Long | å¦ | æ¯é¡µæ¡æ°ï¼é»è®¤10 | |
| | | |
| | | **ååºå段ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | salesLedgerId | Long | éå®å°è´¦ID | |
| | | | salesContractNo | String | éå®ååå· | |
| | | | productId | Long | 产åID | |
| | | | productName | String | 产ååç§° | |
| | | | model | String | è§æ ¼åå· | |
| | | | unit | String | åä½ | |
| | | | contractQuantity | BigDecimal | ååæ°é | |
| | | | taxInclusiveUnitPrice | BigDecimal | åååä»·(å«ç¨) | |
| | | | contractAmount | BigDecimal | ååéé¢ | |
| | | | shippedQuantity | BigDecimal | å·²åè´§æ°é | |
| | | | shippedAmount | BigDecimal | å·²åè´§éé¢ | |
| | | | receivedAmount | BigDecimal | å·²æ¶æ¬¾éé¢ | |
| | | | receivableAmount | BigDecimal | åºæ¶éé¢ | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "msg": "æä½æå", |
| | | "data": { |
| | | "records": [ |
| | | { |
| | | "salesLedgerId": 1, |
| | | "salesContractNo": "HT-2026-001", |
| | | "productId": 10, |
| | | "productName": "产åA", |
| | | "model": "è§æ ¼1", |
| | | "unit": "ä»¶", |
| | | "contractQuantity": 100, |
| | | "taxInclusiveUnitPrice": 50.00, |
| | | "contractAmount": 5000.00, |
| | | "shippedQuantity": 80, |
| | | "shippedAmount": 4000.00, |
| | | "receivedAmount": 3000.00, |
| | | "receivableAmount": 1000.00 |
| | | } |
| | | ], |
| | | "total": 25, |
| | | "pageNum": 1, |
| | | "pageSize": 10 |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ### 4. 客æ·å¾æ¥åè´§æç»ï¼æ°å¢ï¼ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /metricStatistics/customerTransactionsShipments | 客æ·å¾æ¥åè´§æç» | |
| | | |
| | | **请æ±åæ°ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | customerId | Long | æ¯ | 客æ·ID | |
| | | | salesLedgerId | Long | å¦ | éå®å°è´¦IDï¼å¯éï¼ç¨äºçéæååï¼ | |
| | | | pageNum | Long | å¦ | 页ç ï¼é»è®¤1 | |
| | | | pageSize | Long | å¦ | æ¯é¡µæ¡æ°ï¼é»è®¤10 | |
| | | |
| | | **ååºå段ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | salesLedgerId | Long | éå®å°è´¦ID | |
| | | | salesContractNo | String | éå®ååå· | |
| | | | shippingId | Long | åè´§åID | |
| | | | shippingNo | String | åè´§åå· | |
| | | | productName | String | 产ååç§° | |
| | | | model | String | è§æ ¼åå· | |
| | | | shippingQuantity | BigDecimal | åè´§æ°é | |
| | | | shippingAmount | BigDecimal | åè´§éé¢(å«ç¨) | |
| | | | batchNo | String | åºåºæ¹å· | |
| | | | shippingDate | LocalDate | åè´§æ¥æ | |
| | | | approvalStatus | Integer | 审æ¹ç¶æ(0å¾
审/1已审) | |
| | | | receivedAmount | BigDecimal | å·²æ¶æ¬¾éé¢ | |
| | | | receivableAmount | BigDecimal | åºæ¶éé¢ | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "msg": "æä½æå", |
| | | "data": { |
| | | "records": [ |
| | | { |
| | | "salesLedgerId": 1, |
| | | "salesContractNo": "HT-2026-001", |
| | | "shippingId": 100, |
| | | "shippingNo": "FH-2026-001", |
| | | "productName": "产åA", |
| | | "model": "è§æ ¼1", |
| | | "shippingQuantity": 50, |
| | | "shippingAmount": 2500.00, |
| | | "batchNo": "20260618001", |
| | | "shippingDate": "2026-06-18", |
| | | "approvalStatus": 1, |
| | | "receivedAmount": 2000.00, |
| | | "receivableAmount": 500.00 |
| | | } |
| | | ], |
| | | "total": 30, |
| | | "pageNum": 1, |
| | | "pageSize": 10 |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## å端页é¢è®¾è®¡ |
| | | |
| | | ### 1. 客æ·å¾æ¥å表页ï¼ä¼åï¼ |
| | | |
| | | ```html |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- æç´¢æ --> |
| | | <el-form :model="queryParams" ref="queryForm" :inline="true"> |
| | | <el-form-item label="客æ·åç§°" prop="customerName"> |
| | | <el-input v-model="queryParams.customerName" placeholder="请è¾å
¥å®¢æ·åç§°" clearable /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getList">æç´¢</el-button> |
| | | <el-button @click="resetQuery">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- æ°æ®è¡¨æ ¼ --> |
| | | <el-table :data="list" v-loading="loading"> |
| | | <el-table-column label="客æ·åç§°" prop="customerName" /> |
| | | <el-table-column label="ååæ»éé¢" prop="contractAmounts" align="right"> |
| | | <template #default="{ row }"> |
| | | {{ formatMoney(row.contractAmounts) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¶æ¬¾éé¢" prop="receiptPaymentAmount" align="right"> |
| | | <template #default="{ row }"> |
| | | {{ formatMoney(row.receiptPaymentAmount) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åºæ¶éé¢" prop="receiptableAmount" align="right"> |
| | | <template #default="{ row }"> |
| | | <span :class="{ 'text-danger': row.receiptableAmount > 0 }"> |
| | | {{ formatMoney(row.receiptableAmount) }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="150" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button type="text" @click="viewDetail(row)">æ¥çæç»</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <el-pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | :page.sync="queryParams.pageNum" |
| | | :limit.sync="queryParams.pageSize" |
| | | @pagination="getList" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'CustomerTransactions', |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | list: [], |
| | | total: 0, |
| | | queryParams: { |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | customerName: '' |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | this.getList() |
| | | }, |
| | | methods: { |
| | | getList() { |
| | | this.loading = true |
| | | this.$axios.get('/metricStatistics/customewTransactions', { params: this.queryParams }) |
| | | .then(res => { |
| | | this.list = res.data.records |
| | | this.total = res.data.total |
| | | }) |
| | | .finally(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | resetQuery() { |
| | | this.queryParams.customerName = '' |
| | | this.getList() |
| | | }, |
| | | formatMoney(value) { |
| | | if (!value) return '0.00' |
| | | return Number(value).toFixed(2) |
| | | }, |
| | | viewDetail(row) { |
| | | this.$router.push({ path: '/sales/customerTransactions/detail', query: { customerId: row.customerId } }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ### 2. 客æ·å¾æ¥è¯¦æ
é¡µï¼æ°å¢ï¼ |
| | | |
| | | ```html |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- é¡¶é¨ç»è®¡å¡ç --> |
| | | <el-card class="summary-card"> |
| | | <div slot="header"> |
| | | <span>{{ summary.customerName }} - 徿¥ç»è®¡</span> |
| | | </div> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="4"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">ååæ»éé¢</div> |
| | | <div class="stat-value">{{ formatMoney(summary.contractAmounts) }}</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">ååæ°é</div> |
| | | <div class="stat-value">{{ summary.contractCount }}份</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">产åç§ç±»</div> |
| | | <div class="stat-value">{{ summary.productCount }}ç§</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">åè´§éé¢</div> |
| | | <div class="stat-value">{{ formatMoney(summary.shippedAmounts) }}</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">æ¶æ¬¾éé¢</div> |
| | | <div class="stat-value text-success">{{ formatMoney(summary.receivedAmounts) }}</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">åºæ¶éé¢</div> |
| | | <div class="stat-value text-danger">{{ formatMoney(summary.receivableAmounts) }}</div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20" style="margin-top: 15px;"> |
| | | <el-col :span="4"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">éè´§éé¢</div> |
| | | <div class="stat-value">{{ formatMoney(summary.returnAmounts) }}</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">æªåè´§éé¢</div> |
| | | <div class="stat-value text-warning">{{ formatMoney(summary.unshippedAmounts) }}</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">æ¶æ¬¾ç</div> |
| | | <div class="stat-value"> |
| | | <el-progress :percentage="summary.receivedRate || 0" :stroke-width="18" /> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">åè´§ç</div> |
| | | <div class="stat-value"> |
| | | <el-progress :percentage="summary.shippedRate || 0" :stroke-width="18" color="#67c23a" /> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </el-card> |
| | | |
| | | <!-- Tab 忢 --> |
| | | <el-tabs v-model="activeTab" @tab-click="handleTabChange"> |
| | | <el-tab-pane label="产åæç»" name="products"> |
| | | <product-table :customerId="customerId" /> |
| | | </el-tab-pane> |
| | | <el-tab-pane label="åè´§æç»" name="shipments"> |
| | | <shipment-table :customerId="customerId" /> |
| | | </el-tab-pane> |
| | | <el-tab-pane label="ååæç»" name="contracts"> |
| | | <contract-table :customerId="customerId" /> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import ProductTable from './components/ProductTable.vue' |
| | | import ShipmentTable from './components/ShipmentTable.vue' |
| | | import ContractTable from './components/ContractTable.vue' |
| | | |
| | | export default { |
| | | name: 'CustomerTransactionsDetail', |
| | | components: { ProductTable, ShipmentTable, ContractTable }, |
| | | data() { |
| | | return { |
| | | customerId: null, |
| | | summary: {}, |
| | | activeTab: 'products' |
| | | } |
| | | }, |
| | | created() { |
| | | this.customerId = this.$route.query.customerId |
| | | if (this.customerId) { |
| | | this.getSummary() |
| | | } |
| | | }, |
| | | methods: { |
| | | getSummary() { |
| | | this.$axios.get('/metricStatistics/customerTransactionsSummary', { |
| | | params: { customerId: this.customerId } |
| | | }).then(res => { |
| | | this.summary = res.data |
| | | }) |
| | | }, |
| | | formatMoney(value) { |
| | | if (!value) return '0.00' |
| | | return Number(value).toFixed(2) |
| | | }, |
| | | handleTabChange(tab) { |
| | | // Tab 忢æ¶å·æ°åç»ä»¶æ°æ® |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .summary-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | .stat-item { |
| | | text-align: center; |
| | | } |
| | | .stat-label { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | } |
| | | .stat-value { |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | margin-top: 5px; |
| | | } |
| | | .text-success { |
| | | color: #67c23a; |
| | | } |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | } |
| | | .text-warning { |
| | | color: #e6a23c; |
| | | } |
| | | </style> |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ### 3. 产åæç»ç»ä»¶ (ProductTable.vue) |
| | | |
| | | ```html |
| | | <template> |
| | | <div> |
| | | <!-- çé --> |
| | | <el-form :inline="true" size="small"> |
| | | <el-form-item label="ååå·"> |
| | | <el-select v-model="filterData.salesLedgerId" clearable placeholder="å
¨é¨åå" @change="getList"> |
| | | <el-option v-for="item in contractList" :key="item.id" :label="item.salesContractNo" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- è¡¨æ ¼ --> |
| | | <el-table :data="list" v-loading="loading" size="small"> |
| | | <el-table-column label="ååå·" prop="salesContractNo" width="150" /> |
| | | <el-table-column label="产ååç§°" prop="productName" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="model" width="120" /> |
| | | <el-table-column label="åä½" prop="unit" width="80" /> |
| | | <el-table-column label="ååæ°é" prop="contractQuantity" align="right" width="100" /> |
| | | <el-table-column label="åååä»·" prop="taxInclusiveUnitPrice" align="right" width="100"> |
| | | <template #default="{ row }"> |
| | | {{ formatMoney(row.taxInclusiveUnitPrice) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ååéé¢" prop="contractAmount" align="right" width="120"> |
| | | <template #default="{ row }"> |
| | | {{ formatMoney(row.contractAmount) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å·²åè´§æ°é" prop="shippedQuantity" align="right" width="100" /> |
| | | <el-table-column label="å·²åè´§éé¢" prop="shippedAmount" align="right" width="120"> |
| | | <template #default="{ row }"> |
| | | {{ formatMoney(row.shippedAmount) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å·²æ¶æ¬¾éé¢" prop="receivedAmount" align="right" width="120"> |
| | | <template #default="{ row }"> |
| | | <span class="text-success">{{ formatMoney(row.receivedAmount) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åºæ¶éé¢" prop="receivableAmount" align="right" width="120"> |
| | | <template #default="{ row }"> |
| | | <span :class="{ 'text-danger': row.receivableAmount > 0 }"> |
| | | {{ formatMoney(row.receivableAmount) }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åè´§è¿åº¦" width="150"> |
| | | <template #default="{ row }"> |
| | | <el-progress |
| | | :percentage="calcPercent(row.shippedQuantity, row.contractQuantity)" |
| | | :stroke-width="10" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <el-pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | :page.sync="queryParams.pageNum" |
| | | :limit.sync="queryParams.pageSize" |
| | | layout="total, prev, pager, next" |
| | | @pagination="getList" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'ProductTable', |
| | | props: { |
| | | customerId: { |
| | | type: Number, |
| | | required: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | list: [], |
| | | total: 0, |
| | | queryParams: { |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | customerId: null, |
| | | salesLedgerId: null |
| | | }, |
| | | filterData: { |
| | | salesLedgerId: null |
| | | }, |
| | | contractList: [] |
| | | } |
| | | }, |
| | | watch: { |
| | | customerId(val) { |
| | | if (val) { |
| | | this.queryParams.customerId = val |
| | | this.getList() |
| | | this.getContractList() |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | getList() { |
| | | this.loading = true |
| | | this.queryParams.salesLedgerId = this.filterData.salesLedgerId |
| | | this.$axios.get('/metricStatistics/customerTransactionsProducts', { params: this.queryParams }) |
| | | .then(res => { |
| | | this.list = res.data.records |
| | | this.total = res.data.total |
| | | }) |
| | | .finally(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | getContractList() { |
| | | // è·å该客æ·çååå表ç¨äºçé |
| | | this.$axios.get('/metricStatistics/customewTransactionsDetails', { |
| | | params: { customerId: this.customerId, pageNum: 1, pageSize: 100 } |
| | | }).then(res => { |
| | | this.contractList = res.data.records |
| | | }) |
| | | }, |
| | | formatMoney(value) { |
| | | if (!value) return '0.00' |
| | | return Number(value).toFixed(2) |
| | | }, |
| | | calcPercent(shipped, total) { |
| | | if (!total || total === 0) return 0 |
| | | return Math.round((shipped / total) * 100) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ### 4. åè´§æç»ç»ä»¶ (ShipmentTable.vue) |
| | | |
| | | ```html |
| | | <template> |
| | | <div> |
| | | <!-- çé --> |
| | | <el-form :inline="true" size="small"> |
| | | <el-form-item label="ååå·"> |
| | | <el-select v-model="filterData.salesLedgerId" clearable placeholder="å
¨é¨åå" @change="getList"> |
| | | <el-option v-for="item in contractList" :key="item.id" :label="item.salesContractNo" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- è¡¨æ ¼ --> |
| | | <el-table :data="list" v-loading="loading" size="small"> |
| | | <el-table-column label="ååå·" prop="salesContractNo" width="150" /> |
| | | <el-table-column label="åè´§åå·" prop="shippingNo" width="150" /> |
| | | <el-table-column label="产ååç§°" prop="productName" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="model" width="120" /> |
| | | <el-table-column label="åè´§æ°é" prop="shippingQuantity" align="right" width="100" /> |
| | | <el-table-column label="åè´§éé¢" prop="shippingAmount" align="right" width="120"> |
| | | <template #default="{ row }"> |
| | | {{ formatMoney(row.shippingAmount) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åºåºæ¹å·" prop="batchNo" width="150" /> |
| | | <el-table-column label="åè´§æ¥æ" prop="shippingDate" width="120" /> |
| | | <el-table-column label="审æ¹ç¶æ" prop="approvalStatus" width="100"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.approvalStatus === 1 ? 'success' : 'warning'" size="mini"> |
| | | {{ row.approvalStatus === 1 ? '已审' : 'å¾
审' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å·²æ¶æ¬¾éé¢" prop="receivedAmount" align="right" width="120"> |
| | | <template #default="{ row }"> |
| | | <span class="text-success">{{ formatMoney(row.receivedAmount) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åºæ¶éé¢" prop="receivableAmount" align="right" width="120"> |
| | | <template #default="{ row }"> |
| | | <span :class="{ 'text-danger': row.receivableAmount > 0 }"> |
| | | {{ formatMoney(row.receivableAmount) }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <el-pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | :page.sync="queryParams.pageNum" |
| | | :limit.sync="queryParams.pageSize" |
| | | layout="total, prev, pager, next" |
| | | @pagination="getList" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'ShipmentTable', |
| | | props: { |
| | | customerId: { |
| | | type: Number, |
| | | required: true |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | list: [], |
| | | total: 0, |
| | | queryParams: { |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | customerId: null, |
| | | salesLedgerId: null |
| | | }, |
| | | filterData: { |
| | | salesLedgerId: null |
| | | }, |
| | | contractList: [] |
| | | } |
| | | }, |
| | | watch: { |
| | | customerId(val) { |
| | | if (val) { |
| | | this.queryParams.customerId = val |
| | | this.getList() |
| | | this.getContractList() |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | getList() { |
| | | this.loading = true |
| | | this.queryParams.salesLedgerId = this.filterData.salesLedgerId |
| | | this.$axios.get('/metricStatistics/customerTransactionsShipments', { params: this.queryParams }) |
| | | .then(res => { |
| | | this.list = res.data.records |
| | | this.total = res.data.total |
| | | }) |
| | | .finally(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | getContractList() { |
| | | this.$axios.get('/metricStatistics/customewTransactionsDetails', { |
| | | params: { customerId: this.customerId, pageNum: 1, pageSize: 100 } |
| | | }).then(res => { |
| | | this.contractList = res.data.records |
| | | }) |
| | | }, |
| | | formatMoney(value) { |
| | | if (!value) return '0.00' |
| | | return Number(value).toFixed(2) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | 1. **è·¯ç±é
ç½®**ï¼éå¨è·¯ç±ä¸æ°å¢å®¢æ·å¾æ¥è¯¦æ
é¡µè·¯ç± `/sales/customerTransactions/detail` |
| | | 2. **ç»ä»¶æå**ï¼äº§åæç»ååè´§æç»å»ºè®®æå为ç¬ç«ç»ä»¶ï¼ä¾¿äºå¤ç¨åç»´æ¤ |
| | | 3. **çéèå¨**ï¼äº§åæç»ååè´§æç»æ¯ææååçéï¼ååå表ä»åææ¥å£è·å |
| | | 4. **æ°æ®æ ¼å¼**ï¼éé¢å段éç»ä¸ä½¿ç¨ `formatMoney` æ¹æ³æ ¼å¼åæ¾ç¤º |
| | | 5. **è¿åº¦æ¡æ¾ç¤º**ï¼äº§åæç»ä¸çåè´§è¿åº¦ä½¿ç¨ `el-progress` ç»ä»¶ç´è§å±ç¤º |
| | | 6. **ç¶ææ è¯**ï¼åºæ¶éé¢å¤§äº0æ¶ä½¿ç¨çº¢è²æ è¯ï¼å·²æ¶æ¬¾ä½¿ç¨ç»¿è²æ è¯ |
| | | 7. **审æ¹ç¶æ**ï¼åè´§æç»ä¸ç审æ¹ç¶æä½¿ç¨ `el-tag` å±ç¤ºï¼å·²å®¡ä¸ºç»¿è²ï¼å¾
审为é»è² |
| | | |
| | | --- |
| | | |
| | | ## æ°æ®å¯¹æ¯ |
| | | |
| | | ### ä¼åå vs ä¼åå |
| | | |
| | | | 维度 | ä¼åå | ä¼åå | |
| | | |------|--------|--------| |
| | | | 客æ·å¾æ¥ | åªæååéé¢ãæ¶æ¬¾ãåºæ¶ | æ°å¢ååæ°ãäº§åæ°ãåè´§çãæ¶æ¬¾çç12é¡¹ææ | |
| | | | æç»ç»´åº¦ | ä»
ååæç» | æ°å¢äº§åæç»ãåè´§æç» | |
| | | | çéè½å | ä»
æå®¢æ·åçé | æ¯ææååçé产å/åè´§æç» | |
| | | | æ°æ®è¿½æº¯ | æ æ³è¿½æº¯å
·ä½åè´§ | å¯è¿½æº¯æ¯æ¡åè´§è®°å½çæ¶æ¬¾æ
åµ | |
| | | | è¿åº¦å±ç¤º | æ | åè´§è¿åº¦æ¡ç´è§å±ç¤º | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # éè´§ç¶æåæ®µæ¥å£åæ´å端èè°ææ¡£ |
| | | |
| | | > éå®éè´§ãåè´§å°è´¦ãéå®å°è´¦æ¥å£æ°å¢éè´§ç¶æåæ®µ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - éå®ç®¡ç / éå®éè´§ - æ°å¢/ç¼è¾éè´§åé¡µé¢ |
| | | - éå®ç®¡ç / åè´§å°è´¦åè¡¨é¡µé¢ |
| | | - éå®ç®¡ç / éå®å°è´¦åè¡¨é¡µé¢ |
| | | |
| | | --- |
| | | |
| | | ## API åæ´ |
| | | |
| | | ### 1. éå®éè´§-åè´§ä¿¡æ¯æ¥è¯¢ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /shippingInfo/getForReturn | éå®éè´§-éè¿å®¢æ·åç§°æ¥è¯¢åè´§ä¿¡æ¯ | |
| | | |
| | | **æ°å¢ååºå段ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | returnedQuantity | BigDecimal | å·²éè´§æ°é | |
| | | | returnStatus | String | éè´§ç¶æï¼æ éè´§/é¨åéè´§/å
¨é¨éè´§ | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "data": [ |
| | | { |
| | | "shippingId": 100, |
| | | "shippingNo": "FH-2026-001", |
| | | "shippingQuantity": 50, |
| | | "returnedQuantity": 10, |
| | | "returnStatus": "é¨åéè´§", |
| | | "batchNo": "20260618001", |
| | | "displayLabel": "20260618001-京A12345-2026-06-18" |
| | | } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ### 2. åè´§å°è´¦å表 |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /shippingInfo/listPage | åè´§ä¿¡æ¯å页å表 | |
| | | |
| | | **æ°å¢ååºå段ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | returnedQuantity | BigDecimal | å·²éè´§æ°é | |
| | | | returnStatus | String | éè´§ç¶æï¼æ éè´§/é¨åéè´§/å
¨é¨éè´§ | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "data": { |
| | | "records": [ |
| | | { |
| | | "id": 100, |
| | | "shippingNo": "FH-2026-001", |
| | | "totalQuantity": 50, |
| | | "returnedQuantity": 0, |
| | | "returnStatus": "æ éè´§" |
| | | } |
| | | ] |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ### 3. éå®å°è´¦å表 |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /salesLedger/listPage | éå®å°è´¦å页å表 | |
| | | |
| | | **æ°å¢ååºå段ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | returnedQuantity | BigDecimal | å·²éè´§æ°éï¼è¯¥éå®åå䏿æåè´§ç累计éè´§æ°ï¼ | |
| | | | returnStatus | String | éè´§ç¶æï¼æ éè´§/é¨åéè´§/å
¨é¨éè´§ | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "data": { |
| | | "records": [ |
| | | { |
| | | "id": 1, |
| | | "salesContractNo": "HT-2026-001", |
| | | "contractAmount": 100000, |
| | | "returnedQuantity": 30, |
| | | "returnStatus": "é¨åéè´§" |
| | | } |
| | | ] |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## å端修æ¹ç¹ |
| | | |
| | | ### 1. åè´§å°è´¦åè¡¨é¡µé¢ |
| | | |
| | | **æ°å¢è¡¨æ ¼åï¼** |
| | | |
| | | ```html |
| | | <el-table-column label="éè´§ç¶æ" prop="returnStatus" width="100"> |
| | | <template #default="{ row }"> |
| | | <el-tag |
| | | :type="row.returnStatus === 'æ éè´§' ? 'success' : (row.returnStatus === 'å
¨é¨éè´§' ? 'danger' : 'warning')" |
| | | > |
| | | {{ row.returnStatus }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å·²éè´§æ°é" prop="returnedQuantity" width="120" /> |
| | | ``` |
| | | |
| | | ### 2. éå®å°è´¦åè¡¨é¡µé¢ |
| | | |
| | | **æ°å¢è¡¨æ ¼åï¼** |
| | | |
| | | ```html |
| | | <el-table-column label="éè´§ç¶æ" prop="returnStatus" width="100"> |
| | | <template #default="{ row }"> |
| | | <el-tag |
| | | :type="row.returnStatus === 'æ éè´§' ? 'success' : (row.returnStatus === 'å
¨é¨éè´§' ? 'danger' : 'warning')" |
| | | > |
| | | {{ row.returnStatus }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | ``` |
| | | |
| | | ### 3. éå®éè´§é¡µé¢ - åè´§ä¿¡æ¯éæ© |
| | | |
| | | **䏿é项å¢å¼ºæ¾ç¤ºï¼** |
| | | |
| | | ```html |
| | | <el-select v-model="form.shippingId" placeholder="è¯·éæ©åè´§ä¿¡æ¯" filterable> |
| | | <el-option |
| | | v-for="item in shippingList" |
| | | :key="item.shippingId" |
| | | :label="item.displayLabel" |
| | | :value="item.shippingId" |
| | | > |
| | | <div style="display: flex; justify-content: space-between;"> |
| | | <span>{{ item.displayLabel }}</span> |
| | | <el-tag |
| | | size="small" |
| | | :type="item.returnStatus === 'æ éè´§' ? 'success' : 'warning'" |
| | | style="margin-left: 8px;" |
| | | > |
| | | {{ item.returnStatus }} |
| | | </el-tag> |
| | | </div> |
| | | </el-option> |
| | | </el-select> |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## éè´§ç¶æè®¡ç®é»è¾ |
| | | |
| | | | æ¡ä»¶ | ç¶æ | |
| | | |------|------| |
| | | | éè´§æ°é = 0 | æ éè´§ | |
| | | | éè´§æ°é >= åè´§æ°é | å
¨é¨éè´§ | |
| | | | éè´§æ°é > 0 ä¸ < åè´§æ°é | é¨åéè´§ | |
| | | |
| | | --- |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | 1. **éå®å°è´¦çéè´§æ°é**ï¼æ¯æéå®ååç»´åº¦çæ±æ»ï¼å³è¯¥åå䏿æåè´§åç累计éè´§æ°é |
| | | |
| | | 2. **åè´§å°è´¦çéè´§æ°é**ï¼æ¯æåæ¡åè´§è®°å½ç»´åº¦çéè´§æ°é |
| | | |
| | | 3. **å端å±ç¤ºå»ºè®®**ï¼ |
| | | - æ éè´§ï¼ç»¿è²æ ç¾ (`success`) |
| | | - é¨åéè´§ï¼é»è²æ ç¾ (`warning`) |
| | | - å
¨é¨éè´§ï¼çº¢è²æ ç¾ (`danger`) |
| | | |
| | | 4. **éå®éè´§éæ©åè´§ä¿¡æ¯æ¶**ï¼å端å¯ä»¥æ ¹æ® `returnStatus` è¿æ»¤ææç¤ºç¨æ·ï¼é¿å
鿩已å
¨é¨éè´§çåè´§è®°å½ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # éå®éè´§åè´§ä¿¡æ¯æ¥å£ä¼åå端èè°ææ¡£ |
| | | |
| | | > ä¼åéå®éè´§åºæ¯ä¸çåè´§ä¿¡æ¯æ¥è¯¢æ¥å£ï¼æ°å¢æ¹æ¬¡å·å段ï¼å¹¶æä¾ç»åæ¾ç¤ºæ ç¾ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - éå®ç®¡ç / éå®éè´§ - æ°å¢/ç¼è¾éè´§åé¡µé¢ |
| | | - éå®éè´§ - éæ©åè´§åä¸ææ¡ |
| | | |
| | | ## API |
| | | |
| | | ### æ°å¢æ¥å£ï¼éå®éè´§-åè´§ä¿¡æ¯æ¥è¯¢ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /shippingInfo/getForReturn | éå®éè´§-éè¿å®¢æ·åç§°æ¥è¯¢åè´§ä¿¡æ¯ï¼å«æ¹æ¬¡å·ï¼ | |
| | | |
| | | **请æ±åæ°ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | customerName | String | æ¯ | 客æ·åç§° | |
| | | |
| | | **ååºå段ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | shippingId | Long | åè´§åID | |
| | | | shippingNo | String | åè´§åå· | |
| | | | salesContractNo | String | éå®ååå· | |
| | | | customerName | String | 客æ·åç§° | |
| | | | productName | String | 产ååç§° | |
| | | | model | String | è§æ ¼åå· | |
| | | | shippingQuantity | BigDecimal | åè´§æ°é | |
| | | | batchNo | String | æ¹æ¬¡å· | |
| | | | shippingCarNumber | String | 车çå· | |
| | | | createTime | LocalDateTime | å建æ¶é´ | |
| | | | displayLabel | String | æ¾ç¤ºæ ç¾ï¼æ¹æ¬¡å·-车çå·-å建æ¶é´ï¼ | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "msg": "æä½æå", |
| | | "data": [ |
| | | { |
| | | "shippingId": 100, |
| | | "shippingNo": "FH-2026-001", |
| | | "salesContractNo": "HT-2026-001", |
| | | "customerName": "客æ·A", |
| | | "productName": "产åA", |
| | | "model": "è§æ ¼1", |
| | | "shippingQuantity": 50, |
| | | "batchNo": "20260618001", |
| | | "shippingCarNumber": "京A12345", |
| | | "createTime": "2026-06-18 10:30:00", |
| | | "displayLabel": "20260618001-京A12345-2026-06-18" |
| | | }, |
| | | { |
| | | "shippingId": 101, |
| | | "shippingNo": "FH-2026-002", |
| | | "salesContractNo": "HT-2026-002", |
| | | "customerName": "客æ·A", |
| | | "productName": "产åB", |
| | | "model": "è§æ ¼2", |
| | | "shippingQuantity": 30, |
| | | "batchNo": "20260618002", |
| | | "shippingCarNumber": "京B67890", |
| | | "createTime": "2026-06-17 14:20:00", |
| | | "displayLabel": "20260618002-京B67890-2026-06-17" |
| | | } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## åææ¥å£ï¼ä¿çä¸åï¼ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /shippingInfo/getByCustomerName | éè¿å®¢æ·åç§°æ¥è¯¢å
³èçåè´§åå·ï¼åææ¥å£ï¼ | |
| | | |
| | | --- |
| | | |
| | | ## å端修æ¹ç¹ |
| | | |
| | | ### 1. éå®éè´§é¡µé¢ - åè´§åéæ©ä¸ææ¡ |
| | | |
| | | **ä¿®æ¹åï¼** |
| | | ```html |
| | | <el-form-item label="åè´§åå·"> |
| | | <el-select v-model="form.shippingId" placeholder="è¯·éæ©åè´§åå·"> |
| | | <el-option |
| | | v-for="item in shippingList" |
| | | :key="item.id" |
| | | :label="item.shippingNo" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | ``` |
| | | |
| | | **ä¿®æ¹åï¼** |
| | | ```html |
| | | <el-form-item label="åè´§åå·"> |
| | | <el-select |
| | | v-model="form.shippingId" |
| | | placeholder="è¯·éæ©åè´§åå·" |
| | | filterable |
| | | @change="onShippingChange" |
| | | > |
| | | <el-option |
| | | v-for="item in shippingList" |
| | | :key="item.shippingId" |
| | | :label="item.displayLabel" |
| | | :value="item.shippingId" |
| | | > |
| | | <span style="float: left">{{ item.displayLabel }}</span> |
| | | <span style="float: right; color: #8492a6; font-size: 12px"> |
| | | {{ item.productName }} - {{ item.shippingQuantity }}{{ item.model ? '(' + item.model + ')' : '' }} |
| | | </span> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | ``` |
| | | |
| | | ### 2. data æ°æ® |
| | | |
| | | ```js |
| | | data() { |
| | | return { |
| | | form: { |
| | | shippingId: null, |
| | | batchNo: '', |
| | | shippingCarNumber: '', |
| | | productName: '', |
| | | model: '', |
| | | shippingQuantity: null, |
| | | }, |
| | | shippingList: [], |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 3. methods æ¹æ³ |
| | | |
| | | ```js |
| | | methods: { |
| | | // æ¥è¯¢åè´§ä¿¡æ¯å表 |
| | | getShippingList(customerName) { |
| | | if (!customerName) { |
| | | this.shippingList = []; |
| | | return; |
| | | } |
| | | this.$axios.get('/shippingInfo/getForReturn', { |
| | | params: { customerName } |
| | | }).then(res => { |
| | | this.shippingList = res.data || []; |
| | | }); |
| | | }, |
| | | |
| | | // åè´§åéæ©åå |
| | | onShippingChange(shippingId) { |
| | | const selected = this.shippingList.find(item => item.shippingId === shippingId); |
| | | if (selected) { |
| | | // åæ¾æ°æ® |
| | | this.form.batchNo = selected.batchNo || ''; |
| | | this.form.shippingCarNumber = selected.shippingCarNumber || ''; |
| | | this.form.productName = selected.productName || ''; |
| | | this.form.model = selected.model || ''; |
| | | this.form.shippingQuantity = selected.shippingQuantity || 0; |
| | | this.form.salesContractNo = selected.salesContractNo || ''; |
| | | this.form.shippingNo = selected.shippingNo || ''; |
| | | } |
| | | }, |
| | | } |
| | | ``` |
| | | |
| | | ### 4. 宿´ç¤ºä¾ï¼éå®é货表åï¼ |
| | | |
| | | ```html |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <!-- 客æ·éæ© --> |
| | | <el-form-item label="客æ·åç§°" prop="customerName"> |
| | | <el-select |
| | | v-model="form.customerName" |
| | | placeholder="è¯·éæ©å®¢æ·" |
| | | filterable |
| | | @change="onCustomerChange" |
| | | > |
| | | <el-option |
| | | v-for="item in customerList" |
| | | :key="item.id" |
| | | :label="item.customerName" |
| | | :value="item.customerName" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <!-- åè´§åéæ© --> |
| | | <el-form-item label="åè´§ä¿¡æ¯" prop="shippingId"> |
| | | <el-select |
| | | v-model="form.shippingId" |
| | | placeholder="è¯·éæ©åè´§åå·" |
| | | filterable |
| | | :disabled="!form.customerName" |
| | | @change="onShippingChange" |
| | | > |
| | | <el-option |
| | | v-for="item in shippingList" |
| | | :key="item.shippingId" |
| | | :label="item.displayLabel" |
| | | :value="item.shippingId" |
| | | > |
| | | <div style="display: flex; justify-content: space-between;"> |
| | | <span>{{ item.displayLabel }}</span> |
| | | <span style="color: #909399; font-size: 12px"> |
| | | {{ item.productName }} |
| | | </span> |
| | | </div> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <!-- åæ¾ä¿¡æ¯ï¼åªè¯»ï¼ --> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="æ¹æ¬¡å·"> |
| | | <el-input v-model="form.batchNo" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="车çå·"> |
| | | <el-input v-model="form.shippingCarNumber" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="产ååç§°"> |
| | | <el-input v-model="form.productName" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="è§æ ¼åå·"> |
| | | <el-input v-model="form.model" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="åè´§æ°é"> |
| | | <el-input v-model="form.shippingQuantity" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="éå®ååå·"> |
| | | <el-input v-model="form.salesContractNo" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- å
¶ä»é货表ååæ®µ... --> |
| | | </el-form> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'SalesReturnForm', |
| | | data() { |
| | | return { |
| | | form: { |
| | | customerName: '', |
| | | shippingId: null, |
| | | batchNo: '', |
| | | shippingCarNumber: '', |
| | | productName: '', |
| | | model: '', |
| | | shippingQuantity: null, |
| | | salesContractNo: '', |
| | | shippingNo: '', |
| | | }, |
| | | rules: { |
| | | customerName: [{ required: true, message: 'è¯·éæ©å®¢æ·', trigger: 'change' }], |
| | | shippingId: [{ required: true, message: 'è¯·éæ©åè´§ä¿¡æ¯', trigger: 'change' }], |
| | | }, |
| | | customerList: [], |
| | | shippingList: [], |
| | | }; |
| | | }, |
| | | methods: { |
| | | // 客æ·éæ©åå |
| | | onCustomerChange(customerName) { |
| | | // æ¸
空åè´§åéæ© |
| | | this.form.shippingId = null; |
| | | this.clearShippingInfo(); |
| | | // æ¥è¯¢åè´§ä¿¡æ¯ |
| | | this.getShippingList(customerName); |
| | | }, |
| | | |
| | | // æ¥è¯¢åè´§ä¿¡æ¯å表 |
| | | getShippingList(customerName) { |
| | | if (!customerName) { |
| | | this.shippingList = []; |
| | | return; |
| | | } |
| | | this.$axios.get('/shippingInfo/getForReturn', { |
| | | params: { customerName } |
| | | }).then(res => { |
| | | this.shippingList = res.data || []; |
| | | }); |
| | | }, |
| | | |
| | | // åè´§åéæ©åå |
| | | onShippingChange(shippingId) { |
| | | const selected = this.shippingList.find(item => item.shippingId === shippingId); |
| | | if (selected) { |
| | | this.form.batchNo = selected.batchNo || ''; |
| | | this.form.shippingCarNumber = selected.shippingCarNumber || ''; |
| | | this.form.productName = selected.productName || ''; |
| | | this.form.model = selected.model || ''; |
| | | this.form.shippingQuantity = selected.shippingQuantity; |
| | | this.form.salesContractNo = selected.salesContractNo || ''; |
| | | this.form.shippingNo = selected.shippingNo || ''; |
| | | } else { |
| | | this.clearShippingInfo(); |
| | | } |
| | | }, |
| | | |
| | | // æ¸
空åè´§ä¿¡æ¯ |
| | | clearShippingInfo() { |
| | | this.form.batchNo = ''; |
| | | this.form.shippingCarNumber = ''; |
| | | this.form.productName = ''; |
| | | this.form.model = ''; |
| | | this.form.shippingQuantity = null; |
| | | this.form.salesContractNo = ''; |
| | | this.form.shippingNo = ''; |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | 1. **æ°æ¥å£ vs 忥å£**ï¼ |
| | | - `getForReturn`ï¼æ°å¢ï¼ï¼ä¸é¨ç¨äºéå®éè´§åºæ¯ï¼è¿åæ¹æ¬¡å·åç»åæ¾ç¤ºæ ç¾ |
| | | - `getByCustomerName`ï¼åæï¼ï¼ä¿æä¸åï¼å
¶ä»åºæ¯å¯ç»§ç»ä½¿ç¨ |
| | | |
| | | 2. **displayLabel æ ¼å¼**ï¼`æ¹æ¬¡å·-车çå·-å建æ¶é´(yyyy-MM-dd)` |
| | | - 示ä¾ï¼`20260618001-京A12345-2026-06-18` |
| | | - 妿æå段为空ï¼ä¼æ¾ç¤ºç©ºå符串ï¼å¦ `--2026-06-18` |
| | | |
| | | 3. **æ°æ®åæ¾**ï¼éæ©åè´§ååï¼æ¹æ¬¡å·ã车çå·ã产ååç§°çåæ®µèªå¨å¡«å
å°è¡¨åä¸ï¼å端åªéå±ç¤ºå³å¯ |
| | | |
| | | 4. **客æ·åæ¢**ï¼åæ¢å®¢æ·æ¶ï¼éæ¸
空已éçåè´§ååç¸å
³åæ¾æ°æ® |
| | | |
| | | 5. **䏿çé**ï¼å»ºè®®å¼å¯ `filterable` 屿§ï¼æ¹ä¾¿ç¨æ·éè¿æ¹æ¬¡å·æè½¦çå·å¿«éçé |
| | |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // 客æ·å¾æ¥ç»è®¡æ±æ» |
| | | export function customerTransactionsSummary(query) { |
| | | return request({ |
| | | url: "/metricStatistics/customerTransactionsSummary", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // 客æ·å¾æ¥äº§åæç» |
| | | export function customerTransactionsProducts(query) { |
| | | return request({ |
| | | url: "/metricStatistics/customerTransactionsProducts", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // 客æ·å¾æ¥åè´§æç» |
| | | export function customerTransactionsShipments(query) { |
| | | return request({ |
| | | url: "/metricStatistics/customerTransactionsShipments", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | |
| | | }) |
| | | } |
| | | |
| | | // éå®éè´§-åè´§ä¿¡æ¯æ¥è¯¢ï¼å«æ¹æ¬¡å·ï¼ |
| | | // /shippingInfo/getForReturn |
| | | export function getShippingInfoForReturn(query) { |
| | | return request({ |
| | | url: '/shippingInfo/getForReturn', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | } |
| | | |
| | | // å¤ç |
| | | // /returnManagement/handle |
| | | export function returnManagementHandle(data) { |
| | |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | path: "/customer-transactions-detail", |
| | | component: Layout, |
| | | hidden: true, |
| | | children: [ |
| | | { |
| | | path: "", |
| | | component: () => import("@/views/salesManagement/receiptPaymentLedger/detail.vue"), |
| | | name: "CustomerTransactionsDetail", |
| | | meta: { title: "客æ·å¾æ¥è¯¦æ
", activeMenu: "/salesManagement/receiptPaymentLedger" }, |
| | | }, |
| | | ], |
| | | }, |
| | | // è´¢å¡ç®¡ç模åè·¯ç± |
| | | // { |
| | | // path: "/financial", |
| | |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼"> |
| | | <el-input v-model="form.paymentMethod" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | <el-select v-model="form.paymentMethod" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | style="width: 100%"> |
| | | <el-option label="ç°é" value="ç°é" /> |
| | | <el-option label="çµæ±" value="çµæ±" /> |
| | | <el-option label="微信" value="微信" /> |
| | | <el-option label="æ¯ä»å®" value="æ¯ä»å®" /> |
| | | <el-option label="æ¯ç¥¨" value="æ¯ç¥¨" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="éè´§ç¶æ" |
| | | prop="returnStatus" |
| | | align="center" |
| | | width="100" |
| | | > |
| | | <template #default="scope"> |
| | | <el-tag |
| | | :type="getReturnStatusType(scope.row.returnStatus)" |
| | | size="small" |
| | | > |
| | | {{ scope.row.returnStatus || 'æ éè´§' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="å·²éè´§æ°é" |
| | | prop="returnedQuantity" |
| | | align="center" |
| | | width="100" |
| | | > |
| | | <template #default="scope"> |
| | | {{ scope.row.returnedQuantity || 0 }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="åºåºåå·" |
| | | prop="outboundBatches" |
| | | show-overflow-tooltip |
| | |
| | | return statusStr === "å®¡æ ¸ä¸" || statusStr === "1"; |
| | | }; |
| | | |
| | | // è·åéè´§ç¶ææ ç¾ç±»å |
| | | const getReturnStatusType = (returnStatus) => { |
| | | const statusMap = { |
| | | 'æ éè´§': 'success', |
| | | 'é¨åéè´§': 'warning', |
| | | 'å
¨é¨éè´§': 'danger', |
| | | }; |
| | | return statusMap[returnStatus] || 'info'; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="product-table-container"> |
| | | <el-table |
| | | :data="list" |
| | | v-loading="loading" |
| | | size="small" |
| | | border |
| | | class="custom-table" |
| | | :height="tableHeight" |
| | | > |
| | | <el-table-column label="ååå·" prop="salesContractNo" width="160" show-overflow-tooltip> |
| | | <template #default="{ row }"> |
| | | <span class="contract-no">{{ row.salesContractNo || '--' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="产ååç§°" prop="productName" min-width="180" show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" prop="model" width="140" show-overflow-tooltip> |
| | | <template #default="{ row }"> |
| | | <span class="model-text">{{ row.model || '--' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åä½" prop="unit" width="80" align="center" /> |
| | | <el-table-column label="ååæ°é" prop="contractQuantity" align="right" width="100"> |
| | | <template #default="{ row }"> |
| | | <span class="quantity">{{ formatNumber(row.contractQuantity) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åååä»·" prop="taxInclusiveUnitPrice" align="right" width="110"> |
| | | <template #default="{ row }"> |
| | | <span class="price">Â¥{{ formatMoney(row.taxInclusiveUnitPrice) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ååéé¢" prop="contractAmount" align="right" width="130"> |
| | | <template #default="{ row }"> |
| | | <span class="amount">Â¥{{ formatMoney(row.contractAmount) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å·²åè´§æ°é" prop="shippedQuantity" align="right" width="100"> |
| | | <template #default="{ row }"> |
| | | <span class="shipped-qty">{{ formatNumber(row.shippedQuantity) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å·²åè´§éé¢" prop="shippedAmount" align="right" width="130"> |
| | | <template #default="{ row }"> |
| | | <span class="shipped-amt">Â¥{{ formatMoney(row.shippedAmount) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åè´§è¿åº¦" width="180" align="center"> |
| | | <template #default="{ row }"> |
| | | <div class="progress-cell"> |
| | | <el-progress |
| | | :percentage="calcPercent(row.shippedQuantity, row.contractQuantity)" |
| | | :stroke-width="8" |
| | | :show-text="false" |
| | | :color="getProgressColor(calcPercent(row.shippedQuantity, row.contractQuantity))" |
| | | /> |
| | | <span class="progress-text">{{ calcPercent(row.shippedQuantity, row.contractQuantity) }}%</span> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <div class="pagination-wrapper"> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | :page="queryParams.pageNum" |
| | | :limit="queryParams.pageSize" |
| | | @pagination="handlePagination" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, watch, computed } from 'vue' |
| | | import { customerTransactionsProducts } from '@/api/salesManagement/indicatorStats.js' |
| | | import Pagination from '@/components/PIMTable/Pagination.vue' |
| | | |
| | | const props = defineProps({ |
| | | customerId: { |
| | | type: [Number, String], |
| | | required: true |
| | | } |
| | | }) |
| | | |
| | | const loading = ref(false) |
| | | const list = ref([]) |
| | | const total = ref(0) |
| | | const queryParams = ref({ |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | customerId: null |
| | | }) |
| | | |
| | | const tableHeight = computed(() => `calc(100vh - 32em)`) |
| | | |
| | | const getList = () => { |
| | | loading.value = true |
| | | queryParams.value.customerId = props.customerId |
| | | customerTransactionsProducts(queryParams.value) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | list.value = res.data?.records || [] |
| | | total.value = res.data?.total || 0 |
| | | } |
| | | }) |
| | | .finally(() => { |
| | | loading.value = false |
| | | }) |
| | | } |
| | | |
| | | const handlePagination = ({ page, limit }) => { |
| | | queryParams.value.pageNum = page |
| | | queryParams.value.pageSize = limit |
| | | getList() |
| | | } |
| | | |
| | | const formatMoney = (value) => { |
| | | if (!value) return '0.00' |
| | | return Number(value).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) |
| | | } |
| | | |
| | | const formatNumber = (value) => { |
| | | if (!value) return '0' |
| | | return Number(value).toLocaleString('zh-CN') |
| | | } |
| | | |
| | | const calcPercent = (shipped, total) => { |
| | | if (!total || total === 0) return 0 |
| | | return Math.min(100, Math.round((shipped / total) * 100)) |
| | | } |
| | | |
| | | const getProgressColor = (percent) => { |
| | | if (percent < 30) return '#f56c6c' |
| | | if (percent < 60) return '#e6a23c' |
| | | if (percent < 80) return '#409eff' |
| | | return '#67c23a' |
| | | } |
| | | |
| | | watch(() => props.customerId, (val) => { |
| | | if (val) { |
| | | queryParams.value.pageNum = 1 |
| | | getList() |
| | | } |
| | | }, { immediate: true }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .product-table-container { |
| | | .custom-table { |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | |
| | | :deep(.el-table__header-wrapper) { |
| | | th { |
| | | background: #f8f9fb !important; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | } |
| | | |
| | | :deep(.el-table__row) { |
| | | transition: all 0.2s; |
| | | |
| | | &:hover > td { |
| | | background: #f5f7fa !important; |
| | | } |
| | | } |
| | | |
| | | .contract-no { |
| | | color: #409eff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .model-text { |
| | | color: #606266; |
| | | } |
| | | |
| | | .quantity { |
| | | font-weight: 500; |
| | | color: #303133; |
| | | } |
| | | |
| | | .price { |
| | | color: #606266; |
| | | } |
| | | |
| | | .amount { |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .shipped-qty { |
| | | color: #67c23a; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .shipped-amt { |
| | | color: #67c23a; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .progress-cell { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | padding: 0 10px; |
| | | |
| | | .el-progress { |
| | | flex: 1; |
| | | } |
| | | |
| | | .progress-text { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | min-width: 32px; |
| | | text-align: right; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .pagination-wrapper { |
| | | padding: 16px 0 0; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="shipment-table-container"> |
| | | <el-table |
| | | :data="list" |
| | | v-loading="loading" |
| | | size="small" |
| | | border |
| | | class="custom-table" |
| | | :height="tableHeight" |
| | | > |
| | | <el-table-column label="ååå·" prop="salesContractNo" width="160" show-overflow-tooltip> |
| | | <template #default="{ row }"> |
| | | <span class="contract-no">{{ row.salesContractNo || '--' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åè´§åå·" prop="shippingNo" width="160" show-overflow-tooltip> |
| | | <template #default="{ row }"> |
| | | <span class="shipping-no">{{ row.shippingNo || '--' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="产ååç§°" prop="productName" min-width="160" show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" prop="model" width="140" show-overflow-tooltip> |
| | | <template #default="{ row }"> |
| | | <span class="model-text">{{ row.model || '--' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åè´§æ°é" prop="shippingQuantity" align="right" width="100"> |
| | | <template #default="{ row }"> |
| | | <span class="quantity">{{ formatNumber(row.shippingQuantity) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åè´§éé¢" prop="shippingAmount" align="right" width="130"> |
| | | <template #default="{ row }"> |
| | | <span class="amount">Â¥{{ formatMoney(row.shippingAmount) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åºåºæ¹å·" prop="batchNo" width="160" show-overflow-tooltip> |
| | | <template #default="{ row }"> |
| | | <span class="batch-no">{{ row.batchNo || '--' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åè´§æ¥æ" prop="shippingDate" width="120" align="center"> |
| | | <template #default="{ row }"> |
| | | <span class="date-text">{{ row.shippingDate || '--' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="审æ¹ç¶æ" prop="approvalStatus" width="100" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-tag |
| | | :type="row.approvalStatus === 1 ? 'success' : 'warning'" |
| | | size="small" |
| | | effect="light" |
| | | round |
| | | > |
| | | {{ row.approvalStatus === 1 ? '已审' : 'å¾
审' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <div class="pagination-wrapper"> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | :page="queryParams.pageNum" |
| | | :limit="queryParams.pageSize" |
| | | @pagination="handlePagination" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, watch, computed } from 'vue' |
| | | import { customerTransactionsShipments } from '@/api/salesManagement/indicatorStats.js' |
| | | import Pagination from '@/components/PIMTable/Pagination.vue' |
| | | |
| | | const props = defineProps({ |
| | | customerId: { |
| | | type: [Number, String], |
| | | required: true |
| | | } |
| | | }) |
| | | |
| | | const loading = ref(false) |
| | | const list = ref([]) |
| | | const total = ref(0) |
| | | const queryParams = ref({ |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | customerId: null |
| | | }) |
| | | |
| | | const tableHeight = computed(() => `calc(100vh - 32em)`) |
| | | |
| | | const getList = () => { |
| | | loading.value = true |
| | | queryParams.value.customerId = props.customerId |
| | | customerTransactionsShipments(queryParams.value) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | list.value = res.data?.records || [] |
| | | total.value = res.data?.total || 0 |
| | | } |
| | | }) |
| | | .finally(() => { |
| | | loading.value = false |
| | | }) |
| | | } |
| | | |
| | | const handlePagination = ({ page, limit }) => { |
| | | queryParams.value.pageNum = page |
| | | queryParams.value.pageSize = limit |
| | | getList() |
| | | } |
| | | |
| | | const formatMoney = (value) => { |
| | | if (!value) return '0.00' |
| | | return Number(value).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) |
| | | } |
| | | |
| | | const formatNumber = (value) => { |
| | | if (!value) return '0' |
| | | return Number(value).toLocaleString('zh-CN') |
| | | } |
| | | |
| | | watch(() => props.customerId, (val) => { |
| | | if (val) { |
| | | queryParams.value.pageNum = 1 |
| | | getList() |
| | | } |
| | | }, { immediate: true }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .shipment-table-container { |
| | | .custom-table { |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | |
| | | :deep(.el-table__header-wrapper) { |
| | | th { |
| | | background: #f8f9fb !important; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | } |
| | | |
| | | :deep(.el-table__row) { |
| | | transition: all 0.2s; |
| | | |
| | | &:hover > td { |
| | | background: #f5f7fa !important; |
| | | } |
| | | } |
| | | |
| | | .contract-no { |
| | | color: #409eff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .shipping-no { |
| | | color: #909399; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .model-text { |
| | | color: #606266; |
| | | } |
| | | |
| | | .quantity { |
| | | font-weight: 500; |
| | | color: #303133; |
| | | } |
| | | |
| | | .amount { |
| | | font-weight: 600; |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .batch-no { |
| | | color: #606266; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .date-text { |
| | | color: #606266; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .pagination-wrapper { |
| | | padding: 16px 0 0; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="customer-detail-page"> |
| | | <!-- é¡¶é¨å¯¼èªæ --> |
| | | <div class="page-header"> |
| | | <div class="header-left"> |
| | | <el-button @click="goBack" :icon="ArrowLeft" circle /> |
| | | <div class="customer-info"> |
| | | <span class="customer-name">{{ summary.customerName || '客æ·å¾æ¥è¯¦æ
' }}</span> |
| | | <span class="customer-tag">客æ·å¾æ¥ç»è®¡</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- ç»è®¡å¡çåº --> |
| | | <div class="stats-section" v-loading="summaryLoading"> |
| | | <div class="stats-grid"> |
| | | <div class="stat-card primary"> |
| | | <div class="stat-icon"> |
| | | <el-icon><Document /></el-icon> |
| | | </div> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">ååæ»éé¢</div> |
| | | <div class="stat-value"> |
| | | <span class="currency">Â¥</span> |
| | | <span class="number">{{ formatMoney(summary.contractAmounts) }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="stat-card success"> |
| | | <div class="stat-icon"> |
| | | <el-icon><Tickets /></el-icon> |
| | | </div> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">ååæ°é</div> |
| | | <div class="stat-value"> |
| | | <span class="number">{{ summary.contractCount || 0 }}</span> |
| | | <span class="unit">份</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="stat-card info"> |
| | | <div class="stat-icon"> |
| | | <el-icon><Van /></el-icon> |
| | | </div> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">åè´§éé¢</div> |
| | | <div class="stat-value"> |
| | | <span class="currency">Â¥</span> |
| | | <span class="number">{{ formatMoney(summary.shippedAmounts) }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="stat-card danger"> |
| | | <div class="stat-icon"> |
| | | <el-icon><Clock /></el-icon> |
| | | </div> |
| | | <div class="stat-content"> |
| | | <div class="stat-label">æªåè´§éé¢</div> |
| | | <div class="stat-value"> |
| | | <span class="currency">Â¥</span> |
| | | <span class="number">{{ formatMoney(summary.unshippedAmounts) }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="stat-card progress-card"> |
| | | <div class="stat-content-full"> |
| | | <div class="progress-header"> |
| | | <span class="stat-label">åè´§è¿åº¦</span> |
| | | <span class="progress-value">{{ summary.shippedRate || 0 }}%</span> |
| | | </div> |
| | | <el-progress |
| | | :percentage="summary.shippedRate || 0" |
| | | :stroke-width="12" |
| | | :show-text="false" |
| | | :color="getProgressColor(summary.shippedRate)" |
| | | /> |
| | | <div class="progress-footer"> |
| | | <span>å·²åè´§: Â¥{{ formatMoney(summary.shippedAmounts) }}</span> |
| | | <span>æ»é¢: Â¥{{ formatMoney(summary.contractAmounts) }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- Tab 忢 --> |
| | | <div class="table-section"> |
| | | <el-tabs v-model="activeTab" class="custom-tabs"> |
| | | <el-tab-pane label="产åæç»" name="products"> |
| | | <ProductTable :customerId="customerId" v-if="customerId && activeTab === 'products'" /> |
| | | </el-tab-pane> |
| | | <!-- <el-tab-pane label="åè´§æç»" name="shipments"> |
| | | <ShipmentTable :customerId="customerId" v-if="customerId && activeTab === 'shipments'" /> |
| | | </el-tab-pane> --> |
| | | </el-tabs> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import { useRoute, useRouter } from 'vue-router' |
| | | import { ArrowLeft, Document, Tickets, Van, Clock } from '@element-plus/icons-vue' |
| | | import { customerTransactionsSummary } from '@/api/salesManagement/indicatorStats.js' |
| | | import ProductTable from './components/ProductTable.vue' |
| | | // import ShipmentTable from './components/ShipmentTable.vue' |
| | | |
| | | const route = useRoute() |
| | | const router = useRouter() |
| | | |
| | | const customerId = ref(null) |
| | | const summary = ref({}) |
| | | const summaryLoading = ref(false) |
| | | const activeTab = ref('products') |
| | | |
| | | const getSummary = () => { |
| | | if (!customerId.value) return |
| | | summaryLoading.value = true |
| | | customerTransactionsSummary({ customerId: customerId.value }) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | summary.value = res.data || {} |
| | | } |
| | | }) |
| | | .finally(() => { |
| | | summaryLoading.value = false |
| | | }) |
| | | } |
| | | |
| | | const formatMoney = (value) => { |
| | | if (!value) return '0.00' |
| | | return Number(value).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) |
| | | } |
| | | |
| | | const getProgressColor = (rate) => { |
| | | if (!rate || rate < 30) return '#f56c6c' |
| | | if (rate < 60) return '#e6a23c' |
| | | if (rate < 80) return '#409eff' |
| | | return '#67c23a' |
| | | } |
| | | |
| | | const goBack = () => { |
| | | router.push('/salesManagement/receiptPaymentLedger') |
| | | } |
| | | |
| | | onMounted(() => { |
| | | customerId.value = route.query.customerId |
| | | if (customerId.value) { |
| | | getSummary() |
| | | } |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .customer-detail-page { |
| | | background: #f5f7fa; |
| | | min-height: calc(100vh - 84px); |
| | | padding: 20px; |
| | | } |
| | | |
| | | .page-header { |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border-radius: 12px; |
| | | padding: 20px 24px; |
| | | margin-bottom: 20px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3); |
| | | |
| | | .header-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16px; |
| | | |
| | | .el-button { |
| | | background: rgba(255, 255, 255, 0.2); |
| | | border: none; |
| | | color: white; |
| | | |
| | | &:hover { |
| | | background: rgba(255, 255, 255, 0.3); |
| | | } |
| | | } |
| | | |
| | | .customer-info { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 4px; |
| | | |
| | | .customer-name { |
| | | font-size: 20px; |
| | | font-weight: 600; |
| | | color: white; |
| | | } |
| | | |
| | | .customer-tag { |
| | | font-size: 12px; |
| | | color: rgba(255, 255, 255, 0.8); |
| | | background: rgba(255, 255, 255, 0.2); |
| | | padding: 2px 10px; |
| | | border-radius: 10px; |
| | | width: fit-content; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .stats-section { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .stats-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(5, 1fr); |
| | | gap: 16px; |
| | | |
| | | @media (max-width: 1400px) { |
| | | grid-template-columns: repeat(3, 1fr); |
| | | } |
| | | |
| | | @media (max-width: 1000px) { |
| | | grid-template-columns: repeat(2, 1fr); |
| | | } |
| | | } |
| | | |
| | | .stat-card { |
| | | background: white; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16px; |
| | | box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05); |
| | | transition: all 0.3s ease; |
| | | position: relative; |
| | | overflow: hidden; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 4px; |
| | | height: 100%; |
| | | } |
| | | |
| | | &:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | &.primary::before { background: linear-gradient(180deg, #667eea, #764ba2); } |
| | | &.success::before { background: linear-gradient(180deg, #67c23a, #4a9c2d); } |
| | | &.warning::before { background: linear-gradient(180deg, #e6a23c, #c78a2f); } |
| | | &.info::before { background: linear-gradient(180deg, #409eff, #2d7dd2); } |
| | | &.danger::before { background: linear-gradient(180deg, #f56c6c, #c45656); } |
| | | &.progress-card::before { background: linear-gradient(180deg, #909399, #606266); } |
| | | |
| | | .stat-icon { |
| | | width: 52px; |
| | | height: 52px; |
| | | border-radius: 12px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 24px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | &.primary .stat-icon { background: linear-gradient(135deg, rgba(102, 126, 234, 0.15), rgba(118, 75, 162, 0.15)); color: #667eea; } |
| | | &.success .stat-icon { background: linear-gradient(135deg, rgba(103, 194, 58, 0.15), rgba(74, 156, 45, 0.15)); color: #67c23a; } |
| | | &.warning .stat-icon { background: linear-gradient(135deg, rgba(230, 162, 60, 0.15), rgba(199, 138, 47, 0.15)); color: #e6a23c; } |
| | | &.info .stat-icon { background: linear-gradient(135deg, rgba(64, 158, 255, 0.15), rgba(45, 125, 210, 0.15)); color: #409eff; } |
| | | &.danger .stat-icon { background: linear-gradient(135deg, rgba(245, 108, 108, 0.15), rgba(196, 86, 86, 0.15)); color: #f56c6c; } |
| | | &.progress-card .stat-icon { display: none; } |
| | | |
| | | .stat-content { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 13px; |
| | | color: #909399; |
| | | margin-bottom: 6px; |
| | | } |
| | | |
| | | .stat-value { |
| | | .currency { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-right: 2px; |
| | | } |
| | | |
| | | .number { |
| | | font-size: 22px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .unit { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | margin-left: 4px; |
| | | } |
| | | } |
| | | |
| | | &.progress-card { |
| | | grid-column: span 1; |
| | | |
| | | @media (max-width: 1600px) { |
| | | grid-column: span 1; |
| | | } |
| | | |
| | | .stat-content-full { |
| | | width: 100%; |
| | | } |
| | | |
| | | .progress-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 10px; |
| | | |
| | | .stat-label { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .progress-value { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | } |
| | | |
| | | .progress-footer { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-top: 10px; |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .table-section { |
| | | background: white; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05); |
| | | |
| | | .custom-tabs { |
| | | :deep(.el-tabs__header) { |
| | | margin-bottom: 0; |
| | | |
| | | .el-tabs__nav-wrap::after { |
| | | height: 1px; |
| | | } |
| | | |
| | | .el-tabs__item { |
| | | font-size: 15px; |
| | | padding: 0 24px; |
| | | height: 44px; |
| | | line-height: 44px; |
| | | |
| | | &.is-active { |
| | | font-weight: 600; |
| | | } |
| | | } |
| | | |
| | | .el-tabs__active-bar { |
| | | height: 3px; |
| | | border-radius: 2px; |
| | | } |
| | | } |
| | | |
| | | :deep(.el-tabs__content) { |
| | | padding-top: 20px; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="150" /> |
| | | <el-table-column label="æ¶æ¬¾éé¢(å
)" |
| | | prop="receiptPaymentAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="150" /> |
| | | <el-table-column label="åºæ¶éé¢(å
)" |
| | | prop="receiptableAmount" |
| | | show-overflow-tooltip |
| | | width="150"> |
| | | <template #default="{ row }"> |
| | | <span :style="{ color: row.receiptableAmount > 0 ? '#f56c6c' : '#606266' }"> |
| | | {{ formattedNumber(null, null, row.receiptableAmount) }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="100" align="center" fixed="right"> |
| | | <template #default="{ row }"> |
| | | <el-button type="primary" link size="small" @click="viewDetail(row)">æ¥çæç»</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <!-- <el-table-column label="åºæ¶éé¢(å
)"--> |
| | | <!-- prop="receiptableAmount"--> |
| | | <!-- show-overflow-tooltip--> |
| | |
| | | |
| | | <script setup> |
| | | import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue"; |
| | | import { useRouter } from "vue-router"; |
| | | import { |
| | | customewTransactions, |
| | | } from "@/api/salesManagement/indicatorStats.js"; |
| | |
| | | import Pagination from "../../../components/PIMTable/Pagination.vue"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const router = useRouter(); |
| | | const tableData = ref([]); |
| | | const orderRecord = ref([]); |
| | | const tableLoading = ref(false); |
| | |
| | | getOrderList(customerId.value); |
| | | }; |
| | | |
| | | // æ¥çæç» |
| | | const viewDetail = (row) => { |
| | | router.push({ |
| | | path: '/customer-transactions-detail', |
| | | query: { customerId: row.customerId } |
| | | }); |
| | | }; |
| | | |
| | | // å®¡æ ¸ç¶ææ ç¾ç±»å |
| | | const getApprovalStatusType = (status) => { |
| | | const statusMap = { |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="å
³èæ¹å·ï¼" prop="shippingId"> |
| | | <el-select v-model="form.shippingId" filterable placeholder="è¯·éæ©å
³èæ¹å·" @change="outboundNoChange"> |
| | | <el-form-item label="åè´§ä¿¡æ¯ï¼" prop="shippingId"> |
| | | <el-select |
| | | v-model="form.shippingId" |
| | | filterable |
| | | placeholder="è¯·éæ©åè´§ä¿¡æ¯" |
| | | :disabled="!form.customerId" |
| | | @change="outboundNoChange" |
| | | > |
| | | <el-option |
| | | v-for="item in outboundOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | :key="item.shippingId" |
| | | :label="item.displayLabel" |
| | | :value="item.shippingId" |
| | | :disabled="item.returnStatus === 'å
¨é¨éè´§'" |
| | | > |
| | | <div style="display: flex; justify-content: space-between; align-items: center;"> |
| | | <span>{{ item.displayLabel }}</span> |
| | | <el-tag |
| | | size="small" |
| | | :type="getReturnStatusType(item.returnStatus)" |
| | | style="margin-left: 8px;" |
| | | > |
| | | {{ item.returnStatus || 'æ éè´§' }} |
| | | </el-tag> |
| | | </div> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | <el-col :span="4"> |
| | | <el-form-item label="鿬¾æ»é¢ï¼" prop="refundAmount"> |
| | | <el-input v-model="form.refundAmount" disabled placeholder="èªå¨è®¡ç®" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- åè´§ä¿¡æ¯åæ¾ --> |
| | | <el-row :gutter="30" v-if="form.shippingId"> |
| | | <el-col :span="4"> |
| | | <el-form-item label="æ¹æ¬¡å·ï¼"> |
| | | <el-input v-model="form.batchNo" disabled placeholder="--" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="车çå·ï¼"> |
| | | <el-input v-model="form.shippingCarNumber" disabled placeholder="--" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="产ååç§°ï¼"> |
| | | <el-input v-model="form.productName" disabled placeholder="--" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="è§æ ¼åå·ï¼"> |
| | | <el-input v-model="form.model" disabled placeholder="--" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="åè´§æ°éï¼"> |
| | | <el-input v-model="form.shippingQuantity" disabled placeholder="--" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="éå®ååå·ï¼"> |
| | | <el-input v-model="form.salesContractNo" disabled placeholder="--" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | |
| | | <script setup> |
| | | import { reactive, ref, toRefs, getCurrentInstance } from "vue"; |
| | | import { returnManagementAdd, returnManagementUpdate, returnManagementGetByShippingId, getSalesLedger, returnManagementGetById } from "@/api/salesManagement/returnOrder.js"; |
| | | import { returnManagementAdd, returnManagementUpdate, returnManagementGetByShippingId, getSalesLedger, returnManagementGetById, getShippingInfoForReturn } from "@/api/salesManagement/returnOrder.js"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { listProject } from "@/api/projectManagement/project.js"; |
| | |
| | | status: 0, |
| | | returnReason: "", |
| | | refundAmount: "", |
| | | // åè´§ä¿¡æ¯åæ¾å段 |
| | | batchNo: "", |
| | | shippingCarNumber: "", |
| | | productName: "", |
| | | model: "", |
| | | shippingQuantity: null, |
| | | salesContractNo: "", |
| | | shippingNo: "", |
| | | }, |
| | | rules: { |
| | | returnNo: [{ |
| | |
| | | }, trigger: "blur" |
| | | }], |
| | | customerId: [{ required: true, message: "è¯·éæ©å®¢æ·", trigger: "change" }], |
| | | shippingId: [{ required: true, message: "è¯·éæ©å
³èåºåºåå·", trigger: "change" }], |
| | | shippingId: [{ required: true, message: "è¯·éæ©åè´§ä¿¡æ¯", trigger: "change" }], |
| | | } |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | |
| | | status: 0, |
| | | returnReason: "", |
| | | refundAmount: "", |
| | | batchNo: "", |
| | | shippingCarNumber: "", |
| | | productName: "", |
| | | model: "", |
| | | shippingQuantity: null, |
| | | salesContractNo: "", |
| | | shippingNo: "", |
| | | }); |
| | | form.value.maker = userStore.nickName || userStore.name || ""; |
| | | form.value.makeTime = new Date().toISOString().replace('T', ' ').split('.')[0]; // Default to now |
| | |
| | | if (clearDownstream) { |
| | | form.value.shippingId = ""; |
| | | outboundOptions.value = []; |
| | | clearShippingInfo(); |
| | | } |
| | | |
| | | // Find customer name for getSalesLedger if it requires name |
| | | |
| | | // Find customer name for API |
| | | const customer = customerNameOptions.value.find(c => c.id === val); |
| | | if (!customer) return; |
| | | |
| | | // Assuming getSalesLedger takes customerName. If it takes ID, adjust accordingly. |
| | | // Previous code used customerName. Let's try passing customerName. |
| | | getSalesLedger({ |
| | | customerName: customer.label, |
| | | // ä½¿ç¨æ°æ¥å£ getShippingInfoForReturn |
| | | getShippingInfoForReturn({ |
| | | customerName: customer.label, |
| | | }).then(res => { |
| | | if(res.code === 200){ |
| | | outboundOptions.value = res.data.map(item => ({ |
| | | label: item.shippingNo, // Or whatever the outbound number field is |
| | | value: item.id, |
| | | })) |
| | | outboundOptions.value = res.data || []; |
| | | } |
| | | }) |
| | | }; |
| | | |
| | | // æ¸
空åè´§ä¿¡æ¯ |
| | | const clearShippingInfo = () => { |
| | | form.value.batchNo = ""; |
| | | form.value.shippingCarNumber = ""; |
| | | form.value.productName = ""; |
| | | form.value.model = ""; |
| | | form.value.shippingQuantity = null; |
| | | form.value.salesContractNo = ""; |
| | | form.value.shippingNo = ""; |
| | | }; |
| | | |
| | | // è·åéè´§ç¶ææ ç¾ç±»å |
| | | const getReturnStatusType = (returnStatus) => { |
| | | const statusMap = { |
| | | 'æ éè´§': 'success', |
| | | 'é¨åéè´§': 'warning', |
| | | 'å
¨é¨éè´§': 'danger', |
| | | }; |
| | | return statusMap[returnStatus] || 'info'; |
| | | }; |
| | | |
| | | const outboundNoChange = async (val, clearTable = true) => { |
| | | // val is shippingId |
| | | // ä» outboundOptions 䏿¾å°éä¸çåè´§ä¿¡æ¯ |
| | | const selected = outboundOptions.value.find(item => item.shippingId === val); |
| | | |
| | | if (selected) { |
| | | // åæ¾æ°æ® |
| | | form.value.batchNo = selected.batchNo || ""; |
| | | form.value.shippingCarNumber = selected.shippingCarNumber || ""; |
| | | form.value.productName = selected.productName || ""; |
| | | form.value.model = selected.model || ""; |
| | | form.value.shippingQuantity = selected.shippingQuantity || 0; |
| | | form.value.salesContractNo = selected.salesContractNo || ""; |
| | | form.value.shippingNo = selected.shippingNo || ""; |
| | | } else { |
| | | clearShippingInfo(); |
| | | } |
| | | |
| | | // è·å产åå表 |
| | | let res = await returnManagementGetByShippingId({ shippingId: val }); |
| | | if(res.code === 200){ |
| | | // If backend returns project info, set it |
| | | if (res.data.projectId) form.value.projectId = res.data.projectId; |
| | | |
| | | |
| | | availableProducts.value = mergeShippingProductLists(res.data); |
| | | if (clearTable) tableData.value = []; |
| | | } |
| | |
| | | <template #status="{ row }"> |
| | | <el-tag :type="getStatusType(row.status)">{{ getStatusText(row.status) }}</el-tag> |
| | | </template> |
| | | <template #returnStatus="{ row }"> |
| | | <el-tag :type="getReturnStatusType(row.returnStatus)" size="small"> |
| | | {{ row.returnStatus || 'æ éè´§' }} |
| | | </el-tag> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery" /> |
| | |
| | | const defaultColumns = [ |
| | | { label: "éè´§åå·", prop: "returnNo", minWidth: 160 }, |
| | | { label: "åæ®ç¶æ", prop: "status", minWidth: 90, dataType: "slot", slot: "status" }, |
| | | { label: "éè´§ç¶æ", prop: "returnStatus", minWidth: 100, dataType: "slot", slot: "returnStatus" }, |
| | | { label: "å¶åæ¶é´", prop: "makeTime", minWidth: 170 }, |
| | | { label: "客æ·åç§°", prop: "customerName", minWidth: 220 }, |
| | | { label: "éå®åå·", prop: "salesContractNo", minWidth: 160 }, |
| | |
| | | return statusMap[status] || "æªç¥"; |
| | | }; |
| | | |
| | | const getReturnStatusType = (returnStatus) => { |
| | | const statusMap = { |
| | | 'æ éè´§': 'success', |
| | | 'é¨åéè´§': 'warning', |
| | | 'å
¨é¨éè´§': 'danger', |
| | | }; |
| | | return statusMap[returnStatus] || 'info'; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | |
| | | width="200" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="éè´§ç¶æ" |
| | | prop="returnStatus" |
| | | align="center" |
| | | width="100" |
| | | > |
| | | <template #default="scope"> |
| | | <el-tag |
| | | :type="getReturnStatusType(scope.row.returnStatus)" |
| | | size="small" |
| | | > |
| | | {{ scope.row.returnStatus || 'æ éè´§' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" label="æä½" width="220" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" @click="openForm('view', scope.row)" |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼"> |
| | | <el-input |
| | | <el-select |
| | | v-model="form.paymentMethod" |
| | | placeholder="请è¾å
¥" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | /> |
| | | style="width: 100%" |
| | | > |
| | | <el-option label="ç°é" value="ç°é" /> |
| | | <el-option label="çµæ±" value="çµæ±" /> |
| | | <el-option label="微信" value="微信" /> |
| | | <el-option label="æ¯ä»å®" value="æ¯ä»å®" /> |
| | | <el-option label="æ¯ç¥¨" value="æ¯ç¥¨" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | let res = await userStore.getInfo(); |
| | | currentFactoryName.value = res.user.currentFactoryName; |
| | | }; |
| | | |
| | | // è·åéè´§ç¶ææ ç¾ç±»å |
| | | const getReturnStatusType = (returnStatus) => { |
| | | const statusMap = { |
| | | 'æ éè´§': 'success', |
| | | 'é¨åéè´§': 'warning', |
| | | 'å
¨é¨éè´§': 'danger', |
| | | }; |
| | | return statusMap[returnStatus] || 'info'; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | searchForm.salesContractNo = route.query.salesContractNo; |
| | | getList(); |
| | |
| | | <el-row :gutter="24"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼" prop="paymentMethod"> |
| | | <el-input v-model="form.paymentMethod" placeholder="请è¾å
¥ä»æ¬¾æ¹å¼" clearable /> |
| | | <el-select v-model="form.paymentMethod" placeholder="è¯·éæ©ä»æ¬¾æ¹å¼" clearable style="width: 100%"> |
| | | <el-option label="ç°é" value="ç°é" /> |
| | | <el-option label="çµæ±" value="çµæ±" /> |
| | | <el-option label="微信" value="微信" /> |
| | | <el-option label="æ¯ä»å®" value="æ¯ä»å®" /> |
| | | <el-option label="æ¯ç¥¨" value="æ¯ç¥¨" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | salesperson: [{ required: true, message: 'è¯·éæ©ä¸å¡å', trigger: 'change' }], |
| | | quotationDate: [{ required: true, message: 'è¯·éæ©æ¥ä»·æ¥æ', trigger: 'change' }], |
| | | validDate: [{ required: true, message: 'è¯·éæ©æææ', trigger: 'change' }], |
| | | paymentMethod: [{ required: true, message: '请è¾å
¥ä»æ¬¾æ¹å¼', trigger: 'blur' }] |
| | | paymentMethod: [{ required: true, message: 'è¯·éæ©ä»æ¬¾æ¹å¼', trigger: 'change' }] |
| | | } |
| | | |
| | | const productRowRules = { |