spring
2 天以前 261f2ed00235d47df3754291a4fdca9ba5cb8e7a
fix: 合并财务数据
已添加7个文件
已修改20个文件
已删除28个文件
15186 ■■■■■ 文件已修改
src/api/financialManagement/accountPaymentApplication.js 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/accountPurchaseInvoice.js 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/accountPurchasePayment.js 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/accountSalesCollection.js 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/accountSalesInvoice.js 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/accountStatement.js 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/expenseManagement.js 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/invoiceApply.js 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/loanManagement.js 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/revenueManagement.js 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/salesRefund.js 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/invoiceEntry.js 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/paymentEntry.js 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/paymentLedger.js 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/procurementInvoiceLedger.js 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/salesManagement/indicatorStats.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/salesManagement/invoiceLedger.js 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/salesManagement/invoiceRegistration.js 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/salesManagement/receiptPayment.js 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/viewIndex.js 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/user.js 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/expenseManagement/Modal.vue 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/expenseManagement/index.vue 332 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/loanManagement/Modal.vue 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/loanManagement/index.vue 278 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/input-invoice.vue 918 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/payment.vue 562 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/paymentApply.vue 970 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/purchaseIn.vue 283 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/purchaseReturn.vue 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/reconciliation.vue 613 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/invoiceApply.vue 750 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/outputInvoice.vue 489 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/receipt.vue 1063 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/reconciliation.vue 563 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/revenueManagement/Modal.vue 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/revenueManagement/index.vue 355 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/salesRefund/components/ReceiptandRefundPopupWindow.vue 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/salesRefund/index.vue 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/invoiceEntry/components/ExpandTable.vue 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/invoiceEntry/components/Modal.vue 718 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/invoiceEntry/index.vue 311 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentEntry/index.vue 597 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentHistory/index.vue 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentLedger/index.vue 494 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementInvoiceLedger/Modal/EditModal.vue 238 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementInvoiceLedger/Modal/UploadModal.vue 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementInvoiceLedger/index.vue 330 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/safetyTrainingAssessment/detail.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/invoiceLedger/index.vue 438 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/invoiceRegistration/index.vue 814 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/receiptPayment/index.vue 605 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/receiptPaymentHistory/index.vue 279 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/receiptPaymentLedger/index.vue 486 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/accountPaymentApplication.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
import request from "@/utils/request";
/** æ ¹æ®ä¾›åº”商查询可关联入库单 */
export function getInboundBatchesBySupplier(params) {
  return request({
    url: "/accountPaymentApplication/getInboundBatchesBySupplier",
    method: "get",
    params,
  });
}
/** æ–°å¢žä»˜æ¬¾ç”³è¯· */
export function addAccountPaymentApplication(data) {
  return request({
    url: "/accountPaymentApplication/addAccountPaymentApplication",
    method: "post",
    data,
  });
}
/** ä»˜æ¬¾ç”³è¯·åˆ†é¡µåˆ—表 */
export function listPageAccountPaymentApplication(params) {
  return request({
    url: "/accountPaymentApplication/listPageAccountPaymentApplication",
    method: "get",
    params,
  });
}
/** ä¿®æ”¹ä»˜æ¬¾ç”³è¯· */
export function updateAccountPaymentApplication(data) {
  return request({
    url: "/accountPaymentApplication/updateAccountPaymentApplication",
    method: "put",
    data,
  });
}
/** å®¡æ ¸ä»˜æ¬¾ç”³è¯· */
export function auditAccountPaymentApplication(data) {
  return request({
    url: "/accountPaymentApplication/auditAccountPaymentApplication",
    method: "put",
    data,
  });
}
/** åˆ é™¤ä»˜æ¬¾ç”³è¯·ï¼ˆSpring è¦æ±‚ ids=1&ids=2 æŸ¥è¯¢å‚数) */
export function deleteAccountPaymentApplication(ids) {
  const idList = Array.isArray(ids) ? ids : [ids];
  const query = idList
    .filter((id) => id !== undefined && id !== null && id !== "")
    .map((id) => `ids=${encodeURIComponent(id)}`)
    .join("&");
  return request({
    url: `/accountPaymentApplication/deleteAccountPaymentApplication?${query}`,
    method: "delete",
  });
}
src/api/financialManagement/accountPurchaseInvoice.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
import request from "@/utils/request";
/** æ ¹æ®ä¾›åº”商查询可关联入库单 */
export function getInboundBatchesBySupplier(params) {
  return request({
    url: "/accountPurchaseInvoice/getInboundBatchesBySupplier",
    method: "get",
    params,
  });
}
/** æ–°å¢žè¿›é¡¹å‘票 */
export function addAccountPurchaseInvoice(data) {
  return request({
    url: "/accountPurchaseInvoice/addAccountPurchaseInvoice",
    method: "post",
    data,
  });
}
/** è¿›é¡¹å‘票分页列表 */
export function listPageAccountPurchaseInvoice(params) {
  return request({
    url: "/accountPurchaseInvoice/listPageAccountPurchaseInvoice",
    method: "get",
    params,
  });
}
/** ä½œåºŸè¿›é¡¹å‘票 */
export function cancelAccountPurchaseInvoice(data) {
  return request({
    url: "/accountPurchaseInvoice/cancelAccountPurchaseInvoice",
    method: "put",
    data,
  });
}
/** åˆ é™¤è¿›é¡¹å‘票(Spring è¦æ±‚ ids=1&ids=2 æŸ¥è¯¢å‚数) */
export function deleteAccountPurchaseInvoice(ids) {
  const idList = Array.isArray(ids) ? ids : [ids];
  const query = idList
    .filter((id) => id !== undefined && id !== null && id !== "")
    .map((id) => `ids=${encodeURIComponent(id)}`)
    .join("&");
  return request({
    url: `/accountPurchaseInvoice/deleteAccountPurchaseInvoice?${query}`,
    method: "delete",
  });
}
src/api/financialManagement/accountPurchasePayment.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
import request from "@/utils/request";
/** æ–°å¢žä»˜æ¬¾å•(关联付款申请) */
export function addAccountPurchasePayment(data) {
  return request({
    url: "/accountPurchasePayment/addAccountPurchasePayment",
    method: "post",
    data,
  });
}
/** ä»˜æ¬¾å•分页列表 */
export function listPageAccountPurchasePayment(params) {
  return request({
    url: "/accountPurchasePayment/listPageAccountPurchasePayment",
    method: "get",
    params,
  });
}
/** åˆ é™¤ä»˜æ¬¾å•(Spring è¦æ±‚ ids=1&ids=2 æŸ¥è¯¢å‚数) */
export function deleteAccountPurchasePayment(ids) {
  const idList = Array.isArray(ids) ? ids : [ids];
  const query = idList
    .filter((id) => id !== undefined && id !== null && id !== "")
    .map((id) => `ids=${encodeURIComponent(id)}`)
    .join("&");
  return request({
    url: `/accountPurchasePayment/deleteAccountPurchasePayment?${query}`,
    method: "delete",
  });
}
src/api/financialManagement/accountSalesCollection.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
import request from "@/utils/request";
/** æ ¹æ®å®¢æˆ·æŸ¥è¯¢å¯å…³è”出库单 */
export function getOutboundBatchesByCustomer(params) {
  return request({
    url: "/accountSalesCollection/getOutboundBatchesByCustomer",
    method: "get",
    params,
  });
}
/** æ–°å¢žæ”¶æ¬¾å• */
export function addAccountSalesCollection(data) {
  return request({
    url: "/accountSalesCollection/addAccountSalesCollection",
    method: "post",
    data,
  });
}
/** æ”¶æ¬¾å•分页列表 */
export function listPageAccountSalesCollection(params) {
  return request({
    url: "/accountSalesCollection/listPageAccountSalesCollection",
    method: "get",
    params,
  });
}
/** ä¿®æ”¹æ”¶æ¬¾å• */
export function updateAccountSalesCollection(data) {
  return request({
    url: "/accountSalesCollection/updateAccountSalesCollection",
    method: "put",
    data,
  });
}
/** åˆ é™¤æ”¶æ¬¾å•(Spring è¦æ±‚ ids=1&ids=2 æŸ¥è¯¢å‚数) */
export function deleteAccountSalesCollection(ids) {
  const idList = Array.isArray(ids) ? ids : [ids];
  const query = idList
    .filter((id) => id !== undefined && id !== null && id !== "")
    .map((id) => `ids=${encodeURIComponent(id)}`)
    .join("&");
  return request({
    url: `/accountSalesCollection/deleteAccountSalesCollection?${query}`,
    method: "delete",
  });
}
src/api/financialManagement/accountSalesInvoice.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
import request from "@/utils/request";
/** æ–°å¢žé”€é¡¹å‘票 */
export function addAccountSalesInvoice(data) {
  return request({
    url: "/accountSalesInvoice/addAccountSalesInvoice",
    method: "post",
    data,
  });
}
/** é”€é¡¹å‘票分页列表 */
export function listPageAccountSalesInvoice(params) {
  return request({
    url: "/accountSalesInvoice/listPageAccountSalesInvoice",
    method: "get",
    params,
  });
}
/** ä½œåºŸé”€é¡¹å‘票 */
export function cancelAccountSalesInvoice(data) {
  return request({
    url: "/accountSalesInvoice/cancelAccountSalesInvoice",
    method: "put",
    data,
  });
}
/** åˆ é™¤é”€é¡¹å‘票(Spring è¦æ±‚ ids=1&ids=2 æŸ¥è¯¢å‚数) */
export function deleteAccountSalesInvoice(ids) {
  const idList = Array.isArray(ids) ? ids : [ids];
  const query = idList
    .filter((id) => id !== undefined && id !== null && id !== "")
    .map((id) => `ids=${encodeURIComponent(id)}`)
    .join("&");
  return request({
    url: `/accountSalesInvoice/deleteAccountSalesInvoice?${query}`,
    method: "delete",
  });
}
src/api/financialManagement/accountStatement.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
import request from "@/utils/request";
/** æŒ‰æœˆä»½æŸ¥è¯¢å¯¹è´¦å•明细(生成前预览) */
export function getAccountStatementDetailsByMonth(params) {
  return request({
    url: "/accountStatement/getAccountStatementDetailsByMonth",
    method: "get",
    params,
  });
}
/** æ–°å¢žå¯¹è´¦å• */
export function addAccountStatement(data) {
  return request({
    url: "/accountStatement/addAccountStatement",
    method: "post",
    data,
  });
}
/** å¯¹è´¦å•分页列表 */
export function listPageAccountStatement(params) {
  return request({
    url: "/accountStatement/listPageAccountStatement",
    method: "get",
    params,
  });
}
/** åˆ é™¤å¯¹è´¦å•(Spring è¦æ±‚ ids=1&ids=2 æŸ¥è¯¢å‚数) */
export function deleteAccountStatement(ids) {
  const idList = Array.isArray(ids) ? ids : [ids];
  const query = idList
    .filter((id) => id !== undefined && id !== null && id !== "")
    .map((id) => `ids=${encodeURIComponent(id)}`)
    .join("&");
  return request({
    url: `/accountStatement/deleteAccountStatement?${query}`,
    method: "delete",
  });
}
src/api/financialManagement/expenseManagement.js
ÎļþÒÑɾ³ý
src/api/financialManagement/invoiceApply.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
import request from "@/utils/request";
/** æ ¹æ®å®¢æˆ·æŸ¥è¯¢å¯å¼€ç¥¨å‡ºåº“单号列表 */
export function getOutboundBatchesByCustomer(params) {
  return request({
    url: "/accountInvoiceApplication/getOutboundBatchesByCustomer",
    method: "get",
    params,
  });
}
/** æ–°å¢žå¼€ç¥¨ç”³è¯· */
export function addAccountInvoiceApplication(data) {
  return request({
    url: "/accountInvoiceApplication/addAccountInvoiceApplication",
    method: "post",
    data,
  });
}
/** å¼€ç¥¨ç”³è¯·åˆ†é¡µåˆ—表 */
export function listPageAccountInvoiceApplication(params) {
  return request({
    url: "/accountInvoiceApplication/listPageAccountInvoiceApplication",
    method: "get",
    params,
  });
}
/** å¼€ç¥¨ç”³è¯·å®¡æ‰¹ */
export function auditAccountInvoiceApplication(data) {
  return request({
    url: "/accountInvoiceApplication/auditAccountInvoiceApplication",
    method: "put",
    data,
  });
}
/** ä¿®æ”¹å¼€ç¥¨ç”³è¯· */
export function updateAccountInvoiceApplication(data) {
  return request({
    url: "/accountInvoiceApplication/updateAccountInvoiceApplication",
    method: "put",
    data,
  });
}
/** åˆ é™¤å¼€ç¥¨ç”³è¯·ï¼ˆSpring è¦æ±‚ ids=1&ids=2 æŸ¥è¯¢å‚数) */
export function deleteAccountInvoiceApplication(ids) {
  const idList = Array.isArray(ids) ? ids : [ids];
  const query = idList
    .filter((id) => id !== undefined && id !== null && id !== "")
    .map((id) => `ids=${encodeURIComponent(id)}`)
    .join("&");
  return request({
    url: `/accountInvoiceApplication/deleteAccountInvoiceApplication?${query}`,
    method: "delete",
  });
}
src/api/financialManagement/loanManagement.js
ÎļþÒÑɾ³ý
src/api/financialManagement/revenueManagement.js
ÎļþÒÑɾ³ý
src/api/financialManagement/salesRefund.js
ÎļþÒÑɾ³ý
src/api/procurementManagement/invoiceEntry.js
ÎļþÒÑɾ³ý
src/api/procurementManagement/paymentEntry.js
ÎļþÒÑɾ³ý
src/api/procurementManagement/paymentLedger.js
@@ -1,20 +1,20 @@
// é‡‡è´­å°è´¦é¡µé¢æŽ¥å£
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
/** ä»˜æ¬¾å°è´¦ - ä¾›åº”商往来汇总 */
export function paymentLedgerList(query) {
  return request({
    url: "/purchase/paymentRegistration/supplierNameListPage",
    url: "/purchase/report/supplierTransactions",
    method: "get",
    params: query,
  });
}
// åˆ†é¡µæŸ¥è¯¢
export function paymentRecordList(supplierId) {
/** ä»˜æ¬¾å°è´¦ - ä¾›åº”商往来明细 */
export function paymentRecordList(params) {
  return request({
    url: "/purchase/paymentRegistration/supplierNameListPageDetails",
    url: "/purchase/report/supplierTransactionsDetails",
    method: "get",
    params: supplierId,
    params,
  });
}
src/api/procurementManagement/procurementInvoiceLedger.js
ÎļþÒÑɾ³ý
src/api/salesManagement/indicatorStats.js
@@ -18,3 +18,21 @@
    params: query,
  });
}
// å®¢æˆ·å¾€æ¥åˆ—表
export function customewTransactions(query) {
  return request({
    url: "/metricStatistics/customewTransactions",
    method: "get",
    params: query,
  });
}
// å®¢æˆ·å¾€æ¥æ˜Žç»†
export function customewTransactionsDetails(query) {
  return request({
    url: "/metricStatistics/customewTransactionsDetails",
    method: "get",
    params: query,
  });
}
src/api/salesManagement/invoiceLedger.js
@@ -1,92 +1,10 @@
// å¼€ç¥¨å°è´¦é¡µé¢æŽ¥å£
import request from '@/utils/request'
// åˆ†é¡µæŸ¥è¯¢
export function invoiceLedgerList(query) {
    return request({
        url: '/invoiceLedger/page',
        method: 'get',
        params: query
    })
}
// æ–°å¢ž
export function invoiceLedgerSaveOrUpdate(query) {
    return request({
        url: '/invoiceLedger/saveOrUpdate',
        method: 'post',
        data: query
    })
}
// å¼€ç¥¨å°è´¦åˆ é™¤
export function invoiceLedgerDel(query) {
    return request({
        url: '/invoiceLedger/del',
        method: 'delete',
        data: query
    })
}
// è¯¦æƒ…查询
export function invoiceLedgerDetail(query) {
    return request({
        url: '/invoiceLedger/info',
        method: 'get',
        params: query
    })
}
// é™„件提交
export function commitFile(query) {
    return request({
        url: '/invoiceLedger/commitFile',
        method: 'post',
        data: query
    })
}
// å¼€ç¥¨å°è´¦éƒ¨åˆ†ä¹ŸæŸ¥è¯¢
export function invoiceLedgerListNoPage(query) {
    return request({
        url: '/invoiceLedger/list',
        method: 'get',
        data: query
    })
}
// åˆ†é¡µæŸ¥è¯¢
/** å›žæ¬¾å°è´¦ - å®¢æˆ·é”€å”®è´¦æˆ·åˆ†é¡µ */
export function invoiceLedgerSalesAccount(query) {
    return request({
        url: '/invoiceLedger/salesAccount',
        method: 'get',
        params: query
    })
  return request({
    url: '/invoiceLedger/salesAccount',
    method: 'get',
    params: query
  })
}
// äº§å“å¼€ç¥¨è®°å½•分页查询
export function registrationProductPage(query) {
    return request({
        url: '/invoiceLedger/registrationProductPage',
        method: 'get',
        params: query
    })
}
// äº§å“å¼€ç¥¨è¯¦æƒ…查询
export function invoiceLedgerProductInfo(query) {
    return request({
        url: '/invoiceLedger/invoiceLedgerProductInfo',
        method: 'get',
        params: query
    })
}
export function delInvoiceLedgerByRegProductId(invoiceRegistrationProductId) {
    return request({
        url: '/invoiceLedger/delInvoiceLedger/'+ invoiceRegistrationProductId,
        method: 'delete'
    })
}
src/api/salesManagement/invoiceRegistration.js
ÎļþÒÑɾ³ý
src/api/salesManagement/receiptPayment.js
@@ -1,84 +1,10 @@
// å¼€ç¥¨ç™»è®°é¡µé¢æŽ¥å£
import request from '@/utils/request'
// æ–°å¢ž/修改
export function receiptPaymentSaveOrUpdate(query) {
    return request({
        url: '/receiptPayment/saveOrUpdate',
        method: 'post',
        data: query
    })
}
// å®¢æˆ·å¾€æ¥è®°å½•查询
/** å›žæ¬¾å°è´¦ - å®¢æˆ·å¾€æ¥è®°å½• */
export function customerInteractions(query) {
    return request({
        url: '/receiptPayment/customerInteractions',
        method: 'get',
        params: query
    })
}
// è¯¦æƒ…
export function receiptPaymentInfo(query) {
    return request({
        url: '/receiptPayment/info',
        method: 'get',
        params: query
    })
}
// åˆ é™¤
export function receiptPaymentDel(query) {
    return request({
        url: '/receiptPayment/del',
        method: 'delete',
        data: query
    })
}
// æŸ¥è¯¢å·²ç»ç»‘定发票的开票台账
export function bindInvoiceNoRegPage(query) {
    return request({
        url: '/sales/product/listPageSalesLedger',
        method: 'get',
        params: query
    })
}
// å¼€ç¥¨å°è´¦è¯¦æƒ…
export function invoiceInfo(query) {
    return request({
        url: '/receiptPayment/invoiceInfo',
        method: 'get',
        params: query
    })
}
// è¯¢å›žæ¬¾è®°å½•
export function receiptPaymentHistoryList(query) {
    return request({
        url: '/receiptPayment/receiptPaymentHistoryList',
        method: 'get',
        params: query
    })
}
/**
 * æŸ¥è¯¢å›žæ¬¾è®°å½•分页查询
 */
export function receiptPaymentHistoryListPage(query) {
    return request({
        url: '/receiptPayment/receiptPaymentHistoryListPage',
        method: 'get',
        params: query
    })
}
export function receiptPaymentHistoryListNoPage(query) {
    return request({
        url: '/receiptPayment/receiptPaymentHistoryListNoPage',
        method: 'get',
        params: query
    })
  return request({
    url: '/receiptPayment/customerInteractions',
    method: 'get',
    params: query
  })
}
src/api/viewIndex.js
@@ -347,30 +347,18 @@
  });
};
const HOME_PROGRESS_STATUS_LIST = ["all", "waiting", "inProgress", "completed", "paused", "1", "2", "3", "4"];
const HOME_PROGRESS_TAB_LIST = ["all", "inProgress", "completed", "paused"];
const HOME_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
const normalizeDateParam = (value) => {
  const dateText = typeof value === "string" ? value.trim() : "";
  return HOME_DATE_PATTERN.test(dateText) ? dateText : undefined;
};
export const productionOrderProgress = (params = {}) => {
  const safePageNum = Math.max(1, Number(params.pageNum || 1));
  const safePageSize = Math.min(50, Math.max(1, Number(params.pageSize || 10)));
  const rawStatus = String(params.status ?? "").trim();
  const safeStatus = HOME_PROGRESS_STATUS_LIST.includes(rawStatus) ? rawStatus : undefined;
  const safeTab = HOME_PROGRESS_TAB_LIST.includes(params.tab) ? params.tab : "all";
  const normalizedTab = safeStatus && HOME_PROGRESS_TAB_LIST.includes(safeStatus) ? safeStatus : safeTab;
  const safeTab = ["all", "inProgress", "completed", "paused"].includes(params.tab)
    ? params.tab
    : "all";
  return request({
    url: "/home/productionOrderProgress",
    method: "get",
    params: {
      ...params,
      status: safeStatus,
      tab: normalizedTab,
      bizDate: normalizeDateParam(params.bizDate),
      tab: safeTab,
      pageNum: safePageNum,
      pageSize: safePageSize,
    },
@@ -388,7 +376,6 @@
    params: {
      ...params,
      limit: safeLimit,
      planDate: normalizeDateParam(params.planDate),
    },
    headers: {
      handleAuthError: false,
src/store/modules/user.js
@@ -52,13 +52,13 @@
      getInfo() {
        return new Promise((resolve, reject) => {
          getInfo().then(res => {
            res  = res.data
            const user = res.user || {}
            const data = res?.data ?? res
            const user = data.user || {}
            let avatar = user.avatar || ""
            avatar = import.meta.env.VITE_APP_BASE_API + '/profile/' + avatar
            if (res.roles && res.roles.length > 0) { // éªŒè¯è¿”回的roles是否是一个非空数组
              this.roles = res.roles
              this.permissions = res.permissions
            if (data.roles && data.roles.length > 0) { // éªŒè¯è¿”回的roles是否是一个非空数组
              this.roles = data.roles
              this.permissions = data.permissions
            } else {
              this.roles = ['ROLE_DEFAULT']
            }
@@ -70,8 +70,8 @@
            this.roleName = Array.isArray(user.roles) && user.roles.length > 0 ? (user.roles[0].roleName || '') : ''
            this.currentDeptId = user.tenantId || ''
            this.currentLoginTime = this.getCurrentTime()
            this.aiEnabled = Number(res.aiEnabled) === 1 ? 1 : 0
            resolve(res)
            this.aiEnabled = Number(data.aiEnabled) === 1 ? 1 : 0
            resolve(data)
          }).catch(error => {
            reject(error)
          })
src/views/financialManagement/expenseManagement/Modal.vue
ÎļþÒÑɾ³ý
src/views/financialManagement/expenseManagement/index.vue
ÎļþÒÑɾ³ý
src/views/financialManagement/loanManagement/Modal.vue
ÎļþÒÑɾ³ý
src/views/financialManagement/loanManagement/index.vue
ÎļþÒÑɾ³ý
src/views/financialManagement/payable/input-invoice.vue
@@ -1,50 +1,61 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="发票代码:">
        <el-input v-model="filters.invoiceCode" placeholder="请输入发票代码" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="发票号码:">
        <el-input v-model="filters.invoiceNo" placeholder="请输入发票号码" clearable style="width: 200px;" />
        <el-input v-model="filters.invoiceNumber" placeholder="请输入发票号码" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="供应商:">
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable style="width: 200px;">
          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable filterable style="width: 200px;">
          <el-option
            v-for="item in supplierList"
            :key="item.id"
            :label="item.supplierName"
            :value="item.id"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="认证状态:">
        <el-select v-model="filters.certifyStatus" placeholder="请选择认证状态" clearable style="width: 150px;">
          <el-option label="未认证" value="uncertified" />
          <el-option label="已认证" value="certified" />
          <el-option label="认证失败" value="failed" />
      <el-form-item label="开票日期:">
        <el-date-picker
          v-model="filters.dateRange"
          type="daterange"
          value-format="YYYY-MM-DD"
          format="YYYY-MM-DD"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          clearable
          style="width: 240px;"
        />
      </el-form-item>
      <el-form-item label="状态:">
        <el-select v-model="filters.status" placeholder="请选择状态" clearable style="width: 150px;">
          <el-option label="正常" :value="0" />
          <el-option label="作废" :value="1" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button type="primary" @click="onSearch">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div>
          <el-button type="success" @click="handleBatchCertify" icon="Check" :disabled="selectedRows.length === 0">批量认证</el-button>
        </div>
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">录入发票</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
          <el-button @click="handleExport" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        isSelection
        :column="columns"
        :tableData="dataList"
        :tableLoading="tableLoading"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @selection-change="handleSelectionChange"
        @pagination="changePage"
      >
        <template #amount="{ row }">
@@ -56,54 +67,123 @@
        <template #totalAmount="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.totalAmount) }}</span>
        </template>
        <template #certifyStatus="{ row }">
          <el-tag :type="getCertifyStatusType(row.certifyStatus)">{{ getCertifyStatusLabel(row.certifyStatus) }}</el-tag>
        <template #status="{ row }">
          <el-tag :type="getStatusType(row.status)" effect="light" round>
            {{ getStatusLabel(row.status) }}
          </el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)">编辑</el-button>
          <el-button type="success" link @click="handleCertify(row)" v-if="row.certifyStatus === 'uncertified'">认证</el-button>
          <el-button type="warning" link @click="handleCancel(row)" v-if="isNormalStatus(row.status)">作废</el-button>
          <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
        </template>
      </PIMTable>
    </div>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
    <FormDialog
      :title="dialogTitle"
      v-model="dialogVisible"
      width="800px"
      :operation-type="isView ? 'detail' : ''"
      @confirm="submitForm"
      @cancel="closeDialog"
    >
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
        <el-row v-if="isView" :gutter="20">
          <el-col :span="12">
            <el-form-item label="发票代码" prop="invoiceCode">
              <el-input v-model="form.invoiceCode" placeholder="请输入发票代码" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="发票号码" prop="invoiceNo">
              <el-input v-model="form.invoiceNo" placeholder="请输入发票号码" />
            <el-form-item label="状态">
              <el-tag :type="getStatusType(form.status)" effect="light" round>
                {{ getStatusLabel(form.status) }}
              </el-tag>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="发票号码" prop="invoiceNo">
              <el-input v-model="form.invoiceNo" placeholder="请输入发票号码" :disabled="isView" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="供应商" prop="supplierId">
              <el-select v-model="form.supplierId" placeholder="请选择供应商" style="width: 100%;">
                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
              <el-select
                v-model="form.supplierId"
                placeholder="请选择供应商"
                style="width: 100%;"
                filterable
                :disabled="isView"
                @change="handleSupplierChange"
              >
                <el-option
                  v-for="item in supplierList"
                  :key="item.id"
                  :label="item.supplierName"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="关联入库单" prop="stockInRecordIds">
              <el-input
                :model-value="inboundBatchDisplayText"
                placeholder="请先选择供应商"
                readonly
                :disabled="!form.supplierId || isView"
                class="inbound-batch-input"
                @click="handleInboundInputClick"
              >
                <template v-if="!isView" #append>
                  <el-button
                    :disabled="!form.supplierId"
                    :loading="inboundBatchLoading"
                    @click.stop="openInboundSelectDialog"
                  >
                    é€‰æ‹©
                  </el-button>
                </template>
              </el-input>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="开票日期" prop="invoiceDate">
              <el-date-picker v-model="form.invoiceDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
              <el-date-picker
                v-model="form.invoiceDate"
                type="date"
                placeholder="选择日期"
                value-format="YYYY-MM-DD"
                style="width: 100%;"
                :disabled="isView"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="金额(不含税)" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" @change="calculateTax" />
          <el-col :span="12">
            <el-form-item label="发票类型" prop="invoiceType">
              <el-select
                v-model="form.invoiceType"
                placeholder="请选择发票类型"
                style="width: 100%;"
                :disabled="isView"
              >
                <el-option label="增值税专用发票" value="增值税专用发票" />
                <el-option label="增值税普通发票" value="增值税普通发票" />
                <el-option label="电子发票" value="电子发票" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
          <el-col :span="12">
            <el-form-item label="税率" prop="taxRate">
              <el-select v-model="form.taxRate" placeholder="请选择税率" style="width: 100%;" @change="calculateTax">
              <el-select
                v-model="form.taxRate"
                placeholder="请选择税率"
                style="width: 100%;"
                :disabled="isView"
                @change="handleTaxRateChange"
              >
                <el-option
                  v-for="dict in tax_rate"
                  :key="dict.value"
@@ -113,47 +193,109 @@
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="税额">
              <el-input v-model="form.taxAmount" disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="认证状态" prop="certifyStatus">
              <el-select v-model="form.certifyStatus" placeholder="请选择认证状态" style="width: 100%;" disabled>
                <el-option label="未认证" value="uncertified" />
                <el-option label="已认证" value="certified" />
                <el-option label="认证失败" value="failed" />
              </el-select>
          <el-col :span="8">
            <el-form-item label="金额(不含税)" prop="amount">
              <el-input-number
                v-model="form.amount"
                :min="0"
                :precision="2"
                style="width: 100%;"
                :disabled="isView"
                placeholder="根据入库单含税金额自动换算,可修改"
                @change="calculateTaxFromExclusive"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="认证日期" prop="certifyDate">
              <el-date-picker v-model="form.certifyDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" disabled />
          <el-col :span="8">
            <el-form-item label="税额">
              <el-input-number
                v-model="form.taxAmount"
                :min="0"
                :precision="2"
                :controls="false"
                style="width: 100%;"
                disabled
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="价税合计">
              <el-input-number
                v-model="form.totalAmount"
                :min="0"
                :precision="2"
                :controls="false"
                style="width: 100%;"
                disabled
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="发票内容" prop="content">
          <el-input v-model="form.content" type="textarea" :rows="3" placeholder="请输入发票内容" />
          <el-input v-model="form.content" type="textarea" :rows="3" placeholder="请输入发票内容" :disabled="isView" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" :disabled="isView" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      <template v-if="!isView" #footer>
        <el-button type="primary" :loading="submitLoading" @click="submitForm">确定</el-button>
        <el-button @click="closeDialog">取消</el-button>
      </template>
    </FormDialog>
    <el-dialog
      v-model="inboundSelectVisible"
      title="选择入库单号"
      width="1100px"
      append-to-body
      destroy-on-close
      :close-on-click-modal="false"
      @closed="handleInboundDialogClosed"
    >
      <el-table
        ref="inboundTableRef"
        v-loading="inboundBatchLoading"
        :data="inboundBatchList"
        row-key="id"
        border
        stripe
        max-height="480"
        @selection-change="handleInboundDialogSelectionChange"
      >
        <el-table-column type="selection" width="55" align="center" />
        <el-table-column prop="inboundBatches" label="入库单号" min-width="140" show-overflow-tooltip />
        <el-table-column prop="supplierName" label="供应商" min-width="120" show-overflow-tooltip />
        <el-table-column prop="productName" label="产品名称" min-width="120" show-overflow-tooltip />
        <el-table-column prop="specificationModel" label="规格型号" min-width="140" show-overflow-tooltip />
        <el-table-column prop="purchaseContractNumber" label="采购订单号" min-width="140" show-overflow-tooltip />
        <el-table-column prop="inboundDate" label="入库日期" width="110" align="center" />
        <el-table-column prop="inboundAmount" label="入库金额(含税)" width="120" align="right">
          <template #default="{ row }">Â¥{{ formatMoney(getInboundRowTaxInclusiveAmount(row)) }}</template>
        </el-table-column>
      </el-table>
      <template #footer>
        <el-button type="primary" @click="confirmInboundSelection">确定</el-button>
        <el-button @click="inboundSelectVisible = false">取消</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance } from "vue";
import { ref, reactive, computed, onMounted, nextTick, getCurrentInstance } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { getOptions } from "@/api/procurementManagement/procurementLedger.js";
import {
  getInboundBatchesBySupplier,
  addAccountPurchaseInvoice,
  listPageAccountPurchaseInvoice,
  cancelAccountPurchaseInvoice,
  deleteAccountPurchaseInvoice,
} from "@/api/financialManagement/accountPurchaseInvoice.js";
defineOptions({
  name: "进项发票",
@@ -163,10 +305,10 @@
const { tax_rate } = proxy.useDict("tax_rate");
const filters = reactive({
  invoiceCode: "",
  invoiceNo: "",
  invoiceNumber: "",
  supplierId: "",
  certifyStatus: "",
  dateRange: [],
  status: "",
});
const pagination = reactive({
@@ -176,211 +318,601 @@
});
const columns = [
  { label: "发票代码", prop: "invoiceCode", width: "130" },
  { label: "发票号码", prop: "invoiceNo", width: "120" },
  { label: "发票号码", prop: "invoiceNo", width: "140" },
  { label: "供应商", prop: "supplierName", width: "180" },
  { label: "开票日期", prop: "invoiceDate", width: "120" },
  { label: "金额", prop: "amount", slot: "amount" },
  { label: "税额", prop: "taxAmount", slot: "taxAmount" },
  { label: "价税合计", prop: "totalAmount", slot: "totalAmount" },
  { label: "认证状态", prop: "certifyStatus", slot: "certifyStatus" },
  { label: "操作", prop: "operation", slot: "operation", width: "180", fixed: "right" },
  { label: "金额", prop: "amount", dataType: "slot", slot: "amount" },
  { label: "税额", prop: "taxAmount", dataType: "slot", slot: "taxAmount" },
  { label: "价税合计", prop: "totalAmount", dataType: "slot", slot: "totalAmount" },
  { label: "发票类型", prop: "invoiceType", width: "130" },
  { label: "状态", prop: "status", dataType: "slot", slot: "status", width: "90", align: "center" },
  { label: "操作", prop: "operation", dataType: "slot", slot: "operation", width: "200", fixed: "right" },
];
const dataList = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const isView = ref(false);
const submitLoading = ref(false);
const supplierList = ref([]);
const supplierList = [
  { id: 1, name: "北京原材料供应商" },
  { id: 2, name: "上海电子元器件公司" },
  { id: 3, name: "广州包装材料厂" },
  { id: 4, name: "深圳五金配件公司" },
];
const inboundBatchList = ref([]);
const inboundBatchOptions = ref([]);
const inboundBatchLoading = ref(false);
const inboundSelectVisible = ref(false);
const inboundTableRef = ref(null);
const dialogInboundSelection = ref([]);
const STATUS_LABEL_MAP = { 0: "正常", 1: "作废" };
const STATUS_TYPE_MAP = { 0: "success", 1: "info" };
const form = reactive({
  invoiceCode: "",
  invoiceNo: "",
  supplierId: "",
  invoiceDate: "",
  amount: 0,
  invoiceType: "增值税专用发票",
  taxRate: 13,
  amount: 0,
  taxAmount: 0,
  totalAmount: 0,
  certifyStatus: "uncertified",
  certifyDate: "",
  content: "",
  remark: "",
  stockInRecordIds: [],
  inboundBatches: "",
  storageAttachmentId: undefined,
  status: 0,
});
const rules = {
  invoiceCode: [{ required: true, message: "请输入发票代码", trigger: "blur" }],
  invoiceNo: [{ required: true, message: "请输入发票号码", trigger: "blur" }],
  supplierId: [{ required: true, message: "请选择供应商", trigger: "change" }],
  stockInRecordIds: [{ required: true, type: "array", min: 1, message: "请选择关联入库单", trigger: "change" }],
  invoiceDate: [{ required: true, message: "请选择开票日期", trigger: "change" }],
  amount: [{ required: true, message: "请输入金额", trigger: "blur" }],
  invoiceType: [{ required: true, message: "请选择发票类型", trigger: "change" }],
  taxRate: [{ required: true, message: "请选择税率", trigger: "change" }],
  amount: [{ required: true, message: "请输入金额", trigger: "blur" }],
};
const mockData = [
  { id: 1, invoiceCode: "0440021001", invoiceNo: "12345678", supplierId: 1, supplierName: "北京原材料供应商", invoiceDate: "2024-01-08", amount: 8000, taxRate: 13, taxAmount: 1040, totalAmount: 9040, certifyStatus: "certified", certifyDate: "2024-01-15", content: "原材料采购", remark: "" },
  { id: 2, invoiceCode: "0440021002", invoiceNo: "87654321", supplierId: 2, supplierName: "上海电子元器件公司", invoiceDate: "2024-01-10", amount: 12000, taxRate: 13, taxAmount: 1560, totalAmount: 13560, certifyStatus: "uncertified", certifyDate: "", content: "电子元器件", remark: "" },
  { id: 3, invoiceCode: "0440021003", invoiceNo: "11112222", supplierId: 3, supplierName: "广州包装材料厂", invoiceDate: "2024-01-12", amount: 3500, taxRate: 13, taxAmount: 455, totalAmount: 3955, certifyStatus: "certified", certifyDate: "2024-01-18", content: "包装材料", remark: "" },
];
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const calculateTax = () => {
const normalizeStatus = (status) => {
  if (status === undefined || status === null || status === "") return 0;
  const num = Number(status);
  return Number.isNaN(num) ? 0 : num;
};
const isNormalStatus = (status) => normalizeStatus(status) === 0;
const getStatusLabel = (status) => STATUS_LABEL_MAP[normalizeStatus(status)] ?? "正常";
const getStatusType = (status) => STATUS_TYPE_MAP[normalizeStatus(status)] ?? "success";
const parseStockInRecordIds = (value) => {
  if (!value) return [];
  if (Array.isArray(value)) return value;
  return String(value)
    .split(/[,,]/)
    .map((s) => s.trim())
    .filter(Boolean)
    .map((s) => (/^\d+$/.test(s) ? Number(s) : s));
};
const formatInboundBatches = (value) => {
  if (value === undefined || value === null || value === "") return "";
  if (Array.isArray(value)) return value.filter(Boolean).join("、");
  return String(value)
    .split(/[,,]/)
    .map((s) => s.trim())
    .filter(Boolean)
    .join("、");
};
const isSameInboundId = (a, b) => String(a) === String(b);
const getInboundRowId = (row) => row?.id ?? row?.stockInRecordId;
/** å…¥åº“单金额为含税价 */
const getInboundRowTaxInclusiveAmount = (row) =>
  Number(row?.inboundAmount ?? row?.taxInclusivePrice ?? row?.totalAmount ?? 0);
const normalizeInboundBatchOptions = (data) => {
  const list = Array.isArray(data) ? data : [];
  return list.map((item, index) => {
    if (typeof item === "string" || typeof item === "number") {
      const text = String(item);
      return { label: text, value: text, inboundAmount: 0 };
    }
    const label =
      item.inboundBatches ?? item.batchNo ?? item.inboundNo ?? item.label ?? `入库单${index + 1}`;
    const value = item.id ?? item.stockInRecordId ?? label;
    return {
      label: String(label),
      value,
      inboundAmount: getInboundRowTaxInclusiveAmount(item),
    };
  });
};
/** ä¸å«ç¨Žé‡‘额变更:税额、价税合计正向计算 */
const calculateTaxFromExclusive = () => {
  form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(2));
  form.totalAmount = Number((form.amount + form.taxAmount).toFixed(2));
};
const getCertifyStatusLabel = (status) => {
  const map = { uncertified: "未认证", certified: "已认证", failed: "认证失败" };
  return map[status] || status;
/** ä»·ç¨Žåˆè®¡å˜æ›´ï¼šæŒ‰ç¨ŽçŽ‡åç®—ä¸å«ç¨Žé‡‘é¢ã€ç¨Žé¢ */
const calculateTaxFromInclusive = (inclusiveTotal) => {
  const total = Number(inclusiveTotal ?? form.totalAmount ?? 0);
  if (total <= 0) {
    form.amount = 0;
    form.taxAmount = 0;
    form.totalAmount = 0;
    return;
  }
  const rate = Number(form.taxRate) / 100;
  form.totalAmount = Number(total.toFixed(2));
  form.amount = Number((form.totalAmount / (1 + rate)).toFixed(2));
  form.taxAmount = Number((form.totalAmount - form.amount).toFixed(2));
};
const getCertifyStatusType = (status) => {
  const map = { uncertified: "info", certified: "success", failed: "danger" };
  return map[status] || "";
const handleTaxRateChange = () => {
  if (form.totalAmount > 0) {
    calculateTaxFromInclusive(form.totalAmount);
  } else {
    calculateTaxFromExclusive();
  }
};
/** æ ¹æ®å·²é€‰å…¥åº“单汇总含税金额,反算不含税金额与税额 */
const syncInvoiceAmount = () => {
  const selected = form.stockInRecordIds || [];
  const sumFromOptions = inboundBatchOptions.value
    .filter((opt) => selected.some((id) => isSameInboundId(id, opt.value)))
    .reduce((acc, opt) => acc + (Number(opt.inboundAmount) || 0), 0);
  let taxInclusiveSum = sumFromOptions;
  if (taxInclusiveSum <= 0 && selected.length) {
    taxInclusiveSum = inboundBatchList.value
      .filter((row) => selected.some((id) => isSameInboundId(id, getInboundRowId(row))))
      .reduce((acc, row) => acc + getInboundRowTaxInclusiveAmount(row), 0);
  }
  calculateTaxFromInclusive(taxInclusiveSum > 0 ? Number(taxInclusiveSum.toFixed(2)) : 0);
};
const inboundBatchDisplayText = computed(() => {
  if (form.inboundBatches) return form.inboundBatches;
  const ids = form.stockInRecordIds || [];
  if (!ids.length) return "";
  const labels = inboundBatchOptions.value
    .filter((opt) => ids.some((id) => isSameInboundId(id, opt.value)))
    .map((opt) => opt.label);
  if (labels.length) return labels.join("、");
  return ids.join("、");
});
const normalizeTableRow = (row) => ({
  ...row,
  invoiceNo: row.invoiceNumber ?? row.invoiceNo,
  invoiceDate: row.issueDate ?? row.invoiceDate,
  amount: row.taxExclusivelPrice ?? row.amount,
  taxAmount: row.taxPrice ?? row.taxAmount,
  totalAmount: row.taxInclusivePrice ?? row.totalAmount,
  content: row.invoiceContent ?? row.content,
  status: normalizeStatus(row.status),
  stockInRecordIds: row.stockInRecordIds ?? "",
  inboundBatches: formatInboundBatches(row.inboundBatches),
});
const toFormNumber = (val) => {
  const n = Number(val);
  return Number.isFinite(n) ? n : 0;
};
const resolveFormAmounts = (row) => {
  let amount = toFormNumber(row.taxExclusivelPrice ?? row.amount);
  let taxAmount = toFormNumber(row.taxPrice ?? row.taxAmount);
  let totalAmount = toFormNumber(row.taxInclusivePrice ?? row.totalAmount);
  const taxRate = toFormNumber(row.taxRate) || 13;
  if (totalAmount > 0 && amount === 0 && taxAmount === 0) {
    amount = Number((totalAmount / (1 + taxRate / 100)).toFixed(2));
    taxAmount = Number((totalAmount - amount).toFixed(2));
  } else if (totalAmount > 0 && amount > 0 && taxAmount === 0) {
    taxAmount = Number((totalAmount - amount).toFixed(2));
  } else if (amount > 0 && taxAmount === 0 && totalAmount === 0) {
    taxAmount = Number((amount * taxRate / 100).toFixed(2));
    totalAmount = Number((amount + taxAmount).toFixed(2));
  } else if (amount > 0 && taxAmount > 0 && totalAmount === 0) {
    totalAmount = Number((amount + taxAmount).toFixed(2));
  }
  return { amount, taxAmount, totalAmount };
};
const fillFormFromRow = (row) => {
  const stockInRecordIds = parseStockInRecordIds(row.stockInRecordIds);
  const { amount, taxAmount, totalAmount } = resolveFormAmounts(row);
  Object.assign(form, {
    invoiceNo: row.invoiceNo ?? row.invoiceNumber ?? "",
    supplierId: row.supplierId,
    invoiceDate: row.invoiceDate ?? row.issueDate ?? "",
    invoiceType: row.invoiceType ?? "增值税专用发票",
    taxRate: row.taxRate ?? 13,
    amount,
    taxAmount,
    totalAmount,
    content: row.content ?? row.invoiceContent ?? "",
    remark: row.remark ?? "",
    stockInRecordIds,
    inboundBatches: formatInboundBatches(row.inboundBatches),
    storageAttachmentId: row.storageAttachmentId,
    status: normalizeStatus(row.status),
  });
};
const buildCancelPayload = (row) => ({
  id: row.id,
  invoiceNumber: row.invoiceNumber ?? row.invoiceNo,
  taxRate: row.taxRate,
  invoiceType: row.invoiceType,
  issueDate: row.issueDate ?? row.invoiceDate,
  taxExclusivelPrice: row.taxExclusivelPrice ?? row.amount,
  taxPrice: row.taxPrice ?? row.taxAmount,
  taxInclusivePrice: row.taxInclusivePrice ?? row.totalAmount,
  remark: row.remark ?? "",
  invoiceContent: row.invoiceContent ?? row.content,
  supplierId: row.supplierId,
  storageAttachmentId: row.storageAttachmentId,
  stockInRecordIds: row.stockInRecordIds ?? "",
  status: 1,
});
const buildSubmitPayload = () => ({
  invoiceNumber: form.invoiceNo,
  supplierId: form.supplierId,
  issueDate: form.invoiceDate,
  invoiceType: form.invoiceType,
  taxRate: form.taxRate,
  taxExclusivelPrice: form.amount,
  taxPrice: form.taxAmount,
  taxInclusivePrice: form.totalAmount,
  invoiceContent: form.content,
  remark: form.remark || "",
  stockInRecordIds: (form.stockInRecordIds || []).join(","),
  status: 0,
  storageAttachmentId: form.storageAttachmentId,
});
const getSupplierList = () => {
  getOptions().then((res) => {
    if (res.code === 200) {
      supplierList.value = res.data ?? [];
    }
  });
};
const appendFilterParams = (params) => {
  if (filters.invoiceNumber) {
    params.invoiceNumber = filters.invoiceNumber;
  }
  if (filters.supplierId) {
    params.supplierId = filters.supplierId;
  }
  if (filters.dateRange?.length === 2) {
    params.startDate = filters.dateRange[0];
    params.endDate = filters.dateRange[1];
  }
  if (filters.status !== "" && filters.status != null) {
    params.status = filters.status;
  }
  return params;
};
const buildListParams = () =>
  appendFilterParams({
    current: pagination.currentPage,
    size: pagination.pageSize,
  });
const buildExportParams = () => appendFilterParams({});
const handleExport = () => {
  proxy.download(
    "/accountPurchaseInvoice/exportAccountPurchaseInvoice",
    buildExportParams(),
    `进项发票_${Date.now()}.xlsx`
  );
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.invoiceCode) {
    result = result.filter(item => item.invoiceCode.includes(filters.invoiceCode));
  }
  if (filters.invoiceNo) {
    result = result.filter(item => item.invoiceNo.includes(filters.invoiceNo));
  }
  if (filters.supplierId) {
    result = result.filter(item => item.supplierId === filters.supplierId);
  }
  if (filters.certifyStatus) {
    result = result.filter(item => item.certifyStatus === filters.certifyStatus);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
  tableLoading.value = true;
  listPageAccountPurchaseInvoice(buildListParams())
    .then((res) => {
      if (res.code === 200) {
        const records = res.data?.records ?? [];
        dataList.value = records.map(normalizeTableRow);
        pagination.total = res.data?.total ?? 0;
      } else {
        dataList.value = [];
        pagination.total = 0;
        ElMessage.error(res.msg || "查询失败");
      }
    })
    .catch(() => {
      dataList.value = [];
      pagination.total = 0;
      ElMessage.error("查询失败");
    })
    .finally(() => {
      tableLoading.value = false;
    });
};
const resetFilters = () => {
  filters.invoiceCode = "";
  filters.invoiceNo = "";
  filters.supplierId = "";
  filters.certifyStatus = "";
const onSearch = () => {
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
const resetFilters = () => {
  filters.invoiceNumber = "";
  filters.supplierId = "";
  filters.dateRange = [];
  filters.status = "";
  pagination.currentPage = 1;
  getTableData();
};
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
const changePage = ({ page, limit }) => {
  pagination.currentPage = page;
  pagination.pageSize = limit;
  getTableData();
};
const closeDialog = () => {
  dialogVisible.value = false;
  isView.value = false;
  inboundSelectVisible.value = false;
};
const resetForm = () => {
  Object.assign(form, {
    invoiceNo: "",
    supplierId: "",
    invoiceDate: new Date().toISOString().split("T")[0],
    invoiceType: "增值税专用发票",
    taxRate: 13,
    amount: 0,
    taxAmount: 0,
    totalAmount: 0,
    content: "",
    remark: "",
    stockInRecordIds: [],
    inboundBatches: "",
    storageAttachmentId: undefined,
    status: 0,
  });
  inboundBatchList.value = [];
  inboundBatchOptions.value = [];
};
const add = () => {
  isEdit.value = false;
  isView.value = false;
  dialogTitle.value = "录入发票";
  Object.assign(form, {
    invoiceCode: "",
    invoiceNo: "",
    supplierId: "",
    invoiceDate: new Date().toISOString().split('T')[0],
    amount: 0,
    taxRate: 13,
    taxAmount: 0,
    totalAmount: 0,
    certifyStatus: "uncertified",
    certifyDate: "",
    content: "",
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑发票";
  Object.assign(form, row);
  resetForm();
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看发票: ${row.invoiceCode}-${row.invoiceNo}`);
  isView.value = true;
  dialogTitle.value = "查看发票";
  fillFormFromRow(row);
  if (row.supplierId) {
    loadInboundBatches(row.supplierId, true, false);
  }
  dialogVisible.value = true;
};
const handleCertify = (row) => {
  ElMessageBox.confirm("确认认证该发票吗?", "提示", {
    confirmButtonText: "确认",
const handleCancel = (row) => {
  ElMessageBox.confirm(`确认作废发票「${row.invoiceNo ?? row.invoiceNumber}」吗?`, "作废确认", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "info",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].certifyStatus = "certified";
      mockData[index].certifyDate = new Date().toISOString().split('T')[0];
    }
    ElMessage.success("认证成功");
    getTableData();
    cancelAccountPurchaseInvoice(buildCancelPayload(row))
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success("作废成功");
          getTableData();
        } else {
          ElMessage.error(res.msg || "作废失败");
        }
      })
      .catch(() => {
        ElMessage.error("作废失败");
      });
  });
};
const handleBatchCertify = () => {
  ElMessageBox.confirm(`确认批量认证选中的 ${selectedRows.value.length} å¼ å‘票吗?`, "提示", {
    confirmButtonText: "确认",
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除发票「${row.invoiceNo ?? row.invoiceNumber}」吗?`, "删除确认", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "info",
    type: "warning",
  }).then(() => {
    selectedRows.value.forEach(row => {
      const index = mockData.findIndex(item => item.id === row.id);
      if (index !== -1 && mockData[index].certifyStatus === "uncertified") {
        mockData[index].certifyStatus = "certified";
        mockData[index].certifyDate = new Date().toISOString().split('T')[0];
      }
    });
    ElMessage.success("批量认证成功");
    getTableData();
    deleteAccountPurchaseInvoice([row.id])
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success("删除成功");
          getTableData();
        } else {
          ElMessage.error(res.msg || "删除失败");
        }
      })
      .catch(() => {
        ElMessage.error("删除失败");
      });
  });
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const supplier = supplierList.find(item => item.id === form.supplierId);
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name };
  formRef.value?.validate((valid) => {
    if (!valid) return;
    submitLoading.value = true;
    addAccountPurchaseInvoice(buildSubmitPayload())
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success("录入成功");
          closeDialog();
          pagination.currentPage = 1;
          getTableData();
        } else {
          ElMessage.error(res.msg || "录入失败");
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, supplierName: supplier?.name });
        ElMessage.success("录入成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
      })
      .catch(() => {
        ElMessage.error("录入失败");
      })
      .finally(() => {
        submitLoading.value = false;
      });
  });
};
const ensureInboundOptionsForSelected = () => {
  const ids = form.stockInRecordIds || [];
  ids.forEach((id) => {
    const exists = inboundBatchOptions.value.some((opt) => isSameInboundId(opt.value, id));
    if (exists) return;
    const fromList = inboundBatchList.value.find((row) => isSameInboundId(getInboundRowId(row), id));
    if (fromList) {
      const [option] = normalizeInboundBatchOptions([fromList]);
      if (option) inboundBatchOptions.value.push(option);
      return;
    }
    inboundBatchOptions.value.push({
      label: String(id),
      value: id,
      inboundAmount: 0,
    });
  });
};
const restoreInboundTableSelection = () => {
  nextTick(() => {
    const table = inboundTableRef.value;
    if (!table) return;
    table.clearSelection();
    const selectedIds = new Set((form.stockInRecordIds || []).map((id) => String(id)));
    inboundBatchList.value.forEach((row) => {
      const rowId = getInboundRowId(row);
      if (rowId !== undefined && rowId !== null && selectedIds.has(String(rowId))) {
        table.toggleRowSelection(row, true);
      }
    });
  });
};
const loadInboundBatches = (supplierId, keepSelected = false, syncAmount = true) => {
  if (!supplierId) {
    inboundBatchList.value = [];
    inboundBatchOptions.value = [];
    if (!keepSelected) {
      form.stockInRecordIds = [];
      form.inboundBatches = "";
      form.amount = 0;
      form.taxAmount = 0;
      form.totalAmount = 0;
    }
    return Promise.resolve();
  }
  inboundBatchLoading.value = true;
  return getInboundBatchesBySupplier({ supplierId })
    .then((res) => {
      if (res.code === 200) {
        const list = res.data?.records ?? res.data ?? [];
        inboundBatchList.value = Array.isArray(list) ? list : [];
        inboundBatchOptions.value = normalizeInboundBatchOptions(list);
      } else {
        inboundBatchList.value = [];
        inboundBatchOptions.value = [];
      }
    })
    .catch(() => {
      inboundBatchList.value = [];
      inboundBatchOptions.value = [];
    })
    .finally(() => {
      inboundBatchLoading.value = false;
      if (keepSelected) {
        ensureInboundOptionsForSelected();
        restoreInboundTableSelection();
        if (syncAmount && !isView.value) {
          syncInvoiceAmount();
        }
      }
    });
};
const handleSupplierChange = (supplierId) => {
  form.stockInRecordIds = [];
  form.inboundBatches = "";
  form.amount = 0;
  form.taxAmount = 0;
  form.totalAmount = 0;
  loadInboundBatches(supplierId);
};
const handleInboundInputClick = () => {
  if (isView.value) return;
  openInboundSelectDialog();
};
const openInboundSelectDialog = () => {
  if (!form.supplierId || isView.value) return;
  inboundSelectVisible.value = true;
  loadInboundBatches(form.supplierId, true).then(() => {
    restoreInboundTableSelection();
  });
};
const handleInboundDialogSelectionChange = (selection) => {
  dialogInboundSelection.value = selection;
};
const confirmInboundSelection = () => {
  if (dialogInboundSelection.value.length === 0) {
    ElMessage.warning("请至少选择一条入库单");
    return;
  }
  form.stockInRecordIds = dialogInboundSelection.value
    .map((row) => getInboundRowId(row))
    .filter((id) => id !== undefined && id !== null);
  form.inboundBatches = dialogInboundSelection.value
    .map((row) => row.inboundBatches ?? row.batchNo ?? "")
    .filter(Boolean)
    .join("、");
  dialogInboundSelection.value.forEach((row) => {
    const [option] = normalizeInboundBatchOptions([row]);
    if (option && !inboundBatchOptions.value.some((opt) => isSameInboundId(opt.value, option.value))) {
      inboundBatchOptions.value.push(option);
    }
  });
  inboundSelectVisible.value = false;
  syncInvoiceAmount();
  formRef.value?.validateField("stockInRecordIds");
};
const handleInboundDialogClosed = () => {
  dialogInboundSelection.value = [];
};
onMounted(() => {
  getSupplierList();
  getTableData();
});
</script>
@@ -406,4 +938,8 @@
  color: #67c23a;
  font-weight: bold;
}
.inbound-batch-input :deep(.el-input__wrapper) {
  cursor: pointer;
}
</style>
src/views/financialManagement/payable/payment.vue
@@ -1,377 +1,299 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
    <el-form :model="filters"
             :inline="true">
      <el-form-item label="付款单号:">
        <el-input v-model="filters.paymentCode" placeholder="请输入付款单号" clearable style="width: 200px;" />
        <el-input v-model="filters.paymentNumber"
                  placeholder="请输入付款单号"
                  clearable
                  style="width: 200px;" />
      </el-form-item>
      <el-form-item label="供应商:">
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable style="width: 200px;">
          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
        <el-select v-model="filters.supplierId"
                   placeholder="请选择供应商"
                   clearable
                   filterable
                   style="width: 200px;">
          <el-option v-for="item in supplierList"
                     :key="item.id"
                     :label="item.supplierName"
                     :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="付款方式:">
        <el-select v-model="filters.paymentMethod" placeholder="请选择付款方式" clearable style="width: 150px;">
          <el-option label="银行转账" value="bank_transfer" />
          <el-option label="现金" value="cash" />
          <el-option label="支票" value="check" />
          <el-option label="汇票" value="draft" />
        <el-select v-model="filters.paymentMethod"
                   placeholder="请选择付款方式"
                   clearable
                   style="width: 150px;">
          <el-option v-for="item in checkout_payment"
                     :key="item.value"
                     :label="item.label"
                     :value="item.value" />
        </el-select>
      </el-form-item>
      <el-form-item label="状态:">
        <el-select v-model="filters.status" placeholder="请选择状态" clearable style="width: 150px;">
          <el-option label="待付款" value="pending" />
          <el-option label="已完成" value="completed" />
          <el-option label="已取消" value="cancelled" />
        </el-select>
      <el-form-item label="付款日期:">
        <el-date-picker v-model="filters.dateRange"
                        type="daterange"
                        value-format="YYYY-MM-DD"
                        format="YYYY-MM-DD"
                        range-separator="至"
                        start-placeholder="开始日期"
                        end-placeholder="结束日期"
                        clearable
                        style="width: 240px;" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button type="primary"
                   @click="onSearch">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div>
          <el-statistic title="本期付款合计" :value="totalPaymentAmount" precision="2" prefix="Â¥" />
          <el-statistic title="本页付款合计"
                        :value="totalPaymentAmount"
                        :precision="2"
                        prefix="Â¥" />
        </div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增付款</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
          <el-button @click="handleExport"
                     icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
      <PIMTable rowKey="id"
                :column="columns"
                :tableData="dataList"
                :tableLoading="tableLoading"
                :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
                @pagination="changePage">
        <template #amount="{ row }">
          <span class="text-danger">Â¥{{ formatMoney(row.amount) }}</span>
        </template>
        <template #paymentMethod="{ row }">
          <el-tag>{{ getPaymentMethodLabel(row.paymentMethod) }}</el-tag>
        </template>
        <template #status="{ row }">
          <el-tag :type="row.status === 'completed' ? 'success' : row.status === 'pending' ? 'warning' : 'info'">
            {{ row.status === 'completed' ? '已完成' : row.status === 'pending' ? '待付款' : '已取消' }}
          </el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">编辑</el-button>
          <el-button type="success" link @click="handleComplete(row)" v-if="row.status === 'pending'">完成</el-button>
          <el-button type="danger" link @click="handleCancel(row)" v-if="row.status === 'pending'">取消</el-button>
          <el-button :disabled="row.accountStatemen"
                     type="danger"
                     link
                     @click="handleDelete(row)">删除</el-button>
        </template>
      </PIMTable>
    </div>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="付款单号" prop="paymentCode">
              <el-input v-model="form.paymentCode" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="关联申请单" prop="applyCode">
              <el-select v-model="form.applyCode" placeholder="请选择关联申请单" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in applyList" :key="item.applyCode" :label="item.applyCode" :value="item.applyCode" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="供应商" prop="supplierId">
              <el-select v-model="form.supplierId" placeholder="请选择供应商" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款日期" prop="paymentDate">
              <el-date-picker v-model="form.paymentDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="付款金额" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款方式" prop="paymentMethod">
              <el-select v-model="form.paymentMethod" placeholder="请选择付款方式" style="width: 100%;">
                <el-option label="银行转账" value="bank_transfer" />
                <el-option label="现金" value="cash" />
                <el-option label="支票" value="check" />
                <el-option label="汇票" value="draft" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="银行账号" prop="bankAccount" v-if="form.paymentMethod === 'bank_transfer'">
              <el-input v-model="form.bankAccount" placeholder="请输入银行账号" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="开户行" prop="bankName" v-if="form.paymentMethod === 'bank_transfer'">
              <el-input v-model="form.bankName" placeholder="请输入开户行" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
  import { ref, reactive, computed, onMounted, getCurrentInstance } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import { getOptions } from "@/api/procurementManagement/procurementLedger.js";
  import {
    listPageAccountPurchasePayment,
    deleteAccountPurchasePayment,
  } from "@/api/financialManagement/accountPurchasePayment.js";
defineOptions({
  name: "付款单",
});
  defineOptions({
    name: "付款单",
  });
const filters = reactive({
  paymentCode: "",
  supplierId: "",
  paymentMethod: "",
  status: "",
});
  const { proxy } = getCurrentInstance();
  const { checkout_payment } = proxy.useDict("checkout_payment");
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "付款单号", prop: "paymentCode", width: "150" },
  { label: "关联申请单", prop: "applyCode", width: "150" },
  { label: "供应商", prop: "supplierName", width: "180" },
  { label: "付款日期", prop: "paymentDate", width: "120" },
  { label: "付款金额", prop: "amount", slot: "amount" },
  { label: "付款方式", prop: "paymentMethod", slot: "paymentMethod" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "备注", prop: "remark", showOverflowTooltip: true },
  { label: "操作", prop: "operation", slot: "operation", width: "220", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const supplierList = [
  { id: 1, name: "北京原材料供应商" },
  { id: 2, name: "上海电子元器件公司" },
  { id: 3, name: "广州包装材料厂" },
  { id: 4, name: "深圳五金配件公司" },
];
const applyList = [
  { applyCode: "FK2024001", supplierId: 1, amount: 5000 },
  { applyCode: "FK2024002", supplierId: 2, amount: 8000 },
  { applyCode: "FK2024003", supplierId: 3, amount: 3000 },
];
const form = reactive({
  paymentCode: "",
  applyCode: "",
  supplierId: "",
  paymentDate: "",
  amount: 0,
  paymentMethod: "bank_transfer",
  bankAccount: "",
  bankName: "",
  remark: "",
});
const rules = {
  applyCode: [{ required: true, message: "请选择关联申请单", trigger: "change" }],
  supplierId: [{ required: true, message: "请选择供应商", trigger: "change" }],
  paymentDate: [{ required: true, message: "请选择付款日期", trigger: "change" }],
  amount: [{ required: true, message: "请输入付款金额", trigger: "blur" }],
  paymentMethod: [{ required: true, message: "请选择付款方式", trigger: "change" }],
};
const mockData = [
  { id: 1, paymentCode: "FKD2024001", applyCode: "FK2024001", supplierId: 1, supplierName: "北京原材料供应商", paymentDate: "2024-01-15", amount: 5000, paymentMethod: "bank_transfer", status: "completed", bankAccount: "6222021234567890123", bankName: "工商银行", remark: "" },
  { id: 2, paymentCode: "FKD2024002", applyCode: "FK2024002", supplierId: 2, supplierName: "上海电子元器件公司", paymentDate: "2024-01-18", amount: 8000, paymentMethod: "bank_transfer", status: "pending", bankAccount: "6222029876543210987", bankName: "建设银行", remark: "" },
  { id: 3, paymentCode: "FKD2024003", applyCode: "FK2024003", supplierId: 3, supplierName: "广州包装材料厂", paymentDate: "2024-01-20", amount: 3000, paymentMethod: "cash", status: "completed", remark: "" },
];
const totalPaymentAmount = computed(() => {
  return dataList.value.reduce((sum, item) => sum + Number(item.amount), 0);
});
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getPaymentMethodLabel = (method) => {
  const map = {
    bank_transfer: "银行转账",
    cash: "现金",
    check: "支票",
    draft: "汇票",
  };
  return map[method] || method;
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.paymentCode) {
    result = result.filter(item => item.paymentCode.includes(filters.paymentCode));
  }
  if (filters.supplierId) {
    result = result.filter(item => item.supplierId === filters.supplierId);
  }
  if (filters.paymentMethod) {
    result = result.filter(item => item.paymentMethod === filters.paymentMethod);
  }
  if (filters.status) {
    result = result.filter(item => item.status === filters.status);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.paymentCode = "";
  filters.supplierId = "";
  filters.paymentMethod = "";
  filters.status = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增付款";
  Object.assign(form, {
    paymentCode: "FKD" + Date.now().toString().slice(-8),
    applyCode: "",
  const filters = reactive({
    paymentNumber: "",
    supplierId: "",
    paymentDate: new Date().toISOString().split('T')[0],
    amount: 0,
    paymentMethod: "bank_transfer",
    bankAccount: "",
    bankName: "",
    remark: "",
    paymentMethod: "",
    dateRange: [],
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑付款";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看付款单: ${row.paymentCode}`);
};
const handleComplete = (row) => {
  ElMessageBox.confirm("确认该付款单已完成吗?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "info",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "completed";
    }
    ElMessage.success("付款完成");
    getTableData();
  const pagination = reactive({
    currentPage: 1,
    pageSize: 10,
    total: 0,
  });
};
const handleCancel = (row) => {
  ElMessageBox.confirm("确认取消该付款单吗?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "cancelled";
    }
    ElMessage.success("已取消");
    getTableData();
  const columns = [
    { label: "付款单号", prop: "paymentNumber", width: "150" },
    { label: "关联申请单", prop: "invoiceApplicationNo", width: "150" },
    { label: "供应商", prop: "supplierName", width: "180" },
    { label: "付款日期", prop: "paymentDate", width: "120" },
    { label: "付款金额", prop: "amount", dataType: "slot", slot: "amount" },
    {
      label: "付款方式",
      prop: "paymentMethod",
      dataType: "slot",
      slot: "paymentMethod",
      width: "120",
    },
    { label: "备注", prop: "remark", showOverflowTooltip: true },
    {
      label: "操作",
      prop: "operation",
      dataType: "slot",
      slot: "operation",
      width: "80",
      fixed: "right",
    },
  ];
  const dataList = ref([]);
  const tableLoading = ref(false);
  const supplierList = ref([]);
  const totalPaymentAmount = computed(() =>
    dataList.value.reduce((sum, item) => sum + Number(item.amount ?? 0), 0)
  );
  const formatMoney = value => {
    if (value === undefined || value === null) return "0.00";
    return Number(value)
      .toFixed(2)
      .replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  };
  const getPaymentMethodLabel = value => {
    if (value === undefined || value === null || value === "") return "-";
    const item = checkout_payment.value?.find(
      m => String(m.value) === String(value)
    );
    return item?.label ?? value;
  };
  const normalizeTableRow = row => ({
    ...row,
    paymentNumber: row.paymentNumber ?? row.paymentCode,
    invoiceApplicationNo: row.invoiceApplicationNo ?? row.applyCode ?? "",
    amount: row.paymentAmount ?? row.amount,
    bankAccountNum: row.bankAccountNum ?? row.bankAccount ?? "",
    bankAccountName: row.bankAccountName ?? row.bankName ?? "",
  });
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const supplier = supplierList.find(item => item.id === form.supplierId);
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, supplierName: supplier?.name, status: "pending" });
        ElMessage.success("新增成功");
  const getSupplierList = () => {
    getOptions().then(res => {
      if (res.code === 200) {
        supplierList.value = res.data ?? [];
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
    });
  };
onMounted(() => {
  getTableData();
});
  const appendFilterParams = params => {
    if (filters.paymentNumber) {
      params.paymentNumber = filters.paymentNumber;
    }
    if (filters.supplierId) {
      params.supplierId = filters.supplierId;
    }
    if (filters.paymentMethod) {
      params.paymentMethod = filters.paymentMethod;
    }
    if (filters.dateRange?.length === 2) {
      params.startDate = filters.dateRange[0];
      params.endDate = filters.dateRange[1];
    }
    return params;
  };
  const buildListParams = () =>
    appendFilterParams({
      current: pagination.currentPage,
      size: pagination.pageSize,
    });
  const buildExportParams = () => appendFilterParams({});
  const handleExport = () => {
    proxy.download(
      "/accountPurchasePayment/exportAccountPurchasePayment",
      buildExportParams(),
      `付款单_${Date.now()}.xlsx`
    );
  };
  const getTableData = () => {
    tableLoading.value = true;
    listPageAccountPurchasePayment(buildListParams())
      .then(res => {
        if (res.code === 200) {
          dataList.value = (res.data?.records ?? []).map(normalizeTableRow);
          pagination.total = res.data?.total ?? 0;
        } else {
          dataList.value = [];
          pagination.total = 0;
          ElMessage.error(res.msg || "查询失败");
        }
      })
      .catch(() => {
        dataList.value = [];
        pagination.total = 0;
        ElMessage.error("查询失败");
      })
      .finally(() => {
        tableLoading.value = false;
      });
  };
  const onSearch = () => {
    pagination.currentPage = 1;
    getTableData();
  };
  const resetFilters = () => {
    filters.paymentNumber = "";
    filters.supplierId = "";
    filters.paymentMethod = "";
    filters.dateRange = [];
    pagination.currentPage = 1;
    getTableData();
  };
  const changePage = ({ page, limit }) => {
    pagination.currentPage = page;
    pagination.pageSize = limit;
    getTableData();
  };
  const handleDelete = row => {
    ElMessageBox.confirm(`确认删除付款单「${row.paymentNumber}」吗?`, "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      deleteAccountPurchasePayment([row.id])
        .then(res => {
          if (res.code === 200) {
            ElMessage.success("删除成功");
            getTableData();
          } else {
            ElMessage.error(res.msg || "删除失败");
          }
        })
        .catch(() => {
          ElMessage.error("删除失败");
        });
    });
  };
  onMounted(() => {
    getSupplierList();
    getTableData();
  });
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
}
  .actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15px;
  }
.text-danger {
  color: #f56c6c;
  font-weight: bold;
}
  .text-danger {
    color: #f56c6c;
    font-weight: bold;
  }
</style>
src/views/financialManagement/payable/paymentApply.vue
@@ -2,23 +2,40 @@
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="申请单号:">
        <el-input v-model="filters.applyCode" placeholder="请输入申请单号" clearable style="width: 200px;" />
        <el-input v-model="filters.invoiceApplicationNo" placeholder="请输入申请单号" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="供应商:">
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable style="width: 200px;">
          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable filterable style="width: 200px;">
          <el-option
            v-for="item in supplierList"
            :key="item.id"
            :label="item.supplierName"
            :value="item.id"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="状态:">
      <el-form-item label="审核状态:">
        <el-select v-model="filters.status" placeholder="请选择状态" clearable style="width: 150px;">
          <el-option label="待审批" value="pending" />
          <el-option label="已审批" value="approved" />
          <el-option label="已驳回" value="rejected" />
          <el-option label="已付款" value="paid" />
          <el-option label="待审核" :value="0" />
          <el-option label="审核通过" :value="1" />
          <el-option label="审核不通过" :value="2" />
        </el-select>
      </el-form-item>
      <el-form-item label="申请日期:">
        <el-date-picker
          v-model="filters.dateRange"
          type="daterange"
          value-format="YYYY-MM-DD"
          format="YYYY-MM-DD"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          clearable
          style="width: 240px;"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button type="primary" @click="onSearch">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
@@ -27,20 +44,19 @@
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增申请</el-button>
          <el-button @click="handleBatchApply" icon="Document" :disabled="selectedRows.length === 0">批量申请</el-button>
          <el-button @click="handleExport" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        isSelection
        :column="columns"
        :tableData="dataList"
        :tableLoading="tableLoading"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @selection-change="handleSelectionChange"
        @pagination="changePage"
      >
        <template #amount="{ row }">
@@ -54,90 +70,291 @@
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">编辑</el-button>
          <el-button type="success" link @click="handleAudit(row)" v-if="row.status === 'pending'">审批</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="isPendingStatus(row.status)">编辑</el-button>
          <el-button type="success" link @click="handleAudit(row)" v-if="isPendingStatus(row.status)">审核</el-button>
          <el-button type="warning" link @click="openPaymentDialog(row)" v-if="isApprovedStatus(row.status)">付款</el-button>
          <el-button type="danger" link @click="handleDelete(row)" v-if="isPendingStatus(row.status)">删除</el-button>
        </template>
      </PIMTable>
    </div>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
    <FormDialog
      :title="dialogTitle"
      v-model="dialogVisible"
      width="800px"
      :operation-type="isView ? 'detail' : ''"
      @confirm="submitForm"
      @cancel="closeDialog"
    >
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row v-if="isView" :gutter="20">
          <el-col :span="12">
            <el-form-item label="审核状态">
              <el-tag :type="getStatusType(form.status)">{{ getStatusLabel(form.status) }}</el-tag>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="申请单号" prop="applyCode">
              <el-input v-model="form.applyCode" placeholder="系统自动生成" disabled />
            <el-form-item label="申请单号" prop="invoiceApplicationNo">
              <el-input v-model="form.invoiceApplicationNo" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="供应商" prop="supplierId">
              <el-select v-model="form.supplierId" placeholder="请选择供应商" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
              <el-select
                v-model="form.supplierId"
                placeholder="请选择供应商"
                style="width: 100%;"
                filterable
                :disabled="isEdit || isView"
                @change="handleSupplierChange"
              >
                <el-option
                  v-for="item in supplierList"
                  :key="item.id"
                  :label="item.supplierName"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="付款金额" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
            <el-form-item label="关联入库单" prop="stockInRecordIds">
              <el-input
                :model-value="inboundBatchDisplayText"
                placeholder="请先选择供应商"
                readonly
                :disabled="!form.supplierId || isEdit || isView"
                class="inbound-batch-input"
                @click="handleInboundInputClick"
              >
                <template v-if="!isEdit && !isView" #append>
                  <el-button
                    :disabled="!form.supplierId"
                    :loading="inboundBatchLoading"
                    @click.stop="openInboundSelectDialog"
                  >
                    é€‰æ‹©
                  </el-button>
                </template>
              </el-input>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="申请日期" prop="applyDate">
              <el-date-picker
                v-model="form.applyDate"
                type="date"
                placeholder="选择日期"
                value-format="YYYY-MM-DD"
                style="width: 100%;"
                :disabled="isView"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="付款金额" prop="paymentAmount">
              <el-input-number
                v-model="form.paymentAmount"
                :min="0"
                :precision="2"
                style="width: 100%;"
                :disabled="isView"
                placeholder="根据入库单自动汇总,可修改"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款方式" prop="paymentMethod">
              <el-select v-model="form.paymentMethod" placeholder="请选择付款方式" style="width: 100%;">
                <el-option label="银行转账" value="bank_transfer" />
                <el-option label="现金" value="cash" />
                <el-option label="支票" value="check" />
                <el-option label="汇票" value="draft" />
              <el-select
                v-model="form.paymentMethod"
                placeholder="请选择付款方式"
                style="width: 100%;"
                :disabled="isView"
              >
                <el-option
                  v-for="item in checkout_payment"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="付款事由" prop="paymentContent">
          <el-input
            v-model="form.paymentContent"
            type="textarea"
            :rows="3"
            placeholder="请输入付款事由"
            :disabled="isView"
          />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" :disabled="isView" />
        </el-form-item>
      </el-form>
      <template v-if="!isView" #footer>
        <el-button type="primary" :loading="submitLoading" @click="submitForm">确定</el-button>
        <el-button @click="closeDialog">取消</el-button>
      </template>
    </FormDialog>
    <FormDialog
      title="付款"
      v-model="paymentDialogVisible"
      width="800px"
      @confirm="submitPayment"
      @cancel="paymentDialogVisible = false"
    >
      <el-form :model="paymentForm" :rules="paymentRules" ref="paymentFormRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="付款单号" prop="paymentNumber">
              <el-input v-model="paymentForm.paymentNumber" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="关联申请单" prop="invoiceApplicationNo">
              <el-input v-model="paymentForm.invoiceApplicationNo" disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="申请日期" prop="applyDate">
              <el-date-picker v-model="form.applyDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            <el-form-item label="供应商">
              <el-input v-model="paymentForm.supplierName" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="期望付款日期" prop="expectedDate">
              <el-date-picker v-model="form.expectedDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            <el-form-item label="付款日期" prop="paymentDate">
              <el-date-picker
                v-model="paymentForm.paymentDate"
                type="date"
                placeholder="选择日期"
                value-format="YYYY-MM-DD"
                style="width: 100%;"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="关联入库单" prop="relatedDocs">
          <el-select v-model="form.relatedDocs" multiple placeholder="请选择关联入库单" style="width: 100%;">
            <el-option v-for="item in inList" :key="item.inCode" :label="item.inCode" :value="item.inCode" />
          </el-select>
        </el-form-item>
        <el-form-item label="付款事由" prop="reason">
          <el-input v-model="form.reason" type="textarea" :rows="3" placeholder="请输入付款事由" />
        </el-form-item>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="付款金额" prop="paymentAmount">
              <el-input-number
                v-model="paymentForm.paymentAmount"
                :min="0"
                :precision="2"
                style="width: 100%;"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款方式" prop="paymentMethod">
              <el-select v-model="paymentForm.paymentMethod" placeholder="请选择付款方式" style="width: 100%;">
                <el-option
                  v-for="item in checkout_payment"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row v-if="isBankTransferPayment(paymentForm.paymentMethod)" :gutter="20">
          <el-col :span="12">
            <el-form-item label="银行账号" prop="bankAccount">
              <el-input v-model="paymentForm.bankAccount" placeholder="银行账号" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="开户行" prop="bankName">
              <el-input v-model="paymentForm.bankName" placeholder="开户行" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
          <el-input v-model="paymentForm.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="paymentSubmitLoading" @click="submitPayment">确定</el-button>
        <el-button @click="paymentDialogVisible = false">取消</el-button>
      </template>
    </FormDialog>
    <el-dialog
      v-model="inboundSelectVisible"
      title="选择入库单号"
      width="1100px"
      append-to-body
      destroy-on-close
      :close-on-click-modal="false"
      @closed="handleInboundDialogClosed"
    >
      <el-table
        ref="inboundTableRef"
        v-loading="inboundBatchLoading"
        :data="inboundBatchList"
        row-key="id"
        border
        stripe
        max-height="480"
        @selection-change="handleInboundDialogSelectionChange"
      >
        <el-table-column type="selection" width="55" align="center" />
        <el-table-column prop="inboundBatches" label="入库单号" min-width="140" show-overflow-tooltip />
        <el-table-column prop="supplierName" label="供应商" min-width="120" show-overflow-tooltip />
        <el-table-column prop="productName" label="产品名称" min-width="120" show-overflow-tooltip />
        <el-table-column prop="specificationModel" label="规格型号" min-width="140" show-overflow-tooltip />
        <el-table-column prop="purchaseContractNumber" label="采购订单号" min-width="140" show-overflow-tooltip />
        <el-table-column prop="inboundDate" label="入库日期" width="110" align="center" />
        <el-table-column prop="inboundAmount" label="入库金额(含税)" width="120" align="right">
          <template #default="{ row }">Â¥{{ formatMoney(getInboundRowTaxInclusiveAmount(row)) }}</template>
        </el-table-column>
      </el-table>
      <template #footer>
        <el-button type="primary" @click="confirmInboundSelection">确定</el-button>
        <el-button @click="inboundSelectVisible = false">取消</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ref, reactive, computed, onMounted, nextTick, getCurrentInstance } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { getOptions } from "@/api/procurementManagement/procurementLedger.js";
import {
  getInboundBatchesBySupplier,
  addAccountPaymentApplication,
  listPageAccountPaymentApplication,
  updateAccountPaymentApplication,
  auditAccountPaymentApplication,
  deleteAccountPaymentApplication,
} from "@/api/financialManagement/accountPaymentApplication.js";
import { addAccountPurchasePayment } from "@/api/financialManagement/accountPurchasePayment.js";
defineOptions({
  name: "付款申请",
});
const { proxy } = getCurrentInstance();
const { checkout_payment } = proxy.useDict("checkout_payment");
const filters = reactive({
  applyCode: "",
  invoiceApplicationNo: "",
  supplierId: "",
  status: "",
  dateRange: [],
});
const pagination = reactive({
@@ -149,199 +366,634 @@
const columns = [
  { label: "申请单号", prop: "applyCode", width: "150" },
  { label: "供应商", prop: "supplierName", width: "180" },
  { label: "付款金额", prop: "amount", slot: "amount" },
  { label: "付款方式", prop: "paymentMethod", slot: "paymentMethod" },
  { label: "付款金额", prop: "amount", dataType: "slot", slot: "amount" },
  { label: "付款方式", prop: "paymentMethod", dataType: "slot", slot: "paymentMethod", width: "120" },
  { label: "申请日期", prop: "applyDate", width: "120" },
  { label: "期望付款日", prop: "expectedDate", width: "120" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "操作", prop: "operation", slot: "operation", width: "200", fixed: "right" },
  { label: "状态", prop: "status", dataType: "slot", slot: "status", width: "100" },
  { label: "操作", prop: "operation", dataType: "slot", slot: "operation", width: "260", fixed: "right" },
];
const dataList = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const isView = ref(false);
const submitLoading = ref(false);
const currentId = ref(null);
const supplierList = ref([]);
const supplierList = [
  { id: 1, name: "北京原材料供应商" },
  { id: 2, name: "上海电子元器件公司" },
  { id: 3, name: "广州包装材料厂" },
  { id: 4, name: "深圳五金配件公司" },
];
const inboundBatchList = ref([]);
const inboundBatchOptions = ref([]);
const inboundBatchLoading = ref(false);
const inboundSelectVisible = ref(false);
const inboundTableRef = ref(null);
const dialogInboundSelection = ref([]);
const inList = [
  { inCode: "RK2024001", supplierId: 1 },
  { inCode: "RK2024002", supplierId: 2 },
  { inCode: "RK2024003", supplierId: 3 },
];
const paymentDialogVisible = ref(false);
const paymentFormRef = ref(null);
const paymentSubmitLoading = ref(false);
const paymentForm = reactive({
  paymentNumber: "",
  invoiceApplicationNo: "",
  supplierName: "",
  supplierId: "",
  accountPaymentApplicationId: null,
  paymentDate: "",
  paymentAmount: 0,
  paymentMethod: "",
  bankAccount: "",
  bankName: "",
  remark: "",
});
const paymentRules = {
  paymentDate: [{ required: true, message: "请选择付款日期", trigger: "change" }],
  paymentAmount: [{ required: true, message: "请输入付款金额", trigger: "blur" }],
  paymentMethod: [{ required: true, message: "请选择付款方式", trigger: "change" }],
};
const STATUS_LABEL_MAP = { 0: "待审核", 1: "审核通过", 2: "审核不通过" };
const STATUS_TYPE_MAP = { 0: "warning", 1: "success", 2: "danger" };
const form = reactive({
  applyCode: "",
  invoiceApplicationNo: "",
  supplierId: "",
  amount: 0,
  paymentMethod: "bank_transfer",
  paymentAmount: 0,
  paymentMethod: "",
  applyDate: "",
  expectedDate: "",
  relatedDocs: [],
  reason: "",
  paymentContent: "",
  remark: "",
  stockInRecordIds: [],
  inboundBatches: "",
  status: 0,
});
const rules = {
  supplierId: [{ required: true, message: "请选择供应商", trigger: "change" }],
  amount: [{ required: true, message: "请输入付款金额", trigger: "blur" }],
  stockInRecordIds: [{ required: true, type: "array", min: 1, message: "请选择关联入库单", trigger: "change" }],
  paymentAmount: [{ required: true, message: "请输入付款金额", trigger: "blur" }],
  paymentMethod: [{ required: true, message: "请选择付款方式", trigger: "change" }],
  applyDate: [{ required: true, message: "请选择申请日期", trigger: "change" }],
  expectedDate: [{ required: true, message: "请选择期望付款日期", trigger: "change" }],
};
const mockData = [
  { id: 1, applyCode: "FK2024001", supplierId: 1, supplierName: "北京原材料供应商", amount: 5000, paymentMethod: "bank_transfer", applyDate: "2024-01-12", expectedDate: "2024-01-15", status: "pending", relatedDocs: ["RK2024001"], reason: "支付原材料货款", remark: "" },
  { id: 2, applyCode: "FK2024002", supplierId: 2, supplierName: "上海电子元器件公司", amount: 8000, paymentMethod: "bank_transfer", applyDate: "2024-01-14", expectedDate: "2024-01-18", status: "approved", relatedDocs: ["RK2024002"], reason: "支付电子元器件货款", remark: "" },
  { id: 3, applyCode: "FK2024003", supplierId: 3, supplierName: "广州包装材料厂", amount: 3000, paymentMethod: "cash", applyDate: "2024-01-16", expectedDate: "2024-01-20", status: "paid", relatedDocs: ["RK2024003"], reason: "支付包装材料货款", remark: "" },
];
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getPaymentMethodLabel = (method) => {
  const map = {
    bank_transfer: "银行转账",
    cash: "现金",
    check: "支票",
    draft: "汇票",
const normalizeStatus = (status) => {
  if (status === undefined || status === null || status === "") return 0;
  const num = Number(status);
  return Number.isNaN(num) ? 0 : num;
};
const isPendingStatus = (status) => normalizeStatus(status) === 0;
const isApprovedStatus = (status) => normalizeStatus(status) === 1;
const isBankTransferPayment = (method) => {
  if (method === undefined || method === null || method === "") return false;
  const item = checkout_payment.value?.find((m) => String(m.value) === String(method));
  if (item?.label?.includes("银行")) return true;
  return String(method) === "bank_transfer" || String(method).toLowerCase().includes("bank");
};
const getStatusLabel = (status) => STATUS_LABEL_MAP[normalizeStatus(status)] ?? "待审核";
const getStatusType = (status) => STATUS_TYPE_MAP[normalizeStatus(status)] ?? "warning";
const getPaymentMethodLabel = (value) => {
  if (value === undefined || value === null || value === "") return "-";
  const item = checkout_payment.value?.find((m) => String(m.value) === String(value));
  return item?.label ?? value;
};
const getDefaultPaymentMethod = () => checkout_payment.value?.[0]?.value ?? "";
const parseStockInRecordIds = (value) => {
  if (!value) return [];
  if (Array.isArray(value)) return value;
  return String(value)
    .split(/[,,]/)
    .map((s) => s.trim())
    .filter(Boolean)
    .map((s) => (/^\d+$/.test(s) ? Number(s) : s));
};
const formatInboundBatches = (value) => {
  if (value === undefined || value === null || value === "") return "";
  if (Array.isArray(value)) return value.filter(Boolean).join("、");
  return String(value)
    .split(/[,,]/)
    .map((s) => s.trim())
    .filter(Boolean)
    .join("、");
};
const isSameInboundId = (a, b) => String(a) === String(b);
const getInboundRowId = (row) => row?.id ?? row?.stockInRecordId;
const getInboundRowTaxInclusiveAmount = (row) =>
  Number(row?.inboundAmount ?? row?.taxInclusivePrice ?? row?.totalAmount ?? row?.amount ?? 0);
const normalizeInboundBatchOptions = (data) => {
  const list = Array.isArray(data) ? data : [];
  return list.map((item, index) => {
    const label =
      item.inboundBatches ?? item.batchNo ?? item.inboundNo ?? `入库单${index + 1}`;
    const value = item.id ?? item.stockInRecordId ?? label;
    return {
      label: String(label),
      value,
      inboundAmount: getInboundRowTaxInclusiveAmount(item),
    };
  });
};
const syncPaymentAmount = () => {
  const selected = form.stockInRecordIds || [];
  let sum = inboundBatchOptions.value
    .filter((opt) => selected.some((id) => isSameInboundId(id, opt.value)))
    .reduce((acc, opt) => acc + (Number(opt.inboundAmount) || 0), 0);
  if (sum <= 0 && selected.length) {
    sum = inboundBatchList.value
      .filter((row) => selected.some((id) => isSameInboundId(id, getInboundRowId(row))))
      .reduce((acc, row) => acc + getInboundRowTaxInclusiveAmount(row), 0);
  }
  form.paymentAmount = sum > 0 ? Number(sum.toFixed(2)) : 0;
};
const inboundBatchDisplayText = computed(() => {
  if (form.inboundBatches) return form.inboundBatches;
  const ids = form.stockInRecordIds || [];
  if (!ids.length) return "";
  const labels = inboundBatchOptions.value
    .filter((opt) => ids.some((id) => isSameInboundId(id, opt.value)))
    .map((opt) => opt.label);
  if (labels.length) return labels.join("、");
  return ids.join("、");
});
const normalizeTableRow = (row) => ({
  ...row,
  applyCode: row.invoiceApplicationNo ?? row.applyCode,
  amount: row.paymentAmount ?? row.amount,
  reason: row.paymentContent ?? row.reason,
  status: normalizeStatus(row.status),
  stockInRecordIds: row.stockInRecordIds ?? "",
  inboundBatches: formatInboundBatches(row.inboundBatches),
});
const fillFormFromRow = (row) => {
  const stockInRecordIds = parseStockInRecordIds(row.stockInRecordIds);
  Object.assign(form, {
    invoiceApplicationNo: row.invoiceApplicationNo ?? row.applyCode ?? "",
    supplierId: row.supplierId,
    paymentAmount: Number(row.paymentAmount ?? row.amount ?? 0),
    paymentMethod: row.paymentMethod ?? getDefaultPaymentMethod(),
    applyDate: row.applyDate ?? "",
    paymentContent: row.paymentContent ?? row.reason ?? "",
    remark: row.remark ?? "",
    stockInRecordIds,
    inboundBatches: formatInboundBatches(row.inboundBatches),
    status: normalizeStatus(row.status),
  });
};
const buildPayloadFromRow = (row, statusOverride) => ({
  id: row.id,
  supplierId: row.supplierId,
  stockInRecordIds:
    typeof row.stockInRecordIds === "string"
      ? row.stockInRecordIds
      : (row.stockInRecordIds || []).join(","),
  invoiceApplicationNo: row.invoiceApplicationNo ?? row.applyCode ?? "",
  paymentMethod: row.paymentMethod,
  paymentContent: row.paymentContent ?? row.reason ?? "",
  applyDate: row.applyDate,
  remark: row.remark ?? "",
  status: statusOverride !== undefined ? statusOverride : normalizeStatus(row.status),
  paymentAmount: Number(row.paymentAmount ?? row.amount ?? 0),
});
const buildSubmitPayload = (forUpdate = false) => {
  const payload = {
    supplierId: form.supplierId,
    stockInRecordIds: (form.stockInRecordIds || []).join(","),
    invoiceApplicationNo: form.invoiceApplicationNo || "",
    paymentMethod: form.paymentMethod,
    paymentContent: form.paymentContent || "",
    applyDate: form.applyDate,
    remark: form.remark || "",
    status: 0,
    paymentAmount: form.paymentAmount,
  };
  return map[method] || method;
  if (forUpdate) {
    payload.id = currentId.value;
  }
  return payload;
};
const getStatusLabel = (status) => {
  const map = { pending: "待审批", approved: "已审批", rejected: "已驳回", paid: "已付款" };
  return map[status] || status;
const getSupplierList = () => {
  getOptions().then((res) => {
    if (res.code === 200) {
      supplierList.value = res.data ?? [];
    }
  });
};
const getStatusType = (status) => {
  const map = { pending: "warning", approved: "success", rejected: "danger", paid: "primary" };
  return map[status] || "";
const appendFilterParams = (params) => {
  if (filters.invoiceApplicationNo) {
    params.invoiceApplicationNo = filters.invoiceApplicationNo;
  }
  if (filters.supplierId) {
    params.supplierId = filters.supplierId;
  }
  if (filters.status !== "" && filters.status != null) {
    params.status = filters.status;
  }
  if (filters.dateRange?.length === 2) {
    params.startDate = filters.dateRange[0];
    params.endDate = filters.dateRange[1];
  }
  return params;
};
const buildListParams = () =>
  appendFilterParams({
    current: pagination.currentPage,
    size: pagination.pageSize,
  });
const buildExportParams = () => appendFilterParams({});
const handleExport = () => {
  proxy.download(
    "/accountPaymentApplication/exportAccountPaymentApplication",
    buildExportParams(),
    `付款申请_${Date.now()}.xlsx`
  );
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.applyCode) {
    result = result.filter(item => item.applyCode.includes(filters.applyCode));
  }
  if (filters.supplierId) {
    result = result.filter(item => item.supplierId === filters.supplierId);
  }
  if (filters.status) {
    result = result.filter(item => item.status === filters.status);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
  tableLoading.value = true;
  listPageAccountPaymentApplication(buildListParams())
    .then((res) => {
      if (res.code === 200) {
        dataList.value = (res.data?.records ?? []).map(normalizeTableRow);
        pagination.total = res.data?.total ?? 0;
      } else {
        dataList.value = [];
        pagination.total = 0;
        ElMessage.error(res.msg || "查询失败");
      }
    })
    .catch(() => {
      dataList.value = [];
      pagination.total = 0;
      ElMessage.error("查询失败");
    })
    .finally(() => {
      tableLoading.value = false;
    });
};
const resetFilters = () => {
  filters.applyCode = "";
  filters.supplierId = "";
  filters.status = "";
const onSearch = () => {
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
const resetFilters = () => {
  filters.invoiceApplicationNo = "";
  filters.supplierId = "";
  filters.status = "";
  filters.dateRange = [];
  pagination.currentPage = 1;
  getTableData();
};
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
const changePage = ({ page, limit }) => {
  pagination.currentPage = page;
  pagination.pageSize = limit;
  getTableData();
};
const closeDialog = () => {
  dialogVisible.value = false;
  isView.value = false;
  isEdit.value = false;
  inboundSelectVisible.value = false;
};
const resetForm = () => {
  Object.assign(form, {
    invoiceApplicationNo: "",
    supplierId: "",
    paymentAmount: 0,
    paymentMethod: getDefaultPaymentMethod(),
    applyDate: new Date().toISOString().split("T")[0],
    paymentContent: "",
    remark: "",
    stockInRecordIds: [],
    inboundBatches: "",
    status: 0,
  });
  inboundBatchList.value = [];
  inboundBatchOptions.value = [];
};
const add = () => {
  isEdit.value = false;
  isView.value = false;
  dialogTitle.value = "新增付款申请";
  Object.assign(form, {
    applyCode: "FK" + Date.now().toString().slice(-8),
    supplierId: "",
    amount: 0,
    paymentMethod: "bank_transfer",
    applyDate: new Date().toISOString().split('T')[0],
    expectedDate: "",
    relatedDocs: [],
    reason: "",
    remark: "",
  });
  resetForm();
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  isView.value = false;
  currentId.value = row.id;
  dialogTitle.value = "编辑付款申请";
  Object.assign(form, row);
  fillFormFromRow(row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看申请单: ${row.applyCode}`);
  isView.value = true;
  isEdit.value = false;
  dialogTitle.value = "查看付款申请";
  fillFormFromRow(row);
  if (row.supplierId) {
    loadInboundBatches(row.supplierId, true, false);
  }
  dialogVisible.value = true;
};
const submitAudit = (row, status) => {
  auditAccountPaymentApplication(buildPayloadFromRow(row, status))
    .then((res) => {
      if (res.code === 200) {
        ElMessage.success(status === 1 ? "审核通过" : "审核不通过");
        getTableData();
      } else {
        ElMessage.error(res.msg || "审核失败");
      }
    })
    .catch(() => {
      ElMessage.error("审核失败");
    });
};
const handleAudit = (row) => {
  ElMessageBox.confirm("确认审批通过该付款申请吗?", "提示", {
    confirmButtonText: "通过",
    cancelButtonText: "驳回",
  ElMessageBox.confirm("请选择审核结果", "付款申请审核", {
    confirmButtonText: "审核通过",
    cancelButtonText: "审核不通过",
    distinguishCancelAndClose: true,
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "approved";
    }
    ElMessage.success("审批通过");
    getTableData();
  }).catch((action) => {
    if (action === "cancel") {
      const index = mockData.findIndex(item => item.id === row.id);
      if (index !== -1) {
        mockData[index].status = "rejected";
  })
    .then(() => {
      submitAudit(row, 1);
    })
    .catch((action) => {
      if (action === "cancel") {
        submitAudit(row, 2);
      }
      ElMessage.warning("已驳回");
      getTableData();
    }
    });
};
const openPaymentDialog = (row) => {
  Object.assign(paymentForm, {
    paymentNumber: "",
    invoiceApplicationNo: row.invoiceApplicationNo ?? row.applyCode ?? "",
    supplierName: row.supplierName ?? "",
    supplierId: row.supplierId,
    accountPaymentApplicationId: row.id,
    paymentDate: new Date().toISOString().split("T")[0],
    paymentAmount: Number(row.paymentAmount ?? row.amount ?? 0),
    paymentMethod: row.paymentMethod ?? getDefaultPaymentMethod(),
    bankAccount: row.bankAccountNum ?? row.bankAccount ?? "",
    bankName: row.bankAccountName ?? row.bankName ?? "",
    remark: "",
  });
  paymentDialogVisible.value = true;
  nextTick(() => {
    paymentFormRef.value?.clearValidate();
  });
};
const handleBatchApply = () => {
  ElMessage.success(`批量申请 ${selectedRows.value.length} æ¡è®°å½•`);
const submitPayment = () => {
  paymentFormRef.value?.validate((valid) => {
    if (!valid) return;
    paymentSubmitLoading.value = true;
    addAccountPurchasePayment({
      accountPaymentApplicationId: paymentForm.accountPaymentApplicationId,
      supplierId: paymentForm.supplierId,
      paymentDate: paymentForm.paymentDate,
      paymentMethod: paymentForm.paymentMethod,
      paymentAmount: paymentForm.paymentAmount,
      paymentNumber: paymentForm.paymentNumber || "",
      remark: paymentForm.remark || "",
    })
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success("付款成功");
          paymentDialogVisible.value = false;
          getTableData();
        } else {
          ElMessage.error(res.msg || "付款失败");
        }
      })
      .catch(() => {
        ElMessage.error("付款失败");
      })
      .finally(() => {
        paymentSubmitLoading.value = false;
      });
  });
};
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除申请单「${row.applyCode ?? row.invoiceApplicationNo}」吗?`, "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    deleteAccountPaymentApplication([row.id])
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success("删除成功");
          getTableData();
        } else {
          ElMessage.error(res.msg || "删除失败");
        }
      })
      .catch(() => {
        ElMessage.error("删除失败");
      });
  });
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const supplier = supplierList.find(item => item.id === form.supplierId);
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name };
  formRef.value?.validate((valid) => {
    if (!valid) return;
    submitLoading.value = true;
    const request = isEdit.value
      ? updateAccountPaymentApplication(buildSubmitPayload(true))
      : addAccountPaymentApplication(buildSubmitPayload(false));
    request
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success(isEdit.value ? "编辑成功" : "新增成功");
          closeDialog();
          pagination.currentPage = 1;
          getTableData();
        } else {
          ElMessage.error(res.msg || "保存失败");
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, supplierName: supplier?.name, status: "pending" });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
      })
      .catch(() => {
        ElMessage.error("保存失败");
      })
      .finally(() => {
        submitLoading.value = false;
      });
  });
};
const ensureInboundOptionsForSelected = () => {
  const ids = form.stockInRecordIds || [];
  ids.forEach((id) => {
    const exists = inboundBatchOptions.value.some((opt) => isSameInboundId(opt.value, id));
    if (exists) return;
    const fromList = inboundBatchList.value.find((row) => isSameInboundId(getInboundRowId(row), id));
    if (fromList) {
      const [option] = normalizeInboundBatchOptions([fromList]);
      if (option) inboundBatchOptions.value.push(option);
      return;
    }
    inboundBatchOptions.value.push({
      label: String(id),
      value: id,
      inboundAmount: 0,
    });
  });
};
const restoreInboundTableSelection = () => {
  nextTick(() => {
    const table = inboundTableRef.value;
    if (!table) return;
    table.clearSelection();
    const selectedIds = new Set((form.stockInRecordIds || []).map((id) => String(id)));
    inboundBatchList.value.forEach((row) => {
      const rowId = getInboundRowId(row);
      if (rowId !== undefined && rowId !== null && selectedIds.has(String(rowId))) {
        table.toggleRowSelection(row, true);
      }
    });
  });
};
const loadInboundBatches = (supplierId, keepSelected = false, syncAmount = true) => {
  if (!supplierId) {
    inboundBatchList.value = [];
    inboundBatchOptions.value = [];
    if (!keepSelected) {
      form.stockInRecordIds = [];
      form.inboundBatches = "";
      form.paymentAmount = 0;
    }
    return Promise.resolve();
  }
  inboundBatchLoading.value = true;
  return getInboundBatchesBySupplier({ supplierId })
    .then((res) => {
      if (res.code === 200) {
        const list = res.data?.records ?? res.data ?? [];
        inboundBatchList.value = Array.isArray(list) ? list : [];
        inboundBatchOptions.value = normalizeInboundBatchOptions(list);
      } else {
        inboundBatchList.value = [];
        inboundBatchOptions.value = [];
      }
    })
    .catch(() => {
      inboundBatchList.value = [];
      inboundBatchOptions.value = [];
    })
    .finally(() => {
      inboundBatchLoading.value = false;
      if (keepSelected) {
        ensureInboundOptionsForSelected();
        restoreInboundTableSelection();
        if (syncAmount && !isView.value) {
          syncPaymentAmount();
        }
      }
    });
};
const handleSupplierChange = (supplierId) => {
  form.stockInRecordIds = [];
  form.inboundBatches = "";
  form.paymentAmount = 0;
  loadInboundBatches(supplierId);
};
const handleInboundInputClick = () => {
  if (isEdit.value || isView.value) return;
  openInboundSelectDialog();
};
const openInboundSelectDialog = () => {
  if (!form.supplierId || isEdit.value || isView.value) return;
  inboundSelectVisible.value = true;
  loadInboundBatches(form.supplierId, true, false).then(() => {
    restoreInboundTableSelection();
  });
};
const handleInboundDialogSelectionChange = (selection) => {
  dialogInboundSelection.value = selection;
};
const confirmInboundSelection = () => {
  if (dialogInboundSelection.value.length === 0) {
    ElMessage.warning("请至少选择一条入库单");
    return;
  }
  form.stockInRecordIds = dialogInboundSelection.value
    .map((row) => getInboundRowId(row))
    .filter((id) => id !== undefined && id !== null);
  form.inboundBatches = dialogInboundSelection.value
    .map((row) => row.inboundBatches ?? row.batchNo ?? "")
    .filter(Boolean)
    .join("、");
  dialogInboundSelection.value.forEach((row) => {
    const [option] = normalizeInboundBatchOptions([row]);
    if (option && !inboundBatchOptions.value.some((opt) => isSameInboundId(opt.value, option.value))) {
      inboundBatchOptions.value.push(option);
    }
  });
  inboundSelectVisible.value = false;
  syncPaymentAmount();
  formRef.value?.validateField("stockInRecordIds");
};
const handleInboundDialogClosed = () => {
  dialogInboundSelection.value = [];
};
onMounted(() => {
  getSupplierList();
  getTableData();
});
</script>
@@ -357,4 +1009,8 @@
  color: #f56c6c;
  font-weight: bold;
}
.inbound-batch-input :deep(.el-input__wrapper) {
  cursor: pointer;
}
</style>
src/views/financialManagement/payable/purchaseIn.vue
@@ -1,27 +1,39 @@
<template>
  <!-- é‡‡è´­å…¥åº“ -->
  <div class="app-container">
    <el-form :model="filters" :inline="true">
    <el-form :model="filters"
             :inline="true">
      <el-form-item label="入库单号:">
        <el-input v-model="filters.inboundBatches" placeholder="请输入入库单号" clearable style="width: 200px;" />
        <el-input v-model="filters.inboundBatches"
                  placeholder="请输入入库单号"
                  clearable
                  style="width: 200px;" />
      </el-form-item>
      <el-form-item label="供应商:">
        <el-input v-model="filters.supplierName" placeholder="请输入供应商" clearable style="width: 200px;" />
        <el-select v-model="filters.supplierId"
                   placeholder="请选择供应商"
                   clearable
                   filterable
                   style="width: 200px;">
          <el-option v-for="item in supplierList"
                     :key="item.id"
                     :label="item.supplierName"
                     :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="入库日期:">
        <el-date-picker
          v-model="filters.dateRange"
          value-format="YYYY-MM-DD"
          format="YYYY-MM-DD"
          type="daterange"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          clearable
        />
        <el-date-picker v-model="filters.dateRange"
                        value-format="YYYY-MM-DD"
                        format="YYYY-MM-DD"
                        type="daterange"
                        range-separator="至"
                        start-placeholder="开始日期"
                        end-placeholder="结束日期"
                        clearable />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSearch">搜索</el-button>
        <el-button type="primary"
                   @click="onSearch">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
@@ -29,23 +41,22 @@
      <div class="actions">
        <div></div>
        <div>
          <el-button @click="handleOut" icon="Download">导出</el-button>
          <el-button @click="handleOut"
                     icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :tableLoading="tableLoading"
        :page="{
      <PIMTable rowKey="id"
                :column="columns"
                :tableData="dataList"
                :tableLoading="tableLoading"
                :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
                @pagination="changePage">
        <template #inboundDate="{ row }">
          {{ row.InboundDate || row.inboundDate || "" }}
          {{ row.inboundDate ?? row.InboundDate ?? "" }}
        </template>
      </PIMTable>
    </div>
@@ -53,119 +64,149 @@
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance } from "vue";
import { ElMessage } from "element-plus";
import { listPageAccountPurchase } from "@/api/financialManagement/accountPurchase";
  import { ref, reactive, onMounted, getCurrentInstance } from "vue";
  import { ElMessage } from "element-plus";
  import { listPageAccountPurchase } from "@/api/financialManagement/accountPurchase";
  import { listSupplier } from "@/api/basicData/supplierManageFile.js";
defineOptions({
  name: "采购入库",
});
  defineOptions({
    name: "采购入库",
  });
const { proxy } = getCurrentInstance();
  const { proxy } = getCurrentInstance();
const filters = reactive({
  inboundBatches: "",
  supplierName: "",
  dateRange: [],
});
  const filters = reactive({
    inboundBatches: "",
    supplierId: "",
    dateRange: [],
  });
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
  const pagination = reactive({
    currentPage: 1,
    pageSize: 10,
    total: 0,
  });
const columns = [
  { label: "入库单号", prop: "inboundBatches", minWidth: "150" },
  { label: "供应商", prop: "supplierName", minWidth: "180" },
  {
    label: "入库日期",
    prop: "InboundDate",
    minWidth: "170",
    dataType: "slot",
    slot: "inboundDate",
  },
  { label: "产品名称", prop: "productName", minWidth: "140" },
  { label: "产品规格", prop: "specificationModel", minWidth: "140" },
  { label: "采购订单号", prop: "purchaseContractNumber", minWidth: "150" },
];
  const columns = [
    { label: "入库单号", prop: "inboundBatches", minWidth: "150" },
    { label: "供应商", prop: "supplierName", minWidth: "180" },
    {
      label: "入库日期",
      prop: "inboundDate",
      minWidth: "170",
      dataType: "slot",
      slot: "inboundDate",
    },
    { label: "产品名称", prop: "productName", minWidth: "140" },
    { label: "产品规格", prop: "specificationModel", minWidth: "140" },
    {
      label: "金额",
      prop: "inboundAmount",
      minWidth: "120",
      align: "right",
      formatData: val =>
        val === null || val === undefined || val === ""
          ? ""
          : Number(val).toLocaleString("zh-CN", {
              minimumFractionDigits: 2,
              maximumFractionDigits: 2,
            }),
    },
    { label: "采购订单号", prop: "purchaseContractNumber", minWidth: "150" },
  ];
const dataList = ref([]);
const tableLoading = ref(false);
  const dataList = ref([]);
  const tableLoading = ref(false);
  const supplierList = ref([]);
function buildFilterParams() {
  const params = {
    inboundBatches: filters.inboundBatches || undefined,
    supplierName: filters.supplierName || undefined,
  const buildFilterParams = () => {
    const params = {};
    if (filters.inboundBatches) {
      params.inboundBatches = filters.inboundBatches;
    }
    if (filters.supplierId) {
      params.supplierId = filters.supplierId;
    }
    if (filters.dateRange?.length === 2) {
      params.startDate = filters.dateRange[0];
      params.endDate = filters.dateRange[1];
    }
    return params;
  };
  if (filters.dateRange && filters.dateRange.length === 2) {
    params.startDate = filters.dateRange[0];
    params.endDate = filters.dateRange[1];
  }
  return params;
}
const onSearch = () => {
  pagination.currentPage = 1;
  getTableData();
};
const getTableData = () => {
  tableLoading.value = true;
  listPageAccountPurchase({
    ...buildFilterParams(),
    current: pagination.currentPage,
    size: pagination.pageSize,
  })
    .then((res) => {
      const ok = res.code === 200 || res.code === 0;
      if (ok && res.data) {
        pagination.total = res.data.total ?? 0;
        dataList.value = res.data.records ?? [];
      } else {
        ElMessage.error(res.msg || "查询失败");
        dataList.value = [];
  const getSupplierList = () => {
    listSupplier({ current: -1, size: -1, isWhite: 0 }).then(res => {
      if (res.code === 200) {
        supplierList.value = res.data?.records ?? [];
      }
    })
    .catch(() => {
      dataList.value = [];
    })
    .finally(() => {
      tableLoading.value = false;
    });
};
  };
const resetFilters = () => {
  filters.inboundBatches = "";
  filters.supplierName = "";
  filters.dateRange = [];
  pagination.currentPage = 1;
  getTableData();
};
  const onSearch = () => {
    pagination.currentPage = 1;
    getTableData();
  };
const changePage = ({ page, limit }) => {
  pagination.currentPage = page;
  pagination.pageSize = limit;
  getTableData();
};
  const getTableData = () => {
    tableLoading.value = true;
    listPageAccountPurchase({
      ...buildFilterParams(),
      current: pagination.currentPage,
      size: pagination.pageSize,
    })
      .then(res => {
        const ok = res.code === 200 || res.code === 0;
        if (ok && res.data) {
          pagination.total = res.data.total ?? 0;
          dataList.value = res.data.records ?? [];
        } else {
          ElMessage.error(res.msg || "查询失败");
          dataList.value = [];
          pagination.total = 0;
        }
      })
      .catch(() => {
        dataList.value = [];
        pagination.total = 0;
        ElMessage.error("查询失败");
      })
      .finally(() => {
        tableLoading.value = false;
      });
  };
const handleOut = () => {
  proxy.download(
    "/accountPurchase/exportAccountPurchaseInbound",
    buildFilterParams(),
    `采购入库_${new Date().getTime()}.xlsx`
  );
};
  const resetFilters = () => {
    filters.inboundBatches = "";
    filters.supplierId = "";
    filters.dateRange = [];
    pagination.currentPage = 1;
    getTableData();
  };
onMounted(() => {
  getTableData();
});
  const changePage = ({ page, limit }) => {
    pagination.currentPage = page;
    pagination.pageSize = limit;
    getTableData();
  };
  const handleOut = () => {
    proxy.download(
      "/accountPurchase/exportAccountPurchaseInbound",
      buildFilterParams(),
      `采购入库_${Date.now()}.xlsx`
    );
  };
  onMounted(() => {
    getSupplierList();
    getTableData();
  });
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
}
  .actions {
    display: flex;
    justify-content: space-between;
    margin-bottom: 15px;
  }
</style>
src/views/financialManagement/payable/purchaseReturn.vue
@@ -1,26 +1,20 @@
<template>
  <!-- é‡‡è´­é€€è´§ -->
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="退货单号:">
        <el-input
          v-model="filters.returnNo"
          placeholder="请输入退货单号"
          clearable
          style="width: 200px"
        />
        <el-input v-model="filters.returnNo" placeholder="请输入退货单号" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="供应商:">
        <el-input
          v-model="filters.supplierName"
          placeholder="请输入供应商"
          clearable
          style="width: 200px"
        />
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable filterable style="width: 200px;">
          <el-option
            v-for="item in supplierList"
            :key="item.id"
            :label="item.supplierName"
            :value="item.id"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="退货日期:">
        <el-date-picker
          v-model="filters.dateRange"
@@ -33,23 +27,18 @@
          clearable
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSearch">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div></div>
        <div>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
@@ -57,9 +46,7 @@
        :tableLoading="tableLoading"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
@@ -68,14 +55,11 @@
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance } from "vue";
import { ElMessage } from "element-plus";
import { listPageAccountPurchaseReturn } from "@/api/financialManagement/accountPurchase";
import { listSupplier } from "@/api/basicData/supplierManageFile.js";
defineOptions({
  name: "采购退货",
@@ -85,38 +69,26 @@
const filters = reactive({
  returnNo: "",
  supplierName: "",
  supplierId: "",
  dateRange: [],
});
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "退货单号", prop: "returnNo", minWidth: "150" },
  { label: "供应商", prop: "supplierName", minWidth: "180" },
  { label: "关联入库单号", prop: "inboundBatches", minWidth: "150" },
  { label: "退货日期", prop: "preparedAt", minWidth: "170" },
  {
    label: "退款总额",
    prop: "totalAmount",
    minWidth: "150",
    align: "right",
    formatData: (val) =>
      val === null || val === undefined || val === ""
        ? ""
@@ -125,66 +97,65 @@
            maximumFractionDigits: 2,
          }),
  },
  { label: "退货方式", prop: "returnType", minWidth: "150" },
  { label: "采购订单号", prop: "purchaseContractNumber", minWidth: "150" },
];
const dataList = ref([]);
const tableLoading = ref(false);
const supplierList = ref([]);
function buildFilterParams() {
  const params = {
    returnNo: filters.returnNo || undefined,
    supplierName: filters.supplierName || undefined,
  };
  if (filters.dateRange && filters.dateRange.length === 2) {
const buildFilterParams = () => {
  const params = {};
  if (filters.returnNo) {
    params.returnNo = filters.returnNo;
  }
  if (filters.supplierId) {
    params.supplierId = filters.supplierId;
  }
  if (filters.dateRange?.length === 2) {
    params.startDate = filters.dateRange[0];
    params.endDate = filters.dateRange[1];
  }
  return params;
}
};
const getSupplierList = () => {
  listSupplier({ current: -1, size: -1, isWhite: 0 }).then((res) => {
    if (res.code === 200) {
      supplierList.value = res.data?.records ?? [];
    }
  });
};
const onSearch = () => {
  pagination.currentPage = 1;
  getTableData();
};
const getTableData = () => {
  tableLoading.value = true;
  listPageAccountPurchaseReturn({
    ...buildFilterParams(),
    current: pagination.currentPage,
    size: pagination.pageSize,
  })
    .then((res) => {
      const ok = res.code === 200 || res.code === 0;
      if (ok && res.data) {
        pagination.total = res.data.total ?? 0;
        dataList.value = res.data.records ?? [];
      } else {
        ElMessage.error(res.msg || "查询失败");
        dataList.value = [];
        pagination.total = 0;
      }
    })
    .catch(() => {
      dataList.value = [];
      pagination.total = 0;
      ElMessage.error("查询失败");
    })
    .finally(() => {
      tableLoading.value = false;
    });
@@ -192,48 +163,36 @@
const resetFilters = () => {
  filters.returnNo = "";
  filters.supplierName = "";
  filters.supplierId = "";
  filters.dateRange = [];
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ page, limit }) => {
  pagination.currentPage = page;
  pagination.pageSize = limit;
  getTableData();
};
const handleOut = () => {
  proxy.download(
    "/accountPurchase/exportAccountPurchaseReturn",
    buildFilterParams(),
    `采购退货_${new Date().getTime()}.xlsx`
    `采购退货_${Date.now()}.xlsx`
  );
};
onMounted(() => {
  getSupplierList();
  getTableData();
});
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
}
</style>
src/views/financialManagement/payable/reconciliation.vue
@@ -2,8 +2,13 @@
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="供应商:">
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable style="width: 200px;">
          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
        <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable filterable style="width: 200px;">
          <el-option
            v-for="item in supplierList"
            :key="item.id"
            :label="item.supplierName"
            :value="item.id"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="对账期间:">
@@ -12,7 +17,7 @@
        <el-date-picker v-model="filters.endMonth" type="month" placeholder="结束月份" value-format="YYYY-MM" style="width: 140px;" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button type="primary" @click="onSearch">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
@@ -29,6 +34,7 @@
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :tableLoading="tableLoading"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
@@ -36,21 +42,21 @@
        }"
        @pagination="changePage"
      >
        <template #beginBalance="{ row }">
          <span :class="row.beginBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.beginBalance) }}</span>
        <template #openingBalance="{ row }">
          <span :class="row.openingBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.openingBalance) }}</span>
        </template>
        <template #currentPayable="{ row }">
          <span class="text-danger">Â¥{{ formatMoney(row.currentPayable) }}</span>
        <template #currentPlan="{ row }">
          <span class="text-danger">Â¥{{ formatMoney(row.currentPlan) }}</span>
        </template>
        <template #currentPayment="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.currentPayment) }}</span>
        <template #currentActually="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.currentActually) }}</span>
        </template>
        <template #endBalance="{ row }">
          <span :class="row.endBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.endBalance) }}</span>
        <template #closingBalance="{ row }">
          <span :class="row.closingBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.closingBalance) }}</span>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="viewDetail(row)">查看明细</el-button>
          <el-button type="primary" link @click="printStatement(row)">打印</el-button>
          <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
        </template>
      </PIMTable>
    </div>
@@ -60,7 +66,7 @@
        <h3>{{ currentSupplier }} åº”付对账单</h3>
        <p>对账期间: {{ currentPeriod }}</p>
      </div>
      <el-table :data="detailData" border style="width: 100%">
      <el-table :data="detailData" border style="width: 100%" v-loading="detailLoading">
        <el-table-column prop="date" label="日期" width="120" />
        <el-table-column prop="type" label="类型" width="100">
          <template #default="{ row }">
@@ -77,6 +83,7 @@
        <el-table-column prop="credit" label="贷方(应付)" width="120">
          <template #default="{ row }">
            <span v-if="row.credit > 0" class="text-danger">Â¥{{ formatMoney(row.credit) }}</span>
            <span v-else-if="row.credit < 0" class="text-success">Â¥{{ formatMoney(Math.abs(row.credit)) }}</span>
            <span v-else>-</span>
          </template>
        </el-table-column>
@@ -98,52 +105,80 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="选择供应商" prop="supplierId">
              <el-select v-model="generateForm.supplierId" placeholder="请选择供应商" style="width: 100%;" @change="onSupplierChange">
                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
              <el-select
                v-model="generateForm.supplierId"
                placeholder="请选择供应商"
                style="width: 100%;"
                filterable
                @change="onSupplierChange"
              >
                <el-option
                  v-for="item in supplierList"
                  :key="item.id"
                  :label="item.supplierName"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="对账月份" prop="period">
              <el-date-picker v-model="generateForm.period" type="month" placeholder="选择月份" value-format="YYYY-MM" style="width: 100%;" @change="onPeriodChange" />
            <el-form-item label="对账月份" prop="statementMonth">
              <el-date-picker
                v-model="generateForm.statementMonth"
                type="month"
                placeholder="选择月份"
                value-format="YYYY-MM"
                style="width: 100%;"
                @change="onStatementMonthChange"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div v-if="purchaseData.length > 0" class="purchase-section">
        <div class="section-title">本月采购数据</div>
        <el-table :data="purchaseData" border style="width: 100%; margin-bottom: 15px;" v-loading="purchaseLoading" @selection-change="handlePurchaseSelectionChange">
      <div v-if="statementDetailLoaded" class="purchase-section">
        <div v-if="purchaseData.length > 0" class="section-title">本月采购数据</div>
        <el-table
          v-if="purchaseData.length > 0"
          ref="purchaseTableRef"
          :data="purchaseData"
          border
          row-key="id"
          style="width: 100%; margin-bottom: 15px;"
          v-loading="purchaseLoading"
          @selection-change="handlePurchaseSelectionChange"
        >
          <el-table-column type="selection" width="55" align="center" />
          <el-table-column prop="date" label="日期" width="120" />
          <el-table-column prop="code" label="单据编号" width="150" />
          <el-table-column prop="occurrenceDate" label="日期" width="120" />
          <el-table-column prop="receiptNumber" label="单据编号" width="150" />
          <el-table-column prop="type" label="类型" width="100">
            <template #default="{ row }">
              <el-tag :type="row.type === '入库' ? 'success' : 'danger'">{{ row.type }}</el-tag>
              <el-tag :type="getDetailTypeTagType(row.type)">{{ row.typeLabel }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="amount" label="金额" width="120">
            <template #default="{ row }">
              <span :class="row.type === '入库' ? 'text-danger' : 'text-success'">Â¥{{ formatMoney(row.amount) }}</span>
              <span :class="getDetailAmountClass(row.type)">Â¥{{ formatMoney(row.amount) }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="remark" label="备注" />
        </el-table>
        <el-empty v-else description="该供应商本月暂无明细数据" :image-size="80" />
        <div class="summary-row">
          <span>期初余额: <strong class="text-primary">Â¥{{ formatMoney(generateForm.beginBalance) }}</strong></span>
          <span>本期应付: <strong class="text-danger">Â¥{{ formatMoney(generateForm.currentPayable) }}</strong></span>
          <span>本期付款: <strong class="text-success">Â¥{{ formatMoney(generateForm.currentPayment) }}</strong></span>
          <span>期末余额: <strong class="text-primary">Â¥{{ formatMoney(calculateEndBalance(generateForm.beginBalance, generateForm.currentPayable, generateForm.currentPayment)) }}</strong></span>
          <span>期初余额: <strong class="text-primary">Â¥{{ formatMoney(generateForm.openingBalance) }}</strong></span>
          <span>本期应付: <strong class="text-danger">Â¥{{ formatMoney(generateForm.currentPlan) }}</strong></span>
          <span>本期付款: <strong class="text-success">Â¥{{ formatMoney(generateForm.currentActually) }}</strong></span>
          <span>期末余额: <strong :class="displayClosingBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(displayClosingBalance) }}</strong></span>
        </div>
      </div>
      <div v-else-if="generateForm.supplierId && !purchaseLoading" class="empty-tip">
      <div v-else-if="generateForm.supplierId && generateForm.statementMonth && !purchaseLoading" class="empty-tip">
        <el-empty description="该供应商本月暂无采购数据" />
      </div>
      <template #footer>
        <el-button type="primary" @click="confirmGenerate" :disabled="!canGenerate">确认生成</el-button>
        <el-button type="primary" @click="confirmGenerate" :disabled="!canGenerate" :loading="submitLoading">确认生成</el-button>
        <el-button @click="generateDialogVisible = false">取消</el-button>
      </template>
    </FormDialog>
@@ -151,9 +186,20 @@
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage } from "element-plus";
import { ref, reactive, onMounted, computed, nextTick, getCurrentInstance } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { getOptions } from "@/api/procurementManagement/procurementLedger.js";
import {
  getAccountStatementDetailsByMonth,
  addAccountStatement,
  listPageAccountStatement,
  deleteAccountStatement,
} from "@/api/financialManagement/accountStatement.js";
const ACCOUNT_TYPE_PAYABLE = 2;
const { proxy } = getCurrentInstance();
defineOptions({
  name: "应付对账",
@@ -172,56 +218,192 @@
});
const columns = [
  { label: "对账单号", prop: "statementCode", width: "150" },
  { label: "对账单号", prop: "statementNumber", width: "150" },
  { label: "供应商", prop: "supplierName", width: "180" },
  { label: "对账期间", prop: "period", width: "150" },
  { label: "期初余额", prop: "beginBalance", slot: "beginBalance" },
  { label: "本期应付", prop: "currentPayable", slot: "currentPayable" },
  { label: "本期付款", prop: "currentPayment", slot: "currentPayment" },
  { label: "期末余额", prop: "endBalance", slot: "endBalance" },
  { label: "操作", prop: "operation", slot: "operation", width: "150", fixed: "right" },
  { label: "对账期间", prop: "statementMonth", width: "150" },
  { label: "期初余额", prop: "openingBalance", dataType: "slot", slot: "openingBalance" },
  { label: "本期应付", prop: "currentPlan", dataType: "slot", slot: "currentPlan" },
  { label: "本期付款", prop: "currentActually", dataType: "slot", slot: "currentActually" },
  { label: "期末余额", prop: "closingBalance", dataType: "slot", slot: "closingBalance" },
  { label: "操作", prop: "operation", dataType: "slot", slot: "operation", width: "200", fixed: "right" },
];
const dataList = ref([]);
const tableLoading = ref(false);
const submitLoading = ref(false);
const detailDialogVisible = ref(false);
const currentSupplier = ref("");
const currentPeriod = ref("");
const detailData = ref([]);
const detailLoading = ref(false);
const generateDialogVisible = ref(false);
const purchaseLoading = ref(false);
const statementDetailLoaded = ref(false);
const purchaseData = ref([]);
const selectedPurchases = ref([]);
const purchaseTableRef = ref(null);
const supplierList = ref([]);
/** æ˜Žç»† type:1出库 2入库 3收款 4付款 5退货 */
const STATEMENT_DETAIL_TYPE_MAP = {
  1: "出库",
  2: "入库",
  3: "收款",
  4: "付款",
  5: "退货",
};
const calculateEndBalance = (openingBalance, currentPlan, currentActually) => {
  return openingBalance + currentPlan - currentActually;
};
const getDetailTypeLabel = (type) => STATEMENT_DETAIL_TYPE_MAP[Number(type)] ?? "";
const getDetailTypeTagType = (type) => {
  const t = Number(type);
  if (t === 2) return "success";
  if (t === 4) return "primary";
  if (t === 5) return "danger";
  return "info";
};
const getDetailAmountClass = (type) => {
  const t = Number(type);
  if (t === 2) return "text-danger";
  if (t === 4) return "text-success";
  return "text-danger";
};
const generateForm = reactive({
  supplierId: "",
  supplierName: "",
  period: "",
  beginBalance: 0,
  currentPayable: 0,
  currentPayment: 0,
  statementMonth: "",
  openingBalance: 0,
  currentPlan: 0,
  currentActually: 0,
  closingBalance: 0,
});
const canGenerate = computed(() => {
  return generateForm.supplierId && generateForm.period && selectedPurchases.value.length > 0;
});
const displayClosingBalance = computed(() =>
  calculateEndBalance(
    generateForm.openingBalance,
    generateForm.currentPlan,
    generateForm.currentActually
  )
);
const supplierList = [
  { id: 1, name: "北京原材料供应商" },
  { id: 2, name: "上海电子元器件公司" },
  { id: 3, name: "广州包装材料厂" },
  { id: 4, name: "深圳五金配件公司" },
];
const canGenerate = computed(
  () => generateForm.supplierId && generateForm.statementMonth && selectedPurchases.value.length > 0
);
const mockData = [
  { id: 1, statementCode: "DZ202401001", supplierId: 1, supplierName: "北京原材料供应商", period: "2024-01", beginBalance: 20000, currentPayable: 15000, currentPayment: 10000, endBalance: 25000 },
  { id: 2, statementCode: "DZ202401002", supplierId: 2, supplierName: "上海电子元器件公司", period: "2024-01", beginBalance: 10000, currentPayable: 20000, currentPayment: 15000, endBalance: 15000 },
  { id: 3, statementCode: "DZ202402001", supplierId: 1, supplierName: "北京原材料供应商", period: "2024-02", beginBalance: 25000, currentPayable: 18000, currentPayment: 20000, endBalance: 23000 },
];
const calculateEndBalance = (beginBalance, currentPayable, currentPayment) => {
  return beginBalance + currentPayable - currentPayment;
const applyStatementSummary = (data) => {
  generateForm.openingBalance = Number(data.openingBalance ?? 0);
  generateForm.currentPlan = Number(data.currentPlan ?? 0);
  generateForm.currentActually = Number(data.currentActually ?? 0);
  generateForm.closingBalance = Number(
    data.closingBalance ??
      calculateEndBalance(
        generateForm.openingBalance,
        generateForm.currentPlan,
        generateForm.currentActually
      )
  );
};
const getSupplierList = () => {
  getOptions().then((res) => {
    if (res.code === 200) {
      supplierList.value = res.data ?? [];
    }
  });
};
const normalizePurchaseRows = (list) => {
  const rows = Array.isArray(list) ? list : [];
  return rows.map((item, index) => {
    const type = Number(item.type);
    return {
      id: item.id ?? `detail-${index}`,
      accountStatementId: item.accountStatementId,
      occurrenceDate: item.occurrenceDate ?? "",
      receiptNumber: item.receiptNumber ?? "",
      type,
      typeLabel: getDetailTypeLabel(type),
      amount: Math.abs(Number(item.amount ?? 0)),
      remark: item.remark ?? "",
    };
  });
};
const selectAllPurchaseRows = (keepApiSummary = false) => {
  nextTick(() => {
    const table = purchaseTableRef.value;
    if (!table) return;
    table.clearSelection();
    purchaseData.value.forEach((row) => table.toggleRowSelection(row, true));
    selectedPurchases.value = [...purchaseData.value];
    if (!keepApiSummary) {
      calculateSummary();
    }
  });
};
const isNumericId = (id) => id !== undefined && id !== null && id !== "" && /^\d+$/.test(String(id));
const buildFilterParams = (params = {}) => {
  const result = { ...params, accountType: ACCOUNT_TYPE_PAYABLE };
  if (filters.supplierId) {
    result.customerId = filters.supplierId;
  }
  if (filters.startMonth && filters.endMonth && filters.startMonth === filters.endMonth) {
    result.statementMonth = filters.startMonth;
  } else if (filters.startMonth) {
    result.startMonth = filters.startMonth;
  }
  if (filters.endMonth && filters.startMonth !== filters.endMonth) {
    result.endMonth = filters.endMonth;
  }
  return result;
};
const buildListParams = () =>
  buildFilterParams({
    current: pagination.currentPage,
    size: pagination.pageSize,
  });
const buildExportParams = () => buildFilterParams({});
const buildDetailSubmitItem = (row) => {
  const item = {
    occurrenceDate: row.occurrenceDate,
    receiptNumber: row.receiptNumber,
    type: row.type,
    amount: row.amount,
    remark: row.remark ?? "",
  };
  if (isNumericId(row.id)) {
    item.id = Number(row.id);
  }
  if (row.accountStatementId) {
    item.accountStatementId = row.accountStatementId;
  }
  return item;
};
const buildAddPayload = () => ({
  customerId: generateForm.supplierId,
  customerName: generateForm.supplierName,
  statementMonth: generateForm.statementMonth,
  accountType: ACCOUNT_TYPE_PAYABLE,
  statementNumber: "",
  openingBalance: generateForm.openingBalance,
  currentPlan: generateForm.currentPlan,
  currentActually: generateForm.currentActually,
  closingBalance: generateForm.closingBalance,
  accountStatementDetails: selectedPurchases.value.map(buildDetailSubmitItem),
});
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
@@ -229,15 +411,35 @@
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.supplierId) {
    result = result.filter(item => item.supplierId === filters.supplierId);
  }
  if (filters.startMonth && filters.endMonth) {
    result = result.filter(item => item.period >= filters.startMonth && item.period <= filters.endMonth);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
  tableLoading.value = true;
  listPageAccountStatement(buildListParams())
    .then((res) => {
      const ok = res.code === 200 || res.code === 0;
      if (ok && res.data) {
        pagination.total = res.data.total ?? 0;
        dataList.value = (res.data.records ?? []).map((row) => ({
          ...row,
          supplierName: row.supplierName ?? row.customerName,
        }));
      } else {
        ElMessage.error(res.msg || "查询失败");
        dataList.value = [];
        pagination.total = 0;
      }
    })
    .catch(() => {
      dataList.value = [];
      pagination.total = 0;
      ElMessage.error("查询失败");
    })
    .finally(() => {
      tableLoading.value = false;
    });
};
const onSearch = () => {
  pagination.currentPage = 1;
  getTableData();
};
const resetFilters = () => {
@@ -248,92 +450,106 @@
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
const changePage = ({ page, limit }) => {
  pagination.currentPage = page;
  pagination.pageSize = limit;
  getTableData();
};
const generateStatement = () => {
  generateForm.supplierId = "";
  generateForm.supplierName = "";
  generateForm.period = "";
  generateForm.beginBalance = 0;
  generateForm.currentPayable = 0;
  generateForm.currentPayment = 0;
  generateForm.statementMonth = "";
  generateForm.openingBalance = 0;
  generateForm.currentPlan = 0;
  generateForm.currentActually = 0;
  generateForm.closingBalance = 0;
  statementDetailLoaded.value = false;
  purchaseData.value = [];
  selectedPurchases.value = [];
  generateDialogVisible.value = true;
};
const onSupplierChange = (supplierId) => {
  const supplier = supplierList.find(item => item.id === supplierId);
  if (supplier) {
    generateForm.supplierName = supplier.name;
  }
  const supplier = supplierList.value.find((item) => item.id === supplierId);
  generateForm.supplierName = supplier?.supplierName ?? "";
  loadPurchaseData();
};
const onPeriodChange = () => {
const onStatementMonthChange = () => {
  loadPurchaseData();
};
const loadPurchaseData = () => {
  if (!generateForm.supplierId || !generateForm.period) {
  if (!generateForm.supplierId || !generateForm.statementMonth) {
    purchaseData.value = [];
    selectedPurchases.value = [];
    statementDetailLoaded.value = false;
    generateForm.openingBalance = 0;
    generateForm.currentPlan = 0;
    generateForm.currentActually = 0;
    generateForm.closingBalance = 0;
    return;
  }
  purchaseLoading.value = true;
  selectedPurchases.value = [];
  statementDetailLoaded.value = false;
  setTimeout(() => {
    const mockPurchaseData = [
      { id: 1, date: generateForm.period + "-05", code: "RK2024001", type: "入库", amount: 8000, remark: "原材料采购" },
      { id: 2, date: generateForm.period + "-10", code: "FK2024001", type: "付款", amount: 5000, remark: "支付货款" },
      { id: 3, date: generateForm.period + "-15", code: "RK2024002", type: "入库", amount: 12000, remark: "电子元器件" },
      { id: 4, date: generateForm.period + "-18", code: "TH2024001", type: "退货", amount: 2000, remark: "质量问题退货" },
      { id: 5, date: generateForm.period + "-22", code: "RK2024003", type: "入库", amount: 6000, remark: "包装材料" },
      { id: 6, date: generateForm.period + "-25", code: "FK2024002", type: "付款", amount: 8000, remark: "支付货款" },
    ];
  getAccountStatementDetailsByMonth({
    accountType: ACCOUNT_TYPE_PAYABLE,
    customerId: generateForm.supplierId,
    statementMonth: generateForm.statementMonth,
  })
    .then((res) => {
      if (res.code === 200) {
        const data = res.data ?? {};
        const details = data.accountStatementDetails;
        const list = Array.isArray(details) ? details : [];
        purchaseData.value = normalizePurchaseRows(list);
        applyStatementSummary(data);
        statementDetailLoaded.value = true;
    purchaseData.value = mockPurchaseData;
    const lastPeriod = getLastPeriod(generateForm.period);
    const lastStatement = mockData.find(item =>
      item.supplierId === generateForm.supplierId && item.period === lastPeriod
    );
    generateForm.beginBalance = lastStatement ? lastStatement.endBalance : 0;
    calculateSummary();
    purchaseLoading.value = false;
  }, 500);
};
const getLastPeriod = (period) => {
  const [year, month] = period.split("-").map(Number);
  if (month === 1) {
    return `${year - 1}-12`;
  }
  return `${year}-${String(month - 1).padStart(2, "0")}`;
        if (purchaseData.value.length > 0) {
          selectAllPurchaseRows(true);
        }
      } else {
        purchaseData.value = [];
        statementDetailLoaded.value = false;
        ElMessage.error(res.msg || "查询对账明细失败");
      }
    })
    .catch(() => {
      purchaseData.value = [];
      statementDetailLoaded.value = false;
      ElMessage.error("查询对账明细失败");
    })
    .finally(() => {
      purchaseLoading.value = false;
    });
};
const calculateSummary = () => {
  let payable = 0;
  let payment = 0;
  selectedPurchases.value.forEach(item => {
    if (item.type === "入库") {
  selectedPurchases.value.forEach((item) => {
    if (item.type === 2) {
      payable += item.amount;
    } else if (item.type === "退货") {
    } else if (item.type === 5) {
      payable -= item.amount;
    } else if (item.type === "付款") {
    } else if (item.type === 4) {
      payment += item.amount;
    }
  });
  generateForm.currentPayable = payable;
  generateForm.currentPayment = payment;
  generateForm.currentPlan = payable;
  generateForm.currentActually = payment;
  generateForm.closingBalance = calculateEndBalance(
    generateForm.openingBalance,
    generateForm.currentPlan,
    generateForm.currentActually
  );
};
const handlePurchaseSelectionChange = (selection) => {
@@ -342,51 +558,127 @@
};
const confirmGenerate = () => {
  const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
  const endBalance = calculateEndBalance(generateForm.beginBalance, generateForm.currentPayable, generateForm.currentPayment);
  if (!canGenerate.value) return;
  submitLoading.value = true;
  addAccountStatement(buildAddPayload())
    .then((res) => {
      if (res.code === 200) {
        generateDialogVisible.value = false;
        ElMessage.success("对账单生成成功");
        pagination.currentPage = 1;
        getTableData();
      } else {
        ElMessage.error(res.msg || "生成失败");
      }
    })
    .catch(() => {
      ElMessage.error("生成失败");
    })
    .finally(() => {
      submitLoading.value = false;
    });
};
  mockData.unshift({
    id: newId,
    statementCode: "DZ" + Date.now(),
    supplierId: generateForm.supplierId,
    supplierName: generateForm.supplierName,
    period: generateForm.period,
    beginBalance: generateForm.beginBalance,
    currentPayable: generateForm.currentPayable,
    currentPayment: generateForm.currentPayment,
    endBalance,
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除对账单「${row.statementNumber || row.id}」吗?`, "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    deleteAccountStatement([row.id])
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success("删除成功");
          getTableData();
        } else {
          ElMessage.error(res.msg || "删除失败");
        }
      })
      .catch(() => {
        ElMessage.error("删除失败");
      });
  });
};
const buildDetailTableFromApi = (data, statementMonth) => {
  const details = Array.isArray(data.accountStatementDetails) ? data.accountStatementDetails : [];
  let runningBalance = Number(data.openingBalance ?? 0);
  const rows = [
    {
      date: statementMonth ?? "",
      type: "期初",
      code: "-",
      debit: 0,
      credit: 0,
      balance: runningBalance,
      remark: "期初余额",
    },
  ];
  details.forEach((item) => {
    const amount = Math.abs(Number(item.amount ?? 0));
    const type = Number(item.type);
    let debit = 0;
    let credit = 0;
    if (type === 2) {
      credit = amount;
      runningBalance += amount;
    } else if (type === 4) {
      debit = amount;
      runningBalance -= amount;
    } else if (type === 5) {
      credit = -amount;
      runningBalance -= amount;
    }
    rows.push({
      date: item.occurrenceDate ?? "",
      type: getDetailTypeLabel(type),
      code: item.receiptNumber ?? "",
      debit,
      credit,
      balance: runningBalance,
      remark: item.remark ?? "",
    });
  });
  generateDialogVisible.value = false;
  ElMessage.success("对账单生成成功");
  getTableData();
  return rows;
};
const viewDetail = (row) => {
  currentSupplier.value = row.supplierName;
  currentPeriod.value = row.period;
  const partnerId = row.customerId ?? row.supplierId;
  if (!partnerId || !row.statementMonth) {
    ElMessage.warning("缺少供应商或对账月份,无法查询明细");
    return;
  }
  const purchaseInAmount = Math.floor(row.currentPayable * 0.7);
  const returnAmount = Math.floor(row.currentPayable * 0.1);
  const firstPayment = Math.floor(row.currentPayment * 0.5);
  const secondPayment = row.currentPayment - firstPayment;
  let runningBalance = row.beginBalance;
  detailData.value = [
    { date: row.period + "-01", type: "期初", code: "-", debit: 0, credit: 0, balance: runningBalance, remark: "期初余额" },
    { date: row.period + "-05", type: "入库", code: "RK2024001", debit: 0, credit: purchaseInAmount, balance: runningBalance += purchaseInAmount, remark: "采购入库" },
    { date: row.period + "-10", type: "付款", code: "FK2024001", debit: firstPayment, credit: 0, balance: runningBalance -= firstPayment, remark: "支付货款" },
    { date: row.period + "-15", type: "入库", code: "RK2024002", debit: 0, credit: row.currentPayable - purchaseInAmount - returnAmount, balance: runningBalance += (row.currentPayable - purchaseInAmount - returnAmount), remark: "采购入库" },
    { date: row.period + "-20", type: "退货", code: "TH2024001", debit: 0, credit: -returnAmount, balance: runningBalance -= returnAmount, remark: "采购退货" },
    { date: row.period + "-25", type: "付款", code: "FK2024002", debit: secondPayment, credit: 0, balance: runningBalance -= secondPayment, remark: "支付货款" },
  ];
  currentSupplier.value = row.supplierName ?? row.customerName ?? "";
  currentPeriod.value = row.statementMonth ?? "";
  detailData.value = [];
  detailDialogVisible.value = true;
};
  detailLoading.value = true;
const printStatement = (row) => {
  ElMessage.info(`打印对账单: ${row.statementCode}`);
  getAccountStatementDetailsByMonth({
    accountType: ACCOUNT_TYPE_PAYABLE,
    customerId: partnerId,
    statementMonth: row.statementMonth,
  })
    .then((res) => {
      if (res.code === 200) {
        detailData.value = buildDetailTableFromApi(res.data ?? {}, row.statementMonth);
      } else {
        ElMessage.error(res.msg || "查询明细失败");
        detailDialogVisible.value = false;
      }
    })
    .catch(() => {
      ElMessage.error("查询明细失败");
      detailDialogVisible.value = false;
    })
    .finally(() => {
      detailLoading.value = false;
    });
};
const printDetail = () => {
@@ -394,10 +686,15 @@
};
const handleOut = () => {
  ElMessage.success("导出成功");
  proxy.download(
    "/accountStatement/exportAccountStatement",
    buildExportParams(),
    `应付对账单_${Date.now()}.xlsx`
  );
};
onMounted(() => {
  getSupplierList();
  getTableData();
});
</script>
@@ -415,6 +712,10 @@
.text-danger {
  color: #f56c6c;
}
.text-primary {
  color: #409eff;
}
.statement-header {
@@ -461,9 +762,5 @@
.empty-tip {
  margin-top: 30px;
}
.text-primary {
  color: #409eff;
}
</style>
src/views/financialManagement/receivable/invoiceApply.vue
@@ -6,19 +6,31 @@
      </el-form-item>
      <el-form-item label="客户:">
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
          <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="状态:">
        <el-select v-model="filters.status" placeholder="请选择状态" clearable style="width: 150px;">
          <el-option label="待审核" value="pending" />
          <el-option label="已审核" value="approved" />
          <el-option label="已驳回" value="rejected" />
          <el-option label="已开票" value="invoiced" />
      <el-form-item label="审核状态:">
        <el-select v-model="filters.status" placeholder="请选择审核状态" clearable style="width: 150px;">
          <el-option label="待审核" :value="0" />
          <el-option label="审核通过" :value="1" />
          <el-option label="审核不通过" :value="2" />
        </el-select>
      </el-form-item>
      <el-form-item label="申请日期:">
        <el-date-picker
          v-model="filters.dateRange"
          type="daterange"
          value-format="YYYY-MM-DD"
          format="YYYY-MM-DD"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          clearable
          style="width: 240px;"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button type="primary" @click="onSearch">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
@@ -27,12 +39,13 @@
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增申请</el-button>
          <el-button @click="handleBatchApply" icon="Document" :disabled="selectedRows.length === 0">批量申请</el-button>
          <el-button type="success" @click="handleExport" icon="Download">导出开票申请</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        isSelection
        v-loading="tableLoading"
        :column="columns"
        :tableData="dataList"
        :page="{
@@ -50,42 +63,99 @@
          <span>{{ row.taxRate }}%</span>
        </template>
        <template #status="{ row }">
          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
          <el-tag :type="getStatusType(row.status)" effect="light" round>
            {{ getStatusLabel(row.status) }}
          </el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">编辑</el-button>
          <el-button type="success" link @click="handleAudit(row)" v-if="row.status === 'pending'">审核</el-button>
          <el-button type="warning" link @click="handleInvoice(row)" v-if="row.status === 'approved'">开票</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="isPendingStatus(row.status)">编辑</el-button>
          <el-button type="danger" link @click="handleDelete(row)" v-if="isPendingStatus(row.status)">删除</el-button>
          <el-button type="success" link @click="handleAudit(row)" v-if="isPendingStatus(row.status)">审核</el-button>
          <el-button type="primary" link @click="openFileDialog(row)" v-if="isApprovedStatus(row.status)">附件</el-button>
        </template>
      </PIMTable>
    </div>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
    <FormDialog
      :title="dialogTitle"
      v-model="dialogVisible"
      width="800px"
      :operation-type="isView ? 'detail' : ''"
      @confirm="submitForm"
      @cancel="closeDialog"
    >
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
        <el-row v-if="isView" :gutter="20">
          <el-col :span="12">
            <el-form-item label="审核状态">
              <el-tag :type="getStatusType(form.status)" effect="light" round>
                {{ getStatusLabel(form.status) }}
              </el-tag>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="申请单号" prop="applyCode">
              <el-input v-model="form.applyCode" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="客户" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择客户" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
              <el-select
                v-model="form.customerId"
                placeholder="请选择客户"
                style="width: 100%;"
                :disabled="isEdit || isView"
                filterable
                @change="handleCustomerChange"
              >
                <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="出库单号" prop="outboundBatchNos">
              <el-input
                :model-value="outboundBatchDisplayText"
                placeholder="请先选择客户"
                readonly
                :disabled="!form.customerId || isEdit || isView"
                class="outbound-batch-input"
                @click="handleOutboundInputClick"
              >
                <template v-if="!isEdit && !isView" #append>
                  <el-button
                    :disabled="!form.customerId"
                    :loading="outboundBatchLoading"
                    @click.stop="openOutboundSelectDialog"
                  >
                    é€‰æ‹©
                  </el-button>
                </template>
              </el-input>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="开票金额" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
              <el-input-number
                v-model="form.amount"
                :min="0"
                :precision="2"
                :disabled="isView"
                style="width: 100%;"
                placeholder="根据所选出库单自动汇总,可修改"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="税率" prop="taxRate">
              <el-select v-model="form.taxRate" placeholder="请选择税率" style="width: 100%;">
              <el-select v-model="form.taxRate" placeholder="请选择税率" style="width: 100%;" :disabled="isView">
                <el-option
                  v-for="dict in tax_rate"
                  :key="dict.value"
@@ -99,38 +169,103 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="发票类型" prop="invoiceType">
              <el-select v-model="form.invoiceType" placeholder="请选择发票类型" style="width: 100%;">
                <el-option label="增值税专用发票" value="special" />
                <el-option label="增值税普通发票" value="normal" />
                <el-option label="电子发票" value="electronic" />
              <el-select v-model="form.invoiceType" placeholder="请选择发票类型" style="width: 100%;" :disabled="isView">
                <el-option label="增值税专用发票" value="增值税专用发票" />
                <el-option label="增值税普通发票" value="增值税普通发票" />
                <el-option label="电子发票" value="电子发票" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="申请日期" prop="applyDate">
              <el-date-picker v-model="form.applyDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
              <el-date-picker
                v-model="form.applyDate"
                type="date"
                placeholder="选择日期"
                value-format="YYYY-MM-DD"
                style="width: 100%;"
                :disabled="isView"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="发票内容" prop="content">
          <el-input v-model="form.content" type="textarea" :rows="3" placeholder="请输入发票内容" />
          <el-input v-model="form.content" type="textarea" :rows="3" placeholder="请输入发票内容" :disabled="isView" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" :disabled="isView" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      <template v-if="!isView" #footer>
        <el-button type="primary" :loading="submitLoading" @click="submitForm">确定</el-button>
        <el-button @click="closeDialog">取消</el-button>
      </template>
    </FormDialog>
    <el-dialog
      v-model="outboundSelectVisible"
      title="选择出库单"
      width="1200px"
      append-to-body
      destroy-on-close
      :close-on-click-modal="false"
      @closed="handleOutboundDialogClosed"
    >
      <el-table
        ref="outboundTableRef"
        v-loading="outboundBatchLoading"
        :data="outboundBatchList"
        row-key="id"
        border
        stripe
        max-height="480"
        @selection-change="handleOutboundDialogSelectionChange"
      >
        <el-table-column type="selection" width="55" align="center" />
        <el-table-column prop="outboundBatches" label="出库单号" min-width="140" show-overflow-tooltip />
        <el-table-column prop="customerName" label="客户名称" min-width="120" show-overflow-tooltip />
        <el-table-column prop="productName" label="产品名称" min-width="120" show-overflow-tooltip />
        <el-table-column prop="specificationModel" label="规格型号" min-width="140" show-overflow-tooltip />
        <el-table-column prop="salesContractNo" label="销售合同号" min-width="140" show-overflow-tooltip />
        <el-table-column prop="shippingNo" label="发货单号" min-width="130" show-overflow-tooltip />
        <el-table-column prop="shippingDate" label="发货日期" width="110" align="center" />
        <el-table-column prop="outboundAmount" label="出库金额" width="110" align="right">
          <template #default="{ row }">Â¥{{ formatMoney(row.outboundAmount) }}</template>
        </el-table-column>
        <el-table-column prop="taxRate" label="税率" width="80" align="center">
          <template #default="{ row }">{{ row.taxRate }}%</template>
        </el-table-column>
      </el-table>
      <template #footer>
        <el-button type="primary" @click="confirmOutboundSelection">确定</el-button>
        <el-button @click="outboundSelectVisible = false">取消</el-button>
      </template>
    </el-dialog>
    <FileList
      v-if="fileDialogVisible"
      v-model:visible="fileDialogVisible"
      record-type="account_invoice_application"
      :record-id="currentRecordId"
    />
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance } from "vue";
import { ref, reactive, computed, onMounted, nextTick, getCurrentInstance, defineAsyncComponent } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { listCustomer } from "@/api/basicData/customer.js";
import {
  getOutboundBatchesByCustomer,
  addAccountInvoiceApplication,
  listPageAccountInvoiceApplication,
  auditAccountInvoiceApplication,
  updateAccountInvoiceApplication,
  deleteAccountInvoiceApplication,
} from "@/api/financialManagement/invoiceApply.js";
const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
defineOptions({
  name: "开票申请",
@@ -143,6 +278,7 @@
  applyCode: "",
  customerId: "",
  status: "",
  dateRange: [],
});
const pagination = reactive({
@@ -154,35 +290,237 @@
const columns = [
  { label: "申请单号", prop: "applyCode", width: "150" },
  { label: "客户名称", prop: "customerName", width: "180" },
  { label: "开票金额", prop: "amount", slot: "amount" },
  { label: "税率", prop: "taxRate", slot: "taxRate" },
  { label: "发票类型", prop: "invoiceTypeLabel", width: "130" },
  { label: "开票金额", prop: "amount", dataType: "slot", slot: "amount" },
  { label: "税率", prop: "taxRate", dataType: "slot", slot: "taxRate" },
  { label: "发票类型", prop: "invoiceType", width: "130" },
  { label: "申请日期", prop: "applyDate", width: "120" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "操作", prop: "operation", slot: "operation", width: "200", fixed: "right" },
  { label: "审核状态", prop: "status", dataType: "slot", slot: "status", width: "110", align: "center" },
  { label: "操作", prop: "operation", dataType: "slot", slot: "operation", width: "300", fixed: "right" },
];
const dataList = ref([]);
const tableLoading = ref(false);
const selectedRows = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const isView = ref(false);
const currentId = ref(null);
const customerList = [
  { id: 1, name: "北京科技有限公司" },
  { id: 2, name: "上海贸易公司" },
  { id: 3, name: "广州实业有限公司" },
  { id: 4, name: "深圳电子公司" },
];
const closeDialog = () => {
  dialogVisible.value = false;
  outboundSelectVisible.value = false;
  isView.value = false;
  isEdit.value = false;
};
const customerList = ref([]);
const outboundBatchList = ref([]);
const outboundBatchOptions = ref([]);
const outboundBatchLoading = ref(false);
const outboundSelectVisible = ref(false);
const outboundTableRef = ref(null);
const dialogOutboundSelection = ref([]);
const getCustomerList = () => {
  listCustomer({ current: -1, size: -1, type: 0 }).then((res) => {
    if (res.code === 200) {
      customerList.value = res.data?.records || [];
    }
  });
};
const normalizeOutboundBatchOptions = (data) => {
  const list = Array.isArray(data) ? data : [];
  return list.map((item, index) => {
    if (typeof item === "string" || typeof item === "number") {
      const text = String(item);
      return { label: text, value: text, outboundAmount: 0 };
    }
    const label =
      item.outboundBatches ??
      item.batchNo ??
      item.shippingNo ??
      item.outboundNo ??
      item.label ??
      `出库单${index + 1}`;
    const value = item.id ?? item.stockOutRecordId ?? item.stockOutRecordIds ?? label;
    const outboundAmount = Number(item.outboundAmount) || 0;
    const taxRate =
      item.taxRate !== undefined && item.taxRate !== null && item.taxRate !== ""
        ? Number(item.taxRate)
        : undefined;
    return { label: String(label), value, outboundAmount, taxRate };
  });
};
const isSameOutboundId = (a, b) => String(a) === String(b);
const getSelectedOutboundOptions = () => {
  const selected = form.outboundBatchNos || [];
  return outboundBatchOptions.value.filter((opt) =>
    selected.some((id) => isSameOutboundId(id, opt.value))
  );
};
/** æ ¡éªŒæ‰€é€‰å‡ºåº“单税率是否一致,一致则回填 form.taxRate */
const checkTaxRateConsistency = (showMessage = true) => {
  const selected = getSelectedOutboundOptions();
  if (selected.length === 0) return true;
  const withTaxRate = selected.filter(
    (opt) => opt.taxRate !== undefined && opt.taxRate !== null && !Number.isNaN(opt.taxRate)
  );
  if (withTaxRate.length === 0) return true;
  const uniqueRates = [...new Set(withTaxRate.map((opt) => Number(opt.taxRate)))];
  if (uniqueRates.length > 1) {
    if (showMessage) {
      const detail = withTaxRate.map((opt) => `${opt.label}(${opt.taxRate}%)`).join("、");
      ElMessage.error(`所选出库单税率不一致,无法开票:${detail}`);
    }
    return false;
  }
  form.taxRate = uniqueRates[0];
  return true;
};
/** æ ¹æ®æ‰€é€‰å‡ºåº“单汇总 outboundAmount ä½œä¸ºå¼€ç¥¨é‡‘额 */
const syncInvoiceAmount = () => {
  const selected = form.outboundBatchNos || [];
  const sum = outboundBatchOptions.value
    .filter((opt) => selected.some((id) => isSameOutboundId(id, opt.value)))
    .reduce((acc, opt) => acc + (Number(opt.outboundAmount) || 0), 0);
  form.amount = sum > 0 ? Number(sum.toFixed(2)) : 0;
};
const getOutboundRowId = (row) => row?.id ?? row?.stockOutRecordId;
const outboundBatchDisplayText = computed(() => {
  if (isEdit.value || isView.value) {
    return form.outboundBatches || "";
  }
  if (form.outboundBatches) return form.outboundBatches;
  const ids = form.outboundBatchNos || [];
  if (!ids.length) return "";
  return outboundBatchOptions.value
    .filter((opt) => ids.some((id) => isSameOutboundId(id, opt.value)))
    .map((opt) => opt.label)
    .join("、");
});
const handleOutboundInputClick = () => {
  if (isEdit.value || isView.value) return;
  openOutboundSelectDialog();
};
const restoreOutboundTableSelection = () => {
  nextTick(() => {
    const table = outboundTableRef.value;
    if (!table) return;
    table.clearSelection();
    const selectedIds = new Set((form.outboundBatchNos || []).map((id) => String(id)));
    outboundBatchList.value.forEach((row) => {
      const rowId = getOutboundRowId(row);
      if (rowId !== undefined && rowId !== null && selectedIds.has(String(rowId))) {
        table.toggleRowSelection(row, true);
      }
    });
  });
};
const openOutboundSelectDialog = () => {
  if (!form.customerId || isEdit.value || isView.value) return;
  outboundSelectVisible.value = true;
  loadOutboundBatches(form.customerId, true).then(() => {
    restoreOutboundTableSelection();
  });
};
const handleOutboundDialogSelectionChange = (selection) => {
  dialogOutboundSelection.value = selection;
};
const confirmOutboundSelection = () => {
  if (dialogOutboundSelection.value.length === 0) {
    ElMessage.warning("请至少选择一条出库单");
    return;
  }
  const prevIds = [...(form.outboundBatchNos || [])];
  const prevBatches = form.outboundBatches;
  form.outboundBatchNos = dialogOutboundSelection.value
    .map((row) => getOutboundRowId(row))
    .filter((id) => id !== undefined && id !== null);
  form.outboundBatches = dialogOutboundSelection.value
    .map((row) => row.outboundBatches ?? row.batchNo ?? row.shippingNo ?? "")
    .filter(Boolean)
    .join("、");
  if (!checkTaxRateConsistency()) {
    form.outboundBatchNos = prevIds;
    form.outboundBatches = prevBatches;
    return;
  }
  outboundSelectVisible.value = false;
  syncInvoiceAmount();
  formRef.value?.validateField("outboundBatchNos");
};
const handleOutboundDialogClosed = () => {
  dialogOutboundSelection.value = [];
};
const loadOutboundBatches = (customerId, keepSelected = false) => {
  if (!customerId) {
    outboundBatchList.value = [];
    outboundBatchOptions.value = [];
    if (!keepSelected) {
      form.outboundBatchNos = [];
      form.amount = 0;
    }
    return Promise.resolve();
  }
  outboundBatchLoading.value = true;
  return getOutboundBatchesByCustomer({ customerId })
    .then((res) => {
      if (res.code === 200) {
        const list = res.data?.records ?? res.data ?? [];
        outboundBatchList.value = Array.isArray(list) ? list : [];
        outboundBatchOptions.value = normalizeOutboundBatchOptions(list);
      } else {
        outboundBatchList.value = [];
        outboundBatchOptions.value = [];
      }
    })
    .catch(() => {
      outboundBatchList.value = [];
      outboundBatchOptions.value = [];
    })
    .finally(() => {
      outboundBatchLoading.value = false;
      if (keepSelected) {
        syncInvoiceAmount();
        checkTaxRateConsistency(false);
      }
    });
};
const handleCustomerChange = (customerId) => {
  form.outboundBatchNos = [];
  form.outboundBatches = "";
  form.amount = 0;
  loadOutboundBatches(customerId);
};
const form = reactive({
  applyCode: "",
  customerId: "",
  outboundBatchNos: [],
  outboundBatches: "",
  amount: 0,
  taxRate: 13,
  invoiceType: "special",
  invoiceType: "增值税专用发票",
  applyDate: "",
  content: "",
  remark: "",
@@ -190,17 +528,103 @@
const rules = {
  customerId: [{ required: true, message: "请选择客户", trigger: "change" }],
  outboundBatchNos: [{ required: true, type: "array", min: 1, message: "请选择出库单号", trigger: "change" }],
  amount: [{ required: true, message: "请输入开票金额", trigger: "blur" }],
  taxRate: [{ required: true, message: "请选择税率", trigger: "change" }],
  invoiceType: [{ required: true, message: "请选择发票类型", trigger: "change" }],
  applyDate: [{ required: true, message: "请选择申请日期", trigger: "change" }],
};
const mockData = [
  { id: 1, applyCode: "KP2024001", customerId: 1, customerName: "北京科技有限公司", amount: 5000, taxRate: 13, invoiceType: "special", invoiceTypeLabel: "增值税专用发票", applyDate: "2024-01-15", status: "pending", content: "软件服务费", remark: "" },
  { id: 2, applyCode: "KP2024002", customerId: 2, customerName: "上海贸易公司", amount: 8000, taxRate: 13, invoiceType: "normal", invoiceTypeLabel: "增值税普通发票", applyDate: "2024-01-16", status: "approved", content: "商品销售", remark: "" },
  { id: 3, applyCode: "KP2024003", customerId: 3, customerName: "广州实业有限公司", amount: 12000, taxRate: 6, invoiceType: "electronic", invoiceTypeLabel: "电子发票", applyDate: "2024-01-18", status: "invoiced", content: "技术服务费", remark: "" },
];
/** å®¡æ ¸çŠ¶æ€ï¼š0待审核 1审核通过 2审核不通过 */
const STATUS_LABEL_MAP = {
  0: "待审核",
  1: "审核通过",
  2: "审核不通过",
};
const STATUS_TYPE_MAP = {
  0: "warning",
  1: "success",
  2: "danger",
};
const normalizeStatus = (status) => {
  if (status === undefined || status === null || status === "") return status;
  const num = Number(status);
  return Number.isNaN(num) ? status : num;
};
const isPendingStatus = (status) => normalizeStatus(status) === 0;
const isApprovedStatus = (status) => normalizeStatus(status) === 1;
const fileDialogVisible = ref(false);
const currentRecordId = ref(0);
const openFileDialog = (row) => {
  currentRecordId.value = row.id;
  fileDialogVisible.value = true;
};
const formatOutboundBatches = (value) => {
  if (value === undefined || value === null || value === "") return "";
  if (Array.isArray(value)) return value.filter(Boolean).join("、");
  return String(value)
    .split(/[,,]/)
    .map((s) => s.trim())
    .filter(Boolean)
    .join("、");
};
const normalizeTableRow = (row) => ({
  ...row,
  applyCode: row.invoiceApplicationNo ?? row.applyCode,
  amount: row.invoiceAmount ?? row.amount,
  content: row.invoiceContent ?? row.content,
  status: normalizeStatus(row.status ?? row.auditStatus),
  stockOutRecordIds: row.stockOutRecordIds ?? row.stockOutRecordId ?? "",
  outboundBatches: formatOutboundBatches(row.outboundBatches),
});
const appendFilterParams = (params) => {
  if (filters.applyCode) {
    params.invoiceApplicationNo = filters.applyCode;
  }
  if (filters.customerId) {
    params.customerId = filters.customerId;
  }
  if (filters.status !== "" && filters.status != null) {
    params.status = filters.status;
  }
  if (filters.dateRange?.length === 2) {
    params.startDate = filters.dateRange[0];
    params.endDate = filters.dateRange[1];
  }
  return params;
};
const buildListParams = () => {
  return appendFilterParams({
    current: pagination.currentPage,
    size: pagination.pageSize,
  });
};
const buildExportParams = () => {
  const params = appendFilterParams({});
  if (selectedRows.value.length > 0) {
    params.ids = selectedRows.value.map((row) => row.id).join(",");
  }
  return params;
};
const handleExport = () => {
  const params = buildExportParams();
  const filename =
    selectedRows.value.length > 0
      ? `开票申请_已选${selectedRows.value.length}条_${Date.now()}.xlsx`
      : `开票申请_${Date.now()}.xlsx`;
  proxy.download("/accountInvoiceApplication/exportAccountInvoiceApplication", params, filename);
};
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
@@ -208,34 +632,54 @@
};
const getStatusLabel = (status) => {
  const map = { pending: "待审核", approved: "已审核", rejected: "已驳回", invoiced: "已开票" };
  return map[status] || status;
  const num = normalizeStatus(status);
  if (num === 0 || num === 1 || num === 2) {
    return STATUS_LABEL_MAP[num];
  }
  return "-";
};
const getStatusType = (status) => {
  const map = { pending: "warning", approved: "success", rejected: "danger", invoiced: "primary" };
  return map[status] || "";
  const num = normalizeStatus(status);
  if (num === 0 || num === 1 || num === 2) {
    return STATUS_TYPE_MAP[num];
  }
  return "info";
};
const onSearch = () => {
  pagination.currentPage = 1;
  getTableData();
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.applyCode) {
    result = result.filter(item => item.applyCode.includes(filters.applyCode));
  }
  if (filters.customerId) {
    result = result.filter(item => item.customerId === filters.customerId);
  }
  if (filters.status) {
    result = result.filter(item => item.status === filters.status);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
  tableLoading.value = true;
  listPageAccountInvoiceApplication(buildListParams())
    .then((res) => {
      const ok = res.code === 200 || res.code === 0;
      if (ok && res.data) {
        pagination.total = res.data.total ?? 0;
        dataList.value = (res.data.records ?? []).map(normalizeTableRow);
      } else {
        ElMessage.error(res.msg || "查询失败");
        dataList.value = [];
        pagination.total = 0;
      }
    })
    .catch(() => {
      dataList.value = [];
      pagination.total = 0;
    })
    .finally(() => {
      tableLoading.value = false;
    });
};
const resetFilters = () => {
  filters.applyCode = "";
  filters.customerId = "";
  filters.status = "";
  filters.dateRange = [];
  pagination.currentPage = 1;
  getTableData();
};
@@ -250,57 +694,139 @@
  selectedRows.value = selection;
};
const fillFormFromRow = (row) => {
  const outboundBatchNos = Array.isArray(row.outboundBatchNos)
    ? row.outboundBatchNos
    : parseStockOutRecordIds(row.stockOutRecordIds ?? row.stockOutRecordId);
  Object.assign(form, {
    ...row,
    applyCode: row.applyCode ?? row.invoiceApplicationNo ?? "",
    amount: Number(row.amount ?? row.invoiceAmount ?? 0),
    content: row.content ?? row.invoiceContent,
    status: normalizeStatus(row.status ?? row.auditStatus),
    outboundBatchNos,
    outboundBatches: formatOutboundBatches(row.outboundBatches),
  });
};
const add = () => {
  isEdit.value = false;
  isView.value = false;
  dialogTitle.value = "新增开票申请";
  Object.assign(form, {
    applyCode: "KP" + Date.now().toString().slice(-8),
    customerId: "",
    outboundBatchNos: [],
    outboundBatches: "",
    amount: 0,
    taxRate: 13,
    invoiceType: "special",
    applyDate: new Date().toISOString().split('T')[0],
    invoiceType: "增值税专用发票",
    applyDate: new Date().toISOString().split("T")[0],
    content: "",
    remark: "",
  });
  outboundBatchList.value = [];
  outboundBatchOptions.value = [];
  dialogVisible.value = true;
};
const parseStockOutRecordIds = (value) => {
  if (!value) return [];
  if (Array.isArray(value)) return value;
  return String(value)
    .split(/[,,]/)
    .map((s) => s.trim())
    .filter(Boolean)
    .map((s) => (/^\d+$/.test(s) ? Number(s) : s));
};
const buildSubmitPayload = (forUpdate = false) => {
  const payload = {
    customerId: form.customerId,
    stockOutRecordIds: (form.outboundBatchNos || []).join(","),
    invoiceApplicationNo: form.applyCode || "",
    invoiceType: form.invoiceType,
    applyDate: form.applyDate,
    invoiceContent: form.content,
    remark: form.remark || "",
    invoiceAmount: form.amount,
    taxRate: form.taxRate,
    status: 0,
  };
  if (forUpdate) {
    payload.id = currentId.value;
  }
  return payload;
};
const edit = (row) => {
  isEdit.value = true;
  isView.value = false;
  currentId.value = row.id;
  dialogTitle.value = "编辑开票申请";
  Object.assign(form, row);
  fillFormFromRow(row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看申请单: ${row.applyCode}`);
  isView.value = true;
  isEdit.value = false;
  dialogTitle.value = "查看开票申请";
  fillFormFromRow(row);
  dialogVisible.value = true;
};
const submitAudit = (row, status) => {
  auditAccountInvoiceApplication({ id: row.id, status })
    .then((res) => {
      if (res.code === 200) {
        ElMessage.success(status === 1 ? "审核通过" : "审核不通过");
        getTableData();
      } else {
        ElMessage.error(res.msg || "审批失败");
      }
    })
    .catch(() => {
      ElMessage.error("审批失败");
    });
};
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除申请单「${row.applyCode ?? row.invoiceApplicationNo}」吗?`, "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    deleteAccountInvoiceApplication([row.id])
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success("删除成功");
          getTableData();
        } else {
          ElMessage.error(res.msg || "删除失败");
        }
      })
      .catch(() => {
        ElMessage.error("删除失败");
      });
  });
};
const handleAudit = (row) => {
  ElMessageBox.confirm("确认审核通过该开票申请吗?", "提示", {
    confirmButtonText: "通过",
    cancelButtonText: "驳回",
  ElMessageBox.confirm("请选择审批结果", "开票申请审核", {
    confirmButtonText: "审核通过",
    cancelButtonText: "审核不通过",
    distinguishCancelAndClose: true,
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "approved";
    }
    ElMessage.success("审核通过");
    getTableData();
  }).catch((action) => {
    if (action === "cancel") {
      const index = mockData.findIndex(item => item.id === row.id);
      if (index !== -1) {
        mockData[index].status = "rejected";
  })
    .then(() => {
      submitAudit(row, 1);
    })
    .catch((action) => {
      if (action === "cancel") {
        submitAudit(row, 2);
      }
      ElMessage.warning("已驳回");
      getTableData();
    }
  });
    });
};
const handleInvoice = (row) => {
@@ -309,10 +835,6 @@
    cancelButtonText: "取消",
    type: "info",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "invoiced";
    }
    ElMessage.success("开票完成");
    getTableData();
  });
@@ -322,29 +844,39 @@
  ElMessage.success(`批量申请 ${selectedRows.value.length} æ¡è®°å½•`);
};
const submitLoading = ref(false);
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const customer = customerList.find(item => item.id === form.customerId);
      const invoiceTypeMap = { special: "增值税专用发票", normal: "增值税普通发票", electronic: "电子发票" };
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType] };
    if (!valid) return;
    if (!checkTaxRateConsistency()) return;
    submitLoading.value = true;
    const request = isEdit.value
      ? updateAccountInvoiceApplication(buildSubmitPayload(true))
      : addAccountInvoiceApplication(buildSubmitPayload());
    request
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success(isEdit.value ? "修改成功" : "新增成功");
          closeDialog();
          getTableData();
        } else {
          ElMessage.error(res.msg || (isEdit.value ? "修改失败" : "新增失败"));
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType], status: "pending" });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
      })
      .catch(() => {
        ElMessage.error(isEdit.value ? "修改失败" : "新增失败");
      })
      .finally(() => {
        submitLoading.value = false;
      });
  });
};
onMounted(() => {
  getCustomerList();
  getTableData();
});
</script>
@@ -360,4 +892,10 @@
  color: #409eff;
  font-weight: bold;
}
.outbound-batch-input:not(.is-disabled) {
  :deep(.el-input__wrapper) {
    cursor: pointer;
  }
}
</style>
src/views/financialManagement/receivable/outputInvoice.vue
@@ -1,19 +1,35 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="发票代码:">
        <el-input v-model="filters.invoiceCode" placeholder="请输入发票代码" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="发票号码:">
        <el-input v-model="filters.invoiceNo" placeholder="请输入发票号码" clearable style="width: 200px;" />
        <el-input v-model="filters.invoiceNumber" placeholder="请输入发票号码" clearable style="width: 200px;" />
      </el-form-item>
      <el-form-item label="客户:">
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
          <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="开票日期:">
        <el-date-picker
          v-model="filters.dateRange"
          type="daterange"
          value-format="YYYY-MM-DD"
          format="YYYY-MM-DD"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          clearable
          style="width: 240px;"
        />
      </el-form-item>
      <el-form-item label="状态:">
        <el-select v-model="filters.status" placeholder="请选择状态" clearable style="width: 150px;">
          <el-option label="正常" :value="0" />
          <el-option label="作废" :value="1" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button type="primary" @click="onSearch">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
@@ -21,13 +37,13 @@
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">录入发票</el-button>
          <el-button @click="handleImport" icon="Upload">导入</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
          <!-- <el-button type="primary" @click="add" icon="Plus">录入发票</el-button> -->
          <el-button type="success" @click="handleExport" icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        v-loading="tableLoading"
        :column="columns"
        :tableData="dataList"
        :page="{
@@ -46,58 +62,98 @@
        <template #totalAmount="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.totalAmount) }}</span>
        </template>
        <template #invoiceType="{ row }">
          <el-tag :type="row.invoiceType === 'special' ? 'danger' : 'primary'">{{ row.invoiceTypeLabel }}</el-tag>
        <template #status="{ row }">
          <el-tag :type="getStatusType(row.status)" effect="light" round>
            {{ getStatusLabel(row.status) }}
          </el-tag>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)">编辑</el-button>
          <el-button type="danger" link @click="handleDelete(row)">作废</el-button>
          <el-button
            type="primary"
            link
            @click="openFileDialog(row)"
            v-if="row.accountInvoiceApplicationId"
          >
            é™„ä»¶
          </el-button>
          <el-button type="warning" link @click="handleCancel(row)" v-if="isNormalStatus(row.status)">作废</el-button>
          <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
        </template>
      </PIMTable>
    </div>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
    <FormDialog
      :title="dialogTitle"
      v-model="dialogVisible"
      width="800px"
      :operation-type="isView ? 'detail' : ''"
      @confirm="submitForm"
      @cancel="closeDialog"
    >
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
        <el-row v-if="isView" :gutter="20">
          <el-col :span="12">
            <el-form-item label="发票代码" prop="invoiceCode">
              <el-input v-model="form.invoiceCode" placeholder="请输入发票代码" />
            <el-form-item label="状态">
              <el-tag :type="getStatusType(form.status)" effect="light" round>
                {{ getStatusLabel(form.status) }}
              </el-tag>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="发票号码" prop="invoiceNo">
              <el-input v-model="form.invoiceNo" placeholder="请输入发票号码" />
              <el-input v-model="form.invoiceNo" placeholder="请输入发票号码" :disabled="isView" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="客户" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择客户" style="width: 100%;">
                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
              <el-select v-model="form.customerId" placeholder="请选择客户" style="width: 100%;" :disabled="isView">
                <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="开票日期" prop="invoiceDate">
              <el-date-picker v-model="form.invoiceDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="发票类型" prop="invoiceType">
              <el-select v-model="form.invoiceType" placeholder="请选择发票类型" style="width: 100%;" @change="handleInvoiceTypeChange">
                <el-option label="增值税专用发票" value="special" />
                <el-option label="增值税普通发票" value="normal" />
                <el-option label="电子发票" value="electronic" />
              </el-select>
            <el-form-item label="开票日期" prop="invoiceDate">
              <el-date-picker
                v-model="form.invoiceDate"
                type="date"
                placeholder="选择日期"
                value-format="YYYY-MM-DD"
                style="width: 100%;"
                :disabled="isView"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="发票类型" prop="invoiceType">
              <el-select
                v-model="form.invoiceType"
                placeholder="请选择发票类型"
                style="width: 100%;"
                :disabled="isView"
                @change="handleInvoiceTypeChange"
              >
                <el-option label="增值税专用发票" value="增值税专用发票" />
                <el-option label="增值税普通发票" value="增值税普通发票" />
                <el-option label="电子发票" value="电子发票" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="税率" prop="taxRate">
              <el-select v-model="form.taxRate" placeholder="请选择税率" style="width: 100%;" @change="calculateTax">
              <el-select
                v-model="form.taxRate"
                placeholder="请选择税率"
                style="width: 100%;"
                :disabled="isView"
                @change="calculateTax"
              >
                <el-option
                  v-for="dict in tax_rate"
                  :key="dict.value"
@@ -111,7 +167,14 @@
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="金额(不含税)" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" @change="calculateTax" />
              <el-input-number
                v-model="form.amount"
                :min="0"
                :precision="2"
                style="width: 100%;"
                :disabled="isView"
                @change="calculateTax"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
@@ -126,24 +189,41 @@
          </el-col>
        </el-row>
        <el-form-item label="发票内容" prop="content">
          <el-input v-model="form.content" type="textarea" :rows="3" placeholder="请输入发票内容" />
          <el-input v-model="form.content" type="textarea" :rows="3" placeholder="请输入发票内容" :disabled="isView" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" :disabled="isView" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      <template v-if="!isView" #footer>
        <el-button type="primary" :loading="submitLoading" @click="submitForm">确定</el-button>
        <el-button @click="closeDialog">取消</el-button>
      </template>
    </FormDialog>
    <FileList
      v-if="fileDialogVisible"
      v-model:visible="fileDialogVisible"
      record-type="account_invoice_application"
      :record-id="currentRecordId"
      :editable="false"
    />
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed, getCurrentInstance } from "vue";
import { ref, reactive, onMounted, getCurrentInstance, defineAsyncComponent } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { listCustomer } from "@/api/basicData/customer.js";
import {
  addAccountSalesInvoice,
  listPageAccountSalesInvoice,
  cancelAccountSalesInvoice,
  deleteAccountSalesInvoice,
} from "@/api/financialManagement/accountSalesInvoice.js";
const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
defineOptions({
  name: "销项发票",
@@ -153,9 +233,10 @@
const { tax_rate } = proxy.useDict("tax_rate");
const filters = reactive({
  invoiceCode: "",
  invoiceNo: "",
  invoiceNumber: "",
  customerId: "",
  dateRange: [],
  status: "",
});
const pagination = reactive({
@@ -165,47 +246,76 @@
});
const columns = [
  { label: "发票代码", prop: "invoiceCode", width: "130" },
  { label: "发票号码", prop: "invoiceNo", width: "120" },
  { label: "发票号码", prop: "invoiceNo", width: "140" },
  { label: "客户名称", prop: "customerName", width: "180" },
  { label: "开票日期", prop: "invoiceDate", width: "120" },
  { label: "金额", prop: "amount", slot: "amount" },
  { label: "税额", prop: "taxAmount", slot: "taxAmount" },
  { label: "价税合计", prop: "totalAmount", slot: "totalAmount" },
  { label: "发票类型", prop: "invoiceType", slot: "invoiceType" },
  { label: "操作", prop: "operation", slot: "operation", width: "180", fixed: "right" },
  { label: "金额", prop: "amount", dataType: "slot", slot: "amount" },
  { label: "税额", prop: "taxAmount", dataType: "slot", slot: "taxAmount" },
  { label: "价税合计", prop: "totalAmount", dataType: "slot", slot: "totalAmount" },
  { label: "发票类型", prop: "invoiceType", width: "130" },
  { label: "状态", prop: "status", dataType: "slot", slot: "status", width: "90", align: "center" },
  { label: "操作", prop: "operation", dataType: "slot", slot: "operation", width: "260", fixed: "right" },
];
const dataList = ref([]);
const tableLoading = ref(false);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const isView = ref(false);
const submitLoading = ref(false);
const customerList = [
  { id: 1, name: "北京科技有限公司" },
  { id: 2, name: "上海贸易公司" },
  { id: 3, name: "广州实业有限公司" },
  { id: 4, name: "深圳电子公司" },
];
const customerList = ref([]);
const fileDialogVisible = ref(false);
const currentRecordId = ref(0);
const openFileDialog = (row) => {
  if (!row.accountInvoiceApplicationId) {
    ElMessage.warning("未关联开票申请,无法查看附件");
    return;
  }
  currentRecordId.value = row.accountInvoiceApplicationId;
  fileDialogVisible.value = true;
};
/** çŠ¶æ€ï¼š0正常 1作废 */
const STATUS_LABEL_MAP = { 0: "正常", 1: "作废" };
const STATUS_TYPE_MAP = { 0: "success", 1: "info" };
const normalizeStatus = (status) => {
  if (status === undefined || status === null || status === "") return 0;
  const num = Number(status);
  return Number.isNaN(num) ? 0 : num;
};
const isNormalStatus = (status) => normalizeStatus(status) === 0;
const getStatusLabel = (status) => {
  const num = normalizeStatus(status);
  return STATUS_LABEL_MAP[num] ?? "正常";
};
const getStatusType = (status) => {
  const num = normalizeStatus(status);
  return STATUS_TYPE_MAP[num] ?? "success";
};
const form = reactive({
  invoiceCode: "",
  invoiceNo: "",
  customerId: "",
  invoiceDate: "",
  invoiceType: "special",
  invoiceType: "增值税专用发票",
  taxRate: 13,
  amount: 0,
  taxAmount: 0,
  totalAmount: 0,
  content: "",
  remark: "",
  accountInvoiceApplicationId: undefined,
  storageAttachmentId: undefined,
});
const rules = {
  invoiceCode: [{ required: true, message: "请输入发票代码", trigger: "blur" }],
  invoiceNo: [{ required: true, message: "请输入发票号码", trigger: "blur" }],
  customerId: [{ required: true, message: "请选择客户", trigger: "change" }],
  invoiceDate: [{ required: true, message: "请选择开票日期", trigger: "change" }],
@@ -213,12 +323,6 @@
  taxRate: [{ required: true, message: "请选择税率", trigger: "change" }],
  amount: [{ required: true, message: "请输入金额", trigger: "blur" }],
};
const mockData = [
  { id: 1, invoiceCode: "0440021001", invoiceNo: "12345678", customerId: 1, customerName: "北京科技有限公司", invoiceDate: "2024-01-15", amount: 5000, taxRate: 13, taxAmount: 650, totalAmount: 5650, invoiceType: "special", invoiceTypeLabel: "增值税专用发票", content: "软件服务费", remark: "" },
  { id: 2, invoiceCode: "0440021002", invoiceNo: "87654321", customerId: 2, customerName: "上海贸易公司", invoiceDate: "2024-01-16", amount: 8000, taxRate: 13, taxAmount: 1040, totalAmount: 9040, invoiceType: "normal", invoiceTypeLabel: "增值税普通发票", content: "商品销售", remark: "" },
  { id: 3, invoiceCode: "0440021003", invoiceNo: "11112222", customerId: 3, customerName: "广州实业有限公司", invoiceDate: "2024-01-18", amount: 12000, taxRate: 6, taxAmount: 720, totalAmount: 12720, invoiceType: "electronic", invoiceTypeLabel: "电子发票", content: "技术服务费", remark: "" },
];
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
@@ -231,33 +335,142 @@
};
const handleInvoiceTypeChange = () => {
  if (form.invoiceType === "special") {
    form.taxRate = 13;
  } else {
    form.taxRate = 13;
  }
  calculateTax();
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.invoiceCode) {
    result = result.filter(item => item.invoiceCode.includes(filters.invoiceCode));
  }
  if (filters.invoiceNo) {
    result = result.filter(item => item.invoiceNo.includes(filters.invoiceNo));
const normalizeTableRow = (row) => ({
  ...row,
  invoiceNo: row.invoiceNumber ?? row.invoiceNo,
  invoiceDate: row.issueDate ?? row.invoiceDate,
  amount: row.taxExclusivelPrice ?? row.amount,
  taxAmount: row.taxPrice ?? row.taxAmount,
  totalAmount: row.taxInclusivePrice ?? row.totalAmount,
  content: row.invoiceContent ?? row.content,
  status: normalizeStatus(row.status),
});
const fillFormFromRow = (row) => {
  Object.assign(form, {
    invoiceNo: row.invoiceNo ?? row.invoiceNumber ?? "",
    customerId: row.customerId,
    invoiceDate: row.invoiceDate ?? row.issueDate ?? "",
    invoiceType: row.invoiceType ?? "增值税专用发票",
    taxRate: row.taxRate ?? 13,
    amount: row.amount ?? row.taxExclusivelPrice ?? 0,
    taxAmount: row.taxAmount ?? row.taxPrice ?? 0,
    totalAmount: row.totalAmount ?? row.taxInclusivePrice ?? 0,
    content: row.content ?? row.invoiceContent ?? "",
    remark: row.remark ?? "",
    accountInvoiceApplicationId: row.accountInvoiceApplicationId,
    storageAttachmentId: row.storageAttachmentId,
    status: normalizeStatus(row.status),
  });
};
const buildCancelPayload = (row) => ({
  id: row.id,
  accountInvoiceApplicationId: row.accountInvoiceApplicationId,
  invoiceNumber: row.invoiceNumber ?? row.invoiceNo,
  taxRate: row.taxRate,
  invoiceType: row.invoiceType,
  issueDate: row.issueDate ?? row.invoiceDate,
  taxExclusivelPrice: row.taxExclusivelPrice ?? row.amount,
  taxPrice: row.taxPrice ?? row.taxAmount,
  taxInclusivePrice: row.taxInclusivePrice ?? row.totalAmount,
  remark: row.remark ?? "",
  invoiceContent: row.invoiceContent ?? row.content,
  customerId: row.customerId,
  storageAttachmentId: row.storageAttachmentId,
  status: 1,
});
const buildSubmitPayload = () => ({
  invoiceNumber: form.invoiceNo,
  customerId: form.customerId,
  issueDate: form.invoiceDate,
  invoiceType: form.invoiceType,
  taxRate: form.taxRate,
  taxExclusivelPrice: form.amount,
  taxPrice: form.taxAmount,
  taxInclusivePrice: form.totalAmount,
  invoiceContent: form.content,
  remark: form.remark || "",
  accountInvoiceApplicationId: form.accountInvoiceApplicationId,
  storageAttachmentId: form.storageAttachmentId,
});
const getCustomerList = () => {
  listCustomer({ current: -1, size: -1, type: 0 }).then((res) => {
    if (res.code === 200) {
      customerList.value = res.data?.records || [];
    }
  });
};
const appendFilterParams = (params) => {
  if (filters.invoiceNumber) {
    params.invoiceNumber = filters.invoiceNumber;
  }
  if (filters.customerId) {
    result = result.filter(item => item.customerId === filters.customerId);
    params.customerId = filters.customerId;
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
  if (filters.dateRange?.length === 2) {
    params.startDate = filters.dateRange[0];
    params.endDate = filters.dateRange[1];
  }
  if (filters.status !== "" && filters.status != null) {
    params.status = filters.status;
  }
  return params;
};
const buildListParams = () =>
  appendFilterParams({
    current: pagination.currentPage,
    size: pagination.pageSize,
  });
const buildExportParams = () => appendFilterParams({});
const handleExport = () => {
  const params = buildExportParams();
  proxy.download("/accountSalesInvoice/exportAccountSalesInvoice", params, `销项发票_${Date.now()}.xlsx`);
};
const getTableData = () => {
  tableLoading.value = true;
  listPageAccountSalesInvoice(buildListParams())
    .then((res) => {
      if (res.code === 200) {
        const records = res.data?.records ?? [];
        dataList.value = records.map(normalizeTableRow);
        pagination.total = res.data?.total ?? 0;
      } else {
        dataList.value = [];
        pagination.total = 0;
        ElMessage.error(res.msg || "查询失败");
      }
    })
    .catch(() => {
      dataList.value = [];
      pagination.total = 0;
      ElMessage.error("查询失败");
    })
    .finally(() => {
      tableLoading.value = false;
    });
};
const onSearch = () => {
  pagination.currentPage = 1;
  getTableData();
};
const resetFilters = () => {
  filters.invoiceCode = "";
  filters.invoiceNo = "";
  filters.invoiceNumber = "";
  filters.customerId = "";
  filters.dateRange = [];
  filters.status = "";
  pagination.currentPage = 1;
  getTableData();
};
@@ -268,83 +481,105 @@
  getTableData();
};
const closeDialog = () => {
  dialogVisible.value = false;
  isView.value = false;
};
const add = () => {
  isEdit.value = false;
  isView.value = false;
  dialogTitle.value = "录入发票";
  Object.assign(form, {
    invoiceCode: "",
    invoiceNo: "",
    customerId: "",
    invoiceDate: new Date().toISOString().split('T')[0],
    invoiceType: "special",
    invoiceDate: new Date().toISOString().split("T")[0],
    invoiceType: "增值税专用发票",
    taxRate: 13,
    amount: 0,
    taxAmount: 0,
    totalAmount: 0,
    content: "",
    remark: "",
    accountInvoiceApplicationId: undefined,
    storageAttachmentId: undefined,
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑发票";
  Object.assign(form, row);
  dialogVisible.value = true;
};
const view = (row) => {
  ElMessage.info(`查看发票: ${row.invoiceCode}-${row.invoiceNo}`);
  isView.value = true;
  dialogTitle.value = "查看发票";
  fillFormFromRow(row);
  dialogVisible.value = true;
};
const handleDelete = (row) => {
  ElMessageBox.confirm("确认作废该发票吗?", "提示", {
const handleCancel = (row) => {
  ElMessageBox.confirm(`确认作废发票「${row.invoiceNo ?? row.invoiceNumber}」吗?`, "作废确认", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData.splice(index, 1);
    }
    ElMessage.success("作废成功");
    getTableData();
    cancelAccountSalesInvoice(buildCancelPayload(row))
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success("作废成功");
          getTableData();
        } else {
          ElMessage.error(res.msg || "作废失败");
        }
      })
      .catch(() => {
        ElMessage.error("作废失败");
      });
  });
};
const handleImport = () => {
  ElMessage.info("导入功能");
};
const handleOut = () => {
  ElMessage.success("导出成功");
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除发票「${row.invoiceNo ?? row.invoiceNumber}」吗?删除后不可恢复。`, "删除确认", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    deleteAccountSalesInvoice([row.id])
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success("删除成功");
          getTableData();
        } else {
          ElMessage.error(res.msg || "删除失败");
        }
      })
      .catch(() => {
        ElMessage.error("删除失败");
      });
  });
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const customer = customerList.find(item => item.id === form.customerId);
      const invoiceTypeMap = { special: "增值税专用发票", normal: "增值税普通发票", electronic: "电子发票" };
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType] };
    if (!valid) return;
    submitLoading.value = true;
    addAccountSalesInvoice(buildSubmitPayload())
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success("录入成功");
          closeDialog();
          getTableData();
        } else {
          ElMessage.error(res.msg || "录入失败");
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType] });
        ElMessage.success("录入成功");
      }
      dialogVisible.value = false;
      getTableData();
    }
      })
      .catch(() => {
        ElMessage.error("录入失败");
      })
      .finally(() => {
        submitLoading.value = false;
      });
  });
};
onMounted(() => {
  getCustomerList();
  getTableData();
});
</script>
src/views/financialManagement/receivable/receipt.vue
@@ -1,356 +1,855 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
    <el-form :model="filters"
             :inline="true">
      <el-form-item label="收款单号:">
        <el-input v-model="filters.receiptCode" placeholder="请输入收款单号" clearable style="width: 200px;" />
        <el-input v-model="filters.collectionNumber"
                  placeholder="请输入收款单号"
                  clearable
                  style="width: 200px;" />
      </el-form-item>
      <el-form-item label="客户:">
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
        <el-select v-model="filters.customerId"
                   placeholder="请选择客户"
                   clearable
                   filterable
                   style="width: 200px;">
          <el-option v-for="item in customerList"
                     :key="item.id"
                     :label="item.customerName"
                     :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="收款方式:">
        <el-select v-model="filters.receiptMethod" placeholder="请选择收款方式" clearable style="width: 150px;">
          <el-option label="银行转账" value="bank_transfer" />
          <el-option label="现金" value="cash" />
          <el-option label="支票" value="check" />
          <el-option label="汇票" value="draft" />
          <el-option label="支付宝" value="alipay" />
          <el-option label="微信" value="wechat" />
        <el-select v-model="filters.collectionMethod"
                   placeholder="请选择收款方式"
                   clearable
                   style="width: 150px;">
          <el-option v-for="item in payment_methods"
                     :key="item.value"
                     :label="item.label"
                     :value="item.value" />
        </el-select>
      </el-form-item>
      <el-form-item label="收款日期:">
        <el-date-picker v-model="filters.dateRange"
                        type="daterange"
                        value-format="YYYY-MM-DD"
                        format="YYYY-MM-DD"
                        range-separator="至"
                        start-placeholder="开始日期"
                        end-placeholder="结束日期"
                        clearable
                        style="width: 240px;" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button type="primary"
                   @click="onSearch">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="table_list">
      <div class="actions">
        <div>
          <el-statistic title="本期收款合计" :value="totalReceiptAmount" precision="2" prefix="Â¥" />
          <el-statistic title="本页收款合计"
                        :value="totalReceiptAmount"
                        :precision="2"
                        prefix="Â¥" />
        </div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增收款</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
          <el-button type="primary"
                     @click="add"
                     icon="Plus">新增收款</el-button>
          <el-button type="success"
                     @click="handleExport"
                     icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :page="{
      <PIMTable rowKey="id"
                v-loading="tableLoading"
                :column="columns"
                :tableData="dataList"
                :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      >
                @pagination="changePage">
        <template #amount="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.amount) }}</span>
        </template>
        <template #receiptMethod="{ row }">
          <el-tag>{{ getReceiptMethodLabel(row.receiptMethod) }}</el-tag>
        </template>
        <template #status="{ row }">
          <el-tag :type="row.status === 'confirmed' ? 'success' : 'warning'">{{ row.status === 'confirmed' ? '已确认' : '待确认' }}</el-tag>
          <span>{{ getReceiptMethodLabel(row.receiptMethod) }}</span>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="view(row)">查看</el-button>
          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">编辑</el-button>
          <el-button type="success" link @click="handleConfirm(row)" v-if="row.status === 'pending'">确认</el-button>
          <el-button type="danger" link @click="handleDelete(row)" v-if="row.status === 'pending'">删除</el-button>
          <el-button type="primary"
                     link
                     @click="view(row)">查看</el-button>
          <el-button :disabled="row.accountStatemen"
                     type="primary"
                     link
                     @click="edit(row)">编辑</el-button>
          <el-button :disabled="row.accountStatemen"
                     type="danger"
                     link
                     @click="handleDelete(row)">删除</el-button>
        </template>
      </PIMTable>
    </div>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
    <FormDialog :title="dialogTitle"
                v-model="dialogVisible"
                width="800px"
                :operation-type="isView ? 'detail' : ''"
                @confirm="submitForm"
                @cancel="closeDialog">
      <el-form :model="form"
               :rules="rules"
               ref="formRef"
               label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="收款单号" prop="receiptCode">
              <el-input v-model="form.receiptCode" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择客户" style="width: 100%;" :disabled="isEdit">
                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
          <el-col :span="24">
            <el-form-item label="收款单号"
                          prop="receiptCode">
              <el-input v-model="form.receiptCode"
                        placeholder="系统自动生成"
                        disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="收款日期" prop="receiptDate">
              <el-date-picker v-model="form.receiptDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="收款金额" prop="amount">
              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="收款方式" prop="receiptMethod">
              <el-select v-model="form.receiptMethod" placeholder="请选择收款方式" style="width: 100%;">
                <el-option label="银行转账" value="bank_transfer" />
                <el-option label="现金" value="cash" />
                <el-option label="支票" value="check" />
                <el-option label="汇票" value="draft" />
                <el-option label="支付宝" value="alipay" />
                <el-option label="微信" value="wechat" />
            <el-form-item label="客户"
                          prop="customerId">
              <el-select v-model="form.customerId"
                         placeholder="请选择客户"
                         style="width: 100%;"
                         :disabled="isEdit || isView"
                         filterable
                         @change="handleCustomerChange">
                <el-option v-for="item in customerList"
                           :key="item.id"
                           :label="item.customerName"
                           :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="银行账号" prop="bankAccount" v-if="form.receiptMethod === 'bank_transfer'">
              <el-input v-model="form.bankAccount" placeholder="请输入银行账号" />
            <el-form-item label="关联单据"
                          prop="stockOutRecordIds">
              <el-input :model-value="outboundBatchDisplayText"
                        placeholder="请先选择客户"
                        readonly
                        :disabled="!form.customerId || isEdit || isView"
                        class="outbound-batch-input"
                        @click="handleOutboundInputClick">
                <template v-if="!isEdit && !isView"
                          #append>
                  <el-button :disabled="!form.customerId"
                             :loading="outboundBatchLoading"
                             @click.stop="openOutboundSelectDialog">
                    é€‰æ‹©
                  </el-button>
                </template>
              </el-input>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="关联单据" prop="relatedDocs">
          <el-select v-model="form.relatedDocs" multiple placeholder="请选择关联单据" style="width: 100%;">
            <el-option v-for="item in outList" :key="item.outCode" :label="item.outCode" :value="item.outCode" />
          </el-select>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="收款日期"
                          prop="receiptDate">
              <el-date-picker v-model="form.receiptDate"
                              type="date"
                              placeholder="选择日期"
                              value-format="YYYY-MM-DD"
                              style="width: 100%;"
                              :disabled="isView" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="收款金额"
                          prop="amount">
              <el-input-number v-model="form.amount"
                               :min="0"
                               :precision="2"
                               style="width: 100%;"
                               :disabled="isView"
                               placeholder="根据关联单据自动汇总,可修改" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="收款方式"
                          prop="receiptMethod">
              <el-select v-model="form.receiptMethod"
                         placeholder="请选择收款方式"
                         style="width: 100%;"
                         :disabled="isView">
                <el-option v-for="item in payment_methods"
                           :key="item.value"
                           :label="item.label"
                           :value="item.value" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="备注"
                      prop="remark">
          <el-input v-model="form.remark"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入备注"
                    :disabled="isView" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      <template v-if="!isView"
                #footer>
        <el-button type="primary"
                   :loading="submitLoading"
                   @click="submitForm">确定</el-button>
        <el-button @click="closeDialog">取消</el-button>
      </template>
    </FormDialog>
    <el-dialog v-model="outboundSelectVisible"
               title="选择关联单据"
               width="1200px"
               append-to-body
               destroy-on-close
               :close-on-click-modal="false"
               @closed="handleOutboundDialogClosed">
      <el-table ref="outboundTableRef"
                v-loading="outboundBatchLoading"
                :data="outboundBatchList"
                row-key="id"
                border
                stripe
                max-height="480"
                @selection-change="handleOutboundDialogSelectionChange">
        <el-table-column type="selection"
                         width="55"
                         align="center" />
        <el-table-column prop="outboundBatches"
                         label="出库单号"
                         min-width="140"
                         show-overflow-tooltip />
        <el-table-column prop="customerName"
                         label="客户名称"
                         min-width="120"
                         show-overflow-tooltip />
        <el-table-column prop="productName"
                         label="产品名称"
                         min-width="120"
                         show-overflow-tooltip />
        <el-table-column prop="specificationModel"
                         label="规格型号"
                         min-width="140"
                         show-overflow-tooltip />
        <el-table-column prop="salesContractNo"
                         label="销售合同号"
                         min-width="140"
                         show-overflow-tooltip />
        <el-table-column prop="shippingNo"
                         label="发货单号"
                         min-width="130"
                         show-overflow-tooltip />
        <el-table-column prop="shippingDate"
                         label="发货日期"
                         width="110"
                         align="center" />
        <el-table-column prop="outboundAmount"
                         label="出库金额"
                         width="110"
                         align="right">
          <template #default="{ row }">Â¥{{ formatMoney(row.outboundAmount) }}</template>
        </el-table-column>
        <el-table-column prop="taxRate"
                         label="税率"
                         width="80"
                         align="center">
          <template #default="{ row }">{{ row.taxRate }}%</template>
        </el-table-column>
      </el-table>
      <template #footer>
        <el-button type="primary"
                   @click="confirmOutboundSelection">确定</el-button>
        <el-button @click="outboundSelectVisible = false">取消</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
  import {
    ref,
    reactive,
    computed,
    onMounted,
    nextTick,
    getCurrentInstance,
  } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import FormDialog from "@/components/Dialog/FormDialog.vue";
  import { listCustomer } from "@/api/basicData/customer.js";
  import {
    getOutboundBatchesByCustomer,
    addAccountSalesCollection,
    listPageAccountSalesCollection,
    updateAccountSalesCollection,
    deleteAccountSalesCollection,
  } from "@/api/financialManagement/accountSalesCollection.js";
defineOptions({
  name: "收款单",
});
  defineOptions({
    name: "收款单",
  });
const filters = reactive({
  receiptCode: "",
  customerId: "",
  receiptMethod: "",
});
  const { proxy } = getCurrentInstance();
  const { payment_methods } = proxy.useDict("payment_methods");
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const columns = [
  { label: "收款单号", prop: "receiptCode", width: "150" },
  { label: "客户名称", prop: "customerName", width: "180" },
  { label: "收款日期", prop: "receiptDate", width: "120" },
  { label: "收款金额", prop: "amount", slot: "amount" },
  { label: "收款方式", prop: "receiptMethod", slot: "receiptMethod" },
  { label: "状态", prop: "status", slot: "status" },
  { label: "备注", prop: "remark", showOverflowTooltip: true },
  { label: "操作", prop: "operation", slot: "operation", width: "220", fixed: "right" },
];
const dataList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref("");
const formRef = ref(null);
const isEdit = ref(false);
const currentId = ref(null);
const customerList = [
  { id: 1, name: "北京科技有限公司" },
  { id: 2, name: "上海贸易公司" },
  { id: 3, name: "广州实业有限公司" },
  { id: 4, name: "深圳电子公司" },
];
const outList = [
  { outCode: "CK2024001", customerId: 1 },
  { outCode: "CK2024002", customerId: 2 },
  { outCode: "CK2024003", customerId: 3 },
];
const form = reactive({
  receiptCode: "",
  customerId: "",
  receiptDate: "",
  amount: 0,
  receiptMethod: "bank_transfer",
  bankAccount: "",
  relatedDocs: [],
  remark: "",
});
const rules = {
  customerId: [{ required: true, message: "请选择客户", trigger: "change" }],
  receiptDate: [{ required: true, message: "请选择收款日期", trigger: "change" }],
  amount: [{ required: true, message: "请输入收款金额", trigger: "blur" }],
  receiptMethod: [{ required: true, message: "请选择收款方式", trigger: "change" }],
};
const mockData = [
  { id: 1, receiptCode: "SK2024001", customerId: 1, customerName: "北京科技有限公司", receiptDate: "2024-01-16", amount: 3000, receiptMethod: "bank_transfer", status: "confirmed", relatedDocs: ["CK2024001"], remark: "" },
  { id: 2, receiptCode: "SK2024002", customerId: 2, customerName: "上海贸易公司", receiptDate: "2024-01-18", amount: 5000, receiptMethod: "cash", status: "pending", relatedDocs: ["CK2024002"], remark: "" },
  { id: 3, receiptCode: "SK2024003", customerId: 3, customerName: "广州实业有限公司", receiptDate: "2024-01-20", amount: 8000, receiptMethod: "alipay", status: "confirmed", relatedDocs: ["CK2024003"], remark: "" },
];
const totalReceiptAmount = computed(() => {
  return dataList.value.reduce((sum, item) => sum + Number(item.amount), 0);
});
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
const getReceiptMethodLabel = (method) => {
  const map = {
    bank_transfer: "银行转账",
    cash: "现金",
    check: "支票",
    draft: "汇票",
    alipay: "支付宝",
    wechat: "微信",
  };
  return map[method] || method;
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.receiptCode) {
    result = result.filter(item => item.receiptCode.includes(filters.receiptCode));
  }
  if (filters.customerId) {
    result = result.filter(item => item.customerId === filters.customerId);
  }
  if (filters.receiptMethod) {
    result = result.filter(item => item.receiptMethod === filters.receiptMethod);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
};
const resetFilters = () => {
  filters.receiptCode = "";
  filters.customerId = "";
  filters.receiptMethod = "";
  pagination.currentPage = 1;
  getTableData();
};
const changePage = ({ current, size }) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  getTableData();
};
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增收款";
  Object.assign(form, {
    receiptCode: "SK" + Date.now().toString().slice(-8),
  const filters = reactive({
    collectionNumber: "",
    customerId: "",
    receiptDate: new Date().toISOString().split('T')[0],
    collectionMethod: "",
    dateRange: [],
  });
  const pagination = reactive({
    currentPage: 1,
    pageSize: 10,
    total: 0,
  });
  const columns = [
    { label: "收款单号", prop: "receiptCode", width: "150" },
    { label: "客户名称", prop: "customerName", width: "180" },
    { label: "收款日期", prop: "receiptDate", width: "120" },
    { label: "收款金额", prop: "amount", dataType: "slot", slot: "amount" },
    {
      label: "收款方式",
      prop: "receiptMethod",
      dataType: "slot",
      slot: "receiptMethod",
      width: "120",
    },
    { label: "备注", prop: "remark", showOverflowTooltip: true },
    {
      label: "操作",
      prop: "operation",
      dataType: "slot",
      slot: "operation",
      width: "200",
      fixed: "right",
    },
  ];
  const dataList = ref([]);
  const tableLoading = ref(false);
  const dialogVisible = ref(false);
  const dialogTitle = ref("");
  const formRef = ref(null);
  const isEdit = ref(false);
  const isView = ref(false);
  const currentId = ref(null);
  const submitLoading = ref(false);
  const customerList = ref([]);
  const outboundBatchList = ref([]);
  const outboundBatchOptions = ref([]);
  const outboundBatchLoading = ref(false);
  const outboundSelectVisible = ref(false);
  const outboundTableRef = ref(null);
  const dialogOutboundSelection = ref([]);
  const getReceiptMethodLabel = value => {
    if (value === undefined || value === null || value === "") return "-";
    const item = payment_methods.value?.find(
      m => String(m.value) === String(value)
    );
    return item?.label ?? value;
  };
  const getDefaultReceiptMethod = () => payment_methods.value?.[0]?.value ?? "";
  const form = reactive({
    receiptCode: "",
    customerId: "",
    receiptDate: "",
    amount: 0,
    receiptMethod: "bank_transfer",
    bankAccount: "",
    relatedDocs: [],
    receiptMethod: "",
    stockOutRecordIds: [],
    outboundBatches: "",
    remark: "",
  });
  dialogVisible.value = true;
};
const edit = (row) => {
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑收款";
  Object.assign(form, row);
  dialogVisible.value = true;
};
  const rules = {
    customerId: [{ required: true, message: "请选择客户", trigger: "change" }],
    stockOutRecordIds: [
      {
        required: true,
        type: "array",
        min: 1,
        message: "请选择关联单据",
        trigger: "change",
      },
    ],
    receiptDate: [
      { required: true, message: "请选择收款日期", trigger: "change" },
    ],
    amount: [{ required: true, message: "请输入收款金额", trigger: "blur" }],
    receiptMethod: [
      { required: true, message: "请选择收款方式", trigger: "change" },
    ],
  };
const view = (row) => {
  ElMessage.info(`查看收款单: ${row.receiptCode}`);
};
  const totalReceiptAmount = computed(() =>
    dataList.value.reduce((sum, item) => sum + Number(item.amount || 0), 0)
  );
const handleConfirm = (row) => {
  ElMessageBox.confirm("确认该收款单吗?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "info",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData[index].status = "confirmed";
    }
    ElMessage.success("确认成功");
    getTableData();
  const formatMoney = value => {
    if (value === undefined || value === null) return "0.00";
    return Number(value)
      .toFixed(2)
      .replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  };
  const parseStockOutRecordIds = value => {
    if (!value) return [];
    if (Array.isArray(value)) return value;
    return String(value)
      .split(/[,,]/)
      .map(s => s.trim())
      .filter(Boolean)
      .map(s => (/^\d+$/.test(s) ? Number(s) : s));
  };
  const formatOutboundBatches = value => {
    if (value === undefined || value === null || value === "") return "";
    if (Array.isArray(value)) return value.filter(Boolean).join("、");
    return String(value)
      .split(/[,,]/)
      .map(s => s.trim())
      .filter(Boolean)
      .join("、");
  };
  const normalizeTableRow = row => ({
    ...row,
    receiptCode: row.collectionNumber ?? row.receiptCode,
    receiptDate: row.collectionDate ?? row.receiptDate,
    amount: row.collectionAmount ?? row.amount,
    receiptMethod: row.collectionMethod ?? row.receiptMethod ?? "",
    stockOutRecordIds: row.stockOutRecordIds ?? row.stockOutRecordId ?? "",
    outboundBatches: formatOutboundBatches(row.outboundBatches),
  });
};
const handleDelete = (row) => {
  ElMessageBox.confirm("确认删除该收款单吗?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    const index = mockData.findIndex(item => item.id === row.id);
    if (index !== -1) {
      mockData.splice(index, 1);
    }
    ElMessage.success("删除成功");
    getTableData();
  });
};
const handleOut = () => {
  ElMessage.success("导出成功");
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const customer = customerList.find(item => item.id === form.customerId);
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, customerName: customer?.name, status: "pending" });
        ElMessage.success("新增成功");
  const getCustomerList = () => {
    listCustomer({ current: -1, size: -1, type: 0 }).then(res => {
      if (res.code === 200) {
        customerList.value = res.data?.records || [];
      }
      dialogVisible.value = false;
      getTableData();
    }
  });
};
    });
  };
