gongchunyi
6 小时以前 36549e4b0d27c4d73fd29e483b9c39e1b16f3d43
feat: 销售和采购单据打印下载
已添加1个文件
已修改4个文件
777 ■■■■■ 文件已修改
src/api/procurementManagement/procurementLedger.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/salesManagement/salesLedger.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/documentPrint.js 331 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 378 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/procurementLedger.js
@@ -123,3 +123,10 @@
        params: id,
    });
}
// èŽ·å–é‡‡è´­å•æ‰“å°æ•°æ®
export function getPrintData(id) {
    return request({
        url: '/purchase/ledger/printData/' + id,
        method: 'get',
    });
}
src/api/salesManagement/salesLedger.js
@@ -117,3 +117,10 @@
        params: query,
    });
}
// èŽ·å–é”€å”®å•æ‰“å°æ•°æ®
export function getPrintData(id) {
    return request({
        url: '/sales/ledger/printData/' + id,
        method: 'get',
    });
}
src/utils/documentPrint.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,331 @@
import html2canvas from 'html2canvas'
function esc(s) {
  if (!s) return ''
  return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;')
}
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)
}
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("正在加载打印数据...");
  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) {
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("正在获取产品数据,请稍候...");
    proxy.$modal.loading("正在加载打印数据...");
    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 => {
    // æ£€æŸ¥æ˜¯å¦å¯ä»¥å‘è´§