From 36549e4b0d27c4d73fd29e483b9c39e1b16f3d43 Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期六, 18 七月 2026 10:49:32 +0800
Subject: [PATCH] feat: 销售和采购单据打印下载
---
src/views/procurementManagement/procurementLedger/index.vue | 54 ++++
src/api/salesManagement/salesLedger.js | 7
src/utils/documentPrint.js | 331 +++++++++++++++++++++++++
src/api/procurementManagement/procurementLedger.js | 7
src/views/salesManagement/salesLedger/index.vue | 378 ++--------------------------
5 files changed, 434 insertions(+), 343 deletions(-)
diff --git a/src/api/procurementManagement/procurementLedger.js b/src/api/procurementManagement/procurementLedger.js
index 5f9df05..3bdee66 100644
--- a/src/api/procurementManagement/procurementLedger.js
+++ b/src/api/procurementManagement/procurementLedger.js
@@ -122,4 +122,11 @@
method: "get",
params: id,
});
+}
+// 鑾峰彇閲囪喘鍗曟墦鍗版暟鎹�
+export function getPrintData(id) {
+ return request({
+ url: '/purchase/ledger/printData/' + id,
+ method: 'get',
+ });
}
\ No newline at end of file
diff --git a/src/api/salesManagement/salesLedger.js b/src/api/salesManagement/salesLedger.js
index 6548927..841a8a1 100644
--- a/src/api/salesManagement/salesLedger.js
+++ b/src/api/salesManagement/salesLedger.js
@@ -116,4 +116,11 @@
method: "get",
params: query,
});
+}
+// 鑾峰彇閿�鍞崟鎵撳嵃鏁版嵁
+export function getPrintData(id) {
+ return request({
+ url: '/sales/ledger/printData/' + id,
+ method: 'get',
+ });
}
\ No newline at end of file
diff --git a/src/utils/documentPrint.js b/src/utils/documentPrint.js
new file mode 100644
index 0000000..2db84f3
--- /dev/null
+++ b/src/utils/documentPrint.js
@@ -0,0 +1,331 @@
+import html2canvas from 'html2canvas'
+
+function esc(s) {
+ if (!s) return ''
+ return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
+}
+
+function fmtDate(v) {
+ if (!v) return ''
+ const d = new Date(v)
+ const y = d.getFullYear()
+ const m = String(d.getMonth() + 1).padStart(2, '0')
+ const day = String(d.getDate()).padStart(2, '0')
+ return y + '-' + m + '-' + day
+}
+
+function fmtMoney(v) {
+ if (v == null) return '0.00'
+ return Number(v).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
+}
+
+function digitUppercase(n) {
+ const digits = ['闆�', '澹�', '璐�', '鍙�', '鑲�', '浼�', '闄�', '鏌�', '鎹�', '鐜�']
+ const radices = ['', '鎷�', '浣�', '浠�', '涓�', '鎷�', '浣�', '浠�', '浜�']
+ let num = Math.abs(n)
+ let result = ''
+ const intPart = Math.floor(num)
+ const intStr = String(intPart)
+ const len = intStr.length
+ if (intPart === 0) {
+ result = '闆�'
+ } else {
+ let zeroFlag = false
+ for (let i = 0; i < len; i++) {
+ const d = parseInt(intStr[i])
+ const pos = len - 1 - i
+ if (d === 0) {
+ zeroFlag = true
+ if (pos % 4 === 0) {
+ result += radices[pos]
+ zeroFlag = false
+ }
+ } else {
+ if (zeroFlag) {
+ result += '闆�'
+ zeroFlag = false
+ }
+ result += digits[d] + radices[pos]
+ }
+ }
+ if (result.endsWith('闆�')) result = result.slice(0, -1)
+ }
+ result += '鍏�'
+ const decPart = Math.round((num - intPart) * 100)
+ if (decPart === 0) {
+ result += '鏁�'
+ } else {
+ const jiao = Math.floor(decPart / 10)
+ const fen = decPart % 10
+ if (jiao > 0) result += digits[jiao] + '瑙�'
+ if (fen > 0) result += digits[fen] + '鍒�'
+ }
+ return result
+}
+
+function coreCss() {
+ return `*{margin:0;padding:0;box-sizing:border-box}body{font-family:"Microsoft YaHei","SimSun",sans-serif;padding:30px 36px;color:#333;background:#fff}.title{text-align:center;font-size:18px;font-weight:bold;letter-spacing:6px;margin-bottom:4px}.sub{text-align:center;font-size:12px;color:#999;margin-bottom:18px}.info-top{display:flex;justify-content:space-between;font-size:13px;margin-bottom:14px;padding-bottom:10px;border-bottom:1px dashed #ccc}.info-top .left,.info-top .right{line-height:2}.info-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px 36px;font-size:13px;margin-bottom:14px;padding-bottom:8px;border-bottom:1px solid #ccc}.info-grid .l{color:#666}.info-grid .v{font-weight:500}table{width:100%;border-collapse:collapse;font-size:13px;margin-bottom:14px}th,td{border:1px solid #333;padding:5px 6px;text-align:center}th{background:#eee;font-weight:500}td.r{text-align:right}.summary{text-align:right;font-size:13px;margin-bottom:16px;line-height:2.2}.summary .cn{font-weight:bold;color:#cf1322}`
+}
+
+function productRows(products) {
+ if (!products || !products.length) return ''
+ let html = ''
+ products.forEach((p, i) => {
+ const name = p.productCategory || ''
+ const spec = p.specificationModel || ''
+ html += '<tr>'
+ html += '<td>' + (i + 1) + '</td>'
+ html += '<td style="text-align:left">' + esc(name)
+ if (spec) html += '<br><span style="color:#888;font-size:12px">' + esc(spec) + '</span>'
+ html += '</td>'
+ html += '<td>' + esc(p.unit || '') + '</td>'
+ html += '<td class="r">' + (p.quantity != null ? Number(p.quantity).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : '') + '</td>'
+ html += '<td class="r">' + fmtMoney(p.taxInclusiveUnitPrice) + '</td>'
+ html += '<td class="r">' + fmtMoney(p.taxInclusiveTotalPrice) + '</td>'
+ html += '<td>' + (p.taxRate != null ? p.taxRate + '%' : '') + '</td>'
+ html += '<td>' + esc(p.invoiceType || '') + '</td>'
+ html += '</tr>'
+ })
+ return html
+}
+
+function calcTotals(products) {
+ let total = 0, totalNoTax = 0
+ if (products) {
+ products.forEach(p => {
+ if (p.taxInclusiveTotalPrice != null) total += Number(p.taxInclusiveTotalPrice)
+ if (p.taxExclusiveTotalPrice != null) totalNoTax += Number(p.taxExclusiveTotalPrice)
+ })
+ }
+ return { total, totalNoTax }
+}
+
+function buildSalesHtml(companyName, ledger, forPrint) {
+ const products = ledger.productData || []
+ const { total, totalNoTax } = calcTotals(products)
+ const toolbar = forPrint ? '<div class="toolbar"><button onclick="window.print()">鎵撳嵃 / 鍙﹀瓨涓� PDF</button></div>' : ''
+ const printCss = forPrint ? '@media print{body{padding:15px 24px}.toolbar{display:none}}' : ''
+ const printScript = forPrint ? '<script>window.onload=function(){setTimeout(function(){window.print();},300);};<\/script>' : ''
+
+ return `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8">
+<title>閿�鍞崟 - ${esc(ledger.salesContractNo)} - ${esc(companyName)}</title>
+<style>.toolbar{text-align:center;margin-bottom:16px}.toolbar button{padding:6px 20px;font-size:14px;cursor:pointer;background:#1890ff;color:#fff;border:none;border-radius:4px}.toolbar button:hover{background:#40a9ff}${coreCss()}${printCss}</style></head><body>
+${toolbar}
+<div class="title">${esc(companyName)}</div>
+<div class="sub">閿�鍞悎鍚� / 閿�鍞崟</div>
+<div class="info-top">
+ <div class="left"><div><strong>閿�鏂瑰悕绉帮細</strong>${esc(companyName)}</div></div>
+ <div class="right"><div><strong>鍚堝悓缂栧彿锛�</strong>${esc(ledger.salesContractNo)}</div><div><strong>绛捐鏃ユ湡锛�</strong>${fmtDate(ledger.executionDate)}</div></div>
+</div>
+<div class="info-grid">
+ <div><span class="l">瀹㈡埛鍚嶇О锛�</span><span class="v">${esc(ledger.customerName)}</span></div>
+ <div><span class="l">椤圭洰鍚嶇О锛�</span><span class="v">${esc(ledger.projectName)}</span></div>
+ <div><span class="l">涓氬姟鍛橈細</span><span class="v">${esc(ledger.salesman)}</span></div>
+ <div><span class="l">浠樻鏂瑰紡锛�</span><span class="v">${esc(ledger.paymentMethod)}</span></div>
+ <div><span class="l">褰曞叆鏃ユ湡锛�</span><span class="v">${fmtDate(ledger.entryDate)}</span></div>
+ <div><span class="l">浜よ揣鏃ユ湡锛�</span><span class="v">${fmtDate(ledger.deliveryDate)}</span></div>
+ ${ledger.remarks ? `<div style="grid-column:1/-1"><span class="l">澶囨敞锛�</span><span class="v">${esc(ledger.remarks)}</span></div>` : ''}
+</div>
+<table><thead><tr><th>搴忓彿</th><th>浜у搧鍚嶇О / 瑙勬牸鍨嬪彿</th><th>鍗曚綅</th><th>鏁伴噺</th><th>鍚◣鍗曚环</th><th>鍚◣鎬讳环</th><th>绋庣巼</th><th>鍙戠エ绫诲瀷</th></tr></thead><tbody>
+${productRows(products)}
+</tbody></table>
+<div class="summary">
+ <div>涓嶅惈绋庡悎璁★細<strong>${fmtMoney(totalNoTax)}</strong></div>
+ <div>鍚◣鍚堣锛堝ぇ鍐欙級锛�<span class="cn">${digitUppercase(total)}</span></div>
+ <div>鍚◣鍚堣锛堝皬鍐欙級锛�<strong>楼${fmtMoney(total)}</strong></div>
+</div>
+${printScript}
+</body></html>`
+}
+
+function buildPurchaseHtml(companyName, ledger, products, forPrint) {
+ const { total, totalNoTax } = calcTotals(products)
+ const contractAmount = ledger.contractAmount != null ? Number(ledger.contractAmount) : total
+ const toolbar = forPrint ? '<div class="toolbar"><button onclick="window.print()">鎵撳嵃 / 鍙﹀瓨涓� PDF</button></div>' : ''
+ const printCss = forPrint ? '@media print{body{padding:15px 24px}.toolbar{display:none}}' : ''
+ const printScript = forPrint ? '<script>window.onload=function(){setTimeout(function(){window.print();},300);};<\/script>' : ''
+
+ return `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8">
+<title>閲囪喘鍗� - ${esc(ledger.purchaseContractNumber)} - ${esc(companyName)}</title>
+<style>.toolbar{text-align:center;margin-bottom:16px}.toolbar button{padding:6px 20px;font-size:14px;cursor:pointer;background:#1890ff;color:#fff;border:none;border-radius:4px}.toolbar button:hover{background:#40a9ff}${coreCss()}${printCss}</style></head><body>
+${toolbar}
+<div class="title">${esc(companyName)}</div>
+<div class="info-top">
+ <div class="left"><div><strong>璐柟鍚嶇О锛�</strong>${esc(companyName)}</div></div>
+ <div class="right"><div><strong>鍚堝悓缂栧彿锛�</strong>${esc(ledger.purchaseContractNumber)}</div><div><strong>绛捐鏃ユ湡锛�</strong>${fmtDate(ledger.executionDate)}</div></div>
+</div>
+<div class="info-grid">
+ <div><span class="l">渚涘簲鍟嗗悕绉帮細</span><span class="v">${esc(ledger.supplierName)}</span></div>
+ <div><span class="l">椤圭洰鍚嶇О锛�</span><span class="v">${esc(ledger.projectName)}</span></div>
+ <div><span class="l">褰曞叆浜猴細</span><span class="v">${esc(ledger.recorderName)}</span></div>
+ <div><span class="l">浠樻鏂瑰紡锛�</span><span class="v">${esc(ledger.paymentMethod)}</span></div>
+ <div><span class="l">褰曞叆鏃ユ湡锛�</span><span class="v">${fmtDate(ledger.entryDate)}</span></div>
+ ${ledger.salesContractNo ? `<div><span class="l">鍏宠仈閿�鍞悎鍚屽彿锛�</span><span class="v">${esc(ledger.salesContractNo)}</span></div>` : ''}
+ ${ledger.remarks ? `<div style="grid-column:1/-1"><span class="l">澶囨敞锛�</span><span class="v">${esc(ledger.remarks)}</span></div>` : ''}
+</div>
+<table><thead><tr><th>搴忓彿</th><th>浜у搧鍚嶇О / 瑙勬牸鍨嬪彿</th><th>鍗曚綅</th><th>鏁伴噺</th><th>鍚◣鍗曚环</th><th>鍚◣鎬讳环</th><th>绋庣巼</th><th>鍙戠エ绫诲瀷</th></tr></thead><tbody>
+${productRows(products)}
+</tbody></table>
+<div class="summary">
+ <div>涓嶅惈绋庡悎璁★細<strong>${fmtMoney(totalNoTax)}</strong></div>
+ <div>鍚◣鍚堣锛堝ぇ鍐欙級锛�<span class="cn">${digitUppercase(total)}</span></div>
+ <div>鍚◣鍚堣锛堝皬鍐欙級锛�<strong>楼${fmtMoney(total)}</strong></div>
+ <div>鍚堝悓閲戦锛堝ぇ鍐欙級锛�<span class="cn">${digitUppercase(contractAmount)}</span></div>
+ <div>鍚堝悓閲戦锛堝皬鍐欙級锛�<strong>楼${fmtMoney(contractAmount)}</strong></div>
+</div>
+${printScript}
+</body></html>`
+}
+
+export function buildSalesDocument(companyName, ledger) {
+ return buildSalesHtml(companyName, ledger, false)
+}
+
+export function buildPurchaseDocument(companyName, ledger, products) {
+ return buildPurchaseHtml(companyName, ledger, products, false)
+}
+
+function buildSalesPrintHtml(companyName, ledger) {
+ return buildSalesHtml(companyName, ledger, true)
+}
+
+function buildPurchasePrintHtml(companyName, ledger, products) {
+ return buildPurchaseHtml(companyName, ledger, products, true)
+}
+
+export function openPrintWindow(html) {
+ const w = window.open('', '_blank', 'width=900,height=700')
+ if (!w) return
+ w.document.write(html)
+ w.document.close()
+}
+
+export async function downloadAsJpg(html, filename) {
+ const iframe = document.createElement('iframe')
+ iframe.style.position = 'fixed'
+ iframe.style.left = '0'
+ iframe.style.top = '0'
+ iframe.style.width = '900px'
+ iframe.style.height = '600px'
+ iframe.style.opacity = '0'
+ iframe.style.pointerEvents = 'none'
+ iframe.style.zIndex = '99999'
+ document.body.appendChild(iframe)
+
+ return new Promise((resolve, reject) => {
+ const iframeDoc = iframe.contentDocument || iframe.contentWindow.document
+ iframeDoc.open()
+ iframeDoc.write(html)
+ iframeDoc.close()
+
+ const doCapture = async () => {
+ try {
+ const body = iframeDoc.body
+ iframe.style.height = Math.max(body.scrollHeight, 600) + 'px'
+ await new Promise(r => setTimeout(r, 100))
+ const canvas = await html2canvas(body, {
+ scale: 2,
+ useCORS: true,
+ backgroundColor: '#ffffff',
+ windowWidth: 900
+ })
+ document.body.removeChild(iframe)
+ canvas.toBlob(blob => {
+ const url = URL.createObjectURL(blob)
+ const a = document.createElement('a')
+ a.href = url
+ a.download = filename
+ a.click()
+ URL.revokeObjectURL(url)
+ resolve()
+ }, 'image/jpeg', 0.95)
+ } catch (e) {
+ document.body.removeChild(iframe)
+ reject(e)
+ }
+ }
+
+ iframe.onload = () => setTimeout(doCapture, 300)
+ setTimeout(doCapture, 1000)
+ })
+}
+
+export function downloadSalesJpg(companyName, ledger, filename) {
+ const html = buildSalesHtml(companyName, ledger, false)
+ return downloadAsJpg(html, filename)
+}
+
+export function downloadPurchaseJpg(companyName, ledger, products, filename) {
+ const html = buildPurchaseHtml(companyName, ledger, products, false)
+ return downloadAsJpg(html, filename)
+}
+
+export function printSales(companyName, ledger) {
+ const html = buildSalesPrintHtml(companyName, ledger)
+ openPrintWindow(html)
+}
+
+export function printPurchase(companyName, ledger, products) {
+ const html = buildPurchasePrintHtml(companyName, ledger, products)
+ openPrintWindow(html)
+}
+
+export function printSalesMulti(companyName, orders) {
+ let bodies = ''
+ orders.forEach((ledger, i) => {
+ const html = buildSalesHtml(companyName, ledger, true)
+ const bodyMatch = html.match(/<body>([\s\S]*)<\/body>/)
+ if (bodyMatch) {
+ const body = bodyMatch[1].replace(/<script>[\s\S]*?<\/script>/g, '')
+ bodies += '<div class="print-page' + (i < orders.length - 1 ? ' page-break' : '') + '">' + body + '</div>'
+ }
+ })
+ const combined = `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8">
+<title>閿�鍞崟 - ${esc(companyName)}</title>
+<style>
+*{margin:0;padding:0;box-sizing:border-box}
+body{font-family:"Microsoft YaHei","SimSun",sans-serif;padding:20px 36px;color:#333;background:#fff}
+.toolbar{text-align:center;margin-bottom:20px}
+.toolbar button{padding:6px 24px;font-size:14px;cursor:pointer;background:#1890ff;color:#fff;border:none;border-radius:4px}
+.toolbar button:hover{background:#40a9ff}
+${coreCss()}
+.print-page{page-break-inside:avoid}
+.page-break{page-break-after:always}
+@media print{body{padding:15px 24px}.toolbar{display:none}}
+</style></head><body>
+<div class="toolbar"><button onclick="window.print()">鎵撳嵃鍏ㄩ儴 (鍏�${orders.length}浠�)</button></div>
+${bodies}
+<script>window.onload=function(){setTimeout(function(){window.print();},300);};<\/script>
+</body></html>`
+ openPrintWindow(combined)
+}
+
+export function printPurchaseMulti(companyName, orders) {
+ let bodies = ''
+ orders.forEach((item, i) => {
+ const html = buildPurchaseHtml(companyName, item.ledger, item.products, true)
+ const bodyMatch = html.match(/<body>([\s\S]*)<\/body>/)
+ if (bodyMatch) {
+ const body = bodyMatch[1].replace(/<script>[\s\S]*?<\/script>/g, '')
+ bodies += '<div class="print-page' + (i < orders.length - 1 ? ' page-break' : '') + '">' + body + '</div>'
+ }
+ })
+ const combined = `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8">
+<title>閲囪喘鍗� - ${esc(companyName)}</title>
+<style>
+*{margin:0;padding:0;box-sizing:border-box}
+body{font-family:"Microsoft YaHei","SimSun",sans-serif;padding:20px 36px;color:#333;background:#fff}
+.toolbar{text-align:center;margin-bottom:20px}
+.toolbar button{padding:6px 24px;font-size:14px;cursor:pointer;background:#1890ff;color:#fff;border:none;border-radius:4px}
+.toolbar button:hover{background:#40a9ff}
+${coreCss()}
+.print-page{page-break-inside:avoid}
+.page-break{page-break-after:always}
+@media print{body{padding:15px 24px}.toolbar{display:none}}
+</style></head><body>
+<div class="toolbar"><button onclick="window.print()">鎵撳嵃鍏ㄩ儴 (鍏�${orders.length}浠�)</button></div>
+${bodies}
+<script>window.onload=function(){setTimeout(function(){window.print();},300);};<\/script>
+</body></html>`
+ openPrintWindow(combined)
+}
diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index 77c70a3..685a8aa 100644
--- a/src/views/procurementManagement/procurementLedger/index.vue
+++ b/src/views/procurementManagement/procurementLedger/index.vue
@@ -45,6 +45,7 @@
<el-button @click="handleOut">瀵煎嚭</el-button>
<el-button type="danger" plain @click="handleDelete">鍒犻櫎
</el-button>
+ <el-button type="warning" plain @click="handlePrint">鎵撳嵃鍗曟嵁</el-button>
</div>
<el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
:expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary :summary-method="summarizeMainTable"
@@ -100,14 +101,14 @@
<el-table-column label="褰曞叆浜�" prop="recorderName" width="120" show-overflow-tooltip />
<el-table-column label="褰曞叆鏃ユ湡" prop="entryDate" width="100" show-overflow-tooltip />
<el-table-column label="澶囨敞" prop="remarks" width="200" show-overflow-tooltip />
- <el-table-column fixed="right" label="鎿嶄綔" width="180" align="center">
+ <el-table-column fixed="right" label="鎿嶄綔" width="220" align="center">
<template #default="scope">
<el-button link type="primary" @click="openForm('detail', scope.row)">璇︽儏
</el-button>
<el-button link type="primary" @click="openForm('edit', scope.row)"
:disabled="scope.row.stockInStatus === '瀹屽叏鍏ュ簱'">缂栬緫
</el-button>
- <el-button link type="primary" @click="openFileDialog(scope.row)">闄勪欢</el-button>
+ <el-button link type="primary" @click="downloadOrder(scope.row.id)">涓嬭浇</el-button>
</template>
</el-table-column>
</el-table>
@@ -411,6 +412,7 @@
productList,
getPurchaseById,
getOptions,
+ getPrintData,
getPurchaseTemplateList,
delPurchaseTemplate,
} from "@/api/procurementManagement/procurementLedger.js";
@@ -446,6 +448,7 @@
import dayjs from "dayjs";
import FileUpload from "@/components/AttachmentUpload/file/index.vue";
import { tableAmountFormatter, formatDecimal, buildAmountSummaryFormat } from '@/utils/numberFormat';
+import { downloadPurchaseJpg, printPurchaseMulti } from "@/utils/documentPrint.js";
const userStore = useUserStore();
@@ -1436,6 +1439,31 @@
});
};
+// 鎵归噺鎵撳嵃閲囪喘鍗� - 澶氶�夊悎骞堕瑙�
+const handlePrint = async () => {
+ if (selectedRows.value.length === 0) {
+ proxy.$modal.msgWarning("璇烽�夋嫨瑕佹墦鍗扮殑鏁版嵁");
+ return;
+ }
+ proxy.$modal.loading("姝e湪鍔犺浇鎵撳嵃鏁版嵁...");
+ try {
+ const orders = [];
+ let companyName = '';
+ for (const row of selectedRows.value) {
+ const res = await getPrintData(row.id);
+ const data = res.data || res;
+ companyName = data.companyName || companyName;
+ orders.push({ ledger: data.ledger, products: data.products });
+ }
+ printPurchaseMulti(companyName, orders);
+ } catch (e) {
+ console.error('鎵撳嵃澶辫触:', e);
+ proxy.$modal.msgError("鎵撳嵃澶辫触");
+ } finally {
+ proxy.$modal.closeLoading();
+ }
+};
+
// 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD
function getCurrentDate() {
const today = new Date();
@@ -1589,6 +1617,28 @@
fileListDialogVisible.value = true;
};
+// 涓嬭浇閲囪喘鍗�
+const downloadOrder = async (id) => {
+ try {
+ const res = await getPrintData(id);
+ const data = res.data || res;
+ downloadPurchaseJpg(data.companyName, data.ledger, data.products, '閲囪喘鍗昣' + (data.ledger.purchaseContractNumber || id) + '.jpg');
+ } catch (e) {
+ console.error('涓嬭浇澶辫触:', e);
+ }
+};
+
+// 鎵撳嵃閲囪喘鍗�
+const printOrder = async (id) => {
+ try {
+ const res = await getPrintData(id);
+ const data = res.data || res;
+ printPurchase(data.companyName, data.ledger, data.products);
+ } catch (e) {
+ console.error('鎵撳嵃澶辫触:', e);
+ }
+};
+
// 鍒犻櫎妯℃澘
const handleDeleteTemplate = async item => {
if (!item.id) {
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index be5d297..1a0f0a5 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -249,7 +249,7 @@
</el-button>
<el-button link
type="primary"
- @click="openFileDialog(scope.row)">闄勪欢
+ @click="downloadOrder(scope.row.id)">涓嬭浇
</el-button>
</template>
</el-table-column>
@@ -1071,7 +1071,9 @@
delProduct,
delLedgerFile,
getProductInventory,
+ getPrintData,
} from "@/api/salesManagement/salesLedger.js";
+ import { downloadSalesJpg, printSalesMulti } from "@/utils/documentPrint.js";
import { getStockInventoryByModelId } from "@/api/inventoryManagement/stockInventory.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
@@ -2084,359 +2086,31 @@
});
};
- // 鎵撳嵃鍔熻兘
+ // 鎵撳嵃鍔熻兘 - 澶氶�夊悎骞堕瑙�
const handlePrint = async () => {
if (selectedRows.value.length === 0) {
proxy.$modal.msgWarning("璇烽�夋嫨瑕佹墦鍗扮殑鏁版嵁");
return;
}
-
- // 鏄剧ず鍔犺浇鐘舵��
- proxy.$modal.loading("姝e湪鑾峰彇浜у搧鏁版嵁锛岃绋嶅��...");
-
+ proxy.$modal.loading("姝e湪鍔犺浇鎵撳嵃鏁版嵁...");
try {
- // 涓烘瘡涓�変腑鐨勯攢鍞彴璐﹁褰曟煡璇㈠搴旂殑浜у搧鏁版嵁
- const printDataWithProducts = [];
-
+ const orders = [];
+ let companyName = '';
for (const row of selectedRows.value) {
- try {
- // 璋冪敤productList鎺ュ彛鏌ヨ浜у搧鏁版嵁
- const productRes = await productList({
- salesLedgerId: row.id,
- type: 1,
- });
-
- // 灏嗕骇鍝佹暟鎹暣鍚堝埌閿�鍞彴璐﹁褰曚腑
- const rowWithProducts = {
- ...row,
- products: productRes.data || [],
- };
-
- printDataWithProducts.push(rowWithProducts);
- } catch (error) {
- console.error(`鑾峰彇閿�鍞彴璐� ${row.id} 鐨勪骇鍝佹暟鎹け璐�:`, error);
- // 鍗充娇鏌愪釜璁板綍鐨勪骇鍝佹暟鎹幏鍙栧け璐ワ紝涔熻鍖呭惈璇ヨ褰�
- printDataWithProducts.push({
- ...row,
- products: [],
- });
- }
+ const res = await getPrintData(row.id);
+ const data = res.data || res;
+ companyName = data.companyName || companyName;
+ orders.push(data.ledger);
}
-
- printData.value = printDataWithProducts;
- console.log("鎵撳嵃鏁版嵁锛堝寘鍚骇鍝侊級:", printData.value);
- printPreviewVisible.value = true;
- } catch (error) {
- console.error("鑾峰彇浜у搧鏁版嵁澶辫触:", error);
- proxy.$modal.msgError("鑾峰彇浜у搧鏁版嵁澶辫触锛岃閲嶈瘯");
+ printSalesMulti(companyName, orders);
+ } catch (e) {
+ console.error('鎵撳嵃澶辫触:', e);
+ proxy.$modal.msgError("鎵撳嵃澶辫触");
} finally {
proxy.$modal.closeLoading();
}
};
- // 鎵ц鎵撳嵃
- const executePrint = () => {
- console.log("寮�濮嬫墽琛屾墦鍗帮紝鏁版嵁鏉℃暟:", printData.value.length);
- console.log("鎵撳嵃鏁版嵁:", printData.value);
- // 鍒涘缓涓�涓柊鐨勬墦鍗扮獥鍙�
- const printWindow = window.open("", "_blank", "width=800,height=600");
-
- // 鏋勫缓鎵撳嵃鍐呭
- let printContent = `
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>鎵撳嵃棰勮</title>
- <style>
- body {
- margin: 0;
- padding: 0;
- font-family: "SimSun", serif;
- background: white;
- }
- .print-page {
- width: 200mm;
- height: 75mm;
- padding: 10mm;
- padding-left: 20mm;
- background: white;
- box-sizing: border-box;
- page-break-after: always;
- page-break-inside: avoid;
- }
- .print-page:last-child {
- page-break-after: avoid;
- }
- .delivery-note {
- width: 100%;
- height: 100%;
- font-size: 12px;
- line-height: 1.2;
- display: flex;
- flex-direction: column;
- color: #000;
- }
- .header {
- text-align: center;
- margin-bottom: 8px;
- }
- .company-name {
- font-size: 18px;
- font-weight: bold;
- margin-bottom: 4px;
- }
- .document-title {
- font-size: 16px;
- font-weight: bold;
- }
- .info-section {
- margin-bottom: 8px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .info-row {
- line-height: 20px;
- }
- .label {
- font-weight: bold;
- width: 60px;
- font-size: 12px;
- }
- .value {
- margin-right: 20px;
- min-width: 80px;
- font-size: 12px;
- }
- .table-section {
- margin-bottom: 40px;
- // flex: 0.6;
- }
- .product-table {
- width: 100%;
- border-collapse: collapse;
- border: 1px solid #000;
- }
- .product-table th, .product-table td {
- border: 1px solid #000;
- padding: 6px;
- text-align: center;
- font-size: 12px;
- line-height: 1.4;
- }
- .product-table th {
- font-weight: bold;
- }
- .total-value {
- font-weight: bold;
- }
- .footer-section {
- margin-top: auto;
- }
- .footer-row {
- display: flex;
- margin-bottom: 3px;
- line-height: 22px;
- justify-content: space-between;
- }
- .footer-item {
- display: flex;
- margin-right: 20px;
- }
- .footer-item .label {
- font-weight: bold;
- width: 80px;
- font-size: 12px;
- }
- .footer-item .value {
- min-width: 80px;
- font-size: 12px;
- }
- .address-item .address-value {
- min-width: 200px;
- }
- @media print {
- body {
- margin: 0;
- padding: 0;
- }
- .print-page {
- margin: 0;
- padding: 10mm;
- /* padding-left: 20mm; */
- page-break-inside: avoid;
- page-break-after: always;
- }
- .print-page:last-child {
- page-break-after: avoid;
- }
- }
- </style>
- </head>
- <body>
- `;
-
- // 涓烘瘡鏉℃暟鎹敓鎴愭墦鍗伴〉闈�
- printData.value.forEach((item, index) => {
- printContent += `
- <div class="print-page">
- <div class="delivery-note">
- <div class="header">
- <div class="document-title">闆跺敭鍙戣揣鍗�</div>
- </div>
-
- <div class="info-section">
- <div class="info-row">
- <div>
- <span class="label">鍙戣揣鏃ユ湡锛�</span>
- <span class="value">${formatDate(
- item.createTime
- )}</span>
- </div>
- <div>
- <span class="label">瀹㈡埛鍚嶇О锛�</span>
- <span class="value">${
- item.customerName
- }</span>
- </div>
- </div>
- <div class="info-row">
- <span class="label">鍗曞彿锛�</span>
- <span class="value">${
- item.salesContractNo ||
- ""
- }</span>
- </div>
- </div>
-
- <div class="table-section">
- <table class="product-table">
- <thead>
- <tr>
- <th>浜у搧鍚嶇О</th>
- <th>瑙勬牸鍨嬪彿</th>
- <th>鍗曚綅</th>
- <th>鍗曚环</th>
- <th>闆跺敭鏁伴噺</th>
- <th>闆跺敭閲戦</th>
- </tr>
- </thead>
- <tbody>
- ${
- item.products &&
- item
- .products
- .length >
- 0
- ? item.products
- .map(
- product => `
- <tr>
- <td>${
- product.productCategory ||
- ""
- }</td>
- <td>${
- product.specificationModel ||
- ""
- }</td>
- <td>${
- product.unit ||
- ""
- }</td>
- <td>${
- product.taxInclusiveUnitPrice ||
- "0"
- }</td>
- <td>${
- product.quantity ||
- "0"
- }</td>
- <td>${
- product.taxInclusiveTotalPrice ||
- "0"
- }</td>
- </tr>
- `
- )
- .join(
- ""
- )
- : '<tr><td colspan="6" style="text-align: center; color: #999;">鏆傛棤浜у搧鏁版嵁</td></tr>'
- }
- </tbody>
- <tfoot>
- <tr>
- <td class="label">鍚堣</td>
- <td class="total-value"></td>
- <td class="total-value"></td>
- <td class="total-value"></td>
- <td class="total-value">${getTotalQuantityForPrint(
- item.products
- )}</td>
- <td class="total-value">${getTotalAmountForPrint(
- item.products
- )}</td>
- </tr>
- </tfoot>
- </table>
- </div>
-
- <div class="footer-section">
- <div class="footer-row">
- <div class="footer-item">
- <span class="label">鏀惰揣鐢佃瘽锛�</span>
- <span class="value"></span>
- </div>
- <div class="footer-item">
- <span class="label">鏀惰揣浜猴細</span>
- <span class="value"></span>
- </div>
- <div class="footer-item address-item">
- <span class="label">鏀惰揣鍦板潃锛�</span>
- <span class="value address-value"></span>
- </div>
- </div>
- <div class="footer-row">
- <div class="footer-item">
- <span class="label">鎿嶄綔鍛橈細</span>
- <span class="value">${
- userStore.nickName ||
- "鎾曞紑鍓�"
- }</span>
- </div>
- <div class="footer-item">
- <span class="label">鎵撳嵃鏃ユ湡锛�</span>
- <span class="value">${formatDateTime(
- new Date()
- )}</span>
- </div>
- </div>
- </div>
- </div>
- </div>
- `;
- });
-
- printContent += `
- </body>
- </html>
- `;
-
- // 鍐欏叆鍐呭鍒版柊绐楀彛
- printWindow.document.write(printContent);
- printWindow.document.close();
-
- // 绛夊緟鍐呭鍔犺浇瀹屾垚鍚庢墦鍗�
- printWindow.onload = () => {
- setTimeout(() => {
- printWindow.print();
- printWindow.close();
- printPreviewVisible.value = false;
- }, 500);
- };
- };
// 鏍煎紡鍖栨棩鏈�
const formatDate = dateString => {
if (!dateString) return getCurrentDate();
@@ -2767,6 +2441,28 @@
fileDialogVisible.value = true;
};
+ // 涓嬭浇閿�鍞崟
+ const downloadOrder = async (id) => {
+ try {
+ const res = await getPrintData(id);
+ const data = res.data || res;
+ downloadSalesJpg(data.companyName, data.ledger, '閿�鍞崟_' + (data.ledger.salesContractNo || id) + '.jpg');
+ } catch (e) {
+ console.error('涓嬭浇澶辫触:', e);
+ }
+ };
+
+ // 鎵撳嵃閿�鍞崟
+ const printOrder = async (id) => {
+ try {
+ const res = await getPrintData(id);
+ const data = res.data || res;
+ printSales(data.companyName, data.ledger);
+ } catch (e) {
+ console.error('鎵撳嵃澶辫触:', e);
+ }
+ };
+
// 鎵撳紑鍙戣揣寮规
const openDeliveryForm = async row => {
// 妫�鏌ユ槸鍚﹀彲浠ュ彂璐�
--
Gitblit v1.9.3