onMounted(() => {
  getTableData();
});
  const normalizeOutboundBatchOptions = data => {
    const list = Array.isArray(data) ? data : [];
    return list.map((item, index) => {
      if (typeof item === "string" || typeof item === "number") {
        const text = String(item);
        return { label: text, value: text, outboundAmount: 0 };
      }
      const label =
        item.outboundBatches ??
        item.batchNo ??
        item.shippingNo ??
        item.outboundNo ??
        item.label ??
        `出库单${index + 1}`;
      const value = item.id ?? item.stockOutRecordId ?? label;
      return {
        label: String(label),
        value,
        outboundAmount: Number(item.outboundAmount) || 0,
      };
    });
  };
  const isSameOutboundId = (a, b) => String(a) === String(b);
  const getOutboundRowId = row => row?.id ?? row?.stockOutRecordId;
  const outboundBatchDisplayText = computed(() => {
    if (isEdit.value || isView.value) {
      return form.outboundBatches || "";
    }
    if (form.outboundBatches) return form.outboundBatches;
    const ids = form.stockOutRecordIds || [];
    if (!ids.length) return "";
    const labels = outboundBatchOptions.value
      .filter(opt => ids.some(id => isSameOutboundId(id, opt.value)))
      .map(opt => opt.label);
    if (labels.length) return labels.join("、");
    return ids.join("、");
  });
  const handleOutboundInputClick = () => {
    if (isEdit.value || isView.value) return;
    openOutboundSelectDialog();
  };
  /** ä¸ºå·²é€‰ ID è¡¥å…¨é€‰é¡¹ï¼ˆç¼–辑/查看回显) */
  const ensureOutboundOptionsForSelected = () => {
    const ids = form.stockOutRecordIds || [];
    ids.forEach(id => {
      const exists = outboundBatchOptions.value.some(opt =>
        isSameOutboundId(opt.value, id)
      );
      if (exists) return;
      const fromList = outboundBatchList.value.find(row =>
        isSameOutboundId(getOutboundRowId(row), id)
      );
      if (fromList) {
        const [option] = normalizeOutboundBatchOptions([fromList]);
        if (option) outboundBatchOptions.value.push(option);
        return;
      }
      outboundBatchOptions.value.push({
        label: String(id),
        value: id,
        outboundAmount: 0,
      });
    });
  };
  const syncCollectionAmount = () => {
    const selected = form.stockOutRecordIds || [];
    const sum = outboundBatchOptions.value
      .filter(opt => selected.some(id => isSameOutboundId(id, opt.value)))
      .reduce((acc, opt) => acc + (Number(opt.outboundAmount) || 0), 0);
    form.amount = sum > 0 ? Number(sum.toFixed(2)) : 0;
  };
  const restoreOutboundTableSelection = () => {
    nextTick(() => {
      const table = outboundTableRef.value;
      if (!table) return;
      table.clearSelection();
      const selectedIds = new Set(
        (form.stockOutRecordIds || []).map(id => String(id))
      );
      outboundBatchList.value.forEach(row => {
        const rowId = getOutboundRowId(row);
        if (
          rowId !== undefined &&
          rowId !== null &&
          selectedIds.has(String(rowId))
        ) {
          table.toggleRowSelection(row, true);
        }
      });
    });
  };
  const loadOutboundBatches = (customerId, keepSelected = false) => {
    if (!customerId) {
      outboundBatchList.value = [];
      outboundBatchOptions.value = [];
      if (!keepSelected) {
        form.stockOutRecordIds = [];
        form.amount = 0;
      }
      return Promise.resolve();
    }
    outboundBatchLoading.value = true;
    return getOutboundBatchesByCustomer({ customerId })
      .then(res => {
        if (res.code === 200) {
          const list = res.data?.records ?? res.data ?? [];
          outboundBatchList.value = Array.isArray(list) ? list : [];
          outboundBatchOptions.value = normalizeOutboundBatchOptions(list);
        } else {
          outboundBatchList.value = [];
          outboundBatchOptions.value = [];
        }
      })
      .catch(() => {
        outboundBatchList.value = [];
        outboundBatchOptions.value = [];
      })
      .finally(() => {
        outboundBatchLoading.value = false;
        if (keepSelected) {
          ensureOutboundOptionsForSelected();
          restoreOutboundTableSelection();
        }
      });
  };
  const handleCustomerChange = customerId => {
    form.stockOutRecordIds = [];
    form.outboundBatches = "";
    form.amount = 0;
    loadOutboundBatches(customerId);
  };
  const openOutboundSelectDialog = () => {
    if (!form.customerId || isEdit.value || isView.value) return;
    outboundSelectVisible.value = true;
    loadOutboundBatches(form.customerId, true).then(() => {
      restoreOutboundTableSelection();
    });
  };
  const handleOutboundDialogSelectionChange = selection => {
    dialogOutboundSelection.value = selection;
  };
  const confirmOutboundSelection = () => {
    if (dialogOutboundSelection.value.length === 0) {
      ElMessage.warning("请至少选择一条关联单据");
      return;
    }
    form.stockOutRecordIds = dialogOutboundSelection.value
      .map(row => getOutboundRowId(row))
      .filter(id => id !== undefined && id !== null);
    form.outboundBatches = dialogOutboundSelection.value
      .map(row => row.outboundBatches ?? row.batchNo ?? row.shippingNo ?? "")
      .filter(Boolean)
      .join("、");
    outboundSelectVisible.value = false;
    syncCollectionAmount();
    formRef.value?.validateField("stockOutRecordIds");
  };
  const handleOutboundDialogClosed = () => {
    dialogOutboundSelection.value = [];
  };
  const appendFilterParams = params => {
    if (filters.collectionNumber) {
      params.collectionNumber = filters.collectionNumber;
    }
    if (filters.customerId) {
      params.customerId = filters.customerId;
    }
    if (filters.collectionMethod) {
      params.collectionMethod = filters.collectionMethod;
    }
    if (filters.dateRange?.length === 2) {
      params.startDate = filters.dateRange[0];
      params.endDate = filters.dateRange[1];
    }
    return params;
  };
  const buildListParams = () =>
    appendFilterParams({
      current: pagination.currentPage,
      size: pagination.pageSize,
    });
  const buildExportParams = () => appendFilterParams({});
  const buildSubmitPayload = (forUpdate = false) => {
    const payload = {
      customerId: form.customerId,
      collectionDate: form.receiptDate,
      collectionAmount: form.amount,
      collectionMethod: form.receiptMethod,
      collectionNumber: form.receiptCode || "",
      remark: form.remark || "",
      stockOutRecordIds: (form.stockOutRecordIds || []).join(","),
    };
    if (forUpdate) {
      payload.id = currentId.value;
    }
    return payload;
  };
  const fillFormFromRow = row => {
    const stockOutRecordIds = parseStockOutRecordIds(
      row.stockOutRecordIds ?? row.stockOutRecordId
    );
    Object.assign(form, {
      receiptCode: row.receiptCode ?? row.collectionNumber ?? "",
      customerId: row.customerId,
      receiptDate: row.receiptDate ?? row.collectionDate ?? "",
      amount: Number(row.amount ?? row.collectionAmount ?? 0),
      receiptMethod: row.receiptMethod ?? row.collectionMethod ?? "",
      stockOutRecordIds,
      outboundBatches: formatOutboundBatches(row.outboundBatches),
      remark: row.remark ?? "",
    });
  };
  const closeDialog = () => {
    dialogVisible.value = false;
    outboundSelectVisible.value = false;
    isView.value = false;
    isEdit.value = false;
  };
  const handleExport = () => {
    const params = buildExportParams();
    proxy.download(
      "/accountSalesCollection/exportAccountSalesCollection",
      params,
      `收款单_${Date.now()}.xlsx`
    );
  };
  const getTableData = () => {
    tableLoading.value = true;
    listPageAccountSalesCollection(buildListParams())
      .then(res => {
        const ok = res.code === 200 || res.code === 0;
        if (ok && res.data) {
          pagination.total = res.data.total ?? 0;
          dataList.value = (res.data.records ?? []).map(normalizeTableRow);
        } else {
          ElMessage.error(res.msg || "查询失败");
          dataList.value = [];
          pagination.total = 0;
        }
      })
      .catch(() => {
        dataList.value = [];
        pagination.total = 0;
        ElMessage.error("查询失败");
      })
      .finally(() => {
        tableLoading.value = false;
      });
  };
  const onSearch = () => {
    pagination.currentPage = 1;
    getTableData();
  };
  const resetFilters = () => {
    filters.collectionNumber = "";
    filters.customerId = "";
    filters.collectionMethod = "";
    filters.dateRange = [];
    pagination.currentPage = 1;
    getTableData();
  };
  const changePage = ({ current, size }) => {
    pagination.currentPage = current;
    pagination.pageSize = size;
    getTableData();
  };
  const add = () => {
    isEdit.value = false;
    isView.value = false;
    dialogTitle.value = "新增收款";
    Object.assign(form, {
      receiptCode: "SK" + Date.now().toString().slice(-8),
      customerId: "",
      receiptDate: new Date().toISOString().split("T")[0],
      amount: 0,
      receiptMethod: getDefaultReceiptMethod(),
      stockOutRecordIds: [],
      outboundBatches: "",
      remark: "",
    });
    outboundBatchList.value = [];
    outboundBatchOptions.value = [];
    dialogVisible.value = true;
  };
  const edit = row => {
    isEdit.value = true;
    isView.value = false;
    currentId.value = row.id;
    dialogTitle.value = "编辑收款";
    fillFormFromRow(row);
    dialogVisible.value = true;
  };
  const view = row => {
    isView.value = true;
    isEdit.value = false;
    dialogTitle.value = "查看收款";
    fillFormFromRow(row);
    dialogVisible.value = true;
  };
  const handleDelete = row => {
    ElMessageBox.confirm(
      `确认删除收款单「${row.receiptCode ?? row.collectionNumber}」吗?`,
      "提示",
      {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }
    ).then(() => {
      deleteAccountSalesCollection([row.id])
        .then(res => {
          if (res.code === 200) {
            ElMessage.success("删除成功");
            getTableData();
          } else {
            ElMessage.error(res.msg || "删除失败");
          }
        })
        .catch(() => {
          ElMessage.error("删除失败");
        });
    });
  };
  const submitForm = () => {
    formRef.value.validate(valid => {
      if (!valid) return;
      submitLoading.value = true;
      const request = isEdit.value
        ? updateAccountSalesCollection(buildSubmitPayload(true))
        : addAccountSalesCollection(buildSubmitPayload());
      request
        .then(res => {
          if (res.code === 200) {
            ElMessage.success(isEdit.value ? "修改成功" : "新增成功");
            closeDialog();
            getTableData();
          } else {
            ElMessage.error(res.msg || (isEdit.value ? "修改失败" : "新增失败"));
          }
        })
        .catch(() => {
          ElMessage.error(isEdit.value ? "修改失败" : "新增失败");
        })
        .finally(() => {
          submitLoading.value = false;
        });
    });
  };
  onMounted(() => {
    getCustomerList();
    getTableData();
  });
