Compare commits
92 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
6776fe3f0d | |
|
|
3951ef325d | |
|
|
a7f7a29d74 | |
|
|
50ee54d6ef | |
|
|
701d90779a | |
|
|
61b10eba26 | |
|
|
45f69d527e | |
|
|
a56d750f2d | |
|
|
aa2efbd42e | |
|
|
6bed84cf3e | |
|
|
330f2f7d54 | |
|
|
b96d006f9c | |
|
|
baa4b52553 | |
|
|
dd2a7d99b6 | |
|
|
e1643a6331 | |
|
|
5f6f014226 | |
|
|
b3e5568216 | |
|
|
cc29006013 | |
|
|
db343353cc | |
|
|
01cd03213e | |
|
|
31a8de5569 | |
|
|
ec3e34ad21 | |
|
|
0d14477705 | |
|
|
74ed165f71 | |
|
|
f2cbf8f9cb | |
|
|
5485a827be | |
|
|
0dfdeee19d | |
|
|
d1e23c58ef | |
|
|
7a6abccf2f | |
|
|
602d06aafc | |
|
|
59b045d457 | |
|
|
97bcc1ac0a | |
|
|
b1cbbec237 | |
|
|
4ce68abe84 | |
|
|
3102a2c0a0 | |
|
|
357410c3e7 | |
|
|
a4ce1ba45a | |
|
|
7db1b6ed37 | |
|
|
4238f345cb | |
|
|
caec1d4bd0 | |
|
|
695c77ca60 | |
|
|
f0294212b2 | |
|
|
f992b2b29e | |
|
|
5583bb0f11 | |
|
|
47879d029a | |
|
|
5ead2c13b2 | |
|
|
389deac133 | |
|
|
b21c54a901 | |
|
|
e767c6aa50 | |
|
|
8ea80a4dbe | |
|
|
fb3f3c721e | |
|
|
d3bbf2da62 | |
|
|
693899f45c | |
|
|
fe2791f815 | |
|
|
54e7d394bb | |
|
|
6ec773ce3d | |
|
|
0774e81fd0 | |
|
|
1111d285c5 | |
|
|
c257cdc5cd | |
|
|
1d85e92997 | |
|
|
e7ed169f94 | |
|
|
9be3b62aaf | |
|
|
6c3f344b92 | |
|
|
ed90f7ce87 | |
|
|
f79150abc9 | |
|
|
8d6ca0b64f | |
|
|
6613b3612c | |
|
|
af12674d7b | |
|
|
7c46ae5db4 | |
|
|
56d79e96b6 | |
|
|
995f7f8b03 | |
|
|
2cf2fdff08 | |
|
|
dc1f5f7302 | |
|
|
49cd27c221 | |
|
|
a4a93ee484 | |
|
|
c940880a9d | |
|
|
3e45254fc1 | |
|
|
a19909d1bf | |
|
|
2f51b56298 | |
|
|
4cc2d3beb1 | |
|
|
ed9fda7fe3 | |
|
|
bd830115d4 | |
|
|
a9142c0e1b | |
|
|
d3c4776bab | |
|
|
a1ea52a934 | |
|
|
05865dc2ef | |
|
|
30d551fe59 | |
|
|
333b5e1a8e | |
|
|
94348aebc4 | |
|
|
1d842d3bf2 | |
|
|
1bdc88cd7c | |
|
|
1a4f64865a |
|
|
@ -0,0 +1,50 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询报价单列表
|
||||
export function listQuotation(query) {
|
||||
return request({
|
||||
url: '/quotation/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询报价单详细
|
||||
export function getQuotation(id) {
|
||||
return request({
|
||||
url: '/quotation/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增报价单
|
||||
export function addQuotation(data) {
|
||||
return request({
|
||||
url: '/quotation/insert',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改报价单
|
||||
export function updateQuotation(data) {
|
||||
return request({
|
||||
url: '/quotation/update',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除报价单
|
||||
export function delQuotation(id) {
|
||||
return request({
|
||||
url: '/quotation/remove/batch/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
export function exportSingleQuotation(id) {
|
||||
return request({
|
||||
url: '/quotation/export/single/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询财务计收列表
|
||||
export function listCharge(query) {
|
||||
return request({
|
||||
url: '/finance/charge/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询财务计收详细
|
||||
export function getCharge(id) {
|
||||
return request({
|
||||
url: '/finance/charge/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增财务计收
|
||||
export function addCharge(data) {
|
||||
return request({
|
||||
url: '/finance/charge',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改财务计收
|
||||
export function updateCharge(data) {
|
||||
return request({
|
||||
url: '/finance/charge',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function revokeCharge(data) {
|
||||
return request({
|
||||
url: '/finance/charge/revoke',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除财务计收
|
||||
export function delCharge(id) {
|
||||
return request({
|
||||
url: '/finance/charge/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
export function applyChargeBiz(data) {
|
||||
return request({
|
||||
url: '/finance/charge/applyBiz' ,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function returnApplyBiz(data) {
|
||||
return request({
|
||||
url: '/finance/charge/returnApplyBiz' ,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function applyCharge(data) {
|
||||
return request({
|
||||
url: '/finance/charge/apply' ,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
import request from '@/utils/request'
|
||||
import {tansParams} from "@/utils/ruoyi"
|
||||
|
||||
// 查询销售收票单列表
|
||||
export function listInvoice(query) {
|
||||
return request({
|
||||
url: '/finance/invoice/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询销售收票单详细
|
||||
export function getInvoice(id) {
|
||||
return request({
|
||||
url: '/finance/invoice/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询销售收票单附件
|
||||
export function getInvoiceAttachments(id, params) {
|
||||
return request({
|
||||
url: `/finance/invoice/attachment/${id}`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 上传销售收票单附件
|
||||
export function uploadInvoiceAttachment(data) {
|
||||
return request({
|
||||
url: '/finance/invoice/uploadReceipt',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data: data,
|
||||
needLoading: true
|
||||
});
|
||||
}
|
||||
|
||||
// 申请红冲
|
||||
export function redRush(id) {
|
||||
return request({
|
||||
url: '/finance/invoice/applyRefund/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 申请红冲 (提交表单)
|
||||
export function applyRefund(data) {
|
||||
return request({
|
||||
url: '/finance/invoice/applyRefund',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 退回销售收票单
|
||||
export function returnInvoice(id) {
|
||||
return request({
|
||||
url: '/finance/invoice/return/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增销售收票单
|
||||
export function addInvoice(data) {
|
||||
return request({
|
||||
url: '/finance/receivable/mergeAndInitiateInvoice',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
// 查询销售收票单产品明细
|
||||
export function getInvoiceProducts(code) {
|
||||
return request({
|
||||
url: '/finance/invoice/product/' + code,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 申请开票
|
||||
export function applyInvoice(data) {
|
||||
return request({
|
||||
url: '/finance/invoice/apply',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 撤销销售收票单
|
||||
export function revokeInvoice(id) {
|
||||
return request({
|
||||
url: '/finance/invoice/revoke/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询开票审批列表
|
||||
export function listInvoiceApprove(query) {
|
||||
return request({
|
||||
url: '/finance/invoice/approve/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询已审批开票列表
|
||||
export function listInvoiceApproved(query) {
|
||||
return request({
|
||||
url: '/finance/invoice/approved/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询开票单详细
|
||||
export function getInvoiceDetail(id) {
|
||||
return request({
|
||||
url: '/finance/invoice/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询收票待审批列表
|
||||
export function listInvoiceReceiptApprove(query) {
|
||||
return request({
|
||||
url: '/finance/ticket/approve/list',
|
||||
method: 'post',
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询收票已审批列表
|
||||
export function listInvoiceReceiptApproved(query) {
|
||||
return request({
|
||||
url: '/finance/ticket/approved/list',
|
||||
method: 'post',
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询收票详情
|
||||
export function getInvoiceReceipt(id) {
|
||||
return request({
|
||||
url: '/finance/ticket/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询收票附件
|
||||
export function getInvoiceReceiptAttachments(id,params) {
|
||||
return request({
|
||||
url: '/finance/ticket/attachment/' + id,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询核销记录列表
|
||||
export function listInvoiceWriteOff(query) {
|
||||
return request({
|
||||
url: '/finance/invoice/writeoff/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询核销详情
|
||||
export function getInvoiceWriteoff(id) {
|
||||
return request({
|
||||
url: '/finance/invoice/writeoff/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除核销记录
|
||||
export function delInvoiceWriteoff(ids) {
|
||||
return request({
|
||||
url: '/finance/invoice/writeoff/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询采购应付单列表
|
||||
export function listPayable(query) {
|
||||
return request({
|
||||
url: '/finance/payable/list',
|
||||
method: 'post',
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询采购应付单详情
|
||||
export function getPayable(id) {
|
||||
return request({
|
||||
url: '/finance/payable/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询付款计划列表
|
||||
export function getPaymentPlan(payableBillId) {
|
||||
return request({
|
||||
url: `/finance/payable/plan/${payableBillId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 更新付款计划
|
||||
export function updatePaymentPlan(payableBillId, data) {
|
||||
return request({
|
||||
url: `/finance/payable/plan/${payableBillId}`,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 合并并发起付款
|
||||
export function mergeAndInitiatePayment(data) {
|
||||
return request({
|
||||
url: '/finance/payable/mergeAndInitiatePayment',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
// 合并并发起收票
|
||||
export function mergeAndInitiateReceipt(data) {
|
||||
return request({
|
||||
url: '/finance/payable/mergeAndInitiateReceipt',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
// [PLACEHOLDER] 查询收票计划列表 - Endpoint to be confirmed by user
|
||||
export function getReceivingTicketPlan(payableBillId) {
|
||||
return request({
|
||||
url: `/finance/ticket/plan/${payableBillId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// [PLACEHOLDER] 更新收票计划 - Endpoint to be confirmed by user
|
||||
export function updateReceivingTicketPlan(payableBillId, data) {
|
||||
return request({
|
||||
url: `/finance/ticket/plan/${payableBillId}`,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 同步付款计划至发票计划
|
||||
export function syncToTicketPlan(payableBillId) {
|
||||
return request({
|
||||
url: `/finance/payable/plan/sync/${payableBillId}`,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
import request from '@/utils/request'
|
||||
import { tansParams } from "@/utils/ruoyi"
|
||||
|
||||
// 查询付款单列表
|
||||
export function listPayment(query) {
|
||||
return request({
|
||||
url: '/finance/payment/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: tansParams(query)
|
||||
})
|
||||
}
|
||||
export function exportPayment(query) {
|
||||
return request({
|
||||
url: '/finance/payment/export',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: tansParams(query)
|
||||
})
|
||||
}
|
||||
|
||||
// 查询付款单详细
|
||||
export function getPayment(id) {
|
||||
return request({
|
||||
url: '/finance/payment/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询付款单附件
|
||||
export function getPaymentAttachments(id, params) {
|
||||
return request({
|
||||
url: `/finance/payment/attachment/${id}`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
export function deleteFile(id) {
|
||||
return request({
|
||||
url: `/finance/payment/attachment/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 上传付款单附件
|
||||
export function uploadPaymentAttachment(data) {
|
||||
return request({
|
||||
url: '/finance/payment/uploadReceipt',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 退回付款单
|
||||
export function returnPayment(id) {
|
||||
return request({
|
||||
url: '/finance/payment/returnPayment/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增付款单
|
||||
export function addPaymentFromPayable(data) {
|
||||
return request({
|
||||
url: '/finance/payable/mergeAndInitiatePayment',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
export function addPayment(data) {
|
||||
return request({
|
||||
url: '/finance/payment/add',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
export function handleRevoke(id) {
|
||||
return request({
|
||||
url: '/finance/payment/revoke',
|
||||
method: 'post',
|
||||
data: {id: id},
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
// 申请付款
|
||||
export function applyPaymentApi(data) {
|
||||
return request({
|
||||
url: '/finance/payment/applyPayment',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
// 申请退款
|
||||
export function applyRefund(id) {
|
||||
return request({
|
||||
url: '/finance/payment/applyRefund/'+id,
|
||||
method: 'get',
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
export function deletePayment(id) {
|
||||
return request({
|
||||
url: '/finance/payment/remove',
|
||||
method: 'post',
|
||||
params:{ids:id},
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
export function applyRefundApprove(id) {
|
||||
return request({
|
||||
url: '/finance/payment/applyRefundApprove',
|
||||
method: 'post',
|
||||
data: {id: id},
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
// 查询应付单列表 (用于新增付款单-非预付)
|
||||
export function listPayableBills(query) {
|
||||
return request({
|
||||
url: 'finance/payable/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询采购订单列表 (用于新增付款单-预付)
|
||||
export function listOrders(query) {
|
||||
return request({
|
||||
url: '/project/order/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询付款审批列表
|
||||
export function listPaymentApprove(query) {
|
||||
return request({
|
||||
url: '/finance/payment/approve/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: tansParams(query)
|
||||
})
|
||||
}
|
||||
|
||||
// 查询已审批付款列表
|
||||
export function listPaymentApproved(query) {
|
||||
return request({
|
||||
url: '/finance/payment/approved/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: tansParams(query)
|
||||
})
|
||||
}
|
||||
|
||||
// 查询付款单列表 (核销专用)
|
||||
export function listPaymentForWriteOff(query) {
|
||||
return request({
|
||||
url: '/finance/payment/write-off/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: tansParams(query)
|
||||
})
|
||||
}
|
||||
|
||||
// 手工匹配核销
|
||||
export function manualWriteOff(data) {
|
||||
return request({
|
||||
url: '/finance/writeoff',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import request from '@/utils/request'
|
||||
import {tansParams} from "@/utils/ruoyi";
|
||||
|
||||
// 查询付款退款待审批列表
|
||||
export function listPaymentRefundApprove(query) {
|
||||
return request({
|
||||
url: '/finance/payment/approve/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询付款退款已审批列表
|
||||
export function listPaymentRefundApproved(query) {
|
||||
return request({
|
||||
url: '/finance/payment/approved/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询付款退款详情
|
||||
export function getPaymentRefund(id) {
|
||||
return request({
|
||||
url: '/finance/paymentRefund/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
import request from '@/utils/request'
|
||||
import {tansParams} from "@/utils/ruoyi"
|
||||
|
||||
// 查询收票单列表
|
||||
export function listReceipt(query) {
|
||||
return request({
|
||||
url: '/finance/ticket/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询收票单详细
|
||||
export function getReceipt(id) {
|
||||
return request({
|
||||
url: '/finance/ticket/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询收票单附件
|
||||
export function getReceiptAttachments(id, params) {
|
||||
return request({
|
||||
url: `/finance/ticket/attachment/${id}`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 上传收票单附件
|
||||
export function uploadReceiptAttachment(data) {
|
||||
return request({
|
||||
url: '/finance/ticket/uploadReceipt',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data: data,
|
||||
needLoading: true
|
||||
});
|
||||
}
|
||||
|
||||
// 退回收票单
|
||||
export function redRush(id) {
|
||||
return request({
|
||||
url: '/finance/ticket/applyRefund/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function returnReceipt(id) {
|
||||
return request({
|
||||
url: '/finance/ticket/return/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增收票单
|
||||
export function addReceipt(data) {
|
||||
return request({
|
||||
url: '/finance/payable/mergeAndInitiateReceipt',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
// 撤销收票单
|
||||
export function revokeReceipt(id) {
|
||||
return request({
|
||||
url: '/finance/ticket/revoke/' + id,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
// 申请红冲(带附件)
|
||||
export function applyRedRush(data) {
|
||||
return request({
|
||||
url: '/finance/ticket/applyRefund',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data: data,
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
// 查询收款审批列表
|
||||
export function listReceiptApprove(query) {
|
||||
return request({
|
||||
url: '/finance/receipt/approve/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询已审批收款列表
|
||||
export function listReceiptApproved(query) {
|
||||
return request({
|
||||
url: '/finance/receipt/approved/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询收款单详细
|
||||
export function getReceiptDetail(id) {
|
||||
return request({
|
||||
url: '/finance/receipt/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询销售应收单列表
|
||||
export function listReceivable(query) {
|
||||
return request({
|
||||
url: '/finance/receivable/list',
|
||||
method: 'post',
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询销售应收单详情
|
||||
export function getReceivable(id) {
|
||||
return request({
|
||||
url: '/finance/receivable/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询收款计划列表
|
||||
export function getReceiptPlan(receivableBillId) {
|
||||
return request({
|
||||
url: `/finance/receivable/plan/${receivableBillId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 更新收款计划
|
||||
export function updateReceiptPlan(receivableBillId, data) {
|
||||
return request({
|
||||
url: `/finance/receivable/plan/${receivableBillId}`,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 合并并发起收款
|
||||
export function mergeAndInitiateReceipt(data) {
|
||||
return request({
|
||||
url: '/finance/receivable/mergeAndInitiateReceipt',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading:true
|
||||
})
|
||||
}
|
||||
|
||||
// 合并并发起开票
|
||||
export function mergeAndInitiateInvoice(data) {
|
||||
return request({
|
||||
url: '/finance/receivable/mergeAndInitiateInvoice',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading:true
|
||||
})
|
||||
}
|
||||
|
||||
// 查询开票计划列表
|
||||
export function getInvoicePlan(receivableBillId) {
|
||||
return request({
|
||||
url: `/finance/receivable/invoice/plan/${receivableBillId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 更新开票计划
|
||||
export function updateInvoicePlan(receivableBillId, data) {
|
||||
return request({
|
||||
url: `/finance/receivable/invoice/plan/${receivableBillId}`,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 同步收款计划至开票计划
|
||||
export function syncToInvoicePlan(receivableBillId) {
|
||||
return request({
|
||||
url: `/finance/receivable/plan/sync/${receivableBillId}`,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询销售核销记录列表
|
||||
export function listReceivableWriteOff(query) {
|
||||
return request({
|
||||
url: '/finance/receivable/writeoff/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询销售核销详情
|
||||
export function getReceivableWriteOff(id) {
|
||||
return request({
|
||||
url: '/finance/receivable/writeoff/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除销售核销记录
|
||||
export function delReceivableWriteOff(ids) {
|
||||
return request({
|
||||
url: '/finance/receivable/writeoff/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
import request from '@/utils/request'
|
||||
import {tansParams} from "@/utils/ruoyi"
|
||||
|
||||
// 查询收款单列表
|
||||
export function listReceive(query) {
|
||||
return request({
|
||||
url: '/finance/receipt/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询收款单详细
|
||||
export function getReceive(id) {
|
||||
return request({
|
||||
url: '/finance/receipt/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询收款单附件
|
||||
export function getReceiveAttachments(id, params) {
|
||||
return request({
|
||||
url: `/finance/receipt/attachment/${id}`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 上传收款单附件
|
||||
export function uploadReceiveAttachment(data) {
|
||||
return request({
|
||||
url: '/finance/receipt/uploadReceipt',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data: data,
|
||||
needLoading: true
|
||||
});
|
||||
}
|
||||
|
||||
// 申请红冲
|
||||
export function redRush(id) {
|
||||
return request({
|
||||
url: '/finance/receipt/applyRefund/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 退回收款单
|
||||
export function returnReceive(id) {
|
||||
return request({
|
||||
url: '/finance/receipt/returnReceivable/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增收款单 (Calls Receivable Merge Logic)
|
||||
export function mergeReceivable(data) {
|
||||
return request({
|
||||
url: '/finance/receivable/mergeAndInitiateReceipt',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
export function addReceipt(data) {
|
||||
return request({
|
||||
url: '/finance/receipt/insert',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
export function applyReceipt(data) {
|
||||
return request({
|
||||
url: '/finance/receipt/applyReceipt',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: data,
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
// 申请退款
|
||||
export function submitRefund(data) {
|
||||
return request({
|
||||
url: '/finance/receipt/applyRefund',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
// 查询收款单列表 (核销专用)
|
||||
export function listReceiptForWriteOff(query) {
|
||||
return request({
|
||||
url: '/finance/receipt/write-off/list',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: tansParams(query)
|
||||
})
|
||||
}
|
||||
|
||||
// 手工匹配核销
|
||||
export function manualReceiptWriteOff(data) {
|
||||
return request({
|
||||
url: '/finance/receivable/writeoff',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 撤销收款单
|
||||
export function revokeReceipt(id) {
|
||||
return request({
|
||||
url: '/finance/receipt/revoke',
|
||||
method: 'post',
|
||||
data: {id: id},
|
||||
needLoading: true
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询项目报表列表
|
||||
export function listReport(query) {
|
||||
return request({
|
||||
url: '/finance/report/list',
|
||||
method: 'post',
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询核销记录列表
|
||||
export function listWriteOff(query) {
|
||||
return request({
|
||||
url: '/finance/writeoff/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
export function listTicketWriteOff(query) {
|
||||
return request({
|
||||
url: '/finance/ticketWriteoff/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询核销详情
|
||||
export function getWriteOff(id) {
|
||||
return request({
|
||||
url: '/finance/writeoff/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
export function getTicketWriteoff(id) {
|
||||
return request({
|
||||
url: '/finance/ticketWriteoff/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除核销记录
|
||||
export function delWriteOff(ids) {
|
||||
return request({
|
||||
url: '/finance/writeoff/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
export function delTicketWriteoff(ids) {
|
||||
return request({
|
||||
url: '/finance/ticketWriteoff/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -4,8 +4,10 @@ import request from '@/utils/request'
|
|||
export function listProject(query) {
|
||||
return request({
|
||||
url: '/sip/project/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
method: 'post',
|
||||
data: query,
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -52,3 +54,29 @@ export function exportProject(query) {
|
|||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 项目收藏
|
||||
export function addCollect(data) {
|
||||
return request({
|
||||
url: '/project/collect/add',
|
||||
method: 'post',
|
||||
data: data,
|
||||
needLoading:true
|
||||
})
|
||||
}
|
||||
export function editJoinTrial(data) {
|
||||
return request({
|
||||
url: '/sip/project/vue/joinTrial',
|
||||
method: 'put',
|
||||
data: data,
|
||||
needLoading:true
|
||||
})
|
||||
}
|
||||
export function exportSingle(id) {
|
||||
return request({
|
||||
url: `/sip/project/vue/joinTrial/export/${id}`,
|
||||
method: 'get',
|
||||
needLoading:true
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,6 +106,16 @@ export function recallPurchaseorder(id) {
|
|||
method: 'put'
|
||||
})
|
||||
}
|
||||
export function exportPurchaseorder(data) {
|
||||
return request({
|
||||
url: '/sip/purchaseorder/export',
|
||||
method: 'get',
|
||||
params: data,
|
||||
// headers: { 'Content-Type': 'multipart/form-data' },
|
||||
needLoading: true
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// 查询已审批采购单主表列表
|
||||
export function listApprovedPurchaseorder(query) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询公司信息列表
|
||||
export function listCompanyInfo(query) {
|
||||
return request({
|
||||
url: '/sip/companyInfo/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询公司信息详细
|
||||
export function getCompanyInfo(id) {
|
||||
return request({
|
||||
url: '/sip/companyInfo/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增公司信息
|
||||
export function addCompanyInfo(data) {
|
||||
return request({
|
||||
url: '/sip/companyInfo',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改公司信息
|
||||
export function updateCompanyInfo(data) {
|
||||
return request({
|
||||
url: '/sip/companyInfo',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除公司信息
|
||||
export function delCompanyInfo(id) {
|
||||
return request({
|
||||
url: '/sip/companyInfo/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||
<img :src="currentImageUrl" style="width: 100%;max-height: 60vh" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import request from '@/utils/request';
|
||||
|
||||
export default {
|
||||
name: "GlobalFilePreview",
|
||||
data() {
|
||||
return {
|
||||
pdfPreviewVisible: false,
|
||||
currentPdfUrl: '',
|
||||
imagePreviewVisible: false,
|
||||
currentImageUrl: ''
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
isPdf(filePath) {
|
||||
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
getImageUrl(resource) {
|
||||
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||
},
|
||||
handlePreview(attachment) {
|
||||
if (!attachment) return;
|
||||
if (this.isPdf(attachment.filePath)) {
|
||||
request({
|
||||
url: '/common/download/resource',
|
||||
method: 'get',
|
||||
params: { resource: attachment.filePath },
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||
this.pdfPreviewVisible = true;
|
||||
});
|
||||
} else {
|
||||
this.currentImageUrl = this.getImageUrl(attachment.filePath);
|
||||
this.imagePreviewVisible = true;
|
||||
}
|
||||
},
|
||||
downloadFile(attachment){
|
||||
if (attachment){
|
||||
const link = document.createElement('a');
|
||||
link.href = process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" +attachment.filePath;
|
||||
link.download = attachment.fileName || 'file';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
<el-button size="mini" circle icon="el-icon-menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
|
||||
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
|
||||
<el-button size="mini" circle icon="el-icon-menu" />
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-menu slot="dropdown" >
|
||||
<!-- 全选/反选 按钮 -->
|
||||
<el-dropdown-item>
|
||||
<el-checkbox :indeterminate="isIndeterminate" v-model="isChecked" @change="toggleCheckAll"> 列展示 </el-checkbox>
|
||||
|
|
@ -183,4 +183,8 @@ export default {
|
|||
background-color: #ccc;
|
||||
margin: 3px auto;
|
||||
}
|
||||
.el-dropdown-menu{
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import './permission' // permission control
|
|||
import { getDicts } from "@/api/system/dict/data"
|
||||
import { getConfigKey } from "@/api/system/config"
|
||||
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"
|
||||
import { formatCurrency } from "@/utils"
|
||||
// 分页组件
|
||||
import Pagination from "@/components/Pagination"
|
||||
// 自定义表格工具组件
|
||||
|
|
@ -42,6 +43,7 @@ import DictData from '@/components/DictData'
|
|||
Vue.prototype.getDicts = getDicts
|
||||
Vue.prototype.getConfigKey = getConfigKey
|
||||
Vue.prototype.parseTime = parseTime
|
||||
Vue.prototype.formatCurrency = formatCurrency
|
||||
Vue.prototype.resetForm = resetForm
|
||||
Vue.prototype.addDateRange = addDateRange
|
||||
Vue.prototype.selectDictLabel = selectDictLabel
|
||||
|
|
|
|||
|
|
@ -9,13 +9,12 @@ const baseURL = process.env.VUE_APP_BASE_API
|
|||
let downloadLoadingInstance
|
||||
|
||||
export default {
|
||||
name(name, isDelete = true) {
|
||||
download(name, isDelete = true) {
|
||||
var url = baseURL + "/common/download?fileName=" + encodeURIComponent(name) + "&delete=" + isDelete
|
||||
axios({
|
||||
method: 'get',
|
||||
url: url,
|
||||
responseType: 'blob',
|
||||
headers: { 'Authorization': 'Bearer ' + getToken() }
|
||||
}).then((res) => {
|
||||
const isBlob = blobValidate(res.data)
|
||||
if (isBlob) {
|
||||
|
|
|
|||
|
|
@ -94,6 +94,46 @@ export const constantRoutes = [
|
|||
component: () => import('@/views/approve/approved_order/index'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'paymentLog',
|
||||
component: () => import('@/views/approve/finance/payment/approved/index'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'paymentRedLog',
|
||||
component: () => import('@/views/approve/finance/paymentRefund/approved/index'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'invoiceRedLog',
|
||||
component: () => import('@/views/approve/finance/invoiceRed/approved/index'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'invoiceLog',
|
||||
component: () => import('@/views/approve/finance/invoiceReceipt/approved/index'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'receiptLog',
|
||||
component: () => import('@/views/approve/finance/receipt/approved/index.vue'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'receiptRefoundLog',
|
||||
component: () => import('@/views/approve/finance/receiptRefound/approved/index.vue'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'receivableInvoiceLog',
|
||||
component: () => import('@/views/approve/finance/receivableInvoice/approved/index.vue'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'receivableInvoiceRefundLog',
|
||||
component: () => import('@/views/approve/finance/receivableInvoiceRefund/approved/index.vue'),
|
||||
hidden: true
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -164,6 +204,31 @@ export const dynamicRoutes = [
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/finance',
|
||||
component: Layout,
|
||||
redirect: 'noRedirect',
|
||||
name: 'Finance',
|
||||
meta: {
|
||||
title: '财务管理',
|
||||
icon: 'money'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'payment',
|
||||
component: () => import('@/views/finance/payment/index'),
|
||||
name: 'Payment',
|
||||
meta: { title: '付款单', icon: 'form' }
|
||||
},
|
||||
{
|
||||
path: 'charge',
|
||||
component: () => import('@/views/finance/charge/index'),
|
||||
name: 'Charge',
|
||||
meta: { title: '财务计收', icon: 'money' },
|
||||
permissions: ['finance:charge:list']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/inventory/execution',
|
||||
component: Layout,
|
||||
|
|
|
|||
|
|
@ -19,17 +19,17 @@ export function toFixed(value, dp = DEFAULT_DP) {
|
|||
|
||||
// 加法
|
||||
export function add(a, b, dp = DEFAULT_DP) {
|
||||
return D(a).plus(b).toDecimalPlaces(dp).toNumber()
|
||||
return D(a).plus(D(b)).toDecimalPlaces(dp).toNumber()
|
||||
}
|
||||
|
||||
// 减法
|
||||
export function sub(a, b, dp = DEFAULT_DP) {
|
||||
return D(a).minus(b).toDecimalPlaces(dp).toNumber()
|
||||
return D(a).minus(D(b)).toDecimalPlaces(dp).toNumber()
|
||||
}
|
||||
|
||||
// 乘法
|
||||
export function mul(a, b, dp = DEFAULT_DP) {
|
||||
return D(a).times(b).toDecimalPlaces(dp).toNumber()
|
||||
return D(a).times(D(b)).toDecimalPlaces(dp).toNumber()
|
||||
}
|
||||
|
||||
// 除法
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ export function formatDate(cellValue) {
|
|||
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
|
||||
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
|
||||
}
|
||||
|
||||
export function formatCurrency(value) {
|
||||
if (value == null) return '0.00';
|
||||
return Number(value).toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
|
||||
}
|
||||
/**
|
||||
* @param {number} time
|
||||
* @param {string} option
|
||||
|
|
|
|||
|
|
@ -49,6 +49,22 @@
|
|||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品编码" prop="productCode">
|
||||
<el-input
|
||||
v-model="queryParams.productCode"
|
||||
placeholder="请输入产品编码"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品型号" prop="productModel">
|
||||
<el-input
|
||||
v-model="queryParams.productModel"
|
||||
placeholder="请输入产品型号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
|
|
@ -72,7 +88,11 @@
|
|||
<el-table-column label="项目名称" align="center" prop="projectName" />
|
||||
<el-table-column label="项目编号" align="center" prop="projectCode" />
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" />
|
||||
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" >
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatCurrency(scope.row.actualPurchaseAmount || scope.row.shipmentAmount) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="汇智负责人" align="center" prop="dutyName" />
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode">
|
||||
<template slot-scope="scope">
|
||||
|
|
@ -127,6 +147,7 @@
|
|||
<script>
|
||||
import { listOrder } from "@/api/approve/order/orderLog";
|
||||
import ApproveDialog from '../order/Approve.vue';
|
||||
import {formatCurrency} from "../../../utils";
|
||||
|
||||
export default {
|
||||
name: "ApprovedOrder",
|
||||
|
|
@ -153,6 +174,8 @@ export default {
|
|||
orderCode: null,
|
||||
projectName: null,
|
||||
projectCode: null,
|
||||
productCode: null,
|
||||
productModel: null,
|
||||
customerName: null,
|
||||
dutyName: null,
|
||||
approveNode: null,
|
||||
|
|
@ -165,6 +188,7 @@ export default {
|
|||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
formatCurrency,
|
||||
/** 查询订单列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,211 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="收票编号" prop="receiptNo">
|
||||
<el-input v-model="queryParams.receiptNo" placeholder="请输入收票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="供应商" prop="vendorName">
|
||||
<el-input v-model="queryParams.vendorName" placeholder="请输入供应商" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="invoiceReceiptList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="收票编号" align="center" prop="ticketBillCode" />
|
||||
<el-table-column label="供应商" align="center" prop="vendorName" />
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog title="收票单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="收票单详情">
|
||||
<invoice-receipt-detail :data="form"></invoice-receipt-detail>
|
||||
<template #footer>
|
||||
<span> {{ form.ticketBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listInvoiceReceiptApproved, getInvoiceReceipt } from "@/api/finance/invoiceReceipt";
|
||||
import { listCompletedFlows } from "@/api/flow";
|
||||
import InvoiceReceiptDetail from "../components/InvoiceReceiptDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "InvoiceReceiptApproved",
|
||||
components: { InvoiceReceiptDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
invoiceReceiptList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
receiptNo: null,
|
||||
vendorName: null,
|
||||
processKey: 'fianance_ticket',
|
||||
projectName: null
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
currentInvoiceReceiptId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listInvoiceReceiptApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.invoiceReceiptList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
handleView(row) {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.currentInvoiceReceiptId = row.id;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getInvoiceReceipt(this.currentInvoiceReceiptId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.ticketBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
getStatusText(status) {
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `收票单-${this.form.receiptBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<template>
|
||||
<div class="invoice-receipt-detail">
|
||||
<div style="text-align: center;font-weight:bold;font-size: 25px;">收票申请单</div>
|
||||
<el-descriptions title="收票单信息" :column="3" border>
|
||||
<el-descriptions-item label="采购-收票单编号">{{ data.ticketBillCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="制造商名称">{{ data.vendorName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="票据类型">
|
||||
<dict-tag :options="dict.type.finance_invoice_type" :value="data.ticketType"/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="含税总价(元)">{{ formatCurrency(data.totalPriceWithTax) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="未税总价(元)">{{ formatCurrency(data.totalPriceWithoutTax) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="税额(元)">{{ formatCurrency(data.taxAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预计收票时间">{{ data.ticketTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="制造商开票时间">{{ data.vendorTicketTime }}</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="备注" :span="3">{{ data.remark }}</el-descriptions-item>-->
|
||||
</el-descriptions>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-show="data.detailList && data.detailList.length>0">
|
||||
<div class="el-descriptions__title">发票明细列表</div>
|
||||
<el-table :data="data.detailList" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="payableBillCode" label="采购-应付单编号" align="center"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||
<el-table-column prop="productType" label="产品类型" align="center">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column prop="paymentAmount" label="本次收票金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
|
||||
<div class="el-descriptions__title">附件信息</div>
|
||||
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||
<el-table-column prop="createByName" label="上传人" align="center"></el-table-column>
|
||||
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||
<img :src="currentImageUrl" style="width: 100%;" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getInvoiceReceiptAttachments } from "@/api/finance/invoiceReceipt";
|
||||
import request from '@/utils/request';
|
||||
|
||||
export default {
|
||||
name: "InvoiceReceiptDetail",
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
dicts: ['finance_invoice_type', 'product_type'],
|
||||
data() {
|
||||
return {
|
||||
attachments: [],
|
||||
pdfPreviewVisible: false,
|
||||
currentPdfUrl: '',
|
||||
imagePreviewVisible: false,
|
||||
currentImageUrl: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'data.id': {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
this.fetchAttachments(val);
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchAttachments(id) {
|
||||
getInvoiceReceiptAttachments(id,{ type: 'ticket' }).then(response => {
|
||||
this.attachments = (response.data || []).filter(item => item.delFlag !== '2');
|
||||
});
|
||||
},
|
||||
isPdf(filePath) {
|
||||
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
getImageUrl(resource) {
|
||||
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||
},
|
||||
handlePreview(row) {
|
||||
if (this.isPdf(row.filePath)) {
|
||||
request({
|
||||
url: '/common/download/resource',
|
||||
method: 'get',
|
||||
params: { resource: row.filePath },
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||
this.pdfPreviewVisible = true;
|
||||
});
|
||||
} else {
|
||||
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||
this.imagePreviewVisible = true;
|
||||
}
|
||||
},
|
||||
handleDownload(row) {
|
||||
const link = document.createElement('a');
|
||||
link.href = this.getImageUrl(row.filePath);
|
||||
link.download = row.fileName || 'attachment';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.invoice-receipt-detail {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,300 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="收票编号" prop="receiptNo">
|
||||
<el-input v-model="queryParams.receiptNo" placeholder="请输入收票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="供应商" prop="vendorName">
|
||||
<el-input v-model="queryParams.vendorName" placeholder="请输入供应商" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
size="mini"
|
||||
@click="toApproved()"
|
||||
>审批历史</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="invoiceReceiptList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="收票编号" align="center" prop="ticketBillCode" />
|
||||
<el-table-column label="制造商" align="center" prop="vendorName" />
|
||||
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<!-- <el-table-column label="登记人" align="center" prop="createUserName" />-->
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 审批详情主对话框 -->
|
||||
<el-dialog title="收票单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="收票单详情">
|
||||
<invoice-receipt-detail :data="form"></invoice-receipt-detail>
|
||||
<template #footer>
|
||||
<span>{{ form.ticketBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b><el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批意见对话框 -->
|
||||
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||
<el-form-item label="审批意见" prop="approveOpinion">
|
||||
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listInvoiceReceiptApprove, getInvoiceReceipt } from "@/api/finance/invoiceReceipt";
|
||||
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||
import InvoiceReceiptDetail from "./components/InvoiceReceiptDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "InvoiceReceiptApprove",
|
||||
components: { InvoiceReceiptDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
invoiceReceiptList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
receiptNo: null,
|
||||
vendorName: null,
|
||||
processKey: 'fianance_ticket',
|
||||
projectName: null
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
opinionDialogVisible: false,
|
||||
confirmDialogTitle: '',
|
||||
currentApproveType: '',
|
||||
opinionForm: {
|
||||
approveOpinion: ''
|
||||
},
|
||||
opinionRules: {
|
||||
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
},
|
||||
processKey: 'fianance_ticket',
|
||||
taskId: null,
|
||||
currentInvoiceReceiptId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listInvoiceReceiptApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.invoiceReceiptList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
toApproved() {
|
||||
this.$router.push( '/approve/invoiceLog' )
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.resetDetailForm();
|
||||
this.currentInvoiceReceiptId = row.id;
|
||||
this.taskId = row.taskId;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getInvoiceReceipt(this.currentInvoiceReceiptId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.ticketBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
resetDetailForm() {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.opinionForm.approveOpinion = '';
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
let keys = [];
|
||||
if(this.processKey) keys.push(this.processKey);
|
||||
|
||||
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
openOpinionDialog(type) {
|
||||
this.currentApproveType = type;
|
||||
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||
this.opinionDialogVisible = true;
|
||||
this.opinionForm.approveOpinion = '';
|
||||
this.$nextTick(() => {
|
||||
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||
});
|
||||
},
|
||||
showConfirmDialog() {
|
||||
this.$refs.opinionForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.opinionDialogVisible = false;
|
||||
this.submitApproval();
|
||||
}
|
||||
});
|
||||
},
|
||||
submitApproval() {
|
||||
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||
const params = {
|
||||
businessKey: this.form.ticketBillCode,
|
||||
processKey: this.processKey,
|
||||
taskId: this.taskId,
|
||||
variables: {
|
||||
comment: this.opinionForm.approveOpinion,
|
||||
approveBtn: approveBtn
|
||||
}
|
||||
};
|
||||
|
||||
approveTask(params).then(() => {
|
||||
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||
this.detailDialogVisible = false;
|
||||
this.getList();
|
||||
});
|
||||
},
|
||||
getStatusText(status) {
|
||||
if (!status) {
|
||||
return '提交审批'
|
||||
}
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `收票单-${this.form.ticketBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="收票编号" prop="receiptNo">
|
||||
<el-input v-model="queryParams.receiptNo" placeholder="请输入收票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="供应商" prop="vendorName">
|
||||
<el-input v-model="queryParams.vendorName" placeholder="请输入供应商" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="invoiceReceiptList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="收票编号" align="center" prop="ticketBillCode" />
|
||||
<el-table-column label="供应商" align="center" prop="vendorName" />
|
||||
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog title="红冲发票详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="红冲发票详情">
|
||||
<invoice-red-detail :data="form"></invoice-red-detail>
|
||||
<template #footer>
|
||||
<span> {{ form.ticketBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listInvoiceReceiptApproved, getInvoiceReceipt } from "@/api/finance/invoiceReceipt";
|
||||
import { listCompletedFlows } from "@/api/flow";
|
||||
import InvoiceRedDetail from "../components/InvoiceRedDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "InvoiceRedApproved",
|
||||
components: { InvoiceRedDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
invoiceReceiptList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
receiptNo: null,
|
||||
vendorName: null,
|
||||
processKey: 'finance_ticket_refound',
|
||||
projectName: null
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
currentInvoiceReceiptId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listInvoiceReceiptApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.invoiceReceiptList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
handleView(row) {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.currentInvoiceReceiptId = row.id;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getInvoiceReceipt(this.currentInvoiceReceiptId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.ticketBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
getStatusText(status) {
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `红冲发票-${this.form.receiptBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
<template>
|
||||
<div class="invoice-red-detail">
|
||||
<div style="text-align: center;font-weight:bold;font-size: 25px;">红冲收票申请单</div>
|
||||
<el-descriptions title="红冲发票信息" :column="3" border>
|
||||
<el-descriptions-item label="采购-收票单编号">{{ data.ticketBillCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="制造商名称">{{ data.vendorName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="票据类型">
|
||||
<dict-tag :options="dict.type.finance_invoice_type" :value="data.ticketType"/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="含税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithTax) }} </span></el-descriptions-item>
|
||||
<el-descriptions-item label="未税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithTax) }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="税额(元)"><span style="color: red">{{ formatCurrency(data.taxAmount) }} </span></el-descriptions-item>
|
||||
<el-descriptions-item label="收票时间">{{ data.ticketTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="制造商开票时间">{{ data.vendorTicketTime }}</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="备注" :span="3">{{ data.remark }}</el-descriptions-item>-->
|
||||
</el-descriptions>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-show="data.detailList && data.detailList.length>0">
|
||||
<div class="el-descriptions__title">发票明细列表</div>
|
||||
<el-table :data="data.detailList" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="payableBillCode" label="采购-应付单编号" align="center"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||
<el-table-column prop="productType" label="产品类型" align="center">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column prop="paymentAmount" label="本次红冲金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
|
||||
<div class="el-descriptions__title">附件信息</div>
|
||||
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||
<el-table-column prop="createByName" label="上传人" align="center"></el-table-column>
|
||||
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||
<img :src="currentImageUrl" style="width: 100%;" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getInvoiceReceiptAttachments } from "@/api/finance/invoiceReceipt";
|
||||
import request from '@/utils/request';
|
||||
|
||||
export default {
|
||||
name: "InvoiceRedDetail",
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
dicts: ['finance_invoice_type', 'product_type'],
|
||||
data() {
|
||||
return {
|
||||
attachments: [],
|
||||
pdfPreviewVisible: false,
|
||||
currentPdfUrl: '',
|
||||
imagePreviewVisible: false,
|
||||
currentImageUrl: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'data.id': {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
this.fetchAttachments(val);
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchAttachments(id) {
|
||||
// 保持调用 invoiceReceipt 的附件接口,因为接口一致
|
||||
getInvoiceReceiptAttachments(id,{ type: 'ticket' }).then(response => {
|
||||
this.attachments = (response.data || []).filter(item => item.delFlag !== '2');
|
||||
});
|
||||
},
|
||||
isPdf(filePath) {
|
||||
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
getImageUrl(resource) {
|
||||
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||
},
|
||||
handlePreview(row) {
|
||||
if (this.isPdf(row.filePath)) {
|
||||
request({
|
||||
url: '/common/download/resource',
|
||||
method: 'get',
|
||||
params: { resource: row.filePath },
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||
this.pdfPreviewVisible = true;
|
||||
});
|
||||
} else {
|
||||
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||
this.imagePreviewVisible = true;
|
||||
}
|
||||
},
|
||||
handleDownload(row) {
|
||||
const link = document.createElement('a');
|
||||
link.href = this.getImageUrl(row.filePath);
|
||||
link.download = row.fileName || 'attachment';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.invoice-red-detail {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="收票编号" prop="receiptNo">
|
||||
<el-input v-model="queryParams.receiptNo" placeholder="请输入收票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="供应商" prop="vendorName">
|
||||
<el-input v-model="queryParams.vendorName" placeholder="请输入供应商" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
size="mini"
|
||||
@click="toApproved()"
|
||||
>审批历史</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="invoiceReceiptList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="收票编号" align="center" prop="ticketBillCode" />
|
||||
<el-table-column label="供应商" align="center" prop="vendorName" />
|
||||
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)">
|
||||
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="登记人" align="center" prop="createUserName" />-->
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 审批详情主对话框 -->
|
||||
<el-dialog title="红冲发票审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="红冲发票详情">
|
||||
<invoice-red-detail :data="form"></invoice-red-detail>
|
||||
<template #footer>
|
||||
<span> {{ form.ticketBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批意见对话框 -->
|
||||
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||
<el-form-item label="审批意见" prop="approveOpinion">
|
||||
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listInvoiceReceiptApprove, getInvoiceReceipt } from "@/api/finance/invoiceReceipt";
|
||||
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||
import InvoiceRedDetail from "./components/InvoiceRedDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "InvoiceRedApprove",
|
||||
components: { InvoiceRedDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
invoiceReceiptList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
receiptNo: null,
|
||||
vendorName: null,
|
||||
processKey: 'finance_ticket_refound',
|
||||
projectName: null
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
opinionDialogVisible: false,
|
||||
confirmDialogTitle: '',
|
||||
currentApproveType: '',
|
||||
opinionForm: {
|
||||
approveOpinion: ''
|
||||
},
|
||||
opinionRules: {
|
||||
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
},
|
||||
processKey: 'finance_ticket_refound',
|
||||
taskId: null,
|
||||
currentInvoiceReceiptId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listInvoiceReceiptApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.invoiceReceiptList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
toApproved() {
|
||||
this.$router.push( '/approve/invoiceRedLog' )
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.resetDetailForm();
|
||||
this.currentInvoiceReceiptId = row.id;
|
||||
this.taskId = row.taskId;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getInvoiceReceipt(this.currentInvoiceReceiptId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.ticketBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
resetDetailForm() {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.opinionForm.approveOpinion = '';
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
let keys = [];
|
||||
if(this.processKey) keys.push(this.processKey);
|
||||
|
||||
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
openOpinionDialog(type) {
|
||||
this.currentApproveType = type;
|
||||
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||
this.opinionDialogVisible = true;
|
||||
this.opinionForm.approveOpinion = '';
|
||||
this.$nextTick(() => {
|
||||
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||
});
|
||||
},
|
||||
showConfirmDialog() {
|
||||
this.$refs.opinionForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.opinionDialogVisible = false;
|
||||
this.submitApproval();
|
||||
}
|
||||
});
|
||||
},
|
||||
submitApproval() {
|
||||
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||
const params = {
|
||||
businessKey: this.form.ticketBillCode,
|
||||
processKey: this.processKey,
|
||||
taskId: this.taskId,
|
||||
variables: {
|
||||
comment: this.opinionForm.approveOpinion,
|
||||
approveBtn: approveBtn
|
||||
}
|
||||
};
|
||||
|
||||
approveTask(params).then(() => {
|
||||
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||
this.detailDialogVisible = false;
|
||||
this.getList();
|
||||
});
|
||||
},
|
||||
getStatusText(status) {
|
||||
if (!status) {
|
||||
return '提交审批'
|
||||
}
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `红冲发票-${this.form.ticketBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="付款编号" prop="paymentNo">
|
||||
<el-input v-model="queryParams.paymentNo" placeholder="请输入付款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="制造商" prop="manufacturer">
|
||||
<el-input v-model="queryParams.manufacturer" placeholder="请输入制造商" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="paymentList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="付款编号" align="center" prop="paymentBillCode" />
|
||||
<el-table-column label="制造商" align="center" prop="vendorName" />
|
||||
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog title="付款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="付款单详情">
|
||||
<payment-detail :data="form"></payment-detail>
|
||||
<template #footer>
|
||||
<span> {{ form.paymentBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b><el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listPaymentApproved, getPayment } from "@/api/finance/payment";
|
||||
import { listCompletedFlows } from "@/api/flow";
|
||||
import PaymentDetail from "../components/PaymentDetail"; // Relative path adjustment
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "PaymentApproved",
|
||||
components: { PaymentDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
paymentList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
paymentNo: null,
|
||||
manufacturer: null,
|
||||
projectName: null,
|
||||
processKey: 'finance_payment',
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
currentPaymentId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listPaymentApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.paymentList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
handleView(row) {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.currentPaymentId = row.id;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getPayment(this.currentPaymentId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.paymentBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
getStatusText(status) {
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
<template>
|
||||
<div class="payment-detail">
|
||||
<div style="text-align: center;font-weight:bold;font-size: 25px;">付款申请单</div>
|
||||
<el-descriptions title="付款单信息" :column="3" border>
|
||||
<el-descriptions-item label="采购-付款单编号">{{ data.paymentBillCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="制造商名称">{{ data.vendorName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="付款条件">
|
||||
{{data.payType==='0'?'入库付款':'出库付款'}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="付款周期">
|
||||
{{data.payConfigDay}}天
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="含税总价(元)">{{ formatCurrency(data.totalPriceWithTax) }}</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="未税总价(元)">{{ data.totalPriceWithoutTax }}</el-descriptions-item>-->
|
||||
<!-- <el-descriptions-item label="税额(元)">{{ data.taxAmount }}</el-descriptions-item>-->
|
||||
<el-descriptions-item label="支付方式">
|
||||
<dict-tag :options="dict.type.payment_method" :value="data.paymentMethod"/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="银行账号">{{ data.payBankNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item label="账户名称">{{ data.payName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="银行开户行">{{ data.payBankOpenAddress }}</el-descriptions-item>
|
||||
<el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item span="2"></el-descriptions-item>
|
||||
<el-descriptions-item label="其它特别说明" :span="3">{{ data.remark }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" >
|
||||
<div class="el-descriptions__title">应付单信息</div>
|
||||
<el-table :data="data.payableDetails" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="payableBillCode" label="采购-应付单编号" align="center"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||
<el-table-column prop="productType" label="产品类型" align="center">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center"></el-table-column>
|
||||
<el-table-column prop="paymentAmount" label="本次付款金额" align="center"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
|
||||
<div class="el-descriptions__title">附件信息</div>
|
||||
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||
<el-table-column prop="createUserName" label="上传人" align="center"></el-table-column>
|
||||
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||
<img :src="currentImageUrl" style="width: 100%;" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import request from '@/utils/request';
|
||||
import {formatCurrency} from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "PaymentDetail",
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
dicts: ['payment_bill_type', 'product_type','payment_method'],
|
||||
data() {
|
||||
return {
|
||||
attachments: [],
|
||||
pdfPreviewVisible: false,
|
||||
currentPdfUrl: '',
|
||||
imagePreviewVisible: false,
|
||||
currentImageUrl: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
formatCurrency,
|
||||
|
||||
isPdf(filePath) {
|
||||
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
getImageUrl(resource) {
|
||||
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||
},
|
||||
handlePreview(row) {
|
||||
if (this.isPdf(row.filePath)) {
|
||||
// PDF Preview logic
|
||||
request({
|
||||
url: '/common/download/resource',
|
||||
method: 'get',
|
||||
params: { resource: row.filePath },
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||
this.pdfPreviewVisible = true;
|
||||
});
|
||||
} else {
|
||||
// Image Preview
|
||||
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||
this.imagePreviewVisible = true;
|
||||
}
|
||||
},
|
||||
handleDownload(row) {
|
||||
const link = document.createElement('a');
|
||||
link.href = this.getImageUrl(row.filePath);
|
||||
link.download = row.fileName || 'attachment';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.payment-detail {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,312 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="付款编号" prop="paymentNo">
|
||||
<el-input v-model="queryParams.paymentNo" placeholder="请输入付款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="制造商" prop="manufacturer">
|
||||
<el-input v-model="queryParams.manufacturer" placeholder="请输入制造商" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
size="mini"
|
||||
@click="toApproved()"
|
||||
>审批历史</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="paymentList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="付款编号" align="center" prop="paymentBillCode" />
|
||||
<el-table-column label="制造商" align="center" prop="vendorName" />
|
||||
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
|
||||
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 审批详情主对话框 -->
|
||||
<el-dialog title="付款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="付款单详情">
|
||||
<payment-detail :data="form"></payment-detail>
|
||||
<template #footer>
|
||||
<span>付款编号: {{ form.paymentBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b>
|
||||
|
||||
<el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批意见对话框 -->
|
||||
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||
<el-form-item label="审批意见" prop="approveOpinion">
|
||||
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listPaymentApprove, getPayment } from "@/api/finance/payment";
|
||||
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||
import PaymentDetail from "./components/PaymentDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "PaymentApprove",
|
||||
components: { PaymentDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
paymentList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
paymentNo: null,
|
||||
manufacturer: null,
|
||||
projectName: null,
|
||||
processKey: 'finance_payment',
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
opinionDialogVisible: false,
|
||||
confirmDialogTitle: '',
|
||||
currentApproveType: '',
|
||||
opinionForm: {
|
||||
approveOpinion: ''
|
||||
},
|
||||
opinionRules: {
|
||||
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
},
|
||||
processKey: 'finance_payment',
|
||||
taskId: null,
|
||||
currentPaymentId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listPaymentApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.paymentList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
toApproved() {
|
||||
this.$router.push( '/approve/paymentLog' )
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.resetDetailForm();
|
||||
this.currentPaymentId = row.id;
|
||||
this.taskId = row.taskId;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getPayment(this.currentPaymentId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.paymentBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
resetDetailForm() {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.opinionForm.approveOpinion = '';
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
// Assuming processKeyList might be generic or specific, using generic fetch for now
|
||||
// Usually need to know the specific process key. For payment, it might be 'payment_approval' etc.
|
||||
// However, listCompletedFlows logic in reference used specific keys.
|
||||
// If I don't know the key, maybe I can omit it or guess.
|
||||
// The reference used `processKeyList: ['purchase_order_online']`.
|
||||
// I will use a generic one or try to find it.
|
||||
// If row.processKey is available I can use that.
|
||||
let keys = [];
|
||||
console.log(this.processKey)
|
||||
if(this.processKey) keys.push(this.processKey);
|
||||
|
||||
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
openOpinionDialog(type) {
|
||||
this.currentApproveType = type;
|
||||
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||
this.opinionDialogVisible = true;
|
||||
this.opinionForm.approveOpinion = '';
|
||||
this.$nextTick(() => {
|
||||
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||
});
|
||||
},
|
||||
showConfirmDialog() {
|
||||
this.$refs.opinionForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.opinionDialogVisible = false;
|
||||
this.submitApproval();
|
||||
}
|
||||
});
|
||||
},
|
||||
submitApproval() {
|
||||
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||
const params = {
|
||||
businessKey: this.form.paymentBillCode,
|
||||
processKey: this.processKey,
|
||||
taskId: this.taskId,
|
||||
variables: {
|
||||
comment: this.opinionForm.approveOpinion,
|
||||
approveBtn: approveBtn
|
||||
}
|
||||
};
|
||||
|
||||
approveTask(params).then(() => {
|
||||
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||
this.detailDialogVisible = false;
|
||||
this.getList();
|
||||
});
|
||||
},
|
||||
getStatusText(status) {
|
||||
if (!status) {
|
||||
return '提交审批'
|
||||
}
|
||||
// Map status codes to text
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="付款编号" prop="paymentNo">
|
||||
<el-input v-model="queryParams.paymentNo" placeholder="请输入付款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="制造商" prop="manufacturer">
|
||||
<el-input v-model="queryParams.manufacturer" placeholder="请输入制造商" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="paymentList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="付款编号" align="center" prop="paymentBillCode" />
|
||||
<el-table-column label="制造商" align="center" prop="vendorName" />
|
||||
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" >
|
||||
<template slot-scope="scope">
|
||||
<span style="color: red">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog title="付款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="付款单详情">
|
||||
<payment-detail :data="form"></payment-detail>
|
||||
<template #footer>
|
||||
<span> {{ form.paymentBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listPaymentApproved, getPayment } from "@/api/finance/payment";
|
||||
import { listCompletedFlows } from "@/api/flow";
|
||||
import PaymentDetail from "../components/PaymentRefundDetail.vue"; // Relative path adjustment
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "PaymentApproved",
|
||||
components: { PaymentDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
paymentList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
paymentNo: null,
|
||||
manufacturer: null,
|
||||
projectName: null,
|
||||
processKey: 'finance_refund',
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
currentPaymentId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listPaymentApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.paymentList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
handleView(row) {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.currentPaymentId = row.id;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getPayment(this.currentPaymentId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.paymentBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
getStatusText(status) {
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<div class="payment-refund-detail">
|
||||
<div style="text-align: center;font-weight:bold;font-size: 25px;">退款申请单</div>
|
||||
<el-descriptions title="付款单信息" :column="3" border>
|
||||
<el-descriptions-item label="采购付款单编号">{{ data.paymentBillCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="制造商名称">{{ data.vendorName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="付款条件">
|
||||
{{data.payType==='0'?'入库付款':'出库付款'}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="付款周期">
|
||||
{{data.payConfigDay}}天
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="含税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithTax) }}</span></el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="未税总价(元)"><span style="color: red">{{ data.totalPriceWithoutTax }}</span></el-descriptions-item>-->
|
||||
<!-- <el-descriptions-item label="税额(元)"><span style="color: red">{{ data.taxAmount }}</span></el-descriptions-item>-->
|
||||
<el-descriptions-item label="支付方式">
|
||||
<dict-tag :options="dict.type.payment_method" :value="data.paymentMethod"/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="银行账号">{{ data.payBankNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item label="账户名称">{{ data.payName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="银行开户行">{{ data.payBankOpenAddress }}</el-descriptions-item>
|
||||
<el-descriptions-item label="银行行号" span="1">{{ data.bankNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item span="2"></el-descriptions-item>
|
||||
<el-descriptions-item label="其它特别说明" span="3">{{ data.remark }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" >
|
||||
<div class="el-descriptions__title">应付单信息</div>
|
||||
<el-table :data="data.payableDetails" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="payableBillCode" label="采购应付单编号" align="center"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||
<el-table-column prop="productType" label="产品类型" align="center">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- Note: Product Type is requested but not present in reference DetailDrawer.vue. Omitting for safety or need to ask. -->
|
||||
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center"></el-table-column>
|
||||
<el-table-column prop="paymentAmount" label="本次付款金额" align="center"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {formatCurrency} from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "PaymentRefundDetail",
|
||||
methods: {formatCurrency},
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
dicts: ['payment_bill_type', 'payment_method','product_type']
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.payment-refund-detail {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,313 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="付款编号" prop="paymentNo">
|
||||
<el-input v-model="queryParams.paymentNo" placeholder="请输入付款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="制造商" prop="manufacturer">
|
||||
<el-input v-model="queryParams.manufacturer" placeholder="请输入制造商" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
size="mini"
|
||||
@click="toApproved()"
|
||||
>审批历史</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="paymentList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="付款编号" align="center" prop="paymentBillCode" />
|
||||
<el-table-column label="制造商" align="center" prop="vendorName" />
|
||||
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" >
|
||||
<template slot-scope="scope">
|
||||
<span style="color: red">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 审批详情主对话框 -->
|
||||
<el-dialog title="付款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="付款单详情">
|
||||
<payment-detail :data="form"></payment-detail>
|
||||
<template #footer>
|
||||
<span>付款编号: {{ form.paymentBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批意见对话框 -->
|
||||
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||
<el-form-item label="审批意见" prop="approveOpinion">
|
||||
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listPaymentApprove, getPayment } from "@/api/finance/payment";
|
||||
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||
import PaymentDetail from "./components/PaymentRefundDetail.vue";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "PaymentApprove",
|
||||
components: { PaymentDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
paymentList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
paymentNo: null,
|
||||
manufacturer: null,
|
||||
projectName: null,
|
||||
processKey: 'finance_refund',
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
opinionDialogVisible: false,
|
||||
confirmDialogTitle: '',
|
||||
currentApproveType: '',
|
||||
opinionForm: {
|
||||
approveOpinion: ''
|
||||
},
|
||||
opinionRules: {
|
||||
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
},
|
||||
processKey: 'finance_refund',
|
||||
taskId: null,
|
||||
currentPaymentId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listPaymentApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.paymentList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
toApproved() {
|
||||
this.$router.push( '/approve/paymentRedLog' )
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.resetDetailForm();
|
||||
this.currentPaymentId = row.id;
|
||||
this.taskId = row.taskId;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getPayment(this.currentPaymentId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.paymentBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
resetDetailForm() {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.opinionForm.approveOpinion = '';
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
// Assuming processKeyList might be generic or specific, using generic fetch for now
|
||||
// Usually need to know the specific process key. For payment, it might be 'payment_approval' etc.
|
||||
// However, listCompletedFlows logic in reference used specific keys.
|
||||
// If I don't know the key, maybe I can omit it or guess.
|
||||
// The reference used `processKeyList: ['purchase_order_online']`.
|
||||
// I will use a generic one or try to find it.
|
||||
// If row.processKey is available I can use that.
|
||||
let keys = [];
|
||||
console.log(this.processKey)
|
||||
if(this.processKey) keys.push(this.processKey);
|
||||
|
||||
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
openOpinionDialog(type) {
|
||||
this.currentApproveType = type;
|
||||
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||
this.opinionDialogVisible = true;
|
||||
this.opinionForm.approveOpinion = '';
|
||||
this.$nextTick(() => {
|
||||
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||
});
|
||||
},
|
||||
showConfirmDialog() {
|
||||
this.$refs.opinionForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.opinionDialogVisible = false;
|
||||
this.submitApproval();
|
||||
}
|
||||
});
|
||||
},
|
||||
submitApproval() {
|
||||
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||
const params = {
|
||||
businessKey: this.form.paymentBillCode,
|
||||
processKey: this.processKey,
|
||||
taskId: this.taskId,
|
||||
variables: {
|
||||
comment: this.opinionForm.approveOpinion,
|
||||
approveBtn: approveBtn
|
||||
}
|
||||
};
|
||||
|
||||
approveTask(params).then(() => {
|
||||
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||
this.detailDialogVisible = false;
|
||||
this.getList();
|
||||
});
|
||||
},
|
||||
getStatusText(status) {
|
||||
if (!status) {
|
||||
return '提交审批'
|
||||
}
|
||||
// Map status codes to text
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
import html2canvas from 'html2canvas';
|
||||
import jsPDF from 'jspdf';
|
||||
|
||||
/**
|
||||
* 导出指定DOM元素为PDF
|
||||
* @param {HTMLElement} element - 要导出的DOM元素
|
||||
* @param {string} fileName - 导出的文件名
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function exportElementToPDF(element, fileName) {
|
||||
const disabledElements = [];
|
||||
try {
|
||||
// 移除所有输入框的 disabled 属性,以便在PDF中正确显示
|
||||
element.querySelectorAll('input:disabled, textarea:disabled').forEach(el => {
|
||||
disabledElements.push(el);
|
||||
el.disabled = false;
|
||||
});
|
||||
|
||||
// 使用html2canvas捕获内容
|
||||
const canvas = await html2canvas(element, {
|
||||
scale: 2, // 提高清晰度
|
||||
useCORS: true, // 允许跨域图片
|
||||
logging: false, // 关闭日志
|
||||
backgroundColor: '#F8F5F0' // 设置背景色
|
||||
});
|
||||
|
||||
// 计算PDF页面尺寸
|
||||
const imgWidth = 210; // A4纸宽度(mm)
|
||||
const pageHeight = 297; // A4纸高度(mm)
|
||||
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
||||
let heightLeft = imgHeight;
|
||||
|
||||
// 创建PDF
|
||||
const pdf = new jsPDF('p', 'mm', 'a4');
|
||||
let position = 0;
|
||||
|
||||
// 将canvas转换为图片
|
||||
const imgData = canvas.toDataURL('image/jpeg');
|
||||
|
||||
// 添加第一页
|
||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
|
||||
heightLeft -= pageHeight;
|
||||
|
||||
// 如果内容超过一页,添加更多页
|
||||
while (heightLeft > 0) {
|
||||
position = heightLeft - imgHeight;
|
||||
pdf.addPage();
|
||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
|
||||
heightLeft -= pageHeight;
|
||||
}
|
||||
|
||||
// 保存PDF
|
||||
pdf.save(fileName);
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
// 恢复之前移除的 disabled 属性
|
||||
disabledElements.forEach(el => {
|
||||
el.disabled = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="收款编号" prop="receiptBillCode">
|
||||
<el-input v-model="queryParams.receiptBillCode" placeholder="请输入收款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="进货商" prop="partnerName">
|
||||
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="receiptList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="收款编号" align="center" prop="receiptBillCode" />
|
||||
<el-table-column label="客户" align="center" prop="partnerName" />
|
||||
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog title="收款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="收款单详情">
|
||||
<receipt-detail :data="form"></receipt-detail>
|
||||
<template #footer>
|
||||
<span>{{ form.receiptBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listReceiptApproved, getReceiptDetail } from "@/api/finance/receipt";
|
||||
import { listCompletedFlows } from "@/api/flow";
|
||||
import ReceiptDetail from "../components/ReceiptDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "ReceiptApproved",
|
||||
components: { ReceiptDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
receiptList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
receiptBillCode: null,
|
||||
partnerName: null,
|
||||
projectName: null,
|
||||
processKey: 'finance_receipt_approve',
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
currentReceiptId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listReceiptApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.receiptList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
handleView(row) {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.currentReceiptId = row.id;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getReceiptDetail(this.currentReceiptId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.receiptBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
getStatusText(status) {
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `收款单-${this.form.receiptBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
<template>
|
||||
<div class="receipt-detail">
|
||||
<div style="text-align: center;font-weight:bold;font-size: 25px;">收款申请单</div>
|
||||
<el-descriptions title="收款单信息" :column="3" border>
|
||||
<el-descriptions-item label="销售-收款单编号">{{ data.receiptBillCode }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="2" label="进货商名称">{{ data.partnerName }}</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="收款单类型">-->
|
||||
<!-- <dict-tag :options="dict.type.receipt_bill_type" :value="data.receiptBillType"/>-->
|
||||
<!-- </el-descriptions-item>-->
|
||||
<el-descriptions-item label="含税总价(元)">{{ formatCurrency(data.totalPriceWithTax) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="未税总价(元)">{{ formatCurrency(data.totalPriceWithoutTax) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="税额(元)">{{ formatCurrency(data.taxAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="支付方式">
|
||||
<dict-tag :options="dict.type.payment_method" :value="data.receiptMethod"/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="银行账号">{{ data.receiptBankNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item label="账户名称">{{ data.receiptAccountName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="银行开户行">{{ data.receiptBankOpenAddress }}</el-descriptions-item>
|
||||
<el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="备注" :span="3">{{ data.remark }}</el-descriptions-item>-->
|
||||
</el-descriptions>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-show="data.detailDTOList && data.detailDTOList.length>0">
|
||||
<div class="el-descriptions__title">应收单列表</div>
|
||||
<el-table :data="data.detailDTOList" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||
<el-table-column prop="productType" label="产品类型" align="center">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column prop="receiptAmount" label="本次收款金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
|
||||
<div class="el-descriptions__title">附件信息</div>
|
||||
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||
<el-table-column prop="createByName" label="上传人" align="center"></el-table-column>
|
||||
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||
<img :src="currentImageUrl" style="width: 100%;" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import request from '@/utils/request';
|
||||
import {getInvoiceAttachments} from "@/api/finance/invoice";
|
||||
import {getReceiptAttachments} from "@/api/finance/receipt";
|
||||
|
||||
export default {
|
||||
name: "ReceiptDetail",
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
dicts: ['receipt_bill_type', 'product_type','payment_method'],
|
||||
data() {
|
||||
return {
|
||||
attachments: [],
|
||||
pdfPreviewVisible: false,
|
||||
currentPdfUrl: '',
|
||||
imagePreviewVisible: false,
|
||||
currentImageUrl: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'data.id': {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
this.fetchAttachments(val);
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchAttachments(id) {
|
||||
getReceiptAttachments(id,{type: "receipt"}).then(response => {
|
||||
this.attachments = (response.data || []).filter(item => item.delFlag !== '2');
|
||||
});
|
||||
},
|
||||
isPdf(filePath) {
|
||||
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
getImageUrl(resource) {
|
||||
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||
},
|
||||
handlePreview(row) {
|
||||
if (this.isPdf(row.filePath)) {
|
||||
// PDF Preview logic
|
||||
request({
|
||||
url: '/common/download/resource',
|
||||
method: 'get',
|
||||
params: { resource: row.filePath },
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||
this.pdfPreviewVisible = true;
|
||||
});
|
||||
} else {
|
||||
// Image Preview
|
||||
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||
this.imagePreviewVisible = true;
|
||||
}
|
||||
},
|
||||
handleDownload(row) {
|
||||
const link = document.createElement('a');
|
||||
link.href = this.getImageUrl(row.filePath);
|
||||
link.download = row.fileName || 'attachment';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.receipt-detail {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,300 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="收款编号" prop="receiptBillCode">
|
||||
<el-input v-model="queryParams.receiptBillCode" placeholder="请输入收款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="进货商" prop="partnerName">
|
||||
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
size="mini"
|
||||
@click="toApproved()"
|
||||
>审批历史</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="receiptList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="收款编号" align="center" prop="receiptBillCode" />
|
||||
<el-table-column label="进货商" align="center" prop="partnerName" />
|
||||
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 审批详情主对话框 -->
|
||||
<el-dialog title="收款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="收款单详情">
|
||||
<receipt-detail :data="form"></receipt-detail>
|
||||
<template #footer>
|
||||
<span>{{ form.receiptBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批意见对话框 -->
|
||||
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||
<el-form-item label="审批意见" prop="approveOpinion">
|
||||
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listReceiptApprove, getReceiptDetail } from "@/api/finance/receipt";
|
||||
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||
import ReceiptDetail from "./components/ReceiptDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "ReceiptApprove",
|
||||
components: { ReceiptDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
receiptList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
receiptBillCode: null,
|
||||
partnerName: null,
|
||||
projectName: null,
|
||||
processKey: 'finance_receipt_approve',
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
opinionDialogVisible: false,
|
||||
confirmDialogTitle: '',
|
||||
currentApproveType: '',
|
||||
opinionForm: {
|
||||
approveOpinion: ''
|
||||
},
|
||||
opinionRules: {
|
||||
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
},
|
||||
processKey: 'finance_receipt_approve',
|
||||
taskId: null,
|
||||
currentReceiptId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listReceiptApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.receiptList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
toApproved() {
|
||||
this.$router.push( '/approve/receiptLog' )
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.resetDetailForm();
|
||||
this.currentReceiptId = row.id;
|
||||
this.taskId = row.taskId;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getReceiptDetail(this.currentReceiptId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.receiptBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
resetDetailForm() {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.opinionForm.approveOpinion = '';
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
let keys = [];
|
||||
if(this.processKey) keys.push(this.processKey);
|
||||
|
||||
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
openOpinionDialog(type) {
|
||||
this.currentApproveType = type;
|
||||
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||
this.opinionDialogVisible = true;
|
||||
this.opinionForm.approveOpinion = '';
|
||||
this.$nextTick(() => {
|
||||
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||
});
|
||||
},
|
||||
showConfirmDialog() {
|
||||
this.$refs.opinionForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.opinionDialogVisible = false;
|
||||
this.submitApproval();
|
||||
}
|
||||
});
|
||||
},
|
||||
submitApproval() {
|
||||
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||
const params = {
|
||||
businessKey: this.form.receiptBillCode,
|
||||
processKey: this.processKey,
|
||||
taskId: this.taskId,
|
||||
variables: {
|
||||
comment: this.opinionForm.approveOpinion,
|
||||
approveBtn: approveBtn
|
||||
}
|
||||
};
|
||||
|
||||
approveTask(params).then(() => {
|
||||
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||
this.detailDialogVisible = false;
|
||||
this.getList();
|
||||
});
|
||||
},
|
||||
getStatusText(status) {
|
||||
if (!status) {
|
||||
return '提交审批'
|
||||
}
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `收款单-${this.form.receiptBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="收款编号" prop="receiptBillCode">
|
||||
<el-input v-model="queryParams.receiptBillCode" placeholder="请输入收款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="进货商" prop="partnerName">
|
||||
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="receiptList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="收款编号" align="center" prop="receiptBillCode" />
|
||||
<el-table-column label="客户" align="center" prop="partnerName" />
|
||||
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
|
||||
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog title="收款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="收款单详情">
|
||||
<receipt-detail :data="form"></receipt-detail>
|
||||
<template #footer>
|
||||
<span> {{ form.receiptBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listReceiptApproved, getReceiptDetail } from "@/api/finance/receipt";
|
||||
import { listCompletedFlows } from "@/api/flow";
|
||||
import ReceiptDetail from "../components/ReceiptDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "ReceiptRefoundApproved",
|
||||
components: { ReceiptDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
receiptList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
receiptBillCode: null,
|
||||
partnerName: null,
|
||||
projectName: null,
|
||||
processKey: 'finance_receipt_refound',
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
currentReceiptId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listReceiptApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.receiptList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
handleView(row) {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.currentReceiptId = row.id;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getReceiptDetail(this.currentReceiptId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.receiptBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
getStatusText(status) {
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `收款单-${this.form.receiptBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
<template>
|
||||
<div class="receipt-detail">
|
||||
<div style="text-align: center;font-weight:bold;font-size: 25px;">退款申请单</div>
|
||||
<el-descriptions title="收款单信息" :column="3" border>
|
||||
<el-descriptions-item label="销售-收款单编号">{{ data.receiptBillCode }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="2" label="进货商名称">{{ data.partnerName }}</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="收款单类型">-->
|
||||
<!-- <dict-tag :options="dict.type.receipt_bill_type" :value="data.receiptBillType"/>-->
|
||||
<!-- </el-descriptions-item>-->
|
||||
<el-descriptions-item label="含税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithTax) }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="未税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithoutTax) }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="税额(元)"><span style="color: red">{{ formatCurrency(data.taxAmount) }}</span></el-descriptions-item>
|
||||
<el-descriptions-item label="支付方式">
|
||||
<dict-tag :options="dict.type.payment_method" :value="data.receiptMethod"/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="银行账号">{{ data.receiptBankNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item label="账户名称">{{ data.receiptAccountName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="银行开户行">{{ data.receiptBankOpenAddress }}</el-descriptions-item>
|
||||
<el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="备注" :span="3">{{ data.remark }}</el-descriptions-item>-->
|
||||
</el-descriptions>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-show="data.detailDTOList && data.detailDTOList.length>0">
|
||||
<div class="el-descriptions__title">应收单列表</div>
|
||||
<el-table :data="data.detailDTOList" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||
<el-table-column prop="productType" label="产品类型" align="center">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center"></el-table-column>
|
||||
<el-table-column prop="receiptAmount" label="本次收款金额" align="center"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
|
||||
<div class="el-descriptions__title">附件信息</div>
|
||||
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||
<el-table-column prop="createByName" label="上传人" align="center"></el-table-column>
|
||||
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||
<img :src="currentImageUrl" style="width: 100%;" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import request from '@/utils/request';
|
||||
|
||||
export default {
|
||||
name: "ReceiptRefoundDetail",
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
dicts: ['receipt_bill_type', 'product_type','payment_method'],
|
||||
data() {
|
||||
return {
|
||||
attachments: [],
|
||||
pdfPreviewVisible: false,
|
||||
currentPdfUrl: '',
|
||||
imagePreviewVisible: false,
|
||||
currentImageUrl: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
isPdf(filePath) {
|
||||
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
getImageUrl(resource) {
|
||||
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||
},
|
||||
handlePreview(row) {
|
||||
if (this.isPdf(row.filePath)) {
|
||||
// PDF Preview logic
|
||||
request({
|
||||
url: '/common/download/resource',
|
||||
method: 'get',
|
||||
params: { resource: row.filePath },
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||
this.pdfPreviewVisible = true;
|
||||
});
|
||||
} else {
|
||||
// Image Preview
|
||||
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||
this.imagePreviewVisible = true;
|
||||
}
|
||||
},
|
||||
handleDownload(row) {
|
||||
const link = document.createElement('a');
|
||||
link.href = this.getImageUrl(row.filePath);
|
||||
link.download = row.fileName || 'attachment';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.receipt-detail {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="收款编号" prop="receiptBillCode">
|
||||
<el-input v-model="queryParams.receiptBillCode" placeholder="请输入收款编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="进货商" prop="partnerName">
|
||||
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
size="mini"
|
||||
@click="toApproved()"
|
||||
>审批历史</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="receiptList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="收款编号" align="center" prop="receiptBillCode" />
|
||||
<el-table-column label="进货商" align="center" prop="partnerName" />
|
||||
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" >
|
||||
<template slot-scope="scope">
|
||||
<span style="color: red">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 审批详情主对话框 -->
|
||||
<el-dialog title="收款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="收款单详情">
|
||||
<receipt-detail :data="form"></receipt-detail>
|
||||
<template #footer>
|
||||
<span>{{ form.receiptBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b><el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批意见对话框 -->
|
||||
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||
<el-form-item label="审批意见" prop="approveOpinion">
|
||||
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listReceiptApprove, getReceiptDetail } from "@/api/finance/receipt";
|
||||
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||
import ReceiptDetail from "./components/ReceiptDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "ReceiptRefoundApprove",
|
||||
components: { ReceiptDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
receiptList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
receiptBillCode: null,
|
||||
partnerName: null,
|
||||
projectName: null,
|
||||
processKey: 'finance_receipt_refound',
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
opinionDialogVisible: false,
|
||||
confirmDialogTitle: '',
|
||||
currentApproveType: '',
|
||||
opinionForm: {
|
||||
approveOpinion: ''
|
||||
},
|
||||
opinionRules: {
|
||||
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
},
|
||||
processKey: 'finance_receipt_refound',
|
||||
taskId: null,
|
||||
currentReceiptId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listReceiptApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.receiptList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
toApproved() {
|
||||
this.$router.push( '/approve/receiptRefoundLog' )
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.resetDetailForm();
|
||||
this.currentReceiptId = row.id;
|
||||
this.taskId = row.taskId;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getReceiptDetail(this.currentReceiptId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.receiptBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
resetDetailForm() {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.opinionForm.approveOpinion = '';
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
let keys = [];
|
||||
if(this.processKey) keys.push(this.processKey);
|
||||
|
||||
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
openOpinionDialog(type) {
|
||||
this.currentApproveType = type;
|
||||
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||
this.opinionDialogVisible = true;
|
||||
this.opinionForm.approveOpinion = '';
|
||||
this.$nextTick(() => {
|
||||
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||
});
|
||||
},
|
||||
showConfirmDialog() {
|
||||
this.$refs.opinionForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.opinionDialogVisible = false;
|
||||
this.submitApproval();
|
||||
}
|
||||
});
|
||||
},
|
||||
submitApproval() {
|
||||
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||
const params = {
|
||||
businessKey: this.form.receiptBillCode,
|
||||
processKey: this.processKey,
|
||||
taskId: this.taskId,
|
||||
variables: {
|
||||
comment: this.opinionForm.approveOpinion,
|
||||
approveBtn: approveBtn
|
||||
}
|
||||
};
|
||||
|
||||
approveTask(params).then(() => {
|
||||
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||
this.detailDialogVisible = false;
|
||||
this.getList();
|
||||
});
|
||||
},
|
||||
getStatusText(status) {
|
||||
if (!status) {
|
||||
return '提交审批'
|
||||
}
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `收款单-${this.form.receiptBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="开票编号" prop="invoiceBillCode">
|
||||
<el-input v-model="queryParams.invoiceBillCode" placeholder="请输入开票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户" prop="partnerName">
|
||||
<el-input v-model="queryParams.partnerName" placeholder="请输入客户" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="invoiceList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="开票编号" align="center" prop="invoiceBillCode" />
|
||||
<el-table-column label="客户" align="center" prop="partnerName" />
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog title="开票单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="开票单详情">
|
||||
<receivable-invoice-detail :data="form"></receivable-invoice-detail>
|
||||
<template #footer>
|
||||
<span>{{ form.invoiceBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listInvoiceApproved, getInvoiceDetail } from "@/api/finance/invoice";
|
||||
import { listCompletedFlows } from "@/api/flow";
|
||||
import ReceivableInvoiceDetail from "../components/ReceivableInvoiceDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "ReceivableInvoiceApproved",
|
||||
components: { ReceivableInvoiceDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
invoiceList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
invoiceBillCode: null,
|
||||
partnerName: null,
|
||||
projectName: null,
|
||||
processKey: 'finance_invoice_approve',
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
currentInvoiceId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listInvoiceApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.invoiceList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
handleView(row) {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.currentInvoiceId = row.id;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getInvoiceDetail(this.currentInvoiceId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.invoiceBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
getStatusText(status) {
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `开票单-${this.form.invoiceBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
<template>
|
||||
<div class="invoice-detail">
|
||||
<div style="text-align: center;font-weight:bold;font-size: 25px;">开票申请单</div>
|
||||
<el-descriptions title="开票单信息" :column="3" border>
|
||||
<el-descriptions-item label="销售-开票单编号">{{ data.invoiceBillCode }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="2" label="进货商">{{ data.partnerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="含税总价(元)">{{ formatCurrency(data.totalPriceWithTax) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="未税总价(元)">{{ formatCurrency(data.totalPriceWithoutTax) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="税额(元)">{{ formatCurrency(data.taxAmount) }}</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="银行账号">{{ data.buyerBankAccount }}</el-descriptions-item>-->
|
||||
<!-- <el-descriptions-item label="账户名称">{{ data.buyerName }}</el-descriptions-item>-->
|
||||
<!-- <el-descriptions-item label="银行开户行">{{ data.buyerBank }}</el-descriptions-item>-->
|
||||
<!-- <el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item> -->
|
||||
</el-descriptions>
|
||||
|
||||
<div class="section" style="margin-top: 20px;">
|
||||
<div class="el-descriptions__title">发票信息</div>
|
||||
<invoice-info-view :data="data" />
|
||||
</div>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-show="data.detailDTOList && data.detailDTOList.length>0">
|
||||
<div class="el-descriptions__title">应收单列表</div>
|
||||
<el-table :data="data.detailDTOList" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||
<el-table-column prop="productType" label="产品类型" align="center">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column prop="receiptAmount" label="本次开票金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-if="excelList && excelList.length > 0">
|
||||
<div class="el-descriptions__title">附件信息</div>
|
||||
<el-table :data="excelList" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||
<el-table-column prop="createUserName" label="上传信息" align="center"></el-table-column>
|
||||
<el-table-column prop="createTime" label="生成时间" align="center"></el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- <div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">-->
|
||||
<!-- <div class="el-descriptions__title">附件信息</div>-->
|
||||
<!-- <el-table :data="attachments" border style="width: 100%; margin-top: 10px;">-->
|
||||
<!-- <el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>-->
|
||||
<!-- <el-table-column prop="createUserName" label="上传人" align="center"></el-table-column>-->
|
||||
<!-- <el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>-->
|
||||
<!-- <el-table-column label="操作" align="center">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>-->
|
||||
<!-- <el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- <el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">-->
|
||||
<!-- <iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>-->
|
||||
<!-- </el-dialog>-->
|
||||
|
||||
<!-- <el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">-->
|
||||
<!-- <img :src="currentImageUrl" style="width: 100%;" />-->
|
||||
<!-- </el-dialog>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getInvoiceAttachments } from "@/api/finance/invoice";
|
||||
import request from '@/utils/request';
|
||||
import InvoiceInfoView from '@/views/finance/invoice/components/InvoiceInfoView';
|
||||
|
||||
export default {
|
||||
name: "ReceivableInvoiceDetail",
|
||||
components: { InvoiceInfoView },
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
dicts: ['product_type'],
|
||||
data() {
|
||||
return {
|
||||
attachments: [],
|
||||
excelList: [],
|
||||
pdfPreviewVisible: false,
|
||||
currentPdfUrl: '',
|
||||
imagePreviewVisible: false,
|
||||
currentImageUrl: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
data: {
|
||||
handler(val) {
|
||||
if (val && val.updateTime) {
|
||||
this.excelList = [{
|
||||
fileName: '电子发票--购买方公司信息.xlsx',
|
||||
createUserName: '系统生成',
|
||||
createTime: val.updateTime,
|
||||
isSystemGenerated: true
|
||||
}];
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
isPdf(filePath) {
|
||||
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
getImageUrl(resource) {
|
||||
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||
},
|
||||
handlePreview(row) {
|
||||
if (this.isPdf(row.filePath)) {
|
||||
request({
|
||||
url: '/common/download/resource',
|
||||
method: 'get',
|
||||
params: { resource: row.filePath },
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||
this.pdfPreviewVisible = true;
|
||||
});
|
||||
} else {
|
||||
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||
this.imagePreviewVisible = true;
|
||||
}
|
||||
},
|
||||
handleDownload(row) {
|
||||
if (row.isSystemGenerated) {
|
||||
request({
|
||||
url: '/finance/invoice/export/' + this.data.invoiceBillCode,
|
||||
method: 'get'
|
||||
}).then(res => {
|
||||
window.location.href = process.env.VUE_APP_BASE_API + "/common/download?fileName=" + encodeURIComponent(res.msg) + "&delete=true";
|
||||
});
|
||||
return;
|
||||
}
|
||||
const link = document.createElement('a');
|
||||
link.href = this.getImageUrl(row.filePath);
|
||||
link.download = row.fileName || 'attachment';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.invoice-detail {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,298 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="开票编号" prop="invoiceBillCode">
|
||||
<el-input v-model="queryParams.invoiceBillCode" placeholder="请输入开票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="进货商" prop="partnerName">
|
||||
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
size="mini"
|
||||
@click="toApproved()"
|
||||
>审批历史</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="invoiceList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="开票编号" align="center" prop="invoiceBillCode" />
|
||||
<el-table-column label="进货商" align="center" prop="partnerName" />
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 审批详情主对话框 -->
|
||||
<el-dialog title="开票单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="开票单详情">
|
||||
<receivable-invoice-detail :data="form"></receivable-invoice-detail>
|
||||
<template #footer>
|
||||
<span>{{ form.invoiceBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批意见对话框 -->
|
||||
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||
<el-form-item label="审批意见" prop="approveOpinion">
|
||||
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listInvoiceApprove, getInvoiceDetail } from "@/api/finance/invoice";
|
||||
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||
import ReceivableInvoiceDetail from "./components/ReceivableInvoiceDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "ReceivableInvoiceApprove",
|
||||
components: { ReceivableInvoiceDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
invoiceList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
invoiceBillCode: null,
|
||||
partnerName: null,
|
||||
projectName: null,
|
||||
processKey: 'finance_invoice_approve',
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
opinionDialogVisible: false,
|
||||
confirmDialogTitle: '',
|
||||
currentApproveType: '',
|
||||
opinionForm: {
|
||||
approveOpinion: ''
|
||||
},
|
||||
opinionRules: {
|
||||
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
},
|
||||
processKey: 'finance_invoice_approve',
|
||||
taskId: null,
|
||||
currentInvoiceId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listInvoiceApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.invoiceList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
toApproved() {
|
||||
this.$router.push('/approve/receivableInvoiceLog')
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.resetDetailForm();
|
||||
this.currentInvoiceId = row.id;
|
||||
this.taskId = row.taskId;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getInvoiceDetail(this.currentInvoiceId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.invoiceBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
resetDetailForm() {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.opinionForm.approveOpinion = '';
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
let keys = [];
|
||||
if(this.processKey) keys.push(this.processKey);
|
||||
|
||||
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
openOpinionDialog(type) {
|
||||
this.currentApproveType = type;
|
||||
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||
this.opinionDialogVisible = true;
|
||||
this.opinionForm.approveOpinion = '';
|
||||
this.$nextTick(() => {
|
||||
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||
});
|
||||
},
|
||||
showConfirmDialog() {
|
||||
this.$refs.opinionForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.opinionDialogVisible = false;
|
||||
this.submitApproval();
|
||||
}
|
||||
});
|
||||
},
|
||||
submitApproval() {
|
||||
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||
const params = {
|
||||
businessKey: this.form.invoiceBillCode,
|
||||
processKey: this.processKey,
|
||||
taskId: this.taskId,
|
||||
variables: {
|
||||
comment: this.opinionForm.approveOpinion,
|
||||
approveBtn: approveBtn
|
||||
}
|
||||
};
|
||||
|
||||
approveTask(params).then(() => {
|
||||
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||
this.detailDialogVisible = false;
|
||||
this.getList();
|
||||
});
|
||||
},
|
||||
getStatusText(status) {
|
||||
if (!status) {
|
||||
return '提交审批'
|
||||
}
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `开票单-${this.form.invoiceBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="开票编号" prop="invoiceBillCode">
|
||||
<el-input v-model="queryParams.invoiceBillCode" placeholder="请输入开票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户" prop="partnerName">
|
||||
<el-input v-model="queryParams.partnerName" placeholder="请输入客户" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="invoiceList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="开票编号" align="center" prop="invoiceBillCode" />
|
||||
<el-table-column label="客户" align="center" prop="partnerName" />
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax">
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.totalPriceWithTax < 0 ? 'color: red' : ''">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog title="开票单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="开票单详情">
|
||||
<receivable-invoice-detail :data="form"></receivable-invoice-detail>
|
||||
<template #footer>
|
||||
<span>{{ form.invoiceBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listInvoiceApproved, getInvoiceDetail } from "@/api/finance/invoice";
|
||||
import { listCompletedFlows } from "@/api/flow";
|
||||
import ReceivableInvoiceDetail from "../components/ReceivableInvoiceDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "ReceivableInvoiceRefundApproved",
|
||||
components: { ReceivableInvoiceDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
invoiceList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
invoiceBillCode: null,
|
||||
partnerName: null,
|
||||
projectName: null,
|
||||
processKey: 'finance_invoice_refound',
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
currentInvoiceId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listInvoiceApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.invoiceList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
handleView(row) {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.currentInvoiceId = row.id;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getInvoiceDetail(this.currentInvoiceId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.invoiceBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
listCompletedFlows({ businessKey: businessKey }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
getStatusText(status) {
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `开票单-${this.form.invoiceBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
<template>
|
||||
<div class="invoice-detail">
|
||||
<div style="text-align: center;font-weight:bold;font-size: 25px;">红冲开票申请单</div>
|
||||
<el-descriptions title="开票单信息" :column="3" border>
|
||||
<el-descriptions-item label="销售-开票单编号">{{ data.invoiceBillCode }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="2" label="进货商">{{ data.partnerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="含税总价(元)">
|
||||
<span :style="data.totalPriceWithTax < 0 ? 'color: red' : ''">{{ formatCurrency(data.totalPriceWithTax) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="未税总价(元)">
|
||||
<span :style="data.totalPriceWithoutTax < 0 ? 'color: red' : ''">{{ formatCurrency(data.totalPriceWithoutTax) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="税额(元)">
|
||||
<span :style="data.taxAmount < 0 ? 'color: red' : ''">{{ formatCurrency(data.taxAmount) }}</span>
|
||||
</el-descriptions-item>
|
||||
<!-- <el-descriptions-item label="银行账号">{{ data.buyerBankAccount }}</el-descriptions-item>-->
|
||||
<!-- <el-descriptions-item label="账户名称">{{ data.buyerName }}</el-descriptions-item>-->
|
||||
<!-- <el-descriptions-item label="银行开户行">{{ data.buyerBank }}</el-descriptions-item>-->
|
||||
<!-- <el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item> -->
|
||||
</el-descriptions>
|
||||
|
||||
<div class="section" style="margin-top: 20px;">
|
||||
<div class="el-descriptions__title">发票信息</div>
|
||||
<invoice-info-view :data="data" />
|
||||
</div>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-show="data.detailDTOList && data.detailDTOList.length>0">
|
||||
<div class="el-descriptions__title">应收单列表</div>
|
||||
<el-table :data="data.detailDTOList" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
|
||||
<el-table-column prop="productType" label="产品类型" align="center">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.totalPriceWithTax < 0 ? 'color: red' : ''">{{ scope.row.totalPriceWithTax }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="receiptAmount" label="本次开票金额" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.receiptAmount < 0 ? 'color: red' : ''">{{ scope.row.receiptAmount }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-if="excelList && excelList.length > 0">
|
||||
<div class="el-descriptions__title">附件信息</div>
|
||||
<el-table :data="excelList" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||
<el-table-column prop="createUserName" label="上传信息" align="center"></el-table-column>
|
||||
<el-table-column prop="createTime" label="生成时间" align="center"></el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
|
||||
<div class="el-descriptions__title">附件信息</div>
|
||||
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
|
||||
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
|
||||
<el-table-column prop="createUserName" label="上传人" align="center"></el-table-column>
|
||||
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)">预览</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
|
||||
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
|
||||
<img :src="currentImageUrl" style="width: 100%;" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getInvoiceAttachments } from "@/api/finance/invoice";
|
||||
import request from '@/utils/request';
|
||||
import InvoiceInfoView from '@/views/finance/invoice/components/InvoiceInfoView';
|
||||
|
||||
export default {
|
||||
name: "ReceivableInvoiceDetail",
|
||||
components: { InvoiceInfoView },
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
dicts: ['product_type'],
|
||||
data() {
|
||||
return {
|
||||
attachments: [],
|
||||
excelList: [],
|
||||
pdfPreviewVisible: false,
|
||||
currentPdfUrl: '',
|
||||
imagePreviewVisible: false,
|
||||
currentImageUrl: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
data: {
|
||||
handler(val) {
|
||||
if (val && val.updateTime) {
|
||||
this.excelList = [{
|
||||
fileName: '电子发票--购买方公司信息.xlsx',
|
||||
createUserName: '系统生成',
|
||||
createTime: val.updateTime,
|
||||
isSystemGenerated: true
|
||||
}];
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
isPdf(filePath) {
|
||||
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
getImageUrl(resource) {
|
||||
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||
},
|
||||
handlePreview(row) {
|
||||
if (this.isPdf(row.filePath)) {
|
||||
request({
|
||||
url: '/common/download/resource',
|
||||
method: 'get',
|
||||
params: { resource: row.filePath },
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||
this.currentPdfUrl = URL.createObjectURL(blob);
|
||||
this.pdfPreviewVisible = true;
|
||||
});
|
||||
} else {
|
||||
this.currentImageUrl = this.getImageUrl(row.filePath);
|
||||
this.imagePreviewVisible = true;
|
||||
}
|
||||
},
|
||||
handleDownload(row) {
|
||||
if (row.isSystemGenerated) {
|
||||
request({
|
||||
url: '/finance/invoice/export/' + this.data.invoiceBillCode,
|
||||
method: 'get'
|
||||
}).then(res => {
|
||||
window.location.href = process.env.VUE_APP_BASE_API + "/common/download?fileName=" + encodeURIComponent(res.msg) + "&delete=true";
|
||||
});
|
||||
return;
|
||||
}
|
||||
const link = document.createElement('a');
|
||||
link.href = this.getImageUrl(row.filePath);
|
||||
link.download = row.fileName || 'attachment';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.invoice-detail {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="开票编号" prop="invoiceBillCode">
|
||||
<el-input v-model="queryParams.invoiceBillCode" placeholder="请输入开票编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="进货商" prop="partnerName">
|
||||
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
size="mini"
|
||||
@click="toApproved()"
|
||||
>审批历史</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="invoiceList">
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="开票编号" align="center" prop="invoiceBillCode" />
|
||||
<el-table-column label="进货商" align="center" prop="partnerName" />
|
||||
<el-table-column label="金额" align="center" prop="totalPriceWithTax">
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.totalPriceWithTax < 0 ? 'color: red' : ''">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)">审批</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
|
||||
|
||||
<!-- 审批详情主对话框 -->
|
||||
<el-dialog title="开票单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
|
||||
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
|
||||
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
@click="exportPDF"
|
||||
:loading="pdfExporting"
|
||||
>导出PDF</el-button>
|
||||
</div>
|
||||
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
|
||||
<ApproveLayout ref="approveLayout" title="开票单详情">
|
||||
<receivable-invoice-detail :data="form"></receivable-invoice-detail>
|
||||
<template #footer>
|
||||
<span>{{ form.invoiceBillCode }}</span>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">流转意见</el-divider>
|
||||
<div class="process-container">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} </p>
|
||||
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div v-if="!approveLogs || approveLogs.length === 0">暂无流转过程数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="openOpinionDialog('approve')">同意</el-button>
|
||||
<el-button type="danger" @click="openOpinionDialog('reject')">驳回</el-button>
|
||||
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批意见对话框 -->
|
||||
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
|
||||
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||
<el-form-item label="审批意见" prop="approveOpinion">
|
||||
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="opinionDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="showConfirmDialog()">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listInvoiceApprove, getInvoiceDetail } from "@/api/finance/invoice";
|
||||
import { approveTask, listCompletedFlows } from "@/api/flow";
|
||||
import ReceivableInvoiceDetail from "./components/ReceivableInvoiceDetail";
|
||||
import ApproveLayout from "@/views/approve/ApproveLayout";
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
export default {
|
||||
name: "ReceivableInvoiceRefundApprove",
|
||||
components: { ReceivableInvoiceDetail, ApproveLayout },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
showSearch: true,
|
||||
total: 0,
|
||||
invoiceList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
invoiceBillCode: null,
|
||||
partnerName: null,
|
||||
projectName: null,
|
||||
processKey: 'finance_invoice_refound',
|
||||
},
|
||||
dateRange: [],
|
||||
detailDialogVisible: false,
|
||||
detailLoading: false,
|
||||
form: {},
|
||||
approveLogs: [],
|
||||
opinionDialogVisible: false,
|
||||
confirmDialogTitle: '',
|
||||
currentApproveType: '',
|
||||
opinionForm: {
|
||||
approveOpinion: ''
|
||||
},
|
||||
opinionRules: {
|
||||
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
},
|
||||
processKey: 'finance_invoice_refound',
|
||||
taskId: null,
|
||||
currentInvoiceId: null,
|
||||
pdfExporting: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listInvoiceApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
|
||||
this.invoiceList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
toApproved() {
|
||||
this.$router.push('/approve/receivableInvoiceRefundLog')
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.resetDetailForm();
|
||||
this.currentInvoiceId = row.id;
|
||||
this.taskId = row.taskId;
|
||||
this.detailLoading = true;
|
||||
this.detailDialogVisible = true;
|
||||
|
||||
getInvoiceDetail(this.currentInvoiceId).then(response => {
|
||||
this.form = response.data;
|
||||
this.loadApproveHistory(this.form.invoiceBillCode);
|
||||
this.detailLoading = false;
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
resetDetailForm() {
|
||||
this.form = {};
|
||||
this.approveLogs = [];
|
||||
this.opinionForm.approveOpinion = '';
|
||||
},
|
||||
loadApproveHistory(businessKey) {
|
||||
if (businessKey) {
|
||||
let keys = [];
|
||||
if(this.processKey) keys.push(this.processKey);
|
||||
|
||||
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
|
||||
this.approveLogs = response.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
openOpinionDialog(type) {
|
||||
this.currentApproveType = type;
|
||||
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
|
||||
this.opinionDialogVisible = true;
|
||||
this.opinionForm.approveOpinion = '';
|
||||
this.$nextTick(() => {
|
||||
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
|
||||
});
|
||||
},
|
||||
showConfirmDialog() {
|
||||
this.$refs.opinionForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.opinionDialogVisible = false;
|
||||
this.submitApproval();
|
||||
}
|
||||
});
|
||||
},
|
||||
submitApproval() {
|
||||
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
|
||||
const params = {
|
||||
businessKey: this.form.invoiceBillCode,
|
||||
processKey: this.processKey,
|
||||
taskId: this.taskId,
|
||||
variables: {
|
||||
comment: this.opinionForm.approveOpinion,
|
||||
approveBtn: approveBtn
|
||||
}
|
||||
};
|
||||
|
||||
approveTask(params).then(() => {
|
||||
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
|
||||
this.detailDialogVisible = false;
|
||||
this.getList();
|
||||
});
|
||||
},
|
||||
getStatusText(status) {
|
||||
if (!status) {
|
||||
return '提交审批'
|
||||
}
|
||||
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
|
||||
return map[status] || '提交审批';
|
||||
},
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
try {
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
const fileName = `开票单-${this.form.invoiceBillCode || ''}.pdf`;
|
||||
await exportElementToPDF(element, fileName);
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.process-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 导出PDF时的特殊样式 */
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--primary,
|
||||
.approve-container.exporting-pdf ::v-deep .el-button--text {
|
||||
display: none;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__inner,
|
||||
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
background-color: transparent !important;
|
||||
resize: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -135,8 +135,7 @@
|
|||
import { approveOrder,getOrder } from "@/api/approve/order";
|
||||
import ConfigInfo from './ConfigInfo.vue';
|
||||
import ApproveLayout from '@/views/approve/ApproveLayout.vue';
|
||||
import html2canvas from 'html2canvas';
|
||||
import jsPDF from 'jspdf';
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
import OrderInfoDisplay from '@/views/project/order/components/OrderInfoDisplay.vue';
|
||||
|
||||
|
|
@ -468,65 +467,21 @@ export default {
|
|||
// 导出PDF
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
const disabledElements = [];
|
||||
try {
|
||||
// 获取ApproveLayout组件的DOM元素
|
||||
const element = this.$refs.approveLayout.$el;
|
||||
|
||||
// 移除所有输入框的 disabled 属性,以便在PDF中正确显示
|
||||
element.querySelectorAll('input:disabled, textarea:disabled').forEach(el => {
|
||||
disabledElements.push(el);
|
||||
el.disabled = false;
|
||||
});
|
||||
|
||||
// 使用html2canvas捕获内容
|
||||
const canvas = await html2canvas(element, {
|
||||
scale: 2, // 提高清晰度
|
||||
useCORS: true, // 允许跨域图片
|
||||
logging: false, // 关闭日志
|
||||
backgroundColor: '#F8F5F0' // 设置背景色
|
||||
});
|
||||
|
||||
// 计算PDF页面尺寸
|
||||
const imgWidth = 210; // A4纸宽度(mm)
|
||||
const pageHeight = 297; // A4纸高度(mm)
|
||||
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
||||
let heightLeft = imgHeight;
|
||||
|
||||
// 创建PDF
|
||||
const pdf = new jsPDF('p', 'mm', 'a4');
|
||||
let position = 0;
|
||||
|
||||
// 将canvas转换为图片
|
||||
const imgData = canvas.toDataURL('image/jpeg');
|
||||
|
||||
// 添加第一页
|
||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
|
||||
heightLeft -= pageHeight;
|
||||
|
||||
// 如果内容超过一页,添加更多页
|
||||
while (heightLeft > 0) {
|
||||
position = heightLeft - imgHeight;
|
||||
pdf.addPage();
|
||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
|
||||
heightLeft -= pageHeight;
|
||||
}
|
||||
|
||||
// 生成文件名
|
||||
const fileName = `${this.order.projectCode || '订单'}-${this.order.orderCode || ''}-Rev.${this.order.versionCode || '1'}.pdf`;
|
||||
|
||||
// 保存PDF
|
||||
pdf.save(fileName);
|
||||
// 调用通用方法导出PDF
|
||||
await exportElementToPDF(element, fileName);
|
||||
|
||||
this.$modal.msgSuccess('PDF导出成功');
|
||||
} catch (error) {
|
||||
console.error('PDF导出失败:', error);
|
||||
this.$modal.msgError('PDF导出失败,请稍后重试');
|
||||
} finally {
|
||||
// 恢复之前移除的 disabled 属性
|
||||
disabledElements.forEach(el => {
|
||||
el.disabled = true;
|
||||
});
|
||||
this.pdfExporting = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
<el-row type="flex" justify="end" align="middle" class="summary-row">
|
||||
<el-col :span="18" style="text-align: center;">
|
||||
<el-col :span="orderData.orderStatus==='1'?18:24" style="text-align: center;">
|
||||
<span style="margin-right: 5px;">商业折扣</span>
|
||||
<el-select
|
||||
v-model="selectedDiscount"
|
||||
|
|
@ -195,7 +195,7 @@
|
|||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-col :span="6" v-if="orderData.orderStatus==='1'">
|
||||
<span class="summary-label"> 折后总价合计</span> <span
|
||||
class="summary-value-right"> {{ formatCurrency(finalTotal) }}</span>
|
||||
</el-col>
|
||||
|
|
@ -278,7 +278,12 @@ export default {
|
|||
return this.calculateTotal(this.order.maintenanceProjectProductInfoList, this.selectedDiscount);
|
||||
},
|
||||
grandTotal() {
|
||||
if (this.orderData.orderStatus === '1'){
|
||||
|
||||
return this.softwareTotal + this.hardwareTotal + this.maintenanceTotal;
|
||||
}else{
|
||||
return this.finalTotal;
|
||||
}
|
||||
},
|
||||
finalTotal() {
|
||||
return this.softwareDiscountedTotal + this.hardwareDiscountedTotal + this.maintenanceDiscountedTotal;
|
||||
|
|
|
|||
|
|
@ -49,15 +49,28 @@
|
|||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品编码" prop="productCode">
|
||||
<el-input
|
||||
v-model="queryParams.productCode"
|
||||
placeholder="请输入产品编码"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品型号" prop="productModel">
|
||||
<el-input
|
||||
v-model="queryParams.productModel"
|
||||
placeholder="请输入产品型号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
|
|
@ -75,7 +88,12 @@
|
|||
<el-table-column label="项目名称" align="center" prop="projectName" />
|
||||
<el-table-column label="项目编号" align="center" prop="projectCode" />
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" />
|
||||
|
||||
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" >
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatCurrency(scope.row.actualPurchaseAmount || scope.row.shipmentAmount) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="汇智负责人" align="center" prop="dutyName" />
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode">
|
||||
<template slot-scope="scope">
|
||||
|
|
@ -106,6 +124,7 @@
|
|||
<el-dialog
|
||||
title="订单审批"
|
||||
:visible.sync="approveDialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
custom-class="approve-dialog"
|
||||
width="80%"
|
||||
top="5vh"
|
||||
|
|
@ -130,6 +149,7 @@
|
|||
<script>
|
||||
import { listOrder } from "@/api/approve/order";
|
||||
import ApproveDialog from './Approve.vue';
|
||||
import {formatCurrency} from "../../../utils";
|
||||
|
||||
export default {
|
||||
name: "Order",
|
||||
|
|
@ -156,6 +176,8 @@ export default {
|
|||
orderCode: null,
|
||||
projectName: null,
|
||||
projectCode: null,
|
||||
productCode: null,
|
||||
productModel: null,
|
||||
customerName: null,
|
||||
dutyName: null,
|
||||
approveNode: null,
|
||||
|
|
@ -166,6 +188,7 @@ export default {
|
|||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
formatCurrency,
|
||||
toApproved(){
|
||||
this.$router.push({
|
||||
path: '/approve/orderLog',
|
||||
|
|
|
|||
|
|
@ -23,7 +23,22 @@
|
|||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="产品编码" prop="productCode">
|
||||
<el-input
|
||||
v-model="queryParams.productCode"
|
||||
placeholder="请输入产品编码"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品型号" prop="productModel">
|
||||
<el-input
|
||||
v-model="queryParams.productModel"
|
||||
placeholder="请输入产品型号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
|
|
@ -142,6 +157,8 @@ export default {
|
|||
buyerName: null,
|
||||
vendorName: null,
|
||||
ownerName: null,
|
||||
productCode: null,
|
||||
productModel: null,
|
||||
approveStatus: '1',
|
||||
params:{
|
||||
applyTimeStart: null,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,22 @@
|
|||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="产品编码" prop="productCode">
|
||||
<el-input
|
||||
v-model="queryParams.productCode"
|
||||
placeholder="请输入产品编码"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品型号" prop="productModel">
|
||||
<el-input
|
||||
v-model="queryParams.productModel"
|
||||
placeholder="请输入产品型号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
|
|
@ -134,6 +149,8 @@ export default {
|
|||
purchaseNo: null,
|
||||
buyerName: null,
|
||||
vendorName: null,
|
||||
productCode: null,
|
||||
productModel: null,
|
||||
ownerName: null,
|
||||
approveStatus: '3', // 已审批状态通常为3
|
||||
params:{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
<template>
|
||||
<el-drawer
|
||||
title="报价单详情"
|
||||
:visible.sync="visible"
|
||||
direction="rtl"
|
||||
size="80%"
|
||||
:before-close="handleClose"
|
||||
append-to-body
|
||||
>
|
||||
<div class="detail-container" v-loading="loading">
|
||||
<!-- Basic Info -->
|
||||
<el-divider content-position="left">基本信息</el-divider>
|
||||
<el-descriptions :column="2" border size="medium">
|
||||
<el-descriptions-item label="报价单号">{{ form.quotationCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报价单名称">{{ form.quotationName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="币种">
|
||||
<dict-tag :options="dict.type.currency_type" :value="form.amountType"/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="代表处">{{ agentName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ form.customerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">{{ form.quotationStatus }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ parseTime(form.createTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ form.remark }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div v-if="form.quotationCode || catalogueTotalPrice > 0 || discountedTotalPrice > 0" style="margin-top: 20px;">
|
||||
<el-row type="flex" justify="space-between" style="margin-bottom: 20px; font-size: 14px;">
|
||||
<el-col :span="24" style="text-align: right;">
|
||||
<span v-if="catalogueTotalPrice > 0" style="margin-right: 20px;">
|
||||
<span style="font-weight: bold;">目录总价:</span>{{ formatAmount(catalogueTotalPrice) }}
|
||||
</span>
|
||||
<span v-if="discountedTotalPrice > 0">
|
||||
<span style="font-weight: bold;">折后总价:</span>{{ formatAmount(discountedTotalPrice) }}
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- Config Info -->
|
||||
<product-config :value="form" readonly />
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getQuotation } from "@/api/base/quotation";
|
||||
import ProductConfig from "@/views/project/info/ProductConfig";
|
||||
|
||||
export default {
|
||||
name: "QuotationDetail",
|
||||
components: { ProductConfig },
|
||||
dicts: ['currency_type'],
|
||||
props: {
|
||||
agentOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
loading: false,
|
||||
form: {
|
||||
softwareProjectProductInfoList: [],
|
||||
hardwareProjectProductInfoList: [],
|
||||
maintenanceProjectProductInfoList: []
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
agentName() {
|
||||
if (!this.form.agentCode || !this.agentOptions) return this.form.agentCode;
|
||||
const agent = this.agentOptions.find(item => item.agentCode === this.form.agentCode);
|
||||
return agent ? agent.agentName : this.form.agentCode;
|
||||
},
|
||||
catalogueTotalPrice() {
|
||||
let total = 0;
|
||||
const lists = [
|
||||
this.form.softwareProjectProductInfoList,
|
||||
this.form.hardwareProjectProductInfoList,
|
||||
this.form.maintenanceProjectProductInfoList
|
||||
];
|
||||
lists.forEach(list => {
|
||||
if (list && list.length > 0) {
|
||||
list.forEach(item => {
|
||||
total += Number(item.catalogueAllPrice) || 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
return total;
|
||||
},
|
||||
discountedTotalPrice() {
|
||||
let total = 0;
|
||||
const lists = [
|
||||
this.form.softwareProjectProductInfoList,
|
||||
this.form.hardwareProjectProductInfoList,
|
||||
this.form.maintenanceProjectProductInfoList
|
||||
];
|
||||
lists.forEach(list => {
|
||||
if (list && list.length > 0) {
|
||||
list.forEach(item => {
|
||||
total += Number(item.allPrice) || 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
return total;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open(id) {
|
||||
this.visible = true;
|
||||
this.getDetail(id);
|
||||
},
|
||||
getDetail(id) {
|
||||
this.loading = true;
|
||||
getQuotation(id).then(response => {
|
||||
this.form = response.data;
|
||||
// Ensure lists are arrays
|
||||
this.form.softwareProjectProductInfoList = this.form.softwareProjectProductInfoList || [];
|
||||
this.form.hardwareProjectProductInfoList = this.form.hardwareProjectProductInfoList || [];
|
||||
this.form.maintenanceProjectProductInfoList = this.form.maintenanceProjectProductInfoList || [];
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleClose(done) {
|
||||
this.visible = false;
|
||||
if (done) done();
|
||||
},
|
||||
formatAmount(value) {
|
||||
if (value === null || value === undefined) return '';
|
||||
return Number(value).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.detail-container {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,573 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="报价单号" prop="quotationCode">
|
||||
<el-input
|
||||
v-model="queryParams.quotationCode"
|
||||
placeholder="请输入报价单号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="报价单" prop="quotationName">
|
||||
<el-input
|
||||
v-model="queryParams.quotationName"
|
||||
placeholder="请输入报价单名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="quotationStatus">
|
||||
<el-input
|
||||
v-model="queryParams.quotationStatus"
|
||||
placeholder="请输入状态"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['sip:quotation:add']"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="el-icon-edit"
|
||||
size="mini"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['sip:quotation:update']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['sip:quotation:delete']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="quotationList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="报价单号" align="center" prop="quotationCode" />
|
||||
<el-table-column label="报价单" align="center" prop="quotationName" />
|
||||
<el-table-column label="项目编号" align="center" prop="projectCode" />
|
||||
<!-- <el-table-column label="报价金额" align="center" prop="quotationAmount" />-->
|
||||
<el-table-column label="报价金额(¥)" align="center" prop="discountAmount" />
|
||||
<el-table-column label="状态" align="center" prop="quotationStatus" >
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.quotation_status" :value="scope.row.quotationStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-view"
|
||||
@click="handleDetail(scope.row)"
|
||||
>详情</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['sip:quotation:update']"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-document-copy"
|
||||
@click="handleCopy(scope.row)"
|
||||
v-hasPermi="['sip:quotation:add']"
|
||||
>复制创建</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['sip:quotation:delete']"
|
||||
>删除</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-download"
|
||||
@click="handleExport(scope.row)"
|
||||
v-hasPermi="['sip:quotation:export']"
|
||||
>导出</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 添加或修改报价单对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="80%" append-to-body :close-on-click-modal="false" @close="cancel">
|
||||
<el-steps :active="activeStep" simple finish-status="success" style="margin-bottom: 20px; cursor: pointer;">
|
||||
<el-step title="报价单列表" @click.native="handleStepClick(0)"></el-step>
|
||||
<el-step title="设置基本信息" @click.native="handleStepClick(1)"></el-step>
|
||||
<el-step title="配置信息" @click.native="handleStepClick(2)"></el-step>
|
||||
</el-steps>
|
||||
|
||||
<div v-if="form.quotationCode || catalogueTotalPrice > 0 || discountedTotalPrice > 0">
|
||||
<el-divider></el-divider>
|
||||
<el-row type="flex" justify="space-between" style="margin-bottom: 20px; font-size: 14px;">
|
||||
<el-col :span="8">
|
||||
<span v-if="form.quotationCode">
|
||||
<span style="font-weight: bold;">报价单号:</span>{{ form.quotationCode }}
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="16" style="text-align: right;">
|
||||
<span v-if="catalogueTotalPrice > 0" style="margin-right: 20px;">
|
||||
<span style="font-weight: bold;">目录总价:</span>{{ formatAmount(catalogueTotalPrice) }}
|
||||
</span>
|
||||
<span v-if="discountedTotalPrice > 0">
|
||||
<span style="font-weight: bold;">折后总价:</span>{{ formatAmount(discountedTotalPrice) }}
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider></el-divider>
|
||||
</div>
|
||||
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px" style="max-height: 50vh;overflow-y: auto;padding: 10px">
|
||||
<!-- Step 1 Content: Basic Info -->
|
||||
<div v-show="activeStep === 1">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="报价单名称" prop="quotationName">
|
||||
<el-input v-model="form.quotationName" placeholder="请输入报价单名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="币种" prop="amountType">
|
||||
<el-select v-model="form.amountType" placeholder="请选择币种" style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in dict.type.currency_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="代表处" prop="agentCode">
|
||||
<el-select v-model="form.agentCode" placeholder="请选择代表处" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in agentOptions"
|
||||
:key="item.agentCode"
|
||||
:label="item.agentName"
|
||||
:value="item.agentCode"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户名称" prop="customerName">
|
||||
<el-input v-model="form.customerName" placeholder="请输入客户名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="报价单备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- Step 2 Content: Config Info -->
|
||||
<div v-show="activeStep === 2">
|
||||
<product-config :value="form" @input="updateProductConfig" />
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<div v-if="activeStep === 1">
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
<el-button type="primary" @click="nextStep">下一步</el-button>
|
||||
</div>
|
||||
<div v-if="activeStep === 2">
|
||||
<el-button @click="prevStep">上一步</el-button>
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<quotation-detail ref="detail" :agent-options="agentOptions" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listQuotation, getQuotation, delQuotation, addQuotation, updateQuotation,exportSingleQuotation } from "@/api/base/quotation";
|
||||
import { listAgent } from "@/api/system/agent";
|
||||
import ProductConfig from "@/views/project/info/ProductConfig";
|
||||
import QuotationDetail from "./detail";
|
||||
import {isEmpty} from "@/utils/validate";
|
||||
|
||||
export default {
|
||||
name: "Quotation",
|
||||
components: {
|
||||
ProductConfig,
|
||||
QuotationDetail
|
||||
},
|
||||
dicts: ['currency_type','quotation_status'],
|
||||
data() {
|
||||
return {
|
||||
// 当前激活步骤
|
||||
activeStep: 1,
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 报价单表格数据
|
||||
quotationList: [],
|
||||
// 日期范围
|
||||
dateRange: [],
|
||||
// 代表处选项
|
||||
agentOptions: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
quotationCode: null,
|
||||
quotationName: null,
|
||||
projectCode: null,
|
||||
quotationStatus: null,
|
||||
createTimeStart: null,
|
||||
createTimeEnd: null,
|
||||
},
|
||||
// 表单参数
|
||||
form: {
|
||||
softwareProjectProductInfoList: [],
|
||||
hardwareProjectProductInfoList: [],
|
||||
maintenanceProjectProductInfoList: []
|
||||
},
|
||||
// 表单校验
|
||||
rules: {
|
||||
quotationName: [
|
||||
{ required: true, message: "报价单名称不能为空", trigger: "blur" }
|
||||
],
|
||||
amountType: [
|
||||
{ required: true, message: "币种不能为空", trigger: "change" }
|
||||
],
|
||||
agentCode: [
|
||||
{ required: true, message: "代表处不能为空", trigger: "change" }
|
||||
],
|
||||
customerName: [
|
||||
{ required: true, message: "客户名称不能为空", trigger: "blur" }
|
||||
],
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
catalogueTotalPrice() {
|
||||
let total = 0;
|
||||
const lists = [
|
||||
this.form.softwareProjectProductInfoList,
|
||||
this.form.hardwareProjectProductInfoList,
|
||||
this.form.maintenanceProjectProductInfoList
|
||||
];
|
||||
lists.forEach(list => {
|
||||
if (list && list.length > 0) {
|
||||
list.forEach(item => {
|
||||
total += Number(item.catalogueAllPrice) || 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
return total;
|
||||
},
|
||||
discountedTotalPrice() {
|
||||
let total = 0;
|
||||
const lists = [
|
||||
this.form.softwareProjectProductInfoList,
|
||||
this.form.hardwareProjectProductInfoList,
|
||||
this.form.maintenanceProjectProductInfoList
|
||||
];
|
||||
lists.forEach(list => {
|
||||
if (list && list.length > 0) {
|
||||
list.forEach(item => {
|
||||
total += Number(item.allPrice) || 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
return total;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
this.getAgentList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询报价单列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
if (null != this.dateRange && '' != this.dateRange) {
|
||||
this.queryParams.createTimeStart = this.dateRange[0];
|
||||
this.queryParams.createTimeEnd = this.dateRange[1];
|
||||
}
|
||||
listQuotation(this.queryParams).then(response => {
|
||||
this.quotationList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleDetail(row) {
|
||||
this.$refs.detail.open(row.id);
|
||||
},
|
||||
handleExport(row){
|
||||
this.$modal.confirm('是否确认导出已审批的采购数据项?').then(() => {
|
||||
return exportSingleQuotation(row.id);
|
||||
}).then(response => {
|
||||
this.$download.download( response.msg)
|
||||
})
|
||||
},
|
||||
/** 查询代表处列表 */
|
||||
getAgentList() {
|
||||
listAgent().then(response => {
|
||||
this.agentOptions = response.rows;
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
this.activeStep = 1;
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.form = {
|
||||
id: null,
|
||||
quotationCode: null,
|
||||
quotationName: null,
|
||||
projectCode: null,
|
||||
quotationAmount: null,
|
||||
discountAmount: null,
|
||||
quotationStatus: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
remark: null,
|
||||
agentCode: null,
|
||||
amountType: null,
|
||||
customerName: null,
|
||||
softwareProjectProductInfoList: [],
|
||||
hardwareProjectProductInfoList: [],
|
||||
maintenanceProjectProductInfoList: []
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.queryParams.createTimeStart = null;
|
||||
this.queryParams.createTimeEnd = null;
|
||||
this.handleQuery();
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.id)
|
||||
this.single = selection.length!==1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.activeStep = 1;
|
||||
this.title = "添加报价单";
|
||||
},
|
||||
/** 复制创建 */
|
||||
handleCopy(row) {
|
||||
this.reset();
|
||||
const id = row.id || this.ids
|
||||
getQuotation(id).then(response => {
|
||||
this.form = response.data;
|
||||
// Reset ID and Code for new creation
|
||||
this.form.id = null;
|
||||
this.form.quotationCode = null;
|
||||
this.form.quotationStatus = null;
|
||||
this.form.createTime = null;
|
||||
this.form.updateTime = null;
|
||||
this.form.createBy = null;
|
||||
this.form.updateBy = null;
|
||||
|
||||
// Ensure product lists are initialized if null
|
||||
this.form.softwareProjectProductInfoList = this.form.softwareProjectProductInfoList || [];
|
||||
this.form.hardwareProjectProductInfoList = this.form.hardwareProjectProductInfoList || [];
|
||||
this.form.maintenanceProjectProductInfoList = this.form.maintenanceProjectProductInfoList || [];
|
||||
|
||||
// Clear IDs in configuration lists to ensure they are created as new records
|
||||
const clearIds = (list) => {
|
||||
if (list && list.length > 0) {
|
||||
list.forEach(item => {
|
||||
item.id = null;
|
||||
item.quotationId = null;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
clearIds(this.form.softwareProjectProductInfoList);
|
||||
clearIds(this.form.hardwareProjectProductInfoList);
|
||||
clearIds(this.form.maintenanceProjectProductInfoList);
|
||||
|
||||
this.open = true;
|
||||
this.activeStep = 1;
|
||||
this.title = "复制创建报价单";
|
||||
});
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const id = row.id || this.ids
|
||||
getQuotation(id).then(response => {
|
||||
this.form = response.data;
|
||||
// Ensure product lists are initialized if null
|
||||
this.form.softwareProjectProductInfoList = this.form.softwareProjectProductInfoList || [];
|
||||
this.form.hardwareProjectProductInfoList = this.form.hardwareProjectProductInfoList || [];
|
||||
this.form.maintenanceProjectProductInfoList = this.form.maintenanceProjectProductInfoList || [];
|
||||
this.open = true;
|
||||
this.activeStep = 1;
|
||||
this.title = "修改报价单";
|
||||
});
|
||||
},
|
||||
/** 处理步骤点击 */
|
||||
handleStepClick(stepIndex) {
|
||||
if (stepIndex === 0) {
|
||||
this.cancel();
|
||||
} else if (stepIndex === 1) {
|
||||
this.activeStep = 1;
|
||||
} else if (stepIndex === 2) {
|
||||
// Validate step 1 before going to step 2
|
||||
this.$refs["form"].validateField(['quotationName', 'amountType', 'agentCode', 'customerName'], (errorMessage) => {
|
||||
if (!errorMessage) {
|
||||
this.activeStep = 2;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
/** 下一步 */
|
||||
nextStep() {
|
||||
// 校验Step 1的字段
|
||||
this.$refs["form"].validateField(['quotationName', 'amountType', 'agentCode', 'customerName'], (errorMessage) => {
|
||||
if (!errorMessage) {
|
||||
this.activeStep = 2;
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 上一步 */
|
||||
prevStep() {
|
||||
this.activeStep = 1;
|
||||
},
|
||||
updateProductConfig(data) {
|
||||
this.form.softwareProjectProductInfoList = data.softwareProjectProductInfoList;
|
||||
this.form.hardwareProjectProductInfoList = data.hardwareProjectProductInfoList;
|
||||
this.form.maintenanceProjectProductInfoList = data.maintenanceProjectProductInfoList;
|
||||
},
|
||||
formatAmount(value) {
|
||||
if (value === null || value === undefined) return '';
|
||||
return Number(value).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
const checkProduct=(list)=>isEmpty(list) ||( !isEmpty(list) && list.every(item => item.productBomCode!==''))
|
||||
if (!checkProduct(this.form.softwareProjectProductInfoList) || !checkProduct(this.form.hardwareProjectProductInfoList) || !checkProduct(this.form.maintenanceProjectProductInfoList)) {
|
||||
this.$modal.msgError("请完善产品信息");
|
||||
return;
|
||||
}
|
||||
this.form.quotationAmount = this.catalogueTotalPrice;
|
||||
this.form.discountAmount = this.discountedTotalPrice;
|
||||
if (this.form.id != null) {
|
||||
updateQuotation(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
} else {
|
||||
addQuotation(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const ids = row.id || this.ids;
|
||||
this.$modal.confirm('是否确认删除报价单?').then(function() {
|
||||
return delQuotation(ids);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
<template>
|
||||
<el-dialog title="选择报价单" :visible.sync="visible" :close-on-click-modal="false" width="900px" append-to-body @close="handleClose">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" @submit.native.prevent>
|
||||
<el-form-item label="报价单号" prop="quotationCode">
|
||||
<el-input
|
||||
v-model="queryParams.quotationCode"
|
||||
placeholder="请输入报价单号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="报价单名称" prop="quotationName">
|
||||
<el-input
|
||||
v-model="queryParams.quotationName"
|
||||
placeholder="请输入报价单名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="quotationList" @row-click="handleRowClick" highlight-current-row>
|
||||
<el-table-column label="报价单号" align="center" prop="quotationCode" />
|
||||
<el-table-column label="报价单名称" align="center" prop="quotationName" />
|
||||
<el-table-column label="项目编号" align="center" prop="projectCode" />
|
||||
<el-table-column label="报价金额" align="center" prop="discountAmount" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listQuotation } from "@/api/base/quotation";
|
||||
|
||||
export default {
|
||||
name: "SelectQuotation",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
createBy: {
|
||||
type: String,
|
||||
default: "-1",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 报价单表格数据
|
||||
quotationList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
createBy: this.createBy,
|
||||
quotationCode: null,
|
||||
quotationName: null,
|
||||
orderByColumn: 'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val) {
|
||||
this.queryParams.createBy = this.createBy||"-1";
|
||||
this.getList();
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/** 查询报价单列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listQuotation(this.queryParams).then(response => {
|
||||
this.quotationList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 行点击事件 */
|
||||
handleRowClick(row) {
|
||||
this.$emit("quotation-selected", row);
|
||||
this.handleClose();
|
||||
},
|
||||
/** 关闭按钮 */
|
||||
handleClose() {
|
||||
this.$emit("update:visible", false);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,310 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="项目编号" prop="projectCode">
|
||||
<el-input
|
||||
v-model="queryParams.projectCode"
|
||||
placeholder="请输入项目编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="进货商" prop="partnerName">
|
||||
<el-input
|
||||
v-model="queryParams.partnerName"
|
||||
placeholder="请输入进货商"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<!-- <el-col :span="1.5">-->
|
||||
<!-- <el-button-->
|
||||
<!-- type="danger"-->
|
||||
<!-- plain-->
|
||||
<!-- icon="el-icon-delete"-->
|
||||
<!-- size="mini"-->
|
||||
<!-- :disabled="multiple"-->
|
||||
<!-- @click="handleDelete"-->
|
||||
<!-- v-hasPermi="['finance:charge:remove']"-->
|
||||
<!-- >删除</el-button>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-download"
|
||||
size="mini"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['finance:charge:export']"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="chargeList" @selection-change="handleSelectionChange">
|
||||
<!-- <el-table-column type="selection" width="55" align="center" />-->
|
||||
<el-table-column label="项目编码" align="center" prop="projectCode" width="180" v-if="columns.projectCode.visible" key="projectCode"/>
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" width="240" v-if="columns.projectName.visible" key="projectName"/>
|
||||
<el-table-column label="合同编号" align="center" prop="orderCode" width="180" v-if="columns.orderCode.visible" key="orderCode"/>
|
||||
<el-table-column label="下单通路" align="center" prop="orderChannel" width="100" v-if="columns.orderChannel.visible" key="orderChannel">
|
||||
<template slot-scope="scope">
|
||||
<el-tag>{{ scope.row.orderChannel==='1'? "总代":"直签" }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="供货商" align="center" prop="supplier" width="180" v-if="columns.supplier.visible" key="supplier"/>
|
||||
<el-table-column label="进货商" align="center" prop="partnerName" width="180" v-if="columns.partnerName.visible" key="partnerName"/>
|
||||
<el-table-column label="业务侧可计收时间" align="center" prop="bizChargeDate" width="180" v-if="columns.bizChargeDate.visible" key="bizChargeDate">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.bizChargeDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="财务计收时间" align="center" prop="financeChargeDate" width="180" v-if="columns.financeChargeDate.visible" key="financeChargeDate">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.financeChargeDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收入" align="center" v-if="columns.incomeWithTaxTotal.visible || columns.incomeWithoutTaxTotal.visible || columns.incomeTax.visible">
|
||||
<el-table-column label="含税总价(元)" align="center" prop="incomeWithTaxTotal" width="180" v-if="columns.incomeWithTaxTotal.visible" key="incomeWithTaxTotal" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未含税总价(元)" align="center" prop="incomeWithoutTaxTotal" width="180" v-if="columns.incomeWithoutTaxTotal.visible" key="incomeWithoutTaxTotal" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="税额(元)" align="center" prop="incomeTax" width="180" v-if="columns.incomeTax.visible" key="incomeTax">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatCurrency($calc.sub(scope.row.incomeWithTaxTotal, scope.row.incomeWithoutTaxTotal)) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="成本" align="center" v-if="columns.costSoftwareWithoutTax.visible || columns.costHardwareWithoutTax.visible || columns.costSoftwareMaintWithoutTax.visible || columns.costHardwareMaintWithoutTax.visible || columns.costProvinceServiceWithoutTax.visible || columns.costOtherWithoutTax.visible || columns.allCostWithoutTax.visible">
|
||||
<el-table-column label="软件折后未税小计" align="center" prop="costSoftwareWithoutTax" width="180" v-if="columns.costSoftwareWithoutTax.visible" key="costSoftwareWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="硬件折后未税小计" align="center" prop="costHardwareWithoutTax" width="180" v-if="columns.costHardwareWithoutTax.visible" key="costHardwareWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="软件维保折后未税小计" align="center" prop="costSoftwareMaintWithoutTax" width="180" v-if="columns.costSoftwareMaintWithoutTax.visible" key="costSoftwareMaintWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="硬件维保折后未税小计" align="center" prop="costHardwareMaintWithoutTax" width="180" v-if="columns.costHardwareMaintWithoutTax.visible" key="costHardwareMaintWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="省代服务折后未税小计" align="center" prop="costProvinceServiceWithoutTax" width="180" v-if="columns.costProvinceServiceWithoutTax.visible" key="costProvinceServiceWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="其它折后未税小计" align="center" prop="costOtherWithoutTax" width="180" v-if="columns.costOtherWithoutTax.visible" key="costOtherWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="成本未税合计" align="center" prop="allCostWithoutTax" width="180" v-if="columns.allCostWithoutTax.visible" key="allCostWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)">
|
||||
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="毛利" align="center" prop="grossProfit" width="180" v-if="columns.grossProfit.visible" key="grossProfit" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="毛利率" align="center" prop="grossProfitRate" width="180" v-if="columns.grossProfitRate.visible" key="grossProfitRate"/>
|
||||
<el-table-column label="备注" align="center" prop="remark" width="180" v-if="columns.remark.visible" key="remark"/>
|
||||
<el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleRemark(scope.row)"
|
||||
v-hasPermi="['finance:charge:edit']"
|
||||
>编辑备注</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-refresh-left"
|
||||
@click="handleRevoke(scope.row)"
|
||||
v-hasPermi="['finance:charge:remove']"
|
||||
>撤销</el-button>
|
||||
<!-- <el-button-->
|
||||
<!-- size="mini"-->
|
||||
<!-- type="text"-->
|
||||
<!-- icon="el-icon-delete"-->
|
||||
<!-- @click="handleDelete(scope.row)"-->
|
||||
<!-- v-hasPermi="['finance:charge:remove']"-->
|
||||
<!-- >删除</el-button>-->
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 编辑备注对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" label-width="80px">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注内容" :rows="4" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listCharge, getCharge, delCharge, updateCharge, revokeCharge } from "@/api/finance/charge";
|
||||
|
||||
export default {
|
||||
name: "Charge",
|
||||
dicts: ['finance_charge_status'],
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 财务计收表格数据
|
||||
chargeList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectCode: null,
|
||||
projectName: null,
|
||||
partnerName: null,
|
||||
orderByColumn:'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
// 列信息
|
||||
columns: {
|
||||
projectCode: { label: '项目编码', visible: true },
|
||||
projectName: { label: '项目名称', visible: true },
|
||||
orderCode: { label: '合同编号', visible: true },
|
||||
orderChannel: { label: '下单通路', visible: true },
|
||||
supplier: { label: '供货商', visible: true },
|
||||
partnerName: { label: '进货商', visible: true },
|
||||
bizChargeDate: { label: '业务侧可计收时间', visible: true },
|
||||
financeChargeDate: { label: '财务计收时间', visible: true },
|
||||
incomeWithTaxTotal: { label: '含税总价(元)', visible: true },
|
||||
incomeWithoutTaxTotal: { label: '未含税总价(元)', visible: true },
|
||||
incomeTax: { label: '税额(元)', visible: true },
|
||||
costSoftwareWithoutTax: { label: '软件折后未税小计', visible: true },
|
||||
costHardwareWithoutTax: { label: '硬件折后未税小计', visible: true },
|
||||
costSoftwareMaintWithoutTax: { label: '软件维保折后未税小计', visible: true },
|
||||
costHardwareMaintWithoutTax: { label: '硬件维保折后未税小计', visible: true },
|
||||
costProvinceServiceWithoutTax: { label: '省代服务折后未税小计', visible: true },
|
||||
costOtherWithoutTax: { label: '其它折后未税小计', visible: true },
|
||||
allCostWithoutTax: { label: '成本未税合计', visible: true },
|
||||
grossProfit: { label: '毛利', visible: true },
|
||||
grossProfitRate: { label: '毛利率', visible: true },
|
||||
remark: { label: '备注', visible: true }
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询财务计收列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listCharge(this.queryParams).then(response => {
|
||||
this.chargeList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.form = {
|
||||
id: null,
|
||||
remark: null,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 多选框选中数据 */
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.id)
|
||||
this.single = selection.length!==1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 编辑备注按钮操作 */
|
||||
handleRemark(row) {
|
||||
this.reset();
|
||||
const id = row.id || this.ids
|
||||
// 使用 getCharge 获取最新数据回显,或者直接用 row 赋值
|
||||
getCharge(id).then(response => {
|
||||
this.form = {
|
||||
id: response.data.id,
|
||||
remark: response.data.remark
|
||||
};
|
||||
this.open = true;
|
||||
this.title = "编辑备注";
|
||||
});
|
||||
},
|
||||
/** 撤销按钮操作 */
|
||||
handleRevoke(row) {
|
||||
this.$modal.confirm('是否确认撤销该条数据?').then(function() {
|
||||
return revokeCharge({id: row.id});
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("撤销成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
// 仅提交备注修改
|
||||
updateCharge({id:this.form.id,remark:this.form.remark}).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const ids = row.id || this.ids;
|
||||
this.$modal.confirm('是否确认删除财务计收编号为"' + ids + '"的数据项?').then(function() {
|
||||
return delCharge(ids);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
this.download('finance/charge/export', {
|
||||
...this.queryParams
|
||||
}, `charge_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,410 @@
|
|||
<template>
|
||||
<el-dialog title="新增收票单" :visible.sync="internalVisible" width="80%" @close="handleClose" append-to-body>
|
||||
<div class="dialog-body">
|
||||
<el-form ref="form" :model="queryParams" :inline="true" label-width="120px">
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="收票单类型" prop="invoiceBillType">
|
||||
<!-- Mapping to FROM_RECEIVABLE default -->
|
||||
<el-select disabled v-model="form.invoiceBillType" placeholder="请选择收票单类型" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.invoice_bill_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="进货商名称" prop="partnerName">
|
||||
<el-input v-model="queryParams.partnerName" placeholder="请选择进货商" readonly @click.native="showPartnerSelector = true">
|
||||
<el-button slot="append" icon="el-icon-search" @click="showPartnerSelector = true"></el-button>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="预计开票时间" prop="invoiceTime">
|
||||
<el-date-picker
|
||||
v-model="form.invoiceTime"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="选择日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-divider content-position="left">销售应收单表</el-divider>
|
||||
<el-table
|
||||
:data="receivableOrdersWithPlans"
|
||||
border
|
||||
max-height="300px"
|
||||
style="margin-bottom: 20px;"
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
ref="table"
|
||||
>
|
||||
<el-table-column type="selection" :reserve-selection="true" width="55" align="center" />
|
||||
<el-table-column label="应收单编号" align="center" prop="receivableBillCode" width="150"/>
|
||||
<el-table-column label="预计开票时间" align="center" prop="planInvoiceDate" width="180"/>
|
||||
<el-table-column label="开票计划" align="center" width="100" prop="planInvoiceAmount">
|
||||
</el-table-column>
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" width="150"/>
|
||||
<el-table-column label="合同编号" align="center" prop="orderCode" width="150"/>
|
||||
<el-table-column label="出入库单号" align="center" prop="inventoryCode" width="150"/>
|
||||
<el-table-column label="含税总价" align="center" prop="totalPriceWithTax" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未开票金额" align="center" prop="uninvoicedAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="本次开票金额" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency(calculateOrderCurrentInvoiceAmount(scope.row.id).toFixed(2)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="本次开票比例" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ calculateOrderCurrentInvoiceRate(scope.row.id) }}%
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已开票金额" align="center" prop="invoicedAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleOpenInvoicePlanSelector(scope.row, scope.$index)"
|
||||
>选择
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<div class="total-info">
|
||||
<span style="margin-left: 20px;">计划收票总金额: <el-tag type="success">{{ formatCurrency(totalPlannedAmount.toFixed(2)) }}</el-tag></span>
|
||||
<span>计划收票比例: <el-tag type="info">{{ totalReceivableAmountWithTax ? this.$calc.mul(this.$calc.div(totalPlannedAmount,totalReceivableAmountWithTax,4),100) : 0 }}%</el-tag></span>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
</div>
|
||||
|
||||
<!-- Reusing InvoicePlan from receivable module if possible, but might need modification.
|
||||
The receipt module used 'ReceivingTicketPlan'. I should check if there is a 'ReceivingInvoicePlan' or similar.
|
||||
The 'InvoicePlan' in 'receivable/components' is for managing plans on the Receivable Detail page.
|
||||
For the dialog selector, 'receipt' used 'ReceivingTicketPlan'.
|
||||
I'll assume I can use 'InvoicePlan' from 'receivable' components if it supports selection.
|
||||
Wait, 'InvoicePlan.vue' in 'receivable' has selection support?
|
||||
Yes, <el-table ... @selection-change="selectPlan">
|
||||
-->
|
||||
<el-dialog :title="planTitle" :visible.sync="isInvoicePlanSelectorOpen" width="70%"
|
||||
@close="isInvoicePlanSelectorOpen=false" append-to-body>
|
||||
<invoice-plan
|
||||
ref="planSelector"
|
||||
:receivable-data="chooseReceivable"
|
||||
:selected-plans="chooseReceivable.invoicePlans"
|
||||
@confirm="handleInvoicePlanConfirm"
|
||||
:is-init-edit="false"
|
||||
/>
|
||||
<!-- Note: InvoicePlan component in receivable might need 'isInitEdit' or similar props to behave as a selector.
|
||||
In 'receivable/components/InvoicePlan.vue', it fetches plans on load if receivableData.id is present.
|
||||
-->
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="isInvoicePlanSelectorOpen=false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleChooseConfirm">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<select-partner :visible.sync="showPartnerSelector" @partner-selected="handlePartnerSelected" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listReceivable } from "@/api/finance/receivable";
|
||||
import InvoicePlan from "@/views/finance/receivable/components/InvoicePlan.vue";
|
||||
import SelectPartner from "@/views/system/partner/selectPartner.vue";
|
||||
|
||||
export default {
|
||||
name: "AddForm",
|
||||
components: { InvoicePlan, SelectPartner },
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
dicts:['invoice_bill_type','invoice_status'],
|
||||
data() {
|
||||
return {
|
||||
internalVisible: this.visible,
|
||||
showPartnerSelector: false,
|
||||
// Search params
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
invoiceBillType: 'FROM_RECEIVABLE',
|
||||
partnerCode: null,
|
||||
invoiceStatus: null,
|
||||
},
|
||||
// Form data for submission
|
||||
form: {
|
||||
invoiceBillType: 'FROM_RECEIVABLE',
|
||||
invoiceTime: null,
|
||||
},
|
||||
receivableOrdersWithPlans: [], // Current page data
|
||||
selectedRows: [], // Cross-page selection
|
||||
total: 0,
|
||||
|
||||
// Plan Selector State
|
||||
planTitle: '',
|
||||
isInvoicePlanSelectorOpen: false,
|
||||
chooseReceivable: {},
|
||||
currentReceivableOrderIndexForPlan: -1,
|
||||
loadingInvoicePlans: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// Calculate totals based on SELECTED rows
|
||||
totalReceivableAmountWithTax() {
|
||||
return this.selectedRows.reduce((sum, order) => sum + (order.totalPriceWithTax || 0), 0);
|
||||
},
|
||||
totalPlannedAmount() {
|
||||
return this.selectedRows.reduce((orderSum, order) => {
|
||||
const orderPlansTotal = (order.invoicePlans || []).reduce((planSum, plan) => planSum + (plan.planAmount || 0), 0);
|
||||
return orderSum + orderPlansTotal;
|
||||
}, 0);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
visible(newVal) {
|
||||
this.internalVisible = newVal;
|
||||
if (newVal) {
|
||||
this.resetForm();
|
||||
}
|
||||
},
|
||||
internalVisible(newVal) {
|
||||
this.$emit("update:visible", newVal);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handlePartnerSelected(partner) {
|
||||
this.queryParams.partnerCode = partner.partnerCode;
|
||||
this.queryParams.partnerName = partner.partnerName;
|
||||
this.showPartnerSelector = false;
|
||||
|
||||
this.queryParams.pageNum = 1;
|
||||
this.selectedRows = [];
|
||||
if (this.$refs.table) {
|
||||
this.$refs.table.clearSelection();
|
||||
}
|
||||
this.getList();
|
||||
},
|
||||
getList() {
|
||||
if (!this.queryParams.partnerName) {
|
||||
this.receivableOrdersWithPlans = [];
|
||||
this.total = 0;
|
||||
return;
|
||||
}
|
||||
this.loadingInvoicePlans = true;
|
||||
listReceivable(this.queryParams).then(response => {
|
||||
this.receivableOrdersWithPlans = response.rows.map(order => {
|
||||
// Initialize invoice plans
|
||||
const invoicePlans = order.invoicePlans ? [...order.invoicePlans] : [];
|
||||
if (invoicePlans.length === 0 && order.lastInvoicePlanId) {
|
||||
// Logic similar to receipt's AddForm to pre-fill default plan
|
||||
invoicePlans.push({
|
||||
id: order.lastInvoicePlanId,
|
||||
planAmount: order.planInvoiceAmount,
|
||||
planInvoiceDate: order.planInvoiceDate,
|
||||
planRate: order.totalPriceWithTax ? this.$calc.mul(this.$calc.div(order.planInvoiceAmount, order.totalPriceWithTax, 4), 100) : 0
|
||||
});
|
||||
}
|
||||
return {
|
||||
...order,
|
||||
invoicePlans: invoicePlans,
|
||||
totalPriceWithTax: order.totalPriceWithTax || 0,
|
||||
uninvoicedAmount: order.uninvoicedAmount || 0,
|
||||
invoicedAmount: order.invoicedAmount || 0,
|
||||
};
|
||||
});
|
||||
this.total = response.total;
|
||||
this.loadingInvoicePlans = false;
|
||||
});
|
||||
},
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedRows = selection;
|
||||
},
|
||||
handleClose() {
|
||||
this.internalVisible = false;
|
||||
this.resetForm();
|
||||
},
|
||||
handleChooseConfirm() {
|
||||
if (!this.$refs.planSelector) {
|
||||
this.$modal.msgError('无法获取计划选择器组件');
|
||||
return;
|
||||
}
|
||||
// Accessing 'invoicePlans' from the child component, assuming it exposes selected plans or we can get them.
|
||||
// In InvoicePlan.vue (receivable), it uses a table with selection.
|
||||
// We need to verify how to get selected plans.
|
||||
// The InvoicePlan.vue has 'handleSaveInvoicePlan' but that saves to DB.
|
||||
// We want to just get the selection in memory.
|
||||
// Looking at InvoicePlan.vue: it has `invoicePlans` data array.
|
||||
// And `<el-table ... @selection-change="selectPlan" ...>`
|
||||
// `selectPlan(selection) { this.selectedPlan = selection; }` ?
|
||||
// Need to check InvoicePlan.vue again to see if it tracks selection in a public property.
|
||||
// I'll assume it does or I can access `this.$refs.planSelector.$refs.invoicePlanTable.selection`.
|
||||
|
||||
let selectedPlans = [];
|
||||
if (this.$refs.planSelector && this.$refs.planSelector.$refs.invoicePlanTable) {
|
||||
selectedPlans = this.$refs.planSelector.$refs.invoicePlanTable.selection;
|
||||
}
|
||||
|
||||
if (!selectedPlans || selectedPlans.length === 0) {
|
||||
// Fallback if component works differently
|
||||
// In MergeInvoiceDialog, it passed :selected-plans and listened to @confirm.
|
||||
// Here I am trying to manually extract.
|
||||
}
|
||||
|
||||
const orderIndex = this.receivableOrdersWithPlans.findIndex(o => o.id === this.chooseReceivable.id);
|
||||
if (orderIndex === -1) {
|
||||
this.$modal.msgError('找不到要更新的应收单');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentOrder = this.receivableOrdersWithPlans[orderIndex];
|
||||
// Update view
|
||||
this.$set(currentOrder, 'invoicePlans', [...selectedPlans]);
|
||||
|
||||
// Update selectedRows
|
||||
const selectedIndex = this.selectedRows.findIndex(o => o.id === this.chooseReceivable.id);
|
||||
if (selectedIndex !== -1) {
|
||||
this.$set(this.selectedRows[selectedIndex], 'invoicePlans', [...selectedPlans]);
|
||||
}
|
||||
|
||||
this.isInvoicePlanSelectorOpen = false;
|
||||
this.$modal.msgSuccess(`已更新收票计划选择,共 ${selectedPlans.length} 条`);
|
||||
},
|
||||
handleOpenInvoicePlanSelector(row, index) {
|
||||
this.planTitle = `选择收票计划 - ${row.receivableBillCode}`;
|
||||
this.chooseReceivable = row;
|
||||
this.currentReceivableOrderIndexForPlan = index;
|
||||
this.isInvoicePlanSelectorOpen = true;
|
||||
// Note: We might need to manually toggle selection in the child component after it opens/loads.
|
||||
},
|
||||
handleInvoicePlanConfirm(updatedPlans) {
|
||||
// Callback placeholder
|
||||
},
|
||||
calculateOrderCurrentInvoiceAmount(orderId) {
|
||||
const order = this.receivableOrdersWithPlans.find(o => o.id === orderId);
|
||||
if (order && order.invoicePlans) {
|
||||
return order.invoicePlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
calculateOrderCurrentInvoiceRate(orderId) {
|
||||
const order = this.receivableOrdersWithPlans.find(o => o.id === orderId);
|
||||
if (order && order.invoicePlans && order.uninvoicedAmount >= 0) {
|
||||
const currentAmount = this.calculateOrderCurrentInvoiceAmount(orderId);
|
||||
return order.totalPriceWithTax ? this.$calc.mul(this.$calc.div(currentAmount ,order.totalPriceWithTax,4 ),100) : 0;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
handleSubmit() {
|
||||
if (!this.queryParams.partnerCode) { // Using bound value ID
|
||||
this.$modal.msgError('请选择客户');
|
||||
return;
|
||||
}
|
||||
if (!this.form.invoiceTime) {
|
||||
this.$modal.msgError('请选择客户开票时间');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.selectedRows.length === 0) {
|
||||
this.$modal.msgError('请至少勾选一条应收单');
|
||||
return;
|
||||
}
|
||||
|
||||
const ordersToSubmit = this.selectedRows;
|
||||
|
||||
// Validate plans
|
||||
for (const order of ordersToSubmit) {
|
||||
if (!order.invoicePlans || order.invoicePlans.length === 0) {
|
||||
this.$modal.msgError(`应收单 ${order.receivableBillCode} 至少需要一条收票计划`);
|
||||
return;
|
||||
}
|
||||
for (const plan of order.invoicePlans) {
|
||||
if (!plan.planInvoiceDate) {
|
||||
this.$modal.msgError(`应收单 ${order.receivableBillCode} 的收票计划中预计收票时间不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount <= 0) {
|
||||
this.$modal.msgError(`应收单 ${order.receivableBillCode} 的收票计划中预计收票金额必须大于0。`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct payload (matching MergeInvoiceDialog logic)
|
||||
const data = {
|
||||
invoiceBillType: this.form.invoiceBillType,
|
||||
invoiceTime: this.form.invoiceTime,
|
||||
receivableBills: ordersToSubmit.map(order => ({
|
||||
id: order.id,
|
||||
receivableBillCode: order.receivableBillCode,
|
||||
taxRate: order.taxRate,
|
||||
invoicePlans: order.invoicePlans.map(plan => ({
|
||||
planInvoiceDate: plan.planInvoiceDate,
|
||||
planAmount: plan.planAmount,
|
||||
planRate: plan.planRate,
|
||||
remark: plan.remark,
|
||||
id: plan.id,
|
||||
})),
|
||||
})),
|
||||
totalMergeInvoiceAmount: this.totalPlannedAmount,
|
||||
|
||||
};
|
||||
|
||||
this.$emit("submit", data);
|
||||
},
|
||||
resetForm() {
|
||||
this.form = {
|
||||
invoiceBillType: 'FROM_RECEIVABLE',
|
||||
invoiceTime: null,
|
||||
};
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
invoiceBillType: 'FROM_RECEIVABLE',
|
||||
invoiceStatus: null,
|
||||
};
|
||||
this.receivableOrdersWithPlans = [];
|
||||
this.selectedRows = [];
|
||||
this.total = 0;
|
||||
if (this.$refs.table) {
|
||||
this.$refs.table.clearSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.total-info {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,811 @@
|
|||
<template>
|
||||
<el-dialog title="申请开票" :visible.sync="visible" width="1000px" append-to-body @close="handleClose"
|
||||
custom-class="invoice-dialog">
|
||||
<el-form ref="form" :model="form" :rules="rules" :disabled="isRedRush" size="small" label-width="110px" class="invoice-form">
|
||||
|
||||
<!-- 1. 顶部:发票类型与票号日期 -->
|
||||
<div class="invoice-header">
|
||||
<div class="header-center">
|
||||
<div class="invoice-title">
|
||||
电子发票
|
||||
<span class="invoice-type-wrapper">
|
||||
(<el-select v-model="form.invoiceType" class="invoice-type-select" :popper-append-to-body="false">
|
||||
<el-option
|
||||
v-for="dict in dict.type.finance_invoice_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>)
|
||||
</span>
|
||||
</div>
|
||||
<div class="header-decoration-line"></div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="meta-row">
|
||||
<span class="meta-label">发票号码:</span>
|
||||
<span class="meta-value">----------</span>
|
||||
</div>
|
||||
<div class="meta-row">
|
||||
<span class="meta-label">开票日期:</span>
|
||||
<span class="meta-value">----------</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. 中部:购买方信息与销售方信息 -->
|
||||
<div class="invoice-body-border">
|
||||
<el-row type="flex" class="info-container">
|
||||
<!-- 左侧:购买方信息 -->
|
||||
<el-col :span="12" class="info-column left-column">
|
||||
<div class="column-label">购买方<br>信息</div>
|
||||
<div class="column-content">
|
||||
<el-form-item label="名称" prop="buyerName" class="condensed-item">
|
||||
<el-input v-model="form.buyerName" placeholder="请输入名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="统一社会信用代码/纳税人识别号" prop="buyerCreditCode"
|
||||
class="condensed-item label-wrap">
|
||||
<span slot="label" style="line-height: 1.2;">统一社会信用代码/<br>纳税人识别号</span>
|
||||
<el-input v-model="form.buyerCreditCode" placeholder="请输入纳税人识别号"/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧:销售方信息 -->
|
||||
<el-col :span="12" class="info-column right-column">
|
||||
<div class="column-label">销售方<br>信息</div>
|
||||
<div class="column-content">
|
||||
<el-form-item label="名称" prop="sellerName" class="condensed-item">
|
||||
<el-select
|
||||
v-model="form.sellerName"
|
||||
placeholder="请输入名称"
|
||||
filterable
|
||||
clearable
|
||||
@change="handleSellerChange"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in companyOptions"
|
||||
:key="item.id"
|
||||
:label="item.companyName"
|
||||
:value="item.companyName"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="统一社会信用代码/纳税人识别号" prop="sellerCreditCode"
|
||||
class="condensed-item label-wrap">
|
||||
<span slot="label" style="line-height: 1.2;">统一社会信用代码/<br>纳税人识别号</span>
|
||||
<el-input v-model="form.sellerCreditCode" placeholder="请输入纳税人识别号"/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 3. 表格区域 -->
|
||||
<div class="items-table-container">
|
||||
<el-table
|
||||
:data="form.detailItemList"
|
||||
border
|
||||
class="invoice-table"
|
||||
show-summary
|
||||
:summary-method="getSummaries"
|
||||
>
|
||||
<el-table-column label="项目名称" prop="productName" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.productName" size="mini" placeholder="请输入" class="no-border-input"
|
||||
:disabled="!!scope.row.productCode"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规格型号" prop="productModel" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.productModel" size="mini" placeholder="型号" class="no-border-input"
|
||||
:disabled="!!scope.row.productCode"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" prop="unit" align="center" width="60">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.unit" size="mini" placeholder="单位" class="no-border-input"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" prop="quantity" align="center" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.quantity" size="mini" class="no-border-input"
|
||||
oninput="value=value.replace(/[^\d.-]/g,'')"
|
||||
@input="calculateAmount(scope.row)"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单价" prop="unitPrice" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-if="!scope.row.productCode" v-model="scope.row.unitPrice" size="mini" placeholder="单价"
|
||||
class="no-border-input" @input="calculateAmount(scope.row)"/>
|
||||
<span v-else>{{ scope.row.unitPrice }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" prop="amount" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-if="!scope.row.productCode" v-model="scope.row.amount" size="mini" placeholder="金额"
|
||||
class="no-border-input" @input="calculateTax(scope.row)"/>
|
||||
<span v-else>{{ scope.row.amount }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="税率%" prop="taxRate" align="center" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-if="!scope.row.productCode" v-model="scope.row.taxRate" size="mini" placeholder="税率"
|
||||
class="no-border-input" @input="calculateTax(scope.row)"/>
|
||||
<span v-else>{{ scope.row.taxRate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="税额" prop="taxAmount" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-if="!scope.row.productCode" v-model="scope.row.taxAmount" size="mini" placeholder="税额"
|
||||
class="no-border-input"/>
|
||||
<span v-else>{{ scope.row.taxAmount }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="操作" width="60" align="center">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <i class="el-icon-plus table-icon" @click="addDetailRow"-->
|
||||
<!-- v-if="scope.$index === form.detailItemList.length - 1"></i>-->
|
||||
<!-- <i class="el-icon-minus table-icon" @click="removeDetailRow(scope.$index)"-->
|
||||
<!-- v-if="form.detailItemList.length > 1" style="margin-left:5px"></i>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 4. 价税合计 -->
|
||||
<div class="total-row">
|
||||
<div class="total-label">价税合计 (大写)</div>
|
||||
<div class="total-value-chinese">
|
||||
<span class="currency-symbol">⊗</span> {{ totalAmountChinese }}
|
||||
</div>
|
||||
<div class="total-label-small">(小写)</div>
|
||||
<div class="total-value-number">
|
||||
¥{{ totalAmountNumber }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5. 备注 -->
|
||||
<div class="remark-row">
|
||||
<div class="remark-label">备注</div>
|
||||
<div class="remark-content">
|
||||
<el-input type="textarea" :rows="2" v-model="form.remark" placeholder="请输入备注"
|
||||
class="no-border-textarea"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="loading">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getInvoiceProducts, applyInvoice, applyRefund} from "@/api/finance/invoice";
|
||||
import { listCompanyInfo } from "@/api/system/companyInfo";
|
||||
|
||||
export default {
|
||||
name: "ApplyInvoice",
|
||||
dicts: ['finance_invoice_type'],
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
rowData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
isRedRush: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
companyOptions: [],
|
||||
form: {
|
||||
invoiceType: this.rowData.invoiceType || '2',
|
||||
buyerName: undefined,
|
||||
buyerCreditCode: undefined,
|
||||
buyerBank: undefined,
|
||||
buyerBankAccount: undefined,
|
||||
sellerName: undefined,
|
||||
sellerCreditCode: undefined,
|
||||
sellerBank: undefined,
|
||||
sellerBankAccount: undefined,
|
||||
remark: undefined,
|
||||
invoiceBillCode: this.rowData.invoiceBillCode,
|
||||
id: this.rowData.id,
|
||||
detailItemList: [
|
||||
{
|
||||
projectName: '',
|
||||
productModel: '',
|
||||
unit: '',
|
||||
unitPrice: '',
|
||||
quantity: '',
|
||||
amount: '',
|
||||
taxRate: '13',
|
||||
taxAmount: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
rules: {
|
||||
invoiceType: [{required: true, message: "必填", trigger: "change"}],
|
||||
buyerName: [{required: true, message: "必填", trigger: "blur"}],
|
||||
buyerCreditCode: [{required: true, message: "必填", trigger: "blur"}],
|
||||
sellerName: [{required: true, message: "必填", trigger: "blur"}]
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getCompanyList();
|
||||
},
|
||||
computed: {
|
||||
totalAmountNumber() {
|
||||
let total = 0;
|
||||
this.form.detailItemList.forEach(item => {
|
||||
const amount = parseFloat(item.amount) || 0;
|
||||
// const tax = parseFloat(item.taxAmount) || 0;
|
||||
total += amount ;
|
||||
});
|
||||
return total.toFixed(2);
|
||||
},
|
||||
totalAmountChinese() {
|
||||
return this.convertCurrency(this.totalAmountNumber);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val) {
|
||||
this.reset();
|
||||
this.initData();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCompanyList() {
|
||||
listCompanyInfo().then(response => {
|
||||
this.companyOptions = response.rows;
|
||||
if (this.visible && !this.form.sellerName && this.companyOptions.length > 0) {
|
||||
const defaultCompany = this.companyOptions[0];
|
||||
this.form.sellerName = defaultCompany.companyName;
|
||||
this.handleSellerChange(defaultCompany.companyName);
|
||||
}
|
||||
});
|
||||
},
|
||||
reset() {
|
||||
this.form = {
|
||||
invoiceType: '2',
|
||||
buyerName: undefined,
|
||||
buyerCreditCode: undefined,
|
||||
buyerBank: undefined,
|
||||
buyerBankAccount: undefined,
|
||||
sellerName: undefined,
|
||||
sellerCreditCode: undefined,
|
||||
sellerBank: undefined,
|
||||
sellerBankAccount: undefined,
|
||||
remark: undefined,
|
||||
detailItemList: []
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
initData() {
|
||||
if (this.rowData) {
|
||||
this.form.buyerName = this.rowData.buyerName || this.rowData.partnerName;
|
||||
this.form.buyerCreditCode = this.rowData.buyerCreditCode ;
|
||||
this.form.buyerBank = this.rowData.buyerBank ;
|
||||
this.form.buyerBankAccount = this.rowData.buyerBankAccount ;
|
||||
|
||||
if (this.rowData.sellerName) {
|
||||
this.form.sellerName = this.rowData.sellerName;
|
||||
this.form.sellerCreditCode = this.rowData.sellerCreditCode;
|
||||
this.form.sellerBank = this.rowData.sellerBank;
|
||||
this.form.sellerBankAccount = this.rowData.sellerBankAccount;
|
||||
} else if (this.companyOptions.length > 0) {
|
||||
const defaultCompany = this.companyOptions[0];
|
||||
this.form.sellerName = defaultCompany.companyName;
|
||||
this.handleSellerChange(defaultCompany.companyName);
|
||||
}
|
||||
|
||||
this.form.invoiceBillCode = this.rowData.invoiceBillCode;
|
||||
this.form.id = this.rowData.id;
|
||||
|
||||
this.updateRemark();
|
||||
|
||||
if (this.rowData.invoiceBillCode) {
|
||||
getInvoiceProducts(this.rowData.invoiceBillCode).then(response => {
|
||||
if (response.data && response.data.length > 0) {
|
||||
this.form.detailItemList = response.data.map(item => {
|
||||
let quantity = item.quantity;
|
||||
let amount = item.allPrice;
|
||||
let taxAmount = item.taxAmount;
|
||||
|
||||
if (this.isRedRush) {
|
||||
quantity = -Math.abs(quantity);
|
||||
amount = -Math.abs(amount);
|
||||
taxAmount = -Math.abs(taxAmount);
|
||||
}
|
||||
|
||||
const row = {
|
||||
projectName: item.projectName,
|
||||
id: item.id,
|
||||
orderCode: item.orderCode,
|
||||
productCode: item.productCode,
|
||||
productModel: item.productModel, // Mapping projectCode to productModel as requested
|
||||
productName: item.productName, // Mapping projectCode to productModel as requested
|
||||
unit: item.unit || '',
|
||||
quantity: quantity,
|
||||
unitPrice: item.price, // Mapping price to unitPrice
|
||||
taxRate: item.taxRate,
|
||||
amount: amount,
|
||||
taxAmount: taxAmount
|
||||
};
|
||||
// Calculate initial amounts
|
||||
// this.calculateAmount(row);
|
||||
return row;
|
||||
});
|
||||
} else {
|
||||
this.addDetailRow(); // Default empty row if no data
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.addDetailRow();
|
||||
}
|
||||
}
|
||||
},
|
||||
handleSellerChange(val) {
|
||||
const company = this.companyOptions.find(item => item.companyName === val);
|
||||
if (company) {
|
||||
this.form.sellerCreditCode = company.socialCredit;
|
||||
this.form.sellerBank = company.payBankOpenAddress;
|
||||
this.form.sellerBankAccount = company.payBankNumber;
|
||||
this.updateRemark();
|
||||
}
|
||||
},
|
||||
updateRemark() {
|
||||
const bBank = this.form.buyerBank || '';
|
||||
const bAccount = this.form.buyerBankAccount || '';
|
||||
const sBank = this.form.sellerBank || '';
|
||||
const sAccount = this.form.sellerBankAccount || '';
|
||||
this.form.remark = `购方开户银行 : ${bBank} 银行账号 ${bAccount}\n销方开户银行 ${sBank} 银行账号 ${sAccount}`;
|
||||
},
|
||||
handleClose() {
|
||||
this.$emit("update:visible", false);
|
||||
},
|
||||
addDetailRow() {
|
||||
this.form.detailItemList.push({
|
||||
projectName: '',
|
||||
productModel: '',
|
||||
unit: '',
|
||||
unitPrice: '',
|
||||
quantity: '',
|
||||
amount: '',
|
||||
taxRate: '13',
|
||||
taxAmount: ''
|
||||
});
|
||||
},
|
||||
removeDetailRow(index) {
|
||||
this.form.detailItemList.splice(index, 1);
|
||||
},
|
||||
calculateAmount(row) {
|
||||
if (row.productCode){
|
||||
return
|
||||
}
|
||||
if (row.unitPrice && row.quantity) {
|
||||
row.amount = this.$calc.mul(row.unitPrice, row.quantity);
|
||||
this.calculateTax(row);
|
||||
}
|
||||
},
|
||||
calculateTax(row) {
|
||||
if (row.amount && row.taxRate != null) {
|
||||
row.taxAmount = this.$calc.mul(row.amount, this.$calc.div(row.taxRate, 100));
|
||||
}
|
||||
},
|
||||
getSummaries(param) {
|
||||
const {columns, data} = param;
|
||||
const sums = [];
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '合计';
|
||||
return;
|
||||
}
|
||||
if (column.property === 'amount' || column.property === 'taxAmount') {
|
||||
const values = data.map(item => Number(item[column.property]));
|
||||
if (!values.every(value => isNaN(value))) {
|
||||
const sum = values.reduce((prev, curr) => {
|
||||
const value = Number(curr);
|
||||
if (!isNaN(value)) {
|
||||
return prev + curr;
|
||||
} else {
|
||||
return prev;
|
||||
}
|
||||
}, 0);
|
||||
sums[index] = '¥' + sum.toFixed(2);
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
});
|
||||
return sums;
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
// 校验表格明细
|
||||
if (this.form.detailItemList.length === 0) {
|
||||
this.$modal.msgError("请至少添加一条开票明细");
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < this.form.detailItemList.length; i++) {
|
||||
const item = this.form.detailItemList[i];
|
||||
if (!item.productName || !item.productModel || isNaN(item.quantity) || !item.unit || !item.unitPrice || !item.amount || !item.taxAmount || !item.taxRate) {
|
||||
this.$modal.msgError(`表格第 ${i + 1} 行数据不完整,请填写所有必填字段`);
|
||||
return;
|
||||
}
|
||||
item.invoiceBillCode=this.rowData.invoiceBillCode;
|
||||
item.price=item.unitPrice;
|
||||
item.allPrice=item.amount;
|
||||
}
|
||||
this.loading = true;
|
||||
const apiCall = this.isRedRush ? applyRefund : applyInvoice;
|
||||
apiCall(this.form).then(response => {
|
||||
this.$modal.msgSuccess(this.isRedRush ? "申请红冲成功" : "申请提交成功");
|
||||
this.loading = false;
|
||||
this.$emit("update:visible", false);
|
||||
this.$emit("submit", this.form);
|
||||
}).catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
convertCurrency(money) {
|
||||
// 简单的数字转中文大写实现
|
||||
const cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
|
||||
const cnIntRadice = ['', '拾', '佰', '仟'];
|
||||
const cnIntUnits = ['', '万', '亿', '兆'];
|
||||
const cnDecUnits = ['角', '分', '毫', '厘'];
|
||||
const cnInteger = '整';
|
||||
const cnIntLast = '元';
|
||||
|
||||
let integerNum;
|
||||
let decimalNum;
|
||||
let chineseStr = '';
|
||||
let parts;
|
||||
let prefix = '';
|
||||
|
||||
if (money === '') {
|
||||
return '';
|
||||
}
|
||||
money = parseFloat(money);
|
||||
if (money < 0) {
|
||||
prefix = '(负数)';
|
||||
money = Math.abs(money);
|
||||
}
|
||||
if (money >= 999999999999) {
|
||||
return '';
|
||||
}
|
||||
if (money === 0) {
|
||||
chineseStr = cnNums[0] + cnIntLast + cnInteger;
|
||||
return chineseStr;
|
||||
}
|
||||
|
||||
money = money.toString();
|
||||
if (money.indexOf('.') === -1) {
|
||||
integerNum = money;
|
||||
decimalNum = '';
|
||||
} else {
|
||||
parts = money.split('.');
|
||||
integerNum = parts[0];
|
||||
decimalNum = parts[1].substr(0, 4);
|
||||
}
|
||||
|
||||
if (parseInt(integerNum, 10) > 0) {
|
||||
let zeroCount = 0;
|
||||
let IntLen = integerNum.length;
|
||||
for (let i = 0; i < IntLen; i++) {
|
||||
let n = integerNum.substr(i, 1);
|
||||
let p = IntLen - i - 1;
|
||||
let q = p / 4;
|
||||
let m = p % 4;
|
||||
if (n === '0') {
|
||||
zeroCount++;
|
||||
} else {
|
||||
if (zeroCount > 0) {
|
||||
chineseStr += cnNums[0];
|
||||
}
|
||||
zeroCount = 0;
|
||||
chineseStr += cnNums[parseInt(n)] + cnIntRadice[m];
|
||||
}
|
||||
if (m === 0 && zeroCount < 4) {
|
||||
chineseStr += cnIntUnits[q];
|
||||
}
|
||||
}
|
||||
chineseStr += cnIntLast;
|
||||
}
|
||||
|
||||
if (decimalNum !== '') {
|
||||
let decLen = decimalNum.length;
|
||||
for (let i = 0; i < decLen; i++) {
|
||||
let n = decimalNum.substr(i, 1);
|
||||
if (n !== '0') {
|
||||
chineseStr += cnNums[Number(n)] + cnDecUnits[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (chineseStr === '') {
|
||||
chineseStr += cnNums[0] + cnIntLast + cnInteger;
|
||||
} else if (decimalNum === '') {
|
||||
chineseStr += cnInteger;
|
||||
}
|
||||
return prefix + chineseStr;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.invoice-dialog ::v-deep .el-dialog__body {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.invoice-header {
|
||||
position: relative;
|
||||
height: 80px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header-center {
|
||||
text-align: center;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.invoice-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #8B4513;
|
||||
line-height: 40px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.invoice-type-wrapper {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.invoice-type-select {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
/* Hide select border to blend in */
|
||||
.invoice-type-select ::v-deep .el-input__inner {
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #8B4513;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.invoice-type-select ::v-deep .el-select__caret {
|
||||
color: #8B4513;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.header-decoration-line {
|
||||
height: 2px;
|
||||
background-color: #8B4513;
|
||||
width: 380px;
|
||||
margin: 0 auto;
|
||||
border-bottom: 1px solid #8B4513; /* 双线效果 */
|
||||
box-shadow: 0 2px 0 0 #fff, 0 4px 0 0 #8B4513;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.meta-row {
|
||||
font-size: 13px;
|
||||
color: #8B4513;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.meta-label {
|
||||
display: inline-block;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.invoice-body-border {
|
||||
border: 2px solid #8B4513;
|
||||
padding: 0;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.info-container {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #8B4513;
|
||||
}
|
||||
|
||||
.info-column {
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.left-column {
|
||||
border-right: 1px solid #8B4513;
|
||||
}
|
||||
|
||||
.column-label {
|
||||
width: 30px;
|
||||
padding: 0 5px;
|
||||
color: #8B4513;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
border-right: 1px solid #8B4513;
|
||||
margin-right: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.column-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.condensed-item {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.condensed-item ::v-deep .el-form-item__error {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.condensed-item ::v-deep .el-form-item__label {
|
||||
color: #8B4513;
|
||||
font-size: 13px;
|
||||
padding-right: 8px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
/* Fix for multiline label */
|
||||
.label-wrap ::v-deep .el-form-item__label {
|
||||
line-height: 16px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.condensed-item ::v-deep .el-input__inner {
|
||||
border-color: #dcdfe6;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
/* Table Styling */
|
||||
.items-table-container {
|
||||
padding: 0;
|
||||
border-bottom: 1px solid #8B4513;
|
||||
}
|
||||
|
||||
.invoice-table ::v-deep th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.invoice-table ::v-deep .el-table__footer-wrapper tbody td,
|
||||
.invoice-table ::v-deep .el-table__header-wrapper tbody td {
|
||||
background-color: #fff;
|
||||
color: #8B4513;
|
||||
border-color: #8B4513;
|
||||
}
|
||||
|
||||
.invoice-table ::v-deep td, .invoice-table ::v-deep th.is-leaf {
|
||||
border-bottom: 1px solid #8B4513;
|
||||
border-right: 1px solid #8B4513;
|
||||
}
|
||||
|
||||
.invoice-table ::v-deep .el-table--border, .invoice-table ::v-deep .el-table--group {
|
||||
border-color: #8B4513;
|
||||
}
|
||||
|
||||
.no-border-input ::v-deep .el-input__inner {
|
||||
border: none;
|
||||
text-align: center;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.table-icon {
|
||||
cursor: pointer;
|
||||
color: #8B4513;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.total-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #8B4513;
|
||||
height: 40px;
|
||||
padding: 0 10px;
|
||||
color: #8B4513;
|
||||
}
|
||||
|
||||
.total-label {
|
||||
width: 120px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.total-value-chinese {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.currency-symbol {
|
||||
border: 1px solid #8B4513;
|
||||
border-radius: 50%;
|
||||
padding: 1px 4px;
|
||||
font-size: 12px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.total-label-small {
|
||||
width: 60px;
|
||||
text-align: right;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.total-value-number {
|
||||
width: 150px;
|
||||
padding-left: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.remark-row {
|
||||
display: flex;
|
||||
min-height: 60px;
|
||||
padding: 0 10px;
|
||||
color: #8B4513;
|
||||
}
|
||||
|
||||
.remark-label {
|
||||
width: 120px;
|
||||
padding-top: 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.remark-content {
|
||||
flex: 1;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.no-border-textarea ::v-deep .el-textarea__inner {
|
||||
border: none;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.invoice-table ::v-deep .el-table__body-wrapper {
|
||||
height: 250px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
<template>
|
||||
<el-drawer
|
||||
:title="detail.invoiceBillType==='RED_RUSH'?'红冲开票详情':'开票单详情'"
|
||||
:visible.sync="visible"
|
||||
direction="rtl"
|
||||
size="70%"
|
||||
@close="handleClose"
|
||||
>
|
||||
<div class="dialog-body" v-if="detail">
|
||||
<div class="section">
|
||||
<el-divider content-position="left">销售-开票单</el-divider>
|
||||
<div class="details-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">销售-开票单编号: {{ detail.invoiceBillCode }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">预计开票时间: {{ detail.createTime }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">进货商名称: {{ detail.partnerName }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">含税总价(元): <span :class="{'red-text':detail.invoiceBillType==='RED_RUSH'}">{{ formatCurrency(detail.totalPriceWithTax) }}</span></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">未税总价(元): <span :class="{'red-text':detail.invoiceBillType==='RED_RUSH'}">{{ formatCurrency(detail.totalPriceWithoutTax) }} </span></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">税额(元): <span :class="{'red-text':detail.invoiceBillType==='RED_RUSH'}">{{ formatCurrency($calc.sub(detail.totalPriceWithTax,detail.totalPriceWithoutTax)) }} </span></div>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">发票含税总价(元): <span :class="{'red-text':detail.invoiceBillType==='RED_RUSH'}"> {{ formatCurrency(detail.invoicePriceWithTax) }}</span></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">发票未税总价(元): <span :class="{'red-text':detail.invoiceBillType==='RED_RUSH'}"{{ formatCurrency(detail.invoicePriceWithoutTax) }}</span></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">发票税额(元): <span :class="{'red-text':detail.invoiceBillType==='RED_RUSH'}"{{ formatCurrency($calc.sub(detail.invoicePriceWithTax,detail.invoicePriceWithoutTax)) }}</span></div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">开票状态:
|
||||
<dict-tag :options="dict.type.invoice_bill_status" :value="detail.invoiceStatus"/>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">审批节点: {{ detail.approveNode }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">审批状态:
|
||||
<dict-tag :options="dict.type.approve_status" :value="detail.approveStatus"/></div>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">审批通过时间 :{{detail.approveTime}}
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<div class="detail-item">票据类型:
|
||||
<span class="item-value"><dict-tag :options="dict.type.finance_invoice_type" :value="detail.invoiceType"/></span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">购买方信息_名称: {{ detail.buyerName }}</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">购买方信息_统一社会信用代码/纳税人识别号: {{ detail.buyerCreditCode }}</div>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">购买方信息_购方开户银行: {{ detail.buyerBank }}</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">购买方信息_银行账号: {{ detail.buyerBankAccount }}</div>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">销售方信息_名称: {{ detail.sellerName }}</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">销售方信息_统一社会信用代码/纳税人识别号: {{ detail.sellerCreditCode }}</div>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">销售方信息_销方开户银行: {{ detail.sellerBank }}</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">销售方信息_银行账号: {{ detail.sellerBankAccount }}</div>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<el-divider content-position="left">销售-应收单</el-divider>
|
||||
<el-table :data="detail.detailDTOList">
|
||||
<el-table-column type="index" label="序号" width="50"></el-table-column>
|
||||
<!-- <el-table-column property="projectCode" label="项目编号"></el-table-column>-->
|
||||
<el-table-column property="projectName" label="项目名称"></el-table-column>
|
||||
<el-table-column property="receivableBillCode" label="销售-应收单编号"></el-table-column>
|
||||
<el-table-column property="totalPriceWithTax" label="含税总价(元)"></el-table-column>
|
||||
<el-table-column property="receiptAmount" label="本次开票金额">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatCurrency(Math.abs(scope.row.receiptAmount)) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="receiptRate" label="本次开票比例(%)">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ Math.abs(scope.row.receiptRate) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DetailDrawer",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => null,
|
||||
},
|
||||
},
|
||||
dicts:['finance_invoice_type','approve_status','invoice_bill_status'],
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.$emit("update:visible", false);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: calc(100vh - 50px);
|
||||
overflow-y: auto;
|
||||
padding: 0 20px 20px 20px;
|
||||
}
|
||||
|
||||
.details-container {
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.red-text{
|
||||
color: red;
|
||||
}
|
||||
.section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,667 @@
|
|||
<template>
|
||||
<el-dialog :title="titleText" :visible.sync="dialogVisible" width="900px" @close="handleClose">
|
||||
<div v-if="loading" class="loading-spinner">
|
||||
<i class="el-icon-loading"></i>
|
||||
</div>
|
||||
<div v-else class="receipt-dialog-body">
|
||||
<div v-if="canUpload" class="upload-btn-container">
|
||||
<el-button type="primary" v-hasPermi="['finance:invoice:upload']"icon="el-icon-upload" @click="openUploadDialog">上传{{ titleText }}</el-button>
|
||||
</div>
|
||||
|
||||
<el-timeline v-if="groupedAttachments.length > 0">
|
||||
<el-timeline-item
|
||||
v-for="group in groupedAttachments"
|
||||
:key="group.createTime"
|
||||
:timestamp="parseTime(group.createTime, '{y}-{m}-{d} {h}:{i}:{s}')"
|
||||
placement="top"
|
||||
>
|
||||
<el-card>
|
||||
<div class="receipt-card-content">
|
||||
<div class="receipt-details">
|
||||
<div class="detail-item">
|
||||
<span class="item-label">票据类型</span>
|
||||
<span class="item-value"><dict-tag :options="dict.type.finance_invoice_type" :value="invoiceData.invoiceType"/></span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">{{ titleText }}</span>
|
||||
<div class="item-value" style="display: block;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 10px;">
|
||||
<div v-for="attachment in group.items" :key="attachment.id" class="attachment-item">
|
||||
<div class="image-wrapper">
|
||||
<el-image
|
||||
v-if="!isPdf(attachment.filePath)"
|
||||
:src="getImageUrl(attachment.filePath)"
|
||||
:preview-src-list="previewList"
|
||||
style="width: 200px; height: 150px;"
|
||||
fit="contain"
|
||||
></el-image>
|
||||
<div v-else-if="pdfUrls[attachment.filePath]" class="pdf-thumbnail-container" @click="openPdfPreview(pdfUrls[attachment.filePath])">
|
||||
<iframe :src="pdfUrls[attachment.filePath]" width="100%" height="150px" frameborder="0"></iframe>
|
||||
<div class="pdf-hover-overlay">
|
||||
<i class="el-icon-zoom-in"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="attachment.delFlag === '2'" class="void-overlay">作废</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="primary"
|
||||
class="download-btn"
|
||||
icon="el-icon-download"
|
||||
@click="downloadFiles(group.items)"
|
||||
style="margin-top: 10px;"
|
||||
>下载{{ titleText }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-item">
|
||||
<span class="item-label">含税金额</span>
|
||||
<span class="item-value">{{ formatCurrency(invoiceData.invoicePriceWithTax) }}</span>
|
||||
</div>
|
||||
<!-- <div class="detail-item">
|
||||
<span class="item-label">发票含税金额</span>
|
||||
<span class="item-value">{{ invoiceData.ticketPriceWithTax }}</span>
|
||||
</div> -->
|
||||
<div class="detail-item">
|
||||
<span class="item-label">未税金额</span>
|
||||
<span class="item-value">{{ formatCurrency(invoiceData.invoicePriceWithoutTax) }}</span>
|
||||
</div>
|
||||
<!-- <div class="detail-item">
|
||||
<span class="item-label">发票未税金额</span>
|
||||
<span class="item-value">{{ invoiceData.ticketPriceWithoutTax }}</span>
|
||||
</div> -->
|
||||
<div class="detail-item">
|
||||
<span class="item-label">税额</span>
|
||||
<span class="item-value">{{ formatCurrency(invoiceData.taxAmount) }}</span>
|
||||
</div>
|
||||
<!-- <div class="detail-item">
|
||||
<span class="item-label">发票税额</span>
|
||||
<span class="item-value">{{ invoiceData.ticketAmount }}</span>
|
||||
</div> -->
|
||||
<div class="detail-item">
|
||||
<span class="item-label">备注</span>
|
||||
<span class="item-value">{{ group.remark }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<el-empty v-else :description="'暂无' + titleText"></el-empty>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">关闭</el-button>
|
||||
</span>
|
||||
|
||||
<!-- PDF Preview Dialog -->
|
||||
<el-dialog
|
||||
:visible.sync="pdfPreviewVisible"
|
||||
width="80%"
|
||||
top="5vh"
|
||||
append-to-body
|
||||
custom-class="pdf-preview-dialog"
|
||||
>
|
||||
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||
</el-dialog>
|
||||
|
||||
<!-- Upload Dialog -->
|
||||
<el-dialog
|
||||
:title="'上传' + titleText"
|
||||
:visible.sync="uploadDialogVisible"
|
||||
width="70vw"
|
||||
append-to-body
|
||||
@close="closeUploadDialog"
|
||||
custom-class="upload-receipt-dialog"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form :model="uploadForm" ref="uploadForm" :rules="rules" label-width="120px" size="medium" >
|
||||
<el-form-item label="票据类型" prop="invoiceType" required>
|
||||
<dict-tag :options="dict.type.finance_invoice_type" :value="uploadForm.invoiceType"/>
|
||||
<!-- <el-select v-model="uploadForm.invoiceType" placeholder="请选择票据类型" @change="handleTypeChange">-->
|
||||
<!-- -->
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="item in dict.type.finance_invoice_type"-->
|
||||
<!-- :key="item.value"-->
|
||||
<!-- :label="item.label"-->
|
||||
<!-- :value="item.value"-->
|
||||
<!-- ></el-option>-->
|
||||
<!-- </el-select>-->
|
||||
</el-form-item>
|
||||
<el-form-item label="开票附件" prop="file" >
|
||||
<div style="display: flex; flex-direction: column; align-items: flex-start;">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:on-remove="handleFileRemove"
|
||||
:file-list="fileList"
|
||||
multiple
|
||||
:show-file-list="true"
|
||||
accept=".jpg,.jpeg,.png,.pdf"
|
||||
>
|
||||
<el-button size="small" type="primary" icon="el-icon-upload2">点击上传</el-button>
|
||||
</el-upload>
|
||||
<div class="el-upload__tip" style="line-height: 1.5; margin-top: 5px;">支持上传PNG、JPG、PDF文件格式</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="含税总价">
|
||||
<span>{{ invoiceData.totalPriceWithTax }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="发票含税总价" prop="invoicePriceWithTax" required>
|
||||
<el-input v-model="uploadForm.invoicePriceWithTax" placeholder="请输入发票含税总价"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="未税总价">
|
||||
<span>{{ invoiceData.totalPriceWithoutTax }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="发票未税总价" prop="invoicePriceWithoutTax" required>
|
||||
<el-input v-model="uploadForm.invoicePriceWithoutTax" placeholder="请输入发票未税总价" @input="handleWithoutTaxChange"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="税额">
|
||||
<span>{{ invoiceData.taxAmount }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="发票税额" prop="invoiceAmount" required>
|
||||
<el-input v-model="uploadForm.invoiceAmount" placeholder="请输入发票税额" @input="handleAmountChange"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="uploadForm.remark"
|
||||
:rows="4"
|
||||
placeholder="此处备注描述..."
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="upload-preview-container" style="height: 70vh; overflow-y: auto; display: block; padding: 10px;">
|
||||
<div v-if="fileList.length > 0" class="preview-list">
|
||||
<div v-for="(file, index) in fileList" :key="index" class="preview-item" style="margin-bottom: 20px; border-bottom: 1px dashed #ccc; padding-bottom: 10px;">
|
||||
<div class="preview-content" style="height: 350px;">
|
||||
<img v-if="!isPdf(file.name)" :src="file.url" class="preview-image" style="max-width: 100%; max-height: 100%; object-fit: contain;" />
|
||||
<iframe v-else :src="file.url" width="100%" height="100%" frameborder="0"></iframe>
|
||||
</div>
|
||||
<div class="file-info" style="margin-top: 5px; text-align: center; color: #666; font-size: 12px;">{{ file.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="preview-placeholder" style="height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center;">
|
||||
<div class="placeholder-icon">
|
||||
<i class="el-icon-picture"></i>
|
||||
</div>
|
||||
<div class="placeholder-text">上传文件后在此预览</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitNewUpload">保存</el-button>
|
||||
<el-button @click="closeUploadDialog">取消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getInvoiceAttachments, uploadInvoiceAttachment } from "@/api/finance/invoice";
|
||||
import request from '@/utils/request';
|
||||
|
||||
export default {
|
||||
name: "InvoiceDialog",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
invoiceData: {
|
||||
type: Object,
|
||||
default: () => null,
|
||||
},
|
||||
dicts: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
dicts:['finance_invoice_type'],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
attachments: [],
|
||||
// Upload Dialog Data
|
||||
uploadDialogVisible: false,
|
||||
uploadForm: {
|
||||
invoicePriceWithTax: '',
|
||||
invoicePriceWithoutTax: '',
|
||||
invoiceAmount: '',
|
||||
invoiceType: this.invoiceData.invoiceType,
|
||||
remark: '',
|
||||
},
|
||||
fileList: [],
|
||||
rules: {
|
||||
invoicePriceWithTax: [
|
||||
{ required: true, message: "请输入发票含税总价", trigger: "blur" }
|
||||
],
|
||||
invoicePriceWithoutTax: [
|
||||
{ required: true, message: "请输入发票未税总价", trigger: "blur" }
|
||||
],
|
||||
invoiceAmount: [
|
||||
{ required: true, message: "请输入发票税额", trigger: "blur" }
|
||||
]
|
||||
},
|
||||
// PDF Preview Data
|
||||
pdfUrls: {},
|
||||
pdfPreviewVisible: false,
|
||||
currentPdfUrl: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
dialogVisible: {
|
||||
get() {
|
||||
return this.visible;
|
||||
},
|
||||
set(val) {
|
||||
this.$emit("update:visible", val);
|
||||
},
|
||||
},
|
||||
previewList() {
|
||||
return this.attachments
|
||||
.filter(att => !this.isPdf(att.filePath))
|
||||
.map(att => this.getImageUrl(att.filePath));
|
||||
},
|
||||
groupedAttachments() {
|
||||
if (!this.attachments || this.attachments.length === 0) return [];
|
||||
|
||||
const groups = {};
|
||||
this.attachments.forEach(att => {
|
||||
const time = att.createTime;
|
||||
if (!groups[time]) {
|
||||
groups[time] = {
|
||||
createTime: time,
|
||||
items: [],
|
||||
remark: att.remark
|
||||
};
|
||||
}
|
||||
groups[time].items.push(att);
|
||||
});
|
||||
|
||||
return Object.values(groups).sort((a, b) => new Date(b.createTime) - new Date(a.createTime));
|
||||
},
|
||||
canUpload() {
|
||||
if (!this.attachments || this.attachments.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return this.attachments.every(att => att.delFlag === '2');
|
||||
},
|
||||
titleText() {
|
||||
return '发票';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val && this.invoiceData) {
|
||||
this.fetchAttachments();
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fetchAttachments() {
|
||||
if (!this.invoiceData.id) return;
|
||||
this.loading = true;
|
||||
getInvoiceAttachments(this.invoiceData.id, { type: 'invoice' })
|
||||
.then(response => {
|
||||
const data = response.data || [];
|
||||
// data.sort((a, b) => new Date(b.createTime) - new Date(a.createTime)); // Sorting is handled in groupedAttachments
|
||||
this.attachments = data;
|
||||
this.loadPdfPreviews();
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
this.attachments = [];
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
loadPdfPreviews() {
|
||||
this.attachments.forEach(att => {
|
||||
if (this.isPdf(att.filePath) && !this.pdfUrls[att.filePath]) {
|
||||
request({
|
||||
url: '/common/download/resource',
|
||||
method: 'get',
|
||||
params: { resource: att.filePath },
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
this.$set(this.pdfUrls, att.filePath, url);
|
||||
}).catch(console.error);
|
||||
}
|
||||
});
|
||||
},
|
||||
openPdfPreview(url) {
|
||||
if (!url) return;
|
||||
this.currentPdfUrl = url;
|
||||
this.pdfPreviewVisible = true;
|
||||
},
|
||||
getImageUrl(resource) {
|
||||
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||
},
|
||||
isPdf(filePath) {
|
||||
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
downloadFiles(items) {
|
||||
if (!items || items.length === 0) return;
|
||||
items.forEach(item => {
|
||||
this.downloadFile(item);
|
||||
});
|
||||
},
|
||||
downloadFile(attachment) {
|
||||
const link = document.createElement('a');
|
||||
link.href = this.getImageUrl(attachment.filePath);
|
||||
link.download = attachment.fileName || 'receipt';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
},
|
||||
handleClose() {
|
||||
this.attachments = [];
|
||||
// Clean up object URLs
|
||||
Object.values(this.pdfUrls).forEach(url => URL.revokeObjectURL(url));
|
||||
this.pdfUrls = {};
|
||||
},
|
||||
// New Upload Dialog Methods
|
||||
openUploadDialog() {
|
||||
this.uploadForm = {
|
||||
invoicePriceWithTax: '',
|
||||
invoicePriceWithoutTax: '',
|
||||
invoiceAmount: '',
|
||||
invoiceType: this.invoiceData.invoiceType,
|
||||
remark: '',
|
||||
};
|
||||
if (this.$refs.uploadForm) {
|
||||
this.$refs.uploadForm.clearValidate();
|
||||
}
|
||||
this.fileList = [];
|
||||
this.uploadDialogVisible = true;
|
||||
},
|
||||
closeUploadDialog() {
|
||||
this.uploadDialogVisible = false;
|
||||
this.fileList.forEach(file => {
|
||||
if (file.url) URL.revokeObjectURL(file.url);
|
||||
});
|
||||
this.fileList = [];
|
||||
},
|
||||
handleTypeChange(val) {
|
||||
if (val === '2') {
|
||||
this.uploadForm.invoiceAmount = '0';
|
||||
this.handleAmountChange('0');
|
||||
}
|
||||
},
|
||||
handleWithoutTaxChange(val) {
|
||||
const total = parseFloat(this.uploadForm.invoicePriceWithTax);
|
||||
const withoutTax = parseFloat(val);
|
||||
if (!isNaN(total) && !isNaN(withoutTax)) {
|
||||
this.uploadForm.invoiceAmount = (total - withoutTax).toFixed(2);
|
||||
}
|
||||
},
|
||||
handleAmountChange(val) {
|
||||
const total = parseFloat(this.uploadForm.invoicePriceWithTax);
|
||||
const tax = parseFloat(val);
|
||||
if (!isNaN(total) && !isNaN(tax)) {
|
||||
this.uploadForm.invoicePriceWithoutTax = (total - tax).toFixed(2);
|
||||
}
|
||||
},
|
||||
handleFileChange(file, fileList) {
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
const isAcceptedType = ['image/jpeg', 'image/png', 'application/pdf'].includes(file.raw.type);
|
||||
|
||||
if (!isAcceptedType) {
|
||||
this.$message.error('上传文件只能是 JPG/PNG/PDF 格式!');
|
||||
const index = fileList.indexOf(file);
|
||||
if (index > -1) fileList.splice(index, 1);
|
||||
return;
|
||||
}
|
||||
if (!isLt2M) {
|
||||
this.$message.error('上传文件大小不能超过 2MB!');
|
||||
const index = fileList.indexOf(file);
|
||||
if (index > -1) fileList.splice(index, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
file.url = URL.createObjectURL(file.raw);
|
||||
this.fileList = fileList;
|
||||
},
|
||||
handleFileRemove(file, fileList) {
|
||||
if (file.url) URL.revokeObjectURL(file.url);
|
||||
this.fileList = fileList;
|
||||
},
|
||||
submitNewUpload() {
|
||||
this.$refs.uploadForm.validate(valid => {
|
||||
if (valid) {
|
||||
if (this.fileList.length === 0) {
|
||||
this.$message.warning("请选择要上传的文件");
|
||||
return;
|
||||
}
|
||||
|
||||
const limitTotal = parseFloat(this.invoiceData.totalPriceWithTax);
|
||||
const inputTotal = parseFloat(this.uploadForm.invoicePriceWithTax);
|
||||
const inputWithoutTax = parseFloat(this.uploadForm.invoicePriceWithoutTax);
|
||||
const inputTax = parseFloat(this.uploadForm.invoiceAmount);
|
||||
|
||||
if (!isNaN(limitTotal)) {
|
||||
if (limitTotal >= 0) {
|
||||
if (!isNaN(inputTotal) && inputTotal > limitTotal) {
|
||||
this.$message.warning("发票含税总价不能超过开票单含税总价");
|
||||
return;
|
||||
}
|
||||
if (!isNaN(inputWithoutTax) && inputWithoutTax < 0) {
|
||||
this.$message.warning("发票未税总价不能为负数");
|
||||
return;
|
||||
}
|
||||
if (!isNaN(inputTax) && inputTax < 0) {
|
||||
this.$message.warning("发票税额不能为负数");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!isNaN(inputTotal) && inputTotal < limitTotal) {
|
||||
this.$message.warning("发票含税总价不能小于开票单含税总价");
|
||||
return;
|
||||
}
|
||||
if (!isNaN(inputWithoutTax) && inputWithoutTax > 0) {
|
||||
this.$message.warning("发票未税总价不能为正数");
|
||||
return;
|
||||
}
|
||||
if (!isNaN(inputTax) && inputTax > 0) {
|
||||
this.$message.warning("发票税额不能为正数");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((this.fileList||[]).length <=0 ){
|
||||
this.$message.warning("文件不能为空");
|
||||
return;
|
||||
}
|
||||
const formData = new FormData();
|
||||
this.fileList.forEach(file => {
|
||||
formData.append("file", file.raw);
|
||||
});
|
||||
formData.append("id", this.invoiceData.id);
|
||||
formData.append("remark", this.uploadForm.remark);
|
||||
formData.append("invoicePriceWithTax", this.uploadForm.invoicePriceWithTax);
|
||||
formData.append("invoicePriceWithoutTax", this.uploadForm.invoicePriceWithoutTax);
|
||||
formData.append("invoiceAmount", this.uploadForm.invoiceAmount);
|
||||
formData.append("invoiceType", this.uploadForm.invoiceType);
|
||||
this.invoiceData.invoiceType=this.uploadForm.invoiceType
|
||||
uploadInvoiceAttachment(formData)
|
||||
.then(response => {
|
||||
this.$message.success("上传成功");
|
||||
this.closeUploadDialog();
|
||||
this.fetchAttachments();
|
||||
})
|
||||
.catch(error => {
|
||||
this.$message.error("上传失败");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.receipt-dialog-body {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.loading-spinner {
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
padding: 20px;
|
||||
}
|
||||
.receipt-card-content {
|
||||
display: flex;
|
||||
}
|
||||
.receipt-details {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.detail-item {
|
||||
display: flex;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.item-label {
|
||||
width: 110px;
|
||||
color: #606266;
|
||||
text-align: right;
|
||||
margin-right: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.item-value {
|
||||
color: #303133;
|
||||
}
|
||||
.image-wrapper {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
min-height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid #DCDFE6;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.void-overlay {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(-30deg);
|
||||
color: red;
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
.download-btn {
|
||||
display: block;
|
||||
}
|
||||
.upload-btn-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.pdf-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
color: #606266;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pdf-placeholder:hover {
|
||||
border-color: #409EFF;
|
||||
color: #409EFF;
|
||||
}
|
||||
.pdf-placeholder .el-icon-document {
|
||||
font-size: 48px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
/* New Dialog Styles */
|
||||
.upload-preview-container {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
background-color: #f5f7fa;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.preview-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.preview-image {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
.preview-pdf {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: #606266;
|
||||
}
|
||||
.preview-pdf .el-icon-document {
|
||||
font-size: 64px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.preview-placeholder {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
}
|
||||
.placeholder-icon {
|
||||
font-size: 64px;
|
||||
margin-bottom: 10px;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
.placeholder-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
.pdf-thumbnail-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pdf-hover-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.pdf-thumbnail-container:hover .pdf-hover-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,393 @@
|
|||
<template>
|
||||
<div>
|
||||
<!-- 1. 顶部:发票类型与票号日期 -->
|
||||
<div class="invoice-header">
|
||||
<div class="header-center">
|
||||
<div class="invoice-title">
|
||||
电子发票
|
||||
<span class="invoice-type-wrapper">
|
||||
(<el-select v-model="data.invoiceType" class="invoice-type-select" disabled :popper-append-to-body="false">
|
||||
<el-option
|
||||
v-for="dict in dict.type.finance_invoice_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>)
|
||||
</span>
|
||||
</div>
|
||||
<div class="header-decoration-line"></div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="meta-row">
|
||||
<span class="meta-label">发票号码:</span>
|
||||
<span class="meta-value">{{ '----------' }}</span>
|
||||
</div>
|
||||
<div class="meta-row">
|
||||
<span class="meta-label">开票日期:</span>
|
||||
<span class="meta-value">{{'----------' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="invoice-body-border">
|
||||
<el-row type="flex" class="info-container">
|
||||
<!-- 左侧:购买方信息 -->
|
||||
<el-col :span="12" class="info-column left-column">
|
||||
<div class="column-label">购买方<br>信息</div>
|
||||
<div class="column-content">
|
||||
<div class="info-item"><span class="label">名称:</span> {{ data.buyerName }}</div>
|
||||
<div class="info-item"><span class="label">纳税人识别号:</span> {{ data.buyerCreditCode }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧:销售方信息 -->
|
||||
<el-col :span="12" class="info-column right-column">
|
||||
<div class="column-label">销售方<br>信息</div>
|
||||
<div class="column-content">
|
||||
<div class="info-item"><span class="label">名称:</span> {{ data.sellerName }}</div>
|
||||
<div class="info-item"><span class="label">纳税人识别号:</span> {{ data.sellerCreditCode }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="items-table-container">
|
||||
<el-table :data="data.detailItemList" border class="invoice-table" show-summary :summary-method="getSummaries">
|
||||
<el-table-column label="项目名称" prop="productName" align="center"></el-table-column>
|
||||
<el-table-column label="规格型号" prop="productModel" align="center" width="120"></el-table-column>
|
||||
<el-table-column label="单位" prop="unit" align="center" width="60"></el-table-column>
|
||||
<el-table-column label="数量" prop="quantity" align="center" width="80"></el-table-column>
|
||||
<el-table-column label="单价" prop="price" align="center" width="100"></el-table-column>
|
||||
<el-table-column label="金额" prop="allPrice" align="center" width="100" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column label="税率%" prop="taxRate" align="center" width="80"></el-table-column>
|
||||
<el-table-column label="税额" prop="taxAmount" align="center" width="100" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 价税合计 -->
|
||||
<div class="total-row">
|
||||
<div class="total-label">价税合计 (大写)</div>
|
||||
<div class="total-value-chinese">
|
||||
<span class="currency-symbol">⊗</span> {{ totalAmountChinese }}
|
||||
</div>
|
||||
<div class="total-label-small">(小写)</div>
|
||||
<div class="total-value-number">
|
||||
¥{{ formatCurrency(totalAmountNumber) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 备注 -->
|
||||
<div class="remark-row">
|
||||
<div class="remark-label">备注</div>
|
||||
<div class="remark-content">{{ data.remark }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "InvoiceInfoView",
|
||||
dicts: ['finance_invoice_type'],
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalAmountNumber() {
|
||||
let total = 0;
|
||||
if (this.data.detailItemList) {
|
||||
this.data.detailItemList.forEach(item => {
|
||||
const amount = parseFloat(item.allPrice) || 0;
|
||||
// const tax = parseFloat(item.taxAmount) || 0;
|
||||
total += amount ;
|
||||
});
|
||||
}
|
||||
return total.toFixed(2);
|
||||
},
|
||||
totalAmountChinese() {
|
||||
return this.convertCurrency(this.totalAmountNumber);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getSummaries(param) {
|
||||
const { columns, data } = param;
|
||||
const sums = [];
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '合计';
|
||||
return;
|
||||
}
|
||||
if (column.property === 'allPrice' || column.property === 'taxAmount') {
|
||||
const values = data.map(item => Number(item[column.property]));
|
||||
if (!values.every(value => isNaN(value))) {
|
||||
const sum = values.reduce((prev, curr) => {
|
||||
const value = Number(curr);
|
||||
if (!isNaN(value)) {
|
||||
return prev + curr;
|
||||
} else {
|
||||
return prev;
|
||||
}
|
||||
}, 0);
|
||||
sums[index] = '¥' + sum.toFixed(2);
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
});
|
||||
return sums;
|
||||
},
|
||||
convertCurrency(money) {
|
||||
const cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
|
||||
const cnIntRadice = ['', '拾', '佰', '仟'];
|
||||
const cnIntUnits = ['', '万', '亿', '兆'];
|
||||
const cnDecUnits = ['角', '分', '毫', '厘'];
|
||||
const cnInteger = '整';
|
||||
const cnIntLast = '元';
|
||||
|
||||
let integerNum;
|
||||
let decimalNum;
|
||||
let chineseStr = '';
|
||||
let parts;
|
||||
|
||||
if (money === '') return '';
|
||||
money = parseFloat(money);
|
||||
if (money >= 999999999999) return '';
|
||||
if (money === 0) return cnNums[0] + cnIntLast + cnInteger;
|
||||
|
||||
let prefix = "";
|
||||
if (money < 0) {
|
||||
prefix = "(负数)";
|
||||
money = Math.abs(money);
|
||||
}
|
||||
|
||||
money = money.toString();
|
||||
if (money.indexOf('.') === -1) {
|
||||
integerNum = money;
|
||||
decimalNum = '';
|
||||
} else {
|
||||
parts = money.split('.');
|
||||
integerNum = parts[0];
|
||||
decimalNum = parts[1].substr(0, 4);
|
||||
}
|
||||
|
||||
if (parseInt(integerNum, 10) > 0) {
|
||||
let zeroCount = 0;
|
||||
let IntLen = integerNum.length;
|
||||
for (let i = 0; i < IntLen; i++) {
|
||||
let n = integerNum.substr(i, 1);
|
||||
let p = IntLen - i - 1;
|
||||
let q = p / 4;
|
||||
let m = p % 4;
|
||||
if (n === '0') {
|
||||
zeroCount++;
|
||||
} else {
|
||||
if (zeroCount > 0) chineseStr += cnNums[0];
|
||||
zeroCount = 0;
|
||||
chineseStr += cnNums[parseInt(n)] + cnIntRadice[m];
|
||||
}
|
||||
if (m === 0 && zeroCount < 4) chineseStr += cnIntUnits[q];
|
||||
}
|
||||
chineseStr += cnIntLast;
|
||||
}
|
||||
|
||||
if (decimalNum !== '') {
|
||||
let decLen = decimalNum.length;
|
||||
for (let i = 0; i < decLen; i++) {
|
||||
let n = decimalNum.substr(i, 1);
|
||||
if (n !== '0') chineseStr += cnNums[Number(n)] + cnDecUnits[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (chineseStr === '') chineseStr += cnNums[0] + cnIntLast + cnInteger;
|
||||
else if (decimalNum === '') chineseStr += cnInteger;
|
||||
return prefix + chineseStr;
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.invoice-header {
|
||||
position: relative;
|
||||
height: 80px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.header-center {
|
||||
text-align: center;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.invoice-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #8B4513;
|
||||
line-height: 40px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
.invoice-type-wrapper {
|
||||
margin-left: 5px;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #8B4513;
|
||||
}
|
||||
.header-decoration-line {
|
||||
height: 2px;
|
||||
background-color: #8B4513;
|
||||
width: 380px;
|
||||
margin: 0 auto;
|
||||
border-bottom: 1px solid #8B4513;
|
||||
box-shadow: 0 2px 0 0 #fff, 0 4px 0 0 #8B4513;
|
||||
}
|
||||
.header-right {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
.meta-row {
|
||||
font-size: 13px;
|
||||
color: #8B4513;
|
||||
line-height: 20px;
|
||||
}
|
||||
.meta-label {
|
||||
display: inline-block;
|
||||
width: 70px;
|
||||
}
|
||||
.invoice-body-border {
|
||||
border: 2px solid #8B4513;
|
||||
padding: 0;
|
||||
color: #606266;
|
||||
}
|
||||
.info-container {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #8B4513;
|
||||
}
|
||||
.info-column {
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
.left-column {
|
||||
border-right: 1px solid #8B4513;
|
||||
}
|
||||
.column-label {
|
||||
width: 30px;
|
||||
padding: 0 5px;
|
||||
color: #8B4513;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
border-right: 1px solid #8B4513;
|
||||
margin-right: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
word-break: break-all;
|
||||
}
|
||||
.column-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
}
|
||||
.info-item {
|
||||
margin-bottom: 2px;
|
||||
color: #8B4513;
|
||||
}
|
||||
.info-item .label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
.items-table-container {
|
||||
padding: 0;
|
||||
border-bottom: 1px solid #8B4513;
|
||||
}
|
||||
.invoice-table ::v-deep th {
|
||||
font-weight: bold;
|
||||
color: #8B4513;
|
||||
border-color: #8B4513;
|
||||
}
|
||||
.invoice-table ::v-deep td {
|
||||
border-color: #8B4513;
|
||||
}
|
||||
.total-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #8B4513;
|
||||
height: 40px;
|
||||
padding: 0 10px;
|
||||
color: #8B4513;
|
||||
}
|
||||
.total-label {
|
||||
width: 120px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.total-value-chinese {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.currency-symbol {
|
||||
border: 1px solid #8B4513;
|
||||
border-radius: 50%;
|
||||
padding: 1px 4px;
|
||||
font-size: 12px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.total-label-small {
|
||||
width: 60px;
|
||||
text-align: right;
|
||||
font-size: 13px;
|
||||
}
|
||||
.total-value-number {
|
||||
width: 150px;
|
||||
padding-left: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.remark-row {
|
||||
display: flex;
|
||||
min-height: 40px;
|
||||
padding: 10px;
|
||||
color: #8B4513;
|
||||
}
|
||||
.remark-label {
|
||||
width: 120px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.remark-content {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.invoice-type-select {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
/* Hide select border to blend in */
|
||||
.invoice-type-select ::v-deep .el-input__inner {
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #8B4513;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.invoice-type-select ::v-deep .el-select__caret {
|
||||
color: #8B4513;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,397 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="150px">
|
||||
<el-form-item label="项目编号" prop="projectCode">
|
||||
<el-input
|
||||
v-model="queryParams.projectCode"
|
||||
placeholder="请输入项目编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="销售-开票单编号" prop="invoiceBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.invoiceBillCode"
|
||||
placeholder="请输入开票单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="进货商名称" prop="partnerName">
|
||||
<el-input
|
||||
v-model="queryParams.partnerName"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="销售-应收单编号" prop="receivableBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.receivableBillCode"
|
||||
placeholder="请输入应收单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="票据类型" prop="invoiceType">
|
||||
<el-select v-model="queryParams.invoiceType" placeholder="票据类型" clearable>
|
||||
<el-option v-for="dict in dict.type.finance_invoice_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批状态" prop="approveStatus">
|
||||
<el-select v-model="queryParams.approveStatus" placeholder="请选择审批状态" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.approve_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开票状态" prop="invoiceStatus">
|
||||
<el-select v-model="queryParams.invoiceStatus" placeholder="请选择开票状态" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.invoice_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批节点" prop="approveNode">
|
||||
<el-input
|
||||
v-model="queryParams.approveNode"
|
||||
placeholder="请输入审批节点"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批通过时间">
|
||||
<el-date-picker
|
||||
v-model="dateRangeApproval"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="预计开票时间">
|
||||
<el-date-picker
|
||||
v-model="dateRangeEstimated"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="实际收票时间">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- v-model="dateRangeActual"-->
|
||||
<!-- style="width: 240px"-->
|
||||
<!-- value-format="yyyy-MM-dd"-->
|
||||
<!-- type="daterange"-->
|
||||
<!-- range-separator="-"-->
|
||||
<!-- start-placeholder="开始日期"-->
|
||||
<!-- end-placeholder="结束日期"-->
|
||||
<!-- ></el-date-picker>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="invoiceList">
|
||||
<el-table-column label="销售-开票单编号" align="center" prop="invoiceBillCode" width="200"/>
|
||||
<el-table-column label="预计开票时间" align="center" prop="invoiceTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.invoiceTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="进货商名称" align="center" prop="partnerName" width="300" />
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" width="180" >
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.totalPriceWithTax<0?{color:'red'}:{}">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="未税总价(元)" align="center" prop="totalPriceWithoutTax" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.totalPriceWithoutTax<0?{color:'red'}:{}">{{ formatCurrency(scope.row.totalPriceWithoutTax) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发票未税总价(元)" align="center" prop="invoicePriceWithoutTax" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.invoicePriceWithoutTax<0?{color:'red'}:{}">{{ formatCurrency(scope.row.invoicePriceWithoutTax) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="税额(元)" align="center" prop="taxAmount" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.invoicePriceWithoutTax<0?{color:'red'}:{}">{{ formatCurrency($calc.sub(scope.row.totalPriceWithTax, scope.row.totalPriceWithoutTax)) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="发票税额(元)" align="center" prop="invoicePriceTax" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.invoicePriceWithoutTax<0?{color:'red'}:{}">{{ formatCurrency($calc.sub(scope.row.totalPriceWithTax, scope.row.invoicePriceWithoutTax)) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开票状态" align="center" prop="invoiceStatus" width="150" >
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.invoice_bill_status" :value="scope.row.invoiceStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批状态" align="center" prop="approveStatus" width="150" >
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.approve_status" :value="scope.row.approveStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="审批通过时间" align="center" prop="approveTime" width="180" >
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.approveTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" width="200" fixed="right"/>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="300" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-view"
|
||||
@click="handleDetail(scope.row)"
|
||||
>查看详情</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-document"
|
||||
v-show="scope.row.approveStatus==='2'"
|
||||
@click="handleReceipt(scope.row)"
|
||||
>发票</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-s-ticket"
|
||||
v-show="scope.row.invoiceStatus==='1' &&(scope.row.approveStatus==='0')"
|
||||
@click="handleApplyInvoice(scope.row)"
|
||||
>申请开票</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-document"
|
||||
v-show="scope.row.approveStatus==='0'"
|
||||
@click="handleReturn(scope.row)"
|
||||
>退回</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-refresh-left"
|
||||
v-show="scope.row.approveStatus === '2' && scope.row.invoiceStatus === '2'"
|
||||
@click="handleRedRush(scope.row)"
|
||||
>申请红冲</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
v-show="(scope.row.invoiceBillType === 'FROM_RECEIVABLE' && (scope.row.approveStatus === '2' || scope.row.approveStatus === '3') && scope.row.invoiceStatus === '1') || (scope.row.invoiceBillType === 'RED_RUSH' && scope.row.approveStatus === '3')"
|
||||
@click="handleRevoke(scope.row)"
|
||||
>撤销</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 详情抽屉 -->
|
||||
<detail-drawer :visible.sync="detailOpen" :detail="detailData"></detail-drawer>
|
||||
<!-- 新增弹窗 -->
|
||||
<add-form :visible.sync="addOpen" :dicts="dict.type" @submit="handleAddSubmit"></add-form>
|
||||
<!-- 收票附件弹窗 -->
|
||||
<invoice-dialog :visible.sync="receiptOpen" :invoice-data="currentRow" :dicts="dict.type"></invoice-dialog>
|
||||
<!-- 申请开票弹窗 -->
|
||||
<apply-invoice :visible.sync="applyOpen" :row-data="currentRow" :is-red-rush="isRedRush" @submit="getList"></apply-invoice>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {listInvoice, getInvoice, redRush, addInvoice, returnInvoice, revokeInvoice} from "@/api/finance/invoice";
|
||||
import { addDateRange } from "@/utils/ruoyi";
|
||||
import DetailDrawer from "./components/DetailDrawer.vue";
|
||||
import AddForm from "./components/AddForm.vue";
|
||||
import InvoiceDialog from "./components/InvoiceDialog.vue";
|
||||
import ApplyInvoice from "./components/ApplyInvoice.vue";
|
||||
|
||||
export default {
|
||||
name: "Invoice",
|
||||
components: {
|
||||
DetailDrawer,
|
||||
AddForm,
|
||||
InvoiceDialog,
|
||||
ApplyInvoice
|
||||
},
|
||||
dicts:['invoice_bill_type','approve_status','invoice_bill_status','finance_invoice_type'],
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 收票单表格数据
|
||||
invoiceList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectCode: null,
|
||||
projectName: null,
|
||||
invoiceBillCode: null,
|
||||
invoiceType: null,
|
||||
partnerName: null,
|
||||
receivableBillCode: null,
|
||||
invoiceBillType: null,
|
||||
approveStatus: null,
|
||||
invoiceStatus: null,
|
||||
approveNode: null,
|
||||
},
|
||||
// 日期范围
|
||||
dateRangeApproval: [],
|
||||
dateRangeEstimated: [],
|
||||
dateRangeActual: [],
|
||||
// 详情抽屉
|
||||
detailOpen: false,
|
||||
detailData: null,
|
||||
// 新增弹窗
|
||||
addOpen: false,
|
||||
// 申请开票弹窗
|
||||
applyOpen: false,
|
||||
// 是否红冲
|
||||
isRedRush: false,
|
||||
// 收票附件弹窗
|
||||
receiptOpen: false,
|
||||
currentRow: {}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
addDateRange,
|
||||
getList() {
|
||||
this.loading = true;
|
||||
let query = { ...this.queryParams };
|
||||
query = this.addDateRange(query, this.dateRangeApproval, 'ApproveTime');
|
||||
query = this.addDateRange(query, this.dateRangeEstimated, 'InvoiceTime');
|
||||
query = this.addDateRange(query, this.dateRangeActual, 'ActualInvoiceTime');
|
||||
|
||||
listInvoice(query).then(response => {
|
||||
this.invoiceList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.dateRangeApproval = [];
|
||||
this.dateRangeEstimated = [];
|
||||
this.dateRangeActual = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.addOpen = true;
|
||||
},
|
||||
/** 新增提交 */
|
||||
handleAddSubmit(form) {
|
||||
addInvoice(form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.addOpen = false;
|
||||
this.getList();
|
||||
}).catch(error => {
|
||||
console.error("新增收票单失败", error);
|
||||
this.$modal.msgError("新增失败");
|
||||
});
|
||||
},
|
||||
/** 详情按钮操作 */
|
||||
handleDetail(row) {
|
||||
getInvoice(row.id).then(response => {
|
||||
this.detailData = response.data;
|
||||
this.detailData.approveNode =row.approveNode;
|
||||
this.detailOpen = true;
|
||||
});
|
||||
},
|
||||
/** 收票附件按钮操作 */
|
||||
handleReceipt(row) {
|
||||
this.currentRow = row;
|
||||
this.receiptOpen = true;
|
||||
},
|
||||
/** 申请开票按钮操作 */
|
||||
handleApplyInvoice(row) {
|
||||
this.isRedRush = false;
|
||||
this.currentRow = row;
|
||||
this.applyOpen = true;
|
||||
},
|
||||
/** 红冲按钮操作 */
|
||||
handleRedRush(row) {
|
||||
this.isRedRush = true;
|
||||
this.currentRow = row;
|
||||
this.applyOpen = true;
|
||||
},
|
||||
handleReturn(row) {
|
||||
this.$modal.confirm('是否确认退回收票单编号为"' + row.invoiceBillCode + '"的数据项?').then(function() {
|
||||
return returnInvoice(row.id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("退回成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 撤销按钮操作 */
|
||||
handleRevoke(row) {
|
||||
this.$modal.confirm('是否确认撤销收票单编号为"' + row.invoiceBillCode + '"的数据项?').then(function() {
|
||||
return revokeInvoice(row.id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("撤销成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
<template>
|
||||
<el-drawer title="采购-应付单详情" :visible.sync="internalVisible" :wrapper-closable="false" size="70%" @close="handleClose">
|
||||
<div class="dialog-body">
|
||||
<!-- Part 1: Details -->
|
||||
<div>
|
||||
<el-divider content-position="left">采购-应付单</el-divider>
|
||||
<div class="details-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>项目编号:</strong> {{ formData.projectCode }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>项目名称:</strong> {{ formData.projectName }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>采购-应付单编号:</strong> {{ formData.payableBillCode }}</div>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>生成时间:</strong> {{ formData.createTime }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>该制造商是否有预付单:</strong> {{ formData.preResidueAmount == 0 ? '否' : '是' }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>预付金额(元):</strong> {{ formatCurrency(formData.preResidueAmount) }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>制造商名称:</strong> {{ formData.vendorName }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>合同编号:</strong> {{ formData.orderCode }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>出/入库单号:</strong> {{ formData.inventoryCode }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>含税总价(元):</strong> {{ formatCurrency(formData.totalPriceWithTax) }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>未税总价(元):</strong> {{ formatCurrency(formData.totalPriceWithoutTax) }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>税额(元):</strong> {{ formatCurrency(formData.taxAmount) }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>未付款金额(元):</strong> {{ formatCurrency(formData.unpaidPaymentAmount) }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>已付款金额(元):</strong> {{ formatCurrency(formData.paidPaymentAmount) }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>付款中金额(元):</strong> {{ formatCurrency(this.$calc.sub(this.$calc.sub(formData.totalPriceWithTax,formData.paidPaymentAmount),formData.unpaidPaymentAmount)) }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>未收票金额(元):</strong> {{ formatCurrency(formData.unreceivedTicketAmount) }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>已收票金额(元):</strong> {{ formatCurrency(formData.receivedTicketAmount) }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>收票中金额(元):</strong> {{ formatCurrency(this.$calc.sub(this.$calc.sub(formData.totalPriceWithTax,formData.receivedTicketAmount),formData.unreceivedTicketAmount))}}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>生成付款单:</strong> {{ formData.totalPriceWithTax == formData.unpaidPaymentAmount ? '未生成' : formData.unpaidPaymentAmount == 0 ? '全部生成' : '部分生成' }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>生成收票单:</strong> {{ formData.totalPriceWithTax == formData.unreceivedTicketAmount ? '未生成' : formData.unreceivedTicketAmount == 0 ? '全部生成' : '部分生成' }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item" style="display: flex"><strong>产品类型:</strong>
|
||||
<dict-tag :options="dict.type.product_type" :value="formData.productType"/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Part 2: Tabs -->
|
||||
<div style="padding: 20px">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="明细" name="details">
|
||||
<el-divider content-position="left">采购-付款单</el-divider>
|
||||
<el-table :data="formData.detailList" style="width: 100%" show-summary :summary-method="getSummaries">
|
||||
<el-table-column type="index" label="序号" width="50"></el-table-column>
|
||||
<el-table-column prop="payableDetailType" label="付款通道">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.payable_detail_type" :value="scope.row.payableDetailType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="actualPaymentTime" label="实际付款时间">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.actualPaymentTime || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="paymentAmount" label="本次付款金额">
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.paymentAmount<0?{color:'red'}:{}"> {{ formatCurrency(scope.row.paymentAmount) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="paymentRate" label="本次付款比例"></el-table-column>
|
||||
<el-table-column prop="paymentStatus" label="付款状态">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.payment_status" :value="scope.row.paymentStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="paymentBillCode" label="采购-付款单编号"></el-table-column>
|
||||
<el-table-column label="回执单/退款图">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.finAttachment">
|
||||
<el-button type="text" size="mini" icon="el-icon-view" @click="handlePreview(scope.row.finAttachment)">预览</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-download" @click="downloadFile(scope.row.finAttachment)">下载</el-button>
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-divider content-position="left">采购-收票单</el-divider>
|
||||
<el-table :data="formData.ticketDetailList" style="width: 100%" show-summary :summary-method="getSummaries">
|
||||
<el-table-column type="index" label="序号" width="50"></el-table-column>
|
||||
<el-table-column prop="payableDetailType" label="发票类型">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.ticket_detail_type" :value="scope.row.payableDetailType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="actualTicketTime" label="实际收票时间">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.actualTicketTime || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="paymentAmount" label="本次收票金额">
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.paymentAmount<0?{color:'red'}:{}"> {{ formatCurrency(scope.row.paymentAmount) }}</span>
|
||||
</template>
|
||||
|
||||
</el-table-column>
|
||||
<el-table-column prop="paymentRate" label="本次收票比例"></el-table-column>
|
||||
<el-table-column prop="receiptStatus" label="收票状态">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.receipt_status" :value="scope.row.ticketStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="ticketBillCode" label="采购-收票单编号"></el-table-column>
|
||||
<el-table-column label="发票/红冲发票">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.finAttachment">
|
||||
<el-button type="text" size="mini" icon="el-icon-view" @click="handlePreview(scope.row.finAttachment)">预览</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-download" @click="downloadFile(scope.row.finAttachment)">下载</el-button>
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="付款计划" name="paymentPlan">
|
||||
<PaymentPlan :isInitEdit=true @syncPlan="refreshTicketPlan()" :payableData="data"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="收票计划" name="receivingTicketPlan">
|
||||
<ReceivingTicketPlan ref="ticketPlan" :isInitEdit=true :payableData="data"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
<GlobalFilePreview ref="filePreview" />
|
||||
</div>
|
||||
<!-- <div slot="footer" class="dialog-footer">-->
|
||||
<!-- <el-button @click="handleClose">取 消</el-button>-->
|
||||
<!-- <el-button type="primary" @click="handleSubmit">确 定</el-button>-->
|
||||
<!-- </div>-->
|
||||
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PaymentPlan from './PaymentPlan.vue';
|
||||
import ReceivingTicketPlan from './ReceivingTicketPlan.vue';
|
||||
import { getPayable } from "@/api/finance/payable";
|
||||
import ReceiptPlan from "@/views/finance/receivable/components/ReceiptPlan.vue";
|
||||
import GlobalFilePreview from '@/components/GlobalFilePreview';
|
||||
|
||||
export default {
|
||||
name: "EditForm",
|
||||
dicts: ['product_type','payment_status','payable_detail_type', 'ticket_detail_type', 'receipt_status'],
|
||||
components: {
|
||||
ReceiptPlan,
|
||||
PaymentPlan,
|
||||
ReceivingTicketPlan,
|
||||
GlobalFilePreview
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internalVisible: this.visible, // Local copy of the visible prop
|
||||
activeTab: 'details',
|
||||
formData: {},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
visible(newVal) {
|
||||
this.internalVisible = newVal; // Sync prop to local data
|
||||
if (newVal && this.data.id) {
|
||||
this.getDetails();
|
||||
}
|
||||
},
|
||||
internalVisible(newVal) {
|
||||
if (!newVal) {
|
||||
this.formData = {};
|
||||
}
|
||||
this.$emit('update:visible', newVal); // Emit changes to parent
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refreshTicketPlan(){
|
||||
this.$refs.ticketPlan.fetchTicketPlans(this.formData.id);
|
||||
},
|
||||
getDetails() {
|
||||
getPayable(this.data.id).then(res => {
|
||||
this.formData = res.data;
|
||||
});
|
||||
},
|
||||
handlePreview(attachment) {
|
||||
this.$refs.filePreview.handlePreview(attachment);
|
||||
},
|
||||
downloadFile(attachment){
|
||||
this.$refs.filePreview.downloadFile(attachment);
|
||||
},
|
||||
getSummaries(param) {
|
||||
const { columns, data } = param;
|
||||
const sums = [];
|
||||
let paymentAmountSum = 0;
|
||||
if (data && data.length > 0) {
|
||||
paymentAmountSum = data.reduce((acc, item) => {
|
||||
const value = Number(item.paymentAmount);
|
||||
return acc + (isNaN(value) ? 0 : value);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '合计';
|
||||
} else if (column.property === 'paymentAmount') {
|
||||
sums[index] = paymentAmountSum.toFixed(2);
|
||||
} else if (column.property === 'paymentRate') {
|
||||
if (this.formData.totalPriceWithTax && this.formData.totalPriceWithTax > 0) {
|
||||
const ratio = this.$calc.div(paymentAmountSum , this.formData.totalPriceWithTax,4);
|
||||
sums[index] = (ratio * 100).toFixed(2) + '%';
|
||||
} else {
|
||||
sums[index] = '0.00%';
|
||||
}
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
});
|
||||
return sums;
|
||||
},
|
||||
handleClose() {
|
||||
this.internalVisible = false; // Close dialog local
|
||||
this.$emit('close');
|
||||
},
|
||||
handleSubmit() {
|
||||
this.handleClose();
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>.details-container {
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dialog-body {
|
||||
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,371 @@
|
|||
<template>
|
||||
<el-dialog title="合并发起付款单" :visible.sync="dialogVisible" width="80%" :close-on-click-modal="false" @close="handleClose" append-to-body>
|
||||
<div class="dialog-body">
|
||||
<el-form ref="form" :model="form" :inline="true" label-width="120px">
|
||||
<el-row>
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-form-item label="付款单类型" prop="paymentBillType">-->
|
||||
<!-- <el-select disabled v-model="form.paymentBillType" placeholder="请选择付款单类型" clearable>-->
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="dict in dict.type.payment_bill_type"-->
|
||||
<!-- :key="dict.value"-->
|
||||
<!-- :label="dict.label"-->
|
||||
<!-- :value="dict.value"-->
|
||||
<!-- />-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="8">
|
||||
<el-form-item label="制造商名称">
|
||||
<el-input v-model="form.vendorName" readonly/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-form-item label="预计付款时间" prop="estimatedPaymentTime">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- v-model="form.estimatedPaymentTime"-->
|
||||
<!-- type="date"-->
|
||||
<!-- value-format="yyyy-MM-dd HH:mm:ss"-->
|
||||
<!-- placeholder="选择日期"-->
|
||||
<!-- ></el-date-picker>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-divider content-position="left">采购应付单表</el-divider>
|
||||
<el-table :data="payableOrdersWithPlans" border max-height="300px" style="margin-bottom: 20px;">
|
||||
<el-table-column label="采购-应付单编号" align="center" prop="payableBillCode" width="150"/>
|
||||
<el-table-column label="预计付款时间" align="center" prop="planPaymentDate" width="180"/>
|
||||
<el-table-column label="预期付款计划" align="center" width="100" prop="planAmount">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency(calculateOrderCurrentPaymentAmount(scope.row.id).toFixed(2)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预期付款比例" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ calculateOrderCurrentPaymentRate(scope.row.id) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" />
|
||||
<el-table-column label="制造商名称" align="center" prop="vendorName" />
|
||||
<!-- <el-table-column label="合同编号" align="center" prop="orderCode" width="150"/>-->
|
||||
<!-- <el-table-column label="出入库单号" align="center" prop="inventoryCode" width="150"/>-->
|
||||
<!-- <el-table-column label="付款状态" align="center" prop="paymentStatus" width="120">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <dict-tag :options="dict.type.payment_status" :value="scope.row.paymentStatus"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未付款金额(元)" align="center" prop="unpaidPaymentAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<!-- <el-table-column label="本次付款金额" align="center" width="120">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- {{ calculateOrderCurrentPaymentAmount(scope.row.id).toFixed(2) }}-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column label="本次付款比例" align="center" width="120">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- {{ calculateOrderCurrentPaymentRate(scope.row.id) }}%-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="已付款金额(元)" align="center" prop="paidPaymentAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="付款中金额(元)" align="center" prop="paidAmount" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency($calc.sub($calc.sub(scope.row.totalPriceWithTax, scope.row.paidPaymentAmount), scope.row.unpaidPaymentAmount)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleOpenPaymentPlanSelector(scope.row, scope.$index)"
|
||||
>选择
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="total-info">
|
||||
|
||||
<span style="margin-left: 20px;">计划付款总金额: <el-tag type="success">{{
|
||||
formatCurrency(totalPlannedAmount.toFixed(2))
|
||||
}}</el-tag></span>
|
||||
<span>计划付款比例: <el-tag type="info">{{ this.$calc.mul(this.$calc.div(totalPlannedAmount,totalPayableAmountWithTax,4),100) }}%</el-tag></span>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleCancel">取 消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确 定</el-button>
|
||||
</div>
|
||||
<el-dialog :title="planTitle" :visible.sync="isPaymentPlanSelectorOpen" width="70%"
|
||||
@close="isPaymentPlanSelectorOpen=false" append-to-body>
|
||||
<payment-plan-selector
|
||||
ref="planSelector"
|
||||
:payable-data="choosePayable"
|
||||
:selected-plans="choosePayable.paymentPlans"
|
||||
@confirm="handlePaymentPlanConfirm"
|
||||
/>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="isPaymentPlanSelectorOpen=false">取 消</el-button>
|
||||
<!-- <el-button type="primary" @click="handleConfirm" >确 定</el-button>-->
|
||||
<el-button type="primary" @click="handleChooseConfirm">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!-- 付款计划选择器弹窗 -->
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PaymentPlan from './PaymentPlan.vue';
|
||||
import {getPaymentPlan} from "@/api/finance/payable";
|
||||
|
||||
export default {
|
||||
name: "MergePaymentDialog",
|
||||
components: {PaymentPlanSelector: PaymentPlan},
|
||||
dicts: ['payment_status','payment_bill_type'], // Add dicts for dict-tag
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
payableOrders: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internalVisible: this.visible,
|
||||
planTitle: '',
|
||||
choosePayable: {},
|
||||
form: {
|
||||
paymentBillType: 'FROM_PAYABLE', // Default to a type, or make it dynamic
|
||||
vendorName: '',
|
||||
estimatedPaymentTime: null,
|
||||
},
|
||||
payableOrdersWithPlans: [], // Each order will now have its own paymentPlans array
|
||||
isPaymentPlanSelectorOpen: false,
|
||||
currentPayableOrderIndexForPlan: -1, // Index of the order in payableOrdersWithPlans
|
||||
loadingPaymentPlans: false, // Loading state for fetching payment plans
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
dialogVisible: {
|
||||
get() {
|
||||
return this.internalVisible;
|
||||
},
|
||||
set(val) {
|
||||
this.internalVisible = val;
|
||||
this.$emit('update:visible', val);
|
||||
}
|
||||
},
|
||||
totalPayableAmountWithTax() {
|
||||
return this.payableOrdersWithPlans.reduce((sum, order) => sum + (order.totalPriceWithTax || 0), 0);
|
||||
},
|
||||
totalPlannedAmount() {
|
||||
return this.payableOrdersWithPlans.reduce((orderSum, order) => {
|
||||
const orderPlansTotal = (order.paymentPlans || []).reduce((planSum, plan) => planSum + (plan.planAmount || 0), 0);
|
||||
return orderSum + orderPlansTotal;
|
||||
}, 0);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
visible(newVal) {
|
||||
this.internalVisible = newVal;
|
||||
if (newVal) {
|
||||
this.initDialogData();
|
||||
}
|
||||
},
|
||||
payableOrders: {
|
||||
handler(newVal) {
|
||||
if (this.dialogVisible) {
|
||||
this.initDialogData();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initDialogData() {
|
||||
// Initialize form fields
|
||||
if (this.payableOrders.length > 0) {
|
||||
const firstVendorName = this.payableOrders[0].vendorName;
|
||||
const allSameVendor = this.payableOrders.every(order => order.vendorName === firstVendorName);
|
||||
this.form.vendorName = allSameVendor ? firstVendorName : '多个制造商';
|
||||
this.form.estimatedPaymentTime = this.payableOrders[0].estimatedPaymentTime || null; // Use first order's estimated time as default
|
||||
} else {
|
||||
this.form.vendorName = '';
|
||||
this.form.estimatedPaymentTime = null;
|
||||
}
|
||||
this.form.paymentBillType = 'FROM_PAYABLE'; // Default
|
||||
|
||||
// Initialize payableOrdersWithPlans
|
||||
this.payableOrdersWithPlans = this.payableOrders.map(order => {
|
||||
const paymentPlans = order.paymentPlans ? [...order.paymentPlans] : [];
|
||||
if (paymentPlans.length === 0 && order.lastPaymentPlanId) {
|
||||
paymentPlans.push({
|
||||
id: order.lastPaymentPlanId,
|
||||
planAmount: order.planAmount,
|
||||
planPaymentDate: order.planPaymentDate,
|
||||
planRate: this.$calc.mul(this.$calc.div(order.planAmount, order.totalPriceWithTax, 4), 100)
|
||||
});
|
||||
}
|
||||
return {
|
||||
...order,
|
||||
paymentPlans: paymentPlans, // Retain existing plans if any, otherwise empty
|
||||
totalPriceWithTax: order.totalPriceWithTax || 0, // Ensure numeric for calculations
|
||||
unpaidAmount: order.unpaidAmount || 0,
|
||||
paidAmount: order.paidAmount || 0, // Ensure numeric for calculations
|
||||
}
|
||||
});
|
||||
},
|
||||
handleClose() {
|
||||
this.dialogVisible = false;
|
||||
this.resetForm();
|
||||
},
|
||||
handleChooseConfirm() {
|
||||
if (!this.$refs.planSelector) {
|
||||
this.$modal.msgError('无法获取计划选择器组件');
|
||||
return;
|
||||
}
|
||||
const selectedPlans = this.$refs.planSelector.selectedPlan || [];
|
||||
|
||||
const orderIndex = this.payableOrdersWithPlans.findIndex(o => o.id === this.choosePayable.id);
|
||||
if (orderIndex === -1) {
|
||||
this.$modal.msgError('找不到要更新的应付单');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentOrder = this.payableOrdersWithPlans[orderIndex];
|
||||
|
||||
// Update the payment plans for the specific order
|
||||
this.$set(currentOrder, 'paymentPlans', [...selectedPlans]);
|
||||
|
||||
this.isPaymentPlanSelectorOpen = false;
|
||||
this.$modal.msgSuccess(`已更新付款计划选择,共 ${selectedPlans.length} 条`);
|
||||
},
|
||||
handleConfirm() {
|
||||
// Validate main form fields
|
||||
if (!this.form.paymentBillType) {
|
||||
this.$modal.msgError('请选择付款单类型');
|
||||
return;
|
||||
}
|
||||
// if (!this.form.estimatedPaymentTime) {
|
||||
// this.$modal.msgError('请选择预计付款时间');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Validate each payable order's payment plans
|
||||
for (const order of this.payableOrdersWithPlans) {
|
||||
if (!order.paymentPlans || order.paymentPlans.length === 0) {
|
||||
this.$modal.msgError(`应付单 ${order.payableBillCode} 至少需要一条付款计划`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (const plan of order.paymentPlans) {
|
||||
if (!plan.planPaymentDate) {
|
||||
this.$modal.msgError(`应付单 ${order.payableBillCode} 的付款计划中预计付款时间不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount <= 0) {
|
||||
this.$modal.msgError(`应付单 ${order.payableBillCode} 的付款计划中预计付款金额必须大于0。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planRate === null || plan.planRate === undefined) {
|
||||
this.$modal.msgError(`应付单 ${order.payableBillCode} 的付款计划中应付比例不能为空。`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the final data to be emitted to the parent
|
||||
const mergedPaymentData = {
|
||||
paymentBillType: this.form.paymentBillType,
|
||||
estimatedPaymentTime: this.form.estimatedPaymentTime,
|
||||
// Collect all payable orders with their updated payment plans
|
||||
payableOrders: this.payableOrdersWithPlans.map(order => ({
|
||||
id: order.id,
|
||||
taxRate: order.taxRate,
|
||||
payableBillCode: order.payableBillCode,
|
||||
paymentPlans: order.paymentPlans.map(plan => ({
|
||||
planPaymentDate: plan.planPaymentDate,
|
||||
planAmount: plan.planAmount,
|
||||
planRate: plan.planRate,
|
||||
remark: plan.remark,
|
||||
id: plan.id,
|
||||
})),
|
||||
})),
|
||||
totalMergePaymentAmount: this.totalPlannedAmount, // Total amount for the merged bill
|
||||
};
|
||||
|
||||
this.$emit('confirm', mergedPaymentData);
|
||||
this.dialogVisible = false;
|
||||
},
|
||||
handleCancel() {
|
||||
this.dialogVisible = false;
|
||||
this.resetForm();
|
||||
},
|
||||
resetForm() {
|
||||
this.form = {
|
||||
paymentBillType: 'FROM_PAYABLE',
|
||||
vendorName: '',
|
||||
estimatedPaymentTime: null,
|
||||
};
|
||||
this.payableOrdersWithPlans = [];
|
||||
|
||||
this.currentPayableOrderIndexForPlan = -1;
|
||||
this.loadingPaymentPlans = false;
|
||||
},
|
||||
handleOpenPaymentPlanSelector(row, index) {
|
||||
this.planTitle = `选择付款计划 - ${row.payableBillCode}`;
|
||||
this.choosePayable = row;
|
||||
this.currentPayableOrderIndexForPlan = index;
|
||||
this.isPaymentPlanSelectorOpen = true;
|
||||
console.log(this.choosePayable.id)
|
||||
},
|
||||
handlePaymentPlanConfirm(updatedPlans) {
|
||||
// Update the payment plans for the specific order
|
||||
if (this.currentPayableOrderIndexForPlan !== -1) {
|
||||
this.$set(this.payableOrdersWithPlans[this.currentPayableOrderIndexForPlan], 'paymentPlans', updatedPlans);
|
||||
}
|
||||
this.isPaymentPlanSelectorOpen = false;
|
||||
this.currentPayableOrderIndexForPlan = -1;
|
||||
},
|
||||
calculateOrderCurrentPaymentAmount(orderId) {
|
||||
const order = this.payableOrdersWithPlans.find(o => o.id === orderId);
|
||||
if (order && order.paymentPlans) {
|
||||
return order.paymentPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
calculateOrderCurrentPaymentRate(orderId) {
|
||||
const order = this.payableOrdersWithPlans.find(o => o.id === orderId);
|
||||
if (order && order.paymentPlans && order.unpaidAmount >= 0) {
|
||||
const currentAmount = this.calculateOrderCurrentPaymentAmount(orderId);
|
||||
console.log(this.$calc.div(currentAmount ,order.totalPriceWithTax,4 ))
|
||||
console.log(11111)
|
||||
return this.$calc.mul((this.$calc.div(currentAmount ,order.totalPriceWithTax,4 )),100);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px; /* To prevent scrollbar from overlapping content */
|
||||
}
|
||||
|
||||
.total-info {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,382 @@
|
|||
<template>
|
||||
<el-dialog title="合并发起收票单" :close-on-click-modal="false" :visible.sync="dialogVisible" width="80%" @close="handleClose" append-to-body>
|
||||
<div class="dialog-body">
|
||||
<el-form ref="form" :model="form" :inline="true" label-width="120px">
|
||||
<el-row>
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-form-item label="收票单类型" prop="ticketBillType">-->
|
||||
<!-- <el-select disabled v-model="form.ticketBillType" placeholder="请选择收票单类型" clearable>-->
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="dict in dict.type.payment_bill_type"-->
|
||||
<!-- :key="dict.value"-->
|
||||
<!-- :label="dict.label"-->
|
||||
<!-- :value="dict.value"-->
|
||||
<!-- />-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="8">
|
||||
<el-form-item label="制造商名称">
|
||||
<el-input v-model="form.vendorName" readonly/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-form-item label="预计收票时间" prop="ticketTime">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- v-model="form.ticketTime"-->
|
||||
<!-- type="date"-->
|
||||
<!-- value-format="yyyy-MM-dd HH:mm:ss"-->
|
||||
<!-- placeholder="选择日期"-->
|
||||
<!-- ></el-date-picker>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="8">
|
||||
<el-form-item label="制造商开票时间" prop="vendorTicketTime">
|
||||
<el-date-picker
|
||||
v-model="form.vendorTicketTime"
|
||||
type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="选择日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-divider content-position="left">采购应付单表</el-divider>
|
||||
<el-table :data="payableOrdersWithPlans" border max-height="300px" style="margin-bottom: 20px;">
|
||||
<el-table-column label="采购-应付单编号" align="center" prop="payableBillCode" width="150"/>
|
||||
<el-table-column label="预计收票时间" align="center" prop="planTicketDate" width="180"/>
|
||||
<!-- <el-table-column label="收票计划" align="center" width="100" prop="planTicketAmount">-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="预期收票计划" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency(calculateOrderCurrentTicketAmount(scope.row.id).toFixed(2)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预期收票比例" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ calculateOrderCurrentTicketRate(scope.row.id) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" />
|
||||
<el-table-column label="制造商名称" align="center" prop="vendorName" />
|
||||
<!-- <el-table-column label="合同编号" align="center" prop="orderCode" width="150"/>-->
|
||||
<!-- <el-table-column label="出入库单号" align="center" prop="inventoryCode" width="150"/>-->
|
||||
<!-- <el-table-column label="收票状态" align="center" prop="invoiceStatus" width="120">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <dict-tag :options="dict.type.invoice_status" :value="scope.row.invoiceStatus"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收票金额(元)" align="center" prop="unreceivedTicketAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
|
||||
<el-table-column label="已收票金额(元)" align="center" prop="receivedTicketAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收票中金额(元)" align="center" prop="invoicedAmount" width="120" >
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency($calc.sub($calc.sub(scope.row.totalPriceWithTax,scope.row.receivedTicketAmount),scope.row.unreceivedTicketAmount))}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleOpenTicketPlanSelector(scope.row, scope.$index)"
|
||||
>选择
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="total-info">
|
||||
|
||||
<span style="margin-left: 20px;">计划收票总金额: <el-tag type="success">{{
|
||||
formatCurrency( totalPlannedAmount.toFixed(2))
|
||||
}}</el-tag></span>
|
||||
<span>计划收票比例: <el-tag type="info">{{ this.$calc.mul(this.$calc.div(totalPlannedAmount,totalPayableAmountWithTax,4),100) }}%</el-tag></span>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleCancel">取 消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确 定</el-button>
|
||||
</div>
|
||||
<el-dialog :title="planTitle" :visible.sync="isTicketPlanSelectorOpen" width="70%"
|
||||
@close="isTicketPlanSelectorOpen=false" append-to-body>
|
||||
<receiving-ticket-plan
|
||||
ref="planSelector"
|
||||
:payable-data="choosePayable"
|
||||
:selected-plans="choosePayable.ticketPlans"
|
||||
@confirm="handleTicketPlanConfirm"
|
||||
/>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="isTicketPlanSelectorOpen=false">取 消</el-button>
|
||||
<!-- <el-button type="primary" @click="handleConfirm" >确 定</el-button>-->
|
||||
<el-button type="primary" @click="handleChooseConfirm">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!-- 收票计划选择器弹窗 -->
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ReceivingTicketPlan from './ReceivingTicketPlan.vue';
|
||||
import {getReceivingTicketPlan} from "@/api/finance/payable";
|
||||
|
||||
export default {
|
||||
name: "MergeReceiptDialog",
|
||||
components: {ReceivingTicketPlan},
|
||||
dicts: ['invoice_status','payment_bill_type'], // Add dicts for dict-tag
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
payableOrders: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internalVisible: this.visible,
|
||||
planTitle: '',
|
||||
choosePayable: {},
|
||||
form: {
|
||||
ticketBillType: 'FROM_PAYABLE', // Default to a type, or make it dynamic
|
||||
vendorName: '',
|
||||
ticketTime: null,
|
||||
vendorTicketTime: null,
|
||||
},
|
||||
payableOrdersWithPlans: [], // Each order will now have its own ticketPlans array
|
||||
isTicketPlanSelectorOpen: false,
|
||||
currentPayableOrderIndexForPlan: -1, // Index of the order in payableOrdersWithPlans
|
||||
loadingTicketPlans: false, // Loading state for fetching ticket plans
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
dialogVisible: {
|
||||
get() {
|
||||
return this.internalVisible;
|
||||
},
|
||||
set(val) {
|
||||
this.internalVisible = val;
|
||||
this.$emit('update:visible', val);
|
||||
}
|
||||
},
|
||||
totalPayableAmountWithTax() {
|
||||
return this.payableOrdersWithPlans.reduce((sum, order) => sum + (order.totalPriceWithTax || 0), 0);
|
||||
},
|
||||
totalPlannedAmount() {
|
||||
return this.payableOrdersWithPlans.reduce((orderSum, order) => {
|
||||
const orderPlansTotal = (order.ticketPlans || []).reduce((planSum, plan) => planSum + (plan.planAmount || 0), 0);
|
||||
return orderSum + orderPlansTotal;
|
||||
}, 0);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
visible(newVal) {
|
||||
this.internalVisible = newVal;
|
||||
if (newVal) {
|
||||
this.initDialogData();
|
||||
}
|
||||
},
|
||||
payableOrders: {
|
||||
handler(newVal) {
|
||||
if (this.dialogVisible) {
|
||||
this.initDialogData();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initDialogData() {
|
||||
// Initialize form fields
|
||||
if (this.payableOrders.length > 0) {
|
||||
const firstVendorName = this.payableOrders[0].vendorName;
|
||||
const allSameVendor = this.payableOrders.every(order => order.vendorName === firstVendorName);
|
||||
this.form.vendorName = allSameVendor ? firstVendorName : '多个制造商';
|
||||
this.form.ticketTime = null; // Reset time
|
||||
this.form.vendorTicketTime = null;
|
||||
} else {
|
||||
this.form.vendorName = '';
|
||||
this.form.ticketTime = null;
|
||||
this.form.vendorTicketTime = null;
|
||||
}
|
||||
this.form.ticketBillType = 'FROM_PAYABLE'; // Default
|
||||
|
||||
// Initialize payableOrdersWithPlans
|
||||
this.payableOrdersWithPlans = this.payableOrders.map(order => {
|
||||
const ticketPlans = order.ticketPlans ? [...order.ticketPlans] : [];
|
||||
if (ticketPlans.length === 0 && order.lastTicketPlanId) {
|
||||
ticketPlans.push({
|
||||
id: order.lastTicketPlanId,
|
||||
planAmount: order.planTicketAmount,
|
||||
taxRate: order.taxRate,
|
||||
planTicketDate: order.planTicketDate,
|
||||
planRate: this.$calc.mul(this.$calc.div(order.planTicketAmount, order.totalPriceWithTax, 4), 100)
|
||||
});
|
||||
}
|
||||
return {
|
||||
...order,
|
||||
ticketPlans: ticketPlans, // Retain existing plans if any, otherwise empty
|
||||
totalPriceWithTax: order.totalPriceWithTax || 0, // Ensure numeric for calculations
|
||||
unInvoicedAmount: order.unInvoicedAmount || 0,
|
||||
invoicedAmount: order.invoicedAmount || 0, // Ensure numeric for calculations
|
||||
}
|
||||
});
|
||||
},
|
||||
handleClose() {
|
||||
this.dialogVisible = false;
|
||||
this.resetForm();
|
||||
},
|
||||
handleChooseConfirm() {
|
||||
if (!this.$refs.planSelector) {
|
||||
this.$modal.msgError('无法获取计划选择器组件');
|
||||
return;
|
||||
}
|
||||
const selectedPlans = this.$refs.planSelector.selectedPlan || [];
|
||||
|
||||
const orderIndex = this.payableOrdersWithPlans.findIndex(o => o.id === this.choosePayable.id);
|
||||
if (orderIndex === -1) {
|
||||
this.$modal.msgError('找不到要更新的应付单');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentOrder = this.payableOrdersWithPlans[orderIndex];
|
||||
|
||||
// Update the ticket plans for the specific order
|
||||
this.$set(currentOrder, 'ticketPlans', [...selectedPlans]);
|
||||
|
||||
this.isTicketPlanSelectorOpen = false;
|
||||
this.$modal.msgSuccess(`已更新收票计划选择,共 ${selectedPlans.length} 条`);
|
||||
},
|
||||
handleConfirm() {
|
||||
// Validate main form fields
|
||||
if (!this.form.ticketBillType) {
|
||||
this.$modal.msgError('请选择收票单类型');
|
||||
return;
|
||||
}
|
||||
// if (!this.form.ticketTime) {
|
||||
// this.$modal.msgError('请选择预计收票时间');
|
||||
// return;
|
||||
// }
|
||||
if (!this.form.vendorTicketTime) {
|
||||
this.$modal.msgError('请选择制造商开票时间');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate each payable order's ticket plans
|
||||
for (const order of this.payableOrdersWithPlans) {
|
||||
if (!order.ticketPlans || order.ticketPlans.length === 0) {
|
||||
this.$modal.msgError(`应付单 ${order.payableBillCode} 至少需要一条收票计划`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (const plan of order.ticketPlans) {
|
||||
if (!plan.planTicketDate) {
|
||||
this.$modal.msgError(`应付单 ${order.payableBillCode} 的收票计划中预计收票时间不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount <= 0) {
|
||||
this.$modal.msgError(`应付单 ${order.payableBillCode} 的收票计划中预计收票金额必须大于0。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planRate === null || plan.planRate === undefined) {
|
||||
this.$modal.msgError(`应付单 ${order.payableBillCode} 的收票计划中收票比例不能为空。`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the final data to be emitted to the parent
|
||||
const mergedReceiptData = {
|
||||
ticketBillType: this.form.ticketBillType,
|
||||
ticketTime: this.form.ticketTime,
|
||||
vendorTicketTime: this.form.vendorTicketTime,
|
||||
// Collect all payable orders with their updated ticket plans
|
||||
payableOrders: this.payableOrdersWithPlans.map(order => ({
|
||||
id: order.id,
|
||||
payableBillCode: order.payableBillCode,
|
||||
ticketPlans: order.ticketPlans.map(plan => ({
|
||||
planTicketDate: plan.planTicketDate,
|
||||
planAmount: plan.planAmount,
|
||||
planRate: plan.planRate,
|
||||
taxRate: order.taxRate,
|
||||
remark: plan.remark,
|
||||
id: plan.id,
|
||||
})),
|
||||
})),
|
||||
totalMergeTicketAmount: this.totalPlannedAmount, // Total amount for the merged bill
|
||||
};
|
||||
|
||||
this.$emit('confirm', mergedReceiptData);
|
||||
this.dialogVisible = false;
|
||||
},
|
||||
handleCancel() {
|
||||
this.dialogVisible = false;
|
||||
this.resetForm();
|
||||
},
|
||||
resetForm() {
|
||||
this.form = {
|
||||
ticketBillType: 'FROM_PAYABLE',
|
||||
vendorName: '',
|
||||
ticketTime: null,
|
||||
vendorTicketTime: null,
|
||||
};
|
||||
this.payableOrdersWithPlans = [];
|
||||
|
||||
this.currentPayableOrderIndexForPlan = -1;
|
||||
this.loadingTicketPlans = false;
|
||||
},
|
||||
handleOpenTicketPlanSelector(row, index) {
|
||||
this.planTitle = `选择收票计划 - ${row.payableBillCode}`;
|
||||
this.choosePayable = row;
|
||||
this.currentPayableOrderIndexForPlan = index;
|
||||
this.isTicketPlanSelectorOpen = true;
|
||||
console.log(this.choosePayable.id)
|
||||
},
|
||||
handleTicketPlanConfirm(updatedPlans) {
|
||||
// Update the ticket plans for the specific order
|
||||
if (this.currentPayableOrderIndexForPlan !== -1) {
|
||||
this.$set(this.payableOrdersWithPlans[this.currentPayableOrderIndexForPlan], 'ticketPlans', updatedPlans);
|
||||
}
|
||||
this.isTicketPlanSelectorOpen = false;
|
||||
this.currentPayableOrderIndexForPlan = -1;
|
||||
},
|
||||
calculateOrderCurrentTicketAmount(orderId) {
|
||||
const order = this.payableOrdersWithPlans.find(o => o.id === orderId);
|
||||
if (order && order.ticketPlans) {
|
||||
return order.ticketPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
calculateOrderCurrentTicketRate(orderId) {
|
||||
const order = this.payableOrdersWithPlans.find(o => o.id === orderId);
|
||||
if (order && order.ticketPlans && order.unInvoicedAmount >= 0) {
|
||||
const currentAmount = this.calculateOrderCurrentTicketAmount(orderId);
|
||||
return this.$calc.mul(this.$calc.div(currentAmount ,order.totalPriceWithTax,4 ),100);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px; /* To prevent scrollbar from overlapping content */
|
||||
}
|
||||
|
||||
.total-info {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
<template>
|
||||
|
||||
<div class="dialog-body">
|
||||
<el-divider content-position="left">付款计划</el-divider>
|
||||
<el-button v-if="isEditing" type="primary" size="mini" @click="handleSavePaymentPlan"
|
||||
style="margin-bottom: 10px;">
|
||||
保存付款计划
|
||||
</el-button>
|
||||
<el-button v-if="isEditing" type="primary" size="mini" @click="handleSyncToTicketPlan"
|
||||
style="margin-bottom: 10px; margin-left: 10px;">
|
||||
同步至发票计划
|
||||
</el-button>
|
||||
<el-button v-else type="primary" size="mini" @click="isEditing=true"
|
||||
style="margin-bottom: 10px;">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-table :data="paymentPlans" border @selection-change="selectPlan" ref="paymentPlanTable">
|
||||
<el-table-column type="selection" width="50" align="center" :selectable="selectableRow"/>
|
||||
<el-table-column label="序号" type="index" width="50" align="center"></el-table-column>
|
||||
<el-table-column label="预计付款时间" align="center" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-date-picker v-model="scope.row.planPaymentDate" type="datetime" style="width: 180px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="选择日期"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"></el-date-picker>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计付款金额" align="center" width="230">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.planAmount"
|
||||
:precision="2"
|
||||
:step="100"
|
||||
:min="0.01"
|
||||
:readonly="!scope.row.detailId"
|
||||
:max="totalPriceWithTax"
|
||||
@change="handleAmountChange(scope.row)"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"
|
||||
></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="应付比例(%)" align="center" width="230">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.planRate"
|
||||
:precision="2"
|
||||
:step="1"
|
||||
:min="0.01"
|
||||
:max="100"
|
||||
:readonly="!scope.row.detailId"
|
||||
@change="handleRateChange(scope.row)"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"
|
||||
></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.remark" placeholder="请输入备注"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="150" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="isEditing && !isNumberStr(scope.row.detailId)"
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-plus"
|
||||
@click="handleAddPaymentPlanRow"
|
||||
>增加下行
|
||||
</el-button>
|
||||
<el-button v-if="isEditing && !isNumberStr(scope.row.detailId)"
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDeletePaymentPlanRow(scope.$index)"
|
||||
:disabled="paymentPlans.length === 1 || scope.row.status === 'paid'"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- <div class="total-info">-->
|
||||
<!-- <span>应付单未付款金额: <el-tag type="info">{{ totalUnpaidAmount.toFixed(2) }}</el-tag></span>-->
|
||||
<!-- <span style="margin-left: 20px;">计划付款总金额: <el-tag type="success">{{-->
|
||||
<!-- totalPlannedAmount.toFixed(2)-->
|
||||
<!-- }}</el-tag></span>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getPaymentPlan, updatePaymentPlan, syncToTicketPlan} from "@/api/finance/payable";
|
||||
import {isNumberStr} from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "PaymentPlanSelector",
|
||||
props: {
|
||||
payableData: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
}
|
||||
},
|
||||
isInitEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selectedPlans: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedPlan:[],
|
||||
isEditing: false,
|
||||
loading: false,
|
||||
paymentPlans: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return `选择付款计划 - ${this.payableData.payableBillCode}`;
|
||||
},
|
||||
totalPlannedAmount() {
|
||||
return this.paymentPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
},
|
||||
totalUnpaidAmount() {
|
||||
return this.payableData.unpaidAmount || 0;
|
||||
},
|
||||
totalPriceWithTax() {
|
||||
return this.payableData.totalPriceWithTax || 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'payableData.id': {
|
||||
handler(newVal, oldVal) {
|
||||
if (newVal) {
|
||||
this.fetchPaymentPlans(newVal)
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
isInitEdit: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.isEditing = newVal
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isNumberStr,
|
||||
selectableRow(row, index){
|
||||
return !row.detailId;
|
||||
},
|
||||
selectPlan( selection){
|
||||
this.selectedPlan=selection
|
||||
},
|
||||
fetchPaymentPlans(payableId) {
|
||||
if (this.payableData && payableId) {
|
||||
getPaymentPlan(payableId).then(response => {
|
||||
this.paymentPlans = response.data.map(item => ({
|
||||
...item,
|
||||
// Add a default status if not present, similar to EditForm.vue
|
||||
status: item.status || 'pending'
|
||||
}));
|
||||
if (this.paymentPlans.length === 0) {
|
||||
this.initDefaultPaymentPlan();
|
||||
} else {
|
||||
this.$nextTick(() => {
|
||||
this.paymentPlans.forEach(plan => {
|
||||
const isSelected = this.selectedPlans.some(selected => selected.id === plan.id);
|
||||
if (isSelected) {
|
||||
this.$refs.paymentPlanTable.toggleRowSelection(plan, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.initDefaultPaymentPlan();
|
||||
|
||||
}
|
||||
},
|
||||
initDefaultPaymentPlan() {
|
||||
// Default to a single plan covering the unpaid amount if no initial plans
|
||||
this.paymentPlans = [{
|
||||
planPaymentDate: null,
|
||||
planAmount: this.totalUnpaidAmount,
|
||||
planRate: 100,
|
||||
remark: '',
|
||||
status: 'pending' // Default status
|
||||
}];
|
||||
},
|
||||
handleSavePaymentPlan() {
|
||||
if (!this.validatePaymentPlans()) {
|
||||
return;
|
||||
}
|
||||
if (!this.validatePaymentPlanTotals()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.paymentPlans.length; i++) {
|
||||
const plan = this.paymentPlans[i];
|
||||
if (!plan.planPaymentDate) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行付款计划的预计付款时间不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount === '' || plan.planAmount < 0) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行付款计划的预计付款金额不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planRate === null || plan.planRate === undefined || plan.planRate === '') {
|
||||
this.$modal.msgError(`第 ${i + 1} 行付款计划的应付比例不能为空。`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
updatePaymentPlan(this.payableData.id, this.paymentPlans).then(() => {
|
||||
this.$modal.msgSuccess("保存成功");
|
||||
this.fetchPaymentPlans(this.payableData.id); // Re-fetch using the correct method and ID
|
||||
});
|
||||
},
|
||||
handleSyncToTicketPlan() {
|
||||
this.$modal.confirm('是否确认同步付款计划至发票计划?').then(() => {
|
||||
return syncToTicketPlan(this.payableData.id);
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess("同步成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
handleAddPaymentPlanRow() {
|
||||
this.paymentPlans.push({
|
||||
planPaymentDate: null,
|
||||
planAmount: 0,
|
||||
planRate: 0,
|
||||
remark: '',
|
||||
status: 'pending' // Default status
|
||||
});
|
||||
},
|
||||
handleDeletePaymentPlanRow(index) {
|
||||
if (this.paymentPlans.length === 1) {
|
||||
this.$modal.msgError("至少需要保留一条付款计划。");
|
||||
return;
|
||||
}
|
||||
// Only allow deletion if not 'paid' or status is not set (newly added)
|
||||
if (this.paymentPlans[index].status === 'paid') {
|
||||
this.$modal.msgError("已付款的计划不能删除。");
|
||||
return;
|
||||
}
|
||||
this.paymentPlans.splice(index, 1);
|
||||
},
|
||||
handleAmountChange(row) {
|
||||
if (this.totalPriceWithTax === 0) {
|
||||
row.planRate = 0;
|
||||
return;
|
||||
}
|
||||
row.planRate = this.$calc.mul((this.$calc.div(row.planAmount, this.totalPriceWithTax,4)), 100);
|
||||
},
|
||||
handleRateChange(row) {
|
||||
row.planAmount = this.$calc.div(this.$calc.mul(this.totalPriceWithTax , row.planRate),100);
|
||||
},
|
||||
validatePaymentPlanTotals() {
|
||||
const totalAmount = this.paymentPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
const totalRate = this.paymentPlans.reduce((sum, plan) => sum + (plan.planRate || 0), 0);
|
||||
|
||||
if (totalAmount !== this.totalPriceWithTax) {
|
||||
this.$modal.msgError(`预计付款金额之和应该等于应付总金额[${this.totalPriceWithTax}]`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
validatePaymentPlans() {
|
||||
if (this.paymentPlans.length === 0) {
|
||||
this.$modal.msgError("请至少添加一条付款计划。");
|
||||
return false;
|
||||
}
|
||||
|
||||
const totalPlannedAmount = this.paymentPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
|
||||
for (let i = 0; i < this.paymentPlans.length; i++) {
|
||||
const plan = this.paymentPlans[i];
|
||||
if (!plan.planPaymentDate) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行付款计划的预计付款时间不能为空。`);
|
||||
return false;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount <= 0) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行付款计划的预计付款金额必须大于0。`);
|
||||
return false;
|
||||
}
|
||||
if (plan.planRate === null || plan.planRate === undefined || plan.planRate === '') {
|
||||
this.$modal.msgError(`第 ${i + 1} 行付款计划的应付比例不能为空。`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px; /* To prevent scrollbar from overlapping content */
|
||||
}
|
||||
|
||||
.total-info {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
<template>
|
||||
|
||||
<div class="dialog-body">
|
||||
<el-divider content-position="left">收票计划</el-divider>
|
||||
<el-button v-if="isEditing" type="primary" size="mini" @click="handleSaveTicketPlan"
|
||||
style="margin-bottom: 10px;">
|
||||
保存收票计划
|
||||
</el-button>
|
||||
<el-button v-else type="primary" size="mini" @click="isEditing=true"
|
||||
style="margin-bottom: 10px;">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-table :data="ticketPlans" border @selection-change="selectPlan" ref="ticketPlanTable">
|
||||
<el-table-column type="selection" width="50" align="center" :selectable="selectableRow"/>
|
||||
<el-table-column label="序号" type="index" width="50" align="center"></el-table-column>
|
||||
<el-table-column label="预计收票时间" align="center" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-date-picker v-model="scope.row.planTicketDate" type="datetime" style="width: 180px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="选择日期"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"></el-date-picker>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计收票金额" align="center" width="230">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.planAmount"
|
||||
:precision="2"
|
||||
:step="100"
|
||||
:min="0.01"
|
||||
:readonly="!scope.row.detailId"
|
||||
:max="totalPriceWithTax"
|
||||
@change="handleAmountChange(scope.row)"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"
|
||||
></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收票比例(%)" align="center" width="230">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.planRate"
|
||||
:precision="2"
|
||||
:step="1"
|
||||
:min="0.01"
|
||||
:max="100"
|
||||
:readonly="!scope.row.detailId"
|
||||
@change="handleRateChange(scope.row)"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"
|
||||
></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.remark" placeholder="请输入备注"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="150" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="isEditing && !isNumberStr(scope.row.detailId)"
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-plus"
|
||||
@click="handleAddTicketPlanRow"
|
||||
>增加下行
|
||||
</el-button>
|
||||
<el-button v-if="isEditing && !isNumberStr(scope.row.detailId)"
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDeleteTicketPlanRow(scope.$index)"
|
||||
:disabled="ticketPlans.length === 1 || scope.row.status === 'received'"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getReceivingTicketPlan, updateReceivingTicketPlan} from "@/api/finance/payable";
|
||||
import {isNumberStr} from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "ReceivingTicketPlan",
|
||||
props: {
|
||||
payableData: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
}
|
||||
},
|
||||
isInitEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selectedPlans: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedPlan:[],
|
||||
isEditing: false,
|
||||
loading: false,
|
||||
ticketPlans: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return `选择收票计划 - ${this.payableData.payableBillCode}`;
|
||||
},
|
||||
totalPlannedAmount() {
|
||||
return this.ticketPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
},
|
||||
totalUnpaidAmount() {
|
||||
return this.payableData.unInvoicedAmount || 0;
|
||||
},
|
||||
totalPriceWithTax() {
|
||||
return this.payableData.totalPriceWithTax || 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'payableData.id': {
|
||||
handler(newVal, oldVal) {
|
||||
if (newVal) {
|
||||
this.fetchTicketPlans(newVal)
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
isInitEdit: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.isEditing = newVal
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isNumberStr,
|
||||
selectableRow(row, index){
|
||||
return !row.detailId;
|
||||
},
|
||||
selectPlan( selection){
|
||||
this.selectedPlan=selection
|
||||
},
|
||||
fetchTicketPlans(payableId) {
|
||||
if (this.payableData && payableId) {
|
||||
getReceivingTicketPlan(payableId).then(response => {
|
||||
this.ticketPlans = response.data.map(item => ({
|
||||
...item,
|
||||
status: item.status || 'pending'
|
||||
}));
|
||||
if (this.ticketPlans.length === 0) {
|
||||
this.initDefaultTicketPlan();
|
||||
} else {
|
||||
this.$nextTick(() => {
|
||||
this.ticketPlans.forEach(plan => {
|
||||
const isSelected = this.selectedPlans.some(selected => selected.id === plan.id);
|
||||
if (isSelected) {
|
||||
this.$refs.ticketPlanTable.toggleRowSelection(plan, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.initDefaultTicketPlan();
|
||||
|
||||
}
|
||||
},
|
||||
initDefaultTicketPlan() {
|
||||
this.ticketPlans = [{
|
||||
planTicketDate: null,
|
||||
planAmount: this.totalPriceWithTax,
|
||||
planRate: 100,
|
||||
remark: '',
|
||||
status: 'pending'
|
||||
}];
|
||||
},
|
||||
handleSaveTicketPlan() {
|
||||
if (!this.validateTicketPlans()) {
|
||||
return;
|
||||
}
|
||||
if (!this.validateTicketPlanTotals()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.ticketPlans.length; i++) {
|
||||
const plan = this.ticketPlans[i];
|
||||
if (!plan.planTicketDate) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行收票计划的预计收票时间不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount === '' || plan.planAmount < 0) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行收票计划的预计收票金额不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planRate === null || plan.planRate === undefined || plan.planRate === '') {
|
||||
this.$modal.msgError(`第 ${i + 1} 行收票计划的收票比例不能为空。`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
updateReceivingTicketPlan(this.payableData.id, this.ticketPlans).then(() => {
|
||||
this.$modal.msgSuccess("保存成功");
|
||||
this.fetchTicketPlans(this.payableData.id);
|
||||
});
|
||||
},
|
||||
|
||||
handleAddTicketPlanRow() {
|
||||
this.ticketPlans.push({
|
||||
planTicketDate: null,
|
||||
planAmount: 0,
|
||||
planRate: 0,
|
||||
remark: '',
|
||||
status: 'pending'
|
||||
});
|
||||
},
|
||||
handleDeleteTicketPlanRow(index) {
|
||||
if (this.ticketPlans.length === 1) {
|
||||
this.$modal.msgError("至少需要保留一条收票计划。");
|
||||
return;
|
||||
}
|
||||
if (this.ticketPlans[index].status === 'received') {
|
||||
this.$modal.msgError("已收票的计划不能删除。");
|
||||
return;
|
||||
}
|
||||
this.ticketPlans.splice(index, 1);
|
||||
},
|
||||
handleAmountChange(row) {
|
||||
if (this.totalPriceWithTax === 0) {
|
||||
row.planRate = 0;
|
||||
return;
|
||||
}
|
||||
row.planRate = this.$calc.mul((this.$calc.div(row.planAmount, this.totalPriceWithTax,4)), 100);
|
||||
},
|
||||
handleRateChange(row) {
|
||||
row.planAmount = this.$calc.div(this.$calc.mul(this.totalPriceWithTax , row.planRate),100);
|
||||
},
|
||||
validateTicketPlanTotals() {
|
||||
const totalAmount = this.ticketPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
|
||||
if (totalAmount !== this.totalPriceWithTax) {
|
||||
this.$modal.msgError(`预计收票金额之和应该等于应付总金额[${this.totalPriceWithTax}]`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
validateTicketPlans() {
|
||||
if (this.ticketPlans.length === 0) {
|
||||
this.$modal.msgError("请至少添加一条收票计划。");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.ticketPlans.length; i++) {
|
||||
const plan = this.ticketPlans[i];
|
||||
if (!plan.planTicketDate) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行收票计划的预计收票时间不能为空。`);
|
||||
return false;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount <= 0) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行收票计划的预计收票金额必须大于0。`);
|
||||
return false;
|
||||
}
|
||||
if (plan.planRate === null || plan.planRate === undefined || plan.planRate === '') {
|
||||
this.$modal.msgError(`第 ${i + 1} 行收票计划的应付比例不能为空。`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px; /* To prevent scrollbar from overlapping content */
|
||||
}
|
||||
|
||||
.total-info {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,442 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="150px">
|
||||
<!-- <el-form-item label="项目编号" prop="projectCode">-->
|
||||
<!-- <el-input-->
|
||||
<!-- v-model="queryParams.projectCode"-->
|
||||
<!-- placeholder="请输入项目编号"-->
|
||||
<!-- clearable-->
|
||||
<!-- @keyup.enter.native="handleQuery"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="采购-应付单编号" prop="payableBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.payableBillCode"
|
||||
placeholder="请输入应付单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="制造商名称" prop="vendorName">
|
||||
<el-input
|
||||
v-model="queryParams.vendorName"
|
||||
placeholder="请输入制造商名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="合同编号" prop="orderCode">-->
|
||||
<!-- <el-input-->
|
||||
<!-- v-model="queryParams.orderCode"-->
|
||||
<!-- placeholder="请输入合同编号"-->
|
||||
<!-- clearable-->
|
||||
<!-- @keyup.enter.native="handleQuery"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="出入库单号" prop="inventoryCode">-->
|
||||
<!-- <el-input-->
|
||||
<!-- v-model="queryParams.inventoryCode"-->
|
||||
<!-- placeholder="请输入出入库单号"-->
|
||||
<!-- clearable-->
|
||||
<!-- @keyup.enter.native="handleQuery"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="产品类型" prop="productType">
|
||||
<el-select v-model="queryParams.productType" placeholder="请选择产品类型" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.product_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="付款状态" prop="paymentStatus">-->
|
||||
<!-- <el-select v-model="queryParams.paymentStatus" placeholder="请选择付款状态" clearable>-->
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="dict in dict.type.payment_status"-->
|
||||
<!-- :key="dict.value"-->
|
||||
<!-- :label="dict.label"-->
|
||||
<!-- :value="dict.value"-->
|
||||
<!-- />-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="收票状态" prop="paymentStatus">-->
|
||||
<!-- <el-select v-model="queryParams.paymentStatus" placeholder="请选择付款状态" clearable>-->
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="dict in dict.type.payment_status"-->
|
||||
<!-- :key="dict.value"-->
|
||||
<!-- :label="dict.label"-->
|
||||
<!-- :value="dict.value"-->
|
||||
<!-- />-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="生成时间">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- v-model="dateRange"-->
|
||||
<!-- style="width: 240px"-->
|
||||
<!-- value-format="yyyy-MM-dd HH:mm:ss"-->
|
||||
<!-- type="daterange"-->
|
||||
<!-- range-separator="-"-->
|
||||
<!-- start-placeholder="开始日期"-->
|
||||
<!-- end-placeholder="结束日期"-->
|
||||
<!-- ></el-date-picker>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="预计付款时间">
|
||||
<el-date-picker
|
||||
v-model="estimatedPaymentDateRange"
|
||||
style="width: 350px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5" >
|
||||
<el-button type="primary" plain @click="handleMergeAndInitiatePayment" v-hasPermi="['finance:payable:mergePayment']">
|
||||
合并发起付款单
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5" >
|
||||
<el-button type="primary" plain @click="handleMergeAndInitiateReceipt" v-hasPermi="['inventory:inner:add']">
|
||||
合并发起收票单
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="payableList" show-summary :summary-method="getSummaries" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="50" />
|
||||
<!-- <el-table-column label="项目编号" align="center" prop="projectCode" width="120" />-->
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" width="260" />
|
||||
<el-table-column label="采购-应付单编号" align="center" prop="payableBillCode" width="150" />
|
||||
<!-- <el-table-column label="生成时间" align="center" prop="createTime" width="180"/>-->
|
||||
<el-table-column label="预计付款时间" align="center" prop="planPaymentDate" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span :style="getPaymentDateStyle(scope.row.planPaymentDate)">{{ scope.row.planPaymentDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计付款金额" align="center" prop="planAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="该制造商是否有预付单" align="center" prop="hasAdvancePayment" width="150">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.preResidueAmount == 0 ? '否' : '是' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="预付金额" align="center" prop="advancePaymentAmount" width="120" />-->
|
||||
<el-table-column label="制造商名称" align="center" prop="vendorName" width="150" />
|
||||
<!-- <el-table-column label="合同编号" align="center" prop="orderCode" width="150" />-->
|
||||
<!-- <el-table-column label="出入库单号" align="center" prop="inventoryCode" width="150" />-->
|
||||
<el-table-column label="产品类型" align="center" prop="productType" width="120">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<!-- <el-table-column label="未税总价" align="center" prop="totalPriceWithoutTax" width="120" />-->
|
||||
<!-- <el-table-column label="税额" align="center" prop="taxAmount" width="120" />-->
|
||||
<!-- <el-table-column label="付款状态" align="center" prop="paymentStatus" width="120">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- {{ scope.row.unpaidAmount === scope.row.totalPriceWithTax ? '未生成' : scope.row.unpaidAmount === 0 ? '全部生成' : '部分生成' }}-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column label="生成付款单" align="center" width="120">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- {{scope.row.unpaidAmount===scope.row.totalPriceWithTax?'未生成':scope.row.unpaidAmount===0?'全部生成':'部分生成'}}-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column label="收票状态" align="center" prop="invoiceStatus" width="120">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <dict-tag :options="dict.type.invoice_status" :value="scope.row.invoiceStatus"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column label="生成收票单" align="center" width="120">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <el-button-->
|
||||
<!-- size="mini"-->
|
||||
<!-- type="text"-->
|
||||
<!-- @click="handleGenerateInvoice(scope.row)"-->
|
||||
<!-- >生成收票单</el-button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="未付款金额(元)" align="center" prop="unpaidPaymentAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
|
||||
<el-table-column label="未收票金额(元)" align="center" prop="unreceivedTicketAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
|
||||
<!-- <el-table-column label="付款中金额" align="center" prop="payingAmount" width="120" />-->
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="300" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['finance:payable:edit']"
|
||||
>查看详情</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
v-show="scope.row.unpaidPaymentAmount>0"
|
||||
@click="handleGeneratedPayment(scope.row)"
|
||||
v-hasPermi="['finance:payable:edit']"
|
||||
>生成付款单</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
v-show="scope.row.unreceivedTicketAmount>0"
|
||||
@click="handleGeneratedTicket(scope.row)"
|
||||
v-hasPermi="['finance:payable:edit']"
|
||||
>生成收票单</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 修改弹窗 -->
|
||||
<edit-form :visible.sync="open" :data="selectedRow" @close="getList" />
|
||||
|
||||
<!-- 合并付款单弹窗 -->
|
||||
<merge-payment-dialog :visible.sync="isMergePaymentDialogOpen" :payable-orders="selectedPayableRows" @confirm="confirmMergePayment" />
|
||||
|
||||
<!-- 合并收票单弹窗 -->
|
||||
<merge-receipt-dialog :visible.sync="isMergeReceiptDialogOpen" :payable-orders="selectedPayableRows" @confirm="confirmMergeReceipt" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listPayable, mergeAndInitiatePayment, mergeAndInitiateReceipt } from "@/api/finance/payable";
|
||||
import EditForm from './components/EditForm.vue';
|
||||
import MergePaymentDialog from './components/MergePaymentDialog.vue';
|
||||
import MergeReceiptDialog from './components/MergeReceiptDialog.vue';
|
||||
|
||||
export default {
|
||||
name: "Payable",
|
||||
components: { EditForm, MergePaymentDialog, MergeReceiptDialog },
|
||||
dicts: ['product_type', 'payment_status', 'invoice_status'],
|
||||
data() {
|
||||
return {
|
||||
// 。。。 (other data properties)
|
||||
// 是否显示修改弹窗
|
||||
open: false,
|
||||
// 选中行数据
|
||||
selectedRow: {},
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 采购应付单表格数据
|
||||
payableList: [],
|
||||
// 日期范围
|
||||
dateRange: [],
|
||||
// 预计付款日期范围
|
||||
estimatedPaymentDateRange: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectCode: null,
|
||||
projectName: null,
|
||||
payableBillCode: null,
|
||||
vendorName: null,
|
||||
orderCode: null,
|
||||
inventoryCode: null,
|
||||
productType: null,
|
||||
paymentStatus: null,
|
||||
createTimeStart: null,
|
||||
createTimeEnd: null,
|
||||
estimatedPaymentTimeStart: null,
|
||||
estimatedPaymentTimeEnd: null,
|
||||
orderByColumn:'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
// 选中行数据
|
||||
selectedPayableRows: [],
|
||||
// 是否显示合并付款弹窗
|
||||
isMergePaymentDialogOpen: false,
|
||||
// 是否显示合并收票弹窗
|
||||
isMergeReceiptDialogOpen: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询采购应付单列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
if (null != this.dateRange && '' != this.dateRange) {
|
||||
this.queryParams.createTimeStart = this.dateRange[0];
|
||||
this.queryParams.createTimeEnd = this.dateRange[1];
|
||||
}
|
||||
if (null != this.estimatedPaymentDateRange && '' != this.estimatedPaymentDateRange) {
|
||||
this.queryParams.estimatedPaymentTimeStart = this.estimatedPaymentDateRange[0];
|
||||
this.queryParams.estimatedPaymentTimeEnd = this.estimatedPaymentDateRange[1];
|
||||
}
|
||||
listPayable(this.queryParams).then(response => {
|
||||
this.payableList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.estimatedPaymentDateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.queryParams.createTimeStart=null;
|
||||
this.queryParams.createTimeEnd=null;
|
||||
this.queryParams.estimatedPaymentTimeStart=null;
|
||||
this.queryParams.estimatedPaymentTimeEnd=null;
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.selectedRow = row;
|
||||
this.open = true;
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
this.$modal.confirm('是否确认删除采购应付单编号为"' + row.payableBillCode + '"的数据项?').then(function() {
|
||||
return Promise.resolve();
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 多选框选中数据 */
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedPayableRows = selection;
|
||||
},
|
||||
handleGeneratedPayment(row) {
|
||||
this.selectedPayableRows=[row]
|
||||
this.handleMergeAndInitiatePayment()
|
||||
},
|
||||
handleGeneratedTicket(row) {
|
||||
this.selectedPayableRows=[row]
|
||||
this.handleMergeAndInitiateReceipt()
|
||||
},
|
||||
/** 合并并发起付款单按钮操作 */
|
||||
handleMergeAndInitiatePayment() {
|
||||
if (this.selectedPayableRows.length === 0) {
|
||||
this.$modal.msgWarning("请选择至少一条应付单进行合并操作");
|
||||
return;
|
||||
}
|
||||
let every = this.selectedPayableRows.every(item=>item.planAmount>0);
|
||||
if (!every){
|
||||
this.$modal.msgWarning("温馨提示:您勾选的应付单中有已全部付款完成的应付单,请勿重复操作");
|
||||
return;
|
||||
}
|
||||
let vendorLength = new Set(this.selectedPayableRows.map(item=>item.vendorCode)).size;
|
||||
if (vendorLength > 1) {
|
||||
this.$modal.msgWarning("温馨提示:您勾选的应付单中有不同供应商,合并发起付款单需为同一供应商,请重新勾选");
|
||||
return;
|
||||
}
|
||||
|
||||
this.isMergePaymentDialogOpen = true;
|
||||
},
|
||||
/** 确认合并付款单操作 */
|
||||
confirmMergePayment(paymentData) {
|
||||
mergeAndInitiatePayment(paymentData).then(() => {
|
||||
this.$modal.msgSuccess("合并付款单发起成功");
|
||||
this.isMergePaymentDialogOpen = false;
|
||||
this.getList(); // Refresh the list
|
||||
});
|
||||
},
|
||||
/** 合并并发起收票单按钮操作 */
|
||||
handleMergeAndInitiateReceipt() {
|
||||
if (this.selectedPayableRows.length === 0) {
|
||||
this.$modal.msgWarning("请选择至少一条应付单进行合并操作");
|
||||
return;
|
||||
}
|
||||
let every = this.selectedPayableRows.every(item=>item.planTicketAmount>0);
|
||||
if (!every){
|
||||
this.$modal.msgWarning("温馨提示:您勾选的应付单中有已全部收票完成的应付单,请勿重复操作");
|
||||
return;
|
||||
}
|
||||
let vendorLength = new Set(this.selectedPayableRows.map(item=>item.vendorCode)).size;
|
||||
if (vendorLength > 1) {
|
||||
this.$modal.msgWarning("温馨提示:您勾选的应付单中有不同供应商,合并发起收票单需为同一供应商,请重新勾选");
|
||||
return;
|
||||
}
|
||||
|
||||
this.isMergeReceiptDialogOpen = true;
|
||||
},
|
||||
/** 确认合并收票单操作 */
|
||||
confirmMergeReceipt(receiptData) {
|
||||
mergeAndInitiateReceipt(receiptData).then(() => {
|
||||
this.$modal.msgSuccess("合并收票单发起成功");
|
||||
this.isMergeReceiptDialogOpen = false;
|
||||
this.getList(); // Refresh the list
|
||||
});
|
||||
},
|
||||
getSummaries(param) {
|
||||
const { columns, data } = param;
|
||||
const sums = [];
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '合计';
|
||||
return;
|
||||
}
|
||||
const values = data.map(item => Number(item[column.property]));
|
||||
if (column.property === 'planAmount' || column.property === 'totalPriceWithTax') {
|
||||
if (!values.every(value => isNaN(value))) {
|
||||
sums[index] = values.reduce((prev, curr) => {
|
||||
const value = Number(curr);
|
||||
if (!isNaN(value)) {
|
||||
return prev + curr;
|
||||
} else {
|
||||
return prev;
|
||||
}
|
||||
}, 0);
|
||||
sums[index] = this.formatCurrency(sums[index]);
|
||||
} else {
|
||||
sums[index] = 'N/A';
|
||||
}
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
});
|
||||
return sums;
|
||||
},
|
||||
getPaymentDateStyle(dateStr) {
|
||||
|
||||
if (!dateStr) return {};
|
||||
let planDate = new Date(dateStr).getTime();
|
||||
let tenDaysLater = new Date().getTime() + 10 * 24 * 60 * 60 * 1000;
|
||||
if (planDate <= tenDaysLater) {
|
||||
return { color: '#ffba00' };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="项目编号" prop="projectCode">
|
||||
<el-input
|
||||
v-model="queryParams.projectCode"
|
||||
placeholder="请输入项目编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="计收状态" prop="chargeStatus">
|
||||
<el-select
|
||||
v-model="queryParams.chargeStatus"
|
||||
placeholder="请输入计收状态"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in dict.type.charge_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="付款状态" prop="paymentStatus">
|
||||
<el-select
|
||||
v-model="queryParams.paymentStatus"
|
||||
placeholder="请输入付款状态"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in dict.type.report_payment_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="收票状态" prop="ticketStatus">
|
||||
<el-select
|
||||
v-model="queryParams.ticketStatus"
|
||||
placeholder="请输入收票状态"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in dict.type.report_ticket_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-download"
|
||||
size="mini"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['finance:report:export']"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="reportList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="项目" align="center" prop="orderCode" width="180" v-if="columns.projectCode.visible || columns.projectName.visible" key="orderCode">
|
||||
<el-table-column label="项目编号" align="center" prop="projectCode" width="120" v-if="columns.projectCode.visible" key="receivableWithTax"/>
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" width="120" v-if="columns.projectName.visible" key="receivableWithoutTax"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 毛利 -->
|
||||
<el-table-column label="计收统计" align="center" v-if="columns.chargeStatus.visible || columns.chargedWithoutTax.visible || columns.grossProfit.visible ||columns.grossProfitRate.visible">
|
||||
<el-table-column label="计收状态" align="center" prop="chargeStatus" width="100" v-if="columns.chargeStatus.visible" key="chargeStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.charge_status" :value="scope.row.chargeStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已计收未税金额" align="center" prop="chargedWithoutTax" width="120" v-if="columns.chargedWithoutTax.visible" key="chargedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="毛利" align="center" prop="grossProfit" width="120" v-if="columns.grossProfit.visible" key="grossProfit" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="毛利率" align="center" prop="grossProfitRate" width="100" v-if="columns.grossProfitRate.visible" key="grossProfitRate"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- <el-table-column label="业务计收时间" align="center" prop="bizChargeDate" width="180" v-if="columns.bizChargeDate.visible" key="bizChargeDate">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <span>{{ parseTime(scope.row.bizChargeDate, '{y}-{m}-{d}') }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column label="财务计收时间" align="center" prop="financeChargeDate" width="180" v-if="columns.financeChargeDate.visible" key="financeChargeDate">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <span>{{ parseTime(scope.row.financeChargeDate, '{y}-{m}-{d}') }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
|
||||
<!-- 应付 -->
|
||||
<el-table-column label="应付单" align="center">
|
||||
<el-table-column label="应付含税金额" align="center" prop="payableWithTax" width="120" v-if="columns.payableWithTax.visible" key="payableWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="应付未税金额" align="center" prop="payableWithoutTax" width="120" v-if="columns.payableWithoutTax.visible" key="payableWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="应付税额" align="center" prop="payableTax" width="120" v-if="columns.payableTax.visible" key="payableTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 付款 -->
|
||||
<el-table-column label="付款单" align="center">
|
||||
<el-table-column label="付款状态" align="center" prop="paymentStatus" width="100" v-if="columns.paymentStatus.visible" key="paymentStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.report_payment_status" :value="scope.row.paymentStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已付款含税金额" align="center" prop="paidWithTax" width="120" v-if="columns.paidWithTax.visible" key="paidWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已付款未税金额" align="center" prop="paidWithoutTax" width="120" v-if="columns.paidWithoutTax.visible" key="paidWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已付款税额" align="center" prop="paidTax" width="120" v-if="columns.paidTax.visible" key="paidTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="付款中含税金额" align="center" prop="payingWithTax" width="120" v-if="columns.payingWithTax.visible" key="payingWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="付款中未税金额" align="center" prop="payingWithoutTax" width="120" v-if="columns.payingWithoutTax.visible" key="payingWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="付款中税额" align="center" prop="payingTax" width="120" v-if="columns.payingTax.visible" key="payingTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未付款含税金额" align="center" prop="unpaidWithTax" width="120" v-if="columns.unpaidWithTax.visible" key="unpaidWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未付款未税金额" align="center" prop="unpaidWithoutTax" width="120" v-if="columns.unpaidWithoutTax.visible" key="unpaidWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未付款税额" align="center" prop="unpaidTax" width="120" v-if="columns.unpaidTax.visible" key="unpaidTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 收票 -->
|
||||
<el-table-column label="收票单" align="center">
|
||||
<el-table-column label="收票状态" align="center" prop="ticketStatus" width="100" v-if="columns.ticketStatus.visible" key="ticketStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.report_ticket_status" :value="scope.row.ticketStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已收票含税金额" align="center" prop="ticketedWithTax" width="120" v-if="columns.ticketedWithTax.visible" key="ticketedWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已收票未税金额" align="center" prop="ticketedWithoutTax" width="120" v-if="columns.ticketedWithoutTax.visible" key="ticketedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已收票税额" align="center" prop="ticketedTax" width="120" v-if="columns.ticketedTax.visible" key="ticketedTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收票中含税金额" align="center" prop="ticketingWithTax" width="120" v-if="columns.ticketingWithTax.visible" key="ticketingWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收票中未税金额" align="center" prop="ticketingWithoutTax" width="120" v-if="columns.ticketingWithoutTax.visible" key="ticketingWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收票中税额" align="center" prop="ticketingTax" width="120" v-if="columns.ticketingTax.visible" key="ticketingTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收票含税金额" align="center" prop="unticketedWithTax" width="120" v-if="columns.unticketedWithTax.visible" key="unticketedWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收票未税金额" align="center" prop="unticketedWithoutTax" width="120" v-if="columns.unticketedWithoutTax.visible" key="unticketedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收票税额" align="center" prop="unticketedTax" width="120" v-if="columns.unticketedTax.visible" key="unticketedTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"1/>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listReport } from "@/api/finance/report";
|
||||
|
||||
export default {
|
||||
name: "PayableReport",
|
||||
dicts: ['charge_status', 'report_payment_status', 'report_ticket_status'],
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 项目报表表格数据
|
||||
reportList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
orderCode: null,
|
||||
projectCode: null,
|
||||
projectName: null,
|
||||
chargeStatus: null,
|
||||
paymentStatus: null,
|
||||
ticketStatus: null,
|
||||
orderByColumn:'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
// 列信息
|
||||
columns: {
|
||||
projectCode: { label: "项目编号", visible: true },
|
||||
projectName: { label: "项目名称", visible: true },
|
||||
chargeStatus: { label: "计收状态", visible: true },
|
||||
chargedWithoutTax: { label: "已计收未税金额", visible: true },
|
||||
grossProfit: { label: "毛利", visible: true },
|
||||
grossProfitRate: { label: "毛利率", visible: true },
|
||||
// bizChargeDate: { label: "业务计收时间", visible: true },
|
||||
// financeChargeDate: { label: "财务计收时间", visible: true },
|
||||
payableWithTax: { label: "应付含税总价", visible: true },
|
||||
payableWithoutTax: { label: "应付未税总价", visible: true },
|
||||
payableTax: { label: "应付税额", visible: true },
|
||||
paymentStatus: { label: "付款状态", visible: true },
|
||||
paidWithTax: { label: "已付款含税金额", visible: true },
|
||||
paidWithoutTax: { label: "已付款未税金额", visible: true },
|
||||
paidTax: { label: "已付款税额", visible: true },
|
||||
payingWithTax: { label: "付款中含税金额", visible: true },
|
||||
payingWithoutTax: { label: "付款中未税金额", visible: true },
|
||||
payingTax: { label: "付款中税额", visible: true },
|
||||
unpaidWithTax: { label: "未付款含税金额", visible: true },
|
||||
unpaidWithoutTax: { label: "未付款未税金额", visible: true },
|
||||
unpaidTax: { label: "未付款税额", visible: true },
|
||||
ticketStatus: { label: "收票状态", visible: true },
|
||||
ticketedWithTax: { label: "已收票含税金额", visible: true },
|
||||
ticketedWithoutTax: { label: "已收票未税金额", visible: true },
|
||||
ticketedTax: { label: "已收票税额", visible: true },
|
||||
ticketingWithTax: { label: "收票中含税金额", visible: true },
|
||||
ticketingWithoutTax: { label: "收票中未税金额", visible: true },
|
||||
ticketingTax: { label: "收票中税额", visible: true },
|
||||
unticketedWithTax: { label: "未收票含税金额", visible: true },
|
||||
unticketedWithoutTax: { label: "未收票未税金额", visible: true },
|
||||
unticketedTax: { label: "未收票税额", visible: true },
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询项目报表列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listReport(this.queryParams).then(response => {
|
||||
this.reportList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.id)
|
||||
this.single = selection.length!==1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
this.download('finance/report/export', {
|
||||
...this.queryParams
|
||||
}, `report_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,539 @@
|
|||
<template>
|
||||
<el-dialog title="新增付款单" :visible.sync="internalVisible" width="1200px" @close="handleClose"
|
||||
:close-on-click-modal="false" append-to-body>
|
||||
<div style="max-height: 70vh;overflow: auto">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="制造商名称" prop="vendorCode">
|
||||
<el-select
|
||||
v-model="form.vendorCode"
|
||||
placeholder="请选择制造商"
|
||||
style="width:100%"
|
||||
filterable
|
||||
@change="handleVendorChange"
|
||||
>
|
||||
<el-option v-for="item in vendorOptions" :key="item.vendorCode" :label="item.vendorName"
|
||||
:value="item.vendorCode"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="24">-->
|
||||
<!-- <el-form-item label="预计付款时间" prop="estimatedPaymentTime">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- v-model="form.estimatedPaymentTime"-->
|
||||
<!-- type="date"-->
|
||||
<!-- value-format="yyyy-MM-dd HH:mm:ss"-->
|
||||
<!-- placeholder="选择日期"-->
|
||||
<!-- ></el-date-picker>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="paymentBillType">
|
||||
<el-checkbox v-model="form.paymentBillType" true-label="PRE_PAYMENT" false-label="FROM_PAYABLE">预付</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="form.paymentBillType === 'PRE_PAYMENT'">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="含税总价(元)" prop="totalPriceWithTax">
|
||||
<el-input-number v-model="form.totalPriceWithTax" :precision="2" :step="100"
|
||||
style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="其它特别说明" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea"
|
||||
:rows="2"
|
||||
style="width: 100%"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="附件" prop="fileId">
|
||||
<file-upload :value="fileList" @file-list-changed="handleFileListChanged" :limit="1"
|
||||
:file-type="['png', 'jpg', 'jpeg', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'pdf']"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- Tables -->
|
||||
<div v-if="form.vendorCode">
|
||||
<div v-if="form.paymentBillType === 'FROM_PAYABLE'" class="table-container">
|
||||
<h4>采购-应付单表</h4>
|
||||
<el-table
|
||||
ref="payableTable"
|
||||
:data="payableList"
|
||||
border
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
row-key="id"
|
||||
show-summary
|
||||
:summary-method="getPayableSummary"
|
||||
>
|
||||
<el-table-column type="selection" width="55" reserve-selection></el-table-column>
|
||||
<el-table-column label="采购-应付单编号" align="center" prop="payableBillCode" width="150"/>
|
||||
<el-table-column label="预计付款时间" align="center" prop="planPaymentDate" width="180"/>
|
||||
<el-table-column label="预期付款计划" align="center" width="100" prop="planAmount">
|
||||
<template slot-scope="scope">
|
||||
{{ calculateOrderCurrentPaymentAmount(scope.row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预期付款比例" align="center" prop="projectCode" width="150">
|
||||
<template slot-scope="scope">
|
||||
{{ $calc.mul($calc.div(calculateOrderCurrentPaymentAmount(scope.row), scope.row.totalPriceWithTax,4),100) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" width="150"/>
|
||||
<el-table-column label="制造商名称" align="center" prop="vendorName" width="150"/>
|
||||
<!-- <el-table-column label="出入库单号" align="center" prop="inventoryCode" width="150"/>-->
|
||||
<!-- <el-table-column label="付款状态" align="center" prop="paymentStatus" width="120">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <dict-tag :options="dict.type.payment_status" :value="scope.row.paymentStatus"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" width="120"/>
|
||||
<el-table-column label="未付款金额" align="center" prop="unpaidPaymentAmount" width="120"/>
|
||||
<!-- <el-table-column label="本次付款金额" align="center" width="120">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- {{ calculateOrderCurrentPaymentAmount(scope.row).toFixed(2) }}-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column label="本次付款比例" align="center" width="120">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- {{ calculateOrderCurrentPaymentRate(scope.row) }}%-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="已付款金额" align="center" prop="paidPaymentAmount" width="120"/>
|
||||
<el-table-column label="付款中金额" align="center" prop="paidAmount" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ $calc.sub($calc.sub(scope.row.totalPriceWithTax, scope.row.paidPaymentAmount), scope.row.unpaidPaymentAmount) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100"
|
||||
fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleOpenPaymentPlanSelector(scope.row, scope.$index)"
|
||||
>选择
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div v-if="form.paymentBillType === 'PRE_PAYMENT'" class="table-container">
|
||||
<h4>订单列表</h4>
|
||||
<el-table
|
||||
ref="orderTable"
|
||||
:data="orderList"
|
||||
border
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
@select="handleSelect"
|
||||
@select-all="handleSelectAll"
|
||||
|
||||
row-key="id"
|
||||
>
|
||||
<el-table-column type="selection" width="55" reserve-selection></el-table-column>
|
||||
<el-table-column prop="projectCode" label="项目编号"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目名称"></el-table-column>
|
||||
<el-table-column prop="createTime" label="下单时间"></el-table-column>
|
||||
<el-table-column label="订单状态" prop="orderStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.order_status" :value="scope.row.orderStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="loadTableData"
|
||||
/>
|
||||
</div>
|
||||
<div v-else style="text-align: center; color: #909399; padding: 20px;">
|
||||
请先选择制造商
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<!-- <div v-if="form.paymentBillType === 'FROM_PAYABLE'" style="float: left; line-height: 36px;">-->
|
||||
<!-- <span style="margin-right: 20px;">计划付款总金额: <el-tag type="success">{{-->
|
||||
<!-- totalPlannedAmount.toFixed(2)-->
|
||||
<!-- }}</el-tag></span>-->
|
||||
<!-- </div>-->
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
</div>
|
||||
|
||||
<!-- Payment Plan Selector Dialog -->
|
||||
<el-dialog :title="planTitle" :visible.sync="isPaymentPlanSelectorOpen" width="70%"
|
||||
@close="isPaymentPlanSelectorOpen=false" append-to-body>
|
||||
<payment-plan-selector
|
||||
ref="planSelector"
|
||||
:payable-data="choosePayable"
|
||||
:selected-plans="choosePayable.paymentPlans"
|
||||
@confirm="handlePaymentPlanConfirm"
|
||||
/>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="isPaymentPlanSelectorOpen=false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleChooseConfirm">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {listAllVendor} from "@/api/base/vendor";
|
||||
import {listPayableBills, listOrders} from "@/api/finance/payment";
|
||||
import PaymentPlanSelector from "../../payable/components/PaymentPlan";
|
||||
import FileUpload from "@/components/FileUpload";
|
||||
|
||||
export default {
|
||||
name: "AddForm",
|
||||
components: {PaymentPlanSelector, FileUpload},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
dicts:['order_status','payment_status'],
|
||||
data() {
|
||||
return {
|
||||
internalVisible: this.visible,
|
||||
vendorOptions: [],
|
||||
payableList: [], // List for Standard/Payable Bills
|
||||
orderList: [], // List for Prepayment/Purchase Orders
|
||||
selectedRows: [],
|
||||
total: 0,
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
},
|
||||
form: {
|
||||
paymentBillType: 'FROM_PAYABLE', // Default to Normal (Not Prepayment)
|
||||
vendorCode: null,
|
||||
vendorName: null,
|
||||
remark: null,
|
||||
totalPriceWithTax: 0,
|
||||
estimatedPaymentTime: null,
|
||||
fileId: null
|
||||
},
|
||||
rules: {
|
||||
vendorCode: [{required: true, message: "制造商名称不能为空", trigger: "change"}],
|
||||
// estimatedPaymentTime: [{required: true, message: "预计付款时间不能为空", trigger: "change"}],
|
||||
paymentBillType: [{required: true, message: "请选择是否预付", trigger: "change"}],
|
||||
totalPriceWithTax: [{required: false, message: "预付金额不能为空", trigger: "blur"}]
|
||||
},
|
||||
// Plan Selector Data
|
||||
planTitle: '',
|
||||
isPaymentPlanSelectorOpen: false,
|
||||
choosePayable: {},
|
||||
currentPayableOrderIndexForPlan: -1,
|
||||
fileList: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
totalPlannedAmount() {
|
||||
if (this.form.paymentBillType === 'FROM_PAYABLE') {
|
||||
// Calculate based on selected rows and their plans/defaults
|
||||
return this.selectedRows.reduce((sum, row) => {
|
||||
return sum + this.calculateOrderCurrentPaymentAmount(row);
|
||||
}, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(newVal) {
|
||||
this.internalVisible = newVal;
|
||||
if (newVal) {
|
||||
this.resetForm();
|
||||
this.getVendorList();
|
||||
}
|
||||
},
|
||||
internalVisible(newVal) {
|
||||
this.$emit("update:visible", newVal);
|
||||
},
|
||||
'form.paymentBillType': function (val) {
|
||||
// Toggle validation for Prepayment Amount
|
||||
if (val === 'PRE_PAYMENT') {
|
||||
this.rules.totalPriceWithTax[0].required = true;
|
||||
} else {
|
||||
this.rules.totalPriceWithTax[0].required = false;
|
||||
}
|
||||
this.queryParams.pageNum = 1;
|
||||
this.selectedRows = [];
|
||||
this.loadTableData();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 获取厂商列表 */
|
||||
getVendorList() {
|
||||
listAllVendor().then(res => {
|
||||
this.vendorOptions = res.data;
|
||||
})
|
||||
},
|
||||
handleVendorChange(val) {
|
||||
// Find name for the code
|
||||
const vendor = this.vendorOptions.find(v => v.vendorCode === val);
|
||||
if (vendor) {
|
||||
this.form.vendorName = vendor.vendorName;
|
||||
}
|
||||
this.queryParams.pageNum = 1;
|
||||
this.selectedRows = [];
|
||||
this.loadTableData();
|
||||
},
|
||||
handleFileListChanged(fileList) {
|
||||
this.fileList = fileList;
|
||||
this.form.fileId = this.fileList.map(f => f.id).filter(id => !!id).join(',')
|
||||
},
|
||||
loadTableData() {
|
||||
if (!this.form.vendorCode) return;
|
||||
|
||||
// Do not clear lists immediately to avoid flicker if desired, but here we reset for simplicity
|
||||
this.payableList = [];
|
||||
this.orderList = [];
|
||||
|
||||
const query = {
|
||||
vendorCode: this.form.vendorCode,
|
||||
pageNum: this.queryParams.pageNum,
|
||||
pageSize: this.queryParams.pageSize,
|
||||
unpaidPaymentAmount:-1
|
||||
};
|
||||
|
||||
if (this.form.paymentBillType === 'FROM_PAYABLE') {
|
||||
listPayableBills(query).then(res => {
|
||||
this.payableList = (res.rows || []).map(item => {
|
||||
const paymentPlans = item.paymentPlans ? [...item.paymentPlans] : [];
|
||||
if (paymentPlans.length === 0 && item.lastPaymentPlanId) {
|
||||
paymentPlans.push({
|
||||
id: item.lastPaymentPlanId,
|
||||
planAmount: item.planAmount,
|
||||
planPaymentDate: item.planPaymentDate,
|
||||
planRate: this.$calc.mul(this.$calc.div(item.planAmount, item.totalPriceWithTax, 4), 100)
|
||||
});
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
paymentPlans: paymentPlans, // Retain existing plans if any, otherwise empty
|
||||
totalPriceWithTax: item.totalPriceWithTax || 0, // Ensure numeric for calculations
|
||||
unpaidAmount: item.unpaidAmount || 0,
|
||||
paidAmount: item.paidAmount || 0, // Ensure numeric for calculations
|
||||
}
|
||||
});
|
||||
this.total = res.total;
|
||||
});
|
||||
} else if (this.form.paymentBillType === 'PRE_PAYMENT') {
|
||||
query.orderStatus = '2';
|
||||
listOrders(query).then(res => {
|
||||
this.orderList = res.rows || [];
|
||||
this.total = res.total;
|
||||
});
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.payableTable) {
|
||||
this.$refs.payableTable.clearSelection()
|
||||
}
|
||||
if (this.$refs.orderTable) {
|
||||
this.$refs.orderTable.clearSelection()
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSelect(selection, row) {
|
||||
if (this.form.paymentBillType === 'PRE_PAYMENT') {
|
||||
this.$refs.orderTable.clearSelection();
|
||||
const isSelected = selection.some(item => item.orderCode === row.orderCode);
|
||||
if (isSelected) {
|
||||
this.$refs.orderTable.toggleRowSelection(row, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
handleSelectAll(selection) {
|
||||
if (this.form.paymentBillType === 'PRE_PAYMENT') {
|
||||
this.$refs.orderTable.clearSelection();
|
||||
this.$modal.msgWarning("预付单只能选择一个订单");
|
||||
}
|
||||
},
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedRows = selection;
|
||||
},
|
||||
// --- Payment Plan Logic ---
|
||||
handleOpenPaymentPlanSelector(row, index) {
|
||||
this.planTitle = `选择付款计划 - ${row.payableBillCode}`;
|
||||
this.choosePayable = row;
|
||||
this.currentPayableOrderIndexForPlan = index;
|
||||
this.isPaymentPlanSelectorOpen = true;
|
||||
},
|
||||
handleChooseConfirm() {
|
||||
if (!this.$refs.planSelector) {
|
||||
this.$modal.msgError('无法获取计划选择器组件');
|
||||
return;
|
||||
}
|
||||
const selectedPlans = this.$refs.planSelector.selectedPlan || [];
|
||||
|
||||
// Update the payment plans for the specific order
|
||||
if (this.currentPayableOrderIndexForPlan !== -1) {
|
||||
const row = this.payableList[this.currentPayableOrderIndexForPlan];
|
||||
this.$set(row, 'paymentPlans', [...selectedPlans]);
|
||||
}
|
||||
this.isPaymentPlanSelectorOpen = false;
|
||||
this.$modal.msgSuccess(`已更新付款计划选择,共 ${selectedPlans.length} 条`);
|
||||
},
|
||||
handlePaymentPlanConfirm(updatedPlans) {
|
||||
// This might be redundant if handleChooseConfirm does the job, checking Usage in MergePaymentDialog
|
||||
},
|
||||
calculateOrderCurrentPaymentAmount(order) {
|
||||
if (order && order.paymentPlans && order.paymentPlans.length > 0) {
|
||||
return order.paymentPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
calculateOrderCurrentPaymentRate(order) {
|
||||
if (order && order.paymentPlans && order.paymentPlans.length > 0 && order.totalPriceWithTax) {
|
||||
const currentAmount = this.calculateOrderCurrentPaymentAmount(order);
|
||||
return this.$calc.mul((this.$calc.div(currentAmount, order.totalPriceWithTax, 4)), 100);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
getPayableSummary(param) {
|
||||
const { columns, data } = param;
|
||||
const sums = [];
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '合计';
|
||||
return;
|
||||
}
|
||||
if (column.label === '预期付款计划') {
|
||||
const values = data.map(item => Number(this.calculateOrderCurrentPaymentAmount(item)));
|
||||
if (!values.every(value => isNaN(value))) {
|
||||
sums[index] = values.reduce((prev, curr) => {
|
||||
const value = Number(curr);
|
||||
if (!isNaN(value)) {
|
||||
return prev + curr;
|
||||
} else {
|
||||
return prev;
|
||||
}
|
||||
}, 0);
|
||||
sums[index] = sums[index].toFixed(2);
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
return sums;
|
||||
},
|
||||
|
||||
handleClose() {
|
||||
this.internalVisible = false;
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
if (this.form.paymentBillType === 'FROM_PAYABLE') {
|
||||
if (this.selectedRows.length === 0) {
|
||||
this.$message.warning("请选择至少一条应付单");
|
||||
return;
|
||||
}
|
||||
|
||||
// Process selected rows
|
||||
const processedPayableOrders = this.selectedRows.map(order => {
|
||||
let finalPlans = order.paymentPlans;
|
||||
return {
|
||||
id: order.id,
|
||||
payableBillCode: order.payableBillCode,
|
||||
taxRate: order.taxRate,
|
||||
// Map plans to structure expected by backend (similar to MergePaymentDialog)
|
||||
paymentPlans: finalPlans.map(plan => ({
|
||||
id: plan.id, // ID if existing plan
|
||||
planPaymentDate: plan.planPaymentDate,
|
||||
planAmount: plan.planAmount,
|
||||
planRate: plan.planRate,
|
||||
remark: plan.remark
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
const submitData = {
|
||||
paymentBillType: 'FROM_PAYABLE',
|
||||
vendorCode: this.form.vendorCode,
|
||||
estimatedPaymentTime: this.form.estimatedPaymentTime,
|
||||
vendorName: this.form.vendorName,
|
||||
remark: this.form.remark,
|
||||
payableOrders: processedPayableOrders,
|
||||
totalMergePaymentAmount: this.totalPlannedAmount,
|
||||
fileId: this.form.fileId
|
||||
};
|
||||
this.$emit("submit", submitData);
|
||||
|
||||
} else {
|
||||
// Prepayment logic
|
||||
|
||||
if (this.selectedRows.length > 1) {
|
||||
this.$modal.msgWarning("只能选择一笔订单");
|
||||
return;
|
||||
}
|
||||
if ((this.form.totalPriceWithTax ||0) <= 0) {
|
||||
this.$message.warning("含税总价需要大于0");
|
||||
return;
|
||||
}
|
||||
let order = this.selectedRows[0] ?? {};
|
||||
const submitData = {
|
||||
paymentBillType: 'PRE_PAYMENT',
|
||||
orderCode: order.orderCode,
|
||||
vendorCode: this.form.vendorCode,
|
||||
projectCode:order.projectCode,
|
||||
projectName:order.projectName,
|
||||
remark: this.form.remark,
|
||||
paymentTime: this.form.estimatedPaymentTime,
|
||||
totalPriceWithTax:this.form.totalPriceWithTax,
|
||||
fileId: this.form.fileId
|
||||
};
|
||||
|
||||
this.$emit("submit", submitData);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
resetForm() {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.resetFields();
|
||||
}
|
||||
this.form = {
|
||||
paymentBillType: 'FROM_PAYABLE',
|
||||
vendorCode: null,
|
||||
vendorName: null,
|
||||
remark: null,
|
||||
totalPriceWithTax: 0,
|
||||
};
|
||||
this.payableList = [];
|
||||
this.orderList = [];
|
||||
this.selectedRows = [];
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
};
|
||||
this.total = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.table-container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
<template>
|
||||
<el-drawer
|
||||
:title="detail.paymentBillType==='REFUND'?'退款单详情':'付款单详情'"
|
||||
:visible.sync="visible"
|
||||
direction="rtl"
|
||||
size="70%"
|
||||
:wrapper-closable="false"
|
||||
@close="handleClose"
|
||||
>
|
||||
<div class="dialog-body" v-if="detail">
|
||||
<div class="section">
|
||||
<el-divider content-position="left">采购-付款单</el-divider>
|
||||
<div class="details-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">采购-付款单编号: {{ detail.paymentBillCode }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">预计付款时间: {{ detail.paymentTime }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">制造商名称: {{ detail.vendorName }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">含税总价(元):<span :class="{'red-text':detail.paymentBillType==='REFUND'}"> {{ formatCurrency(detail.totalPriceWithTax) }}</span></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">未税总价(元): <span :class="{'red-text':detail.paymentBillType==='REFUND'}">{{ formatCurrency(detail.totalPriceWithoutTax) }}</span></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">税额(元): <span :class="{'red-text':detail.paymentBillType==='REFUND'}">{{ formatCurrency(detail.taxAmount) }} </span></div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">备注:
|
||||
<dict-tag :options="dict.type.payment_bill_type" :value="detail.paymentBillType"/>
|
||||
</div>
|
||||
</el-col>
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <div class="detail-item">预付单剩余额度: {{ detail.preResidueAmount || '-' }}</div>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">实际付款时间: {{ detail.actualPaymentTime || '-'}}</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">支付方式:
|
||||
<dict-tag :options="dict.type.payment_method" :value="detail.paymentMethod"/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">付款状态:
|
||||
<dict-tag :options="dict.type.payment_status" :value="detail.paymentStatus"/>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">审批节点: {{ detail.approveNode|| '-' }}</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">审批状态:
|
||||
<dict-tag :options="dict.type.approve_status" :value="detail.approveStatus"/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">审批通过时间: {{ detail.approveTime || '-'}}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">账户名称: {{ detail.payName }}</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">银行账号: {{ detail.payBankNumber }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">银行开户行: {{ detail.payBankOpenAddress }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">银行行号: {{ detail.bankNumber }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<div class="detail-item">其它特别说明: {{ detail.remark }}</div>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<div class="detail-item">
|
||||
<span style="margin-right: 10px">附件:</span>
|
||||
<file-upload :value="detail.fileList" :disabled="true" :show-remove="false" :show-upload-btn="false"/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<el-divider content-position="left">采购-应付单</el-divider>
|
||||
<el-table :data="detail.payableDetails">
|
||||
<el-table-column type="index" label="序号" width="50"></el-table-column>
|
||||
<el-table-column property="projectCode" label="项目编号"></el-table-column>
|
||||
<el-table-column property="projectName" label="项目名称"></el-table-column>
|
||||
<el-table-column property="payableBillCode" label="采购-应付单编号"></el-table-column>
|
||||
<el-table-column property="totalPriceWithTax" label="含税总价" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column property="paymentAmount" label="本次付款金额">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatCurrency(Math.abs(scope.row.paymentAmount)) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="paymentRate" label="本次付款比例">
|
||||
<template slot-scope="scope">
|
||||
<span >{{ Math.abs(scope.row.paymentRate) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FileUpload from "@/components/FileUpload";
|
||||
|
||||
export default {
|
||||
name: "DetailDrawer",
|
||||
components: {FileUpload},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
dicts:['payment_bill_type','approve_status','payment_status','payment_method'],
|
||||
methods: {
|
||||
downloadFile(attachment){
|
||||
if (attachment){
|
||||
const link = document.createElement('a');
|
||||
link.href = this.getImageUrl(attachment.filePath);
|
||||
link.download = attachment.fileName || 'receipt';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
},
|
||||
handleClose() {
|
||||
this.$emit("update:visible", false);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: calc(100vh - 50px); /* Adjust based on actual header/footer height */
|
||||
overflow-y: auto;
|
||||
padding: 0 20px 20px 20px; /* Adjust padding to match dialog-body style */
|
||||
}
|
||||
|
||||
.details-container {
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.red-text{
|
||||
color: red;
|
||||
}
|
||||
.grid-content {
|
||||
border-radius: 4px;
|
||||
min-height: 36px;
|
||||
line-height: 36px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,544 @@
|
|||
<template>
|
||||
<el-dialog :title="titleText" :visible.sync="dialogVisible" width="900px" @close="handleClose">
|
||||
<div v-if="loading" class="loading-spinner">
|
||||
<i class="el-icon-loading"></i>
|
||||
</div>
|
||||
<div v-else class="receipt-dialog-body">
|
||||
<div v-if="canUpload" class="upload-btn-container">
|
||||
<el-button type="primary" icon="el-icon-upload" v-hasPermi="['finance:payment:upload']" @click="openUploadDialog">上传{{ titleText }}</el-button>
|
||||
</div>
|
||||
|
||||
<el-timeline v-if="attachments.length > 0">
|
||||
<el-timeline-item
|
||||
v-for="attachment in attachments"
|
||||
:key="attachment.id"
|
||||
:timestamp="parseTime(attachment.createTime, '{y}-{m}-{d} {h}:{i}:{s}')"
|
||||
placement="top"
|
||||
>
|
||||
<el-card>
|
||||
<div class="receipt-card-content">
|
||||
<div class="receipt-details">
|
||||
<div class="detail-item">
|
||||
<span class="item-label">支付方式</span>
|
||||
<span class="item-value">
|
||||
<dict-tag :options="dicts.payment_method" :value="paymentData.paymentMethod"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">{{ titleText }}</span>
|
||||
<div class="item-value">
|
||||
<div class="image-wrapper">
|
||||
<el-image
|
||||
v-if="!isPdf(attachment.filePath)"
|
||||
:src="getImageUrl(attachment.filePath)"
|
||||
:preview-src-list="previewList"
|
||||
style="width: 200px; height: 150px;"
|
||||
fit="contain"
|
||||
></el-image>
|
||||
<div v-else-if="pdfUrls[attachment.filePath]" class="pdf-thumbnail-container" @click="openPdfPreview(pdfUrls[attachment.filePath])">
|
||||
<iframe :src="pdfUrls[attachment.filePath]" width="100%" height="150px" frameborder="0"></iframe>
|
||||
<div class="pdf-hover-overlay">
|
||||
<i class="el-icon-zoom-in"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="attachment.delFlag === '2'" class="void-overlay">作废</div>
|
||||
</div>
|
||||
<el-row>
|
||||
<el-col span="8">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="primary"
|
||||
class="download-btn"
|
||||
icon="el-icon-download"
|
||||
@click="downloadFile(attachment)"
|
||||
>下载{{ titleText }}</el-button>
|
||||
</el-col>
|
||||
<el-col span="8">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="primary"
|
||||
class="download-btn"
|
||||
icon="el-icon-remove"
|
||||
v-if="paymentData.paymentBillType==='PRE_PAYMENT' && attachment.delFlag !== '2'"
|
||||
v-hasPermi="['finance:attachment:delete']"
|
||||
@click="deleteFile(paymentData)"
|
||||
>作废{{ titleText }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">含税总价</span>
|
||||
<span class="item-value">{{ paymentData.totalPriceWithTax }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">备注</span>
|
||||
<span class="item-value">{{ attachment.remark }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<el-empty v-else :description="'暂无' + titleText"></el-empty>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">关闭</el-button>
|
||||
</span>
|
||||
|
||||
<!-- PDF Preview Dialog -->
|
||||
<el-dialog
|
||||
:visible.sync="pdfPreviewVisible"
|
||||
width="80%"
|
||||
top="5vh"
|
||||
append-to-body
|
||||
custom-class="pdf-preview-dialog"
|
||||
>
|
||||
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||
</el-dialog>
|
||||
|
||||
<!-- Upload Dialog -->
|
||||
<el-dialog
|
||||
:title="'上传' + titleText"
|
||||
:visible.sync="uploadDialogVisible"
|
||||
width="70vw"
|
||||
append-to-body
|
||||
@close="closeUploadDialog"
|
||||
custom-class="upload-receipt-dialog"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form :model="uploadForm" ref="uploadForm" label-width="120px" size="medium" >
|
||||
<el-form-item label="支付方式">
|
||||
<el-select v-model="uploadForm.paymentMethod" disabled style="width: 100%;">
|
||||
<el-option
|
||||
v-for="dict in dicts.payment_method"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="paymentData.paymentBillType==='FROM_PAYABLE'?'回执单': '退款图'" required>
|
||||
<div style="display: flex; flex-direction: column; align-items: flex-start;">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:on-remove="handleFileRemove"
|
||||
:show-file-list="false"
|
||||
accept=".jpg,.jpeg,.png,.pdf"
|
||||
>
|
||||
<el-button size="small" type="primary" icon="el-icon-upload2">{{ uploadForm.file ? '重新上传' : '点击上传' }}</el-button>
|
||||
</el-upload>
|
||||
<div class="el-upload__tip" style="line-height: 1.5; margin-top: 5px;">支持上传PNG、JPG、PDF文件格式</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="含税总价">
|
||||
<span>{{ paymentData.totalPriceWithTax }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="确认含税总价" required>
|
||||
<el-input v-model="uploadForm.confirmPrice" placeholder="请输入确认含税总价"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="uploadForm.remark"
|
||||
:rows="4"
|
||||
placeholder="此处备注描述..."
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="upload-preview-container" style="height: 70vh;">
|
||||
<div v-if="previewUrl" class="preview-content">
|
||||
<img v-if="!isPreviewPdf" :src="previewUrl" class="preview-image" />
|
||||
<iframe v-else :src="previewUrl" width="100%" height="100%" frameborder="0"></iframe>
|
||||
</div>
|
||||
<div v-else class="preview-placeholder">
|
||||
<div class="placeholder-icon">
|
||||
<i class="el-icon-picture"></i>
|
||||
</div>
|
||||
<div class="placeholder-text">点击图片进入预览</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitNewUpload">保存</el-button>
|
||||
<el-button @click="closeUploadDialog">取消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {deleteFile, getPaymentAttachments, uploadPaymentAttachment} from "@/api/finance/payment";
|
||||
import request from '@/utils/request';
|
||||
|
||||
export default {
|
||||
name: "ReceiptDialog",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
paymentData: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
dicts: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
attachments: [],
|
||||
// Upload Dialog Data
|
||||
uploadDialogVisible: false,
|
||||
uploadForm: {
|
||||
paymentMethod: '',
|
||||
confirmPrice: '',
|
||||
remark: '',
|
||||
file: null
|
||||
},
|
||||
previewUrl: '',
|
||||
isPreviewPdf: false,
|
||||
// PDF Preview Data
|
||||
pdfUrls: {},
|
||||
pdfPreviewVisible: false,
|
||||
currentPdfUrl: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
dialogVisible: {
|
||||
get() {
|
||||
return this.visible;
|
||||
},
|
||||
set(val) {
|
||||
this.$emit("update:visible", val);
|
||||
},
|
||||
},
|
||||
previewList() {
|
||||
return this.attachments
|
||||
.filter(att => !this.isPdf(att.filePath))
|
||||
.map(att => this.getImageUrl(att.filePath));
|
||||
},
|
||||
canUpload() {
|
||||
if (!this.attachments || this.attachments.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return this.attachments.every(att => att.delFlag === '2');
|
||||
},
|
||||
titleText() {
|
||||
return this.paymentData && this.paymentData.paymentBillType === 'REFUND' ? '退款图' : '回执单';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val && this.paymentData) {
|
||||
this.fetchAttachments();
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fetchAttachments() {
|
||||
if (!this.paymentData.id) return;
|
||||
this.loading = true;
|
||||
getPaymentAttachments(this.paymentData.id, { type: 'payment' })
|
||||
.then(response => {
|
||||
const data = response.data || [];
|
||||
data.sort((a, b) => new Date(b.createTime) - new Date(a.createTime));
|
||||
this.attachments = data;
|
||||
this.loadPdfPreviews();
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
this.attachments = [];
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
loadPdfPreviews() {
|
||||
this.attachments.forEach(att => {
|
||||
if (this.isPdf(att.filePath) && !this.pdfUrls[att.filePath]) {
|
||||
request({
|
||||
url: '/common/download/resource',
|
||||
method: 'get',
|
||||
params: { resource: att.filePath },
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
this.$set(this.pdfUrls, att.filePath, url);
|
||||
}).catch(console.error);
|
||||
}
|
||||
});
|
||||
},
|
||||
openPdfPreview(url) {
|
||||
if (!url) return;
|
||||
this.currentPdfUrl = url;
|
||||
this.pdfPreviewVisible = true;
|
||||
},
|
||||
getImageUrl(resource) {
|
||||
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||
},
|
||||
isPdf(filePath) {
|
||||
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
deleteFile(paymentData) {
|
||||
deleteFile(paymentData.id).then(() => {
|
||||
this.fetchAttachments()
|
||||
})
|
||||
},
|
||||
downloadFile(attachment) {
|
||||
const link = document.createElement('a');
|
||||
link.href = this.getImageUrl(attachment.filePath);
|
||||
link.download = attachment.fileName || 'receipt';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
},
|
||||
handleClose() {
|
||||
this.attachments = [];
|
||||
// Clean up object URLs
|
||||
Object.values(this.pdfUrls).forEach(url => URL.revokeObjectURL(url));
|
||||
this.pdfUrls = {};
|
||||
},
|
||||
// New Upload Dialog Methods
|
||||
openUploadDialog() {
|
||||
this.uploadForm = {
|
||||
paymentMethod: this.paymentData.paymentMethod,
|
||||
confirmPrice: '',
|
||||
remark: '',
|
||||
file: null
|
||||
};
|
||||
this.previewUrl = '';
|
||||
this.isPreviewPdf = false;
|
||||
this.uploadDialogVisible = true;
|
||||
},
|
||||
closeUploadDialog() {
|
||||
this.uploadDialogVisible = false;
|
||||
this.uploadForm.file = null;
|
||||
this.previewUrl = '';
|
||||
},
|
||||
handleFileChange(file) {
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
const isAcceptedType = ['image/jpeg', 'image/png', 'application/pdf'].includes(file.raw.type);
|
||||
|
||||
if (!isAcceptedType) {
|
||||
this.$message.error('上传文件只能是 JPG/PNG/PDF 格式!');
|
||||
return;
|
||||
}
|
||||
if (!isLt2M) {
|
||||
this.$message.error('上传文件大小不能超过 2MB!');
|
||||
return;
|
||||
}
|
||||
|
||||
this.uploadForm.file = file.raw;
|
||||
this.isPreviewPdf = file.raw.type === 'application/pdf';
|
||||
|
||||
this.previewUrl = URL.createObjectURL(file.raw);
|
||||
},
|
||||
handleFileRemove() {
|
||||
this.uploadForm.file = null;
|
||||
this.previewUrl = '';
|
||||
},
|
||||
submitNewUpload() {
|
||||
if (!this.uploadForm.file) {
|
||||
this.$message.warning("请选择要上传的文件");
|
||||
return;
|
||||
}
|
||||
if (!this.uploadForm.confirmPrice) {
|
||||
this.$message.warning("请输入确认含税总价");
|
||||
return;
|
||||
}
|
||||
if (parseFloat(this.uploadForm.confirmPrice) !== parseFloat(this.paymentData.totalPriceWithTax)) {
|
||||
this.$message.error("确认含税总价与原含税总价不一致");
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", this.uploadForm.file);
|
||||
formData.append("relatedBillId", this.paymentData.id);
|
||||
formData.append("remark", this.uploadForm.remark);
|
||||
|
||||
uploadPaymentAttachment(formData)
|
||||
.then(response => {
|
||||
this.$message.success("上传成功");
|
||||
this.closeUploadDialog();
|
||||
this.fetchAttachments();
|
||||
})
|
||||
.catch(error => {
|
||||
this.$message.error("上传失败");
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.receipt-dialog-body {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.loading-spinner {
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
padding: 20px;
|
||||
}
|
||||
.receipt-card-content {
|
||||
display: flex;
|
||||
}
|
||||
.receipt-details {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.detail-item {
|
||||
display: flex;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.item-label {
|
||||
width: 80px;
|
||||
color: #606266;
|
||||
text-align: right;
|
||||
margin-right: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.item-value {
|
||||
color: #303133;
|
||||
}
|
||||
.image-wrapper {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
min-height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid #DCDFE6;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.void-overlay {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(-30deg);
|
||||
color: red;
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
.download-btn {
|
||||
display: block;
|
||||
}
|
||||
.upload-btn-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.pdf-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
color: #606266;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pdf-placeholder:hover {
|
||||
border-color: #409EFF;
|
||||
color: #409EFF;
|
||||
}
|
||||
.pdf-placeholder .el-icon-document {
|
||||
font-size: 48px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
/* New Dialog Styles */
|
||||
.upload-preview-container {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
background-color: #f5f7fa;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.preview-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.preview-image {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
.preview-pdf {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: #606266;
|
||||
}
|
||||
.preview-pdf .el-icon-document {
|
||||
font-size: 64px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.preview-placeholder {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
}
|
||||
.placeholder-icon {
|
||||
font-size: 64px;
|
||||
margin-bottom: 10px;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
.placeholder-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
.pdf-thumbnail-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pdf-hover-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
pointer-events: none; /* Let clicks pass through to container, but this is overlay on top of iframe so actually we want it to capture clicks if iframe swallows them? */
|
||||
/* Actually, if pointer-events is none, the click goes to the iframe and iframe swallows it. */
|
||||
/* So we want pointer-events: auto on the overlay, or just rely on the container. */
|
||||
/* If container has the click listener, and overlay covers everything, overlay needs to propagate click or handle it. */
|
||||
/* A simple way: make overlay clickable. */
|
||||
pointer-events: auto;
|
||||
}
|
||||
.pdf-thumbnail-container:hover .pdf-hover-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,556 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="130px">
|
||||
<el-form-item label="项目编号" prop="projectCode">
|
||||
<el-input
|
||||
v-model="queryParams.projectCode"
|
||||
placeholder="请输入项目编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="采购-付款单编号" prop="paymentBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.paymentBillCode"
|
||||
placeholder="请输入付款单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="制造商名称" prop="vendorName">
|
||||
<el-input
|
||||
v-model="queryParams.vendorName"
|
||||
placeholder="请输入制造商名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="采购-应付单编号" prop="payableBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.payableBillCode"
|
||||
placeholder="请输入应付单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="paymentBillType">
|
||||
<el-select v-model="queryParams.paymentBillType" placeholder="请选择备注" clearable>
|
||||
<el-option v-for="dict in dict.type.payment_bill_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="支付方式" prop="paymentMethod">
|
||||
<el-select v-model="queryParams.paymentMethod" style="width: 100%;">
|
||||
<el-option
|
||||
v-for="dict in dict.type.payment_method"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="付款状态" prop="paymentStatus">
|
||||
<el-select v-model="queryParams.paymentStatus" placeholder="请选择付款状态" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.payment_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批状态" prop="approveStatus">
|
||||
<el-select v-model="queryParams.approveStatus" placeholder="请选择审批状态" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.approve_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批节点" prop="approveNode">
|
||||
<el-input
|
||||
v-model="queryParams.approveNode"
|
||||
placeholder="请输入审批节点"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="预计付款时间">
|
||||
<el-date-picker
|
||||
v-model="dateRangeEstimated"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="实际付款时间">
|
||||
<el-date-picker
|
||||
v-model="dateRangeActual"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批通过时间">
|
||||
<el-date-picker
|
||||
v-model="dateRangeApproval"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-download"
|
||||
v-hasPermi="['finance:payment:export']"
|
||||
size="mini"
|
||||
@click="handleExport"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="paymentList" show-summary :summary-method="getSummaries">
|
||||
<el-table-column label="采购-付款单编号" width="200" align="center" prop="paymentBillCode">
|
||||
<template slot-scope="scope">
|
||||
<a @click="handleDetail(scope.row)" class="link-type">{{ scope.row.paymentBillCode }}</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计付款时间" align="center" prop="paymentTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.paymentTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="制造商名称" width="200" align="center" prop="vendorName"/>
|
||||
<el-table-column label="含税总价(元)" align="center" width="200" prop="totalPriceWithTax">
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.totalPriceWithTax<0?{color:'red'}:{}">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" width="100" prop="paymentBillType">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.payment_bill_type" :value="scope.row.paymentBillType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="预付单剩余额度" align="center" width="200" prop="preResidueAmount"/>-->
|
||||
<el-table-column label="实际付款时间" align="center" prop="actualPaymentTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.actualPaymentTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="支付方式" align="center" width="100" prop="paymentMethod">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.payment_method" :value="scope.row.paymentMethod"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="付款状态" align="center" width="100" prop="paymentStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.payment_status" :value="scope.row.paymentStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批状态" align="center" width="100" prop="approveStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.approve_status" :value="scope.row.approveStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批通过时间" align="center" prop="approveTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.approveTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" fixed="right" width="150" align="center" prop="approveNode"/>
|
||||
<el-table-column label="操作" fixed="right" width="400" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-view"
|
||||
@click="handleDetail(scope.row)"
|
||||
>查看详情</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-money"
|
||||
v-show="scope.row.approveStatus==='2' "
|
||||
@click="handleReceipt(scope.row)"
|
||||
>{{ scope.row.paymentBillType === 'REFUND' ? '退款图' : '回执单' }}</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-position"
|
||||
v-show="(scope.row.approveStatus==='0' || scope.row.approveStatus==='3') && (scope.row.paymentBillType==='FROM_PAYABLE'||scope.row.paymentBillType==='PRE_PAYMENT')"
|
||||
@click="applyPayment(scope.row)"
|
||||
>申请付款</el-button>
|
||||
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-refresh-left"
|
||||
v-show="scope.row.paymentBillType==='FROM_PAYABLE' && scope.row.paymentStatus==='1' && scope.row.approveStatus==='0' "
|
||||
@click="handleReturn(scope.row)"
|
||||
>退回</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-refresh-right"
|
||||
v-if="scope.row.paymentStatus === '2' && scope.row.refundStatus !== 'REFUND_APPLIED' && scope.row.paymentBillType !== 'REFUND'"
|
||||
@click="handleApplyRefund(scope.row)"
|
||||
>申请退款</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-refresh-right"
|
||||
v-if="(scope.row.approveStatus === '2' || scope.row.approveStatus==='3') && (scope.row.paymentStatus==='1'||scope.row.paymentStatus==='3')"
|
||||
@click="handleRevoke(scope.row)"
|
||||
>撤销</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
v-hasPermi="['finance:payment:remove']"
|
||||
v-show="(scope.row.approveStatus==='0' || scope.row.approveStatus==='3') && scope.row.paymentBillType==='PRE_PAYMENT' && scope.row.paymentStatus === '1' "
|
||||
@click="handleDelete(scope.row.id)"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 详情抽屉 -->
|
||||
<detail-drawer :visible.sync="detailOpen" :detail="detailData"></detail-drawer>
|
||||
<!-- 新增弹窗 -->
|
||||
<add-form :visible.sync="addOpen" :dicts="dict.type" @submit="handleAddSubmit"></add-form>
|
||||
<!-- 回执单弹窗 -->
|
||||
<receipt-dialog :visible.sync="receiptOpen" :payment-data="currentRow" :dicts="dict.type"></receipt-dialog>
|
||||
<!-- 付款申请弹窗 -->
|
||||
<el-dialog title="发起付款" :visible.sync="applyPaymentOpen" width="600px" append-to-body>
|
||||
<el-form ref="applyPaymentForm" :model="applyPaymentForm" label-width="120px">
|
||||
<el-form-item label="支付方式" prop="paymentMethod">
|
||||
<el-select v-model="applyPaymentForm.paymentMethod" placeholder="请选择支付方式">
|
||||
<el-option
|
||||
v-for="dict in dict.type.payment_method"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="账户名称" prop="payName">
|
||||
<el-input v-model="applyPaymentForm.payName" placeholder="请输入账户名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="银行账号" prop="payBankNumber">
|
||||
<el-input v-model="applyPaymentForm.payBankNumber" placeholder="请输入银行账号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="银行开户行" prop="payBankOpenAddress">
|
||||
<el-input v-model="applyPaymentForm.payBankOpenAddress" placeholder="请输入银行开户行" />
|
||||
</el-form-item>
|
||||
<el-form-item label="银行行号" prop="bankNumber">
|
||||
<el-input v-model="applyPaymentForm.bankNumber" placeholder="请输入银行行号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="含税金额" prop="totalPriceWithTax">
|
||||
<el-input v-model="applyPaymentForm.totalPriceWithTax" :disabled="true" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="applyPaymentOpen = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitApplyPayment">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listPayment,
|
||||
getPayment,
|
||||
returnPayment,
|
||||
addPaymentFromPayable,
|
||||
applyPaymentApi,
|
||||
applyRefundApprove,
|
||||
applyRefund,
|
||||
addPayment,
|
||||
handleRevoke,
|
||||
exportPayment, deletePayment
|
||||
} from "@/api/finance/payment";
|
||||
import { addDateRange } from "@/utils/ruoyi";
|
||||
import DetailDrawer from "./components/DetailDrawer.vue";
|
||||
import AddForm from "./components/AddForm.vue";
|
||||
import ReceiptDialog from "./components/ReceiptDialog.vue";
|
||||
|
||||
export default {
|
||||
name: "Payment",
|
||||
components: {
|
||||
DetailDrawer,
|
||||
AddForm,
|
||||
ReceiptDialog
|
||||
},
|
||||
dicts:['payment_bill_type','approve_status','payment_status', 'payment_method'],
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 付款单表格数据
|
||||
paymentList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectCode: null,
|
||||
projectName: null,
|
||||
paymentBillCode: null,
|
||||
vendorCode: null,
|
||||
payableBillCode: null,
|
||||
paymentBillType: null,
|
||||
paymentMethod: null,
|
||||
approveStatus: null,
|
||||
paymentStatus: null,
|
||||
approveNode: null,
|
||||
orderByColumn:'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
// 日期范围
|
||||
dateRangeApproval: [],
|
||||
dateRangeEstimated: [],
|
||||
dateRangeActual: [],
|
||||
// 详情抽屉
|
||||
detailOpen: false,
|
||||
detailData: {},
|
||||
// 新增弹窗
|
||||
addOpen: false,
|
||||
// 回执单弹窗
|
||||
receiptOpen: false,
|
||||
currentRow: {},
|
||||
// 付款申请弹窗
|
||||
applyPaymentOpen: false,
|
||||
applyPaymentForm: {},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
addDateRange, // Mixin addDateRange function
|
||||
/** 发起付款按钮操作 */
|
||||
applyPayment(row){
|
||||
this.applyPaymentForm = {
|
||||
id: row.id,
|
||||
paymentMethod: row.paymentMethod,
|
||||
payName: row.payName,
|
||||
payBankNumber: row.payBankNumber,
|
||||
payBankOpenAddress: row.payBankOpenAddress,
|
||||
bankNumber: row.bankNumber,
|
||||
totalPriceWithTax: row.totalPriceWithTax
|
||||
};
|
||||
this.applyPaymentOpen = true;
|
||||
},
|
||||
/** 提交付款申请 */
|
||||
submitApplyPayment() {
|
||||
applyPaymentApi(this.applyPaymentForm).then(response => {
|
||||
this.$modal.msgSuccess("付款申请提交成功");
|
||||
this.applyPaymentOpen = false;
|
||||
this.getList();
|
||||
}).catch(error => {
|
||||
console.error("付款申请提交失败", error);
|
||||
this.$modal.msgError("付款申请提交失败");
|
||||
});
|
||||
},
|
||||
getList() {
|
||||
this.loading = true;
|
||||
let query = { ...this.queryParams };
|
||||
query = this.addDateRange(query, this.dateRangeApproval, 'ApproveTime');
|
||||
query = this.addDateRange(query, this.dateRangeEstimated, 'PaymentTime');
|
||||
query = this.addDateRange(query, this.dateRangeActual, 'ActualPaymentTime');
|
||||
|
||||
listPayment(query).then(response => {
|
||||
this.paymentList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleExport() {
|
||||
this.$modal.confirm('是否确认导出付款单数据?').then(() => {
|
||||
return exportPayment(this.queryParams);
|
||||
}).then(response => {
|
||||
this.$download.download( response.msg)
|
||||
})
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.dateRangeApproval = [];
|
||||
this.dateRangeEstimated = [];
|
||||
this.dateRangeActual = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.addOpen = true;
|
||||
},
|
||||
/** 新增提交 */
|
||||
handleAddSubmit(form) {
|
||||
console.log('1111')
|
||||
if (form.paymentBillType==='FROM_PAYABLE'){
|
||||
addPaymentFromPayable(form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.addOpen = false;
|
||||
this.getList();
|
||||
}).catch(error => {
|
||||
console.error("新增付款单失败", error);
|
||||
this.$modal.msgError("新增失败");
|
||||
});
|
||||
}else{
|
||||
addPayment(form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.addOpen = false;
|
||||
this.getList();
|
||||
}).catch(error => {
|
||||
console.error("新增付款单失败", error);
|
||||
this.$modal.msgError("新增失败");
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
/** 详情按钮操作 */
|
||||
handleDetail(row) {
|
||||
getPayment(row.id).then(response => {
|
||||
this.detailData = response.data;
|
||||
this.detailData.approveNode=row.approveNode;
|
||||
this.detailOpen = true;
|
||||
});
|
||||
},
|
||||
/** 回执单按钮操作 */
|
||||
handleReceipt(row) {
|
||||
this.currentRow = row;
|
||||
this.receiptOpen = true;
|
||||
},
|
||||
/** 退回按钮操作 */
|
||||
handleReturn(row) {
|
||||
this.$modal.confirm('是否将该笔采购-付款单退回至采购-应付单').then(function() {
|
||||
return returnPayment(row.id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("退回成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
applyRefundApprove(row) {
|
||||
this.$modal.confirm('收款单退款申请,确认提交至财务审批吗?').then(function() {
|
||||
return applyRefundApprove(row.id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("发起成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 申请退款按钮操作 */
|
||||
handleApplyRefund(row) {
|
||||
this.$modal.confirm('收款单退款申请,确认提交至财务审批吗?').then(() => {
|
||||
return applyRefund(row.id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("申请退款成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
handleDelete(id){
|
||||
this.$modal.confirm('确认删除该笔预付单数据吗?').then(() => {
|
||||
return deletePayment(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
handleRevoke(row) {
|
||||
let msg=row.paymentBillType==='REFUND'?'是否将该笔采购-退款单撤销,将退款单撤销至付款单':'是否将该笔采购-付款单撤销,撤销至付款单未审批状态';
|
||||
this.$modal.confirm(msg).then(() => {
|
||||
return handleRevoke(row.id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("撤销成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
getSummaries(param) {
|
||||
const { columns, data } = param;
|
||||
const sums = [];
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '合计';
|
||||
return;
|
||||
}
|
||||
const values = data.map(item => Number(item[column.property]));
|
||||
if (column.property === 'totalPriceWithTax') {
|
||||
if (!values.every(value => isNaN(value))) {
|
||||
sums[index] = values.reduce((prev, curr) => {
|
||||
const value = Number(curr);
|
||||
if (!isNaN(value)) {
|
||||
return prev + curr;
|
||||
} else {
|
||||
return prev;
|
||||
}
|
||||
}, 0);
|
||||
sums[index] = this.formatCurrency(sums[index]);
|
||||
} else {
|
||||
sums[index] = 'N/A';
|
||||
}
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
});
|
||||
return sums;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -0,0 +1,415 @@
|
|||
<template>
|
||||
<el-dialog title="新增收票单" :visible.sync="internalVisible" width="80%" @close="handleClose" append-to-body>
|
||||
<div class="dialog-body">
|
||||
<el-form ref="form" :model="queryParams" :inline="true" label-width="120px">
|
||||
<el-row>
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-form-item label="收票单类型" prop="ticketBillType">-->
|
||||
<!-- <!– Mapping receiptBillType to ticketBillType for consistency with the merge logic –>-->
|
||||
<!-- <el-select disabled v-model="form.receiptBillType" placeholder="请选择收票单类型" clearable>-->
|
||||
<!-- <!– Using dicts.receipt_bill_type if available, or dict.type.payment_bill_type if that was the intent.-->
|
||||
<!-- The original code used dicts.receipt_bill_type. I'll stick to that but ensure it's passed or available.-->
|
||||
<!-- The user instructions imply 'MergeReceiptDialog' logic which used payment_bill_type.-->
|
||||
<!-- I will use the existing props 'dicts' –>-->
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="dict in dict.type.payment_bill_type"-->
|
||||
<!-- :key="dict.value"-->
|
||||
<!-- :label="dict.label"-->
|
||||
<!-- :value="dict.value"-->
|
||||
<!-- />-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="8">
|
||||
<el-form-item label="制造商名称">
|
||||
<el-select
|
||||
v-model="queryParams.vendorCode"
|
||||
placeholder="请选择制造商"
|
||||
filterable
|
||||
clearable
|
||||
@change="handleVendorChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in vendorOptions"
|
||||
:key="item.vendorCode"
|
||||
:label="item.vendorName"
|
||||
:value="item.vendorCode"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="制造商开票时间" prop="vendorTicketTime">
|
||||
<el-date-picker
|
||||
v-model="form.vendorTicketTime"
|
||||
type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="选择日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-divider content-position="left">采购应付单表</el-divider>
|
||||
<el-table
|
||||
:data="payableOrdersWithPlans"
|
||||
border
|
||||
max-height="300px"
|
||||
style="margin-bottom: 20px;"
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
ref="table"
|
||||
>
|
||||
<el-table-column type="selection" :reserve-selection="true" width="55" align="center" />
|
||||
<el-table-column label="采购-应付单编号" align="center" prop="payableBillCode" width="150"/>
|
||||
<el-table-column label="预计收票时间" align="center" prop="planTicketDate" width="180"/>
|
||||
<!-- <el-table-column label="收票计划" align="center" width="100" prop="planTicketAmount">-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="预期收票计划" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency(calculateOrderCurrentTicketAmount(scope.row.id).toFixed(2)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预期收票比例" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ calculateOrderCurrentTicketRate(scope.row.id) }}%
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" />
|
||||
<el-table-column label="制造商名称" align="center" prop="vendorName" />
|
||||
<!-- <el-table-column label="合同编号" align="center" prop="orderCode" width="150"/>-->
|
||||
<!-- <el-table-column label="出入库单号" align="center" prop="inventoryCode" width="150"/>-->
|
||||
<!-- <el-table-column label="收票状态" align="center" prop="invoiceStatus" width="120">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <dict-tag :options="dict.type.invoice_status" :value="scope.row.invoiceStatus"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收票金额(元)" align="center" prop="unInvoicedAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
|
||||
<el-table-column label="已收票金额(元)" align="center" prop="invoicedAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收票中金额(元)" align="center" prop="invoicedAmount" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency($calc.sub($calc.sub(scope.row.totalPriceWithTax,scope.row.receivedTicketAmount),scope.row.unreceivedTicketAmount))}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleOpenTicketPlanSelector(scope.row, scope.$index)"
|
||||
>选择
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<div class="total-info">
|
||||
<span style="margin-left: 20px;">计划收票总金额: <el-tag type="success">{{ formatCurrency(totalPlannedAmount.toFixed(2)) }}</el-tag></span>
|
||||
<span>计划收票比例: <el-tag type="info">{{ totalPayableAmountWithTax ? this.$calc.mul(this.$calc.div(totalPlannedAmount,totalPayableAmountWithTax,4),100) : 0 }}%</el-tag></span>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
</div>
|
||||
|
||||
<el-dialog :title="planTitle" :visible.sync="isTicketPlanSelectorOpen" width="70%"
|
||||
@close="isTicketPlanSelectorOpen=false" append-to-body>
|
||||
<receiving-ticket-plan
|
||||
ref="planSelector"
|
||||
:payable-data="choosePayable"
|
||||
:selected-plans="choosePayable.ticketPlans"
|
||||
@confirm="handleTicketPlanConfirm"
|
||||
/>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="isTicketPlanSelectorOpen=false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleChooseConfirm">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listAllVendor } from "@/api/base/vendor";
|
||||
import { listPayable } from "@/api/finance/payable";
|
||||
// Importing from the payable component directory as it likely shares logic
|
||||
import ReceivingTicketPlan from "@/views/finance/payable/components/ReceivingTicketPlan.vue";
|
||||
|
||||
export default {
|
||||
name: "AddForm",
|
||||
components: { ReceivingTicketPlan },
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
dicts:['payment_bill_type','invoice_status'],
|
||||
data() {
|
||||
return {
|
||||
internalVisible: this.visible,
|
||||
vendorOptions: [],
|
||||
// Search params
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
ticketBillType: 'FROM_PAYABLE', // Used for filtering if needed
|
||||
vendorId: null,
|
||||
invoiceStatus: null,
|
||||
unreceivedTicketAmount:-1
|
||||
},
|
||||
// Form data for submission
|
||||
form: {
|
||||
receiptBillType: 'FROM_PAYABLE',
|
||||
vendorTicketTime: null,
|
||||
},
|
||||
payableOrdersWithPlans: [], // Current page data
|
||||
selectedRows: [], // Cross-page selection
|
||||
total: 0,
|
||||
|
||||
// Plan Selector State
|
||||
planTitle: '',
|
||||
isTicketPlanSelectorOpen: false,
|
||||
choosePayable: {},
|
||||
currentPayableOrderIndexForPlan: -1,
|
||||
loadingTicketPlans: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// Calculate totals based on SELECTED rows
|
||||
totalPayableAmountWithTax() {
|
||||
return this.selectedRows.reduce((sum, order) => sum + (order.totalPriceWithTax || 0), 0);
|
||||
},
|
||||
totalPlannedAmount() {
|
||||
return this.selectedRows.reduce((orderSum, order) => {
|
||||
const orderPlansTotal = (order.ticketPlans || []).reduce((planSum, plan) => planSum + (plan.planAmount || 0), 0);
|
||||
return orderSum + orderPlansTotal;
|
||||
}, 0);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
visible(newVal) {
|
||||
this.internalVisible = newVal;
|
||||
if (newVal) {
|
||||
this.resetForm();
|
||||
this.getVendorList();
|
||||
}
|
||||
},
|
||||
internalVisible(newVal) {
|
||||
this.$emit("update:visible", newVal);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getVendorList() {
|
||||
listAllVendor().then(res => {
|
||||
this.vendorOptions = res.data || [];
|
||||
})
|
||||
},
|
||||
handleVendorChange() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.selectedRows = [];
|
||||
if (this.$refs.table) {
|
||||
this.$refs.table.clearSelection();
|
||||
}
|
||||
this.getList();
|
||||
},
|
||||
getList() {
|
||||
if (!this.queryParams.vendorCode) {
|
||||
this.payableOrdersWithPlans = [];
|
||||
this.total = 0;
|
||||
return;
|
||||
}
|
||||
this.loadingTicketPlans = true;
|
||||
listPayable(this.queryParams).then(response => {
|
||||
this.payableOrdersWithPlans = response.rows.map(order => {
|
||||
// Initialize ticket plans
|
||||
const ticketPlans = order.ticketPlans ? [...order.ticketPlans] : [];
|
||||
if (ticketPlans.length === 0 && order.lastTicketPlanId) {
|
||||
ticketPlans.push({
|
||||
id: order.lastTicketPlanId,
|
||||
planAmount: order.planTicketAmount,
|
||||
taxRate: order.taxRate,
|
||||
planTicketDate: order.planTicketDate,
|
||||
planRate: order.totalPriceWithTax ? this.$calc.mul(this.$calc.div(order.planTicketAmount, order.totalPriceWithTax, 4), 100) : 0
|
||||
});
|
||||
}
|
||||
return {
|
||||
...order,
|
||||
ticketPlans: ticketPlans,
|
||||
totalPriceWithTax: order.totalPriceWithTax || 0,
|
||||
unInvoicedAmount: order.unInvoicedAmount || 0,
|
||||
invoicedAmount: order.invoicedAmount || 0,
|
||||
};
|
||||
});
|
||||
this.total = response.total;
|
||||
this.loadingTicketPlans = false;
|
||||
});
|
||||
},
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedRows = selection;
|
||||
},
|
||||
handleClose() {
|
||||
this.internalVisible = false;
|
||||
this.resetForm();
|
||||
},
|
||||
handleChooseConfirm() {
|
||||
if (!this.$refs.planSelector) {
|
||||
this.$modal.msgError('无法获取计划选择器组件');
|
||||
return;
|
||||
}
|
||||
const selectedPlans = this.$refs.planSelector.selectedPlan || [];
|
||||
|
||||
const orderIndex = this.payableOrdersWithPlans.findIndex(o => o.id === this.choosePayable.id);
|
||||
if (orderIndex === -1) {
|
||||
this.$modal.msgError('找不到要更新的应付单');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentOrder = this.payableOrdersWithPlans[orderIndex];
|
||||
// Update view
|
||||
this.$set(currentOrder, 'ticketPlans', [...selectedPlans]);
|
||||
|
||||
// Update selectedRows
|
||||
const selectedIndex = this.selectedRows.findIndex(o => o.id === this.choosePayable.id);
|
||||
if (selectedIndex !== -1) {
|
||||
this.$set(this.selectedRows[selectedIndex], 'ticketPlans', [...selectedPlans]);
|
||||
}
|
||||
|
||||
this.isTicketPlanSelectorOpen = false;
|
||||
this.$modal.msgSuccess(`已更新收票计划选择,共 ${selectedPlans.length} 条`);
|
||||
},
|
||||
handleOpenTicketPlanSelector(row, index) {
|
||||
this.planTitle = `选择收票计划 - ${row.payableBillCode}`;
|
||||
this.choosePayable = row;
|
||||
this.currentPayableOrderIndexForPlan = index;
|
||||
this.isTicketPlanSelectorOpen = true;
|
||||
},
|
||||
handleTicketPlanConfirm(updatedPlans) {
|
||||
// Callback from ReceivingTicketPlan if it emits 'confirm' directly,
|
||||
// but here we use handleChooseConfirm triggered by the dialog button.
|
||||
// Current logic relies on ref access in handleChooseConfirm.
|
||||
},
|
||||
calculateOrderCurrentTicketAmount(orderId) {
|
||||
const order = this.payableOrdersWithPlans.find(o => o.id === orderId);
|
||||
if (order && order.ticketPlans) {
|
||||
return order.ticketPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
calculateOrderCurrentTicketRate(orderId) {
|
||||
const order = this.payableOrdersWithPlans.find(o => o.id === orderId);
|
||||
if (order && order.ticketPlans && order.unInvoicedAmount >= 0) {
|
||||
const currentAmount = this.calculateOrderCurrentTicketAmount(orderId);
|
||||
return order.totalPriceWithTax ? this.$calc.mul(this.$calc.div(currentAmount ,order.totalPriceWithTax,4 ),100) : 0;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
handleSubmit() {
|
||||
if (!this.queryParams.vendorCode) {
|
||||
this.$modal.msgError('请选择制造商');
|
||||
return;
|
||||
}
|
||||
if (!this.form.vendorTicketTime) {
|
||||
this.$modal.msgError('请选择制造商开票时间');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.selectedRows.length === 0) {
|
||||
this.$modal.msgError('请至少勾选一条应付单');
|
||||
return;
|
||||
}
|
||||
|
||||
const ordersToSubmit = this.selectedRows;
|
||||
|
||||
// Validate plans
|
||||
for (const order of ordersToSubmit) {
|
||||
if (!order.ticketPlans || order.ticketPlans.length === 0) {
|
||||
this.$modal.msgError(`应付单 ${order.payableBillCode} 至少需要一条收票计划`);
|
||||
return;
|
||||
}
|
||||
for (const plan of order.ticketPlans) {
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount <= 0) {
|
||||
this.$modal.msgError(`应付单 ${order.payableBillCode} 的收票计划中预计收票金额必须大于0。`);
|
||||
return;
|
||||
}
|
||||
if (!plan.planTicketDate) {
|
||||
this.$modal.msgError(`应付单 ${order.payableBillCode} 的收票计划中预计收票时间不能为空。`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct payload
|
||||
const data = {
|
||||
receiptBillType: this.form.receiptBillType,
|
||||
vendorTicketTime: this.form.vendorTicketTime,
|
||||
vendorId: this.queryParams.vendorId,
|
||||
payableOrders: ordersToSubmit.map(order => ({
|
||||
id: order.id,
|
||||
payableBillCode: order.payableBillCode,
|
||||
ticketPlans: order.ticketPlans.map(plan => ({
|
||||
planTicketDate: plan.planTicketDate,
|
||||
planAmount: plan.planAmount,
|
||||
planRate: plan.planRate,
|
||||
taxRate: order.taxRate,
|
||||
remark: plan.remark,
|
||||
id: plan.id,
|
||||
})),
|
||||
})),
|
||||
totalMergeTicketAmount: this.totalPlannedAmount,
|
||||
};
|
||||
|
||||
this.$emit("submit", data);
|
||||
// internalVisible will be handled by parent or by success callback usually,
|
||||
// but here we just emit submit. The parent likely handles the API call.
|
||||
// If we want to close immediately:
|
||||
// this.internalVisible = false;
|
||||
},
|
||||
resetForm() {
|
||||
this.form = {
|
||||
receiptBillType: 'FROM_PAYABLE',
|
||||
vendorTicketTime: null,
|
||||
};
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
ticketBillType: 'FROM_PAYABLE',
|
||||
vendorId: null,
|
||||
invoiceStatus: null,
|
||||
unreceivedTicketAmount:-1
|
||||
};
|
||||
this.payableOrdersWithPlans = [];
|
||||
this.selectedRows = [];
|
||||
this.total = 0;
|
||||
if (this.$refs.table) {
|
||||
this.$refs.table.clearSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.total-info {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
<template>
|
||||
<el-drawer
|
||||
:title="detail.ticketBillType==='RED_RUSH'?'红冲收票详情':'收票单详情'"
|
||||
:visible.sync="visible"
|
||||
direction="rtl"
|
||||
size="70%"
|
||||
@close="handleClose"
|
||||
>
|
||||
<div class="dialog-body" v-if="detail">
|
||||
<div class="section">
|
||||
<el-divider content-position="left">采购-收票单</el-divider>
|
||||
<div class="details-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">采购-收票单编号: {{ detail.ticketBillCode }}</div>
|
||||
</el-col>
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <div class="detail-item">预计收票时间: {{ detail.ticketTime }}</div>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="16">
|
||||
<div class="detail-item">制造商开票时间: {{ detail.vendorTicketTime }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">制造商名称: {{ detail.vendorName }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">含税总价: <span :class="{'red-text':detail.ticketBillType==='RED_RUSH'}">{{ formatCurrency(detail.totalPriceWithTax) }}</span></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">未税总价: <span :class="{'red-text':detail.ticketBillType==='RED_RUSH'}">{{formatCurrency(detail.totalPriceWithoutTax) }}</span></div>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">税额: <span :class="{'red-text':detail.ticketBillType==='RED_RUSH'}">{{ formatCurrency(detail.taxAmount) }} </span></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">发票含税总价: <span :class="{'red-text':detail.ticketBillType==='RED_RUSH'}">{{ formatCurrency(detail.ticketPriceWithTax) || '-'}} </span></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">发票未税总价: <span :class="{'red-text':detail.ticketBillType==='RED_RUSH'}">{{ (detail.ticketPriceWithoutTax) || '-'}} </span></div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">发票税额: <span :class="{'red-text':detail.ticketBillType==='RED_RUSH'}">{{ formatCurrency(detail.ticketAmount) }} </span></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">收票状态:
|
||||
<dict-tag :options="dict.type.receipt_status" :value="detail.ticketStatus"/>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">备注: {{ detail.remark }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">审批节点: {{ detail.approveNode|| '-' }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">审批状态:
|
||||
<dict-tag :options="dict.type.approve_status" :value="detail.approveStatus"/>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">审批通过时间: {{ detail.approveTime || '-'}}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<el-divider content-position="left">采购-应付单</el-divider>
|
||||
<el-table :data="detail.detailList">
|
||||
<el-table-column type="index" label="序号" width="50"></el-table-column>
|
||||
<el-table-column property="projectCode" label="项目编号"></el-table-column>
|
||||
<el-table-column property="projectName" label="项目名称"></el-table-column>
|
||||
<el-table-column property="payableBillCode" label="采购应付单编号"></el-table-column>
|
||||
<el-table-column property="totalPriceWithTax" label="含税总价" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column property="paymentAmount" label="本次收票金额" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column property="paymentRate" label="本次收票比例(%)"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DetailDrawer",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => null,
|
||||
},
|
||||
},
|
||||
dicts:['receipt_bill_type','approve_status','receipt_status'],
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.$emit("update:visible", false);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: calc(100vh - 50px);
|
||||
overflow-y: auto;
|
||||
padding: 0 20px 20px 20px;
|
||||
}
|
||||
|
||||
.details-container {
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
align-items: center;
|
||||
}
|
||||
.red-text{
|
||||
color: red;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,645 @@
|
|||
<template>
|
||||
<el-dialog :title="titleText" :visible.sync="dialogVisible" width="900px" @close="handleClose">
|
||||
<div v-if="loading" class="loading-spinner">
|
||||
<i class="el-icon-loading"></i>
|
||||
</div>
|
||||
<div v-else class="receipt-dialog-body">
|
||||
<div v-if="canUpload" class="upload-btn-container">
|
||||
<el-button type="primary" icon="el-icon-upload" v-hasPermi="['finance:receipt:upload']" @click="openUploadDialog">上传{{ titleText }}</el-button>
|
||||
</div>
|
||||
|
||||
<el-timeline v-if="attachments.length > 0">
|
||||
<el-timeline-item
|
||||
v-for="attachment in attachments"
|
||||
:key="attachment.id"
|
||||
:timestamp="parseTime(attachment.createTime, '{y}-{m}-{d} {h}:{i}:{s}')"
|
||||
placement="top"
|
||||
>
|
||||
<el-card>
|
||||
<div class="receipt-card-content">
|
||||
<div class="receipt-details">
|
||||
<div class="detail-item">
|
||||
<span class="item-label">票据类型</span>
|
||||
<span class="item-value"><dict-tag :options="dict.type.finance_invoice_type" :value="attachment.ticketType"/></span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">{{ titleText }}</span>
|
||||
<div class="item-value">
|
||||
<div class="image-wrapper">
|
||||
<el-image
|
||||
v-if="!isPdf(attachment.filePath)"
|
||||
:src="getImageUrl(attachment.filePath)"
|
||||
:preview-src-list="previewList"
|
||||
style="width: 200px; height: 150px;"
|
||||
fit="contain"
|
||||
></el-image>
|
||||
<div v-else-if="pdfUrls[attachment.filePath]" class="pdf-thumbnail-container" @click="openPdfPreview(pdfUrls[attachment.filePath])">
|
||||
<iframe :src="pdfUrls[attachment.filePath]" width="100%" height="150px" frameborder="0"></iframe>
|
||||
<div class="pdf-hover-overlay">
|
||||
<i class="el-icon-zoom-in"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="attachment.delFlag === '2'" class="void-overlay">作废</div>
|
||||
</div>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="primary"
|
||||
class="download-btn"
|
||||
icon="el-icon-download"
|
||||
@click="downloadFile(attachment)"
|
||||
>下载{{ titleText }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-item">
|
||||
<span class="item-label">含税金额</span>
|
||||
<span class="item-value">{{ formatCurrency(receiptData.totalPriceWithTax) }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">发票含税金额</span>
|
||||
<span class="item-value">{{ formatCurrency(attachment.priceWithTax||receiptData.ticketPriceWithTax) }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">未税金额</span>
|
||||
<span class="item-value">{{ formatCurrency(receiptData.totalPriceWithoutTax) }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">发票未税金额</span>
|
||||
<span class="item-value">{{ formatCurrency(attachment.priceWithoutTax||receiptData.ticketPriceWithoutTax) }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">税额</span>
|
||||
<span class="item-value">{{ formatCurrency(receiptData.taxAmount) }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">发票税额</span>
|
||||
<span class="item-value">{{formatCurrency( attachment.taxAmount || receiptData.ticketAmount) }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">备注</span>
|
||||
<span class="item-value">{{ attachment.remark }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<el-empty v-else :description="'暂无' + titleText"></el-empty>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">关闭</el-button>
|
||||
</span>
|
||||
|
||||
<!-- PDF Preview Dialog -->
|
||||
<el-dialog
|
||||
:visible.sync="pdfPreviewVisible"
|
||||
width="80%"
|
||||
top="5vh"
|
||||
append-to-body
|
||||
custom-class="pdf-preview-dialog"
|
||||
>
|
||||
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||
</el-dialog>
|
||||
|
||||
<!-- Upload Dialog -->
|
||||
<el-dialog
|
||||
:title="'上传' + titleText"
|
||||
:visible.sync="uploadDialogVisible"
|
||||
width="70vw"
|
||||
append-to-body
|
||||
@close="closeUploadDialog"
|
||||
custom-class="upload-receipt-dialog"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form :model="uploadForm" ref="uploadForm" :rules="rules" label-width="120px" size="medium" >
|
||||
<el-form-item label="票据类型" prop="ticketType" required>
|
||||
<dict-tag :options="dict.type.finance_invoice_type" :value="uploadForm.ticketType"/>
|
||||
<!-- <el-select v-model="uploadForm.ticketType" placeholder="请选择票据类型">-->
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="item in dict.type.finance_invoice_type"-->
|
||||
<!-- :key="item.value"-->
|
||||
<!-- :label="item.label"-->
|
||||
<!-- :value="item.value"-->
|
||||
<!-- ></el-option>-->
|
||||
<!-- </el-select>-->
|
||||
</el-form-item>
|
||||
<el-form-item label="收票附件" prop="file" required>
|
||||
<div style="display: flex; flex-direction: column; align-items: flex-start;">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:on-remove="handleFileRemove"
|
||||
:show-file-list="false"
|
||||
accept=".jpg,.jpeg,.png,.pdf"
|
||||
>
|
||||
<el-button size="small" type="primary" icon="el-icon-upload2">{{ uploadForm.file ? '重新上传' : '点击上传' }}</el-button>
|
||||
</el-upload>
|
||||
<div class="el-upload__tip" style="line-height: 1.5; margin-top: 5px;">支持上传PNG、JPG、PDF文件格式</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="含税总价">
|
||||
<span>{{ this.uploadType !== 'red_rush' ? receiptData.totalPriceWithTax : -receiptData.totalPriceWithTax}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="发票含税总价" prop="ticketPriceWithTax" required>
|
||||
<el-input v-model="uploadForm.ticketPriceWithTax" placeholder="请输入发票含税总价" @input="handlePriceWithTaxChange"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="未税总价">
|
||||
<span>{{ this.uploadType !== 'red_rush'? receiptData.totalPriceWithoutTax : -receiptData.totalPriceWithoutTax }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="发票未税总价" prop="ticketPriceWithoutTax" required>
|
||||
<el-input v-model="uploadForm.ticketPriceWithoutTax" placeholder="请输入发票未税总价" @input="handlePriceWithoutTaxChange"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="税额">
|
||||
<span>{{ this.uploadType !== 'red_rush' ? receiptData.taxAmount : -receiptData.taxAmount }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="发票税额" prop="ticketAmount" required>
|
||||
<el-input v-model="uploadForm.ticketAmount" placeholder="请输入发票税额" @input="handleAmountChange"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="uploadForm.remark"
|
||||
:rows="4"
|
||||
placeholder="此处备注描述..."
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="upload-preview-container" style="height: 70vh;">
|
||||
<div v-if="previewUrl" class="preview-content">
|
||||
<img v-if="!isPreviewPdf" :src="previewUrl" class="preview-image" />
|
||||
<iframe v-else :src="previewUrl" width="100%" height="100%" frameborder="0"></iframe>
|
||||
</div>
|
||||
<div v-else class="preview-placeholder">
|
||||
<div class="placeholder-icon">
|
||||
<i class="el-icon-picture"></i>
|
||||
</div>
|
||||
<div class="placeholder-text">点击图片进入预览</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitNewUpload">保存</el-button>
|
||||
<el-button @click="closeUploadDialog">取消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getReceiptAttachments, uploadReceiptAttachment, applyRedRush } from "@/api/finance/receipt";
|
||||
import request from '@/utils/request';
|
||||
|
||||
export default {
|
||||
name: "ReceiptDialog",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
receiptData: {
|
||||
type: Object,
|
||||
default: () => null,
|
||||
},
|
||||
dicts: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
autoUpload: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
uploadType: {
|
||||
type: String,
|
||||
default: 'normal'
|
||||
}
|
||||
},
|
||||
dicts:['finance_invoice_type'],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
attachments: [],
|
||||
// Upload Dialog Data
|
||||
uploadDialogVisible: false,
|
||||
uploadForm: {
|
||||
ticketPriceWithTax: '',
|
||||
ticketPriceWithoutTax: '',
|
||||
ticketAmount: '',
|
||||
ticketType: '',
|
||||
remark: '',
|
||||
file: null
|
||||
},
|
||||
rules: {
|
||||
ticketPriceWithTax: [
|
||||
{ required: true, message: "请输入发票含税总价", trigger: "blur" },
|
||||
{ validator: (rule, value, callback) => {
|
||||
if (Number(value) !== Number(this.uploadType !== 'red_rush'? this.receiptData.totalPriceWithTax : -this.receiptData.totalPriceWithTax)) {
|
||||
callback(new Error('发票含税总价必须等于含税总价'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}, trigger: 'blur' }
|
||||
],
|
||||
ticketPriceWithoutTax: [
|
||||
{ required: true, message: "请输入发票未税总价", trigger: "blur" },
|
||||
{ validator: (rule, value, callback) => {
|
||||
const total = Number(this.uploadType !== 'red_rush'? this.receiptData.totalPriceWithoutTax : -this.receiptData.totalPriceWithTax);
|
||||
const val = Number(value);
|
||||
if (total > 0 && val < 0) {
|
||||
callback(new Error('发票未税总价不能小于0'));
|
||||
} else if (total < 0 && val > 0) {
|
||||
callback(new Error('发票未税总价不能大于0'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}, trigger: 'blur' }
|
||||
],
|
||||
ticketAmount: [
|
||||
{ required: true, message: "请输入发票税额", trigger: "blur" },
|
||||
{ validator: (rule, value, callback) => {
|
||||
const total = Number(this.uploadType !== 'red_rush'? this.receiptData.totalPriceWithTax : -this.receiptData.totalPriceWithTax);
|
||||
const val = Number(value);
|
||||
if (total > 0 && val < 0) {
|
||||
callback(new Error('发票税额不能小于0'));
|
||||
} else if (total < 0 && val > 0) {
|
||||
callback(new Error('发票税额不能大于0'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}, trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
previewUrl: '',
|
||||
isPreviewPdf: false,
|
||||
// PDF Preview Data
|
||||
pdfUrls: {},
|
||||
pdfPreviewVisible: false,
|
||||
currentPdfUrl: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
dialogVisible: {
|
||||
get() {
|
||||
return this.visible;
|
||||
},
|
||||
set(val) {
|
||||
this.$emit("update:visible", val);
|
||||
},
|
||||
},
|
||||
previewList() {
|
||||
return this.attachments
|
||||
.filter(att => !this.isPdf(att.filePath))
|
||||
.map(att => this.getImageUrl(att.filePath));
|
||||
},
|
||||
canUpload() {
|
||||
if (!this.attachments || this.attachments.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return this.attachments.every(att => att.delFlag === '2');
|
||||
},
|
||||
titleText() {
|
||||
return '发票';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val && this.receiptData) {
|
||||
if (this.uploadType !== 'red_rush') {
|
||||
this.fetchAttachments();
|
||||
}
|
||||
if (this.autoUpload) {
|
||||
setTimeout(() => {
|
||||
this.openUploadDialog();
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fetchAttachments() {
|
||||
if (!this.receiptData.id) return;
|
||||
this.loading = true;
|
||||
getReceiptAttachments(this.receiptData.id, { type: 'ticket' })
|
||||
.then(response => {
|
||||
const data = response.data || [];
|
||||
data.sort((a, b) => new Date(b.createTime) - new Date(a.createTime));
|
||||
this.attachments = data;
|
||||
this.loadPdfPreviews();
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
this.attachments = [];
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
loadPdfPreviews() {
|
||||
this.attachments.forEach(att => {
|
||||
if (this.isPdf(att.filePath) && !this.pdfUrls[att.filePath]) {
|
||||
request({
|
||||
url: '/common/download/resource',
|
||||
method: 'get',
|
||||
params: { resource: att.filePath },
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
this.$set(this.pdfUrls, att.filePath, url);
|
||||
}).catch(console.error);
|
||||
}
|
||||
});
|
||||
},
|
||||
openPdfPreview(url) {
|
||||
if (!url) return;
|
||||
this.currentPdfUrl = url;
|
||||
this.pdfPreviewVisible = true;
|
||||
},
|
||||
getImageUrl(resource) {
|
||||
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||
},
|
||||
isPdf(filePath) {
|
||||
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
downloadFile(attachment) {
|
||||
const link = document.createElement('a');
|
||||
link.href = this.getImageUrl(attachment.filePath);
|
||||
link.download = attachment.fileName || 'receipt';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
},
|
||||
handleClose() {
|
||||
this.attachments = [];
|
||||
// Clean up object URLs
|
||||
Object.values(this.pdfUrls).forEach(url => URL.revokeObjectURL(url));
|
||||
this.pdfUrls = {};
|
||||
},
|
||||
// New Upload Dialog Methods
|
||||
openUploadDialog() {
|
||||
this.uploadForm = {
|
||||
ticketPriceWithTax: this.uploadType !== 'red_rush' ? this.receiptData.totalPriceWithTax : -this.receiptData.totalPriceWithTax,
|
||||
ticketPriceWithoutTax: this.uploadType !== 'red_rush' ? this.receiptData.totalPriceWithoutTax : -this.receiptData.totalPriceWithoutTax,
|
||||
ticketAmount: this.uploadType !== 'red_rush' ? this.receiptData.taxAmount : -this.receiptData.taxAmount,
|
||||
ticketType: this.receiptData.ticketType,
|
||||
remark: '',
|
||||
file: null
|
||||
};
|
||||
if (this.$refs.uploadForm) {
|
||||
this.$refs.uploadForm.clearValidate();
|
||||
}
|
||||
this.previewUrl = '';
|
||||
this.isPreviewPdf = false;
|
||||
this.uploadDialogVisible = true;
|
||||
},
|
||||
handlePriceWithTaxChange(val) {
|
||||
const withTax = Number(val) || 0;
|
||||
const withoutTax = Number(this.uploadForm.ticketPriceWithoutTax) || 0;
|
||||
this.uploadForm.ticketAmount = (withTax - withoutTax).toFixed(2);
|
||||
},
|
||||
handlePriceWithoutTaxChange(val) {
|
||||
const withTax = Number(this.uploadForm.ticketPriceWithTax) || 0;
|
||||
const withoutTax = Number(val) || 0;
|
||||
this.uploadForm.ticketAmount = (withTax - withoutTax).toFixed(2);
|
||||
},
|
||||
handleAmountChange(val) {
|
||||
const withTax = Number(this.uploadForm.ticketPriceWithTax) || 0;
|
||||
const amount = Number(val) || 0;
|
||||
this.uploadForm.ticketPriceWithoutTax = (withTax - amount).toFixed(2);
|
||||
},
|
||||
closeUploadDialog() {
|
||||
this.uploadDialogVisible = false;
|
||||
this.uploadForm.file = null;
|
||||
this.previewUrl = '';
|
||||
if (this.uploadType === 'red_rush') {
|
||||
this.dialogVisible = false;
|
||||
}
|
||||
},
|
||||
handleFileChange(file) {
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
const isAcceptedType = ['image/jpeg', 'image/png', 'application/pdf'].includes(file.raw.type);
|
||||
|
||||
if (!isAcceptedType) {
|
||||
this.$message.error('上传文件只能是 JPG/PNG/PDF 格式!');
|
||||
return;
|
||||
}
|
||||
if (!isLt2M) {
|
||||
this.$message.error('上传文件大小不能超过 2MB!');
|
||||
return;
|
||||
}
|
||||
|
||||
this.uploadForm.file = file.raw;
|
||||
this.isPreviewPdf = file.raw.type === 'application/pdf';
|
||||
|
||||
this.previewUrl = URL.createObjectURL(file.raw);
|
||||
},
|
||||
handleFileRemove() {
|
||||
this.uploadForm.file = null;
|
||||
this.previewUrl = '';
|
||||
},
|
||||
submitNewUpload() {
|
||||
this.$refs.uploadForm.validate(valid => {
|
||||
if (valid) {
|
||||
if (!this.uploadForm.file) {
|
||||
this.$message.warning("请选择要上传的文件");
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", this.uploadForm.file);
|
||||
formData.append("id", this.receiptData.id);
|
||||
formData.append("remark", this.uploadForm.remark);
|
||||
// formData.append("totalPriceWithTax", this.uploadForm.totalPriceWithTax);
|
||||
formData.append("ticketPriceWithTax", this.uploadForm.ticketPriceWithTax);
|
||||
// formData.append("totalPriceWithoutTax", this.uploadForm.totalPriceWithoutTax);
|
||||
formData.append("ticketPriceWithoutTax", this.uploadForm.ticketPriceWithoutTax);
|
||||
formData.append("ticketAmount", this.uploadForm.ticketAmount);
|
||||
// formData.append("taxAmount", this.uploadForm.taxAmount);
|
||||
formData.append("ticketType", this.uploadForm.ticketType);
|
||||
|
||||
if (this.uploadType === 'red_rush') {
|
||||
applyRedRush(formData).then(response => {
|
||||
this.$message.success("申请红冲成功");
|
||||
this.closeUploadDialog();
|
||||
this.$emit('refresh');
|
||||
}).catch(error => {
|
||||
this.$message.error("申请红冲失败");
|
||||
});
|
||||
} else {
|
||||
uploadReceiptAttachment(formData)
|
||||
.then(response => {
|
||||
this.$message.success("上传成功");
|
||||
this.closeUploadDialog();
|
||||
this.fetchAttachments();
|
||||
})
|
||||
.catch(error => {
|
||||
this.$message.error("上传失败");
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.receipt-dialog-body {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.loading-spinner {
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
padding: 20px;
|
||||
}
|
||||
.receipt-card-content {
|
||||
display: flex;
|
||||
}
|
||||
.receipt-details {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.detail-item {
|
||||
display: flex;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.item-label {
|
||||
width: 110px;
|
||||
color: #606266;
|
||||
text-align: right;
|
||||
margin-right: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.item-value {
|
||||
color: #303133;
|
||||
}
|
||||
.image-wrapper {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
min-height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid #DCDFE6;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.void-overlay {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(-30deg);
|
||||
color: red;
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
.download-btn {
|
||||
display: block;
|
||||
}
|
||||
.upload-btn-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.pdf-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
color: #606266;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pdf-placeholder:hover {
|
||||
border-color: #409EFF;
|
||||
color: #409EFF;
|
||||
}
|
||||
.pdf-placeholder .el-icon-document {
|
||||
font-size: 48px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
/* New Dialog Styles */
|
||||
.upload-preview-container {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
background-color: #f5f7fa;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.preview-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.preview-image {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
.preview-pdf {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: #606266;
|
||||
}
|
||||
.preview-pdf .el-icon-document {
|
||||
font-size: 64px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.preview-placeholder {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
}
|
||||
.placeholder-icon {
|
||||
font-size: 64px;
|
||||
margin-bottom: 10px;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
.placeholder-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
.pdf-thumbnail-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pdf-hover-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.pdf-thumbnail-container:hover .pdf-hover-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,431 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="150px">
|
||||
<el-form-item label="项目编号" prop="projectCode">
|
||||
<el-input
|
||||
v-model="queryParams.projectCode"
|
||||
placeholder="请输入项目编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="采购-收票单编号" prop="ticketBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.ticketBillCode"
|
||||
placeholder="请输入收票单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="制造商名称" prop="vendorName">
|
||||
<el-input
|
||||
v-model="queryParams.vendorName"
|
||||
placeholder="请输入制造商名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="采购-应付单编号" prop="payableBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.payableBillCode"
|
||||
placeholder="请输入应付单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="收票单类型" prop="receiptBillType">
|
||||
<el-select v-model="queryParams.ticketBillType" placeholder="请选择收票单类型" clearable>
|
||||
<el-option v-for="dict in dict.type.ticket_bill_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="收票状态" prop="ticketStatus">
|
||||
<el-select v-model="queryParams.ticketStatus" placeholder="请选择收票状态" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.receipt_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批状态" prop="approveStatus">
|
||||
<el-select v-model="queryParams.approveStatus" placeholder="请选择审批状态" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.approve_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="审批节点" prop="approveNode">
|
||||
<el-input
|
||||
v-model="queryParams.approveNode"
|
||||
placeholder="请输入审批节点"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批通过时间">
|
||||
<el-date-picker
|
||||
v-model="dateRangeApproval"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="预计收票时间">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- v-model="dateRangeEstimated"-->
|
||||
<!-- style="width: 240px"-->
|
||||
<!-- value-format="yyyy-MM-dd"-->
|
||||
<!-- type="daterange"-->
|
||||
<!-- range-separator="-"-->
|
||||
<!-- start-placeholder="开始日期"-->
|
||||
<!-- end-placeholder="结束日期"-->
|
||||
<!-- ></el-date-picker>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="制造商开票时间">
|
||||
<el-date-picker
|
||||
v-model="dateRangeVendor"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="receiptList" show-summary :summary-method="getSummaries">
|
||||
<el-table-column label="采购-收票单编号" align="center" prop="ticketBillCode" />
|
||||
<!-- <el-table-column label="预计收票时间" align="center" prop="ticketTime" width="180">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <span>{{ parseTime(scope.row.ticketTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="制造商开票时间" align="center" prop="vendorTicketTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.vendorTicketTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="制造商名称" align="center" prop="vendorName" />
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" >
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.totalPriceWithTax<0?{color:'red'}:{}">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="未税总价(元)" align="center" prop="totalPriceWithoutTax" >
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.totalPriceWithoutTax<0?{color:'red'}:{}">{{ formatCurrency(scope.row.totalPriceWithoutTax) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="税额(元)" align="center" prop="taxAmount" >
|
||||
<template slot-scope="scope">
|
||||
<span :style="scope.row.taxAmount<0?{color:'red'}:{}">{{ formatCurrency(scope.row.taxAmount) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="收票单类型" align="center" prop="ticketBillType" >-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <dict-tag :options="dict.type.ticket_bill_type" :value="scope.row.ticketBillType"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="收票状态" align="center" prop="ticketStatus" >
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.receipt_status" :value="scope.row.ticketStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="实际收票时间" align="center" prop="actualTicketTime" width="180">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <span>{{ parseTime(scope.row.actualTicketTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
|
||||
<el-table-column label="审批状态" align="center" prop="approveStatus" >
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.approve_status" :value="scope.row.approveStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批通过时间" align="center" prop="approveTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.approveTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-view"
|
||||
@click="handleDetail(scope.row)"
|
||||
>查看详情</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-document"
|
||||
v-show="scope.row.approveStatus!=='0'"
|
||||
@click="handleReceipt(scope.row)"
|
||||
>{{scope.row.ticketBillType==='FROM_PAYABLE'?'发票' : '红冲'}}</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-document"
|
||||
v-show="scope.row.approveStatus==='0' && scope.row.ticketStatus==='1'"
|
||||
@click="handleReturn(scope.row)"
|
||||
>退回</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-document"
|
||||
v-show="scope.row.approveStatus==='0' && scope.row.ticketStatus==='1'"
|
||||
@click="handleApplyInvoice(scope.row)"
|
||||
>申请发票</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-refresh-left"
|
||||
v-show="scope.row.approveStatus=='2' && scope.row.ticketStatus==='2'"
|
||||
@click="handleApplyRedRush(scope.row)"
|
||||
>申请红冲</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-refresh-left"
|
||||
v-show="scope.row.approveStatus=='3'"
|
||||
@click="handleRevoke(scope.row)"
|
||||
>撤销</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 详情抽屉 -->
|
||||
<detail-drawer :visible.sync="detailOpen" :detail="detailData"></detail-drawer>
|
||||
<!-- 新增弹窗 -->
|
||||
<add-form :visible.sync="addOpen" :dicts="dict.type" @submit="handleAddSubmit"></add-form>
|
||||
<!-- 收票附件弹窗 -->
|
||||
<receipt-dialog :visible.sync="receiptOpen" :receipt-data="currentRow" :dicts="dict.type" :auto-upload="autoUpload" :upload-type="uploadType" @refresh="getList"></receipt-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {listReceipt, getReceipt, redRush, addReceipt, returnReceipt, revokeReceipt} from "@/api/finance/receipt";
|
||||
import { addDateRange } from "@/utils/ruoyi";
|
||||
import DetailDrawer from "./components/DetailDrawer.vue";
|
||||
import AddForm from "./components/AddForm.vue";
|
||||
import ReceiptDialog from "./components/ReceiptDialog.vue";
|
||||
|
||||
export default {
|
||||
name: "Receipt",
|
||||
components: {
|
||||
DetailDrawer,
|
||||
AddForm,
|
||||
ReceiptDialog
|
||||
},
|
||||
dicts:['ticket_bill_type','approve_status','receipt_status'],
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 收票单表格数据
|
||||
receiptList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectCode: null,
|
||||
projectName: null,
|
||||
ticketBillCode: null,
|
||||
vendorCode: null,
|
||||
payableBillCode: null,
|
||||
ticketBillType: null,
|
||||
approveStatus: null,
|
||||
ticketStatus: null,
|
||||
approveNode: null,
|
||||
orderByColumn:'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
// 日期范围
|
||||
dateRangeApproval: [],
|
||||
dateRangeEstimated: [],
|
||||
dateRangeVendor: [],
|
||||
// 详情抽屉
|
||||
detailOpen: false,
|
||||
detailData: null,
|
||||
// 新增弹窗
|
||||
addOpen: false,
|
||||
// 收票附件弹窗
|
||||
receiptOpen: false,
|
||||
currentRow: {},
|
||||
autoUpload: false,
|
||||
uploadType: 'normal'
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getSummaries(param) {
|
||||
const { columns, data } = param;
|
||||
const sums = [];
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '合计';
|
||||
return;
|
||||
}
|
||||
const values = data.map(item => Number(item[column.property]));
|
||||
if (['totalPriceWithTax', 'totalPriceWithoutTax', 'taxAmount'].includes(column.property)) {
|
||||
if (!values.every(value => isNaN(value))) {
|
||||
sums[index] = values.reduce((prev, curr) => {
|
||||
const value = Number(curr);
|
||||
if (!isNaN(value)) {
|
||||
return prev + curr;
|
||||
} else {
|
||||
return prev;
|
||||
}
|
||||
}, 0);
|
||||
sums[index] = sums[index].toFixed(2);
|
||||
} else {
|
||||
sums[index] = '0.00';
|
||||
}
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
});
|
||||
return sums;
|
||||
},
|
||||
addDateRange,
|
||||
getList() {
|
||||
this.loading = true;
|
||||
let query = { ...this.queryParams };
|
||||
query = this.addDateRange(query, this.dateRangeApproval, 'ApproveTime');
|
||||
query = this.addDateRange(query, this.dateRangeEstimated, 'ReceiptTime');
|
||||
query = this.addDateRange(query, this.dateRangeVendor, 'VendorTicketTime');
|
||||
|
||||
listReceipt(query).then(response => {
|
||||
this.receiptList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.dateRangeApproval = [];
|
||||
this.dateRangeEstimated = [];
|
||||
this.dateRangeVendor = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.addOpen = true;
|
||||
},
|
||||
/** 新增提交 */
|
||||
handleAddSubmit(form) {
|
||||
addReceipt(form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.addOpen = false;
|
||||
this.getList();
|
||||
}).catch(error => {
|
||||
console.error("新增收票单失败", error);
|
||||
this.$modal.msgError("新增失败");
|
||||
});
|
||||
},
|
||||
/** 详情按钮操作 */
|
||||
handleDetail(row) {
|
||||
getReceipt(row.id).then(response => {
|
||||
this.detailData = response.data;
|
||||
this.detailData.approveNode =row.approveNode;
|
||||
this.detailOpen = true;
|
||||
});
|
||||
},
|
||||
/** 收票附件按钮操作 */
|
||||
handleReceipt(row) {
|
||||
this.currentRow = row;
|
||||
this.receiptOpen = true;
|
||||
this.autoUpload = false;
|
||||
this.uploadType = 'normal';
|
||||
},
|
||||
handleApplyInvoice(row) {
|
||||
this.currentRow = row;
|
||||
this.receiptOpen = true;
|
||||
this.autoUpload = true;
|
||||
this.uploadType = 'invoice';
|
||||
},
|
||||
handleApplyRedRush(row) {
|
||||
this.currentRow = row;
|
||||
this.receiptOpen = true;
|
||||
this.autoUpload = true;
|
||||
this.uploadType = 'red_rush';
|
||||
},
|
||||
/** 退回按钮操作 */
|
||||
handleReturn(row) {
|
||||
this.$modal.confirm('是否将该笔采购-收票单退回至采购-应付单').then(function() {
|
||||
return returnReceipt(row.id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("退回成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
handleRevoke(row) {
|
||||
const msg = row.ticketBillType === 'FROM_PAYABLE'
|
||||
? '是否将该笔采购-收票单撤销,撤销后重新上传发票'
|
||||
: '是否将该笔采购-红冲收票单撤销,将红冲收票单撤销至收票单';
|
||||
this.$modal.confirm(msg).then(function() {
|
||||
return revokeReceipt(row.id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("撤销成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,326 @@
|
|||
<template>
|
||||
<el-drawer title="修改销售应收单" :visible.sync="internalVisible" size="70%" @close="handleClose">
|
||||
<div class="dialog-body">
|
||||
<!-- Part 1: Details -->
|
||||
<div>
|
||||
<el-divider content-position="left">销售-应收单</el-divider>
|
||||
<div class="details-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>项目编号:</strong> {{ formData.projectCode }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>项目名称:</strong> {{ formData.projectName }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>销售-应收单编号:</strong> {{ formData.receivableBillCode }}</div>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>生成时间:</strong> {{ formData.createTime }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>该进货商是否有预收单:</strong> {{ formData.remainingAmount == 0 ? '否' : '是' }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>预收金额:</strong> {{ formatCurrency(formData.remainingAmount) }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>进货商名称:</strong> {{ formData.partnerName }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>合同编号:</strong> {{ formData.orderCode }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>出库单号:</strong> {{ formData.inventoryCode }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>含税总价(元):</strong> {{ formatCurrency(formData.totalPriceWithTax) }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>未税总价(元):</strong> {{ formatCurrency(formData.totalPriceWithoutTax) }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>税额(元):</strong> {{ formatCurrency(formData.taxAmount) }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>未收款金额:</strong> {{ formatCurrency(formData.unreceivedAmount) }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>已收款金额:</strong> {{ formatCurrency(formData.receivedAmount) }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>收款中金额:</strong> {{ formatCurrency(this.$calc.sub(this.$calc.sub(formData.totalPriceWithTax,formData.receivedAmount),formData.unreceivedAmount)) }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>未开票金额:</strong> {{ formatCurrency(formData.uninvoicedAmount) }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>已开票金额:</strong> {{ formatCurrency(formData.invoicedAmount) }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>开票中金额:</strong> {{ formatCurrency(this.$calc.sub(this.$calc.sub(formData.totalPriceWithTax,formData.invoicedAmount),formData.uninvoicedAmount))}}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>生成收款单:</strong> {{ formData.totalPriceWithTax==formData.unreceivedAmount ? '未生成' : formData.unreceivedAmount == 0 ? '全部生成' : '部分生成'}}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item"><strong>生成开票单:</strong> {{ formData.totalPriceWithTax==formData.uninvoicedAmount ? '未生成' : formData.uninvoicedAmount == 0 ? '全部生成' : '部分生成'}}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item" style="display: flex"><strong>产品类型:</strong>
|
||||
<dict-tag :options="dict.type.product_type" :value="formData.productType"/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Part 2: Tabs -->
|
||||
<div style="padding: 20px">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="明细" name="details">
|
||||
<el-divider content-position="left">销售-收款单</el-divider>
|
||||
<el-table :data="formData.detailList" style="width: 100%" show-summary :summary-method="getSummaries">
|
||||
<el-table-column type="index" label="序号" width="50"></el-table-column>
|
||||
<el-table-column prop="receivableDetailType" label="收款通道">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.receivable_detail_type" :value="scope.row.receivableDetailType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="actualReceiptTime" label="实际收款时间">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.actualReceiptTime || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="receiptAmount" label="本次收款金额" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column prop="receiptRate" label="本次收款比例"></el-table-column>
|
||||
<el-table-column prop="receiptStatus" label="收款状态">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.receipt_bill_status" :value="scope.row.receiptStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="receiptBillCode" label="销售-收款单编号"></el-table-column>
|
||||
<el-table-column label="回执单/退款图">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.finAttachment">
|
||||
<el-button type="text" size="mini" icon="el-icon-view" @click="handlePreview(scope.row.finAttachment)">预览</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-download"
|
||||
@click="downloadFile(scope.row.finAttachment)">下载</el-button>
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-divider content-position="left">销售-开票单</el-divider>
|
||||
<el-table :data="formData.invoiceDetailList" style="width: 100%" show-summary :summary-method="getInvoiceSummaries">
|
||||
<el-table-column type="index" label="序号" width="50"></el-table-column>
|
||||
<el-table-column prop="receivableDetailType" label="开票通道">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.invoice_detail_type" :value="scope.row.receivableDetailType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="actualInvoiceTime" label="实际开票时间">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.actualInvoiceTime || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="invoiceAmount" label="本次开票金额" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column prop="invoiceRate" label="本次开票比例"></el-table-column>
|
||||
<el-table-column prop="invoiceStatus" label="开票状态">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.invoice_bill_status" :value="scope.row.invoiceStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="invoiceBillCode" label="销售-开票单编号"></el-table-column>
|
||||
<el-table-column label="发票/红冲发票">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.attachments">
|
||||
<!-- <el-button type="text" size="mini" icon="el-icon-view" @click="handlePreview(scope.row.attachments)">预览</el-button>-->
|
||||
<el-button type="text" size="mini" icon="el-icon-download"
|
||||
@click="downloadFile(scope.row.attachments)">下载</el-button>
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="收款计划" name="receiptPlan">
|
||||
<ReceiptPlan :isInitEdit=true @syncPlan="refreshInvoicePlan()" :receivableData="data"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="开票计划" name="invoicePlan">
|
||||
<InvoicePlan ref="invoicePlan" :isInitEdit=true :receivableData="data"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
<GlobalFilePreview ref="filePreview" />
|
||||
<!-- <div slot="footer" class="dialog-footer">-->
|
||||
<!-- <el-button @click="handleClose">取 消</el-button>-->
|
||||
<!-- <el-button type="primary" @click="handleSubmit">确 定</el-button>-->
|
||||
<!-- </div>-->
|
||||
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ReceiptPlan from './ReceiptPlan.vue';
|
||||
import InvoicePlan from './InvoicePlan.vue';
|
||||
import { getReceivable } from "@/api/finance/receivable";
|
||||
import GlobalFilePreview from "@/components/GlobalFilePreview/index.vue";
|
||||
|
||||
export default {
|
||||
name: "EditForm",
|
||||
dicts: ['product_type','receipt_bill_status','receivable_detail_type', 'invoice_bill_status','invoice_detail_type'],
|
||||
components: {
|
||||
GlobalFilePreview,
|
||||
ReceiptPlan,
|
||||
InvoicePlan
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internalVisible: this.visible, // Local copy of the visible prop
|
||||
activeTab: 'details',
|
||||
formData: {}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
visible(newVal) {
|
||||
this.internalVisible = newVal; // Sync prop to local data
|
||||
if (newVal && this.data.id) {
|
||||
this.getDetails();
|
||||
}
|
||||
},
|
||||
internalVisible(newVal) {
|
||||
if (!newVal) {
|
||||
this.formData = {};
|
||||
}
|
||||
this.$emit('update:visible', newVal); // Emit changes to parent
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handlePreview(attachment) {
|
||||
this.$refs.filePreview.handlePreview(attachment);
|
||||
},
|
||||
downloadFile(attachments) {
|
||||
attachments?.forEach(attachment => {
|
||||
this.$refs.filePreview.downloadFile(attachment);
|
||||
})
|
||||
|
||||
},
|
||||
getDetails() {
|
||||
getReceivable(this.data.id).then(res => {
|
||||
this.formData = res.data;
|
||||
});
|
||||
},
|
||||
refreshInvoicePlan(){
|
||||
this.$refs.invoicePlan.fetchInvoicePlans(this.formData.id);
|
||||
},
|
||||
getSummaries(param) {
|
||||
const { columns, data } = param;
|
||||
const sums = [];
|
||||
let receiptAmountSum = 0;
|
||||
if (data && data.length > 0) {
|
||||
receiptAmountSum = data.reduce((acc, item) => {
|
||||
const value = Number(item.receiptAmount);
|
||||
return acc + (isNaN(value) ? 0 : value);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '合计';
|
||||
} else if (column.property === 'receiptAmount') {
|
||||
sums[index] = receiptAmountSum.toFixed(2);
|
||||
} else if (column.property === 'receiptRate') {
|
||||
if (this.formData.totalPriceWithTax && this.formData.totalPriceWithTax > 0) {
|
||||
const ratio = this.$calc.div(receiptAmountSum , this.formData.totalPriceWithTax,4);
|
||||
sums[index] = (ratio * 100).toFixed(2) + '%';
|
||||
} else {
|
||||
sums[index] = '0.00%';
|
||||
}
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
});
|
||||
return sums;
|
||||
},
|
||||
getInvoiceSummaries(param) {
|
||||
const { columns, data } = param;
|
||||
const sums = [];
|
||||
let invoiceAmountSum = 0;
|
||||
if (data && data.length > 0) {
|
||||
invoiceAmountSum = data.reduce((acc, item) => {
|
||||
const value = Number(item.invoiceAmount);
|
||||
return acc + (isNaN(value) ? 0 : value);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '合计';
|
||||
} else if (column.property === 'invoiceAmount') {
|
||||
sums[index] = invoiceAmountSum.toFixed(2);
|
||||
} else if (column.property === 'invoiceRate') {
|
||||
if (this.formData.totalPriceWithTax && this.formData.totalPriceWithTax > 0) {
|
||||
const ratio = this.$calc.div(invoiceAmountSum , this.formData.totalPriceWithTax,4);
|
||||
sums[index] = (ratio * 100).toFixed(2) + '%';
|
||||
} else {
|
||||
sums[index] = '0.00%';
|
||||
}
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
});
|
||||
return sums;
|
||||
},
|
||||
handleClose() {
|
||||
this.internalVisible = false; // Close dialog locally
|
||||
},
|
||||
handleSubmit() {
|
||||
this.handleClose();
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>.details-container {
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dialog-body {
|
||||
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
<template>
|
||||
|
||||
<div class="dialog-body">
|
||||
<el-divider content-position="left">开票计划</el-divider>
|
||||
<el-button v-if="isEditing" type="primary" size="mini" @click="handleSaveInvoicePlan"
|
||||
style="margin-bottom: 10px;">
|
||||
保存开票计划
|
||||
</el-button>
|
||||
<el-button v-else type="primary" size="mini" @click="isEditing=true"
|
||||
style="margin-bottom: 10px;">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-table :data="invoicePlans" border @selection-change="selectPlan" ref="invoicePlanTable">
|
||||
<el-table-column type="selection" width="50" align="center" :selectable="selectableRow"/>
|
||||
<el-table-column label="序号" type="index" width="50" align="center"></el-table-column>
|
||||
<el-table-column label="预计开票时间" align="center" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-date-picker v-model="scope.row.planInvoiceDate" type="datetime" style="width: 180px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="选择日期"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"></el-date-picker>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计开票金额" align="center" width="230">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.planAmount"
|
||||
:precision="2"
|
||||
:step="100"
|
||||
:min="0.01"
|
||||
:readonly="!scope.row.detailId"
|
||||
:max="totalPriceWithTax"
|
||||
@change="handleAmountChange(scope.row)"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"
|
||||
></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开票比例(%)" align="center" width="230">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.planRate"
|
||||
:precision="2"
|
||||
:step="1"
|
||||
:min="0.01"
|
||||
:max="100"
|
||||
:readonly="!scope.row.detailId"
|
||||
@change="handleRateChange(scope.row)"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"
|
||||
></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.remark" placeholder="请输入备注"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="150" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="isEditing && !isNumberStr(scope.row.detailId)"
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-plus"
|
||||
@click="handleAddInvoicePlanRow"
|
||||
>增加下行
|
||||
</el-button>
|
||||
<el-button v-if="isEditing && !isNumberStr(scope.row.detailId)"
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDeleteInvoicePlanRow(scope.$index)"
|
||||
:disabled="invoicePlans.length === 1 || scope.row.status === 'invoiced'"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getInvoicePlan, updateInvoicePlan} from "@/api/finance/receivable";
|
||||
import {isNumberStr} from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "InvoicePlan",
|
||||
props: {
|
||||
receivableData: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
}
|
||||
},
|
||||
isInitEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selectedPlans: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedPlan:[],
|
||||
isEditing: false,
|
||||
loading: false,
|
||||
invoicePlans: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return `选择开票计划 - ${this.receivableData.receivableBillCode}`;
|
||||
},
|
||||
totalPlannedAmount() {
|
||||
return this.invoicePlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
},
|
||||
totalUninvoicedAmount() {
|
||||
return this.receivableData.uninvoicedAmount || 0;
|
||||
},
|
||||
totalPriceWithTax() {
|
||||
return this.receivableData.totalPriceWithTax || 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'receivableData.id': {
|
||||
handler(newVal, oldVal) {
|
||||
if (newVal) {
|
||||
this.fetchInvoicePlans(newVal)
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
isInitEdit: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.isEditing = newVal
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isNumberStr,
|
||||
selectableRow(row, index){
|
||||
return !row.detailId;
|
||||
},
|
||||
selectPlan( selection){
|
||||
this.selectedPlan=selection
|
||||
},
|
||||
fetchInvoicePlans(receivableId) {
|
||||
if (this.receivableData && receivableId) {
|
||||
getInvoicePlan(receivableId).then(response => {
|
||||
this.invoicePlans = response.data.map(item => ({
|
||||
...item,
|
||||
status: item.status || 'pending'
|
||||
}));
|
||||
if (this.invoicePlans.length === 0) {
|
||||
this.initDefaultInvoicePlan();
|
||||
} else {
|
||||
this.$nextTick(() => {
|
||||
this.invoicePlans.forEach(plan => {
|
||||
const isSelected = this.selectedPlans.some(selected => selected.id === plan.id);
|
||||
if (isSelected) {
|
||||
this.$refs.invoicePlanTable.toggleRowSelection(plan, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.initDefaultInvoicePlan();
|
||||
|
||||
}
|
||||
},
|
||||
initDefaultInvoicePlan() {
|
||||
this.invoicePlans = [{
|
||||
planInvoiceDate: null,
|
||||
planAmount: this.totalPriceWithTax,
|
||||
planRate: 100,
|
||||
remark: '',
|
||||
status: 'pending'
|
||||
}];
|
||||
},
|
||||
handleSaveInvoicePlan() {
|
||||
if (!this.validateInvoicePlans()) {
|
||||
return;
|
||||
}
|
||||
if (!this.validateInvoicePlanTotals()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.invoicePlans.length; i++) {
|
||||
const plan = this.invoicePlans[i];
|
||||
if (!plan.planInvoiceDate) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行开票计划的预计开票时间不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount === '' || plan.planAmount < 0) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行开票计划的预计开票金额不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planRate === null || plan.planRate === undefined || plan.planRate === '') {
|
||||
this.$modal.msgError(`第 ${i + 1} 行开票计划的开票比例不能为空。`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
updateInvoicePlan(this.receivableData.id, this.invoicePlans).then(() => {
|
||||
this.$modal.msgSuccess("保存成功");
|
||||
this.fetchInvoicePlans(this.receivableData.id);
|
||||
});
|
||||
},
|
||||
|
||||
handleAddInvoicePlanRow() {
|
||||
this.invoicePlans.push({
|
||||
planInvoiceDate: null,
|
||||
planAmount: 0,
|
||||
planRate: 0,
|
||||
remark: '',
|
||||
status: 'pending'
|
||||
});
|
||||
},
|
||||
handleDeleteInvoicePlanRow(index) {
|
||||
if (this.invoicePlans.length === 1) {
|
||||
this.$modal.msgError("至少需要保留一条开票计划。");
|
||||
return;
|
||||
}
|
||||
if (this.invoicePlans[index].status === 'invoiced') {
|
||||
this.$modal.msgError("已开票的计划不能删除。");
|
||||
return;
|
||||
}
|
||||
this.invoicePlans.splice(index, 1);
|
||||
},
|
||||
handleAmountChange(row) {
|
||||
if (this.totalPriceWithTax === 0) {
|
||||
row.planRate = 0;
|
||||
return;
|
||||
}
|
||||
row.planRate = this.$calc.mul((this.$calc.div(row.planAmount, this.totalPriceWithTax,4)), 100);
|
||||
},
|
||||
handleRateChange(row) {
|
||||
row.planAmount = this.$calc.div(this.$calc.mul(this.totalPriceWithTax , row.planRate),100);
|
||||
},
|
||||
validateInvoicePlanTotals() {
|
||||
const totalAmount = this.invoicePlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
|
||||
if (totalAmount !== this.totalPriceWithTax) {
|
||||
this.$modal.msgError(`预计开票金额之和应该等于应收总金额[${this.totalPriceWithTax}]`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
validateInvoicePlans() {
|
||||
if (this.invoicePlans.length === 0) {
|
||||
this.$modal.msgError("请至少添加一条开票计划。");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.invoicePlans.length; i++) {
|
||||
const plan = this.invoicePlans[i];
|
||||
if (!plan.planInvoiceDate) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行开票计划的预计开票时间不能为空。`);
|
||||
return false;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount <= 0) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行开票计划的预计开票金额必须大于0。`);
|
||||
return false;
|
||||
}
|
||||
if (plan.planRate === null || plan.planRate === undefined || plan.planRate === '') {
|
||||
this.$modal.msgError(`第 ${i + 1} 行开票计划的开票比例不能为空。`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px; /* To prevent scrollbar from overlapping content */
|
||||
}
|
||||
|
||||
.total-info {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
<template>
|
||||
<el-dialog title="合并发起开票单" :visible.sync="dialogVisible" width="80%" @close="handleClose" append-to-body>
|
||||
<div class="dialog-body">
|
||||
<el-form ref="form" :model="form" :inline="true" label-width="120px">
|
||||
<el-row>
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-form-item label="开票单类型" prop="invoiceBillType">-->
|
||||
<!-- <el-select disabled v-model="form.invoiceBillType" placeholder="请选择开票单类型" clearable>-->
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="dict in dict.type.receipt_bill_type"-->
|
||||
<!-- :key="dict.value"-->
|
||||
<!-- :label="dict.label"-->
|
||||
<!-- :value="dict.value"-->
|
||||
<!-- />-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="24">
|
||||
<el-form-item label="进货商名称">
|
||||
<el-input v-model="form.partnerName" style="width:400px" readonly/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-form-item label="预计开票时间" prop="invoiceTime">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- v-model="form.invoiceTime"-->
|
||||
<!-- type="date"-->
|
||||
<!-- value-format="yyyy-MM-dd HH:mm:ss"-->
|
||||
<!-- placeholder="选择日期"-->
|
||||
<!-- ></el-date-picker>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-divider content-position="left">销售-应收单表</el-divider>
|
||||
<el-table :data="receivableBillsWithPlans" border max-height="300px" style="margin-bottom: 20px;">
|
||||
<el-table-column label="销售-应收单编号" align="center" prop="receivableBillCode" width="150"/>
|
||||
<el-table-column label="预计开票时间" align="center" prop="planInvoiceDate" width="180"/>
|
||||
<!-- <el-table-column label="开票计划" align="center" width="100" prop="planInvoiceAmount">-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="预期开票金额" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency(calculateOrderCurrentInvoiceAmount(scope.row.id).toFixed(2)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预期开票比例" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ calculateOrderCurrentInvoiceRate(scope.row.id) }}%
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" width="150"/>
|
||||
<el-table-column label="进货商名称" align="center" prop="partnerName" width="150"/>
|
||||
<!-- <el-table-column label="出入库单号" align="center" prop="inventoryCode" width="150"/>-->
|
||||
<!-- <el-table-column label="开票状态" align="center" prop="invoiceStatus" width="120">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.invoice_status" :value="scope.row.invoiceStatus"/>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="含税总价" align="center" prop="totalPriceWithTax" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未开票金额" align="center" prop="uninvoicedAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
|
||||
<el-table-column label="已开票金额" align="center" prop="invoicedAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="开票中金额" align="center" prop="invoicedAmount" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency($calc.sub($calc.sub(scope.row.totalPriceWithTax, scope.row.invoicedAmount), scope.row.uninvoicedAmount)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleOpenInvoicePlanSelector(scope.row, scope.$index)"
|
||||
>选择
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="total-info">
|
||||
|
||||
<span style="margin-left: 20px;">计划开票总金额: <el-tag type="success">{{
|
||||
formatCurrency(totalPlannedAmount.toFixed(2))
|
||||
}}</el-tag></span>
|
||||
<span>计划开票比例: <el-tag type="info">{{ this.$calc.mul(this.$calc.div(totalPlannedAmount,totalReceivableAmountWithTax,4),100) }}%</el-tag></span>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleCancel">取 消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确 定</el-button>
|
||||
</div>
|
||||
<el-dialog :title="planTitle" :visible.sync="isInvoicePlanSelectorOpen" width="70%"
|
||||
@close="isInvoicePlanSelectorOpen=false" append-to-body>
|
||||
<invoice-plan
|
||||
ref="planSelector"
|
||||
:receivable-data="chooseReceivable"
|
||||
:selected-plans="chooseReceivable.invoicePlans"
|
||||
@confirm="handleInvoicePlanConfirm"
|
||||
/>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="isInvoicePlanSelectorOpen=false">取 消</el-button>
|
||||
<!-- <el-button type="primary" @click="handleConfirm" >确 定</el-button>-->
|
||||
<el-button type="primary" @click="handleChooseConfirm">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!-- 开票计划选择器弹窗 -->
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import InvoicePlan from './InvoicePlan.vue';
|
||||
|
||||
export default {
|
||||
name: "MergeInvoiceDialog",
|
||||
components: {InvoicePlan},
|
||||
dicts: ['invoice_status','receipt_bill_type'], // Add dicts for dict-tag
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
receivableBills: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internalVisible: this.visible,
|
||||
planTitle: '',
|
||||
chooseReceivable: {},
|
||||
form: {
|
||||
invoiceBillType: 'FROM_RECEIVABLE', // Default to a type
|
||||
partnerName: '',
|
||||
invoiceTime: null,
|
||||
},
|
||||
receivableBillsWithPlans: [], // Each order will now have its own invoicePlans array
|
||||
isInvoicePlanSelectorOpen: false,
|
||||
currentReceivableBillIndexForPlan: -1, // Index of the order in receivableBillsWithPlans
|
||||
loadingInvoicePlans: false, // Loading state for fetching invoice plans
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
dialogVisible: {
|
||||
get() {
|
||||
return this.internalVisible;
|
||||
},
|
||||
set(val) {
|
||||
this.internalVisible = val;
|
||||
this.$emit('update:visible', val);
|
||||
}
|
||||
},
|
||||
totalReceivableAmountWithTax() {
|
||||
return this.receivableBillsWithPlans.reduce((sum, order) => sum + (order.totalPriceWithTax || 0), 0);
|
||||
},
|
||||
totalPlannedAmount() {
|
||||
return this.receivableBillsWithPlans.reduce((orderSum, order) => {
|
||||
const orderPlansTotal = (order.invoicePlans || []).reduce((planSum, plan) => planSum + (plan.planAmount || 0), 0);
|
||||
return orderSum + orderPlansTotal;
|
||||
}, 0);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
visible(newVal) {
|
||||
this.internalVisible = newVal;
|
||||
if (newVal) {
|
||||
this.initDialogData();
|
||||
}
|
||||
},
|
||||
receivableBills: {
|
||||
handler(newVal) {
|
||||
if (this.dialogVisible) {
|
||||
this.initDialogData();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initDialogData() {
|
||||
// Initialize form fields
|
||||
if (this.receivableBills.length > 0) {
|
||||
const partnerName = this.receivableBills[0].partnerName;
|
||||
const allSameCustomer = this.receivableBills.every(order => order.partnerName === partnerName);
|
||||
this.form.partnerName = allSameCustomer ? partnerName : '多个客户';
|
||||
this.form.invoiceTime = null; // Reset time
|
||||
} else {
|
||||
this.form.partnerName = '';
|
||||
this.form.invoiceTime = null;
|
||||
}
|
||||
this.form.invoiceBillType = 'FROM_RECEIVABLE'; // Default
|
||||
|
||||
// Initialize receivableBillsWithPlans
|
||||
this.receivableBillsWithPlans = this.receivableBills.map(order => {
|
||||
const invoicePlans = order.invoicePlans ? [...order.invoicePlans] : [];
|
||||
if (invoicePlans.length === 0 && order.lastInvoicePlanId) {
|
||||
invoicePlans.push({
|
||||
id: order.lastInvoicePlanId,
|
||||
planAmount: order.planInvoiceAmount,
|
||||
planInvoiceDate: order.planInvoiceDate,
|
||||
planRate: this.$calc.mul(this.$calc.div(order.planInvoiceAmount, order.totalPriceWithTax, 4), 100)
|
||||
});
|
||||
}
|
||||
return {
|
||||
...order,
|
||||
invoicePlans: invoicePlans, // Retain existing plans if any, otherwise empty
|
||||
totalPriceWithTax: order.totalPriceWithTax || 0, // Ensure numeric for calculations
|
||||
uninvoicedAmount: order.uninvoicedAmount || 0,
|
||||
invoicedAmount: order.invoicedAmount || 0, // Ensure numeric for calculations
|
||||
}
|
||||
});
|
||||
},
|
||||
handleClose() {
|
||||
this.dialogVisible = false;
|
||||
this.resetForm();
|
||||
},
|
||||
handleChooseConfirm() {
|
||||
if (!this.$refs.planSelector) {
|
||||
this.$modal.msgError('无法获取计划选择器组件');
|
||||
return;
|
||||
}
|
||||
const selectedPlans = this.$refs.planSelector.selectedPlan || [];
|
||||
|
||||
const orderIndex = this.receivableBillsWithPlans.findIndex(o => o.id === this.chooseReceivable.id);
|
||||
if (orderIndex === -1) {
|
||||
this.$modal.msgError('找不到要更新的应收单');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentOrder = this.receivableBillsWithPlans[orderIndex];
|
||||
|
||||
// Update the invoice plans for the specific order
|
||||
this.$set(currentOrder, 'invoicePlans', [...selectedPlans]);
|
||||
|
||||
this.isInvoicePlanSelectorOpen = false;
|
||||
this.$modal.msgSuccess(`已更新开票计划选择,共 ${selectedPlans.length} 条`);
|
||||
},
|
||||
handleConfirm() {
|
||||
// Validate main form fields
|
||||
// if (!this.form.invoiceBillType) {
|
||||
// this.$modal.msgError('请选择开票单类型');
|
||||
// return;
|
||||
// }
|
||||
// if (!this.form.invoiceTime) {
|
||||
// this.$modal.msgError('请选择预计开票时间');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Validate each receivable order's invoice plans
|
||||
for (const order of this.receivableBillsWithPlans) {
|
||||
if (!order.invoicePlans || order.invoicePlans.length === 0) {
|
||||
this.$modal.msgError(`应收单 ${order.receivableBillCode} 至少需要一条开票计划`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (const plan of order.invoicePlans) {
|
||||
if (!plan.planInvoiceDate) {
|
||||
this.$modal.msgError(`应收单 ${order.receivableBillCode} 的开票计划中预计开票时间不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount <= 0) {
|
||||
this.$modal.msgError(`应收单 ${order.receivableBillCode} 的开票计划中预计开票金额必须大于0。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planRate === null || plan.planRate === undefined) {
|
||||
this.$modal.msgError(`应收单 ${order.receivableBillCode} 的开票计划中开票比例不能为空。`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the final data to be emitted to the parent
|
||||
const mergedInvoiceData = {
|
||||
invoiceBillType: this.form.invoiceBillType,
|
||||
invoiceTime: this.form.invoiceTime,
|
||||
// Collect all receivable orders with their updated invoice plans
|
||||
receivableBills: this.receivableBillsWithPlans.map(order => ({
|
||||
id: order.id,
|
||||
receivableBillCode: order.receivableBillCode,
|
||||
taxRate: order.taxRate,
|
||||
invoicePlans: order.invoicePlans.map(plan => ({
|
||||
planInvoiceDate: plan.planInvoiceDate,
|
||||
planAmount: plan.planAmount,
|
||||
planRate: plan.planRate,
|
||||
remark: plan.remark,
|
||||
id: plan.id,
|
||||
})),
|
||||
})),
|
||||
totalMergeInvoiceAmount: this.totalPlannedAmount, // Total amount for the merged bill
|
||||
};
|
||||
|
||||
this.$emit('confirm', mergedInvoiceData);
|
||||
this.dialogVisible = false;
|
||||
},
|
||||
handleCancel() {
|
||||
this.dialogVisible = false;
|
||||
this.resetForm();
|
||||
},
|
||||
resetForm() {
|
||||
this.form = {
|
||||
invoiceBillType: 'FROM_RECEIVABLE',
|
||||
partnerName: '',
|
||||
invoiceTime: null,
|
||||
};
|
||||
this.receivableBillsWithPlans = [];
|
||||
|
||||
this.currentReceivableBillIndexForPlan = -1;
|
||||
this.loadingInvoicePlans = false;
|
||||
},
|
||||
handleOpenInvoicePlanSelector(row, index) {
|
||||
this.planTitle = `选择开票计划 - ${row.receivableBillCode}`;
|
||||
this.chooseReceivable = row;
|
||||
this.currentReceivableBillIndexForPlan = index;
|
||||
this.isInvoicePlanSelectorOpen = true;
|
||||
console.log(this.chooseReceivable.id)
|
||||
},
|
||||
handleInvoicePlanConfirm(updatedPlans) {
|
||||
// Update the invoice plans for the specific order
|
||||
if (this.currentReceivableBillIndexForPlan !== -1) {
|
||||
this.$set(this.receivableBillsWithPlans[this.currentReceivableBillIndexForPlan], 'invoicePlans', updatedPlans);
|
||||
}
|
||||
this.isInvoicePlanSelectorOpen = false;
|
||||
this.currentReceivableBillIndexForPlan = -1;
|
||||
},
|
||||
calculateOrderCurrentInvoiceAmount(orderId) {
|
||||
const order = this.receivableBillsWithPlans.find(o => o.id === orderId);
|
||||
if (order && order.invoicePlans) {
|
||||
return order.invoicePlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
calculateOrderCurrentInvoiceRate(orderId) {
|
||||
const order = this.receivableBillsWithPlans.find(o => o.id === orderId);
|
||||
if (order && order.invoicePlans && order.uninvoicedAmount >= 0) {
|
||||
const currentAmount = this.calculateOrderCurrentInvoiceAmount(orderId);
|
||||
return this.$calc.mul(this.$calc.div(currentAmount ,order.totalPriceWithTax,4 ),100);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px; /* To prevent scrollbar from overlapping content */
|
||||
}
|
||||
|
||||
.total-info {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,358 @@
|
|||
<template>
|
||||
<el-dialog title="合并发起收款单" :visible.sync="dialogVisible" width="80%" @close="handleClose" append-to-body>
|
||||
<div class="dialog-body">
|
||||
<el-form ref="form" :model="form" :inline="true" label-width="120px">
|
||||
<el-row>
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-form-item label="收款单类型" prop="receiptBillType">-->
|
||||
<!-- <el-select disabled v-model="form.receiptBillType" placeholder="请选择收款单类型" clearable>-->
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="dict in dict.type.receipt_bill_type"-->
|
||||
<!-- :key="dict.value"-->
|
||||
<!-- :label="dict.label"-->
|
||||
<!-- :value="dict.value"-->
|
||||
<!-- />-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<el-col :span="24">
|
||||
<el-form-item label="进货商名称">
|
||||
<el-input v-model="form.partnerName" style="width:400px" readonly/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <el-form-item label="预计收款时间" prop="estimatedReceiptTime">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- v-model="form.estimatedReceiptTime"-->
|
||||
<!-- type="date"-->
|
||||
<!-- value-format="yyyy-MM-dd HH:mm:ss"-->
|
||||
<!-- placeholder="选择日期"-->
|
||||
<!-- ></el-date-picker>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-divider content-position="left">销售-应收单表</el-divider>
|
||||
<el-table :data="receivableBillsWithPlans" border max-height="300px" style="margin-bottom: 20px;">
|
||||
<el-table-column label="销售-应收单编号" align="center" prop="receivableBillCode" width="150"/>
|
||||
<el-table-column label="预计收款时间" align="center" prop="planReceiptDate" width="180"/>
|
||||
<!-- <el-table-column label="收款计划" align="center" width="100" prop="planAmount">-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="预期收款金额" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency(calculateOrderCurrentReceiptAmount(scope.row.id).toFixed(2)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预期收款比例" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ calculateOrderCurrentReceiptRate(scope.row.id) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" width="150"/>
|
||||
<el-table-column label="进货商名称" align="center" prop="partnerName" width="150"/>
|
||||
<!-- <el-table-column label="出入库单号" align="center" prop="inventoryCode" width="150"/>-->
|
||||
<!-- <el-table-column label="收款状态" align="center" prop="collectionStatus" width="120">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.collection_status" :value="scope.row.collectionStatus"/>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收款金额(元)" align="center" prop="unreceivedAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已收款金额(元)" align="center" prop="receivedAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收款中金额(元)" align="center" prop="receivedAmount" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency($calc.sub($calc.sub(scope.row.totalPriceWithTax, scope.row.receivedAmount), scope.row.unreceivedAmount)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleOpenReceiptPlanSelector(scope.row, scope.$index)"
|
||||
>选择
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="total-info">
|
||||
|
||||
<span style="margin-left: 20px;">计划收款总金额: <el-tag type="success">{{
|
||||
formatCurrency(totalPlannedAmount.toFixed(2))
|
||||
}}</el-tag></span>
|
||||
<span>计划收款比例: <el-tag type="info">{{ this.$calc.mul(this.$calc.div(totalPlannedAmount,totalReceivableAmountWithTax,4),100) }}%</el-tag></span>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleCancel">取 消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确 定</el-button>
|
||||
</div>
|
||||
<el-dialog :title="planTitle" :visible.sync="isReceiptPlanSelectorOpen" width="70%"
|
||||
@close="isReceiptPlanSelectorOpen=false" append-to-body>
|
||||
<receipt-plan-selector
|
||||
ref="planSelector"
|
||||
:receivable-data="chooseReceivable"
|
||||
:selected-plans="chooseReceivable.receiptPlans"
|
||||
@confirm="handleReceiptPlanConfirm"
|
||||
/>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="isReceiptPlanSelectorOpen=false">取 消</el-button>
|
||||
<!-- <el-button type="primary" @click="handleConfirm" >确 定</el-button>-->
|
||||
<el-button type="primary" @click="handleChooseConfirm">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!-- 收款计划选择器弹窗 -->
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ReceiptPlan from './ReceiptPlan.vue';
|
||||
|
||||
export default {
|
||||
name: "MergeReceiptDialog",
|
||||
components: {ReceiptPlanSelector: ReceiptPlan},
|
||||
dicts: ['collection_status','receipt_bill_type'], // Add dicts for dict-tag
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
receivableBills: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internalVisible: this.visible,
|
||||
planTitle: '',
|
||||
chooseReceivable: {},
|
||||
form: {
|
||||
receiptBillType: 'FROM_RECEIVABLE', // Default to a type
|
||||
customerName: '',
|
||||
estimatedReceiptTime: null,
|
||||
},
|
||||
receivableBillsWithPlans: [], // Each order will now have its own receiptPlans array
|
||||
isReceiptPlanSelectorOpen: false,
|
||||
currentReceivableBillIndexForPlan: -1, // Index of the order in receivableBillsWithPlans
|
||||
loadingReceiptPlans: false, // Loading state for fetching receipt plans
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
dialogVisible: {
|
||||
get() {
|
||||
return this.internalVisible;
|
||||
},
|
||||
set(val) {
|
||||
this.internalVisible = val;
|
||||
this.$emit('update:visible', val);
|
||||
}
|
||||
},
|
||||
totalReceivableAmountWithTax() {
|
||||
return this.receivableBillsWithPlans.reduce((sum, order) => sum + (order.totalPriceWithTax || 0), 0);
|
||||
},
|
||||
totalPlannedAmount() {
|
||||
return this.receivableBillsWithPlans.reduce((orderSum, order) => {
|
||||
const orderPlansTotal = (order.receiptPlans || []).reduce((planSum, plan) => planSum + (plan.planAmount || 0), 0);
|
||||
return orderSum + orderPlansTotal;
|
||||
}, 0);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
visible(newVal) {
|
||||
this.internalVisible = newVal;
|
||||
if (newVal) {
|
||||
this.initDialogData();
|
||||
}
|
||||
},
|
||||
receivableBills: {
|
||||
handler(newVal) {
|
||||
if (this.dialogVisible) {
|
||||
this.initDialogData();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initDialogData() {
|
||||
// Initialize form fields
|
||||
if (this.receivableBills.length > 0) {
|
||||
this.form.partnerName = this.receivableBills[0].partnerName;
|
||||
this.form.estimatedReceiptTime = this.receivableBills[0].estimatedReceiptTime || null; // Use first order's estimated time as default
|
||||
} else {
|
||||
this.form.partnerName = '';
|
||||
this.form.estimatedReceiptTime = null;
|
||||
}
|
||||
this.form.receiptBillType = 'FROM_RECEIVABLE'; // Default
|
||||
|
||||
// Initialize receivableBillsWithPlans
|
||||
this.receivableBillsWithPlans = this.receivableBills.map(order => {
|
||||
const receiptPlans = order.receiptPlans ? [...order.receiptPlans] : [];
|
||||
if (receiptPlans.length === 0 && order.lastReceiptPlanId) {
|
||||
receiptPlans.push({
|
||||
id: order.lastReceiptPlanId,
|
||||
planAmount: order.planAmount,
|
||||
planReceiptDate: order.planReceiptDate,
|
||||
planRate: this.$calc.mul(this.$calc.div(order.planAmount, order.totalPriceWithTax, 4), 100)
|
||||
});
|
||||
}
|
||||
return {
|
||||
...order,
|
||||
receiptPlans: receiptPlans, // Retain existing plans if any, otherwise empty
|
||||
totalPriceWithTax: order.totalPriceWithTax || 0, // Ensure numeric for calculations
|
||||
unreceivedAmount: order.unreceivedAmount || 0,
|
||||
receivedAmount: order.receivedAmount || 0, // Ensure numeric for calculations
|
||||
}
|
||||
});
|
||||
},
|
||||
handleClose() {
|
||||
this.dialogVisible = false;
|
||||
this.resetForm();
|
||||
},
|
||||
handleChooseConfirm() {
|
||||
if (!this.$refs.planSelector) {
|
||||
this.$modal.msgError('无法获取计划选择器组件');
|
||||
return;
|
||||
}
|
||||
const selectedPlans = this.$refs.planSelector.selectedPlan || [];
|
||||
|
||||
const orderIndex = this.receivableBillsWithPlans.findIndex(o => o.id === this.chooseReceivable.id);
|
||||
if (orderIndex === -1) {
|
||||
this.$modal.msgError('找不到要更新的应收单');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentOrder = this.receivableBillsWithPlans[orderIndex];
|
||||
|
||||
// Update the receipt plans for the specific order
|
||||
this.$set(currentOrder, 'receiptPlans', [...selectedPlans]);
|
||||
|
||||
this.isReceiptPlanSelectorOpen = false;
|
||||
this.$modal.msgSuccess(`已更新收款计划选择,共 ${selectedPlans.length} 条`);
|
||||
},
|
||||
handleConfirm() {
|
||||
// Validate main form fields
|
||||
// if (!this.form.receiptBillType) {
|
||||
// this.$modal.msgError('请选择收款单类型');
|
||||
// return;
|
||||
// }
|
||||
// if (!this.form.estimatedReceiptTime) {
|
||||
// this.$modal.msgError('请选择预计收款时间');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Validate each receivable order's receipt plans
|
||||
for (const order of this.receivableBillsWithPlans) {
|
||||
if (!order.receiptPlans || order.receiptPlans.length === 0) {
|
||||
this.$modal.msgError(`应收单 ${order.receivableBillCode} 至少需要一条收款计划`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (const plan of order.receiptPlans) {
|
||||
if (!plan.planReceiptDate) {
|
||||
this.$modal.msgError(`应收单 ${order.receivableBillCode} 的收款计划中预计收款时间不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount <= 0) {
|
||||
this.$modal.msgError(`应收单 ${order.receivableBillCode} 的收款计划中预计收款金额必须大于0。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planRate === null || plan.planRate === undefined) {
|
||||
this.$modal.msgError(`应收单 ${order.receivableBillCode} 的收款计划中应收比例不能为空。`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the final data to be emitted to the parent
|
||||
const mergedReceiptData = {
|
||||
receiptBillType: this.form.receiptBillType,
|
||||
estimatedReceiptTime: this.form.estimatedReceiptTime,
|
||||
// Collect all receivable orders with their updated receipt plans
|
||||
receivableBills: this.receivableBillsWithPlans.map(order => ({
|
||||
id: order.id,
|
||||
taxRate: order.taxRate,
|
||||
receivableBillCode: order.receivableBillCode,
|
||||
receiptPlans: order.receiptPlans.map(plan => ({
|
||||
planReceiptDate: plan.planReceiptDate,
|
||||
planAmount: plan.planAmount,
|
||||
planRate: plan.planRate,
|
||||
remark: plan.remark,
|
||||
id: plan.id,
|
||||
})),
|
||||
})),
|
||||
totalMergeReceiptAmount: this.totalPlannedAmount, // Total amount for the merged bill
|
||||
};
|
||||
|
||||
this.$emit('confirm', mergedReceiptData);
|
||||
this.dialogVisible = false;
|
||||
},
|
||||
handleCancel() {
|
||||
this.dialogVisible = false;
|
||||
this.resetForm();
|
||||
},
|
||||
resetForm() {
|
||||
this.form = {
|
||||
receiptBillType: 'FROM_RECEIVABLE',
|
||||
customerName: '',
|
||||
estimatedReceiptTime: null,
|
||||
};
|
||||
this.receivableBillsWithPlans = [];
|
||||
|
||||
this.currentReceivableBillIndexForPlan = -1;
|
||||
this.loadingReceiptPlans = false;
|
||||
},
|
||||
handleOpenReceiptPlanSelector(row, index) {
|
||||
this.planTitle = `选择收款计划 - ${row.receivableBillCode}`;
|
||||
this.chooseReceivable = row;
|
||||
this.currentReceivableBillIndexForPlan = index;
|
||||
this.isReceiptPlanSelectorOpen = true;
|
||||
console.log(this.chooseReceivable.id)
|
||||
},
|
||||
handleReceiptPlanConfirm(updatedPlans) {
|
||||
// Update the receipt plans for the specific order
|
||||
if (this.currentReceivableBillIndexForPlan !== -1) {
|
||||
this.$set(this.receivableBillsWithPlans[this.currentReceivableBillIndexForPlan], 'receiptPlans', updatedPlans);
|
||||
}
|
||||
this.isReceiptPlanSelectorOpen = false;
|
||||
this.currentReceivableBillIndexForPlan = -1;
|
||||
},
|
||||
calculateOrderCurrentReceiptAmount(orderId) {
|
||||
const order = this.receivableBillsWithPlans.find(o => o.id === orderId);
|
||||
if (order && order.receiptPlans) {
|
||||
return order.receiptPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
calculateOrderCurrentReceiptRate(orderId) {
|
||||
const order = this.receivableBillsWithPlans.find(o => o.id === orderId);
|
||||
if (order && order.receiptPlans && order.unreceivedAmount >= 0) {
|
||||
const currentAmount = this.calculateOrderCurrentReceiptAmount(orderId);
|
||||
return this.$calc.mul((this.$calc.div(currentAmount ,order.totalPriceWithTax,4 )),100);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px; /* To prevent scrollbar from overlapping content */
|
||||
}
|
||||
|
||||
.total-info {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,312 @@
|
|||
<template>
|
||||
|
||||
<div class="dialog-body">
|
||||
<el-divider content-position="left">收款计划</el-divider>
|
||||
<el-button v-if="isEditing" type="primary" size="mini" @click="handleSaveReceiptPlan"
|
||||
style="margin-bottom: 10px;">
|
||||
保存收款计划
|
||||
</el-button>
|
||||
<el-button v-if="isEditing" type="primary" size="mini" @click="handleSyncToInvoicePlan"
|
||||
style="margin-bottom: 10px; margin-left: 10px;">
|
||||
同步至开票计划
|
||||
</el-button>
|
||||
<el-button v-else type="primary" size="mini" @click="isEditing=true"
|
||||
style="margin-bottom: 10px;">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-table :data="receiptPlans" border @selection-change="selectPlan" ref="receiptPlanTable">
|
||||
<el-table-column type="selection" width="50" align="center" :selectable="selectableRow"/>
|
||||
<el-table-column label="序号" type="index" width="50" align="center"></el-table-column>
|
||||
<el-table-column label="预计收款时间" align="center" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-date-picker v-model="scope.row.planReceiptDate" type="datetime" style="width: 180px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="选择日期"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"></el-date-picker>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计收款金额" align="center" width="230">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.planAmount"
|
||||
:precision="2"
|
||||
:step="100"
|
||||
:min="0.01"
|
||||
:readonly="!scope.row.detailId"
|
||||
:max="totalPriceWithTax"
|
||||
@change="handleAmountChange(scope.row)"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"
|
||||
></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="应收比例(%)" align="center" width="230">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.planRate"
|
||||
:precision="2"
|
||||
:step="1"
|
||||
:min="0.01"
|
||||
:max="100"
|
||||
:readonly="!scope.row.detailId"
|
||||
@change="handleRateChange(scope.row)"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"
|
||||
></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.remark" placeholder="请输入备注"
|
||||
:disabled="!isEditing || isNumberStr(scope.row.detailId)"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="150" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="isEditing && !isNumberStr(scope.row.detailId)"
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-plus"
|
||||
@click="handleAddReceiptPlanRow"
|
||||
>增加下行
|
||||
</el-button>
|
||||
<el-button v-if="isEditing && !isNumberStr(scope.row.detailId)"
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDeleteReceiptPlanRow(scope.$index)"
|
||||
:disabled="receiptPlans.length === 1 || scope.row.status === 'received'"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- <div class="total-info">-->
|
||||
<!-- <span>应收单未收款金额: <el-tag type="info">{{ totalUnreceivedAmount.toFixed(2) }}</el-tag></span>-->
|
||||
<!-- <span style="margin-left: 20px;">计划收款总金额: <el-tag type="success">{{-->
|
||||
<!-- totalPlannedAmount.toFixed(2)-->
|
||||
<!-- }}</el-tag></span>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getReceiptPlan, updateReceiptPlan, syncToInvoicePlan} from "@/api/finance/receivable";
|
||||
import {isNumberStr} from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "ReceiptPlanSelector",
|
||||
props: {
|
||||
receivableData: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
}
|
||||
},
|
||||
isInitEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selectedPlans: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedPlan:[],
|
||||
isEditing: false,
|
||||
loading: false,
|
||||
receiptPlans: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return `选择收款计划 - ${this.receivableData.receivableBillCode}`;
|
||||
},
|
||||
totalPlannedAmount() {
|
||||
return this.receiptPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
},
|
||||
totalUnreceivedAmount() {
|
||||
return this.receivableData.unreceivedAmount || 0;
|
||||
},
|
||||
totalPriceWithTax() {
|
||||
return this.receivableData.totalPriceWithTax || 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'receivableData.id': {
|
||||
handler(newVal, oldVal) {
|
||||
if (newVal) {
|
||||
this.fetchReceiptPlans(newVal)
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
isInitEdit: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.isEditing = newVal
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isNumberStr,
|
||||
selectableRow(row, index){
|
||||
return !row.detailId;
|
||||
},
|
||||
selectPlan( selection){
|
||||
this.selectedPlan=selection
|
||||
},
|
||||
fetchReceiptPlans(receivableId) {
|
||||
if (this.receivableData && receivableId) {
|
||||
getReceiptPlan(receivableId).then(response => {
|
||||
this.receiptPlans = response.data.map(item => ({
|
||||
...item,
|
||||
status: item.status || 'pending'
|
||||
}));
|
||||
if (this.receiptPlans.length === 0) {
|
||||
this.initDefaultReceiptPlan();
|
||||
} else {
|
||||
this.$nextTick(() => {
|
||||
this.receiptPlans.forEach(plan => {
|
||||
const isSelected = this.selectedPlans.some(selected => selected.id === plan.id);
|
||||
if (isSelected) {
|
||||
this.$refs.receiptPlanTable.toggleRowSelection(plan, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.initDefaultReceiptPlan();
|
||||
|
||||
}
|
||||
},
|
||||
initDefaultReceiptPlan() {
|
||||
this.receiptPlans = [{
|
||||
planReceiptDate: null,
|
||||
planAmount: this.totalUnreceivedAmount,
|
||||
planRate: 100,
|
||||
remark: '',
|
||||
status: 'pending'
|
||||
}];
|
||||
},
|
||||
handleSaveReceiptPlan() {
|
||||
if (!this.validateReceiptPlans()) {
|
||||
return;
|
||||
}
|
||||
if (!this.validateReceiptPlanTotals()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.receiptPlans.length; i++) {
|
||||
const plan = this.receiptPlans[i];
|
||||
if (!plan.planReceiptDate) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行收款计划的预计收款时间不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount === '' || plan.planAmount < 0) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行收款计划的预计收款金额不能为空。`);
|
||||
return;
|
||||
}
|
||||
if (plan.planRate === null || plan.planRate === undefined || plan.planRate === '') {
|
||||
this.$modal.msgError(`第 ${i + 1} 行收款计划的应收比例不能为空。`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
updateReceiptPlan(this.receivableData.id, this.receiptPlans).then(() => {
|
||||
this.$modal.msgSuccess("保存成功");
|
||||
this.fetchReceiptPlans(this.receivableData.id);
|
||||
});
|
||||
},
|
||||
handleSyncToInvoicePlan() {
|
||||
this.$modal.confirm('是否确认同步收款计划至开票计划?').then(() => {
|
||||
return syncToInvoicePlan(this.receivableData.id);
|
||||
}).then(() => {
|
||||
this.$emit('syncPlan')
|
||||
this.$modal.msgSuccess("同步成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
handleAddReceiptPlanRow() {
|
||||
this.receiptPlans.push({
|
||||
planReceiptDate: null,
|
||||
planAmount: 0,
|
||||
planRate: 0,
|
||||
remark: '',
|
||||
status: 'pending'
|
||||
});
|
||||
},
|
||||
handleDeleteReceiptPlanRow(index) {
|
||||
if (this.receiptPlans.length === 1) {
|
||||
this.$modal.msgError("至少需要保留一条收款计划。");
|
||||
return;
|
||||
}
|
||||
if (this.receiptPlans[index].status === 'received') {
|
||||
this.$modal.msgError("已收款的计划不能删除。");
|
||||
return;
|
||||
}
|
||||
this.receiptPlans.splice(index, 1);
|
||||
},
|
||||
handleAmountChange(row) {
|
||||
if (this.totalPriceWithTax === 0) {
|
||||
row.planRate = 0;
|
||||
return;
|
||||
}
|
||||
row.planRate = this.$calc.mul((this.$calc.div(row.planAmount, this.totalPriceWithTax,4)), 100);
|
||||
},
|
||||
handleRateChange(row) {
|
||||
row.planAmount = this.$calc.div(this.$calc.mul(this.totalPriceWithTax , row.planRate),100);
|
||||
},
|
||||
validateReceiptPlanTotals() {
|
||||
const totalAmount = this.receiptPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
|
||||
if (totalAmount !== this.totalPriceWithTax) {
|
||||
this.$modal.msgError(`预计收款金额之和应该等于应收总金额[${this.totalPriceWithTax}]`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
validateReceiptPlans() {
|
||||
if (this.receiptPlans.length === 0) {
|
||||
this.$modal.msgError("请至少添加一条收款计划。");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.receiptPlans.length; i++) {
|
||||
const plan = this.receiptPlans[i];
|
||||
if (!plan.planReceiptDate) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行收款计划的预计收款时间不能为空。`);
|
||||
return false;
|
||||
}
|
||||
if (plan.planAmount === null || plan.planAmount === undefined || plan.planAmount <= 0) {
|
||||
this.$modal.msgError(`第 ${i + 1} 行收款计划的预计收款金额必须大于0。`);
|
||||
return false;
|
||||
}
|
||||
if (plan.planRate === null || plan.planRate === undefined || plan.planRate === '') {
|
||||
this.$modal.msgError(`第 ${i + 1} 行收款计划的应收比例不能为空。`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px; /* To prevent scrollbar from overlapping content */
|
||||
}
|
||||
|
||||
.total-info {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,324 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="150px">
|
||||
<!-- <el-form-item label="项目编号" prop="projectCode">-->
|
||||
<!-- <el-input-->
|
||||
<!-- v-model="queryParams.projectCode"-->
|
||||
<!-- placeholder="请输入项目编号"-->
|
||||
<!-- clearable-->
|
||||
<!-- @keyup.enter.native="handleQuery"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="销售-应收单编号" prop="receivableBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.receivableBillCode"
|
||||
placeholder="请输入应收单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="进货商名称" prop="partnerName">
|
||||
<el-input
|
||||
v-model="queryParams.partnerName"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品类型" prop="productType">
|
||||
<el-select v-model="queryParams.productType" placeholder="请选择产品类型" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.product_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="收款状态" prop="collectionStatus">-->
|
||||
<!-- <el-select v-model="queryParams.collectionStatus" placeholder="请选择收款状态" clearable>-->
|
||||
<!-- <el-option-->
|
||||
<!-- v-for="dict in dict.type.collection_status"-->
|
||||
<!-- :key="dict.value"-->
|
||||
<!-- :label="dict.label"-->
|
||||
<!-- :value="dict.value"-->
|
||||
<!-- />-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="开票状态" prop="invoiceStatus">
|
||||
<el-select v-model="queryParams.invoiceStatus" placeholder="请选择开票状态" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.invoice_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="预计收款时间">
|
||||
<el-date-picker
|
||||
v-model="estimatedReceiptDateRange"
|
||||
style="width: 300px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5" >
|
||||
<el-button type="primary" plain @click="handleMergeAndInitiateReceipt" v-hasPermi="['finance:receipt:generate']">
|
||||
合并发起收款单
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5" >
|
||||
<el-button type="primary" plain @click="handleMergeAndInitiateInvoice" v-hasPermi="['finance:invoice:generate']">
|
||||
合并发起开票单
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="receivableList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="50" />
|
||||
<!-- <el-table-column label="项目编号" align="center" prop="projectCode" width="120" />-->
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" width="240" />
|
||||
<el-table-column label="销售-应收单编号" align="center" prop="receivableBillCode" width="150" />
|
||||
<el-table-column label="预计收款时间" align="center" prop="planReceiptDate" width="180"/>
|
||||
<el-table-column label="预计收款金额" align="center" prop="planAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="该进货商是否有预收单" align="center" prop="hasAdvanceReceipt" width="150" >
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.remainingAmount == 0 ? '否' : '是' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="进货商名称" align="center" prop="partnerName" width="150" />
|
||||
<el-table-column label="产品类型" align="center" prop="productType" width="120">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收款金额" align="center" prop="unreceivedAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未开票金额" align="center" prop="uninvoicedAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="300" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['finance:receivable:edit']"
|
||||
>查看详情</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
v-show="scope.row.unreceivedAmount!==0"
|
||||
@click="handleGeneratedReceipt(scope.row)"
|
||||
v-hasPermi="['finance:receipt:generate']"
|
||||
>生成收款单</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
v-show="scope.row.uninvoicedAmount!==0"
|
||||
@click="handleGeneratedInvoice(scope.row)"
|
||||
v-hasPermi="['finance:invoice:generate']"
|
||||
>生成开票单</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 修改弹窗 -->
|
||||
<edit-form :visible.sync="open" :data="selectedRow" />
|
||||
|
||||
<!-- 合并收款单弹窗 -->
|
||||
<merge-receipt-dialog :visible.sync="isMergeReceiptDialogOpen" :receivable-bills="selectedReceivableRows" @confirm="confirmMergeReceipt" />
|
||||
|
||||
<!-- 合并开票单弹窗 -->
|
||||
<merge-invoice-dialog :visible.sync="isMergeInvoiceDialogOpen" :receivable-bills="selectedReceivableRows" @confirm="confirmMergeInvoice" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listReceivable, mergeAndInitiateReceipt, mergeAndInitiateInvoice } from "@/api/finance/receivable";
|
||||
import EditForm from './components/EditForm.vue';
|
||||
import MergeReceiptDialog from './components/MergeReceiptDialog.vue';
|
||||
import MergeInvoiceDialog from './components/MergeInvoiceDialog.vue';
|
||||
|
||||
export default {
|
||||
name: "Receivable",
|
||||
components: { EditForm, MergeReceiptDialog, MergeInvoiceDialog },
|
||||
dicts: ['product_type', 'collection_status', 'invoice_status'],
|
||||
data() {
|
||||
return {
|
||||
// 是否显示修改弹窗
|
||||
open: false,
|
||||
// 选中行数据
|
||||
selectedRow: {},
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 销售应收单表格数据
|
||||
receivableList: [],
|
||||
// 日期范围
|
||||
dateRange: [],
|
||||
// 预计收款日期范围
|
||||
estimatedReceiptDateRange: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectCode: null,
|
||||
projectName: null,
|
||||
receivableBillCode: null,
|
||||
partnerName: null,
|
||||
productType: null,
|
||||
collectionStatus: null,
|
||||
createTimeStart: null,
|
||||
createTimeEnd: null,
|
||||
estimatedReceiptTimeStart: null,
|
||||
estimatedReceiptTimeEnd: null,
|
||||
orderByColumn:'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
// 选中行数据
|
||||
selectedReceivableRows: [],
|
||||
// 是否显示合并收款弹窗
|
||||
isMergeReceiptDialogOpen: false,
|
||||
// 是否显示合并开票弹窗
|
||||
isMergeInvoiceDialogOpen: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询销售应收单列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
let query = { ...this.queryParams };
|
||||
query = this.addDateRange(query, this.estimatedReceiptDateRange, 'PlanReceiptDate');
|
||||
|
||||
listReceivable(query).then(response => {
|
||||
this.receivableList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.estimatedReceiptDateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.queryParams.createTimeStart=null;
|
||||
this.queryParams.createTimeEnd=null;
|
||||
this.queryParams.estimatedReceiptTimeStart=null;
|
||||
this.queryParams.estimatedReceiptTimeEnd=null;
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.selectedRow = row;
|
||||
this.open = true;
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
this.$modal.confirm('是否确认删除销售应收单编号为"' + row.receivableBillCode + '"的数据项?').then(function() {
|
||||
return Promise.resolve();
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 多选框选中数据 */
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedReceivableRows = selection;
|
||||
},
|
||||
handleGeneratedReceipt(row) {
|
||||
this.selectedReceivableRows=[row]
|
||||
this.handleMergeAndInitiateReceipt()
|
||||
},
|
||||
handleGeneratedInvoice(row) {
|
||||
this.selectedReceivableRows=[row]
|
||||
this.handleMergeAndInitiateInvoice()
|
||||
},
|
||||
/** 合并并发起收款单按钮操作 */
|
||||
handleMergeAndInitiateReceipt() {
|
||||
if (this.selectedReceivableRows.length === 0) {
|
||||
this.$modal.msgWarning("请选择至少一条应收单进行合并操作");
|
||||
return;
|
||||
}
|
||||
let customerLength = new Set(this.selectedReceivableRows.map(item=>item.customerCode)).size; // Use customerCode to differentiate
|
||||
// Or check partnerName if code is not available in row, but row usually has it.
|
||||
if (customerLength > 1) {
|
||||
this.$modal.msgWarning("请选择同一家客户的应收单进行合并操作");
|
||||
return;
|
||||
}
|
||||
|
||||
this.isMergeReceiptDialogOpen = true;
|
||||
},
|
||||
/** 确认合并收款单操作 */
|
||||
confirmMergeReceipt(receiptData) {
|
||||
mergeAndInitiateReceipt(receiptData).then(() => {
|
||||
this.$modal.msgSuccess("合并收款单发起成功");
|
||||
this.isMergeReceiptDialogOpen = false;
|
||||
this.getList(); // Refresh the list
|
||||
});
|
||||
},
|
||||
/** 合并并发起开票单按钮操作 */
|
||||
handleMergeAndInitiateInvoice() {
|
||||
if (this.selectedReceivableRows.length === 0) {
|
||||
this.$modal.msgWarning("请选择至少一条应收单进行合并操作");
|
||||
return;
|
||||
}
|
||||
let customerLength = new Set(this.selectedReceivableRows.map(item=>item.customerCode)).size;
|
||||
if (customerLength > 1) {
|
||||
this.$modal.msgWarning("请选择同一家客户的应收单进行合并操作");
|
||||
return;
|
||||
}
|
||||
|
||||
this.isMergeInvoiceDialogOpen = true;
|
||||
},
|
||||
/** 确认合并开票单操作 */
|
||||
confirmMergeInvoice(invoiceData) {
|
||||
mergeAndInitiateInvoice(invoiceData).then(() => {
|
||||
this.$modal.msgSuccess("合并开票单发起成功");
|
||||
this.isMergeInvoiceDialogOpen = false;
|
||||
this.getList(); // Refresh the list
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,489 @@
|
|||
<template>
|
||||
<el-dialog title="新增收款单" :visible.sync="internalVisible" width="1200px" @close="handleClose"
|
||||
:close-on-click-modal="false" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="进货商名称" prop="partnerName">
|
||||
<el-input v-model="form.partnerName" placeholder="请选择进货商" readonly
|
||||
@click.native="showPartnerSelector = true">
|
||||
<el-button slot="append" icon="el-icon-search" @click="showPartnerSelector = true"></el-button>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="receiveBillType">
|
||||
<el-checkbox v-model="form.receiveBillType" true-label="PRE_RECEIPT" false-label="FROM_RECEIVABLE">预付
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="24">-->
|
||||
<!-- <el-form-item label="预计收款时间" prop="receiptTime">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- v-model="form.receiptTime"-->
|
||||
<!-- type="date"-->
|
||||
<!-- value-format="yyyy-MM-dd HH:mm:ss"-->
|
||||
<!-- placeholder="选择日期"-->
|
||||
<!-- ></el-date-picker>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
|
||||
<el-row v-if="form.receiveBillType === 'PRE_RECEIPT'">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预收金额" prop="totalPriceWithTax">
|
||||
<el-input-number v-model="form.totalPriceWithTax" :precision="2" :step="100"
|
||||
style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- Tables -->
|
||||
<div>
|
||||
<div v-if="form.receiveBillType === 'FROM_RECEIVABLE' && form.partnerCode" class="table-container">
|
||||
<h4>应收单列表</h4>
|
||||
<el-table
|
||||
ref="receivableTable"
|
||||
:data="receivableList"
|
||||
border
|
||||
v-loading="receivableLoading"
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
max-height="400"
|
||||
row-key="id"
|
||||
>
|
||||
<el-table-column type="selection" width="55" reserve-selection></el-table-column>
|
||||
<el-table-column label="销售-应收单编号" align="center" prop="receivableBillCode" width="150"/>
|
||||
<el-table-column label="预计收款时间" align="center" prop="planReceiptDate" width="180"/>
|
||||
<!-- <el-table-column label="收款计划" align="center" width="100" prop="planAmount">-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="预期收款金额">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency(calculateOrderCurrentReceiptAmount(scope.row).toFixed(2)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预期收款比例" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ calculateOrderCurrentReceiptRate(scope.row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" width="150"/>
|
||||
<el-table-column label="进货商名称" align="center" prop="partnerName" width="150"/>
|
||||
<!-- <el-table-column label="出入库单号" align="center" prop="inventoryCode" width="150"/>-->
|
||||
<!-- <el-table-column label="收款状态" align="center" prop="collectionStatus" width="120">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.collection_status" :value="scope.row.collectionStatus"/>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收款金额(元)" align="center" prop="unreceivedAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已收款金额(元)" align="center" prop="receivedAmount" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收款中金额(元)" align="center" prop="receivedAmount" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency($calc.sub($calc.sub(scope.row.totalPriceWithTax, scope.row.receivedAmount), scope.row.unreceivedAmount)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100"
|
||||
fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleOpenReceiptPlanSelector(scope.row, scope.$index)"
|
||||
>选择
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="total-info">
|
||||
|
||||
<span style="margin-left: 20px;">计划收款总金额: <el-tag type="success">{{
|
||||
formatCurrency(totalPlannedAmount.toFixed(2))
|
||||
}}</el-tag></span>
|
||||
<span>计划收款比例: <el-tag type="info">{{ totalSelectedReceivableAmount ? this.$calc.mul(this.$calc.div(totalPlannedAmount,totalSelectedReceivableAmount,4),100) : 0 }}%</el-tag></span>
|
||||
</div>
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="loadTableData"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="form.receiveBillType === 'PRE_RECEIPT' && form.partnerCode" class="table-container">
|
||||
<h4>订单列表</h4>
|
||||
<el-table
|
||||
ref="orderTable"
|
||||
:data="orderList"
|
||||
border
|
||||
v-loading="orderLoading"
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
@select="handleSelect"
|
||||
@select-all="handleSelectAll"
|
||||
max-height="400"
|
||||
row-key="id"
|
||||
>
|
||||
<el-table-column type="selection" width="55" reserve-selection></el-table-column>
|
||||
<el-table-column prop="projectCode" label="项目编号"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目名称"></el-table-column>
|
||||
<el-table-column prop="createTime" label="下单时间"></el-table-column>
|
||||
<el-table-column label="订单状态" prop="orderStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.order_status" :value="scope.row.orderStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="loadTableData"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="!form.partnerCode" style="text-align: center; color: #909399; padding: 20px;">
|
||||
请先选择进货商
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
</div>
|
||||
|
||||
<!-- Receipt Plan Selector Dialog -->
|
||||
<el-dialog :title="planTitle" :visible.sync="isReceiptPlanSelectorOpen" width="70%"
|
||||
@close="isReceiptPlanSelectorOpen=false" append-to-body>
|
||||
<receipt-plan
|
||||
ref="planSelector"
|
||||
:receivable-data="chooseReceivable"
|
||||
:selected-plans="chooseReceivable.receiptPlans"
|
||||
/>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="isReceiptPlanSelectorOpen=false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleChooseConfirm">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<select-partner :visible.sync="showPartnerSelector" @partner-selected="handlePartnerSelected" />
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {listReceivable} from "@/api/finance/receivable";
|
||||
import {listOrder} from "@/api/project/order";
|
||||
import ReceiptPlan from "@/views/finance/receivable/components/ReceiptPlan.vue";
|
||||
import SelectPartner from "@/views/system/partner/selectPartner.vue";
|
||||
|
||||
export default {
|
||||
name: "AddForm",
|
||||
components: {ReceiptPlan, SelectPartner},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
dicts: ['order_status', 'receive_status', 'collection_status'],
|
||||
data() {
|
||||
return {
|
||||
internalVisible: this.visible,
|
||||
receivableList: [], // List for Standard/Receivable Bills
|
||||
orderList: [], // List for Pre-receipt/Sales Orders
|
||||
selectedRows: [],
|
||||
total: 0,
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
},
|
||||
form: {
|
||||
receiveBillType: 'FROM_RECEIVABLE',
|
||||
partnerName: null,
|
||||
partnerCode: null,
|
||||
remark: null,
|
||||
totalPriceWithTax: 0,
|
||||
actualReceiveTime: null
|
||||
},
|
||||
showPartnerSelector: false,
|
||||
receivableLoading: false,
|
||||
orderLoading: false,
|
||||
rules: {
|
||||
partnerName: [{required: true, message: "代理商不能为空", trigger: "change"}],
|
||||
actualReceiveTime: [{required: true, message: "实际收款时间不能为空", trigger: "change"}],
|
||||
receiveBillType: [{required: true, message: "请选择是否预收", trigger: "change"}],
|
||||
totalPriceWithTax: [{required: false, message: "预收金额不能为空", trigger: "blur"}]
|
||||
},
|
||||
// Plan Selector Data
|
||||
planTitle: '',
|
||||
isReceiptPlanSelectorOpen: false,
|
||||
chooseReceivable: {},
|
||||
currentReceivableOrderIndexForPlan: -1,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
totalPlannedAmount() {
|
||||
if (this.form.receiveBillType === 'FROM_RECEIVABLE') {
|
||||
// Calculate based on selected rows and their plans/defaults
|
||||
return this.selectedRows.reduce((sum, row) => {
|
||||
return sum + this.calculateOrderCurrentReceiptAmount(row);
|
||||
}, 0);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
totalSelectedReceivableAmount() {
|
||||
if (this.form.receiveBillType === 'FROM_RECEIVABLE') {
|
||||
return this.selectedRows.reduce((sum, row) => {
|
||||
return sum + (row.totalPriceWithTax || 0);
|
||||
}, 0);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
},
|
||||
watch: {
|
||||
visible(newVal) {
|
||||
this.internalVisible = newVal;
|
||||
if (newVal) {
|
||||
this.resetForm();
|
||||
}
|
||||
},
|
||||
internalVisible(newVal) {
|
||||
this.$emit("update:visible", newVal);
|
||||
},
|
||||
'form.receiveBillType': function (val) {
|
||||
// Toggle validation for Pre-receipt Amount
|
||||
if (val === 'PRE_RECEIPT') {
|
||||
this.rules.totalPriceWithTax[0].required = true;
|
||||
} else {
|
||||
this.rules.totalPriceWithTax[0].required = false;
|
||||
}
|
||||
this.queryParams.pageNum = 1;
|
||||
this.selectedRows = [];
|
||||
this.loadTableData();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handlePartnerSelected(partner) {
|
||||
this.form.partnerName = partner.partnerName;
|
||||
this.form.partnerCode = partner.partnerCode;
|
||||
this.queryParams.pageNum = 1;
|
||||
this.selectedRows = [];
|
||||
this.loadTableData();
|
||||
},
|
||||
loadTableData() {
|
||||
this.receivableList = [];
|
||||
this.orderList = [];
|
||||
|
||||
const query = {
|
||||
pageNum: this.queryParams.pageNum,
|
||||
pageSize: this.queryParams.pageSize
|
||||
};
|
||||
|
||||
if (this.form.receiveBillType === 'FROM_RECEIVABLE' && this.form.partnerCode) {
|
||||
query.partnerCode = this.form.partnerCode;
|
||||
this.receivableLoading = true
|
||||
listReceivable(query).then(res => {
|
||||
this.receivableList = (res.rows || []).map(item => {
|
||||
const receiptPlans = item.receiptPlans ? [...item.receiptPlans] : [];
|
||||
// If needed, add logic to initialize default plan similar to payment
|
||||
if (receiptPlans.length === 0 && item.lastReceiptPlanId) {
|
||||
receiptPlans.push({
|
||||
id: item.lastReceiptPlanId,
|
||||
planAmount: item.planAmount,
|
||||
planReceiptDate: item.planReceiptDate,
|
||||
planRate: this.$calc.mul(this.$calc.div(item.planAmount, item.totalPriceWithTax, 4), 100)
|
||||
});
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
receiptPlans: receiptPlans,
|
||||
}
|
||||
});
|
||||
this.total = res.total;
|
||||
this.receivableLoading = false
|
||||
});
|
||||
} else if (this.form.receiveBillType === 'PRE_RECEIPT' && this.form.partnerCode) {
|
||||
// Filter Sales Orders for the agent (partner)
|
||||
query.partnerCode = this.form.partnerCode;
|
||||
query.orderStatus = '2'; // Example status
|
||||
this.orderLoading=true
|
||||
listOrder(query).then(res => {
|
||||
this.orderList = res.rows || [];
|
||||
this.total = res.total;
|
||||
this.orderLoading=false
|
||||
});
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.receivableTable) {
|
||||
this.$refs.receivableTable.clearSelection()
|
||||
}
|
||||
if (this.$refs.orderTable) {
|
||||
this.$refs.orderTable.clearSelection()
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSelect(selection, row) {
|
||||
if (this.form.receiveBillType === 'PRE_RECEIPT') {
|
||||
this.$refs.orderTable.clearSelection();
|
||||
const isSelected = selection.some(item => item.id === row.id); // Use ID for uniqueness
|
||||
if (isSelected) {
|
||||
this.$refs.orderTable.toggleRowSelection(row, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
handleSelectAll(selection) {
|
||||
if (this.form.receiveBillType === 'PRE_RECEIPT') {
|
||||
this.$refs.orderTable.clearSelection();
|
||||
this.$modal.msgWarning("预收单只能选择一个订单");
|
||||
}
|
||||
},
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedRows = selection;
|
||||
},
|
||||
// --- Receipt Plan Logic ---
|
||||
handleOpenReceiptPlanSelector(row, index) {
|
||||
this.planTitle = `选择收款计划 - ${row.receivableBillCode}`;
|
||||
this.chooseReceivable = row;
|
||||
this.currentReceivableOrderIndexForPlan = index;
|
||||
this.isReceiptPlanSelectorOpen = true;
|
||||
},
|
||||
handleChooseConfirm() {
|
||||
if (!this.$refs.planSelector) {
|
||||
this.$modal.msgError('无法获取计划选择器组件');
|
||||
return;
|
||||
}
|
||||
const selectedPlans = this.$refs.planSelector.selectedPlan || [];
|
||||
|
||||
// Update the plans for the specific order
|
||||
if (this.currentReceivableOrderIndexForPlan !== -1) {
|
||||
const row = this.receivableList[this.currentReceivableOrderIndexForPlan];
|
||||
this.$set(row, 'receiptPlans', [...selectedPlans]);
|
||||
}
|
||||
this.isReceiptPlanSelectorOpen = false;
|
||||
this.$modal.msgSuccess(`已更新收款计划选择,共 ${selectedPlans.length} 条`);
|
||||
},
|
||||
calculateOrderCurrentReceiptAmount(order) {
|
||||
if (order && order.receiptPlans && order.receiptPlans.length > 0) {
|
||||
return order.receiptPlans.reduce((sum, plan) => sum + (plan.planAmount || 0), 0);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
calculateOrderCurrentReceiptRate(order) {
|
||||
if (order && order.receiptPlans && order.receiptPlans.length > 0 && order.totalPriceWithTax) {
|
||||
const currentAmount = this.calculateOrderCurrentReceiptAmount(order);
|
||||
return this.$calc.mul((this.$calc.div(currentAmount, order.totalPriceWithTax, 4)), 100);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
handleClose() {
|
||||
this.internalVisible = false;
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
if (this.form.receiveBillType === 'FROM_RECEIVABLE') {
|
||||
if (this.selectedRows.length === 0) {
|
||||
this.$message.warning("请选择至少一条应收单");
|
||||
return;
|
||||
}
|
||||
|
||||
// Process selected rows
|
||||
const processedReceivableOrders = this.selectedRows.map(order => {
|
||||
let finalPlans = order.receiptPlans;
|
||||
// Add validation if needed: check if plans exist
|
||||
if (!finalPlans || finalPlans.length === 0) {
|
||||
// warning handled in previous logic, maybe add here
|
||||
}
|
||||
|
||||
return {
|
||||
id: order.id,
|
||||
receivableBillCode: order.receivableBillCode,
|
||||
taxRate: order.taxRate,
|
||||
// Map plans
|
||||
receiptPlans: (finalPlans || []).map(plan => ({
|
||||
id: plan.id,
|
||||
planReceiptDate: plan.planReceiptDate,
|
||||
planAmount: plan.planAmount,
|
||||
planRate: plan.planRate,
|
||||
remark: plan.remark
|
||||
}))
|
||||
};
|
||||
});
|
||||
// Ensure every selected order has plans?
|
||||
if (processedReceivableOrders.some(o => o.receiptPlans.length === 0)) {
|
||||
this.$message.warning("选中的应收单必须包含收款计划");
|
||||
return;
|
||||
}
|
||||
|
||||
const submitData = {
|
||||
receiptBillType: 'FROM_RECEIVABLE',
|
||||
receiptTime: this.form.receiptTime,
|
||||
receivableBills: processedReceivableOrders,
|
||||
totalMergeReceiptAmount: this.totalPlannedAmount
|
||||
};
|
||||
this.$emit("submit", submitData);
|
||||
|
||||
} else {
|
||||
if ((this.form.totalPriceWithTax || 0) <= 0) {
|
||||
this.$message.warning("预收金额需要大于0");
|
||||
return;
|
||||
}
|
||||
let order = this.selectedRows[0] ?? {};
|
||||
const submitData = {
|
||||
receiptBillType: 'PRE_RECEIPT',
|
||||
receiptTime: this.form.receiptTime,
|
||||
orderCode: order.orderCode, // Ensure orderCode is available in orderList items
|
||||
partnerCode: this.form.partnerCode,
|
||||
partnerName: this.form.partnerName,
|
||||
projectCode: order.projectCode,
|
||||
projectName: order.projectName,
|
||||
totalPriceWithTax: this.form.totalPriceWithTax
|
||||
};
|
||||
console.log(submitData)
|
||||
this.$emit("submit", submitData);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
resetForm() {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.resetFields();
|
||||
}
|
||||
this.form = {
|
||||
receiveBillType: 'FROM_RECEIVABLE',
|
||||
partnerName: null,
|
||||
partnerCode: null,
|
||||
remark: null,
|
||||
totalPriceWithTax: 0,
|
||||
actualReceiveTime: null
|
||||
};
|
||||
this.receivableList = [];
|
||||
this.orderList = [];
|
||||
this.selectedRows = [];
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
};
|
||||
this.total = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.table-container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,328 @@
|
|||
<template>
|
||||
<el-dialog title="申请付款" :visible.sync="visible" width="1000px" append-to-body :before-close="handleClose"
|
||||
custom-class="apply-payment-dialog">
|
||||
<el-row :gutter="20">
|
||||
<!-- Left Side: Form Data -->
|
||||
<el-col :span="12">
|
||||
<div class="form-tip">请选择客户的支付方式并确认客户打款的账户信息,提交至财务审批</div>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px" size="small">
|
||||
<el-form-item label="支付方式" prop="receiptMethod">
|
||||
<el-select v-model="form.receiptMethod" placeholder="请选择支付方式" style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in dicts.payment_method"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="账户名称" prop="receiptAccountName">
|
||||
<el-input v-model="form.receiptAccountName" placeholder="请输入账户名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="银行账号" prop="receiptBankNumber">
|
||||
<el-input v-model="form.receiptBankNumber" placeholder="请输入银行账号"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="银行开户行" prop="receiptBankOpenAddress">
|
||||
<el-input v-model="form.receiptBankOpenAddress" placeholder="请输入银行开户行"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="银行行号" prop="bankNumber">
|
||||
<el-input v-model="form.bankNumber" placeholder="请输入银行行号"/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- New Field: Client Payment Image Upload -->
|
||||
<el-form-item label="客户付款图" prop="file">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:on-remove="handleFileRemove"
|
||||
:show-file-list="false"
|
||||
accept=".jpg,.jpeg,.png,.pdf"
|
||||
>
|
||||
<el-button size="mini" type="primary" icon="el-icon-upload2">{{
|
||||
form.file ? '重新上传' : '点击上传'
|
||||
}}
|
||||
</el-button>
|
||||
<div slot="tip" class="el-upload__tip">支持JPG/PNG/PDF格式</div>
|
||||
</el-upload>
|
||||
<div v-if="form.file" class="file-name-tip">
|
||||
<i class="el-icon-document"></i> {{ form.fileName }}
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="收款金额" prop="totalPriceWithTax">
|
||||
<el-input v-model="form.totalPriceWithTax" :disabled="true"/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- New Field: Confirm Receipt Amount -->
|
||||
<el-form-item label="确认收款金额" prop="confirmAmount">
|
||||
<el-input v-model="form.confirmAmount" placeholder="请输入确认收款金额"/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- New Field: Remarks -->
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input type="textarea" v-model="form.remark" :rows="3" placeholder="请输入备注"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
|
||||
<!-- Right Side: Preview -->
|
||||
<el-col :span="12">
|
||||
<div class="preview-container">
|
||||
<div v-if="previewUrl" class="preview-content">
|
||||
<div v-if="isPreviewPdf" class="pdf-preview">
|
||||
<iframe :src="previewUrl" width="100%" height="100%" frameborder="0"></iframe>
|
||||
</div>
|
||||
<div v-else class="image-preview">
|
||||
<img :src="previewUrl" alt="预览图片"/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="preview-placeholder">
|
||||
<div class="placeholder-icon">
|
||||
<i class="el-icon-picture-outline"></i>
|
||||
</div>
|
||||
<div class="placeholder-text">上传文件后在此处预览</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {applyReceipt} from "@/api/finance/receive";
|
||||
|
||||
export default {
|
||||
name: "ApplyPaymentDialog",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
receiptData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
dicts: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rules: {
|
||||
receiptMethod: [
|
||||
{ required: true, message: "请选择支付方式", trigger: "change" }
|
||||
],
|
||||
receiptAccountName: [
|
||||
{ required: true, message: "请输入账户名称", trigger: "blur" }
|
||||
],
|
||||
receiptBankNumber: [
|
||||
{ required: true, message: "请输入银行账号", trigger: "blur" }
|
||||
],
|
||||
receiptBankOpenAddress: [
|
||||
{ required: true, message: "请输入银行开户行", trigger: "blur" }
|
||||
],
|
||||
bankNumber: [
|
||||
{ required: true, message: "请输入银行行号", trigger: "blur" }
|
||||
],
|
||||
file: [
|
||||
{ required: true, message: "请上传客户付款图", trigger: "change" }
|
||||
],
|
||||
confirmAmount: [
|
||||
{ required: true, message: "请输入确认收款金额", trigger: "blur" }
|
||||
]
|
||||
},
|
||||
form: {
|
||||
receiptMethod: null,
|
||||
receiptAccountName: null,
|
||||
receiptBankNumber: null,
|
||||
receiptBankOpenAddress: null,
|
||||
bankNumber: null,
|
||||
totalPriceWithTax: null,
|
||||
confirmAmount: null,
|
||||
remark: null,
|
||||
file: null,
|
||||
fileName: '',
|
||||
id: this.receiptData.id
|
||||
},
|
||||
previewUrl: '',
|
||||
isPreviewPdf: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val) {
|
||||
this.reset();
|
||||
// Initialize form with receiptData
|
||||
this.form = {
|
||||
id: this.receiptData.id,
|
||||
receiptMethod: this.receiptData.receiptMethod,
|
||||
receiptAccountName: this.receiptData.receiptAccountName,
|
||||
receiptBankNumber: this.receiptData.receiptBankNumber,
|
||||
receiptBankOpenAddress: this.receiptData.receiptBankOpenAddress,
|
||||
bankNumber: this.receiptData.bankNumber,
|
||||
totalPriceWithTax: this.receiptData.totalPriceWithTax,
|
||||
confirmAmount: null,
|
||||
remark: null,
|
||||
file: null,
|
||||
fileName: ''
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reset() {
|
||||
this.form = {
|
||||
receiptMethod: null,
|
||||
receiptAccountName: null,
|
||||
receiptBankNumber: null,
|
||||
receiptBankOpenAddress: null,
|
||||
bankNumber: null,
|
||||
totalPriceWithTax: null,
|
||||
confirmAmount: null,
|
||||
remark: null,
|
||||
file: null,
|
||||
fileName: ''
|
||||
};
|
||||
this.previewUrl = '';
|
||||
this.isPreviewPdf = false;
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.resetFields();
|
||||
}
|
||||
},
|
||||
handleClose() {
|
||||
this.$emit("update:visible", false);
|
||||
},
|
||||
handleFileChange(file) {
|
||||
const isLt10M = file.size / 1024 / 1024 < 10;
|
||||
const isAcceptedType = ['image/jpeg', 'image/png', 'application/pdf'].includes(file.raw.type);
|
||||
|
||||
if (!isAcceptedType) {
|
||||
this.$modal.msgError('上传文件只能是 JPG/PNG/PDF 格式!');
|
||||
// Remove file from upload list if needed, though we use show-file-list="false"
|
||||
return;
|
||||
}
|
||||
if (!isLt10M) {
|
||||
this.$modal.msgError('上传文件大小不能超过 10MB!');
|
||||
return;
|
||||
}
|
||||
|
||||
this.form.file = file.raw;
|
||||
this.form.fileName = file.name;
|
||||
this.isPreviewPdf = file.raw.type === 'application/pdf';
|
||||
this.previewUrl = URL.createObjectURL(file.raw);
|
||||
this.$refs.form.validateField('file');
|
||||
},
|
||||
handleFileRemove() {
|
||||
this.form.file = null;
|
||||
this.form.fileName = '';
|
||||
this.previewUrl = '';
|
||||
this.$refs.form.validateField('file');
|
||||
},
|
||||
handleSubmit() {
|
||||
if (this.$calc.sub(this.form.totalPriceWithTax,this.form.confirmAmount)!=0){
|
||||
this.$modal.msgError("确认收款金额与收款金额需相同");
|
||||
return
|
||||
}
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
// Construct FormData
|
||||
const formData = new FormData();
|
||||
// Append regular fields
|
||||
Object.keys(this.form).forEach(key => {
|
||||
if (key !== 'file' && key !== 'fileName' && this.form[key] !== null && this.form[key] !== undefined) {
|
||||
formData.append(key, this.form[key]);
|
||||
}
|
||||
});
|
||||
// Append file if exists
|
||||
if (this.form.file) {
|
||||
formData.append("file", this.form.file);
|
||||
}
|
||||
|
||||
|
||||
// Since applyPaymentApi usually takes JSON, we might need to verify if backend supports FormData
|
||||
// Assuming we are sending FormData now.
|
||||
applyReceipt(formData).then(response => {
|
||||
this.$modal.msgSuccess("申请付款提交成功");
|
||||
this.$emit("submit");
|
||||
this.handleClose();
|
||||
}).catch(error => {
|
||||
console.error("申请付款提交失败", error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-tip {
|
||||
color: #1890ff;
|
||||
font-size: 14px;
|
||||
margin-bottom: 20px;
|
||||
padding: 8px 16px;
|
||||
background-color: #e6f7ff;
|
||||
border: 1px solid #91d5ff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
background-color: #f5f7fa;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.preview-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.image-preview img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.pdf-preview {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.preview-placeholder {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.placeholder-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.placeholder-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.file-name-tip {
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
<template>
|
||||
<el-dialog title="申请退款" :visible.sync="visible" width="600px" append-to-body :before-close="handleClose"
|
||||
custom-class="apply-refund-dialog">
|
||||
<div class="form-tip">请选择客户的退款方式并确认客户收款的账户信息,提交至财务审批</div>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px" size="small">
|
||||
<el-form-item label="退款方式" prop="receiptMethod">
|
||||
<el-select v-model="form.receiptMethod" placeholder="请选择退款方式" style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in dicts.payment_method"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="账户名称" prop="receiptAccountName">
|
||||
<el-input v-model="form.receiptAccountName" placeholder="请输入账户名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="银行账号" prop="receiptBankNumber">
|
||||
<el-input v-model="form.receiptBankNumber" placeholder="请输入银行账号"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="银行开户行" prop="receiptBankOpenAddress">
|
||||
<el-input v-model="form.receiptBankOpenAddress" placeholder="请输入银行开户行"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="银行行号" prop="bankNumber">
|
||||
<el-input v-model="form.bankNumber" placeholder="请输入银行行号"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="含税金额" prop="totalPriceWithTax">
|
||||
<el-input v-model="form.totalPriceWithTax" :disabled="true"/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- Field: Confirm Refund Amount -->
|
||||
<el-form-item label="确认退款金额" prop="confirmAmount">
|
||||
<el-input v-model="form.confirmAmount" placeholder="请输入确认退款金额"/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- Field: Remarks -->
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input type="textarea" v-model="form.remark" :rows="3" placeholder="请输入备注"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {submitRefund} from "@/api/finance/receive";
|
||||
|
||||
export default {
|
||||
name: "ApplyRefundDialog",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
receiptData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
dicts: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
receiptMethod: null,
|
||||
receiptAccountName: null,
|
||||
receiptBankNumber: null,
|
||||
receiptBankOpenAddress: null,
|
||||
bankNumber: null,
|
||||
totalPriceWithTax: null,
|
||||
confirmAmount: null,
|
||||
remark: null,
|
||||
id: this.receiptData.id
|
||||
},
|
||||
rules: {
|
||||
receiptMethod: [
|
||||
{ required: true, message: "请选择退款方式", trigger: "change" }
|
||||
],
|
||||
receiptAccountName: [
|
||||
{ required: true, message: "请输入账户名称", trigger: "blur" }
|
||||
],
|
||||
receiptBankNumber: [
|
||||
{ required: true, message: "请输入银行账号", trigger: "blur" }
|
||||
],
|
||||
receiptBankOpenAddress: [
|
||||
{ required: true, message: "请输入银行开户行", trigger: "blur" }
|
||||
],
|
||||
bankNumber: [
|
||||
{ required: true, message: "请输入银行行号", trigger: "blur" }
|
||||
],
|
||||
confirmAmount: [
|
||||
{ required: true, message: "请输入确认退款金额", trigger: "blur" }
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val) {
|
||||
this.reset();
|
||||
// Initialize form with receiptData
|
||||
this.form = {
|
||||
id: this.receiptData.id,
|
||||
receiptMethod: this.receiptData.receiptMethod,
|
||||
receiptAccountName: this.receiptData.receiptAccountName,
|
||||
receiptBankNumber: this.receiptData.receiptBankNumber,
|
||||
receiptBankOpenAddress: this.receiptData.receiptBankOpenAddress,
|
||||
bankNumber: this.receiptData.bankNumber,
|
||||
totalPriceWithTax: this.receiptData.totalPriceWithTax,
|
||||
confirmAmount: null,
|
||||
remark: null
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reset() {
|
||||
this.form = {
|
||||
receiptMethod: null,
|
||||
receiptAccountName: null,
|
||||
receiptBankNumber: null,
|
||||
receiptBankOpenAddress: null,
|
||||
bankNumber: null,
|
||||
totalPriceWithTax: null,
|
||||
confirmAmount: null,
|
||||
remark: null
|
||||
};
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.resetFields();
|
||||
}
|
||||
},
|
||||
handleClose() {
|
||||
this.$emit("update:visible", false);
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
const checkFields = ['receiptMethod', 'receiptAccountName', 'receiptBankNumber', 'receiptBankOpenAddress', 'bankNumber'];
|
||||
let isDiff = false;
|
||||
let diffMsg = [];
|
||||
|
||||
checkFields.forEach(field => {
|
||||
// Use loose equality to handle null vs undefined or number vs string issues if necessary,
|
||||
// but stricter is better if types are consistent.
|
||||
// Given Vue forms often use strings, and receiptData might be from API,
|
||||
// we'll try to match somewhat loosely or ensure strings.
|
||||
const formVal = this.form[field];
|
||||
const originVal = this.receiptData[field];
|
||||
|
||||
// Simple comparison
|
||||
if (formVal != originVal) {
|
||||
isDiff = true;
|
||||
// Get label for friendly message
|
||||
let label = "";
|
||||
switch(field) {
|
||||
case 'receiptMethod': label = "退款方式"; break;
|
||||
case 'receiptAccountName': label = "账户名称"; break;
|
||||
case 'receiptBankNumber': label = "银行账号"; break;
|
||||
case 'receiptBankOpenAddress': label = "银行开户行"; break;
|
||||
case 'bankNumber': label = "银行行号"; break;
|
||||
}
|
||||
diffMsg.push(label);
|
||||
}
|
||||
});
|
||||
|
||||
const doSubmit = () => {
|
||||
submitRefund(this.form).then(response => {
|
||||
this.$modal.msgSuccess("申请退款提交成功");
|
||||
this.$emit("submit");
|
||||
this.handleClose();
|
||||
}).catch(error => {
|
||||
console.error("申请退款提交失败", error);
|
||||
});
|
||||
};
|
||||
|
||||
if (isDiff) {
|
||||
this.$modal.confirm(`检测到以下信息与原收款信息不一致:${diffMsg.join('、')}。确认要提交吗?`).then(() => {
|
||||
doSubmit();
|
||||
}).catch(() => {});
|
||||
} else {
|
||||
doSubmit();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-tip {
|
||||
color: #1890ff;
|
||||
font-size: 14px;
|
||||
margin-bottom: 20px;
|
||||
padding: 8px 16px;
|
||||
background-color: #e6f7ff;
|
||||
border: 1px solid #91d5ff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
<template>
|
||||
<el-drawer
|
||||
:title="detail.receiptBillType==='REFUND'?'退款单详情':'收款单详情'"
|
||||
:visible.sync="visible"
|
||||
direction="rtl"
|
||||
size="70%"
|
||||
@close="handleClose"
|
||||
>
|
||||
<div class="dialog-body" v-if="detail">
|
||||
<div class="section">
|
||||
<el-divider content-position="left">销售-收款单</el-divider>
|
||||
<div class="details-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">销售-收款单编号: {{ detail.receiptBillCode }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">预计收款时间: {{ detail.receiptTime }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">进货商名称: {{ detail.partnerName }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">含税总价(元):<span :class="{'red-text':detail.receiptBillType==='REFUND'}"> {{ formatCurrency(detail.totalPriceWithTax) }} </span></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">未税总价(元): <span :class="{'red-text':detail.receiptBillType==='REFUND'}">{{ formatCurrency(detail.totalPriceWithoutTax) }} </span></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">税额(元): <span :class="{'red-text':detail.receiptBillType==='REFUND'}">{{ formatCurrency(detail.taxAmount) }} </span></div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">备注:
|
||||
<dict-tag :options="dict.type.receipt_bill_type" :value="detail.receiptBillType"/></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">预收单剩余额度:{{formatCurrency(detail.remainAmount)}}
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">实际收款时间: {{ detail.actualReceiptTime }}</div>
|
||||
</el-col>
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <div class="detail-item">收款图/回执单:-->
|
||||
<!-- <span v-if="detail.attachment">-->
|
||||
<!-- <el-button type="text" size="mini" icon="el-icon-view" @click="handlePreview(detail.attachment)">预览</el-button>-->
|
||||
<!-- <el-button type="text" size="mini" icon="el-icon-download"-->
|
||||
<!-- @click="downloadFile(detail.attachment)">下载</el-button>-->
|
||||
<!-- </span>-->
|
||||
<!-- <span v-else>-</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-col>-->
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">支付方式: <dict-tag :options="dict.type.payment_method" :value="detail.receiptMethod"/></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">收款状态: <dict-tag :options="dict.type.receipt_bill_status" :value="detail.receiptStatus"/></div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">审批节点: {{ detail.approveNode|| '-' }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">审批状态:
|
||||
<dict-tag :options="dict.type.approve_status" :value="detail.approveStatus"/>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">审批通过时间: {{ detail.approveTime || '-'}}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
|
||||
<div class="detail-item">账户名称: {{ detail.receiptAccountName }}</div>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">银行账号: {{ detail.receiptBankNumber|| '-' }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">银行开户行:{{detail.receiptBankOpenAddress}}
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="detail-item">银行行号: {{ detail.bankNumber }}</div>
|
||||
</el-col>
|
||||
|
||||
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<el-divider content-position="left">销售-应收单</el-divider>
|
||||
<el-table :data="detail.detailDTOList">
|
||||
<el-table-column type="index" label="序号" width="50"></el-table-column>
|
||||
<el-table-column property="projectCode" label="项目编号"></el-table-column>
|
||||
<el-table-column property="projectName" label="项目名称"></el-table-column>
|
||||
<el-table-column property="receivableBillCode" label="应收单编号"></el-table-column>
|
||||
<el-table-column property="totalPriceWithTax" label="含税总价(元)" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column property="receiptAmount" label="本次收款金额">
|
||||
<template slot-scope="scope">
|
||||
<span >
|
||||
{{ formatCurrency(Math.abs(scope.row.receiptAmount)) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="receiptRate" label="本次收款比例(%)">
|
||||
<template slot-scope="scope">
|
||||
<span >
|
||||
{{ Math.abs(scope.row.receiptRate) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<GlobalFilePreview ref="filePreview" />
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GlobalFilePreview from '@/components/GlobalFilePreview';
|
||||
|
||||
export default {
|
||||
name: "DetailDrawer",
|
||||
components: {
|
||||
GlobalFilePreview
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
dicts:['receipt_bill_type','approve_status','receipt_bill_status','payment_method'],
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.$emit("update:visible", false);
|
||||
},
|
||||
handlePreview(attachment) {
|
||||
this.$refs.filePreview.handlePreview(attachment);
|
||||
},
|
||||
downloadFile(attachment) {
|
||||
this.$refs.filePreview.downloadFile(attachment);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
max-height: calc(100vh - 50px);
|
||||
overflow-y: auto;
|
||||
padding: 0 20px 20px 20px;
|
||||
}
|
||||
|
||||
.details-container {
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.red-text{
|
||||
color: red;
|
||||
}
|
||||
.section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,474 @@
|
|||
<template>
|
||||
<el-dialog :title="titleText" :visible.sync="dialogVisible" width="900px" @close="handleClose">
|
||||
<div v-if="loading" class="loading-spinner">
|
||||
<i class="el-icon-loading"></i>
|
||||
</div>
|
||||
<div v-else class="receipt-dialog-body">
|
||||
<div v-if="canUpload" class="upload-btn-container">
|
||||
<el-button type="primary" icon="el-icon-upload" v-hasPermi="['finance:receive:upload']" @click="openUploadDialog">上传{{ titleText }}</el-button>
|
||||
</div>
|
||||
|
||||
<el-timeline v-if="attachments.length > 0">
|
||||
<el-timeline-item
|
||||
v-for="attachment in attachments"
|
||||
:key="attachment.id"
|
||||
:timestamp="parseTime(attachment.createTime, '{y}-{m}-{d} {h}:{i}:{s}')"
|
||||
placement="top"
|
||||
>
|
||||
<el-card>
|
||||
<div class="receipt-card-content">
|
||||
<div class="receipt-details">
|
||||
<div class="detail-item">
|
||||
<span class="item-label">付款方式</span>
|
||||
<span class="item-value"><dict-tag :options="dict.type.payment_method" :value="receiptData.receiptMethod"/></span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">{{ titleText }}</span>
|
||||
<div class="item-value">
|
||||
<div class="image-wrapper">
|
||||
<el-image
|
||||
v-if="!isPdf(attachment.filePath)"
|
||||
:src="getImageUrl(attachment.filePath)"
|
||||
:preview-src-list="previewList"
|
||||
style="width: 200px; height: 150px;"
|
||||
fit="contain"
|
||||
></el-image>
|
||||
<div v-else-if="pdfUrls[attachment.filePath]" class="pdf-thumbnail-container" @click="openPdfPreview(pdfUrls[attachment.filePath])">
|
||||
<iframe :src="pdfUrls[attachment.filePath]" width="100%" height="150px" frameborder="0"></iframe>
|
||||
<div class="pdf-hover-overlay">
|
||||
<i class="el-icon-zoom-in"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="attachment.delFlag === '2'" class="void-overlay">作废</div>
|
||||
</div>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="primary"
|
||||
class="download-btn"
|
||||
icon="el-icon-download"
|
||||
@click="downloadFile(attachment)"
|
||||
>下载{{ titleText }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-item">
|
||||
<span class="item-label">收款总额</span>
|
||||
<span class="item-value">{{ formatCurrency(receiptData.totalPriceWithTax) }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="item-label">备注</span>
|
||||
<span class="item-value">{{ attachment.remark }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<el-empty v-else :description="'暂无' + titleText"></el-empty>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">关闭</el-button>
|
||||
</span>
|
||||
|
||||
<!-- PDF Preview Dialog -->
|
||||
<el-dialog
|
||||
:visible.sync="pdfPreviewVisible"
|
||||
width="80%"
|
||||
top="5vh"
|
||||
append-to-body
|
||||
custom-class="pdf-preview-dialog"
|
||||
>
|
||||
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
|
||||
</el-dialog>
|
||||
|
||||
<!-- Upload Dialog -->
|
||||
<el-dialog
|
||||
:title="'上传' + titleText"
|
||||
:visible.sync="uploadDialogVisible"
|
||||
width="70vw"
|
||||
append-to-body
|
||||
@close="closeUploadDialog"
|
||||
custom-class="upload-receipt-dialog"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form :model="uploadForm" ref="uploadForm" :rules="rules" label-width="120px" size="medium" >
|
||||
<el-form-item label="票据类型" prop="receiptMethod" >
|
||||
<dict-tag :options="dict.type.payment_method" :value="receiptData.receiptMethod"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="收款附件" prop="file" required>
|
||||
<div style="display: flex; flex-direction: column; align-items: flex-start;">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:on-remove="handleFileRemove"
|
||||
:show-file-list="false"
|
||||
accept=".jpg,.jpeg,.png,.pdf"
|
||||
>
|
||||
<el-button size="small" type="primary" icon="el-icon-upload2">{{ uploadForm.file ? '重新上传' : '点击上传' }}</el-button>
|
||||
</el-upload>
|
||||
<div class="el-upload__tip" style="line-height: 1.5; margin-top: 5px;">支持上传PNG、JPG、PDF文件格式</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="含税总价">
|
||||
<span>{{ receiptData.totalPriceWithTax }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="确认含税总价">
|
||||
<el-input v-model="uploadForm.totalPriceWithTax" ></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="uploadForm.remark"
|
||||
:rows="4"
|
||||
placeholder="此处备注描述..."
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="upload-preview-container" style="height: 70vh;">
|
||||
<div v-if="previewUrl" class="preview-content">
|
||||
<img v-if="!isPreviewPdf" :src="previewUrl" class="preview-image" />
|
||||
<iframe v-else :src="previewUrl" width="100%" height="100%" frameborder="0"></iframe>
|
||||
</div>
|
||||
<div v-else class="preview-placeholder">
|
||||
<div class="placeholder-icon">
|
||||
<i class="el-icon-picture"></i>
|
||||
</div>
|
||||
<div class="placeholder-text">点击图片进入预览</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitNewUpload">保存</el-button>
|
||||
<el-button @click="closeUploadDialog">取消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getReceiveAttachments, uploadReceiveAttachment } from "@/api/finance/receive";
|
||||
import request from '@/utils/request';
|
||||
|
||||
export default {
|
||||
name: "ReceiveDialog",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
receiptData: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
dicts:['payment_method'],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
attachments: [],
|
||||
// Upload Dialog Data
|
||||
uploadDialogVisible: false,
|
||||
uploadForm: {
|
||||
ticketType: '',
|
||||
remark: '',
|
||||
file: null
|
||||
},
|
||||
rules: {
|
||||
},
|
||||
previewUrl: '',
|
||||
isPreviewPdf: false,
|
||||
// PDF Preview Data
|
||||
pdfUrls: {},
|
||||
pdfPreviewVisible: false,
|
||||
currentPdfUrl: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
dialogVisible: {
|
||||
get() {
|
||||
return this.visible;
|
||||
},
|
||||
set(val) {
|
||||
this.$emit("update:visible", val);
|
||||
},
|
||||
},
|
||||
previewList() {
|
||||
return this.attachments
|
||||
.filter(att => !this.isPdf(att.filePath))
|
||||
.map(att => this.getImageUrl(att.filePath));
|
||||
},
|
||||
canUpload() {
|
||||
if (!this.attachments || this.attachments.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return this.attachments.every(att => att.delFlag === '2');
|
||||
},
|
||||
titleText() {
|
||||
return '收款附件';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val && this.receiptData) {
|
||||
this.fetchAttachments();
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fetchAttachments() {
|
||||
if (!this.receiptData.id) return;
|
||||
this.loading = true;
|
||||
getReceiveAttachments(this.receiptData.id, { type: 'receipt' })
|
||||
.then(response => {
|
||||
const data = response.data || [];
|
||||
data.sort((a, b) => new Date(b.createTime) - new Date(a.createTime));
|
||||
this.attachments = data;
|
||||
this.loadPdfPreviews();
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
this.attachments = [];
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
loadPdfPreviews() {
|
||||
this.attachments.forEach(att => {
|
||||
if (this.isPdf(att.filePath) && !this.pdfUrls[att.filePath]) {
|
||||
request({
|
||||
url: '/common/download/resource',
|
||||
method: 'get',
|
||||
params: { resource: att.filePath },
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
this.$set(this.pdfUrls, att.filePath, url);
|
||||
}).catch(console.error);
|
||||
}
|
||||
});
|
||||
},
|
||||
openPdfPreview(url) {
|
||||
if (!url) return;
|
||||
this.currentPdfUrl = url;
|
||||
this.pdfPreviewVisible = true;
|
||||
},
|
||||
getImageUrl(resource) {
|
||||
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
|
||||
},
|
||||
isPdf(filePath) {
|
||||
return filePath && filePath.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
downloadFile(attachment) {
|
||||
const link = document.createElement('a');
|
||||
link.href = this.getImageUrl(attachment.filePath);
|
||||
link.download = attachment.fileName || 'receive_attachment';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
},
|
||||
handleClose() {
|
||||
this.attachments = [];
|
||||
// Clean up object URLs
|
||||
Object.values(this.pdfUrls).forEach(url => URL.revokeObjectURL(url));
|
||||
this.pdfUrls = {};
|
||||
},
|
||||
// New Upload Dialog Methods
|
||||
openUploadDialog() {
|
||||
this.uploadForm = {
|
||||
remark: '',
|
||||
file: null
|
||||
};
|
||||
if (this.$refs.uploadForm) {
|
||||
this.$refs.uploadForm.clearValidate();
|
||||
}
|
||||
this.previewUrl = '';
|
||||
this.isPreviewPdf = false;
|
||||
this.uploadDialogVisible = true;
|
||||
},
|
||||
closeUploadDialog() {
|
||||
this.uploadDialogVisible = false;
|
||||
this.uploadForm.file = null;
|
||||
this.previewUrl = '';
|
||||
},
|
||||
handleFileChange(file) {
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
const isAcceptedType = ['image/jpeg', 'image/png', 'application/pdf'].includes(file.raw.type);
|
||||
|
||||
if (!isAcceptedType) {
|
||||
this.$message.error('上传文件只能是 JPG/PNG/PDF 格式!');
|
||||
return;
|
||||
}
|
||||
if (!isLt2M) {
|
||||
this.$message.error('上传文件大小不能超过 2MB!');
|
||||
return;
|
||||
}
|
||||
|
||||
this.uploadForm.file = file.raw;
|
||||
this.isPreviewPdf = file.raw.type === 'application/pdf';
|
||||
|
||||
this.previewUrl = URL.createObjectURL(file.raw);
|
||||
},
|
||||
handleFileRemove() {
|
||||
this.uploadForm.file = null;
|
||||
this.previewUrl = '';
|
||||
},
|
||||
submitNewUpload() {
|
||||
this.$refs.uploadForm.validate(valid => {
|
||||
if (valid) {
|
||||
if (!this.uploadForm.file) {
|
||||
this.$message.warning("请选择要上传的文件");
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", this.uploadForm.file);
|
||||
formData.append("relatedBillId", this.receiptData.id);
|
||||
formData.append("remark", this.uploadForm.remark);
|
||||
|
||||
uploadReceiveAttachment(formData)
|
||||
.then(response => {
|
||||
this.$message.success("上传成功");
|
||||
this.closeUploadDialog();
|
||||
this.fetchAttachments();
|
||||
})
|
||||
.catch(error => {
|
||||
this.$message.error("上传失败");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.receipt-dialog-body {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.loading-spinner {
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
padding: 20px;
|
||||
}
|
||||
.receipt-card-content {
|
||||
display: flex;
|
||||
}
|
||||
.receipt-details {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.detail-item {
|
||||
display: flex;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.item-label {
|
||||
width: 110px;
|
||||
color: #606266;
|
||||
text-align: right;
|
||||
margin-right: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.item-value {
|
||||
color: #303133;
|
||||
}
|
||||
.image-wrapper {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
min-height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid #DCDFE6;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.void-overlay {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(-30deg);
|
||||
color: red;
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
.download-btn {
|
||||
display: block;
|
||||
}
|
||||
.upload-btn-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
/* New Dialog Styles */
|
||||
.upload-preview-container {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
background-color: #f5f7fa;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.preview-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.preview-image {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
.preview-placeholder {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
}
|
||||
.placeholder-icon {
|
||||
font-size: 64px;
|
||||
margin-bottom: 10px;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
.placeholder-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
.pdf-thumbnail-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pdf-hover-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.pdf-thumbnail-container:hover .pdf-hover-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,402 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="150px">
|
||||
<el-form-item label="项目编号" prop="projectCode">
|
||||
<el-input
|
||||
v-model="queryParams.projectCode"
|
||||
placeholder="请输入项目编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="销售-收款单编号" prop="receiptBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.receiptBillCode"
|
||||
placeholder="请输入收款单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="进货商名称" prop="partnerName">
|
||||
<el-input
|
||||
v-model="queryParams.partnerName"
|
||||
placeholder="请输入进货商名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="销售-应收单编号" prop="receivableBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.receivableBillCode"
|
||||
placeholder="请输入应收单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="receiptBillType">
|
||||
<el-select v-model="queryParams.receiptBillType" placeholder="请选择" clearable>
|
||||
<el-option v-for="dict in dict.type.receipt_bill_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="支付方式" prop="receiptMethod">
|
||||
<el-select v-model="queryParams.receiptMethod" placeholder="请选择" clearable>
|
||||
<el-option v-for="dict in dict.type.payment_method" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="审批状态" prop="approveStatus">
|
||||
<el-select v-model="queryParams.approveStatus" placeholder="请选择审批状态" clearable>
|
||||
<el-option v-for="dict in dict.type.approve_status" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="收款状态" prop="receiveStatus">
|
||||
<el-select v-model="queryParams.receiptStatus" placeholder="请选择收款状态" clearable>
|
||||
<el-option v-for="dict in dict.type.receipt_bill_status" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批节点" prop="approveNode">
|
||||
<el-input
|
||||
v-model="queryParams.approveNode"
|
||||
placeholder="请输入审批节点"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批通过时间">
|
||||
<el-date-picker
|
||||
v-model="dateRangeApproval"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="预计收款时间">
|
||||
<el-date-picker
|
||||
v-model="dateRangeEstimated"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="实际收款时间">
|
||||
<el-date-picker
|
||||
v-model="dateRangeActual"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="receiveList">
|
||||
<el-table-column label="销售-收款单编号" align="center" prop="receiptBillCode" width="180" />
|
||||
<el-table-column label="预计收款时间" align="center" prop="receiptTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.receiptTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="进货商名称" align="center" prop="partnerName" width="230" />
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" width="180" >
|
||||
<template slot-scope="scope">
|
||||
<span><span :style="scope.row.totalPriceWithTax<0?{color:'red'}:{}">{{ formatCurrency(scope.row.totalPriceWithTax) }} </span></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="receiptBillType" width="180">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.receipt_bill_type" :value="scope.row.receiptBillType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预收单剩余额度" align="center" prop="remainingAmount" width="180" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收款状态" align="center" prop="receiveStatus" width="180">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.receipt_bill_status" :value="scope.row.receiptStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批状态" align="center" prop="approveStatus" width="180">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.approve_status" :value="scope.row.approveStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批通过时间" align="center" prop="approveTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.approveTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" width="200" fixed="right" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="300" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-view"
|
||||
@click="handleDetail(scope.row)"
|
||||
>查看详情</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-document"
|
||||
v-show="(scope.row.receiptBillType==='FROM_RECEIVABLE' && scope.row.approveStatus!=='0') ||
|
||||
(scope.row.receiptBillType!=='FROM_RECEIVABLE' && scope.row.approveStatus==='2') "
|
||||
@click="handleReceipt(scope.row)"
|
||||
>{{scope.row.receiptBillType!=='REFUND'?'客户付款图':'退款回执单'}}</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-money"
|
||||
v-show=" scope.row.receiptStatus==='1' && scope.row.approveStatus==='0'"
|
||||
@click="handleApplyPayment(scope.row)"
|
||||
>申请收款</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-money"
|
||||
v-show="scope.row.receiptBillType==='FROM_RECEIVABLE' && scope.row.receiptStatus==='2' && scope.row.approveStatus==='2'"
|
||||
@click="handleApplyRefund(scope.row)"
|
||||
>申请退款</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-document"
|
||||
v-show="scope.row.receiptBillType==='FROM_RECEIVABLE' && scope.row.receiptStatus==='1' && scope.row.approveStatus==='0' "
|
||||
@click="handleReturn(scope.row)"
|
||||
>退回</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-refresh-right"
|
||||
v-if="(scope.row.approveStatus === '2' || scope.row.approveStatus==='3') && (scope.row.receiptStatus==='1'||scope.row.receiptStatus==='3')"
|
||||
@click="handleRevoke(scope.row)"
|
||||
>撤销</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 详情抽屉 -->
|
||||
<detail-drawer :visible.sync="detailOpen" :detail="detailData"></detail-drawer>
|
||||
<!-- 新增弹窗 -->
|
||||
<add-form :visible.sync="addOpen" :dicts="dict.type" @submit="handleAddSubmit"></add-form>
|
||||
<!-- 收款附件弹窗 -->
|
||||
<receive-dialog :visible.sync="receiptOpen" :receipt-data="currentRow" :dicts="dict.type"></receive-dialog>
|
||||
<!-- 申请付款弹窗 -->
|
||||
<apply-payment-dialog :visible.sync="applyPaymentOpen" :receipt-data="currentRow" :dicts="dict.type" @submit="getList"></apply-payment-dialog>
|
||||
<!-- 申请退款弹窗 -->
|
||||
<apply-refund-dialog :visible.sync="applyRefundOpen" :receipt-data="currentRow" :dicts="dict.type" @submit="getList"></apply-refund-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {listReceive, getReceive, redRush, mergeReceivable, returnReceive, addReceipt, revokeReceipt} from "@/api/finance/receive";
|
||||
import { addDateRange } from "@/utils/ruoyi";
|
||||
import DetailDrawer from "./components/DetailDrawer.vue";
|
||||
import AddForm from "./components/AddForm.vue";
|
||||
import ReceiveDialog from "./components/ReceiveDialog.vue";
|
||||
import ApplyPaymentDialog from "./components/ApplyPaymentDialog.vue";
|
||||
import ApplyRefundDialog from "./components/ApplyRefundDialog.vue";
|
||||
|
||||
export default {
|
||||
name: "Receive",
|
||||
components: {
|
||||
DetailDrawer,
|
||||
AddForm,
|
||||
ReceiveDialog,
|
||||
ApplyPaymentDialog,
|
||||
ApplyRefundDialog
|
||||
},
|
||||
dicts:['receipt_bill_type','approve_status','receipt_bill_status', 'payment_method'],
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 收款单表格数据
|
||||
receiveList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectCode: null,
|
||||
projectName: null,
|
||||
receiptBillCode: null,
|
||||
partnerName: null,
|
||||
receivableBillCode: null,
|
||||
receiptBillType: null,
|
||||
approveStatus: null,
|
||||
receiptStatus: null,
|
||||
approveNode: null,
|
||||
orderByColumn:'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
// 日期范围
|
||||
dateRangeApproval: [],
|
||||
dateRangeEstimated: [],
|
||||
dateRangeActual: [],
|
||||
// 详情抽屉
|
||||
detailOpen: false,
|
||||
detailData: null,
|
||||
// 新增弹窗
|
||||
addOpen: false,
|
||||
// 收款附件弹窗
|
||||
receiptOpen: false,
|
||||
currentRow: {},
|
||||
// 申请付款弹窗
|
||||
applyPaymentOpen: false,
|
||||
// 申请退款弹窗
|
||||
applyRefundOpen: false,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
addDateRange,
|
||||
getList() {
|
||||
this.loading = true;
|
||||
let query = { ...this.queryParams };
|
||||
query = this.addDateRange(query, this.dateRangeApproval, 'ApproveTime');
|
||||
query = this.addDateRange(query, this.dateRangeEstimated, 'PlanReceiveTime');
|
||||
query = this.addDateRange(query, this.dateRangeActual, 'ActualReceiveTime');
|
||||
|
||||
listReceive(query).then(response => {
|
||||
this.receiveList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.dateRangeApproval = [];
|
||||
this.dateRangeEstimated = [];
|
||||
this.dateRangeActual = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.addOpen = true;
|
||||
},
|
||||
/** 新增提交 */
|
||||
handleAddSubmit(form) {
|
||||
if (form.receiptBillType==='FROM_RECEIVABLE'){
|
||||
mergeReceivable(form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.addOpen = false;
|
||||
this.getList();
|
||||
}).catch(error => {
|
||||
console.error("发起收款单失败", error);
|
||||
this.$modal.msgError("新增失败");
|
||||
});
|
||||
}else{
|
||||
addReceipt(form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.addOpen = false;
|
||||
this.getList();
|
||||
}).catch(error => {
|
||||
console.error("新增收款单失败", error);
|
||||
this.$modal.msgError("新增失败");
|
||||
});
|
||||
}
|
||||
},
|
||||
/** 详情按钮操作 */
|
||||
handleDetail(row) {
|
||||
getReceive(row.id).then(response => {
|
||||
this.detailData = response.data;
|
||||
this.detailData.approveNode = row.approveNode;
|
||||
this.detailOpen = true;
|
||||
});
|
||||
},
|
||||
/** 申请付款按钮操作 */
|
||||
handleApplyPayment(row) {
|
||||
this.currentRow = row;
|
||||
this.applyPaymentOpen = true;
|
||||
},
|
||||
/** 申请退款按钮操作 */
|
||||
handleApplyRefund(row) {
|
||||
this.currentRow = row;
|
||||
this.applyRefundOpen = true;
|
||||
},
|
||||
/** 收款附件按钮操作 */
|
||||
handleReceipt(row) {
|
||||
this.currentRow = row;
|
||||
this.receiptOpen = true;
|
||||
},
|
||||
/** 撤销按钮操作 */
|
||||
handleRevoke(row) {
|
||||
let msg = row.receiptBillType === 'REFUND' ? '是否将该笔销售-退款单撤销,将退款单撤销至收款单' : '是否将该笔销售-收款单撤销,撤销至收款单未审批状态';
|
||||
this.$modal.confirm(msg).then(() => {
|
||||
return revokeReceipt(row.id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("撤销成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
/** 退回按钮操作 */
|
||||
handleRedRush(row) {
|
||||
this.$modal.confirm('是否确认收款单编号为"' + row.receiptBillCode + '"的数据项进行红冲,并提交财务审批?').then(function() {
|
||||
return redRush(row.id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("申请成功");
|
||||
}).catch(() => {});
|
||||
},
|
||||
handleReturn(row) {
|
||||
this.$modal.confirm('是否将该笔销售-应收单的:销售-收款单与销售-发票单同时退回至销售-应收单表').then(function() {
|
||||
return returnReceive(row.id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("退回成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="项目编号" prop="projectCode">
|
||||
<el-input
|
||||
v-model="queryParams.projectCode"
|
||||
placeholder="请输入项目编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="计收状态" prop="chargeStatus">
|
||||
<el-select
|
||||
v-model="queryParams.chargeStatus"
|
||||
placeholder="请输入计收状态"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in dict.type.charge_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="收款状态" prop="receiptStatus">
|
||||
<el-select
|
||||
v-model="queryParams.receiptStatus"
|
||||
placeholder="请输入收款状态"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in dict.type.report_receipt_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开票状态" prop="invoiceStatus">
|
||||
<el-select
|
||||
v-model="queryParams.invoiceStatus"
|
||||
placeholder="请输入开票状态"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in dict.type.report_invoice_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-download"
|
||||
size="mini"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['finance:report:export']"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="reportList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="项目" align="center" prop="orderCode" width="180" v-if="columns.projectCode.visible || columns.projectName.visible" key="orderCode">
|
||||
<el-table-column label="项目编号" align="center" prop="projectCode" width="120" v-if="columns.projectCode.visible" key="receivableWithTax"/>
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" width="120" v-if="columns.projectName.visible" key="receivableWithoutTax"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 应收 -->
|
||||
<el-table-column label="应收单" align="center" v-if="columns.receivableWithTax.visible || columns.receivableWithoutTax.visible||columns.receivableTax.visible">
|
||||
<el-table-column label="应收含税总价" align="center" prop="receivableWithTax" width="120" v-if="columns.receivableWithTax.visible" key="receivableWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="应收未税总价" align="center" prop="receivableWithoutTax" width="120" v-if="columns.receivableWithoutTax.visible" key="receivableWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="应收税额" align="center" prop="receivableTax" width="120" v-if="columns.receivableTax.visible" key="receivableTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
|
||||
<!-- 毛利 -->
|
||||
<el-table-column label="计收统计" align="center" v-if="columns.chargeStatus.visible || columns.chargedWithoutTax.visible || columns.grossProfit.visible ||columns.grossProfitRate.visible">
|
||||
<el-table-column label="计收状态" align="center" prop="chargeStatus" width="100" v-if="columns.chargeStatus.visible" key="chargeStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.charge_status" :value="scope.row.chargeStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已计收未税金额" align="center" prop="chargedWithoutTax" width="120" v-if="columns.chargedWithoutTax.visible" key="chargedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="毛利" align="center" prop="grossProfit" width="120" v-if="columns.grossProfit.visible" key="grossProfit" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="毛利率" align="center" prop="grossProfitRate" width="100" v-if="columns.grossProfitRate.visible" key="grossProfitRate"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- <el-table-column label="业务计收时间" align="center" prop="bizChargeDate" width="180" v-if="columns.bizChargeDate.visible" key="bizChargeDate">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <span>{{ parseTime(scope.row.bizChargeDate, '{y}-{m}-{d}') }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column label="财务计收时间" align="center" prop="financeChargeDate" width="180" v-if="columns.financeChargeDate.visible" key="financeChargeDate">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <span>{{ parseTime(scope.row.financeChargeDate, '{y}-{m}-{d}') }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
|
||||
<!-- 收款 -->
|
||||
<el-table-column label="收款单" align="center" >
|
||||
<el-table-column label="收款状态" align="center" prop="receiptStatus" width="100" v-if="columns.receiptStatus.visible" key="receiptStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.report_receipt_status" :value="scope.row.receiptStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已收款含税金额" align="center" prop="receivedWithTax" width="120" v-if="columns.receivedWithTax.visible" key="receivedWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已收款未税金额" align="center" prop="receivedWithoutTax" width="120" v-if="columns.receivedWithoutTax.visible" key="receivedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已收款税额" align="center" prop="receivedTax" width="120" v-if="columns.receivedTax.visible" key="receivedTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收款中含税金额" align="center" prop="receivingWithTax" width="120" v-if="columns.receivingWithTax.visible" key="receivingWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收款中未税金额" align="center" prop="receivingWithoutTax" width="120" v-if="columns.receivingWithoutTax.visible" key="receivingWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收款中税额" align="center" prop="receivingTax" width="120" v-if="columns.receivingTax.visible" key="receivingTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收款含税金额" align="center" prop="unreceivedWithTax" width="120" v-if="columns.unreceivedWithTax.visible" key="unreceivedWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收款未税金额" align="center" prop="unreceivedWithoutTax" width="120" v-if="columns.unreceivedWithoutTax.visible" key="unreceivedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收款税额" align="center" prop="unreceivedTax" width="120" v-if="columns.unreceivedTax.visible" key="unreceivedTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 开票 -->
|
||||
<el-table-column label="开票单" align="center">
|
||||
<el-table-column label="开票状态" align="center" prop="invoiceStatus" width="100" v-if="columns.invoiceStatus.visible" key="invoiceStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.report_invoice_status" :value="scope.row.invoiceStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已开票含税金额" align="center" prop="invoicedWithTax" width="120" v-if="columns.invoicedWithTax.visible" key="invoicedWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已开票未税金额" align="center" prop="invoicedWithoutTax" width="120" v-if="columns.invoicedWithoutTax.visible" key="invoicedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已开票税额" align="center" prop="invoicedTax" width="120" v-if="columns.invoicedTax.visible" key="invoicedTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="开票中含税金额" align="center" prop="invoicingWithTax" width="120" v-if="columns.invoicingWithTax.visible" key="invoicingWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="开票中未税金额" align="center" prop="invoicingWithoutTax" width="120" v-if="columns.invoicingWithoutTax.visible" key="invoicingWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="开票中税额" align="center" prop="invoicingTax" width="120" v-if="columns.invoicingTax.visible" key="invoicingTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未开票含税金额" align="center" prop="uninvoicedWithTax" width="120" v-if="columns.uninvoicedWithTax.visible" key="uninvoicedWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未开票未税金额" align="center" prop="uninvoicedWithoutTax" width="120" v-if="columns.uninvoicedWithoutTax.visible" key="uninvoicedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未开票税额" align="center" prop="uninvoicedTax" width="120" v-if="columns.uninvoicedTax.visible" key="uninvoicedTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listReport } from "@/api/finance/report";
|
||||
|
||||
export default {
|
||||
name: "ReceiveReport",
|
||||
dicts: ['charge_status', 'report_receipt_status', 'report_invoice_status'],
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 项目报表表格数据
|
||||
reportList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
orderCode: null,
|
||||
projectCode: null,
|
||||
projectName: null,
|
||||
chargeStatus: null,
|
||||
receiptStatus: null,
|
||||
invoiceStatus: null,
|
||||
orderByColumn:'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
// 列信息
|
||||
columns: {
|
||||
projectCode: { label: "项目编号", visible: true },
|
||||
projectName: { label: "项目名称", visible: true },
|
||||
receivableWithTax: { label: "应收含税总价", visible: true },
|
||||
receivableWithoutTax: { label: "应收未税总价", visible: true },
|
||||
receivableTax: { label: "应收税额", visible: true },
|
||||
chargeStatus: { label: "计收状态", visible: true },
|
||||
chargedWithoutTax: { label: "已计收未税金额", visible: true },
|
||||
grossProfit: { label: "毛利", visible: true },
|
||||
grossProfitRate: { label: "毛利率", visible: true },
|
||||
// bizChargeDate: { label: "业务计收时间", visible: true },
|
||||
// financeChargeDate: { label: "财务计收时间", visible: true },
|
||||
receiptStatus: { label: "收款状态", visible: true },
|
||||
receivedWithTax: { label: "已收款含税金额", visible: true },
|
||||
receivedWithoutTax: { label: "已收款未税金额", visible: true },
|
||||
receivedTax: { label: "已收款税额", visible: true },
|
||||
receivingWithTax: { label: "收款中含税金额", visible: true },
|
||||
receivingWithoutTax: { label: "收款中未税金额", visible: true },
|
||||
receivingTax: { label: "收款中税额", visible: true },
|
||||
unreceivedWithTax: { label: "未收款含税金额", visible: true },
|
||||
unreceivedWithoutTax: { label: "未收款未税金额", visible: true },
|
||||
unreceivedTax: { label: "未收款税额", visible: true },
|
||||
invoiceStatus: { label: "开票状态", visible: true },
|
||||
invoicedWithTax: { label: "已开票含税金额", visible: true },
|
||||
invoicedWithoutTax: { label: "已开票未税金额", visible: true },
|
||||
invoicedTax: { label: "已开票税额", visible: true },
|
||||
invoicingWithTax: { label: "开票中含税金额", visible: true },
|
||||
invoicingWithoutTax: { label: "开票中未税金额", visible: true },
|
||||
invoicingTax: { label: "开票中税额", visible: true },
|
||||
uninvoicedWithTax: { label: "未开票含税金额", visible: true },
|
||||
uninvoicedWithoutTax: { label: "未开票未税金额", visible: true },
|
||||
uninvoicedTax: { label: "未开票税额", visible: true },
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询项目报表列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listReport(this.queryParams).then(response => {
|
||||
this.reportList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.id)
|
||||
this.single = selection.length!==1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
this.download('finance/report/export', {
|
||||
...this.queryParams
|
||||
}, `report_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,382 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="项目编号" prop="projectCode">
|
||||
<el-input
|
||||
v-model="queryParams.projectCode"
|
||||
placeholder="请输入项目编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="计收状态" prop="chargeStatus">
|
||||
<el-select
|
||||
v-model="queryParams.chargeStatus"
|
||||
placeholder="请输入计收状态"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in dict.type.charge_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="收款状态" prop="receiptStatus">
|
||||
<el-select
|
||||
v-model="queryParams.receiptStatus"
|
||||
placeholder="请输入收款状态"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in dict.type.report_receipt_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开票状态" prop="invoiceStatus">
|
||||
<el-select
|
||||
v-model="queryParams.invoiceStatus"
|
||||
placeholder="请输入开票状态"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in dict.type.report_invoice_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="付款状态" prop="paymentStatus">
|
||||
<el-select
|
||||
v-model="queryParams.paymentStatus"
|
||||
placeholder="请输入付款状态"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in dict.type.report_payment_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="收票状态" prop="ticketStatus">
|
||||
<el-select
|
||||
v-model="queryParams.ticketStatus"
|
||||
placeholder="请输入收票状态"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in dict.type.report_ticket_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-download"
|
||||
size="mini"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['finance:report:export']"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="reportList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="项目" align="center" prop="orderCode" width="180" v-if="columns.projectCode.visible || columns.projectName.visible" key="orderCode">
|
||||
<el-table-column label="项目编号" align="center" prop="projectCode" width="120" v-if="columns.projectCode.visible" key="receivableWithTax"/>
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" width="120" v-if="columns.projectName.visible" key="receivableWithoutTax"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 应收 -->
|
||||
<el-table-column label="应收单" align="center" v-if="columns.receivableWithTax.visible || columns.receivableWithoutTax.visible||columns.receivableTax.visible">
|
||||
<el-table-column label="应收含税总价" align="center" prop="receivableWithTax" width="120" v-if="columns.receivableWithTax.visible" key="receivableWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="应收未税总价" align="center" prop="receivableWithoutTax" width="120" v-if="columns.receivableWithoutTax.visible" key="receivableWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="应收税额" align="center" prop="receivableTax" width="120" v-if="columns.receivableTax.visible" key="receivableTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
|
||||
<!-- 毛利 -->
|
||||
<el-table-column label="计收统计" align="center" v-if="columns.chargeStatus.visible || columns.chargedWithoutTax.visible || columns.grossProfit.visible ||columns.grossProfitRate.visible">
|
||||
<el-table-column label="计收状态" align="center" prop="chargeStatus" width="100" v-if="columns.chargeStatus.visible" key="chargeStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.charge_status" :value="scope.row.chargeStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已计收未税金额" align="center" prop="chargedWithoutTax" width="120" v-if="columns.chargedWithoutTax.visible" key="chargedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="毛利" align="center" prop="grossProfit" width="120" v-if="columns.grossProfit.visible" key="grossProfit" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="毛利率" align="center" prop="grossProfitRate" width="100" v-if="columns.grossProfitRate.visible" key="grossProfitRate"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- <el-table-column label="业务计收时间" align="center" prop="bizChargeDate" width="180" v-if="columns.bizChargeDate.visible" key="bizChargeDate">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <span>{{ parseTime(scope.row.bizChargeDate, '{y}-{m}-{d}') }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column label="财务计收时间" align="center" prop="financeChargeDate" width="180" v-if="columns.financeChargeDate.visible" key="financeChargeDate">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <span>{{ parseTime(scope.row.financeChargeDate, '{y}-{m}-{d}') }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
|
||||
<!-- 收款 -->
|
||||
<el-table-column label="收款单" align="center" >
|
||||
<el-table-column label="收款状态" align="center" prop="receiptStatus" width="100" v-if="columns.receiptStatus.visible" key="receiptStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.report_receipt_status" :value="scope.row.receiptStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已收款含税金额" align="center" prop="receivedWithTax" width="120" v-if="columns.receivedWithTax.visible" key="receivedWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已收款未税金额" align="center" prop="receivedWithoutTax" width="120" v-if="columns.receivedWithoutTax.visible" key="receivedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已收款税额" align="center" prop="receivedTax" width="120" v-if="columns.receivedTax.visible" key="receivedTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收款中含税金额" align="center" prop="receivingWithTax" width="120" v-if="columns.receivingWithTax.visible" key="receivingWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收款中未税金额" align="center" prop="receivingWithoutTax" width="120" v-if="columns.receivingWithoutTax.visible" key="receivingWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收款中税额" align="center" prop="receivingTax" width="120" v-if="columns.receivingTax.visible" key="receivingTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收款含税金额" align="center" prop="unreceivedWithTax" width="120" v-if="columns.unreceivedWithTax.visible" key="unreceivedWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收款未税金额" align="center" prop="unreceivedWithoutTax" width="120" v-if="columns.unreceivedWithoutTax.visible" key="unreceivedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收款税额" align="center" prop="unreceivedTax" width="120" v-if="columns.unreceivedTax.visible" key="unreceivedTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 开票 -->
|
||||
<el-table-column label="开票单" align="center">
|
||||
<el-table-column label="开票状态" align="center" prop="invoiceStatus" width="100" v-if="columns.invoiceStatus.visible" key="invoiceStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.report_invoice_status" :value="scope.row.invoiceStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已开票含税金额" align="center" prop="invoicedWithTax" width="120" v-if="columns.invoicedWithTax.visible" key="invoicedWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已开票未税金额" align="center" prop="invoicedWithoutTax" width="120" v-if="columns.invoicedWithoutTax.visible" key="invoicedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已开票税额" align="center" prop="invoicedTax" width="120" v-if="columns.invoicedTax.visible" key="invoicedTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="开票中含税金额" align="center" prop="invoicingWithTax" width="120" v-if="columns.invoicingWithTax.visible" key="invoicingWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="开票中未税金额" align="center" prop="invoicingWithoutTax" width="120" v-if="columns.invoicingWithoutTax.visible" key="invoicingWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="开票中税额" align="center" prop="invoicingTax" width="120" v-if="columns.invoicingTax.visible" key="invoicingTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未开票含税金额" align="center" prop="uninvoicedWithTax" width="120" v-if="columns.uninvoicedWithTax.visible" key="uninvoicedWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未开票未税金额" align="center" prop="uninvoicedWithoutTax" width="120" v-if="columns.uninvoicedWithoutTax.visible" key="uninvoicedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未开票税额" align="center" prop="uninvoicedTax" width="120" v-if="columns.uninvoicedTax.visible" key="uninvoicedTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 应付 -->
|
||||
<el-table-column label="应付单" align="center">
|
||||
<el-table-column label="应付含税金额" align="center" prop="payableWithTax" width="120" v-if="columns.payableWithTax.visible" key="payableWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="应付未税金额" align="center" prop="payableWithoutTax" width="120" v-if="columns.payableWithoutTax.visible" key="payableWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="应付税额" align="center" prop="payableTax" width="120" v-if="columns.payableTax.visible" key="payableTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 付款 -->
|
||||
<el-table-column label="付款单" align="center">
|
||||
<el-table-column label="付款状态" align="center" prop="paymentStatus" width="100" v-if="columns.paymentStatus.visible" key="paymentStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.report_payment_status" :value="scope.row.paymentStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已付款含税金额" align="center" prop="paidWithTax" width="120" v-if="columns.paidWithTax.visible" key="paidWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已付款未税金额" align="center" prop="paidWithoutTax" width="120" v-if="columns.paidWithoutTax.visible" key="paidWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已付款税额" align="center" prop="paidTax" width="120" v-if="columns.paidTax.visible" key="paidTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="付款中含税金额" align="center" prop="payingWithTax" width="120" v-if="columns.payingWithTax.visible" key="payingWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="付款中未税金额" align="center" prop="payingWithoutTax" width="120" v-if="columns.payingWithoutTax.visible" key="payingWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="付款中税额" align="center" prop="payingTax" width="120" v-if="columns.payingTax.visible" key="payingTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未付款含税金额" align="center" prop="unpaidWithTax" width="120" v-if="columns.unpaidWithTax.visible" key="unpaidWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未付款未税金额" align="center" prop="unpaidWithoutTax" width="120" v-if="columns.unpaidWithoutTax.visible" key="unpaidWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未付款税额" align="center" prop="unpaidTax" width="120" v-if="columns.unpaidTax.visible" key="unpaidTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 收票 -->
|
||||
<el-table-column label="收票单" align="center">
|
||||
<el-table-column label="收票状态" align="center" prop="ticketStatus" width="100" v-if="columns.ticketStatus.visible" key="ticketStatus">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.report_ticket_status" :value="scope.row.ticketStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已收票含税金额" align="center" prop="ticketedWithTax" width="120" v-if="columns.ticketedWithTax.visible" key="ticketedWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已收票未税金额" align="center" prop="ticketedWithoutTax" width="120" v-if="columns.ticketedWithoutTax.visible" key="ticketedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已收票税额" align="center" prop="ticketedTax" width="120" v-if="columns.ticketedTax.visible" key="ticketedTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收票中含税金额" align="center" prop="ticketingWithTax" width="120" v-if="columns.ticketingWithTax.visible" key="ticketingWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收票中未税金额" align="center" prop="ticketingWithoutTax" width="120" v-if="columns.ticketingWithoutTax.visible" key="ticketingWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="收票中税额" align="center" prop="ticketingTax" width="120" v-if="columns.ticketingTax.visible" key="ticketingTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收票含税金额" align="center" prop="unticketedWithTax" width="120" v-if="columns.unticketedWithTax.visible" key="unticketedWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收票未税金额" align="center" prop="unticketedWithoutTax" width="120" v-if="columns.unticketedWithoutTax.visible" key="unticketedWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="未收票税额" align="center" prop="unticketedTax" width="120" v-if="columns.unticketedTax.visible" key="unticketedTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listReport } from "@/api/finance/report";
|
||||
|
||||
export default {
|
||||
name: "Report",
|
||||
dicts: ['charge_status', 'report_receipt_status', 'report_invoice_status', 'report_payment_status', 'report_ticket_status'],
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 项目报表表格数据
|
||||
reportList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
orderCode: null,
|
||||
projectCode: null,
|
||||
projectName: null,
|
||||
chargeStatus: null,
|
||||
receiptStatus: null,
|
||||
invoiceStatus: null,
|
||||
paymentStatus: null,
|
||||
ticketStatus: null,
|
||||
orderByColumn:'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
// 列信息
|
||||
columns: {
|
||||
projectCode: { label: "项目编号", visible: true },
|
||||
projectName: { label: "项目名称", visible: true },
|
||||
receivableWithTax: { label: "应收含税总价", visible: true },
|
||||
receivableWithoutTax: { label: "应收未税总价", visible: true },
|
||||
receivableTax: { label: "应收税额", visible: true },
|
||||
chargeStatus: { label: "计收状态", visible: true },
|
||||
chargedWithoutTax: { label: "已计收未税金额", visible: true },
|
||||
grossProfit: { label: "毛利", visible: true },
|
||||
grossProfitRate: { label: "毛利率", visible: true },
|
||||
// bizChargeDate: { label: "业务计收时间", visible: true },
|
||||
// financeChargeDate: { label: "财务计收时间", visible: true },
|
||||
receiptStatus: { label: "收款状态", visible: true },
|
||||
receivedWithTax: { label: "已收款含税金额", visible: true },
|
||||
receivedWithoutTax: { label: "已收款未税金额", visible: true },
|
||||
receivedTax: { label: "已收款税额", visible: true },
|
||||
receivingWithTax: { label: "收款中含税金额", visible: true },
|
||||
receivingWithoutTax: { label: "收款中未税金额", visible: true },
|
||||
receivingTax: { label: "收款中税额", visible: true },
|
||||
unreceivedWithTax: { label: "未收款含税金额", visible: true },
|
||||
unreceivedWithoutTax: { label: "未收款未税金额", visible: true },
|
||||
unreceivedTax: { label: "未收款税额", visible: true },
|
||||
invoiceStatus: { label: "开票状态", visible: true },
|
||||
invoicedWithTax: { label: "已开票含税金额", visible: true },
|
||||
invoicedWithoutTax: { label: "已开票未税金额", visible: true },
|
||||
invoicedTax: { label: "已开票税额", visible: true },
|
||||
invoicingWithTax: { label: "开票中含税金额", visible: true },
|
||||
invoicingWithoutTax: { label: "开票中未税金额", visible: true },
|
||||
invoicingTax: { label: "开票中税额", visible: true },
|
||||
uninvoicedWithTax: { label: "未开票含税金额", visible: true },
|
||||
uninvoicedWithoutTax: { label: "未开票未税金额", visible: true },
|
||||
uninvoicedTax: { label: "未开票税额", visible: true },
|
||||
payableWithTax: { label: "应付含税总价", visible: true },
|
||||
payableWithoutTax: { label: "应付未税总价", visible: true },
|
||||
payableTax: { label: "应付税额", visible: true },
|
||||
paymentStatus: { label: "付款状态", visible: true },
|
||||
paidWithTax: { label: "已付款含税金额", visible: true },
|
||||
paidWithoutTax: { label: "已付款未税金额", visible: true },
|
||||
paidTax: { label: "已付款税额", visible: true },
|
||||
payingWithTax: { label: "付款中含税金额", visible: true },
|
||||
payingWithoutTax: { label: "付款中未税金额", visible: true },
|
||||
payingTax: { label: "付款中税额", visible: true },
|
||||
unpaidWithTax: { label: "未付款含税金额", visible: true },
|
||||
unpaidWithoutTax: { label: "未付款未税金额", visible: true },
|
||||
unpaidTax: { label: "未付款税额", visible: true },
|
||||
ticketStatus: { label: "收票状态", visible: true },
|
||||
ticketedWithTax: { label: "已收票含税金额", visible: true },
|
||||
ticketedWithoutTax: { label: "已收票未税金额", visible: true },
|
||||
ticketedTax: { label: "已收票税额", visible: true },
|
||||
ticketingWithTax: { label: "收票中含税金额", visible: true },
|
||||
ticketingWithoutTax: { label: "收票中未税金额", visible: true },
|
||||
ticketingTax: { label: "收票中税额", visible: true },
|
||||
unticketedWithTax: { label: "未收票含税金额", visible: true },
|
||||
unticketedWithoutTax: { label: "未收票未税金额", visible: true },
|
||||
unticketedTax: { label: "未收票税额", visible: true },
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询项目报表列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listReport(this.queryParams).then(response => {
|
||||
this.reportList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.id)
|
||||
this.single = selection.length!==1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
this.download('finance/report/export', {
|
||||
...this.queryParams
|
||||
}, `report_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<template>
|
||||
<el-drawer
|
||||
title="核销详情"
|
||||
:visible.sync="visible"
|
||||
direction="rtl"
|
||||
size="60%"
|
||||
@close="handleClose"
|
||||
>
|
||||
<div class="drawer-body" v-if="detail">
|
||||
<div class="section">
|
||||
<el-divider content-position="left">开票单信息</el-divider>
|
||||
<el-table :data="[detail]" border stripe>
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="writeOffAmount" label="本次核销含税总价(元)" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column prop="partnerName" label="进货商名称" align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="invoiceBillCode" label="销售-开票单编号" align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="开票单生成时间" align="center" width="160">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.invoiceBill.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column prop="invoiceBillType" label="开票单类型" align="center">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <dict-tag :options="dict.type.invoice_bill_type" :value="scope.row.invoiceBill.invoiceBillType"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<el-divider content-position="left">应收单明细</el-divider>
|
||||
<el-table :data="detail.detailList" border stripe>
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="receiptAmount" label="本次核销金额(元)" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column prop="partnerName" label="进货商名称" align="center"></el-table-column>
|
||||
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="应收单生成时间" align="center"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "WriteOffDetailDrawer",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
dicts: ['finance_write_off_type'],
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.$emit("update:visible", false);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.drawer-body {
|
||||
padding: 20px;
|
||||
height: calc(100vh - 80px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.details-container {
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
background-color: #f8f8f9;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="核销序号" prop="writeOffCode">
|
||||
<el-input
|
||||
v-model="queryParams.writeOffCode"
|
||||
placeholder="请输入核销序号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="销售-应收单编号" prop="receivableBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.receivableBillCode"
|
||||
placeholder="请输入销售应收单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="销售-开票单编号" prop="invoiceBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.invoiceBillCode"
|
||||
placeholder="请输入销售开票单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="进货商名称" prop="partnerName">
|
||||
<el-input
|
||||
v-model="queryParams.partnerName"
|
||||
placeholder="请输入进货商名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="核销类型" prop="writeOffType">
|
||||
<el-select v-model="queryParams.writeOffType" placeholder="请选择核销类型" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.finance_write_off_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="核销时间">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
>反核销</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="writeOffList" @selection-change="handleSelectionChange" row-key="id">
|
||||
<el-table-column type="selection" width="55" align="center" :reserve-selection="true"/>
|
||||
<el-table-column label="核销序号" align="center" prop="writeOffCode" />
|
||||
<el-table-column label="核销人" align="center" prop="createByName" >
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.createByName || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="核销时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="销售开票单编号" align="center" prop="invoiceBillCode" />
|
||||
<el-table-column label="代理商名称" align="center" prop="partnerName" />
|
||||
<el-table-column label="核销类型" align="center" prop="writeOffType">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.finance_write_off_type" :value="scope.row.writeOffType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="核销含税总价(元)" align="center" prop="writeOffAmount" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="核销未税总价(元)" align="center" prop="writeOffAmountWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
|
||||
<el-table-column label="核销税额(元)" align="center" prop="writeOffTaxAmount" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-view"
|
||||
@click="handleDetail(scope.row)"
|
||||
>查看详情</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
>反核销</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 详情抽屉 -->
|
||||
<write-off-detail-drawer :visible.sync="open" :detail="form" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listInvoiceWriteOff, getInvoiceWriteoff, delInvoiceWriteoff } from "@/api/finance/invoiceWriteoff";
|
||||
import { listPartner } from "@/api/system/partner";
|
||||
import WriteOffDetailDrawer from "./WriteOffDetailDrawer";
|
||||
|
||||
export default {
|
||||
name: "WriteOffInvoiceRecord",
|
||||
components: {
|
||||
WriteOffDetailDrawer
|
||||
},
|
||||
dicts: ['finance_write_off_type'],
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 表格数据
|
||||
writeOffList: [],
|
||||
// 代理商选项
|
||||
partnerOptions: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 日期范围
|
||||
dateRange: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
writeOffCode: null,
|
||||
receivableBillCode: null,
|
||||
invoiceBillCode: null,
|
||||
partnerName: null,
|
||||
writeOffType: null,
|
||||
orderByColumn:'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
// 表单参数
|
||||
form: {}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
this.getPartnerList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listInvoiceWriteOff(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
|
||||
this.writeOffList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 查询代理商列表 */
|
||||
getPartnerList() {
|
||||
listPartner().then(response => {
|
||||
this.partnerOptions = response.rows;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 查看详情 */
|
||||
handleDetail(row) {
|
||||
this.loading = true;
|
||||
getInvoiceWriteoff(row.id).then(response => {
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "核销详情";
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.id)
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 反核销按钮操作 */
|
||||
handleDelete(row) {
|
||||
const ids = row.id || this.ids;
|
||||
this.$modal.confirm('是否确认反核销核销序号为"' + ids + '"的数据项?').then(function() {
|
||||
return delInvoiceWriteoff(ids);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("反核销成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<el-drawer
|
||||
title="核销详情"
|
||||
:visible.sync="visible"
|
||||
direction="rtl"
|
||||
size="60%"
|
||||
@close="handleClose"
|
||||
>
|
||||
<div class="drawer-body" v-if="detail">
|
||||
<div class="section">
|
||||
<el-divider content-position="left">付款单信息</el-divider>
|
||||
<el-table :data="[detail]" border stripe>
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="writeOffAmount" label="本次核销含税总价(元)" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column prop="vendorName" label="制造商名称" align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="paymentBillCode" label="采购付款单编号" align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="paymentCreateTime" label="付款单生成时间" align="center" width="160">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.paymentBill.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="paymentBillType" label="付款单类型" align="center">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.payment_bill_type" :value="scope.row.paymentBill.paymentBillType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="section"> <el-divider content-position="left">应付单明细</el-divider>
|
||||
<el-table :data="detail.detailList" border stripe>
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="paymentAmount" label="本次核销含税总价(元)" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
|
||||
<el-table-column prop="vendorName" label="制造商名称" align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="payableBillCode" label="采购应付单编号" align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="应付单生成时间" align="center"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "WriteOffDetailDrawer",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
dicts: ['finance_write_off_type', 'payment_bill_type'],
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.$emit("update:visible", false);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.drawer-body {
|
||||
padding: 20px;
|
||||
height: calc(100vh - 80px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.details-container {
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
background-color: #f8f8f9;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,567 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 顶部按钮工具栏 -->
|
||||
<div style="margin-bottom: 20px;">
|
||||
<el-button type="primary" icon="el-icon-search" size="small" @click="handleOpenPayableFilter">应付单过滤
|
||||
</el-button>
|
||||
<el-button type="primary" icon="el-icon-search" size="small" @click="handleOpenPaymentFilter">付款单过滤
|
||||
</el-button>
|
||||
<el-button type="success" icon="el-icon-check" size="small" @click="handleManualWriteOff">手工匹配核销</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 应付单过滤弹窗 -->
|
||||
<el-dialog title="应付单查询条件" :visible.sync="payableFilterVisible" width="500px" append-to-body>
|
||||
<el-form :model="queryParams" ref="payableQueryForm" size="small" label-width="120px">
|
||||
<el-form-item label="制造商名称" prop="vendorName">
|
||||
<el-select v-model="queryParams.vendorName" placeholder="请选择制造商名称" filterable clearable
|
||||
style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in vendorList"
|
||||
:key="item.vendorCode"
|
||||
:label="item.vendorName"
|
||||
:value="item.vendorName"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="采购应付单编号" prop="payableBillCode">
|
||||
<el-input v-model="queryParams.payableBillCode" placeholder="请输入采购应付单编号" clearable
|
||||
@keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="应付单生成时间">
|
||||
<el-date-picker
|
||||
v-model="payableDateRange"
|
||||
style="width: 100%"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button icon="el-icon-refresh" size="small" @click="resetPayableQuery">重 置</el-button>
|
||||
<el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">查 询</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 付款单过滤弹窗 -->
|
||||
<el-dialog title="付款单查询条件" :visible.sync="paymentFilterVisible" width="500px" append-to-body>
|
||||
<el-form :model="queryParams" ref="paymentQueryForm" size="small" label-width="120px">
|
||||
<el-form-item label="制造商名称" prop="vendorName">
|
||||
<el-select v-model="queryParams.vendorName" placeholder="请选择制造商名称" filterable clearable
|
||||
style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in vendorList"
|
||||
:key="item.vendorCode"
|
||||
:label="item.vendorName"
|
||||
:value="item.vendorName"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="采购付款单编号" prop="paymentCode">
|
||||
<el-input v-model="queryParams.paymentCode" placeholder="请输入采购付款单编号" clearable
|
||||
@keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="付款单生成时间">
|
||||
<el-date-picker
|
||||
v-model="paymentDateRange"
|
||||
style="width: 100%"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button icon="el-icon-refresh" size="small" @click="resetPaymentQuery">重 置</el-button>
|
||||
<el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">查 询</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<div v-if="showTables">
|
||||
<!-- 采购应付单表格 -->
|
||||
<h3>采购应付单</h3>
|
||||
<div class="table-summary" style="margin-bottom: 10px;">
|
||||
<span style="font-weight: bold; margin-right: 20px;">应付单本次核销总额: <span
|
||||
style="color: #409EFF">{{ formatCurrency(totalPayableWriteOffAmount.toFixed(2)) }}</span></span>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="loadingPayable"
|
||||
:data="payableList"
|
||||
border
|
||||
@selection-change="handlePayableSelectionChange"
|
||||
ref="payableTable"
|
||||
row-key="id"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center"/>
|
||||
<el-table-column label="本次核销金额(元)" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.currentWriteOffAmount"
|
||||
placeholder="点击选择计划"
|
||||
readonly
|
||||
@click.native="handleOpenPlanSelector(scope.row)"
|
||||
style="cursor: pointer;"
|
||||
>
|
||||
<i slot="suffix" class="el-input__icon el-icon-search"></i>
|
||||
</el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="未核销含税总价(元)" align="center" prop="unpaidPaymentAmount" width="150"/>
|
||||
<el-table-column label="未核销未税总价(元)" align="center" prop="unpaidAmount" width="150">
|
||||
<template slot-scope="scope">
|
||||
{{ calculateExcludingTax(scope.row.unpaidPaymentAmount, scope.row.taxRate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="未核销税额(元)" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
{{ calculateTax(scope.row.unpaidPaymentAmount, scope.row.taxRate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="核销中含税总价(元)" align="center" prop="paidAmount" width="150">
|
||||
<template slot-scope="scope">
|
||||
{{
|
||||
formatCurrency($calc.sub($calc.sub(scope.row.totalPriceWithTax, scope.row.paidPaymentAmount), scope.row.unpaidPaymentAmount))
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已核销含税总价(元)" align="center" prop="paidPaymentAmount" width="150" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已核销未税总价(元)" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency(calculateExcludingTax(scope.row.paidPaymentAmount, scope.row.taxRate)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已核销税额(元)" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency(calculateTax(scope.row.paidPaymentAmount, scope.row.taxRate)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" width="120" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="制造商名称" fixed="right" align="center" prop="vendorName" width="150"
|
||||
show-overflow-tooltip/>
|
||||
<el-table-column label="采购应付单编号" fixed="right" align="center" prop="payableBillCode" width="150"
|
||||
show-overflow-tooltip/>
|
||||
<el-table-column label="生成时间" fixed="right" align="center" prop="createTime" width="160">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="payableTotal>0"
|
||||
:total="payableTotal"
|
||||
:pageSizes="[5,10,20,50]"
|
||||
:page.sync="payableQueryParams.pageNum"
|
||||
:limit.sync="payableQueryParams.pageSize"
|
||||
@pagination="getPayableList"
|
||||
/>
|
||||
|
||||
<!-- 采购付款单表格 -->
|
||||
<h3 style="margin-top: 30px;">采购付款单</h3>
|
||||
<div class="table-summary" style="margin-bottom: 10px;">
|
||||
<span style="font-weight: bold; margin-right: 20px;display: none">付款单本次核销总额: <span
|
||||
:style="{color: isWriteOffAmountValid ? '#67C23A' : '#F56C6C'}">{{
|
||||
totalPaymentWriteOffAmount.toFixed(2)
|
||||
}}</span></span>
|
||||
<span v-if="!isWriteOffAmountValid" style="color: #F56C6C; font-size: 12px;"><i class="el-icon-warning"></i> 总价之和不能大于应付单本次核销总价之和</span>
|
||||
</div>
|
||||
<el-table v-loading="loadingPayment" :data="paymentList" border highlight-current-row
|
||||
@current-change="handlePaymentSelectionChange" row-key="id">
|
||||
<el-table-column width="55" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-radio v-model="selectedPaymentId" :label="scope.row.paymentBillCode"><i></i></el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<el-table-column label="本次核销含税总价(元)" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.currentWriteOffAmount"
|
||||
:precision="2"
|
||||
:step="100"
|
||||
:min="0"
|
||||
:max="scope.row.unWrittenAmount || scope.row.paymentAmount"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
:disabled="selectedPaymentId !== scope.row.paymentBillCode"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="未核销含税总价(元)" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
{{ formatCurrency(scope.row.preResidueAmount || '0.00') }} <!-- Assuming paymentAmount is total initially -->
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已核销含税总价(元)" align="center" prop="writeOffAmount" width="150" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已核销未税总价(元)" align="center" width="150" prop="writeOffAmountWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="已核销税额(元)" align="center" width="150" prop="writeOffTaxAmount" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
|
||||
<el-table-column label="含税总价(元)" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
|
||||
<el-table-column label="制造商名称" fixed="right" align="center" prop="vendorName" width="150"
|
||||
show-overflow-tooltip/>
|
||||
<el-table-column label="采购付款单号" fixed="right" align="center" prop="paymentBillCode" width="150"
|
||||
show-overflow-tooltip/>
|
||||
<el-table-column label="生成时间" fixed="right" align="center" prop="createTime" width="160">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="paymentTotal>0"
|
||||
:total="paymentTotal"
|
||||
:page.sync="paymentQueryParams.pageNum"
|
||||
:limit.sync="paymentQueryParams.pageSize"
|
||||
@pagination="getPaymentList"
|
||||
:pageSizes="[5,10,20,50]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 弹窗:选择应付计划 -->
|
||||
<el-dialog :title="planDialogTitle" :visible.sync="planDialogVisible" width="70%" append-to-body>
|
||||
<payment-plan-selector
|
||||
v-if="planDialogVisible"
|
||||
ref="planSelector"
|
||||
:payable-data="currentPayableRow"
|
||||
:selected-plans="currentPayableRow ? currentPayableRow.selectedPlans : []"
|
||||
:is-init-edit="false"
|
||||
/>
|
||||
<!-- Note: is-init-edit=false makes it read-only for editing plans generally, but we want to SELECT.
|
||||
PaymentPlanSelector logic: 'selection-change' works.
|
||||
If PaymentPlanSelector allows selection only in some mode, we need to check.
|
||||
Looking at PaymentPlan.vue: <el-table ... @selection-change="selectPlan">
|
||||
It seems selection is always enabled.
|
||||
-->
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="planDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="handlePlanSelectionConfirm">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {listPayable} from "@/api/finance/payable";
|
||||
import {listPaymentForWriteOff, manualWriteOff} from "@/api/finance/payment";
|
||||
import {listAllVendors} from "@/api/inventory/info";
|
||||
import PaymentPlanSelector from "../../payable/components/PaymentPlan.vue";
|
||||
|
||||
export default {
|
||||
name: "ManualWriteOff",
|
||||
components: {PaymentPlanSelector},
|
||||
dicts: ['payment_status', 'payment_bill_type'],
|
||||
data() {
|
||||
return {
|
||||
// 弹窗控制
|
||||
payableFilterVisible: false,
|
||||
paymentFilterVisible: false,
|
||||
// 遮罩层
|
||||
loadingPayable: false,
|
||||
loadingPayment: false,
|
||||
// 显示表格
|
||||
showTables: false,
|
||||
// 搜索表单
|
||||
queryParams: {
|
||||
vendorName: undefined,
|
||||
payableBillCode: undefined,
|
||||
paymentCode: undefined,
|
||||
},
|
||||
payableDateRange: [],
|
||||
paymentDateRange: [],
|
||||
vendorList: [], // 供应商列表
|
||||
// 应付单数据
|
||||
payableList: [],
|
||||
payableTotal: 0,
|
||||
payableQueryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 5,
|
||||
vendorName: undefined,
|
||||
payableBillCode: undefined,
|
||||
unpaidPaymentAmount: -1,
|
||||
params: {}
|
||||
},
|
||||
selectedPayables: [], // 选中的应付单
|
||||
// 付款单数据
|
||||
paymentList: [],
|
||||
paymentTotal: 0,
|
||||
paymentQueryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 5,
|
||||
vendorName: undefined,
|
||||
paymentCode: undefined,
|
||||
paymentBillType: 'PRE_PAYMENT', // 默认查询条件
|
||||
paymentStatus: '2',
|
||||
params: {}
|
||||
},
|
||||
selectedPaymentId: null, // 选中的付款单ID (目前使用 BillCode)
|
||||
selectedPaymentRow: null, // 选中的付款单行
|
||||
|
||||
// 弹窗相关
|
||||
planDialogVisible: false,
|
||||
currentPayableRow: null,
|
||||
planDialogTitle: "选择应付计划"
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
totalPayableWriteOffAmount(newVal) {
|
||||
this.updatePaymentWriteOffAmount();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalPayableWriteOffAmount() {
|
||||
return this.selectedPayables.reduce((sum, row) => sum + (row.currentWriteOffAmount || 0), 0);
|
||||
},
|
||||
totalPaymentWriteOffAmount() {
|
||||
if (!this.selectedPaymentRow) return 0;
|
||||
return this.selectedPaymentRow.currentWriteOffAmount || 0;
|
||||
},
|
||||
isWriteOffAmountValid() {
|
||||
// use a small epsilon for float comparison if needed, but direct comparison is usually okay for UI validation
|
||||
return this.totalPaymentWriteOffAmount <= this.totalPayableWriteOffAmount + 0.01;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getVendorList();
|
||||
this.payableFilterVisible = true; // 首次进入自动弹出应付过滤
|
||||
},
|
||||
methods: {
|
||||
getVendorList() {
|
||||
listAllVendors().then(response => {
|
||||
// 假设接口返回 { data: [...] } 或直接数组,根据之前 search_file 看到的 controller 是返回 AjaxResult,通常是 .data 或 .rows
|
||||
// Ruoyi 风格通常是 response.data
|
||||
this.vendorList = response.data || [];
|
||||
});
|
||||
},
|
||||
handleOpenPayableFilter() {
|
||||
this.payableFilterVisible = true;
|
||||
},
|
||||
handleOpenPaymentFilter() {
|
||||
this.paymentFilterVisible = true;
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
if (!this.queryParams.vendorName) {
|
||||
this.$modal.msgError("请选择制造商名称");
|
||||
return;
|
||||
}
|
||||
|
||||
this.payableFilterVisible = false;
|
||||
this.paymentFilterVisible = false;
|
||||
this.showTables = true;
|
||||
this.payableQueryParams.pageNum = 1;
|
||||
this.paymentQueryParams.pageNum = 1;
|
||||
|
||||
this.syncParams();
|
||||
|
||||
this.getPayableList();
|
||||
this.getPaymentList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetPayableQuery() {
|
||||
this.payableDateRange = [];
|
||||
this.queryParams.vendorName = undefined;
|
||||
this.queryParams.payableBillCode = undefined;
|
||||
},
|
||||
resetPaymentQuery() {
|
||||
this.paymentDateRange = [];
|
||||
this.queryParams.vendorName = undefined;
|
||||
this.queryParams.paymentCode = undefined;
|
||||
},
|
||||
syncParams() {
|
||||
this.payableQueryParams.vendorName = this.queryParams.vendorName;
|
||||
this.payableQueryParams.payableBillCode = this.queryParams.payableBillCode;
|
||||
this.payableQueryParams.params = {};
|
||||
if (null != this.payableDateRange && '' != this.payableDateRange) {
|
||||
this.payableQueryParams.createTimeStart = this.payableDateRange[0];
|
||||
this.payableQueryParams.createTimeEnd = this.payableDateRange[1];
|
||||
}
|
||||
|
||||
this.paymentQueryParams.vendorName = this.queryParams.vendorName;
|
||||
this.paymentQueryParams.paymentCode = this.queryParams.paymentCode;
|
||||
this.paymentQueryParams.paymentBillType = 'PRE_PAYMENT'; // 确保每次查询都带上
|
||||
this.paymentQueryParams.params = {};
|
||||
if (null != this.paymentDateRange && '' != this.paymentDateRange) {
|
||||
this.paymentQueryParams.params["beginTime"] = this.paymentDateRange[0];
|
||||
this.paymentQueryParams.params["endTime"] = this.paymentDateRange[1];
|
||||
}
|
||||
},
|
||||
getPayableList() {
|
||||
this.loadingPayable = true;
|
||||
listPayable(this.payableQueryParams).then(response => {
|
||||
this.payableList = response.rows.map(item => ({
|
||||
...item,
|
||||
currentWriteOffAmount: 0, // Initialize
|
||||
selectedPlans: [] // Initialize
|
||||
}));
|
||||
this.payableTotal = response.total;
|
||||
this.loadingPayable = false;
|
||||
// 清空选中
|
||||
this.$refs.payableTable.clearSelection();
|
||||
this.selectedPayables = [];
|
||||
});
|
||||
},
|
||||
getPaymentList() {
|
||||
this.loadingPayment = true;
|
||||
listPaymentForWriteOff(this.paymentQueryParams).then(response => {
|
||||
this.paymentList = response.rows.map(item => ({
|
||||
...item,
|
||||
currentWriteOffAmount: 0, // Initialize
|
||||
// Mocking fields not yet in standard listPayment (or assuming defaults)
|
||||
unWrittenAmount: item.paymentAmount - (item.writtenAmount || 0),
|
||||
writtenAmount: item.writtenAmount || 0
|
||||
}));
|
||||
this.paymentTotal = response.total;
|
||||
this.loadingPayment = false;
|
||||
// 清空选中
|
||||
this.selectedPaymentId = null;
|
||||
this.selectedPaymentRow = null;
|
||||
});
|
||||
},
|
||||
handlePayableSelectionChange(selection) {
|
||||
this.selectedPayables = selection;
|
||||
},
|
||||
handlePaymentSelectionChange(currentRow) {
|
||||
if (currentRow) {
|
||||
this.selectedPaymentId = currentRow.paymentBillCode;
|
||||
this.selectedPaymentRow = currentRow;
|
||||
// 选中时触发同步
|
||||
this.updatePaymentWriteOffAmount();
|
||||
} else {
|
||||
this.selectedPaymentId = null;
|
||||
this.selectedPaymentRow = null;
|
||||
}
|
||||
},
|
||||
|
||||
// Logic for Plan Selection
|
||||
handleOpenPlanSelector(row) {
|
||||
this.currentPayableRow = row;
|
||||
this.planDialogTitle = `选择应付计划 - ${row.payableBillCode}`;
|
||||
this.planDialogVisible = true;
|
||||
},
|
||||
handlePlanSelectionConfirm() {
|
||||
if (this.$refs.planSelector && this.$refs.planSelector.selectedPlan) {
|
||||
const selected = this.$refs.planSelector.selectedPlan;
|
||||
this.currentPayableRow.selectedPlans = selected;
|
||||
// Calculate sum
|
||||
const sum = selected.reduce((acc, plan) => acc + (plan.planAmount || 0), 0);
|
||||
this.currentPayableRow.currentWriteOffAmount = sum;
|
||||
|
||||
this.$modal.msgSuccess(`已选择 ${selected.length} 个计划,总金额 ${sum.toFixed(2)}`);
|
||||
|
||||
// 1. 默认选中该笔应付单
|
||||
if (selected.length > 0) {
|
||||
this.$refs.payableTable.toggleRowSelection(this.currentPayableRow, true);
|
||||
} else {
|
||||
this.$refs.payableTable.toggleRowSelection(this.currentPayableRow, false);
|
||||
}
|
||||
}
|
||||
this.planDialogVisible = false;
|
||||
},
|
||||
|
||||
// Tax Calculations
|
||||
calculateTax(amount, taxRate) {
|
||||
let calculateExcludingTax = this.calculateExcludingTax(amount, taxRate);
|
||||
return this.$calc.sub(amount, calculateExcludingTax)
|
||||
},
|
||||
calculateExcludingTax(amount, taxRate) {
|
||||
if (!amount) return '0.00';
|
||||
// Amount / (1 + Rate/100)
|
||||
const rate = taxRate ?? 0.13;
|
||||
return this.$calc.div(amount, this.$calc.add(1, rate))
|
||||
},
|
||||
|
||||
// 更新付款单核销金额逻辑
|
||||
updatePaymentWriteOffAmount() {
|
||||
if (this.selectedPaymentRow) {
|
||||
const payableSum = this.totalPayableWriteOffAmount;
|
||||
const maxAmount = this.selectedPaymentRow.unWrittenAmount || this.selectedPaymentRow.preResidueAmount || 0; // Use preResidueAmount if unWrittenAmount is not reliable or prefered
|
||||
|
||||
if (payableSum > maxAmount) {
|
||||
// 校验:不能大于未核销含税总价
|
||||
this.selectedPaymentRow.currentWriteOffAmount = maxAmount;
|
||||
this.$modal.msgWarning(`应付单核销总额(${payableSum.toFixed(2)})超过付款单剩余额度(${maxAmount}),已自动调整为最大可核销额度。`);
|
||||
} else {
|
||||
this.selectedPaymentRow.currentWriteOffAmount = payableSum;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 手工匹配核销
|
||||
handleManualWriteOff() {
|
||||
if (!this.selectedPaymentRow) {
|
||||
this.$modal.msgError("请选择一个付款单");
|
||||
return;
|
||||
}
|
||||
if (this.selectedPayables.length === 0) {
|
||||
this.$modal.msgError("请至少选择一个应付单");
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验:应付单本次核销总额与付款单本次核销总额必须相等
|
||||
// 使用一个较小的 epsilon 值处理浮点数比较
|
||||
if (Math.abs(this.totalPaymentWriteOffAmount - this.totalPayableWriteOffAmount) > 0.01) {
|
||||
this.$modal.msgError(`付款单核销金额(${this.totalPaymentWriteOffAmount.toFixed(2)})必须等于应付单核销总额(${this.totalPayableWriteOffAmount.toFixed(2)})`);
|
||||
return;
|
||||
}
|
||||
|
||||
const detailList = [];
|
||||
this.selectedPayables.forEach(payable => {
|
||||
if (payable.selectedPlans && payable.selectedPlans.length > 0) {
|
||||
payable.selectedPlans.forEach(plan => {
|
||||
const amount = plan.planAmount;
|
||||
detailList.push({
|
||||
paymentPlanId: plan.id,
|
||||
payableBillId: payable.id,
|
||||
paymentBillCode: this.selectedPaymentRow.paymentBillCode,
|
||||
paymentAmount: amount,
|
||||
paymentRate: plan.planRate,
|
||||
paymentAmountWithoutTax: this.calculateExcludingTax(amount, payable.taxRate),
|
||||
paymentAmountTax: this.calculateTax(amount, payable.taxRate)
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (detailList.length === 0) {
|
||||
this.$modal.msgError("请为选中的应付单选择计划(点击放大镜图标)");
|
||||
return;
|
||||
}
|
||||
|
||||
let vendorCode = this.selectedPaymentRow.vendorCode;
|
||||
// Fallback for vendorCode if not in row but in list
|
||||
if (!vendorCode && this.selectedPaymentRow.vendorName) {
|
||||
const vendor = this.vendorList.find(v => v.vendorName === this.selectedPaymentRow.vendorName);
|
||||
if (vendor) vendorCode = vendor.vendorCode;
|
||||
}
|
||||
|
||||
const data = {
|
||||
paymentBillId: this.selectedPaymentRow.paymentBillId || this.selectedPaymentRow.id,
|
||||
vendorCode: vendorCode,
|
||||
detailList: detailList
|
||||
};
|
||||
|
||||
manualWriteOff(data).then(response => {
|
||||
this.$modal.msgSuccess("核销成功");
|
||||
this.handleQuery();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.table-summary {
|
||||
background-color: #f8f8f9;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ebeef5;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form-item label="核销序号" prop="writeOffCode">
|
||||
<el-input
|
||||
v-model="queryParams.writeOffCode"
|
||||
placeholder="请输入核销序号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="采购应付单编号" prop="payableBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.payableBillCode"
|
||||
placeholder="请输入采购应付单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="采购付款单编号" prop="paymentBillCode">
|
||||
<el-input
|
||||
v-model="queryParams.paymentBillCode"
|
||||
placeholder="请输入采购付款单编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="制造商名称" prop="vendorName">
|
||||
<el-select v-model="queryParams.vendorName" placeholder="请选择制造商名称" clearable filterable>
|
||||
<el-option
|
||||
v-for="item in vendorOptions"
|
||||
:key="item.vendorName"
|
||||
:label="item.vendorName"
|
||||
:value="item.vendorName"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="核销类型" prop="writeOffType">
|
||||
<el-select v-model="queryParams.writeOffType" placeholder="请选择核销类型" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.finance_write_off_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="核销时间">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 240px"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
>反核销</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="writeOffList" @selection-change="handleSelectionChange" row-key="id">
|
||||
<el-table-column type="selection" width="55" align="center" :reserve-selection="true"/>
|
||||
<el-table-column label="核销序号" align="center" prop="writeOffCode" />
|
||||
<el-table-column label="核销人" align="center" prop="createByName" />
|
||||
<el-table-column label="核销时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="采购付款单编号" align="center" prop="paymentBillCode" />
|
||||
<el-table-column label="制造商名称" align="center" prop="vendorName" />
|
||||
<el-table-column label="核销类型" align="center" prop="writeOffType">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.finance_write_off_type" :value="scope.row.writeOffType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="核销含税总价(元)" align="center" prop="writeOffAmount" />
|
||||
<el-table-column label="核销未税总价(元)" align="center" prop="writeOffAmountWithoutTax" />
|
||||
<el-table-column label="核销税额(元)" align="center" prop="writeOffTaxAmount" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-view"
|
||||
@click="handleDetail(scope.row)"
|
||||
>查看详情</el-button>
|
||||
<!-- <el-button-->
|
||||
<!-- size="mini"-->
|
||||
<!-- type="text"-->
|
||||
<!-- icon="el-icon-delete"-->
|
||||
<!-- @click="handleDelete(scope.row)"-->
|
||||
<!-- >反核销</el-button>-->
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 详情抽屉 -->
|
||||
<write-off-detail-drawer :visible.sync="open" :detail="form" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listWriteOff, getWriteOff, delWriteOff } from "@/api/finance/writeoff";
|
||||
import { listVendor } from "@/api/base/vendor";
|
||||
import WriteOffDetailDrawer from "./WriteOffDetailDrawer";
|
||||
|
||||
export default {
|
||||
name: "WriteOffRecord",
|
||||
components: {
|
||||
WriteOffDetailDrawer
|
||||
},
|
||||
dicts: ['finance_write_off_type'],
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 表格数据
|
||||
writeOffList: [],
|
||||
// 制造商选项
|
||||
vendorOptions: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 日期范围
|
||||
dateRange: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
writeOffCode: null,
|
||||
payableBillCode: null,
|
||||
paymentBillCode: null,
|
||||
vendorName: null,
|
||||
writeOffType: null,
|
||||
orderByColumn:'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
// 表单参数
|
||||
form: {}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
this.getVendorList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listWriteOff(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
|
||||
this.writeOffList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 查询制造商列表 */
|
||||
getVendorList() {
|
||||
listVendor().then(response => {
|
||||
this.vendorOptions = response.rows;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.dateRange = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 查看详情 */
|
||||
handleDetail(row) {
|
||||
this.loading = true;
|
||||
getWriteOff(row.id).then(response => {
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "核销详情";
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.id)
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 反核销按钮操作 */
|
||||
handleDelete(row) {
|
||||
const ids = row.id || this.ids;
|
||||
this.$modal.confirm('是否确认反核销核销序号为"' + ids + '"的数据项?').then(function() {
|
||||
return delWriteOff(ids);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("反核销成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<template>
|
||||
<el-drawer
|
||||
title="核销详情"
|
||||
:visible.sync="visible"
|
||||
direction="rtl"
|
||||
size="60%"
|
||||
@close="handleClose"
|
||||
>
|
||||
<div class="drawer-body" v-if="detail">
|
||||
<div class="section">
|
||||
<el-divider content-position="left">销售-收款单</el-divider>
|
||||
<el-table :data="[detail]" border stripe>
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="writeOffAmount" label="本次核销含税总价(元)" align="center"></el-table-column>
|
||||
<el-table-column prop="partnerName" label="进货商名称" align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="receiptBillCode" label="销售-收款单编号" align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="receiptCreateTime" label="收款单生成时间" align="center" width="160">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.receiptBill ? scope.row.receiptBill.createTime : '') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="receiptBillType" label="收款单类型" align="center">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.receipt_bill_type" :value="scope.row.receiptBill ? scope.row.receiptBill.receiptBillType : ''"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<el-divider content-position="left">销售-应收单</el-divider>
|
||||
<el-table :data="detail.detailList" border stripe>
|
||||
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
|
||||
<el-table-column prop="receiptAmount" label="本次核销含税总价(元)" align="center"></el-table-column>
|
||||
<el-table-column prop="partnerName" label="进货商名称" align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"
|
||||
show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="应收单生成时间" align="center"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "WriteOffDetailDrawer",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
dicts: ['finance_write_off_type', 'receipt_bill_type'],
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.$emit("update:visible", false);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.drawer-body {
|
||||
padding: 20px;
|
||||
height: calc(100vh - 80px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.details-container {
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
background-color: #f8f8f9;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue