jiaxing.zhou

Merge remote-tracking branch 'origin/master'

Showing 26 changed files with 503 additions and 65 deletions
...@@ -141,3 +141,4 @@ public class DeliveryMainController { ...@@ -141,3 +141,4 @@ public class DeliveryMainController {
141 } 141 }
142 } 142 }
143 143
144 +
......
...@@ -87,7 +87,7 @@ public class InvoiceMainController { ...@@ -87,7 +87,7 @@ public class InvoiceMainController {
87 87
88 @Operation(summary = "删除发票", description = "根据发票ID逻辑删除发票") 88 @Operation(summary = "删除发票", description = "根据发票ID逻辑删除发票")
89 @DeleteMapping("/{invoiceId}") 89 @DeleteMapping("/{invoiceId}")
90 - @PreAuthorize("hasAuthority('invoice:delete')") 90 + @PreAuthorize("hasAuthority('invoice:remove')")
91 public ApiRes<Void> deleteInvoice( 91 public ApiRes<Void> deleteInvoice(
92 @Parameter(description = "发票ID", required = true) @PathVariable Long invoiceId) { 92 @Parameter(description = "发票ID", required = true) @PathVariable Long invoiceId) {
93 try { 93 try {
...@@ -141,3 +141,4 @@ public class InvoiceMainController { ...@@ -141,3 +141,4 @@ public class InvoiceMainController {
141 } 141 }
142 } 142 }
143 143
144 +
......
...@@ -61,3 +61,4 @@ public class DeliveryAddReq { ...@@ -61,3 +61,4 @@ public class DeliveryAddReq {
61 private List<DeliveryItemAddReq> deliveryItems; 61 private List<DeliveryItemAddReq> deliveryItems;
62 } 62 }
63 63
64 +
......
...@@ -37,3 +37,4 @@ public class DeliveryItemAddReq { ...@@ -37,3 +37,4 @@ public class DeliveryItemAddReq {
37 private BigDecimal deliveryAmount; 37 private BigDecimal deliveryAmount;
38 } 38 }
39 39
40 +
......
...@@ -59,3 +59,4 @@ public class DeliveryItemRes { ...@@ -59,3 +59,4 @@ public class DeliveryItemRes {
59 private LocalDateTime updateTime; 59 private LocalDateTime updateTime;
60 } 60 }
61 61
62 +
......
...@@ -40,3 +40,4 @@ public class DeliveryItemUpdateReq { ...@@ -40,3 +40,4 @@ public class DeliveryItemUpdateReq {
40 private BigDecimal deliveryAmount; 40 private BigDecimal deliveryAmount;
41 } 41 }
42 42
43 +
......
...@@ -67,3 +67,4 @@ public class DeliveryRes { ...@@ -67,3 +67,4 @@ public class DeliveryRes {
67 private List<DeliveryItemRes> deliveryItems; 67 private List<DeliveryItemRes> deliveryItems;
68 } 68 }
69 69
70 +
......
...@@ -65,3 +65,4 @@ public class DeliveryUpdateReq { ...@@ -65,3 +65,4 @@ public class DeliveryUpdateReq {
65 private List<DeliveryItemUpdateReq> deliveryItems; 65 private List<DeliveryItemUpdateReq> deliveryItems;
66 } 66 }
67 67
68 +
......
...@@ -42,3 +42,4 @@ public class InvoiceItemAddReq { ...@@ -42,3 +42,4 @@ public class InvoiceItemAddReq {
42 private BigDecimal taxAmount; 42 private BigDecimal taxAmount;
43 } 43 }
44 44
45 +
......
...@@ -62,3 +62,4 @@ public class InvoiceItemRes { ...@@ -62,3 +62,4 @@ public class InvoiceItemRes {
62 private LocalDateTime updateTime; 62 private LocalDateTime updateTime;
63 } 63 }
64 64
65 +
......
...@@ -45,3 +45,4 @@ public class InvoiceItemUpdateReq { ...@@ -45,3 +45,4 @@ public class InvoiceItemUpdateReq {
45 private BigDecimal taxAmount; 45 private BigDecimal taxAmount;
46 } 46 }
47 47
48 +
......
...@@ -55,3 +55,4 @@ public class InvoiceQueryReq { ...@@ -55,3 +55,4 @@ public class InvoiceQueryReq {
55 private Integer pageSize = 10; 55 private Integer pageSize = 10;
56 } 56 }
57 57
58 +
......
...@@ -66,3 +66,4 @@ public class DeliveryItem { ...@@ -66,3 +66,4 @@ public class DeliveryItem {
66 private String delFlag; 66 private String delFlag;
67 } 67 }
68 68
69 +
......
...@@ -72,3 +72,4 @@ public class InvoiceItem { ...@@ -72,3 +72,4 @@ public class InvoiceItem {
72 private String delFlag; 72 private String delFlag;
73 } 73 }
74 74
75 +
......
...@@ -81,3 +81,4 @@ public class InvoiceMain { ...@@ -81,3 +81,4 @@ public class InvoiceMain {
81 private String delFlag; 81 private String delFlag;
82 } 82 }
83 83
84 +
......
...@@ -14,3 +14,4 @@ import org.apache.ibatis.annotations.Mapper; ...@@ -14,3 +14,4 @@ import org.apache.ibatis.annotations.Mapper;
14 public interface DeliveryItemMapper extends BaseMapper<DeliveryItem> { 14 public interface DeliveryItemMapper extends BaseMapper<DeliveryItem> {
15 } 15 }
16 16
17 +
......
...@@ -14,3 +14,4 @@ import org.apache.ibatis.annotations.Mapper; ...@@ -14,3 +14,4 @@ import org.apache.ibatis.annotations.Mapper;
14 public interface DeliveryMainMapper extends BaseMapper<DeliveryMain> { 14 public interface DeliveryMainMapper extends BaseMapper<DeliveryMain> {
15 } 15 }
16 16
17 +
......
...@@ -14,3 +14,4 @@ import org.apache.ibatis.annotations.Mapper; ...@@ -14,3 +14,4 @@ import org.apache.ibatis.annotations.Mapper;
14 public interface InvoiceItemMapper extends BaseMapper<InvoiceItem> { 14 public interface InvoiceItemMapper extends BaseMapper<InvoiceItem> {
15 } 15 }
16 16
17 +
......
...@@ -14,3 +14,4 @@ import org.apache.ibatis.annotations.Mapper; ...@@ -14,3 +14,4 @@ import org.apache.ibatis.annotations.Mapper;
14 public interface InvoiceMainMapper extends BaseMapper<InvoiceMain> { 14 public interface InvoiceMainMapper extends BaseMapper<InvoiceMain> {
15 } 15 }
16 16
17 +
......
...@@ -69,3 +69,4 @@ public interface DeliveryMainService extends IService<DeliveryMain> { ...@@ -69,3 +69,4 @@ public interface DeliveryMainService extends IService<DeliveryMain> {
69 boolean updateDeliveryStatus(Long deliveryId, Integer deliveryStatus); 69 boolean updateDeliveryStatus(Long deliveryId, Integer deliveryStatus);
70 } 70 }
71 71
72 +
......
...@@ -69,3 +69,4 @@ public interface InvoiceMainService extends IService<InvoiceMain> { ...@@ -69,3 +69,4 @@ public interface InvoiceMainService extends IService<InvoiceMain> {
69 boolean updateInvoiceStatus(Long invoiceId, Integer invoiceStatus); 69 boolean updateInvoiceStatus(Long invoiceId, Integer invoiceStatus);
70 } 70 }
71 71
72 +
......
...@@ -229,3 +229,4 @@ public class DeliveryMainServiceImpl extends ServiceImpl<DeliveryMainMapper, Del ...@@ -229,3 +229,4 @@ public class DeliveryMainServiceImpl extends ServiceImpl<DeliveryMainMapper, Del
229 } 229 }
230 } 230 }
231 231
232 +
......
...@@ -112,6 +112,7 @@ public class InvoiceMainServiceImpl extends ServiceImpl<InvoiceMainMapper, Invoi ...@@ -112,6 +112,7 @@ public class InvoiceMainServiceImpl extends ServiceImpl<InvoiceMainMapper, Invoi
112 112
113 InvoiceMain invoiceMain = new InvoiceMain(); 113 InvoiceMain invoiceMain = new InvoiceMain();
114 BeanUtils.copyProperties(addReq, invoiceMain); 114 BeanUtils.copyProperties(addReq, invoiceMain);
115 + invoiceMain.setUpdateTime(LocalDateTime.now());
115 invoiceMain.setCreateTime(LocalDateTime.now()); 116 invoiceMain.setCreateTime(LocalDateTime.now());
116 invoiceMain.setUpdateTime(LocalDateTime.now()); 117 invoiceMain.setUpdateTime(LocalDateTime.now());
117 invoiceMain.setDelFlag("0"); 118 invoiceMain.setDelFlag("0");
......
...@@ -7,31 +7,10 @@ export {} ...@@ -7,31 +7,10 @@ export {}
7 7
8 declare module 'vue' { 8 declare module 'vue' {
9 export interface GlobalComponents { 9 export interface GlobalComponents {
10 - ElButton: typeof import('element-plus/es')['ElButton']
11 - ElCard: typeof import('element-plus/es')['ElCard']
12 - ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
13 - ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
14 - ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
15 - ElDialog: typeof import('element-plus/es')['ElDialog']
16 - ElForm: typeof import('element-plus/es')['ElForm']
17 - ElFormItem: typeof import('element-plus/es')['ElFormItem']
18 - ElIcon: typeof import('element-plus/es')['ElIcon']
19 - ElInput: typeof import('element-plus/es')['ElInput']
20 - ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
21 - ElOption: typeof import('element-plus/es')['ElOption']
22 - ElPagination: typeof import('element-plus/es')['ElPagination']
23 - ElSelect: typeof import('element-plus/es')['ElSelect']
24 - ElTable: typeof import('element-plus/es')['ElTable']
25 - ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
26 - ElTag: typeof import('element-plus/es')['ElTag']
27 - ElText: typeof import('element-plus/es')['ElText']
28 Header: typeof import('./src/components/layout/Header.vue')['default'] 10 Header: typeof import('./src/components/layout/Header.vue')['default']
29 RouterLink: typeof import('vue-router')['RouterLink'] 11 RouterLink: typeof import('vue-router')['RouterLink']
30 RouterView: typeof import('vue-router')['RouterView'] 12 RouterView: typeof import('vue-router')['RouterView']
31 Sidebar: typeof import('./src/components/layout/Sidebar.vue')['default'] 13 Sidebar: typeof import('./src/components/layout/Sidebar.vue')['default']
32 SidebarItem: typeof import('./src/components/layout/SidebarItem.vue')['default'] 14 SidebarItem: typeof import('./src/components/layout/SidebarItem.vue')['default']
33 } 15 }
34 - export interface ComponentCustomProperties {
35 - vLoading: typeof import('element-plus/es')['ElLoadingDirective']
36 - }
37 } 16 }
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
63 <!-- 操作按钮区域 --> 63 <!-- 操作按钮区域 -->
64 <div class="action-section"> 64 <div class="action-section">
65 <div class="action-buttons"> 65 <div class="action-buttons">
66 - <button @click="handleAdd" class="action-btn primary">➕ 新增出库</button> 66 + <button @click="handleAdd" class="action-btn primary">✨ 新增</button>
67 <button @click="handleExport" class="action-btn secondary">📋 导出</button> 67 <button @click="handleExport" class="action-btn secondary">📋 导出</button>
68 </div> 68 </div>
69 </div> 69 </div>
...@@ -178,8 +178,16 @@ ...@@ -178,8 +178,16 @@
178 <h4>出库基本信息</h4> 178 <h4>出库基本信息</h4>
179 <div class="detail-grid"> 179 <div class="detail-grid">
180 <div class="detail-item"> 180 <div class="detail-item">
181 - <label>出库单号:</label> 181 + <label>出库单ID:</label>
182 - <span>{{ deliveryDetail.deliveryNo }}</span> 182 + <span>{{ orderDetail.deliveryId }}</span>
183 + </div>
184 + <div class="detail-item">
185 + <label>出库单编号:</label>
186 + <span>{{ orderDetail.deliveryNo }}</span>
187 + </div>
188 + <div class="detail-item">
189 + <label>关联订单编号:</label>
190 + <span>{{ orderDetail.orderNo }}</span>
183 </div> 191 </div>
184 <div class="detail-item"> 192 <div class="detail-item">
185 <label>关联订单编号:</label> 193 <label>关联订单编号:</label>
...@@ -195,11 +203,7 @@ ...@@ -195,11 +203,7 @@
195 </div> 203 </div>
196 <div class="detail-item"> 204 <div class="detail-item">
197 <label>出库日期:</label> 205 <label>出库日期:</label>
198 - <span>{{ formatDate(deliveryDetail.deliveryDate) }}</span> 206 + <span>{{ formatDate(orderDetail.deliveryDate) }}</span>
199 - </div>
200 - <div class="detail-item">
201 - <label>出库总金额:</label>
202 - <span>{{ formatCurrency(deliveryDetail.totalAmount) }}</span>
203 </div> 207 </div>
204 <div class="detail-item"> 208 <div class="detail-item">
205 <label>出库状态:</label> 209 <label>出库状态:</label>
...@@ -208,33 +212,47 @@ ...@@ -208,33 +212,47 @@
208 </span> 212 </span>
209 </div> 213 </div>
210 <div class="detail-item"> 214 <div class="detail-item">
215 + <label>仓库编码:</label>
216 + <span>{{ orderDetail.warehouseCode }}</span>
217 + </div>
218 + <div class="detail-item">
219 + <label>数据来源:</label>
220 + <span>{{ orderDetail.dataSource || '-' }}</span>
221 + </div>
222 + <div class="detail-item">
223 + <label>上传时间:</label>
224 + <span>{{ formatTime(orderDetail.uploadTime || '') }}</span>
225 + </div>
226 + <div class="detail-item">
211 <label>创建时间:</label> 227 <label>创建时间:</label>
212 - <span>{{ formatTime(deliveryDetail.createTime || '') }}</span> 228 + <span>{{ formatTime(orderDetail.createTime || '') }}</span>
229 + </div>
230 + <div class="detail-item">
231 + <label>更新时间:</label>
232 + <span>{{ formatTime(orderDetail.updateTime || '') }}</span>
213 </div> 233 </div>
214 </div> 234 </div>
215 </div> 235 </div>
216 236
217 - <div class="detail-section" v-if="deliveryDetail.deliveryItems && deliveryDetail.deliveryItems.length > 0"> 237 + <div class="detail-section" v-if="orderDetail.deliveryItems && orderDetail.deliveryItems.length > 0">
218 <h4>出库明细</h4> 238 <h4>出库明细</h4>
219 <table class="detail-table"> 239 <table class="detail-table">
220 <thead> 240 <thead>
221 <tr> 241 <tr>
222 <th>产品编码</th> 242 <th>产品编码</th>
223 <th>产品名称</th> 243 <th>产品名称</th>
224 - <th>产品型号</th>
225 <th>出库数量</th> 244 <th>出库数量</th>
226 <th>出库单价</th> 245 <th>出库单价</th>
227 - <th>出库总价</th> 246 + <th>出库金额</th>
228 </tr> 247 </tr>
229 </thead> 248 </thead>
230 <tbody> 249 <tbody>
231 - <tr v-for="item in deliveryDetail.deliveryItems" :key="item.itemId"> 250 + <tr v-for="item in orderDetail.deliveryItems" :key="item.deliveryItemId">
232 <td>{{ item.productCode }}</td> 251 <td>{{ item.productCode }}</td>
233 <td>{{ item.productName }}</td> 252 <td>{{ item.productName }}</td>
234 - <td>{{ item.productModel }}</td> 253 + <td>{{ item.deliveryQty }}</td>
235 - <td>{{ item.productQty }}</td> 254 + <td>{{ formatCurrency(item.deliveryPrice) }}</td>
236 - <td>{{ formatCurrency(item.unitPrice) }}</td> 255 + <td>{{ formatCurrency(item.deliveryAmount) }}</td>
237 - <td>{{ formatCurrency(item.totalPrice) }}</td>
238 </tr> 256 </tr>
239 </tbody> 257 </tbody>
240 </table> 258 </table>
......
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
71 <!-- 操作按钮区域 --> 71 <!-- 操作按钮区域 -->
72 <div class="action-section"> 72 <div class="action-section">
73 <div class="action-buttons"> 73 <div class="action-buttons">
74 - <button @click="handleAdd" class="action-btn primary">✨ 新增发票</button> 74 + <button @click="handleAdd" class="action-btn primary">✨ 新增</button>
75 <button @click="handleExport" class="action-btn secondary">📋 导出</button> 75 <button @click="handleExport" class="action-btn secondary">📋 导出</button>
76 </div> 76 </div>
77 </div> 77 </div>
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
101 <th>开票日期</th> 101 <th>开票日期</th>
102 <th>发票金额</th> 102 <th>发票金额</th>
103 <th>开票状态</th> 103 <th>开票状态</th>
104 - <th>开票时间</th> 104 + <th>上传时间</th>
105 <th>关联订单编号</th> 105 <th>关联订单编号</th>
106 <th>操作</th> 106 <th>操作</th>
107 </tr> 107 </tr>
...@@ -124,6 +124,7 @@ ...@@ -124,6 +124,7 @@
124 <td class="action-col"> 124 <td class="action-col">
125 <button @click="handleView(order)" class="view-btn">👁️ 查看</button> 125 <button @click="handleView(order)" class="view-btn">👁️ 查看</button>
126 <button @click="handleEdit(order)" class="edit-btn">✏️ 编辑</button> 126 <button @click="handleEdit(order)" class="edit-btn">✏️ 编辑</button>
127 + <button @click="handleDelete(order)" class="delete-btn">🗑️ 删除</button>
127 </td> 128 </td>
128 </tr> 129 </tr>
129 </tbody> 130 </tbody>
...@@ -177,7 +178,7 @@ ...@@ -177,7 +178,7 @@
177 178
178 <!-- 新增/编辑发票对话框 --> 179 <!-- 新增/编辑发票对话框 -->
179 <div v-if="showDialog" class="dialog-overlay" @click="closeDialog"> 180 <div v-if="showDialog" class="dialog-overlay" @click="closeDialog">
180 - <div class="dialog-content" @click.stop> 181 + <div class="dialog-content large-dialog" @click.stop>
181 <div class="dialog-header"> 182 <div class="dialog-header">
182 <h3>{{ isEdit ? '编辑发票' : '新增发票' }}</h3> 183 <h3>{{ isEdit ? '编辑发票' : '新增发票' }}</h3>
183 <button @click="closeDialog" class="close-btn">×</button> 184 <button @click="closeDialog" class="close-btn">×</button>
...@@ -206,11 +207,41 @@ ...@@ -206,11 +207,41 @@
206 </div> 207 </div>
207 <div class="form-row"> 208 <div class="form-row">
208 <div class="form-item"> 209 <div class="form-item">
210 + <label>出库单编号:</label>
211 + <input
212 + v-model="formData.deliveryNo"
213 + class="form-input"
214 + placeholder="请输入出库单编号"
215 + />
216 + </div>
217 + <div class="form-item">
218 + <label class="required">经销商编码:</label>
219 + <input
220 + v-model="formData.dealerCode"
221 + class="form-input"
222 + placeholder="请输入经销商编码"
223 + required
224 + />
225 + </div>
226 + </div>
227 + <div class="form-row">
228 + <div class="form-item">
229 + <label class="required">经销商名称:</label>
230 + <input
231 + v-model="formData.dealerName"
232 + class="form-input"
233 + placeholder="请输入经销商名称"
234 + required
235 + />
236 + </div>
237 + </div>
238 + <div class="form-row">
239 + <div class="form-item">
209 <label class="required">开票日期:</label> 240 <label class="required">开票日期:</label>
210 <input 241 <input
211 v-model="formData.invoiceDate" 242 v-model="formData.invoiceDate"
212 class="form-input" 243 class="form-input"
213 - type="datetime-local" 244 + type="date"
214 required 245 required
215 /> 246 />
216 </div> 247 </div>
...@@ -221,7 +252,8 @@ ...@@ -221,7 +252,8 @@
221 class="form-input" 252 class="form-input"
222 type="number" 253 type="number"
223 step="0.01" 254 step="0.01"
224 - placeholder="请输入开票金额" 255 + placeholder="开票金额(自动计算)"
256 + readonly
225 required 257 required
226 /> 258 />
227 </div> 259 </div>
...@@ -235,6 +267,7 @@ ...@@ -235,6 +267,7 @@
235 type="number" 267 type="number"
236 step="0.01" 268 step="0.01"
237 placeholder="请输入税率" 269 placeholder="请输入税率"
270 + @input="calculateTaxAmount"
238 /> 271 />
239 </div> 272 </div>
240 <div class="form-item"> 273 <div class="form-item">
...@@ -244,10 +277,84 @@ ...@@ -244,10 +277,84 @@
244 class="form-input" 277 class="form-input"
245 type="number" 278 type="number"
246 step="0.01" 279 step="0.01"
247 - placeholder="请输入税额" 280 + placeholder="税额(自动计算)"
281 + readonly
282 + />
283 + </div>
284 + </div>
285 +
286 + <!-- 发票明细信息 -->
287 + <div class="form-section">
288 + <div class="section-header">
289 + <h4>发票明细</h4>
290 + <button type="button" @click="addInvoiceItem" class="add-item-btn">+ 添加明细</button>
291 + </div>
292 + <div class="invoice-items-container">
293 + <div v-for="(item, index) in formData.invoiceItems" :key="index" class="invoice-item">
294 + <div class="item-row-single">
295 + <div class="form-item" style="flex: 0 0 120px;">
296 + <label>产品编码:</label>
297 + <input
298 + v-model="item.productCode"
299 + class="form-input"
300 + placeholder="产品编码"
301 + />
302 + </div>
303 + <div class="form-item" style="flex: 1 1 200px; min-width: 150px;">
304 + <label>产品名称:</label>
305 + <input
306 + v-model="item.productName"
307 + class="form-input"
308 + placeholder="产品名称"
309 + />
310 + </div>
311 + <div class="form-item" style="flex: 0 0 80px;">
312 + <label>数量:</label>
313 + <input
314 + v-model.number="item.productQty"
315 + class="form-input"
316 + type="number"
317 + @input="calculateInvoiceItemTotal(item)"
318 + />
319 + </div>
320 + <div class="form-item" style="flex: 0 0 100px;">
321 + <label>单价:</label>
322 + <input
323 + v-model.number="item.unitPrice"
324 + class="form-input"
325 + type="number"
326 + step="0.01"
327 + @input="calculateInvoiceItemTotal(item)"
328 + />
329 + </div>
330 + <div class="form-item" style="flex: 0 0 100px;">
331 + <label>金额:</label>
332 + <input
333 + v-model.number="item.itemAmount"
334 + class="form-input"
335 + type="number"
336 + step="0.01"
337 + readonly
248 /> 338 />
249 </div> 339 </div>
340 + <div class="form-item" style="flex: 0 0 100px;">
341 + <label>税额:</label>
342 + <input
343 + v-model.number="item.taxAmount"
344 + class="form-input"
345 + type="number"
346 + step="0.01"
347 + readonly
348 + />
349 + </div>
350 + <div class="form-item" style="flex: 0 0 60px;">
351 + <button type="button" @click="removeInvoiceItem(index)" class="remove-item-btn">删除</button>
352 + </div>
353 + </div>
250 </div> 354 </div>
355 + </div>
356 + </div>
357 +
251 <div class="form-row"> 358 <div class="form-row">
252 <div class="form-item full-width"> 359 <div class="form-item full-width">
253 <label>备注:</label> 360 <label>备注:</label>
...@@ -321,11 +428,11 @@ ...@@ -321,11 +428,11 @@
321 </div> 428 </div>
322 <div class="detail-item"> 429 <div class="detail-item">
323 <label>税率:</label> 430 <label>税率:</label>
324 - <span>{{ invoiceDetail.taxRate ? (invoiceDetail.taxRate * 100).toFixed(2) + '%' : '-' }}</span> 431 + <span>{{ invoiceDetail.taxRate ? invoiceDetail.taxRate + '%' : '-' }}</span>
325 </div> 432 </div>
326 <div class="detail-item"> 433 <div class="detail-item">
327 <label>税额:</label> 434 <label>税额:</label>
328 - <span class="amount-text">{{ formatCurrency((invoiceDetail.totalAmount || 0) * (invoiceDetail.taxRate || 0)) }}</span> 435 + <span class="amount-text">{{ formatCurrency((invoiceDetail.totalAmount || 0) * (invoiceDetail.taxRate || 0) / 100) }}</span>
329 </div> 436 </div>
330 <div class="detail-item"> 437 <div class="detail-item">
331 <label>数据来源:</label> 438 <label>数据来源:</label>
...@@ -363,22 +470,22 @@ ...@@ -363,22 +470,22 @@
363 </tr> 470 </tr>
364 </thead> 471 </thead>
365 <tbody> 472 <tbody>
366 - <tr v-for="item in invoiceDetail.invoiceItems" :key="item.itemId"> 473 + <tr v-for="item in invoiceDetail.invoiceItems" :key="item.invoiceItemId">
367 <td>{{ item.productCode }}</td> 474 <td>{{ item.productCode }}</td>
368 <td>{{ item.productName }}</td> 475 <td>{{ item.productName }}</td>
369 - <td>{{ item.productQty }}</td> 476 + <td>{{ item.invoiceQty }}</td>
370 - <td>{{ formatCurrency(item.unitPrice) }}</td> 477 + <td>{{ formatCurrency(item.unitPriceNoTax) }}</td>
371 - <td class="amount-text">{{ formatCurrency(item.itemAmount) }}</td> 478 + <td class="amount-text">{{ formatCurrency(item.amountNoTax) }}</td>
372 - <td>{{ item.taxRate ? (item.taxRate * 100).toFixed(2) + '%' : '-' }}</td> 479 + <td>{{ invoiceDetail.taxRate ? (invoiceDetail.taxRate / 100).toFixed(2) + '%' : '-' }}</td>
373 <td class="amount-text">{{ formatCurrency(item.taxAmount || 0) }}</td> 480 <td class="amount-text">{{ formatCurrency(item.taxAmount || 0) }}</td>
374 </tr> 481 </tr>
375 </tbody> 482 </tbody>
376 <tfoot> 483 <tfoot>
377 <tr class="total-row"> 484 <tr class="total-row">
378 <td colspan="4" class="total-label">合计:</td> 485 <td colspan="4" class="total-label">合计:</td>
379 - <td class="amount-text total-amount">{{ formatCurrency(invoiceDetail.totalAmount) }}</td> 486 + <td class="amount-text total-amount">{{ formatCurrency(invoiceDetail.invoiceItems.reduce((sum, item) => sum + (item.amountNoTax || 0), 0)) }}</td>
380 <td>-</td> 487 <td>-</td>
381 - <td class="amount-text total-amount">{{ formatCurrency((invoiceDetail.totalAmount || 0) * (invoiceDetail.taxRate || 0)) }}</td> 488 + <td class="amount-text total-amount">{{ formatCurrency(invoiceDetail.invoiceItems.reduce((sum, item) => sum + (item.taxAmount || 0), 0)) }}</td>
382 </tr> 489 </tr>
383 </tfoot> 490 </tfoot>
384 </table> 491 </table>
...@@ -445,13 +552,18 @@ const totalPages = computed(() => Math.ceil(pagination.total / pagination.pageSi ...@@ -445,13 +552,18 @@ const totalPages = computed(() => Math.ceil(pagination.total / pagination.pageSi
445 552
446 // 表单数据 553 // 表单数据
447 const formData = reactive({ 554 const formData = reactive({
555 + invoiceId: 0,
448 orderNo: '', 556 orderNo: '',
449 invoiceNo: '', 557 invoiceNo: '',
558 + deliveryNo: '',
559 + dealerCode: '',
560 + dealerName: '',
450 invoiceDate: '', 561 invoiceDate: '',
451 invoiceAmount: 0, 562 invoiceAmount: 0,
452 taxRate: 0, 563 taxRate: 0,
453 taxAmount: 0, 564 taxAmount: 0,
454 - remark: '' 565 + remark: '',
566 + invoiceItems: [] as any[]
455 }) 567 })
456 568
457 // 状态相关方法 569 // 状态相关方法
...@@ -550,29 +662,148 @@ const handleReset = () => { ...@@ -550,29 +662,148 @@ const handleReset = () => {
550 const handleAdd = () => { 662 const handleAdd = () => {
551 isEdit.value = false 663 isEdit.value = false
552 Object.assign(formData, { 664 Object.assign(formData, {
665 + invoiceId: 0,
553 orderNo: '', 666 orderNo: '',
554 invoiceNo: '', 667 invoiceNo: '',
668 + deliveryNo: '',
669 + dealerCode: '',
670 + dealerName: '',
555 invoiceDate: '', 671 invoiceDate: '',
556 invoiceAmount: 0, 672 invoiceAmount: 0,
557 taxRate: 0, 673 taxRate: 0,
558 taxAmount: 0, 674 taxAmount: 0,
559 - remark: '' 675 + remark: '',
676 + invoiceItems: []
560 }) 677 })
561 showDialog.value = true 678 showDialog.value = true
562 } 679 }
563 680
564 -const handleEdit = (order: InvoiceInfo) => { 681 +// 发票明细相关方法
682 +const addInvoiceItem = () => {
683 + const newItem = {
684 + productCode: '',
685 + productName: '',
686 + productQty: 1,
687 + unitPrice: 0,
688 + itemAmount: 0,
689 + taxAmount: 0
690 + }
691 + formData.invoiceItems.push(newItem)
692 + // 计算新明细项目的金额
693 + calculateInvoiceItemTotal(newItem)
694 +}
695 +
696 +const removeInvoiceItem = (index: number) => {
697 + formData.invoiceItems.splice(index, 1)
698 + // 删除明细项目后,重新计算开票金额和税额
699 + calculateTaxAmount()
700 +}
701 +
702 +const calculateInvoiceItemTotal = (item: any) => {
703 + if (item.productQty && item.unitPrice) {
704 + item.itemAmount = parseFloat((item.productQty * item.unitPrice).toFixed(2))
705 + }
706 + // 明细项目变化时,重新计算开票金额和税额
707 + calculateTaxAmount()
708 +}
709 +
710 +const calculateTaxAmount = () => {
711 + // 计算开票金额(基于明细项目的金额总和)
712 + const totalAmount = formData.invoiceItems.reduce((sum, item) => {
713 + return sum + (item.itemAmount || 0)
714 + }, 0)
715 +
716 + console.log('计算开票金额:', {
717 + invoiceItems: formData.invoiceItems,
718 + totalAmount: totalAmount,
719 + taxRate: formData.taxRate
720 + })
721 +
722 + // 更新开票金额(保留两位小数)
723 + formData.invoiceAmount = parseFloat(totalAmount.toFixed(2))
724 +
725 + // 计算税额(基于主表税率)
726 + if (totalAmount && formData.taxRate) {
727 + formData.taxAmount = parseFloat((totalAmount * formData.taxRate / 100).toFixed(2))
728 +
729 + // 更新明细项目的税额(基于主表税率)
730 + formData.invoiceItems.forEach(item => {
731 + if (item.itemAmount && formData.taxRate) {
732 + item.taxAmount = parseFloat((item.itemAmount * formData.taxRate / 100).toFixed(2))
733 + } else {
734 + item.taxAmount = 0
735 + }
736 + })
737 + } else {
738 + formData.taxAmount = 0
739 + // 清空明细项目的税额
740 + formData.invoiceItems.forEach(item => {
741 + item.taxAmount = 0
742 + })
743 + }
744 +
745 + console.log('计算结果:', {
746 + invoiceAmount: formData.invoiceAmount,
747 + taxAmount: formData.taxAmount
748 + })
749 +}
750 +
751 +const handleEdit = async (order: InvoiceInfo) => {
752 + try {
753 + // 调用详情API获取最新的发票信息
754 + const response = await invoiceApi.getInvoiceDetail(order.invoiceId) as any
755 + console.log('发票编辑详情API响应:', response)
756 +
757 + if (response && response.invoiceId) {
565 isEdit.value = true 758 isEdit.value = true
566 Object.assign(formData, { 759 Object.assign(formData, {
567 - orderNo: order.orderNo, 760 + invoiceId: response.invoiceId,
568 - invoiceNo: order.invoiceNo, 761 + orderNo: response.orderNo,
569 - invoiceDate: order.invoiceDate, 762 + invoiceNo: response.invoiceNo,
570 - invoiceAmount: order.totalAmount, 763 + deliveryNo: response.deliveryNo || '',
571 - taxRate: 0, 764 + dealerCode: response.dealerCode,
572 - taxAmount: 0, 765 + dealerName: response.dealerName,
573 - remark: '' 766 + invoiceDate: response.invoiceDate,
767 + invoiceAmount: response.totalAmount,
768 + taxRate: response.taxRate || 0,
769 + taxAmount: (response.totalAmount || 0) * (response.taxRate || 0) / 100,
770 + remark: '',
771 + invoiceItems: (response.invoiceItems || []).map((item: any) => ({
772 + invoiceItemId: item.invoiceItemId,
773 + invoiceId: item.invoiceId,
774 + invoiceNo: item.invoiceNo,
775 + orderNo: item.orderNo,
776 + productCode: item.productCode,
777 + productName: item.productName,
778 + productQty: item.invoiceQty, // 映射字段名
779 + unitPrice: item.unitPriceNoTax, // 映射字段名
780 + itemAmount: item.amountNoTax, // 映射字段名
781 + taxAmount: item.taxAmount
782 + }))
574 }) 783 })
784 + // 确保税额计算正确
785 + calculateTaxAmount()
575 showDialog.value = true 786 showDialog.value = true
787 + } else {
788 + showMessage('获取发票详情失败', 'error')
789 + }
790 + } catch (error) {
791 + console.error('获取发票详情失败:', error)
792 + showMessage('获取发票详情失败, 请重试', 'error')
793 + }
794 +}
795 +
796 +const handleDelete = async (order: InvoiceInfo) => {
797 + if (confirm(`确定要删除发票"${order.invoiceNo}"吗?`)) {
798 + try {
799 + await invoiceApi.deleteInvoice(order.invoiceId)
800 + showMessage('删除发票成功', 'success')
801 + fetchOrders() // 刷新列表
802 + } catch (error) {
803 + console.error('删除发票失败:', error)
804 + showMessage('删除发票失败, 请重试', 'error')
805 + }
806 + }
576 } 807 }
577 808
578 const handleView = async (order: InvoiceInfo) => { 809 const handleView = async (order: InvoiceInfo) => {
...@@ -606,8 +837,85 @@ const handlePrintInvoice = (invoice: InvoiceInfo) => { ...@@ -606,8 +837,85 @@ const handlePrintInvoice = (invoice: InvoiceInfo) => {
606 837
607 const handleSubmit = async () => { 838 const handleSubmit = async () => {
608 try { 839 try {
609 - showMessage('发票保存功能开发中...', 'warning') 840 + // 验证必填字段
841 + if (!formData.orderNo || !formData.invoiceNo || !formData.dealerCode || !formData.dealerName || !formData.invoiceDate || !formData.invoiceAmount) {
842 + showMessage('请填写所有必填字段', 'error')
843 + return
844 + }
845 +
846 + // 验证明细
847 + if (!formData.invoiceItems || formData.invoiceItems.length === 0) {
848 + showMessage('请至少添加一个发票明细', 'error')
849 + return
850 + }
851 +
852 + // 验证明细字段
853 + for (let i = 0; i < formData.invoiceItems.length; i++) {
854 + const item = formData.invoiceItems[i]
855 + if (!item.productCode || !item.productName || !item.productQty || !item.unitPrice) {
856 + showMessage(`请填写第${i + 1}个明细的所有必填字段`, 'error')
857 + return
858 + }
859 + }
860 +
861 + if (isEdit.value) {
862 + // 编辑发票
863 + const updateData = {
864 + invoiceId: formData.invoiceId,
865 + invoiceNo: formData.invoiceNo,
866 + orderNo: formData.orderNo,
867 + deliveryNo: formData.deliveryNo,
868 + dealerCode: formData.dealerCode,
869 + dealerName: formData.dealerName,
870 + totalAmount: formData.invoiceAmount,
871 + invoiceDate: formData.invoiceDate,
872 + invoiceStatus: 1,
873 + taxRate: formData.taxRate,
874 + dataSource: 'ERP系统',
875 + invoiceItems: formData.invoiceItems.map((item: any) => ({
876 + itemId: item.invoiceItemId,
877 + productCode: item.productCode,
878 + productName: item.productName,
879 + invoiceQty: item.productQty, // 映射字段名
880 + unitPriceNoTax: item.unitPrice, // 映射字段名
881 + amountNoTax: item.itemAmount, // 映射字段名
882 + taxRate: item.taxRate,
883 + taxAmount: item.taxAmount
884 + }))
885 + }
886 +
887 + await invoiceApi.updateInvoice(updateData)
888 + showMessage('发票更新成功', 'success')
889 + } else {
890 + // 新增发票
891 + const addData = {
892 + invoiceNo: formData.invoiceNo,
893 + orderNo: formData.orderNo,
894 + deliveryNo: formData.deliveryNo,
895 + dealerCode: formData.dealerCode,
896 + dealerName: formData.dealerName,
897 + totalAmount: formData.invoiceAmount,
898 + invoiceDate: formData.invoiceDate,
899 + invoiceStatus: 1,
900 + taxRate: formData.taxRate,
901 + dataSource: 'ERP系统',
902 + invoiceItems: formData.invoiceItems.map((item: any) => ({
903 + productCode: item.productCode,
904 + productName: item.productName,
905 + invoiceQty: item.productQty, // 映射字段名
906 + unitPriceNoTax: item.unitPrice, // 映射字段名
907 + amountNoTax: item.itemAmount, // 映射字段名
908 + taxRate: item.taxRate,
909 + taxAmount: item.taxAmount
910 + }))
911 + }
912 +
913 + await invoiceApi.addInvoice(addData)
914 + showMessage('发票新增成功', 'success')
915 + }
916 +
610 closeDialog() 917 closeDialog()
918 + fetchOrders() // 刷新列表
611 } catch (error) { 919 } catch (error) {
612 console.error('保存发票失败:', error) 920 console.error('保存发票失败:', error)
613 showMessage('保存发票失败, 请重试', 'error') 921 showMessage('保存发票失败, 请重试', 'error')
...@@ -883,6 +1191,19 @@ onMounted(() => { ...@@ -883,6 +1191,19 @@ onMounted(() => {
883 color: #333; 1191 color: #333;
884 } 1192 }
885 1193
1194 +.delete-btn {
1195 + background: #dc3545;
1196 + color: white;
1197 +}
1198 +
1199 +/* 只读字段样式 */
1200 +.form-input[readonly] {
1201 + background-color: #f8f9fa;
1202 + color: #6c757d;
1203 + cursor: not-allowed;
1204 + border-color: #dee2e6;
1205 +}
1206 +
886 .status-btn { 1207 .status-btn {
887 background: #28a745; 1208 background: #28a745;
888 color: white; 1209 color: white;
...@@ -1310,4 +1631,101 @@ onMounted(() => { ...@@ -1310,4 +1631,101 @@ onMounted(() => {
1310 .detail-actions .close-btn:hover { 1631 .detail-actions .close-btn:hover {
1311 background: #5a6268; 1632 background: #5a6268;
1312 } 1633 }
1634 +
1635 +/* 发票明细样式 */
1636 +.form-section {
1637 + margin: 20px 0;
1638 + border: 1px solid #e0e0e0;
1639 + border-radius: 6px;
1640 + padding: 15px;
1641 + background: #f9f9f9;
1642 +}
1643 +
1644 +.section-header {
1645 + display: flex;
1646 + justify-content: space-between;
1647 + align-items: center;
1648 + margin-bottom: 15px;
1649 + padding-bottom: 10px;
1650 + border-bottom: 1px solid #ddd;
1651 +}
1652 +
1653 +.section-header h4 {
1654 + margin: 0;
1655 + color: #333;
1656 + font-size: 16px;
1657 + font-weight: 600;
1658 +}
1659 +
1660 +.add-item-btn {
1661 + background: #28a745;
1662 + color: white;
1663 + border: none;
1664 + padding: 6px 12px;
1665 + border-radius: 4px;
1666 + cursor: pointer;
1667 + font-size: 12px;
1668 + transition: background 0.2s;
1669 +}
1670 +
1671 +.add-item-btn:hover {
1672 + background: #218838;
1673 +}
1674 +
1675 +.invoice-items-container {
1676 + max-height: 300px;
1677 + overflow-y: auto;
1678 +}
1679 +
1680 +.invoice-item {
1681 + background: white;
1682 + border: 1px solid #ddd;
1683 + border-radius: 4px;
1684 + padding: 10px;
1685 + margin-bottom: 10px;
1686 +}
1687 +
1688 +.item-row-single {
1689 + display: flex;
1690 + gap: 8px;
1691 + align-items: center;
1692 + flex-wrap: nowrap;
1693 +}
1694 +
1695 +.item-row-single .form-item {
1696 + flex: 0 0 auto;
1697 + margin-bottom: 0;
1698 + min-width: 0;
1699 +}
1700 +
1701 +.item-row-single .form-item label {
1702 + font-size: 11px;
1703 + color: #666;
1704 + margin-bottom: 2px;
1705 + display: block;
1706 +}
1707 +
1708 +.item-row-single .form-input {
1709 + width: 100%;
1710 + padding: 4px 6px;
1711 + font-size: 11px;
1712 + border: 1px solid #ddd;
1713 + border-radius: 3px;
1714 +}
1715 +
1716 +.remove-item-btn {
1717 + background: #dc3545;
1718 + color: white;
1719 + border: none;
1720 + padding: 4px 8px;
1721 + border-radius: 3px;
1722 + cursor: pointer;
1723 + font-size: 11px;
1724 + height: 28px;
1725 + transition: background 0.2s;
1726 +}
1727 +
1728 +.remove-item-btn:hover {
1729 + background: #c82333;
1730 +}
1313 </style> 1731 </style>
......