</script>
<style lang="scss" scoped>
.actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
}
  .actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15px;
  }
.text-success {
  color: #67c23a;
  font-weight: bold;
}
  .text-success {
    color: #67c23a;
    font-weight: bold;
  }
  .outbound-batch-input:not(.is-disabled) {
    :deep(.el-input__wrapper) {
      cursor: pointer;
    }
  }
</style>
src/views/financialManagement/receivable/reconciliation.vue
@@ -2,8 +2,8 @@
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="客户:">
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable filterable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="对账期间:">
@@ -29,6 +29,7 @@
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :tableLoading="tableLoading"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
@@ -36,21 +37,22 @@
        }"
        @pagination="changePage"
      >
        <template #beginBalance="{ row }">
          <span :class="row.beginBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.beginBalance) }}</span>
        <template #openingBalance="{ row }">
          <span :class="row.openingBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.openingBalance) }}</span>
        </template>
        <template #currentReceivable="{ row }">
          <span class="text-primary">Â¥{{ formatMoney(row.currentReceivable) }}</span>
        <template #currentPlan="{ row }">
          <span class="text-primary">Â¥{{ formatMoney(row.currentPlan) }}</span>
        </template>
        <template #currentReceipt="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.currentReceipt) }}</span>
        <template #currentActually="{ row }">
          <span class="text-success">Â¥{{ formatMoney(row.currentActually) }}</span>
        </template>
        <template #endBalance="{ row }">
          <span :class="row.endBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.endBalance) }}</span>
        <template #closingBalance="{ row }">
          <span :class="row.closingBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.closingBalance) }}</span>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="viewDetail(row)">查看明细</el-button>
          <el-button type="primary" link @click="printStatement(row)">打印</el-button>
          <!-- <el-button type="primary" link @click="printStatement(row)">打印</el-button> -->
          <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
        </template>
      </PIMTable>
    </div>
@@ -60,7 +62,7 @@
        <h3>{{ currentCustomer }} åº”收对账单</h3>
        <p>对账期间: {{ currentPeriod }}</p>
      </div>
      <el-table :data="detailData" border style="width: 100%">
      <el-table :data="detailData" border style="width: 100%" v-loading="detailLoading">
        <el-table-column prop="date" label="日期" width="120" />
        <el-table-column prop="type" label="类型" width="100">
          <template #default="{ row }">
@@ -98,52 +100,68 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="选择客户" prop="customerId">
              <el-select v-model="generateForm.customerId" placeholder="请选择客户" style="width: 100%;" @change="onCustomerChange">
                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
              <el-select
                v-model="generateForm.customerId"
                placeholder="请选择客户"
                style="width: 100%;"
                filterable
                @change="onCustomerChange"
              >
                <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="对账月份" prop="period">
              <el-date-picker v-model="generateForm.period" type="month" placeholder="选择月份" value-format="YYYY-MM" style="width: 100%;" @change="onPeriodChange" />
            <el-form-item label="对账月份" prop="statementMonth">
              <el-date-picker v-model="generateForm.statementMonth" type="month" placeholder="选择月份" value-format="YYYY-MM" style="width: 100%;" @change="onStatementMonthChange" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div v-if="salesData.length > 0" class="sales-section">
        <div class="section-title">本月销售数据</div>
        <el-table :data="salesData" border style="width: 100%; margin-bottom: 15px;" v-loading="salesLoading" @selection-change="handleSalesSelectionChange">
      <div v-if="statementDetailLoaded" class="sales-section">
        <div v-if="salesData.length > 0" class="section-title">本月销售数据</div>
        <el-table
          v-if="salesData.length > 0"
          ref="salesTableRef"
          :data="salesData"
          border
          row-key="id"
          style="width: 100%; margin-bottom: 15px;"
          v-loading="salesLoading"
          @selection-change="handleSalesSelectionChange"
        >
          <el-table-column type="selection" width="55" align="center" />
          <el-table-column prop="date" label="日期" width="120" />
          <el-table-column prop="code" label="单据编号" width="150" />
          <el-table-column prop="occurrenceDate" label="日期" width="120" />
          <el-table-column prop="receiptNumber" label="单据编号" width="150" />
          <el-table-column prop="type" label="类型" width="100">
            <template #default="{ row }">
              <el-tag :type="row.type === '出库' ? 'success' : row.type === '收款' ? 'primary' : 'danger'">{{ row.type }}</el-tag>
              <el-tag :type="getDetailTypeTagType(row.type)">{{ row.typeLabel }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="amount" label="金额" width="120">
            <template #default="{ row }">
              <span :class="row.type === '出库' ? 'text-primary' : row.type === '收款' ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.amount) }}</span>
              <span :class="getDetailAmountClass(row.type)">Â¥{{ formatMoney(row.amount) }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="remark" label="备注" />
        </el-table>
        <el-empty v-else description="该客户本月暂无明细数据" :image-size="80" />
        <div class="summary-row">
          <span>期初余额: <strong class="text-primary">Â¥{{ formatMoney(generateForm.beginBalance) }}</strong></span>
          <span>本期应收: <strong class="text-primary">Â¥{{ formatMoney(generateForm.currentReceivable) }}</strong></span>
          <span>本期收款: <strong class="text-success">Â¥{{ formatMoney(generateForm.currentReceipt) }}</strong></span>
          <span>期末余额: <strong :class="calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt) >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt)) }}</strong></span>
          <span>期初余额: <strong class="text-primary">Â¥{{ formatMoney(generateForm.openingBalance) }}</strong></span>
          <span>本期应收: <strong class="text-primary">Â¥{{ formatMoney(generateForm.currentPlan) }}</strong></span>
          <span>本期收款: <strong class="text-success">Â¥{{ formatMoney(generateForm.currentActually) }}</strong></span>
          <span>期末余额: <strong :class="displayClosingBalance >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(displayClosingBalance) }}</strong></span>
        </div>
      </div>
      <div v-else-if="generateForm.customerId && !salesLoading" class="empty-tip">
      <div v-else-if="generateForm.customerId && generateForm.statementMonth && !salesLoading" class="empty-tip">
        <el-empty description="该客户本月暂无销售数据" />
      </div>
      <template #footer>
        <el-button type="primary" @click="confirmGenerate" :disabled="!canGenerate">确认生成</el-button>
        <el-button type="primary" @click="confirmGenerate" :disabled="!canGenerate" :loading="submitLoading">确认生成</el-button>
        <el-button @click="generateDialogVisible = false">取消</el-button>
      </template>
    </FormDialog>
@@ -151,9 +169,20 @@
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage } from "element-plus";
import { ref, reactive, onMounted, computed, nextTick, getCurrentInstance } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { listCustomer } from "@/api/basicData/customer.js";
import {
  getAccountStatementDetailsByMonth,
  addAccountStatement,
  listPageAccountStatement,
  deleteAccountStatement,
} from "@/api/financialManagement/accountStatement.js";
const ACCOUNT_TYPE_RECEIVABLE = 1;
const { proxy } = getCurrentInstance();
defineOptions({
  name: "应收对账",
@@ -172,56 +201,192 @@
});
const columns = [
  { label: "对账单号", prop: "statementCode", width: "150" },
  { label: "对账单号", prop: "statementNumber", width: "150" },
  { label: "客户名称", prop: "customerName", width: "180" },
  { label: "对账期间", prop: "period", width: "150" },
  { label: "期初余额", prop: "beginBalance", slot: "beginBalance" },
  { label: "本期应收", prop: "currentReceivable", slot: "currentReceivable" },
  { label: "本期收款", prop: "currentReceipt", slot: "currentReceipt" },
  { label: "期末余额", prop: "endBalance", slot: "endBalance" },
  { label: "操作", prop: "operation", slot: "operation", width: "150", fixed: "right" },
  { label: "对账期间", prop: "statementMonth", width: "150" },
  { label: "期初余额", prop: "openingBalance", dataType: "slot", slot: "openingBalance" },
  { label: "本期应收", prop: "currentPlan", dataType: "slot", slot: "currentPlan" },
  { label: "本期收款", prop: "currentActually", dataType: "slot", slot: "currentActually" },
  { label: "期末余额", prop: "closingBalance", dataType: "slot", slot: "closingBalance" },
  { label: "操作", prop: "operation", dataType: "slot", slot: "operation", width: "200", fixed: "right" },
];
const dataList = ref([]);
const tableLoading = ref(false);
const submitLoading = ref(false);
const detailDialogVisible = ref(false);
const currentCustomer = ref("");
const currentPeriod = ref("");
const detailData = ref([]);
const detailLoading = ref(false);
const generateDialogVisible = ref(false);
const salesLoading = ref(false);
const statementDetailLoaded = ref(false);
const salesData = ref([]);
const selectedSales = ref([]);
const salesTableRef = ref(null);
const customerList = ref([]);
/** æ˜Žç»† type:1出库 2入库 3收款 4付款 5退货 */
const STATEMENT_DETAIL_TYPE_MAP = {
  1: "出库",
  2: "入库",
  3: "收款",
  4: "付款",
  5: "退货",
};
const calculateEndBalance = (openingBalance, currentPlan, currentActually) => {
  return openingBalance + currentPlan - currentActually;
};
const getDetailTypeLabel = (type) => STATEMENT_DETAIL_TYPE_MAP[Number(type)] ?? "";
const getDetailTypeTagType = (type) => {
  const t = Number(type);
  if (t === 1) return "success";
  if (t === 3) return "primary";
  if (t === 5) return "danger";
  return "info";
};
const getDetailAmountClass = (type) => {
  const t = Number(type);
  if (t === 1) return "text-primary";
  if (t === 3) return "text-success";
  return "text-danger";
};
const generateForm = reactive({
  customerId: "",
  customerName: "",
  period: "",
  beginBalance: 0,
  currentReceivable: 0,
  currentReceipt: 0,
  statementMonth: "",
  openingBalance: 0,
  currentPlan: 0,
  currentActually: 0,
  closingBalance: 0,
});
const displayClosingBalance = computed(() => {
  return calculateEndBalance(
    generateForm.openingBalance,
    generateForm.currentPlan,
    generateForm.currentActually
  );
});
const canGenerate = computed(() => {
  return generateForm.customerId && generateForm.period && selectedSales.value.length > 0;
  return generateForm.customerId && generateForm.statementMonth && selectedSales.value.length > 0;
});
const customerList = [
  { id: 1, name: "北京科技有限公司" },
  { id: 2, name: "上海贸易公司" },
  { id: 3, name: "广州实业有限公司" },
  { id: 4, name: "深圳电子公司" },
];
const mockData = [
  { id: 1, statementCode: "DZ202401001", customerId: 1, customerName: "北京科技有限公司", period: "2024-01", beginBalance: 10000, currentReceivable: 15000, currentReceipt: 8000, endBalance: 17000 },
  { id: 2, statementCode: "DZ202401002", customerId: 2, customerName: "上海贸易公司", period: "2024-01", beginBalance: 5000, currentReceivable: 12000, currentReceipt: 10000, endBalance: 7000 },
  { id: 3, statementCode: "DZ202402001", customerId: 1, customerName: "北京科技有限公司", period: "2024-02", beginBalance: 17000, currentReceivable: 20000, currentReceipt: 15000, endBalance: 22000 },
];
const calculateEndBalance = (beginBalance, currentReceivable, currentReceipt) => {
  return beginBalance + currentReceivable - currentReceipt;
const applyStatementSummary = (data) => {
  generateForm.openingBalance = Number(data.openingBalance ?? 0);
  generateForm.currentPlan = Number(data.currentPlan ?? 0);
  generateForm.currentActually = Number(data.currentActually ?? 0);
  generateForm.closingBalance = Number(
    data.closingBalance ??
      calculateEndBalance(
        generateForm.openingBalance,
        generateForm.currentPlan,
        generateForm.currentActually
      )
  );
};
const getCustomerList = () => {
  listCustomer({ current: -1, size: -1, type: 0 }).then((res) => {
    if (res.code === 200) {
      customerList.value = res.data?.records || [];
    }
  });
};
const normalizeSalesRows = (list) => {
  const rows = Array.isArray(list) ? list : [];
  return rows.map((item, index) => {
    const type = Number(item.type);
    return {
      id: item.id ?? `detail-${index}`,
      accountStatementId: item.accountStatementId,
      occurrenceDate: item.occurrenceDate ?? "",
      receiptNumber: item.receiptNumber ?? "",
      type,
      typeLabel: getDetailTypeLabel(type),
      amount: Math.abs(Number(item.amount ?? 0)),
      remark: item.remark ?? "",
    };
  });
};
const selectAllSalesRows = (keepApiSummary = false) => {
  nextTick(() => {
    const table = salesTableRef.value;
    if (!table) return;
    table.clearSelection();
    salesData.value.forEach((row) => table.toggleRowSelection(row, true));
    selectedSales.value = [...salesData.value];
    if (!keepApiSummary) {
      calculateSummary();
    }
  });
};
const isNumericId = (id) => id !== undefined && id !== null && id !== "" && /^\d+$/.test(String(id));
const buildFilterParams = (params = {}) => {
  const result = { ...params, accountType: ACCOUNT_TYPE_RECEIVABLE };
  if (filters.customerId) {
    result.customerId = filters.customerId;
  }
  if (filters.startMonth && filters.endMonth && filters.startMonth === filters.endMonth) {
    result.statementMonth = filters.startMonth;
  } else if (filters.startMonth) {
    result.startMonth = filters.startMonth;
  }
  if (filters.endMonth && filters.startMonth !== filters.endMonth) {
    result.endMonth = filters.endMonth;
  }
  return result;
};
const buildListParams = () =>
  buildFilterParams({
    current: pagination.currentPage,
    size: pagination.pageSize,
  });
const buildExportParams = () => buildFilterParams({});
const buildDetailSubmitItem = (row) => {
  const item = {
    occurrenceDate: row.occurrenceDate,
    receiptNumber: row.receiptNumber,
    type: row.type,
    amount: row.amount,
    remark: row.remark ?? "",
  };
  if (isNumericId(row.id)) {
    item.id = Number(row.id);
  }
  if (row.accountStatementId) {
    item.accountStatementId = row.accountStatementId;
  }
  return item;
};
const buildAddPayload = () => ({
  customerId: generateForm.customerId,
  customerName: generateForm.customerName,
  statementMonth: generateForm.statementMonth,
  accountType: ACCOUNT_TYPE_RECEIVABLE,
  statementNumber: "",
  openingBalance: generateForm.openingBalance,
  currentPlan: generateForm.currentPlan,
  currentActually: generateForm.currentActually,
  closingBalance: generateForm.closingBalance,
  accountStatementDetails: selectedSales.value.map(buildDetailSubmitItem),
});
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
@@ -229,15 +394,27 @@
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.customerId) {
    result = result.filter(item => item.customerId === filters.customerId);
  }
  if (filters.startMonth && filters.endMonth) {
    result = result.filter(item => item.period >= filters.startMonth && item.period <= filters.endMonth);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
  tableLoading.value = true;
  listPageAccountStatement(buildListParams())
    .then((res) => {
      const ok = res.code === 200 || res.code === 0;
      if (ok && res.data) {
        pagination.total = res.data.total ?? 0;
        dataList.value = res.data.records ?? [];
      } else {
        ElMessage.error(res.msg || "查询失败");
        dataList.value = [];
        pagination.total = 0;
      }
    })
    .catch(() => {
      dataList.value = [];
      pagination.total = 0;
      ElMessage.error("查询失败");
    })
    .finally(() => {
      tableLoading.value = false;
    });
};
const resetFilters = () => {
@@ -257,83 +434,97 @@
const generateStatement = () => {
  generateForm.customerId = "";
  generateForm.customerName = "";
  generateForm.period = "";
  generateForm.beginBalance = 0;
  generateForm.currentReceivable = 0;
  generateForm.currentReceipt = 0;
  generateForm.statementMonth = "";
  generateForm.openingBalance = 0;
  generateForm.currentPlan = 0;
  generateForm.currentActually = 0;
  generateForm.closingBalance = 0;
  statementDetailLoaded.value = false;
  salesData.value = [];
  selectedSales.value = [];
  generateDialogVisible.value = true;
};
const onCustomerChange = (customerId) => {
  const customer = customerList.find(item => item.id === customerId);
  if (customer) {
    generateForm.customerName = customer.name;
  }
  const customer = customerList.value.find((item) => item.id === customerId);
  generateForm.customerName = customer?.customerName ?? "";
  loadSalesData();
};
const onPeriodChange = () => {
const onStatementMonthChange = () => {
  loadSalesData();
};
const loadSalesData = () => {
  if (!generateForm.customerId || !generateForm.period) {
  if (!generateForm.customerId || !generateForm.statementMonth) {
    salesData.value = [];
    selectedSales.value = [];
    statementDetailLoaded.value = false;
    generateForm.openingBalance = 0;
    generateForm.currentPlan = 0;
    generateForm.currentActually = 0;
    generateForm.closingBalance = 0;
    return;
  }
  salesLoading.value = true;
  selectedSales.value = [];
  statementDetailLoaded.value = false;
  setTimeout(() => {
    const mockSalesData = [
      { id: 1, date: generateForm.period + "-03", code: "CK2024001", type: "出库", amount: 8000, remark: "产品A销售" },
      { id: 2, date: generateForm.period + "-08", code: "SK2024001", type: "收款", amount: 5000, remark: "客户回款" },
      { id: 3, date: generateForm.period + "-12", code: "CK2024002", type: "出库", amount: 12000, remark: "产品B销售" },
      { id: 4, date: generateForm.period + "-15", code: "TH2024001", type: "退货", amount: 2000, remark: "质量问题退货" },
      { id: 5, date: generateForm.period + "-20", code: "CK2024003", type: "出库", amount: 5000, remark: "产品C销售" },
      { id: 6, date: generateForm.period + "-25", code: "SK2024002", type: "收款", amount: 8000, remark: "客户回款" },
    ];
  getAccountStatementDetailsByMonth({
    accountType: ACCOUNT_TYPE_RECEIVABLE,
    customerId: generateForm.customerId,
    statementMonth: generateForm.statementMonth,
  })
    .then((res) => {
      if (res.code === 200) {
        const data = res.data ?? {};
        const details = data.accountStatementDetails;
        const list = Array.isArray(details) ? details : [];
        salesData.value = normalizeSalesRows(list);
        applyStatementSummary(data);
        statementDetailLoaded.value = true;
    salesData.value = mockSalesData;
    const lastPeriod = getLastPeriod(generateForm.period);
    const lastStatement = mockData.find(item =>
      item.customerId === generateForm.customerId && item.period === lastPeriod
    );
    generateForm.beginBalance = lastStatement ? lastStatement.endBalance : 0;
    calculateSummary();
    salesLoading.value = false;
  }, 500);
};
const getLastPeriod = (period) => {
  const [year, month] = period.split("-").map(Number);
  if (month === 1) {
    return `${year - 1}-12`;
  }
  return `${year}-${String(month - 1).padStart(2, "0")}`;
        if (salesData.value.length > 0) {
          selectAllSalesRows(true);
        }
      } else {
        salesData.value = [];
        statementDetailLoaded.value = false;
        ElMessage.error(res.msg || "查询对账明细失败");
      }
    })
    .catch(() => {
      salesData.value = [];
      statementDetailLoaded.value = false;
      ElMessage.error("查询对账明细失败");
    })
    .finally(() => {
      salesLoading.value = false;
    });
};
const calculateSummary = () => {
  let receivable = 0;
  let receipt = 0;
  selectedSales.value.forEach(item => {
    if (item.type === "出库") {
  selectedSales.value.forEach((item) => {
    if (item.type === 1) {
      receivable += item.amount;
    } else if (item.type === "退货") {
    } else if (item.type === 5) {
      receivable -= item.amount;
    } else if (item.type === "收款") {
    } else if (item.type === 3) {
      receipt += item.amount;
    }
  });
  generateForm.currentReceivable = receivable;
  generateForm.currentReceipt = receipt;
  generateForm.currentPlan = receivable;
  generateForm.currentActually = receipt;
  generateForm.closingBalance = calculateEndBalance(
    generateForm.openingBalance,
    generateForm.currentPlan,
    generateForm.currentActually
  );
};
const handleSalesSelectionChange = (selection) => {
@@ -342,51 +533,127 @@
};
const confirmGenerate = () => {
  const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
  const endBalance = calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt);
  if (!canGenerate.value) return;
  submitLoading.value = true;
  addAccountStatement(buildAddPayload())
    .then((res) => {
      if (res.code === 200) {
        generateDialogVisible.value = false;
        ElMessage.success("对账单生成成功");
        pagination.currentPage = 1;
        getTableData();
      } else {
        ElMessage.error(res.msg || "生成失败");
      }
    })
    .catch(() => {
      ElMessage.error("生成失败");
    })
    .finally(() => {
      submitLoading.value = false;
    });
};
  mockData.unshift({
    id: newId,
    statementCode: "DZ" + Date.now(),
    customerId: generateForm.customerId,
    customerName: generateForm.customerName,
    period: generateForm.period,
    beginBalance: generateForm.beginBalance,
    currentReceivable: generateForm.currentReceivable,
    currentReceipt: generateForm.currentReceipt,
    endBalance,
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除对账单「${row.statementNumber || row.id}」吗?`, "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    deleteAccountStatement([row.id])
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success("删除成功");
          getTableData();
        } else {
          ElMessage.error(res.msg || "删除失败");
        }
      })
      .catch(() => {
        ElMessage.error("删除失败");
      });
  });
};
const buildDetailTableFromApi = (data, statementMonth) => {
  const details = Array.isArray(data.accountStatementDetails) ? data.accountStatementDetails : [];
  let runningBalance = Number(data.openingBalance ?? 0);
  const rows = [
    {
      date: statementMonth ?? "",
      type: "期初",
      code: "-",
      debit: 0,
      credit: 0,
      balance: runningBalance,
      remark: "期初余额",
    },
  ];
  details.forEach((item) => {
    const amount = Math.abs(Number(item.amount ?? 0));
    const type = Number(item.type);
    let debit = 0;
    let credit = 0;
    if (type === 1) {
      debit = amount;
      runningBalance += amount;
    } else if (type === 3 || type === 5) {
      credit = amount;
      runningBalance -= amount;
    }
    rows.push({
      date: item.occurrenceDate ?? "",
      type: getDetailTypeLabel(type),
      code: item.receiptNumber ?? "",
      debit,
      credit,
      balance: runningBalance,
      remark: item.remark ?? "",
    });
  });
  generateDialogVisible.value = false;
  ElMessage.success("对账单生成成功");
  getTableData();
  return rows;
};
const viewDetail = (row) => {
  currentCustomer.value = row.customerName;
  currentPeriod.value = row.period;
  if (!row.customerId || !row.statementMonth) {
    ElMessage.warning("缺少客户或对账月份,无法查询明细");
    return;
  }
  const saleOutAmount = Math.floor(row.currentReceivable * 0.6);
  const returnAmount = Math.floor(row.currentReceivable * 0.1);
  const firstReceipt = Math.floor(row.currentReceipt * 0.4);
  const secondReceipt = row.currentReceipt - firstReceipt;
  let runningBalance = row.beginBalance;
  detailData.value = [
    { date: row.period + "-01", type: "期初", code: "-", debit: 0, credit: 0, balance: runningBalance, remark: "期初余额" },
    { date: row.period + "-05", type: "出库", code: "CK2024001", debit: saleOutAmount, credit: 0, balance: runningBalance += saleOutAmount, remark: "销售出库" },
    { date: row.period + "-10", type: "收款", code: "SK2024001", debit: 0, credit: firstReceipt, balance: runningBalance -= firstReceipt, remark: "客户回款" },
    { date: row.period + "-15", type: "出库", code: "CK2024002", debit: row.currentReceivable - saleOutAmount - returnAmount, credit: 0, balance: runningBalance += (row.currentReceivable - saleOutAmount - returnAmount), remark: "销售出库" },
    { date: row.period + "-20", type: "退货", code: "TH2024001", debit: 0, credit: returnAmount, balance: runningBalance -= returnAmount, remark: "销售退货" },
    { date: row.period + "-25", type: "收款", code: "SK2024002", debit: 0, credit: secondReceipt, balance: runningBalance -= secondReceipt, remark: "客户回款" },
  ];
  currentCustomer.value = row.customerName ?? "";
  currentPeriod.value = row.statementMonth ?? "";
  detailData.value = [];
  detailDialogVisible.value = true;
  detailLoading.value = true;
  getAccountStatementDetailsByMonth({
    accountType: ACCOUNT_TYPE_RECEIVABLE,
    customerId: row.customerId,
    statementMonth: row.statementMonth,
  })
    .then((res) => {
      if (res.code === 200) {
        detailData.value = buildDetailTableFromApi(res.data ?? {}, row.statementMonth);
      } else {
        ElMessage.error(res.msg || "查询明细失败");
        detailDialogVisible.value = false;
      }
    })
    .catch(() => {
      ElMessage.error("查询明细失败");
      detailDialogVisible.value = false;
    })
    .finally(() => {
      detailLoading.value = false;
    });
};
const printStatement = (row) => {
  ElMessage.info(`打印对账单: ${row.statementCode}`);
  ElMessage.info(`打印对账单: ${row.statementNumber}`);
};
const printDetail = () => {
@@ -394,10 +661,12 @@
};
const handleOut = () => {
  ElMessage.success("导出成功");
  const params = buildExportParams();
  proxy.download("/accountStatement/exportAccountStatement", params, `应收对账单_${Date.now()}.xlsx`);
};
onMounted(() => {
  getCustomerList();
  getTableData();
});
</script>
src/views/financialManagement/revenueManagement/Modal.vue
ÎļþÒÑɾ³ý
src/views/financialManagement/revenueManagement/index.vue
ÎļþÒÑɾ³ý
src/views/financialManagement/salesRefund/components/ReceiptandRefundPopupWindow.vue
ÎļþÒÑɾ³ý
src/views/financialManagement/salesRefund/index.vue
ÎļþÒÑɾ³ý
src/views/procurementManagement/invoiceEntry/components/ExpandTable.vue
ÎļþÒÑɾ³ý
src/views/procurementManagement/invoiceEntry/components/Modal.vue
ÎļþÒÑɾ³ý
src/views/procurementManagement/invoiceEntry/index.vue
ÎļþÒÑɾ³ý
src/views/procurementManagement/paymentEntry/index.vue
ÎļþÒÑɾ³ý
src/views/procurementManagement/paymentHistory/index.vue
ÎļþÒÑɾ³ý
src/views/procurementManagement/paymentLedger/index.vue
@@ -1,64 +1,54 @@
<template>
  <div class="app-container">
    <div class="search_form" style="margin-bottom: 20px;">
    <div class="search_form"
         style="margin-bottom: 20px;">
      <div>
        <span class="search_title">供应商名称:</span>
        <el-input
          v-model="searchForm.supplierName"
          style="width: 240px"
          placeholder="输入供应商名称"
          @change="handleQuery"
          clearable
          :prefix-icon="Search"
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
          >搜索</el-button
        >
        <el-input v-model="searchForm.supplierName"
                  style="width: 240px"
                  placeholder="输入供应商名称"
                  @change="handleQuery"
                  clearable
                  :prefix-icon="Search" />
        <el-button type="primary"
                   @click="handleQuery"
                   style="margin-left: 10px">搜索</el-button>
      </div>
      <div></div>
    </div>
    <el-row :gutter="20">
      <el-col :span="14">
        <div class="table_list">
          <el-table
            ref="multipleTable"
            border
            v-loading="tableLoading"
            :data="tableData"
            :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
            height="calc(100vh - 18.5em)"
            :highlight-current-row="true"
            style="width: 100%"
            tooltip-effect="dark"
            @row-click="rowClick"
            :show-summary="isShowSummary"
            :summary-method="summarizeMainTable"
            class="lims-table"
          >
            <el-table-column
              align="center"
              label="序号"
              type="index"
              width="60"
            />
            <el-table-column label="供应商名称" prop="supplierName" />
            <el-table-column
              label="合同金额(元)"
              prop="invoiceAmount"
              show-overflow-tooltip
              :formatter="formattedNumber"
            />
            <el-table-column
              label="付款金额(元)"
              prop="paymentAmount"
              show-overflow-tooltip
              :formatter="formattedNumber"
            />
            <el-table-column
              label="应付金额(元)"
              prop="payableAmount"
              show-overflow-tooltip
            >
          <el-table ref="multipleTable"
                    border
                    v-loading="tableLoading"
                    :data="tableData"
                    :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
                    height="calc(100vh - 18.5em)"
                    :highlight-current-row="true"
                    style="width: 100%"
                    tooltip-effect="dark"
                    @row-click="rowClick"
                    :show-summary="isShowSummary"
                    :summary-method="summarizeMainTable"
                    class="lims-table">
            <el-table-column align="center"
                             label="序号"
                             type="index"
                             width="60" />
            <el-table-column label="供应商名称"
                             prop="supplierName" />
            <el-table-column label="合同金额(元)"
                             prop="contractAmounts"
                             show-overflow-tooltip
                             :formatter="formattedNumber" />
            <el-table-column label="付款金额(元)"
                             prop="paymentAmount"
                             show-overflow-tooltip
                             :formatter="formattedNumber" />
            <el-table-column label="应付金额(元)"
                             prop="payableAmount"
                             show-overflow-tooltip>
              <template #default="{ row, column }">
                <el-text type="danger">
                  {{ formattedNumber(row, column, row.payableAmount) }}
@@ -66,29 +56,27 @@
              </template>
            </el-table-column>
          </el-table>
          <pagination
            v-show="total > 0"
            @pagination="paginationSearch"
            :total="total"
            :layout="page.layout"
            :page="page.current"
            :limit="page.size"
          />
          <pagination v-show="total > 0"
                      @pagination="paginationSearch"
                      :total="total"
                      :layout="page.layout"
                      :page="page.current"
                      :limit="page.size" />
        </div>
      </el-col>
      <el-col :span="10">
        <div class="table_list">
          <PIMTable
            rowKey="id"
            :column="tableColumnSon"
            :tableData="originalTableDataSon"
            :isSelection="false"
            :isShowPagination="false"
            :tableLoading="tableLoadingSon"
            :isShowSummary="isShowSummarySon"
            :summaryMethod="summarizeMainTable1"
                        height="calc(100vh - 18.5em)"
          >
          <PIMTable rowKey="id"
                    :column="tableColumnSon"
                    :tableData="originalTableDataSon"
                    :isSelection="false"
                    :isShowPagination="true"
                    :page="sonPage"
                    :tableLoading="tableLoadingSon"
                    :isShowSummary="isShowSummarySon"
                    :summaryMethod="summarizeMainTable1"
                    height="calc(100vh - 18.5em)"
                    @pagination="sonPaginationSearch">
            <template #payableAmountSlot="{ row }">
              <el-text type="danger">
                {{ parseFloat(row.payableAmount).toFixed(2) }}
@@ -102,191 +90,205 @@
</template>
<script setup>
import { ref, toRefs } from "vue";
import { Search } from "@element-plus/icons-vue";
import {
  paymentLedgerList,
  paymentRecordList,
} from "@/api/procurementManagement/paymentLedger.js";
import Pagination from "../../../components/PIMTable/Pagination.vue";
  import { ref, toRefs } from "vue";
  import { Search } from "@element-plus/icons-vue";
  import {
    paymentLedgerList,
    paymentRecordList,
  } from "@/api/procurementManagement/paymentLedger.js";
  import Pagination from "../../../components/PIMTable/Pagination.vue";
const tableData = ref([]);
const tableLoading = ref(false);
const data = reactive({
  searchForm: {
    supplierNameOrContractNo: "",
  },
});
const page = reactive({
  current: 1,
  size: 100,
});
const sonPage = reactive({
  current: 1,
  size: 100,
});
const total = ref(0);
const sonTotal = ref(0);
const isShowSummary = ref(true);
const { searchForm } = toRefs(data);
const currentSupplierId = ref("");
const rowClick = (row) => {
  currentSupplierId.value = row.supplierId;
  getPaymenRecordtList(row.supplierId);
};
// å­æ¨¡å—
const tableColumnSon = ref([
  {
    label: "发生日期",
    prop: "paymentDate",
        width: 110,
  },
  {
    label: "采购合同号",
    prop: "purchaseContractNumber",
        width: 150,
  },
  {
    label: "合同金额(元)",
    prop: "invoiceAmount",
        width: 200,
    formatData: (params) => {
      return params ? parseFloat(params).toFixed(2) : 0;
  const tableData = ref([]);
  const tableLoading = ref(false);
  const data = reactive({
    searchForm: {
      supplierName: "",
    },
  },
  {
    label: "付款金额(元)",
    prop: "paymentAmount",
        width: 200,
    formatData: (params) => {
      return params ? parseFloat(params).toFixed(2) : 0;
    },
  },
  {
    label: "应付金额(元)",
    dataType: "slot",
        width: 200,
    prop: "payableAmount",
    slot: "payableAmountSlot",
  },
]);
const tableDataSon = ref([]);
const originalTableDataSon = ref([]);
const tableLoadingSon = ref(false);
const isShowSummarySon = ref(true);
const detailPageNum = ref(1);
const detailPageSize = ref(10);
const { proxy } = getCurrentInstance();
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
  return proxy.summarizeTable(
    param,
    ["invoiceAmount", "paymentAmount", "payableAmount"],
    {
      ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
    }
  );
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable1 = (param) => {
  let summarizeTable = proxy.summarizeTable(
    param,
    ["invoiceAmount", "paymentAmount"],
    {
      ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
    }
  );
  if (originalTableDataSon.value.length > 0) {
    summarizeTable[summarizeTable.length - 1] =
      originalTableDataSon.value[
        originalTableDataSon.value.length - 1
      ].payableAmount.toFixed(2);
  } else {
    summarizeTable[summarizeTable.length - 1] = 0.0;
  }
  return summarizeTable;
};
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
const paginationSearch = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  paymentLedgerList({
    ...searchForm.value,
    ...page,
  }).then((res) => {
    let result = res.data;
    tableLoading.value = false;
    tableData.value = result.records || [];
    total.value = result.total || 0;
    if (tableData.value.length > 0) {
      getPaymenRecordtList(tableData.value[0].supplierId);
      currentSupplierId.value = tableData.value[0].supplierId;
    }
  });
};
  const page = reactive({
    current: 1,
    size: 100,
  });
  const sonPage = reactive({
    current: 1,
    size: 10,
    total: 0,
    layout: "total, sizes, prev, pager, next, jumper",
  });
  const total = ref(0);
  const isShowSummary = ref(true);
  const { searchForm } = toRefs(data);
  const currentSupplierId = ref("");
  const rowClick = row => {
    currentSupplierId.value = row.supplierId;
    sonPage.current = 1;
    getPaymenRecordtList(row.supplierId);
  };
  // å­æ¨¡å—
  const tableColumnSon = ref([
    {
      label: "合同签订日期",
      prop: "executionDate",
      width: 110,
    },
    {
      label: "采购合同号",
      prop: "purchaseContractNumber",
      width: 150,
    },
    {
      label: "合同金额(元)",
      prop: "contractAmount",
      width: 200,
      formatData: params => {
        return params ? parseFloat(params).toFixed(2) : 0;
      },
    },
    {
      label: "付款金额(元)",
      prop: "paymentAmount",
      width: 200,
      formatData: params => {
        return params ? parseFloat(params).toFixed(2) : 0;
      },
    },
    {
      label: "应付金额(元)",
      dataType: "slot",
      width: 200,
      prop: "payableAmount",
      slot: "payableAmountSlot",
    },
  ]);
  const tableDataSon = ref([]);
  const originalTableDataSon = ref([]);
  const tableLoadingSon = ref(false);
  const isShowSummarySon = ref(true);
  const { proxy } = getCurrentInstance();
const getPaymenRecordtList = (supplierId) => {
  tableLoadingSon.value = true;
  paymentRecordList({supplierId: supplierId})
    .then((res) => {
      tableLoadingSon.value = false;
      tableDataSon.value = res.data;
      handlePagination({ page: 1, limit: sonPage.size });
      sonTotal.value = res.data.length;
    })
    .catch((e) => {
      tableLoadingSon.value = false;
  // ä¸»è¡¨åˆè®¡æ–¹æ³•
  const summarizeMainTable = param => {
    return proxy.summarizeTable(
      param,
      ["contractAmounts", "paymentAmount", "payableAmount"],
      {
        ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      }
    );
  };
  // å­è¡¨åˆè®¡æ–¹æ³•
  const summarizeMainTable1 = param => {
    let summarizeTable = proxy.summarizeTable(
      param,
      ["contractAmount", "invoiceAmount", "paymentAmount"],
      {
        ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      }
    );
    if (originalTableDataSon.value.length > 0) {
      summarizeTable[summarizeTable.length - 1] =
        originalTableDataSon.value[
          originalTableDataSon.value.length - 1
        ].payableAmount.toFixed(2);
    } else {
      summarizeTable[summarizeTable.length - 1] = 0.0;
    }
    return summarizeTable;
  };
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  const paginationSearch = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    paymentLedgerList({
      ...searchForm.value,
      ...page,
    }).then(res => {
      let result = res.data;
      tableLoading.value = false;
      tableData.value = result.records || [];
      total.value = result.total || 0;
      if (tableData.value.length > 0) {
        currentSupplierId.value = tableData.value[0].supplierId;
        sonPage.current = 1;
        getPaymenRecordtList(tableData.value[0].supplierId);
      }
    });
};
const handlePagination = ({ page, limit }) => {
  sonPage.current = page;
  sonPage.size = limit;
  };
  const start = (page - 1) * limit;
  const end = start + limit;
  const getPaymenRecordtList = supplierId => {
    tableLoadingSon.value = true;
    paymentRecordList({
      supplierId: supplierId,
      current: sonPage.current,
      size: sonPage.size,
    })
      .then(res => {
        tableLoadingSon.value = false;
        let result = res.data;
        if (Array.isArray(result)) {
          tableDataSon.value = result;
          sonPage.total = result.length;
          handlePagination({ page: sonPage.current, limit: sonPage.size });
        } else {
          originalTableDataSon.value = result.records || [];
          sonPage.total = result.total || 0;
        }
      })
      .catch(e => {
        tableLoadingSon.value = false;
      });
  };
  const handlePagination = ({ page, limit }) => {
    console.log(page, limit);
    sonPage.current = page;
    sonPage.size = limit;
  originalTableDataSon.value = tableDataSon.value.slice(start, end);
};
    const start = (page - 1) * limit;
    const end = start + limit;
const sonPaginationSearch = (pagination) => {
  // æŽ¥æ”¶åˆ†é¡µå™¨å‚æ•° { page, limit }
  handlePagination(pagination);
};
const formattedNumber = (row, column, cellValue) => {
  if (column.property !== "supplierName") {
    return parseFloat(cellValue).toFixed(2);
  } else {
    return cellValue;
  }
};
getList();
    originalTableDataSon.value = tableDataSon.value.slice(start, end);
  };
  const sonPaginationSearch = pagination => {
    // æŽ¥æ”¶åˆ†é¡µå™¨å‚æ•° { page, limit }
    sonPage.current = pagination.page;
    sonPage.size = pagination.limit;
    getPaymenRecordtList(currentSupplierId.value);
  };
  const formattedNumber = (row, column, cellValue) => {
    if (column.property !== "supplierName") {
      return parseFloat(cellValue).toFixed(2);
    } else {
      return cellValue;
    }
  };
  getList();
</script>
<style scoped lang="scss">
.el-pagination {
  width: 100%;
  height: 55px;
  display: flex;
  justify-content: flex-end;
  float: right;
  flex-direction: row;
  align-items: center;
  background: #fff;
  margin: -20px 0 0 0;
  padding: 0 20px;
}
.pagination-container {
  margin-top: 0;
}
  .el-pagination {
    width: 100%;
    height: 55px;
    display: flex;
    justify-content: flex-end;
    float: right;
    flex-direction: row;
    align-items: center;
    background: #fff;
    margin: -20px 0 0 0;
    padding: 0 20px;
  }
  .pagination-container {
    margin-top: 0;
  }
</style>
src/views/procurementManagement/procurementInvoiceLedger/Modal/EditModal.vue
ÎļþÒÑɾ³ý
src/views/procurementManagement/procurementInvoiceLedger/Modal/UploadModal.vue
ÎļþÒÑɾ³ý
src/views/procurementManagement/procurementInvoiceLedger/index.vue
ÎļþÒÑɾ³ý
src/views/productionManagement/productionOrder/index.vue
@@ -676,7 +676,6 @@
            try {
              const workOrderRes = await productWorkOrderPage({
                npsNo: item.npsNo,
                isProduction: 1,
                size: 100,
              });
              const workOrders = workOrderRes.data.records || [];
src/views/safeProduction/safetyTrainingAssessment/detail.vue
@@ -111,8 +111,6 @@
<script setup>
  import { onMounted, ref } from "vue";
  import { invoiceLedgerSalesAccount } from "@/api/salesManagement/invoiceLedger.js";
  import { customerInteractions } from "@/api/salesManagement/receiptPayment.js";
  import Pagination from "@/components/PIMTable/Pagination.vue";
  import {
    safeTrainingDetailListPage,
src/views/salesManagement/invoiceLedger/index.vue
ÎļþÒÑɾ³ý
src/views/salesManagement/invoiceRegistration/index.vue
ÎļþÒÑɾ³ý
src/views/salesManagement/receiptPayment/index.vue
ÎļþÒÑɾ³ý
src/views/salesManagement/receiptPaymentHistory/index.vue
ÎļþÒÑɾ³ý
src/views/salesManagement/receiptPaymentLedger/index.vue
@@ -1,268 +1,252 @@
<template>
  <div class="app-container">
    <div class="search_form" style="margin-bottom: 20px;">
    <div class="search_form"
         style="margin-bottom: 20px;">
      <div>
        <span class="search_title">客户名称:</span>
        <el-input
          v-model="searchForm.searchText"
          style="width: 240px"
          placeholder="输入客户名称搜索"
          @change="handleQuery"
          clearable
          prefix-icon="Search"
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
          >搜索</el-button
        >
        <el-input v-model="searchForm.searchText"
                  style="width: 240px"
                  placeholder="输入客户名称搜索"
                  @change="handleQuery"
                  clearable
                  prefix-icon="Search" />
        <el-button type="primary"
                   @click="handleQuery"
                   style="margin-left: 10px">搜索</el-button>
      </div>
    </div>
    <div style="display: flex">
      <div class="table_list">
        <el-table
          :data="tableData"
          border
          v-loading="tableLoading"
          :row-key="(row) => row.id"
          show-summary
          :summary-method="summarizeMainTable"
          @row-click="rowClickMethod"
          height="calc(100vh - 18.5em)"
        >
          <el-table-column
            align="center"
            label="序号"
            type="index"
            width="60"
          />
          <el-table-column
            label="客户名称"
            prop="customerName"
            show-overflow-tooltip
                        width="200"
          />
          <el-table-column
            label="合同金额(元)"
            prop="invoiceTotal"
            show-overflow-tooltip
            :formatter="formattedNumber"
                        width="200"
          />
          <el-table-column
            label="回款金额(元)"
            prop="receiptPaymentAmount"
            show-overflow-tooltip
            :formatter="formattedNumber"
                        width="200"
          />
          <el-table-column
            label="应收金额(元)"
            prop="unReceiptPaymentAmount"
            show-overflow-tooltip
                        width="200"
          >
            <template #default="{ row, column }">
              <el-text type="danger">
                {{ formattedNumber(row, column, row.unReceiptPaymentAmount) }}
              </el-text>
            </template>
          </el-table-column>
        </el-table>
        <pagination
          v-show="total > 0"
          :total="total"
          layout="total, sizes, prev, pager, next, jumper"
          :page="page.current"
          :limit="page.size"
          @pagination="paginationChange"
        />
      </div>
      <div class="table_list">
        <el-table
          :data="receiptRecord"
          border
          :row-key="(row) => row.id"
          show-summary
          :summary-method="summarizeMainTable1"
          height="calc(100vh - 18.5em)"
        >
          <el-table-column
            align="center"
            label="序号"
            type="index"
            width="60"
          />
          <el-table-column
            label="发生日期"
            prop="receiptPaymentDate"
            show-overflow-tooltip
                        width="110"
          />
          <el-table-column
            label="销售合同号"
            prop="salesContractNo"
            show-overflow-tooltip
                        width="200"
          />
          <el-table-column
            label="合同金额(元)"
            prop="invoiceTotal"
            show-overflow-tooltip
            :formatter="formattedNumber"
                        width="200"
          />
          <el-table-column
            label="回款金额(元)"
            prop="receiptPaymentAmount"
            show-overflow-tooltip
            :formatter="formattedNumber"
                        width="200"
          />
          <el-table-column
            label="应收金额(元)"
            prop="unReceiptPaymentAmount"
            show-overflow-tooltip
                        width="200"
          >
            <template #default="{ row, column }">
              <el-text type="danger">
                {{ formattedNumber(row, column, row.unReceiptPaymentAmount) }}
              </el-text>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </div>
    <el-row :gutter="20">
      <el-col :span="12">
        <div class="table_list"
             style="width: 100%">
          <el-table :data="tableData"
                    border
                    v-loading="tableLoading"
                    :row-key="(row) => row.customerId"
                    show-summary
                    :summary-method="summarizeMainTable"
                    @row-click="rowClickMethod"
                    highlight-current-row
                    height="calc(100vh - 18.5em)">
            <el-table-column align="center"
                             label="序号"
                             type="index"
                             width="60" />
            <el-table-column label="客户名称"
                             prop="customerName"
                             show-overflow-tooltip
                             width="200" />
            <el-table-column label="合同金额(元)"
                             prop="invoiceTotal"
                             show-overflow-tooltip
                             :formatter="formattedNumber"
                             width="200" />
            <el-table-column label="回款金额(元)"
                             prop="receiptPaymentAmount"
                             show-overflow-tooltip
                             :formatter="formattedNumber"
                             width="200" />
            <el-table-column label="应收金额(元)"
                             prop="unReceiptPaymentAmount"
                             show-overflow-tooltip
                             width="200">
              <template #default="{ row, column }">
                <el-text type="danger">
                  {{ formattedNumber(row, column, row.unReceiptPaymentAmount) }}
                </el-text>
              </template>
            </el-table-column>
          </el-table>
          <pagination v-show="total > 0"
                      :total="total"
                      layout="total, sizes, prev, pager, next, jumper"
                      :page="page.current"
                      :limit="page.size"
                      @pagination="paginationChange" />
        </div>
      </el-col>
      <el-col :span="12">
        <div class="table_list"
             style="width: 100%">
          <el-table :data="receiptRecord"
                    border
                    :row-key="(row) => row.id"
                    show-summary
                    :summary-method="summarizeMainTable1"
                    height="calc(100vh - 18.5em)">
            <el-table-column align="center"
                             label="序号"
                             type="index"
                             width="60" />
            <el-table-column label="合同签订日期"
                             prop="executionDate"
                             show-overflow-tooltip
                             width="110" />
            <el-table-column label="销售合同号"
                             prop="salesContractNo"
                             show-overflow-tooltip
                             width="200" />
            <el-table-column label="合同金额(元)"
                             prop="contractAmount"
                             show-overflow-tooltip
                             :formatter="formattedNumber"
                             width="200" />
            <el-table-column label="回款金额(元)"
                             prop="receiptPaymentAmount"
                             show-overflow-tooltip
                             :formatter="formattedNumber"
                             width="200" />
            <el-table-column label="应收金额(元)"
                             prop="receiptableAmount"
                             show-overflow-tooltip
                             width="200">
              <template #default="{ row, column }">
                <el-text type="danger">
                  {{ formattedNumber(row, column, row.receiptableAmount) }}
                </el-text>
              </template>
            </el-table-column>
          </el-table>
          <pagination v-show="recordTotal > 0"
                      :total="recordTotal"
                      layout="total, sizes, prev, pager, next, jumper"
                      :page="recordPage.current"
                      :limit="recordPage.size"
                      @pagination="recordPaginationChange" />
        </div>
      </el-col>
    </el-row>
  </div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import { invoiceLedgerSalesAccount } from "../../../api/salesManagement/invoiceLedger.js";
import { customerInteractions } from "../../../api/salesManagement/receiptPayment.js";
import Pagination from "../../../components/PIMTable/Pagination.vue";
const { proxy } = getCurrentInstance();
const tableData = ref([]);
const receiptRecord = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
});
const recordPage = reactive({
  current: 1,
  size: 100,
});
const total = ref(0);
const recordTotal = ref(0);
const data = reactive({
  searchForm: {
    searchText: "",
    invoiceDate: "",
  },
});
const customerId = ref("");
const { searchForm } = toRefs(data);
const originReceiptRecord = ref([]);
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  invoiceLedgerSalesAccount({ ...searchForm.value, ...page }).then((res) => {
    tableLoading.value = false;
    tableData.value = res.data.records;
    total.value = res.data.total;
    if (tableData.value.length > 0) {
      recordPage.current = 1;
      customerId.value = tableData.value[0].id;
      receiptPaymentList(customerId.value);
    }
  import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
  import {
    customewTransactions,
    customewTransactionsDetails,
  } from "@/api/salesManagement/indicatorStats.js";
  import Pagination from "../../../components/PIMTable/Pagination.vue";
  const { proxy } = getCurrentInstance();
  const tableData = ref([]);
  const receiptRecord = ref([]);
  const tableLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
  });
};
const formattedNumber = (row, column, cellValue) => {
  return parseFloat(cellValue).toFixed(2);
};
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
  return proxy.summarizeTable(
    param,
    ["invoiceTotal", "receiptPaymentAmount", "unReceiptPaymentAmount"],
    {
      ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
    }
  );
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable1 = (param) => {
  var summarizeTable = proxy.summarizeTable(
    param,
    ["invoiceAmount", "receiptAmount", "unReceiptAmount"],
    {
      ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
    }
  );
  // å–最后一行数据;
  if (receiptRecord.value?.length > 0) {
    const index = tableData.value.findIndex(
      (item) => item.id == customerId.value
    );
    summarizeTable[summarizeTable.length - 1] =
      tableData.value[index].unReceiptPaymentAmount.toFixed(2);
  } else {
    summarizeTable[summarizeTable.length - 1] = 0.0;
  }
  return summarizeTable;
};
const receiptPaymentList = (id) => {
  const param = {
    customerId: id,
  const recordPage = reactive({
    current: 1,
    size: 100,
  });
  const total = ref(0);
  const recordTotal = ref(0);
  const data = reactive({
    searchForm: {
      searchText: "",
      invoiceDate: "",
    },
  });
  const customerId = ref("");
  const { searchForm } = toRefs(data);
  const originReceiptRecord = ref([]);
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  console.log("param", param);
  customerInteractions(param).then((res) => {
    originReceiptRecord.value = res.data;
    handlePagination({ page: 1, limit: recordPage.size });
    recordTotal.value = res.data.length;
  const paginationChange = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    customewTransactions({ ...searchForm.value, ...page }).then(res => {
      tableLoading.value = false;
      tableData.value = res.data.records;
      total.value = res.data.total;
      if (tableData.value.length > 0) {
        recordPage.current = 1;
        customerId.value = tableData.value[0].customerId;
        receiptPaymentList(customerId.value);
      }
    });
  };
  const formattedNumber = (row, column, cellValue) => {
    return cellValue ? parseFloat(cellValue).toFixed(2) : "0.00";
  };
  // ä¸»è¡¨åˆè®¡æ–¹æ³•
  const summarizeMainTable = param => {
    return proxy.summarizeTable(
      param,
      ["invoiceTotal", "receiptPaymentAmount", "unReceiptPaymentAmount"],
      {
        ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      }
    );
  };
  // å­è¡¨åˆè®¡æ–¹æ³•
  const summarizeMainTable1 = param => {
    var summarizeTable = proxy.summarizeTable(
      param,
      ["contractAmount", "receiptPaymentAmount", "receiptableAmount"],
      {
        ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      }
    );
    return summarizeTable;
  };
  const receiptPaymentList = id => {
    const param = {
      customerId: id,
      current: recordPage.current,
      size: recordPage.size,
    };
    customewTransactionsDetails(param).then(res => {
      if (Array.isArray(res.data)) {
        originReceiptRecord.value = res.data;
        recordTotal.value = res.data.length;
        handlePagination({ page: 1, limit: recordPage.size });
      } else {
        receiptRecord.value = res.data.records;
        recordTotal.value = res.data.total;
      }
    });
  };
  // æ±‡æ¬¾è®°å½•列表分页
  const recordPaginationChange = pagination => {
    recordPage.current = pagination.page;
    recordPage.size = pagination.limit;
    receiptPaymentList(customerId.value);
  };
  const rowClickMethod = row => {
    customerId.value = row.customerId;
    receiptPaymentList(customerId.value);
  };
  const handlePagination = ({ page, limit }) => {
    recordPage.current = page;
    recordPage.size = limit;
    const start = (page - 1) * limit;
    const end = start + limit;
    receiptRecord.value = originReceiptRecord.value.slice(start, end);
  };
  onMounted(() => {
    getList();
  });
};
// æ±‡æ¬¾è®°å½•列表分页
const recordPaginationChange = (pagination) => {
  handlePagination(pagination);
};
const rowClickMethod = (row) => {
  customerId.value = row.id;
  receiptPaymentList(customerId.value);
};
const handlePagination = ({ page, limit }) => {
  recordPage.current = page;
  recordPage.size = limit;
  const start = (page - 1) * limit;
  const end = start + limit;
  receiptRecord.value = originReceiptRecord.value.slice(start, end);
};
onMounted(() => {
    getList();
});
</script>
<style scoped lang="scss">
.table_list {
  width: 50%;
}
  .table_list {
    width: 50%;
  }
</style>