From ac8777c3df1de48c4e69a60829c0238af99a12f3 Mon Sep 17 00:00:00 2001
From: zss <zss@example.com>
Date: 星期四, 23 七月 2026 09:46:19 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_pro_河南鹤壁' into dev_pro_河南鹤壁

---
 src/views/basicData/product/ProductSelectDialog.vue                        |   11 
 src/views/collaborativeApproval/rulesRegulationsManagement/index.vue       |    2 
 multiple/assets/favicon/TDDQfavicon.ico                                    |    0 
 multiple/config.json                                                       |   18 
 src/views/basicData/product/index.vue                                      |    2 
 src/utils/documentPrint.js                                                 |  331 ++++++++++
 src/api/procurementManagement/procurementLedger.js                         |    7 
 src/views/salesManagement/receiptPaymentLedger/index.vue                   |   27 
 src/views/salesManagement/salesLedger/index.vue                            |  598 +++++-------------
 src/views/qualityManagement/metricBinding/index.vue                        |  179 ++++-
 src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue |   35 
 src/views/inventoryManagement/dispatchLog/Record.vue                       |    9 
 multiple/assets/favicon/JHJMfavicon.ico                                    |    0 
 src/views/procurementManagement/procurementLedger/detail.vue               |    8 
 src/views/inventoryManagement/stockManagement/Record.vue                   |    4 
 src/views/inventoryManagement/receiptManagement/Record.vue                 |    9 
 src/utils/summarizeTable.js                                                |   21 
 src/views/basicData/supplierManage/components/HomeTab.vue                  |   43 
 src/views/qualityManagement/processInspection/components/formDia.vue       |   46 +
 src/views/salesManagement/deliveryLedger/index.vue                         |   23 
 multiple/assets/logo/JHJMLogo.png                                          |    0 
 src/views/equipmentManagement/spareParts/index.vue                         |   27 
 src/views/procurementManagement/procurementReport/index.vue                |    7 
 src/utils/numberFormat.js                                                  |   47 +
 src/views/qualityManagement/finalInspection/components/formDia.vue         |   47 +
 multiple/assets/logo/TDDQLogo.png                                          |    0 
 src/components/PIMTable/PIMTable.vue                                       |    5 
 src/views/salesManagement/salesQuotation/index.vue                         |   16 
 src/views/procurementManagement/paymentLedger/index.vue                    |   43 
 src/views/procurementManagement/procurementLedger/index.vue                |  199 ++++-
 src/api/salesManagement/salesLedger.js                                     |    7 
 src/views/reportAnalysis/reportManagement/index.vue                        |   54 
 src/views/qualityManagement/rawMaterialInspection/components/formDia.vue   |   38 +
 package.json                                                               |    1 
 src/views/basicData/supplierManage/components/BlacklistTab.vue             |   44 
 35 files changed, 1,234 insertions(+), 674 deletions(-)

diff --git a/multiple/assets/favicon/JHJMfavicon.ico b/multiple/assets/favicon/JHJMfavicon.ico
new file mode 100644
index 0000000..ed59d69
--- /dev/null
+++ b/multiple/assets/favicon/JHJMfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/TDDQfavicon.ico b/multiple/assets/favicon/TDDQfavicon.ico
new file mode 100644
index 0000000..78a6cf8
--- /dev/null
+++ b/multiple/assets/favicon/TDDQfavicon.ico
Binary files differ
diff --git a/multiple/assets/logo/JHJMLogo.png b/multiple/assets/logo/JHJMLogo.png
new file mode 100644
index 0000000..3b95956
--- /dev/null
+++ b/multiple/assets/logo/JHJMLogo.png
Binary files differ
diff --git a/multiple/assets/logo/TDDQLogo.png b/multiple/assets/logo/TDDQLogo.png
new file mode 100644
index 0000000..b34689f
--- /dev/null
+++ b/multiple/assets/logo/TDDQLogo.png
Binary files differ
diff --git a/multiple/config.json b/multiple/config.json
index 9cdff0b..94d369a 100644
--- a/multiple/config.json
+++ b/multiple/config.json
@@ -132,6 +132,24 @@
     "logo": "logo/XSWHLogo.png",
     "favicon": "favicon/XSWHfavicon.ico"
   },
+  "TDDQ": {
+    "env": {
+      "VITE_APP_TITLE": "娌冲崡鍚屽痉鐢靛櫒鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://localhost:9001",
+      "VITE_JAVA_API": "http://localhost:9002"
+    },
+    "logo": "logo/TDDQLogo.png",
+    "favicon": "favicon/TDDQfavicon.ico"
+  },
+  "JHJM": {
+    "env": {
+      "VITE_APP_TITLE": "楣ゅ甯傞敠姹囩簿瀵嗘満姊版湁闄愬叕鍙�",
+      "VITE_BASE_API": "http://103.189.141.42:9001",
+      "VITE_JAVA_API": "http://103.189.141.42:9002"
+    },
+    "logo": "logo/JHJMLogo.png",
+    "favicon": "favicon/JHJMfavicon.ico"
+  },
   "logo": "/src/assets/logo/logo.png",
   "favicon": "/public/favicon.ico"
 }
\ No newline at end of file
diff --git a/package.json b/package.json
index 11fa01d..4e2b8d8 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
     "element-plus": "2.7.7",
     "file-saver": "2.0.5",
     "fuse.js": "6.6.2",
+    "html2canvas": "^1.4.1",
     "js-beautify": "1.14.11",
     "js-cookie": "3.0.5",
     "jsencrypt": "3.3.2",
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/components/PIMTable/PIMTable.vue b/src/components/PIMTable/PIMTable.vue
index 631d956..aad24fa 100644
--- a/src/components/PIMTable/PIMTable.vue
+++ b/src/components/PIMTable/PIMTable.vue
@@ -8,6 +8,7 @@
             :highlight-current-row="highlightCurrentRow"
             :row-class-name="rowClassName"
             :row-style="rowStyle"
+            :cell-style="cellStyle"
             :row-key="rowKey"
             :style="tableStyle"
             tooltip-effect="dark"
@@ -293,6 +294,10 @@
       type: [Object, Function],
       default: () => ({}),
     },
+    cellStyle: {
+      type: [Object, Function],
+      default: () => ({}),
+    },
     tableData: {
       type: Array,
       default: () => [],
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, '&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)
+}
diff --git a/src/utils/numberFormat.js b/src/utils/numberFormat.js
new file mode 100644
index 0000000..50175f0
--- /dev/null
+++ b/src/utils/numberFormat.js
@@ -0,0 +1,47 @@
+/** 閲戦绫诲瓧娈甸粯璁ゆ渶澶у皬鏁颁綅鏁� */
+export const AMOUNT_DECIMAL_SCALE = 6
+
+/**
+ * 鏍煎紡鍖栨暟瀛楋細鏈�澶氫繚鐣� scale 浣嶅皬鏁帮紝骞跺幓闄ゆ湯灏惧浣欑殑 0
+ */
+export function formatDecimal(value, scale = AMOUNT_DECIMAL_SCALE, fallback = '0') {
+  if (value === null || value === undefined || value === '') {
+    return fallback
+  }
+  const num = Number(value)
+  if (Number.isNaN(num)) {
+    return fallback
+  }
+  return parseFloat(num.toFixed(scale)).toString()
+}
+
+/** 甯﹁揣甯佺鍙风殑閲戦灞曠ず */
+export function formatCurrency(value, scale = AMOUNT_DECIMAL_SCALE) {
+  return `楼${formatDecimal(value, scale)}`
+}
+
+/** el-table-column formatter */
+export function tableAmountFormatter(row, column, cellValue) {
+  return formatDecimal(cellValue)
+}
+
+/** 璁$畻鍚庝繚鐣欐寚瀹氫綅灏忔暟 */
+export function roundAmount(value, scale = AMOUNT_DECIMAL_SCALE) {
+  if (value === null || value === undefined || value === '') {
+    return 0
+  }
+  const num = Number(value)
+  if (Number.isNaN(num)) {
+    return 0
+  }
+  return parseFloat(num.toFixed(scale))
+}
+
+/** 鏋勫缓 summarizeTable 閲戦鍒楁牸寮忛厤缃� */
+export function buildAmountSummaryFormat(fields, extraFormat = {}) {
+  const format = { ...extraFormat }
+  fields.forEach(field => {
+    format[field] = { decimalPlaces: AMOUNT_DECIMAL_SCALE }
+  })
+  return format
+}
diff --git a/src/utils/summarizeTable.js b/src/utils/summarizeTable.js
index 1ad480d..3602523 100644
--- a/src/utils/summarizeTable.js
+++ b/src/utils/summarizeTable.js
@@ -1,3 +1,5 @@
+import { formatDecimal, AMOUNT_DECIMAL_SCALE } from './numberFormat'
+
 /**
  * 閫氱敤鐨勮〃鏍煎悎璁℃柟娉�
  * @param {Object} param - 鍖呭惈琛ㄦ牸鍒楅厤缃拰鏁版嵁婧愮殑瀵硅薄
@@ -26,28 +28,35 @@
           // 濡傛灉鎸囧畾浜嗕笉闇�瑕佷繚鐣欏皬鏁帮紝鍒欑洿鎺ヨ浆鎹负鏁存暟
           sums[index] = Math.round(sum).toString();
         } else {
-          // 榛樿淇濈暀涓や綅灏忔暟
-          sums[index] = parseFloat(sum).toFixed(
+          sums[index] = formatDecimal(
+            sum,
             specialFormat[prop]?.decimalPlaces ?? 2
           );
         }
       } else {
-        sums[index] = "";
+        sums[index] = '';
       }
     } else {
-      sums[index] = "";
+      sums[index] = '';
     }
   });
   return sums;
 };
+
 // 涓嶅惈绋庢�讳环璁$畻
 const calculateTaxExclusiveTotalPrice = (taxInclusiveTotalPrice, taxRate) => {
   const taxRateDecimal = taxRate / 100;
-  return (taxInclusiveTotalPrice / (1 + taxRateDecimal)).toFixed(2);
+  return formatDecimal(
+    Number(taxInclusiveTotalPrice) / (1 + taxRateDecimal),
+    AMOUNT_DECIMAL_SCALE
+  );
 };
 // 鍚◣鎬讳环璁$畻
 const calculateTaxIncludeTotalPrice = (taxInclusiveUnitPrice, quantity) => {
-  return (taxInclusiveUnitPrice * quantity).toFixed(2);
+  return formatDecimal(
+    Number(taxInclusiveUnitPrice) * Number(quantity),
+    AMOUNT_DECIMAL_SCALE
+  );
 };
 // 瀵煎嚭鍑芥暟渚涘叾浠栨枃浠朵娇鐢�
 export {
diff --git a/src/views/basicData/product/ProductSelectDialog.vue b/src/views/basicData/product/ProductSelectDialog.vue
index ad27baa..590f8ca 100644
--- a/src/views/basicData/product/ProductSelectDialog.vue
+++ b/src/views/basicData/product/ProductSelectDialog.vue
@@ -2,11 +2,11 @@
   <el-dialog v-model="visible" title="閫夋嫨浜у搧" width="900px" destroy-on-close :close-on-click-modal="false">
     <el-form :inline="true" :model="query" class="mb-2">
       <el-form-item label="浜у搧鍚嶇О">
-        <el-input v-model="query.productName" placeholder="杈撳叆浜у搧鍚嶇О" clearable @keyup.enter="onSearch" />
+        <el-input v-model="query.productName" placeholder="杈撳叆浜у搧鍚嶇О" clearable @keyup.enter="onSearch" style="width: 200px" />
       </el-form-item>
 
       <el-form-item label="浜у搧鍨嬪彿">
-        <el-input v-model="query.model" placeholder="杈撳叆浜у搧鍨嬪彿" clearable @keyup.enter="onSearch" />
+        <el-input v-model="query.model" placeholder="杈撳叆浜у搧鍨嬪彿" clearable @keyup.enter="onSearch" style="width: 200px" />
       </el-form-item>
 
       <el-form-item>
@@ -20,9 +20,10 @@
       @selection-change="handleSelectionChange" @select="handleSelect">
       <el-table-column type="selection" width="55" />
       <el-table-column type="index" label="搴忓彿" width="60" />
-      <el-table-column prop="productName" label="浜у搧鍚嶇О" min-width="160" />
-      <el-table-column prop="model" label="浜у搧鍨嬪彿" min-width="200" />
-      <el-table-column prop="unit" label="鍗曚綅" min-width="160" />
+      <el-table-column prop="topProductName" label="浜у搧澶х被" min-width="120" />
+      <el-table-column prop="productName" label="浜у搧鍚嶇О" min-width="140" />
+      <el-table-column prop="model" label="浜у搧鍨嬪彿" min-width="180" />
+      <el-table-column prop="unit" label="鍗曚綅" min-width="100" />
     </el-table>
 
     <div class="mt-3 flex justify-end">
diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index cdc8265..15f817d 100644
--- a/src/views/basicData/product/index.vue
+++ b/src/views/basicData/product/index.vue
@@ -335,7 +335,7 @@
     rules: {
       productName: [
         { required: true, message: "璇疯緭鍏�", trigger: "blur" },
-        { max: 20, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃20涓瓧绗�", trigger: "blur" },
+        { max: 50, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃50涓瓧绗�", trigger: "blur" },
       ],
     },
     modelForm: {
diff --git a/src/views/basicData/supplierManage/components/BlacklistTab.vue b/src/views/basicData/supplierManage/components/BlacklistTab.vue
index 952d9b7..9e5a75f 100644
--- a/src/views/basicData/supplierManage/components/BlacklistTab.vue
+++ b/src/views/basicData/supplierManage/components/BlacklistTab.vue
@@ -96,7 +96,7 @@
           </el-col>
         </el-row>
         <el-row :gutter="30">
-          <el-col :span="12">
+          <!-- <el-col :span="12">
             <el-form-item label="渚涘簲鍟嗙被鍨嬶細"
                           prop="supplierType">
               <el-select v-model="form.supplierType"
@@ -112,7 +112,7 @@
                            value="涓�" />
               </el-select>
             </el-form-item>
-          </el-col>
+          </el-col> -->
           <el-col :span="12">
             <el-form-item label="鏄惁鐧藉悕鍗曪細"
                           prop="isWhite">
@@ -203,22 +203,22 @@
     {
       label: "渚涘簲鍟嗗悕绉�",
       prop: "supplierName",
-      width: 250,
+      // width: 250,
     },
-    {
-      label: "渚涘簲鍟嗙被鍨�",
-      prop: "supplierType",
-      width: 120,
-    },
+    // {
+    //   label: "渚涘簲鍟嗙被鍨�",
+    //   prop: "supplierType",
+    //   width: 120,
+    // },
     {
       label: "绾崇◣浜鸿瘑鍒彿",
       prop: "taxpayerIdentificationNum",
-      width: 230,
+      // width: 230,
     },
     {
       label: "鍏徃鍦板潃",
       prop: "companyAddress",
-      width: 220,
+      // width: 300,
     },
     {
       label: "缁存姢浜�",
@@ -228,7 +228,7 @@
     {
       label: "缁存姢鏃堕棿",
       prop: "maintainTime",
-      width: 100,
+      // width: 100,
     },
     {
       dataType: "action",
@@ -244,14 +244,14 @@
             openForm("edit", row);
           },
         },
-        {
-          //璧勮川闄勪欢
-          name: "璧勮川鏂囦欢",
-          type: "text",
-          clickFun: row => {
-            openFileDialog(row);
-          },
-        },
+        // {
+        //   //璧勮川闄勪欢
+        //   name: "璧勮川鏂囦欢",
+        //   type: "text",
+        //   clickFun: row => {
+        //     openFileDialog(row);
+        //   },
+        // },
       ],
     },
   ]);
@@ -290,9 +290,9 @@
       companyAddress: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
       maintainUserId: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
       maintainTime: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
-      supplierType: [
-        { required: true, message: "璇烽�夋嫨渚涘簲鍟嗙被鍨�", trigger: "change" },
-      ],
+      // supplierType: [
+      //   { required: true, message: "璇烽�夋嫨渚涘簲鍟嗙被鍨�", trigger: "change" },
+      // ],
     },
   });
   const { searchForm, form, rules } = toRefs(data);
diff --git a/src/views/basicData/supplierManage/components/HomeTab.vue b/src/views/basicData/supplierManage/components/HomeTab.vue
index 7edf87b..2c78f95 100644
--- a/src/views/basicData/supplierManage/components/HomeTab.vue
+++ b/src/views/basicData/supplierManage/components/HomeTab.vue
@@ -102,7 +102,7 @@
           </el-col>
         </el-row>
         <el-row :gutter="30">
-          <el-col :span="12">
+          <!-- <el-col :span="12">
             <el-form-item label="渚涘簲鍟嗙被鍨嬶細"
                           prop="supplierType">
               <el-select v-model="form.supplierType"
@@ -118,7 +118,7 @@
                            value="涓�" />
               </el-select>
             </el-form-item>
-          </el-col>
+          </el-col> -->
           <el-col :span="12">
             <el-form-item label="鏄惁鐧藉悕鍗曪細"
                           prop="isWhite">
@@ -209,26 +209,27 @@
     {
       label: "渚涘簲鍟嗗悕绉�",
       prop: "supplierName",
-      width: 250,
+      // width: 250,
     },
-    {
-      label: "渚涘簲鍟嗙被鍨�",
-      prop: "supplierType",
-      width: 120,
-    },
+    // {
+    //   label: "渚涘簲鍟嗙被鍨�",
+    //   prop: "supplierType",
+    //   width: 120,
+    // },
     {
       label: "绾崇◣浜鸿瘑鍒彿",
       prop: "taxpayerIdentificationNum",
-      width: 230,
+      // width: 230,
     },
     {
       label: "鍏徃鍦板潃",
       prop: "companyAddress",
-      width: 220,
+      // width: 220,
     },
     {
       label: "缁存姢浜�",
       prop: "maintainUserName",
+      // width: 220,
     },
 
     {
@@ -250,14 +251,14 @@
             openForm("edit", row);
           },
         },
-        {
-          //璧勮川闄勪欢
-          name: "璧勮川鏂囦欢",
-          type: "text",
-          clickFun: row => {
-            openFileDialog(row);
-          },
-        },
+        // {
+        //   //璧勮川闄勪欢
+        //   name: "璧勮川鏂囦欢",
+        //   type: "text",
+        //   clickFun: row => {
+        //     openFileDialog(row);
+        //   },
+        // },
       ],
     },
   ]);
@@ -296,9 +297,9 @@
       companyAddress: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
       maintainUserId: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
       maintainTime: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
-      supplierType: [
-        { required: true, message: "璇烽�夋嫨渚涘簲鍟嗙被鍨�", trigger: "change" },
-      ],
+      // supplierType: [
+      //   { required: true, message: "璇烽�夋嫨渚涘簲鍟嗙被鍨�", trigger: "change" },
+      // ],
     },
   });
   const { searchForm, form, rules } = toRefs(data);
diff --git a/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue b/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
index 7c7524f..e4cc4c0 100644
--- a/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
+++ b/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -22,14 +22,10 @@
         <el-row>
           <el-col :span="24">
             <el-form-item label="鐢宠閮ㄩ棬锛�">
-              <el-select disabled
-                         v-model="form.approveDeptId"
-                         placeholder="閫夋嫨閮ㄩ棬">
-                <el-option v-for="user in productOptions"
-                           :key="user.deptId"
-                           :label="user.deptName"
-                           :value="user.deptId" />
-              </el-select>
+              <el-input v-model="form.approveDeptName"
+                        placeholder=""
+                        clearable
+                        disabled />
             </el-form-item>
           </el-col>
         </el-row>
@@ -42,6 +38,18 @@
                         clearable
                         type="textarea"
                         disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row v-if="Number(props.approveType) === 4">
+          <el-col :span="24">
+            <el-form-item label="鎶ラ攢閲戦锛�">
+              <el-input-number v-model="form.price"
+                               disabled
+                               :min="0"
+                               :precision="2"
+                               style="width: 100%"
+                               placeholder="" />
             </el-form-item>
           </el-col>
         </el-row>
@@ -75,7 +83,7 @@
                 <el-descriptions-item label="鎶ヤ环鎬婚"
                                       :span="2">
                   <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">
-                    楼{{ Number(currentQuotation.totalAmount ?? 0).toFixed(2) }}
+                    {{ formatCurrency(currentQuotation.totalAmount) }}
                   </span>
                 </el-descriptions-item>
               </el-descriptions>
@@ -92,7 +100,7 @@
                                    label="鍗曚綅" />
                   <el-table-column prop="unitPrice"
                                    label="鍗曚环">
-                    <template #default="scope">楼{{ Number(scope.row.unitPrice ?? 0).toFixed(2) }}</template>
+                    <template #default="scope">{{ formatCurrency(scope.row.unitPrice) }}</template>
                   </el-table-column>
                 </el-table>
               </div>
@@ -135,7 +143,7 @@
                 <el-descriptions-item label="鍚堝悓閲戦"
                                       :span="2">
                   <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">
-                    楼{{ Number(currentPurchase.contractAmount ?? 0).toFixed(2) }}
+                    {{ formatCurrency(currentPurchase.contractAmount) }}
                   </span>
                 </el-descriptions-item>
               </el-descriptions>
@@ -154,11 +162,11 @@
                                    label="鏁伴噺" />
                   <el-table-column prop="taxInclusiveUnitPrice"
                                    label="鍚◣鍗曚环">
-                    <template #default="scope">楼{{ Number(scope.row.taxInclusiveUnitPrice ?? 0).toFixed(2) }}</template>
+                    <template #default="scope">{{ formatCurrency(scope.row.taxInclusiveUnitPrice) }}</template>
                   </el-table-column>
                   <el-table-column prop="taxInclusiveTotalPrice"
                                    label="鍚◣鎬讳环">
-                    <template #default="scope">楼{{ Number(scope.row.taxInclusiveTotalPrice ?? 0).toFixed(2) }}</template>
+                    <template #default="scope">{{ formatCurrency(scope.row.taxInclusiveTotalPrice) }}</template>
                   </el-table-column>
                 </el-table>
               </div>
@@ -332,6 +340,7 @@
   import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js";
   import { getDeliveryDetailByShippingNo } from "@/api/salesManagement/deliveryLedger.js";
   import ImagePreview from "@/components/AttachmentPreview/image/index.vue";
+  import { formatCurrency } from "@/utils/numberFormat";
   const emit = defineEmits(["close"]);
   const { proxy } = getCurrentInstance();
 
diff --git a/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue b/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
index f7ba9d9..edf045c 100644
--- a/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
+++ b/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
@@ -318,7 +318,7 @@
       formatData: (v) => (v === "active" ? "鐢熸晥涓�" : "宸插簾姝�"),
       formatType: (v) => (v === "active" ? "success" : "info"),
     },
-    { label: "宸茶浜烘暟", prop: "readCount", width: 100 },
+    // { label: "宸茶浜烘暟", prop: "readCount", width: 100 },
     {
       dataType: "action",
       label: "鎿嶄綔",
diff --git a/src/views/equipmentManagement/spareParts/index.vue b/src/views/equipmentManagement/spareParts/index.vue
index 8abe35d..a1a4026 100644
--- a/src/views/equipmentManagement/spareParts/index.vue
+++ b/src/views/equipmentManagement/spareParts/index.vue
@@ -29,6 +29,7 @@
 						:tableLoading="loading"
 						:page="pagination"
 						:isShowPagination="true"
+						:row-class-name="rowClassName"
 						@pagination="handleSizeChange"
 					>
 						<template #status="{ row }">
@@ -95,7 +96,7 @@
         </el-dialog>
       </el-tab-pane>
 
-      <el-tab-pane label="澶囦欢棰嗙敤璁板綍" name="usage">
+      <el-tab-pane label="澶囦欢浣跨敤璁板綍" name="usage">
         <div class="search_form">
           <el-form :inline="true" :model="usageQuery" class="search-form">
             <el-form-item label="澶囦欢鍚嶇О">
@@ -521,9 +522,17 @@
 onMounted(() => {
   fetchListData();
 });
+
+// 鏍规嵁鏁伴噺杩涜鍒ゆ柇鑳屾櫙棰滆壊
+const rowClassName = ({ row }) => {
+  const num = Number(row.quantity);
+  if (num <= 1) return 'row-qty-low';
+  if (num >= 8) return 'row-qty-high';
+  return 'row-qty-mid';
+};
 </script>
 
-<style scoped>
+<style>
 .spare-part-category {
   padding: 20px;
 }
@@ -547,8 +556,17 @@
   font-weight: 600;
 }
 
-.el-table__row:hover > td {
-  background-color: #fafafa;
+.el-table .row-qty-low td.el-table__cell,
+.el-table .row-qty-low:hover > td.el-table__cell {
+  background-color: #fde8e8 !important;
+}
+.el-table .row-qty-mid td.el-table__cell,
+.el-table .row-qty-mid:hover > td.el-table__cell {
+  background-color: #f0f4f8 !important;
+}
+.el-table .row-qty-high td.el-table__cell,
+.el-table .row-qty-high:hover > td.el-table__cell {
+  background-color: #e8f5e9 !important;
 }
 
 /* 鎸夐挳缁勬牱寮� */
@@ -567,4 +585,5 @@
   font-size: 12px;
   margin-right: 4px;
 }
+
 </style>
\ No newline at end of file
diff --git a/src/views/inventoryManagement/dispatchLog/Record.vue b/src/views/inventoryManagement/dispatchLog/Record.vue
index bb5c5c1..c7d53ab 100644
--- a/src/views/inventoryManagement/dispatchLog/Record.vue
+++ b/src/views/inventoryManagement/dispatchLog/Record.vue
@@ -102,9 +102,13 @@
         <el-table-column
           label="鍑哄簱鏃ユ湡"
           prop="createTime"
-          width="180"
+          width="120"
           show-overflow-tooltip
-        />
+        >
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
         <el-table-column
           label="浜у搧澶х被"
           prop="productName"
@@ -167,6 +171,7 @@
   findAllQualifiedStockOutRecordTypeOptions,
   findAllUnQualifiedStockOutRecordTypeOptions,
 } from "@/api/basicData/enum.js";
+import { parseTime } from "@/utils/ruoyi";
 
 const userStore = useUserStore();
 const { proxy } = getCurrentInstance();
diff --git a/src/views/inventoryManagement/receiptManagement/Record.vue b/src/views/inventoryManagement/receiptManagement/Record.vue
index f66d970..2fe86a2 100644
--- a/src/views/inventoryManagement/receiptManagement/Record.vue
+++ b/src/views/inventoryManagement/receiptManagement/Record.vue
@@ -106,8 +106,12 @@
                          show-overflow-tooltip/>
         <el-table-column label="鍏ュ簱鏃堕棿"
                          prop="createTime"
-                         width="160"
-                         show-overflow-tooltip/>
+                         width="120"
+                         show-overflow-tooltip>
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
         <el-table-column label="浜у搧澶х被"
                          prop="productName"
                          show-overflow-tooltip/>
@@ -185,6 +189,7 @@
   findAllQualifiedStockInRecordTypeOptions, 
   // findAllUnQualifiedStockInRecordTypeOptions,
 } from "@/api/basicData/enum.js";
+import { parseTime } from "@/utils/ruoyi";
 
 const {proxy} = getCurrentInstance();
 
diff --git a/src/views/inventoryManagement/stockManagement/Record.vue b/src/views/inventoryManagement/stockManagement/Record.vue
index d871952..0f3c79c 100644
--- a/src/views/inventoryManagement/stockManagement/Record.vue
+++ b/src/views/inventoryManagement/stockManagement/Record.vue
@@ -104,11 +104,11 @@
           prop="unQualifiedLockedQuantity"
           show-overflow-tooltip
         /> -->
-        <el-table-column
+<!--        <el-table-column
           label="搴撳瓨棰勮鏁伴噺"
           prop="warnNum"
           show-overflow-tooltip
-        />
+        />-->
         <el-table-column label="澶囨敞" prop="remark" show-overflow-tooltip />
         <el-table-column
           label="鏈�杩戞洿鏂版椂闂�"
diff --git a/src/views/procurementManagement/paymentLedger/index.vue b/src/views/procurementManagement/paymentLedger/index.vue
index ae2c30b..b913032 100644
--- a/src/views/procurementManagement/paymentLedger/index.vue
+++ b/src/views/procurementManagement/paymentLedger/index.vue
@@ -79,7 +79,7 @@
                     @pagination="sonPaginationSearch">
             <template #payableAmountSlot="{ row }">
               <el-text type="danger">
-                {{ parseFloat(row.unshippedAmount).toFixed(2) }}
+                {{ formatDecimal(row.unshippedAmount) }}
               </el-text>
             </template>
           </PIMTable>
@@ -90,13 +90,14 @@
 </template>
 
 <script setup>
-  import { ref, toRefs } from "vue";
+  import { ref, toRefs, reactive, getCurrentInstance } 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 { tableAmountFormatter, formatDecimal, buildAmountSummaryFormat } from "@/utils/numberFormat";
 
   const tableData = ref([]);
   const tableLoading = ref(false);
@@ -140,17 +141,13 @@
       label: "鍚堝悓閲戦(鍏�)",
       prop: "contractAmount",
       width: 200,
-      formatData: params => {
-        return params ? parseFloat(params).toFixed(2) : 0;
-      },
+      formatData: params => formatDecimal(params),
     },
     {
       label: "宸插叆搴撻噾棰�(鍏�)",
       prop: "shippedAmount",
       width: 200,
-      formatData: params => {
-        return params ? parseFloat(params).toFixed(2) : 0;
-      },
+      formatData: params => formatDecimal(params),
     },
     {
       label: "鏈叆搴撻噾棰�(鍏�)",
@@ -171,10 +168,13 @@
     return proxy.summarizeTable(
       param,
       ["contractAmounts", "shippedAmount", "unshippedAmount"],
-      {
-        ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
-        futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
-      }
+      buildAmountSummaryFormat(
+        ["contractAmounts", "shippedAmount", "unshippedAmount"],
+        {
+          ticketsNum: { noDecimal: true },
+          futureTickets: { noDecimal: true },
+        }
+      )
     );
   };
   // 瀛愯〃鍚堣鏂规硶
@@ -182,10 +182,13 @@
     let summarizeTable = proxy.summarizeTable(
       param,
       ["contractAmount", "shippedAmount", "unshippedAmount"],
-      {
-        ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
-        futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
-      }
+      buildAmountSummaryFormat(
+        ["contractAmount", "shippedAmount", "unshippedAmount"],
+        {
+          ticketsNum: { noDecimal: true },
+          futureTickets: { noDecimal: true },
+        }
+      )
     );
     return summarizeTable;
   };
@@ -257,13 +260,7 @@
     sonPage.size = pagination.limit;
     getPaymenRecordtList(currentSupplierId.value);
   };
-  const formattedNumber = (row, column, cellValue) => {
-    if (column.property !== "supplierName") {
-      return parseFloat(cellValue).toFixed(2);
-    } else {
-      return cellValue;
-    }
-  };
+  const formattedNumber = tableAmountFormatter;
   getList();
 </script>
 
diff --git a/src/views/procurementManagement/procurementLedger/detail.vue b/src/views/procurementManagement/procurementLedger/detail.vue
index 37b7fdb..80aa42b 100644
--- a/src/views/procurementManagement/procurementLedger/detail.vue
+++ b/src/views/procurementManagement/procurementLedger/detail.vue
@@ -127,6 +127,7 @@
 import FormDialog from "@/components/Dialog/FormDialog.vue"
 import filePreview from '@/components/filePreview/index.vue'
 import { getPurchaseById } from "@/api/procurementManagement/procurementLedger"
+import { tableAmountFormatter } from '@/utils/numberFormat'
 
 const visible = ref(false)
 // 鏄惁鏄剧ず閿�鍞悎鍚屽彿缁戝畾
@@ -157,12 +158,7 @@
   fileList.value = []
 }
 
-const formattedNumber = (row, column, cellValue) => {
-  if (cellValue != null && !isNaN(cellValue)) {
-    return Number(cellValue).toFixed(2)
-  }
-  return cellValue
-}
+const formattedNumber = tableAmountFormatter
 
 const sumTaxInclusiveUnitPrice = computed(() => {
   return productData.value.reduce((sum, item) => {
diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index e512edb..e2d340b 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>
@@ -161,7 +162,7 @@
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="褰曞叆浜猴細" prop="recorderId">
-              <el-select v-model="form.recorderId" placeholder="璇烽�夋嫨" clearable filterable>
+              <el-select v-model="form.recorderId" placeholder="璇烽�夋嫨" clearable filterable disabled>
                 <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
               </el-select>
             </el-form-item>
@@ -208,7 +209,7 @@
           <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
           <el-table-column label="鍗曚綅" prop="unit" width="70" />
           <el-table-column label="鏁伴噺" prop="quantity" width="70" />
-          <el-table-column label="搴撳瓨棰勮鏁伴噺" prop="warnNum" width="120" show-overflow-tooltip />
+<!--          <el-table-column label="搴撳瓨棰勮鏁伴噺" prop="warnNum" width="120" show-overflow-tooltip />-->
           <el-table-column label="绋庣巼(%)" prop="taxRate" width="80" />
           <el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" width="150" />
           <el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" width="150" />
@@ -276,20 +277,22 @@
       @cancel="closeProductDia">
       <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
         <el-row :gutter="30">
-          <el-col :span="24">
-            <el-form-item label="浜у搧澶х被锛�" prop="productId">
-              <el-tree-select v-model="productForm.productId" placeholder="璇烽�夋嫨" clearable filterable check-strictly
-                @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
+          <el-col :span="12">
+            <el-form-item label="浜у搧澶х被锛�" prop="productCategory">
+              <el-input v-model="productForm.productCategory"
+                        placeholder="鐐瑰嚮閫夋嫨"
+                        readonly
+                        @click="showProductSelectDialog = true"
+                        style="cursor:pointer" />
             </el-form-item>
           </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="24">
+          <el-col :span="12">
             <el-form-item label="瑙勬牸鍨嬪彿锛�" prop="productModelId">
-              <el-select v-model="productForm.productModelId" placeholder="璇烽�夋嫨" filterable clearable
-                @change="getProductModel">
-                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
-              </el-select>
+              <el-input v-model="productForm.specificationModel"
+                        placeholder="鐐瑰嚮閫夋嫨"
+                        readonly
+                        @click="showProductSelectDialog = true"
+                        style="cursor:pointer" />
             </el-form-item>
           </el-col>
         </el-row>
@@ -310,7 +313,7 @@
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="鍚◣鍗曚环(鍏�)锛�" prop="taxInclusiveUnitPrice">
-              <el-input-number v-model="productForm.taxInclusiveUnitPrice" :precision="2" :step="0.1" :min="0" clearable
+              <el-input-number v-model="productForm.taxInclusiveUnitPrice" :precision="6" :step="0.000001" :min="0" clearable
                 style="width: 100%" @change="mathNum" />
             </el-form-item>
           </el-col>
@@ -324,13 +327,13 @@
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="鍚◣鎬讳环(鍏�)锛�" prop="taxInclusiveTotalPrice">
-              <el-input-number v-model="productForm.taxInclusiveTotalPrice" :precision="2" :step="0.1" :min="0"
+              <el-input-number v-model="productForm.taxInclusiveTotalPrice" :precision="6" :step="0.000001" :min="0"
                 clearable style="width: 100%" @change="reverseMathNum('taxInclusiveTotalPrice')" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
             <el-form-item label="涓嶅惈绋庢�讳环(鍏�)锛�" prop="taxExclusiveTotalPrice">
-              <el-input-number v-model="productForm.taxExclusiveTotalPrice" :precision="2" :step="0.1" :min="0"
+              <el-input-number v-model="productForm.taxExclusiveTotalPrice" :precision="6" :step="0.000001" :min="0"
                 clearable style="width: 100%" @change="reverseMathNum('taxExclusiveTotalPrice')" />
             </el-form-item>
           </el-col>
@@ -344,14 +347,12 @@
               </el-select>
             </el-form-item>
           </el-col>
-          <el-col :span="12">
-            <el-form-item label="搴撳瓨棰勮鏁伴噺锛�" prop="warnNum">
-              <el-input-number v-model="productForm.warnNum" :precision="2" :step="0.1" :min="0" clearable
-                style="width: 100%" />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
+<!--          <el-col :span="12">-->
+<!--            <el-form-item label="搴撳瓨棰勮鏁伴噺锛�" prop="warnNum">-->
+<!--              <el-input-number v-model="productForm.warnNum" :precision="2" :step="0.1" :min="0" clearable-->
+<!--                style="width: 100%" />-->
+<!--            </el-form-item>-->
+<!--          </el-col>-->
           <el-col :span="12">
             <el-form-item label="鏄惁璐ㄦ锛�" prop="isChecked">
               <el-radio-group v-model="productForm.isChecked">
@@ -361,8 +362,21 @@
             </el-form-item>
           </el-col>
         </el-row>
+<!--        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鏄惁璐ㄦ锛�" prop="isChecked">
+              <el-radio-group v-model="productForm.isChecked">
+                <el-radio label="鏄�" :value="true" />
+                <el-radio label="鍚�" :value="false" />
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>-->
       </el-form>
     </FormDialog>
+    <ProductSelectDialog v-model="showProductSelectDialog"
+                         @confirm="handleProductSelect"
+                         single />
     <FileList v-if="fileListDialogVisible" v-model:visible="fileListDialogVisible" record-type="purchase_ledger"
       :record-id="recordId" />
     <Detail ref="detailRef" />
@@ -403,6 +417,7 @@
   productList,
   getPurchaseById,
   getOptions,
+  getPrintData,
   getPurchaseTemplateList,
   delPurchaseTemplate,
 } from "@/api/procurementManagement/procurementLedger.js";
@@ -435,8 +450,11 @@
 const fileList = ref([]);
 import useUserStore from "@/store/modules/user";
 import { modelList, productTreeList } from "@/api/basicData/product.js";
+import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
 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();
 
@@ -636,6 +654,7 @@
 
 // 浜у搧琛ㄥ崟寮规鏁版嵁
 const productFormVisible = ref(false);
+const showProductSelectDialog = ref(false);
 const productOperationType = ref("");
 const productOperationIndex = ref("");
 const currentId = ref("");
@@ -656,15 +675,15 @@
     isChecked: false,
   },
   productRules: {
-    productId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-    productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    productCategory: [{ required: true, message: "璇烽�夋嫨浜у搧澶х被", trigger: "change" }],
+    productModelId: [{ required: true, message: "璇烽�夋嫨瑙勬牸鍨嬪彿", trigger: "change" }],
     unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
     quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
     taxInclusiveUnitPrice: [
       { required: true, message: "璇疯緭鍏�", trigger: "blur" },
     ],
     taxRate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-    warnNum: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    // warnNum: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
     taxInclusiveTotalPrice: [
       { required: true, message: "璇疯緭鍏�", trigger: "blur" },
     ],
@@ -763,9 +782,7 @@
   handleQuery();
 };
 
-const formattedNumber = (row, column, cellValue) => {
-  return parseFloat(cellValue).toFixed(2);
-};
+const formattedNumber = tableAmountFormatter;
 // 鏌ヨ鍒楄〃
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
@@ -883,10 +900,19 @@
       "futureTickets",
       "futureTicketsAmount",
     ],
-    {
-      ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
-      futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
-    }
+    buildAmountSummaryFormat(
+      [
+        "taxInclusiveUnitPrice",
+        "taxInclusiveTotalPrice",
+        "taxExclusiveTotalPrice",
+        "ticketsAmount",
+        "futureTicketsAmount",
+      ],
+      {
+        ticketsNum: { noDecimal: true },
+        futureTickets: { noDecimal: true },
+      }
+    )
   );
 };
 const paginationChange = obj => {
@@ -949,15 +975,27 @@
 };
 // 涓昏〃鍚堣鏂规硶
 const summarizeMainTable = param => {
-  return proxy.summarizeTable(param, ["contractAmount"]);
+  return proxy.summarizeTable(
+    param,
+    ["contractAmount"],
+    buildAmountSummaryFormat(["contractAmount"])
+  );
 };
 // 瀛愯〃鍚堣鏂规硶
 const summarizeProTable = param => {
-  return proxy.summarizeTable(param, [
-    "taxInclusiveUnitPrice",
-    "taxInclusiveTotalPrice",
-    "taxExclusiveTotalPrice",
-  ]);
+  return proxy.summarizeTable(
+    param,
+    [
+      "taxInclusiveUnitPrice",
+      "taxInclusiveTotalPrice",
+      "taxExclusiveTotalPrice",
+    ],
+    buildAmountSummaryFormat([
+      "taxInclusiveUnitPrice",
+      "taxInclusiveTotalPrice",
+      "taxExclusiveTotalPrice",
+    ])
+  );
 };
 // 鎵撳紑寮规
 const openForm = async (type, row) => {
@@ -998,6 +1036,7 @@
     );
 
     form.value.entryDate = getCurrentDate();
+    form.value.recorderId = userStore.id;
 
     if (type === "add") {
       // 鏂板鏃剁敓鎴愰噰璐悎鍚屽彿
@@ -1225,6 +1264,19 @@
     await nextTick();
   }
 };
+
+// ProductSelectDialog 閫変腑鍥炶皟
+const handleProductSelect = (products) => {
+  if (!products || !products.length) return;
+  const p = products[0];
+  productForm.value.productId = p.productId;
+  productForm.value.productCategory = p.productName || '';
+  productForm.value.productModelId = p.id;
+  productForm.value.specificationModel = p.model || '';
+  productForm.value.unit = p.unit || '';
+  showProductSelectDialog.value = false;
+};
+
 const getProductOptions = () => {
   return productTreeList().then(res => {
     productOptions.value = convertIdToValue(res);
@@ -1407,6 +1459,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();
@@ -1466,10 +1543,10 @@
   if (field === "taxInclusiveTotalPrice") {
     // 宸茬煡鍚◣鎬讳环鍜屾暟閲忥紝鍙嶇畻鍚◣鍗曚环
     if (productForm.value.quantity) {
-      productForm.value.taxInclusiveUnitPrice = (
+      productForm.value.taxInclusiveUnitPrice = formatDecimal(
         Number(productForm.value.taxInclusiveTotalPrice) /
         Number(productForm.value.quantity)
-      ).toFixed(2);
+      );
       // 纭繚缁撴灉涓嶄负璐熸暟
       if (Number(productForm.value.taxInclusiveUnitPrice) < 0) {
         productForm.value.taxInclusiveUnitPrice = "0";
@@ -1487,30 +1564,30 @@
       }
     }
     // 鍙嶇畻涓嶅惈绋庢�讳环
-    productForm.value.taxExclusiveTotalPrice = (
+    productForm.value.taxExclusiveTotalPrice = formatDecimal(
       Number(productForm.value.taxInclusiveTotalPrice) /
       (1 + taxRate / 100)
-    ).toFixed(2);
+    );
     // 纭繚缁撴灉涓嶄负璐熸暟
     if (Number(productForm.value.taxExclusiveTotalPrice) < 0) {
       productForm.value.taxExclusiveTotalPrice = "0";
     }
   } else if (field === "taxExclusiveTotalPrice") {
     // 鍙嶇畻鍚◣鎬讳环
-    productForm.value.taxInclusiveTotalPrice = (
+    productForm.value.taxInclusiveTotalPrice = formatDecimal(
       Number(productForm.value.taxExclusiveTotalPrice) *
       (1 + taxRate / 100)
-    ).toFixed(2);
+    );
     // 纭繚缁撴灉涓嶄负璐熸暟
     if (Number(productForm.value.taxInclusiveTotalPrice) < 0) {
       productForm.value.taxInclusiveTotalPrice = "0";
     }
     // 宸茬煡鏁伴噺锛屽弽绠楀惈绋庡崟浠�
     if (productForm.value.quantity) {
-      productForm.value.taxInclusiveUnitPrice = (
+      productForm.value.taxInclusiveUnitPrice = formatDecimal(
         Number(productForm.value.taxInclusiveTotalPrice) /
         Number(productForm.value.quantity)
-      ).toFixed(2);
+      );
       // 纭繚缁撴灉涓嶄负璐熸暟
       if (Number(productForm.value.taxInclusiveUnitPrice) < 0) {
         productForm.value.taxInclusiveUnitPrice = "0";
@@ -1560,6 +1637,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/procurementManagement/procurementReport/index.vue b/src/views/procurementManagement/procurementReport/index.vue
index 26e682d..e891868 100644
--- a/src/views/procurementManagement/procurementReport/index.vue
+++ b/src/views/procurementManagement/procurementReport/index.vue
@@ -83,6 +83,7 @@
 import PIMTable from '@/components/PIMTable/PIMTable.vue'
 import { procurementBusinessSummaryListPage } from '@/api/procurementManagement/procurementReport'
 import { productTreeList } from '@/api/basicData/product'
+import { formatDecimal, formatCurrency } from '@/utils/numberFormat'
 
 const { proxy } = getCurrentInstance()
 
@@ -135,14 +136,14 @@
     prop: 'returnAmount',
     width: 120,
     formatData: (val) => {
-      return val ? `楼${parseFloat(val).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : '楼0.00'
+      return val ? formatCurrency(val) : '楼0'
     }
   },
   {
     label: '閫�娆惧偍閲�',
     prop: 'purchaseAmount',
     formatData: (val) => {
-      return val ? `楼${parseFloat(val).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : '楼0.00'
+      return val ? formatCurrency(val) : '楼0'
     }
   },
   {
@@ -155,7 +156,7 @@
     prop: 'averagePrice',
     width: 120,
     formatData: (val) => {
-      return val ? `楼${parseFloat(val).toFixed(2)}` : '楼0.00'
+      return val ? formatCurrency(val) : '楼0'
     }
   },
   {
diff --git a/src/views/qualityManagement/finalInspection/components/formDia.vue b/src/views/qualityManagement/finalInspection/components/formDia.vue
index 31ba755..e5cd36e 100644
--- a/src/views/qualityManagement/finalInspection/components/formDia.vue
+++ b/src/views/qualityManagement/finalInspection/components/formDia.vue
@@ -97,6 +97,34 @@
         </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
+            <el-form-item label="鏄惁鎶芥锛�"
+                          prop="isSampling">
+              <el-radio-group v-model="form.isSampling"
+                              class="sampling-radio-group"
+                              :disabled="isViewMode"
+                              @change="handleSamplingChange">
+                <el-radio-button :label="1">鏄�</el-radio-button>
+                <el-radio-button :label="0">鍚�</el-radio-button>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12"
+                  v-if="form.isSampling === 1">
+            <el-form-item label="鎶芥鏁伴噺锛�"
+                          prop="samplingQuantity">
+              <el-input-number :step="0.01"
+                               :min="0"
+                               style="width: 100%"
+                               v-model="form.samplingQuantity"
+                               placeholder="璇疯緭鍏�"
+                               clearable
+                               :precision="2"
+                               :disabled="isViewMode" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
             <el-form-item label="妫�娴嬪崟浣嶏細" prop="checkCompany">
               <el-input v-model="form.checkCompany" placeholder="璇疯緭鍏�" clearable :disabled="isViewMode"/>
             </el-form-item>
@@ -184,6 +212,8 @@
     qualifiedQuantity: "",
     unqualifiedQuantity: "",
     checkCompany: "",
+    isSampling: 0,
+    samplingQuantity: "",
   },
   rules: {
     checkTime: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
@@ -197,6 +227,8 @@
     qualifiedQuantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
     unqualifiedQuantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
     checkCompany: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+    isSampling: [{ required: true, message: "璇烽�夋嫨鏄惁鎶芥", trigger: "change" }],
+    samplingQuantity: [{ required: true, message: "璇疯緭鍏ユ娊妫�鏁伴噺", trigger: "blur" }],
   },
 });
 const { form, rules } = toRefs(data);
@@ -289,7 +321,7 @@
   ]);
   userList.value = userListsRes.data;
 
-  form.value = {}
+  form.value = { isSampling: 0, samplingQuantity: "" }
   testStandardOptions.value = [];
   tableData.value = [];
 
@@ -502,6 +534,15 @@
     tableData.value = res.data;
   });
 }
+// 鏄惁鎶芥鍒囨崲
+const handleSamplingChange = (value) => {
+  if (value !== 1) {
+    form.value.samplingQuantity = "";
+    nextTick(() => {
+      proxy.$refs?.formRef?.clearValidate?.("samplingQuantity");
+    });
+  }
+}
 // 鍏抽棴寮规
 const closeDia = () => {
   proxy.resetForm("formRef");
@@ -517,5 +558,7 @@
 </script>
 
 <style scoped>
-
+.sampling-radio-group {
+  height: 32px;
+}
 </style>
diff --git a/src/views/qualityManagement/metricBinding/index.vue b/src/views/qualityManagement/metricBinding/index.vue
index 1ac268a..ba989ea 100644
--- a/src/views/qualityManagement/metricBinding/index.vue
+++ b/src/views/qualityManagement/metricBinding/index.vue
@@ -120,25 +120,45 @@
     <el-dialog
       v-model="bindingDialogVisible"
       title="娣诲姞缁戝畾"
-      width="520px"
+      width="640px"
       @close="closeBindingDialog"
     >
-      <el-form label-width="100px">
-        <el-form-item label="浜у搧">
-          <el-tree-select
-            v-model="selectedProductIds"
-            multiple
-            collapse-tags
-            collapse-tags-tooltip
-            placeholder="璇烽�夋嫨浜у搧锛堝彲澶氶�夛級"
-            clearable
-            check-strictly
-            :data="productOptions"
-            :render-after-expand="false"
-            style="width: 100%"
-          />
-        </el-form-item>
-      </el-form>
+      <div class="binding-dialog">
+        <el-input
+          v-model="productSearchKeyword"
+          placeholder="鎼滅储浜у搧"
+          clearable
+          prefix-icon="Search"
+          class="binding-search"
+        />
+        <el-tree
+          ref="productTreeRef"
+          :key="productTreeKey"
+          v-loading="productTreeLoading"
+          :data="productTreeData"
+          show-checkbox
+          check-strictly
+          node-key="id"
+          :props="{ label: 'label', children: 'children', disabled: 'disabled' }"
+          :filter-node-method="filterProductNode"
+          class="product-binding-tree"
+          @check="handleProductTreeCheck"
+        />
+        <div v-if="selectedProductLabels.length" class="selected-products">
+          <div class="selected-title">宸查�夋嫨锛坽{ selectedProductLabels.length }}锛�</div>
+          <div class="selected-tags">
+            <el-tag
+              v-for="item in selectedProductLabels"
+              :key="item.id"
+              closable
+              type="info"
+              @close="removeSelectedProduct(item.id)"
+            >
+              {{ item.label }}
+            </el-tag>
+          </div>
+        </div>
+      </div>
       <template #footer>
         <span class="dialog-footer">
           <el-button @click="closeBindingDialog">鍙栨秷</el-button>
@@ -150,8 +170,7 @@
 </template>
 
 <script setup>
-import { Search } from '@element-plus/icons-vue'
-import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
+import { ref, reactive, toRefs, computed, watch, nextTick, onMounted, getCurrentInstance } from 'vue'
 import { ElMessageBox } from 'element-plus'
 import PIMTable from '@/components/PIMTable/PIMTable.vue'
 import { productTreeList } from '@/api/basicData/product.js'
@@ -261,28 +280,74 @@
 const bindingDialogVisible = ref(false)
 
 // 浜у搧鏍戯紙鐢ㄤ簬缁戝畾閫夋嫨锛�
-const productOptions = ref([])
+const productTreeData = ref([])
+const productTreeLoading = ref(false)
+const productTreeRef = ref(null)
+const productTreeKey = ref(0)
+const productSearchKeyword = ref('')
 const selectedProductIds = ref([])
 
-const getProductOptions = async () => {
-  // 閬垮厤閲嶅璇锋眰
-  if (productOptions.value?.length) return
-  const res = await productTreeList()
-  productOptions.value = convertIdToValue(Array.isArray(res) ? res : [])
+const markParentNodesDisabled = (nodes) => {
+  return (nodes || []).map((node) => {
+    const children = node.children?.length ? markParentNodesDisabled(node.children) : []
+    return {
+      ...node,
+      children,
+      disabled: children.length > 0
+    }
+  })
 }
 
-function convertIdToValue(data) {
-  return (data || []).map((item) => {
-    const { id, children, ...rest } = item
-    const newItem = {
-      ...rest,
-      value: id
+const buildProductLabelMap = (nodes, map = {}) => {
+  ;(nodes || []).forEach((node) => {
+    if (node.id != null) {
+      map[node.id] = node.label
     }
-    if (children && children.length > 0) {
-      newItem.children = convertIdToValue(children)
+    if (node.children?.length) {
+      buildProductLabelMap(node.children, map)
     }
-    return newItem
   })
+  return map
+}
+
+const productLabelMap = computed(() => buildProductLabelMap(productTreeData.value))
+
+const selectedProductLabels = computed(() =>
+  selectedProductIds.value.map((id) => ({
+    id,
+    label: productLabelMap.value[id] || id
+  }))
+)
+
+const filterProductNode = (value, data) => {
+  if (!value) return true
+  return String(data.label || '').includes(value)
+}
+
+watch(productSearchKeyword, (val) => {
+  productTreeRef.value?.filter(val)
+})
+
+const getProductTreeData = async () => {
+  if (productTreeData.value?.length) return
+  productTreeLoading.value = true
+  try {
+    const res = await productTreeList()
+    productTreeData.value = markParentNodesDisabled(Array.isArray(res) ? res : [])
+  } catch (error) {
+    console.error('鑾峰彇浜у搧鏍戝け璐�:', error)
+  } finally {
+    productTreeLoading.value = false
+  }
+}
+
+const handleProductTreeCheck = () => {
+  selectedProductIds.value = productTreeRef.value?.getCheckedKeys(true) || []
+}
+
+const removeSelectedProduct = (id) => {
+  selectedProductIds.value = selectedProductIds.value.filter((item) => item !== id)
+  productTreeRef.value?.setChecked(id, false, false)
 }
 
 const handleQuery = () => {
@@ -354,12 +419,20 @@
 const openBindingDialog = () => {
   if (!currentStandard.value?.id) return
   selectedProductIds.value = []
-  getProductOptions()
+  productSearchKeyword.value = ''
+  productTreeKey.value += 1
   bindingDialogVisible.value = true
+  nextTick(() => {
+    productTreeRef.value?.setCheckedKeys([])
+    productTreeRef.value?.filter('')
+  })
 }
 
 const closeBindingDialog = () => {
   bindingDialogVisible.value = false
+  selectedProductIds.value = []
+  productSearchKeyword.value = ''
+  productTreeRef.value?.setCheckedKeys([])
 }
 
 const submitBinding = async () => {
@@ -428,6 +501,7 @@
 onMounted(() => {
   getStandardList()
   getProcessList()
+  getProductTreeData()
 })
 </script>
 
@@ -533,4 +607,39 @@
   width: 100%;
   margin-top: 4px;
 }
+
+.binding-dialog {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.binding-search {
+  width: 100%;
+}
+
+.product-binding-tree {
+  max-height: 360px;
+  overflow-y: auto;
+  border: 1px solid #ebeef5;
+  border-radius: 4px;
+  padding: 8px;
+}
+
+.selected-products {
+  border-top: 1px solid #ebeef5;
+  padding-top: 12px;
+}
+
+.selected-title {
+  font-size: 13px;
+  color: #606266;
+  margin-bottom: 8px;
+}
+
+.selected-tags {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
 </style>
diff --git a/src/views/qualityManagement/processInspection/components/formDia.vue b/src/views/qualityManagement/processInspection/components/formDia.vue
index f28cec2..a02976e 100644
--- a/src/views/qualityManagement/processInspection/components/formDia.vue
+++ b/src/views/qualityManagement/processInspection/components/formDia.vue
@@ -130,6 +130,34 @@
         </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
+            <el-form-item label="鏄惁鎶芥锛�"
+                          prop="isSampling">
+              <el-radio-group v-model="form.isSampling"
+                              class="sampling-radio-group"
+                              :disabled="isViewMode"
+                              @change="handleSamplingChange">
+                <el-radio-button :label="1">鏄�</el-radio-button>
+                <el-radio-button :label="0">鍚�</el-radio-button>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12"
+                  v-if="form.isSampling === 1">
+            <el-form-item label="鎶芥鏁伴噺锛�"
+                          prop="samplingQuantity">
+              <el-input-number :step="0.01"
+                               :min="0"
+                               style="width: 100%"
+                               v-model="form.samplingQuantity"
+                               placeholder="璇疯緭鍏�"
+                               clearable
+                               :precision="2"
+                               :disabled="isViewMode" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
             <el-form-item label="妫�娴嬪崟浣嶏細"
                           prop="checkCompany">
               <el-input v-model="form.checkCompany"
@@ -242,6 +270,8 @@
       qualifiedQuantity: "",
       unqualifiedQuantity: "",
       checkCompany: "",
+      isSampling: 0,
+      samplingQuantity: "",
     },
     rules: {
       checkTime: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
@@ -255,6 +285,8 @@
       qualifiedQuantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
       unqualifiedQuantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
       checkCompany: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+      isSampling: [{ required: true, message: "璇烽�夋嫨鏄惁鎶芥", trigger: "change" }],
+      samplingQuantity: [{ required: true, message: "璇疯緭鍏ユ娊妫�鏁伴噺", trigger: "blur" }],
     },
   });
   const userList = ref([]);
@@ -359,6 +391,8 @@
       unit: "",
       quantity: "",
       checkCompany: "",
+      isSampling: 0,
+      samplingQuantity: "",
     };
     testStandardOptions.value = [];
     tableData.value = [];
@@ -607,6 +641,15 @@
       tableData.value = res.data;
     });
   };
+  // 鏄惁鎶芥鍒囨崲
+  const handleSamplingChange = (value) => {
+    if (value !== 1) {
+      form.value.samplingQuantity = "";
+      nextTick(() => {
+        proxy.$refs?.formRef?.clearValidate?.("samplingQuantity");
+      });
+    }
+  };
   // 鍏抽棴寮规
   const closeDia = () => {
     proxy.resetForm("formRef");
@@ -622,4 +665,7 @@
 </script>
 
 <style scoped>
+.sampling-radio-group {
+  height: 32px;
+}
 </style>
diff --git a/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue b/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
index b745e9d..d3f9fc0 100644
--- a/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
+++ b/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
@@ -14,6 +14,7 @@
                   v-model="form.supplier"
                   placeholder="璇烽�夋嫨"
                   clearable
+                  filterable
                   :disabled="isViewMode || supplierQuantityDisabled"
               >
                 <el-option
@@ -96,6 +97,23 @@
               <el-input-number :step="0.01" :min="0" :max="form.quantity || 0" style="width: 100%"
                                v-model="form.unqualifiedQuantity" placeholder="璇疯緭鍏�" :precision="2"
                                @change="onUnqualifiedChange" :disabled="isViewMode"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鏄惁鎶芥锛�" prop="isSampling">
+              <el-radio-group v-model="form.isSampling" class="sampling-radio-group" :disabled="isViewMode" @change="handleSamplingChange">
+                <el-radio-button :label="1">鏄�</el-radio-button>
+                <el-radio-button :label="0">鍚�</el-radio-button>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12" v-if="form.isSampling === 1">
+            <el-form-item label="鎶芥鏁伴噺锛�" prop="samplingQuantity">
+              <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.samplingQuantity"
+                               placeholder="璇疯緭鍏�" clearable :precision="2" :disabled="isViewMode"/>
             </el-form-item>
           </el-col>
         </el-row>
@@ -190,6 +208,8 @@
     unit: "",
     quantity: "",
     checkCompany: "",
+    isSampling: 0,
+    samplingQuantity: "",
   },
   rules: {
     checkTime: [{required: true, message: "璇疯緭鍏�", trigger: "blur"},],
@@ -203,6 +223,8 @@
     qualifiedQuantity: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
     unqualifiedQuantity: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
     checkCompany: [{required: false, message: "璇疯緭鍏�", trigger: "blur"}],
+    isSampling: [{required: true, message: "璇烽�夋嫨鏄惁鎶芥", trigger: "change"}],
+    samplingQuantity: [{required: true, message: "璇疯緭鍏ユ娊妫�鏁伴噺", trigger: "blur"}],
   },
 });
 const tableColumn = ref([
@@ -307,6 +329,8 @@
     unit: "",
     quantity: "",
     checkCompany: "",
+    isSampling: 0,
+    samplingQuantity: "",
   }
   testStandardOptions.value = [];
   tableData.value = [];
@@ -531,6 +555,16 @@
   }
 };
 
+// 鏄惁鎶芥鍒囨崲
+const handleSamplingChange = (value) => {
+  if (value !== 1) {
+    form.value.samplingQuantity = "";
+    nextTick(() => {
+      proxy.$refs?.formRef?.clearValidate?.("samplingQuantity");
+    });
+  }
+};
+
 // 鍏抽棴寮规
 const closeDia = () => {
   proxy.resetForm("formRef");
@@ -546,5 +580,7 @@
 </script>
 
 <style scoped>
-
+.sampling-radio-group {
+  height: 32px;
+}
 </style>
diff --git a/src/views/reportAnalysis/reportManagement/index.vue b/src/views/reportAnalysis/reportManagement/index.vue
index 3bcb8ae..4c9e1ea 100644
--- a/src/views/reportAnalysis/reportManagement/index.vue
+++ b/src/views/reportAnalysis/reportManagement/index.vue
@@ -37,7 +37,7 @@
                   </div>
                 </div>
               </div>
-              <div class="typeNum">
+              <div class="typeNum" v-if="showSemiFinished">
                 <div class="typeNum-left">
                   <img src="~@/assets/images/chartCard2.svg" alt="鍥捐〃"
                     style="width: 40px; height: 40px; object-fit: contain;">
@@ -123,7 +123,7 @@
                   </div>
                 </div>
               </div>
-              <div class="quality-card green-card">
+              <div class="quality-card green-card" v-if="showSemiFinished">
                 <div class="quality-card-title">
                   <img src="~@/assets/images/chartCard2.svg" alt="鍗婃垚鍝�"
                     style="width: 24px; height: 24px; margin-right: 8px;">
@@ -202,7 +202,7 @@
                     <div class="chart-box-title">鍘熸潗鏂欐�绘暟</div>
                     <div class="chart-box-num">{{ getYearlyStatValue(0, 'totalCount') }}</div>
                   </div>
-                  <div class="inspection-chart-box">
+                  <div class="inspection-chart-box" v-if="showSemiFinished">
                     <div class="chart-box-title">鍗婃垚鍝佹�绘暟</div>
                     <div class="chart-box-num">{{ getYearlyStatValue(1, 'totalCount') }}</div>
                   </div>
@@ -266,7 +266,7 @@
             <!-- Tab 閫夋嫨鍣� -->
             <div class="tab-selector">
               <div class="tab-item" :class="{ active: activeTab === 'raw' }" @click="activeTab = 'raw'">鍘熸潗鏂�</div>
-              <div class="tab-item" :class="{ active: activeTab === 'semi' }" @click="activeTab = 'semi'">鍗婃垚鍝�</div>
+              <div v-if="showSemiFinished" class="tab-item" :class="{ active: activeTab === 'semi' }" @click="activeTab = 'semi'">鍗婃垚鍝�</div>
               <div class="tab-item" :class="{ active: activeTab === 'final' }" @click="activeTab = 'final'">鎴愬搧</div>
             </div>
           </el-card>
@@ -295,6 +295,9 @@
 const monthlyCompletionDetailsData = ref([]);
 const topParametersData = ref({ totalCount: 0, list: [] });
 const activeTab = ref("raw");
+
+// 鏄惁鏄剧ず鍗婃垚鍝佺浉鍏冲唴瀹广�傛湁鐨勪紒涓氭病鏈夊崐鎴愬搧锛岃涓� false 鍗冲彲闅愯棌鎵�鏈夊崐鎴愬搧鐩稿叧灞曠ず
+const showSemiFinished = ref(false);
 
 const getParameterColor = (index) => {
   const colors = ['#165DFF', '#14C9C9', '#F7BA1E', '#722ED1', '#3491FA', '#FF7D00', '#F53F3F'];
@@ -526,7 +529,7 @@
         containLabel: true,
       },
       legend: {
-        data: ["鍘熸潗鏂�", "鍗婃垚鍝�", "鎴愬搧"], // 鍥句緥鏁版嵁
+        data: showSemiFinished.value ? ["鍘熸潗鏂�", "鍗婃垚鍝�", "鎴愬搧"] : ["鍘熸潗鏂�", "鎴愬搧"], // 鍥句緥鏁版嵁
         icon: ["circle", "circle", "circle"],
         itemWidth: 10, // 璁剧疆鍥炬爣瀹藉害
         itemHeight: 10,
@@ -564,7 +567,7 @@
             color: "#409EFF",
           },
         },
-        {
+        ...(showSemiFinished.value ? [{
           name: "鍗婃垚鍝�",
           type: "bar",
           barWidth: "15%",
@@ -573,7 +576,7 @@
           itemStyle: {
             color: "#67C23A",
           },
-        },
+        }] : []),
         {
           name: "鎴愬搧",
           type: "bar",
@@ -607,7 +610,7 @@
           radius: "70%",
           data: [
             { value: getYearlyStatValue(0, 'totalCount'), name: "鍘熸潗鏂�", itemStyle: { color: "#1890FF" } },
-            { value: getYearlyStatValue(1, 'totalCount'), name: "鍗婃垚鍝�", itemStyle: { color: "#F7BA1E" } },
+            ...(showSemiFinished.value ? [{ value: getYearlyStatValue(1, 'totalCount'), name: "鍗婃垚鍝�", itemStyle: { color: "#F7BA1E" } }] : []),
             { value: getYearlyStatValue(2, 'totalCount'), name: "鎴愬搧", itemStyle: { color: "#14C9C9" } },
           ],
           label: {
@@ -655,7 +658,7 @@
       },
       // 鍥句緥閰嶇疆
       legend: {
-        data: ["鍘熸潗鏂�", "鍗婃垚鍝�", "鎴愬搧"], // 鍥句緥鏁版嵁
+        data: showSemiFinished.value ? ["鍘熸潗鏂�", "鍗婃垚鍝�", "鎴愬搧"] : ["鍘熸潗鏂�", "鎴愬搧"], // 鍥句緥鏁版嵁
         icon: ["circle", "circle", "circle"],
         itemWidth: 10, // 璁剧疆鍥炬爣瀹藉害
         itemHeight: 10,
@@ -697,7 +700,7 @@
           },
           data: monthlyPassRateData.value.map(item => item.rawMaterial.passRate),
         },
-        {
+        ...(showSemiFinished.value ? [{
           name: "鍗婃垚鍝�", // 绯诲垪鍚嶇О
           type: "line", // 鍥捐〃绫诲瀷涓烘姌绾垮浘
           // stack: "Total", // 鍫嗗彔鍚嶇О
@@ -706,7 +709,7 @@
             color: "#F7BA1E", // 璁剧疆杩欐潯绾跨殑棰滆壊
           },
           data: monthlyPassRateData.value.map(item => item.process.passRate),
-        },
+        }] : []),
         {
           name: "鎴愬搧", // 绯诲垪鍚嶇О
           type: "line", // 鍥捐〃绫诲瀷涓烘姌绾垮浘
@@ -769,16 +772,18 @@
     "#52c41a",
     getPassRateStatValue(0, 'passRate')
   );
-  semiCompletionChartInstance = initQualityChart(
-    semiCompletionChart,
-    "#1890ff",
-    getPassRateStatValue(1, 'completionRate')
-  );
-  semiQualityChartInstance = initQualityChart(
-    semiQualityChart,
-    "#52c41a",
-    getPassRateStatValue(1, 'passRate')
-  );
+  if (showSemiFinished.value) {
+    semiCompletionChartInstance = initQualityChart(
+      semiCompletionChart,
+      "#1890ff",
+      getPassRateStatValue(1, 'completionRate')
+    );
+    semiQualityChartInstance = initQualityChart(
+      semiQualityChart,
+      "#52c41a",
+      getPassRateStatValue(1, 'passRate')
+    );
+  }
   finalCompletionChartInstance = initQualityChart(
     finalCompletionChart,
     "#1890ff",
@@ -1161,7 +1166,7 @@
 
 .typeNum {
   height: 100%;
-  width: 33.33%;
+  flex: 1;
   display: flex;
   align-items: center;
   justify-content: center;
@@ -1297,11 +1302,12 @@
 
 .flex-center {
   justify-content: space-evenly;
+  gap: 15px;
 }
 
 .quality-card {
-  /* flex: 1; */
-  width: 32%;
+  flex: 1;
+  min-width: 0;
   /* height: 100px; */
   border-radius: 8px;
   padding: 12px;
diff --git a/src/views/salesManagement/deliveryLedger/index.vue b/src/views/salesManagement/deliveryLedger/index.vue
index 9907cf6..68ad099 100644
--- a/src/views/salesManagement/deliveryLedger/index.vue
+++ b/src/views/salesManagement/deliveryLedger/index.vue
@@ -22,7 +22,7 @@
             @change="handleQuery"
           />
         </el-form-item>
-        <el-form-item label="蹇�掑崟鍙凤細" v-if="false">
+        <el-form-item label="蹇�掑崟鍙凤細" >
           <el-input
             v-model="searchForm.expressNumber"
             placeholder="璇疯緭鍏�"
@@ -95,17 +95,16 @@
           label="鍙戣揣杞︾墝鍙�"
           prop="shippingCarNumber"
           show-overflow-tooltip
+          v-if="false"
         />
         <el-table-column
           label="蹇�掑叕鍙�"
           prop="expressCompany"
-          v-if="false"
           show-overflow-tooltip
         />
         <el-table-column
           label="蹇�掑崟鍙�"
           prop="expressNumber"
-          v-if="false"
           show-overflow-tooltip
         />
         <el-table-column
@@ -182,8 +181,8 @@
                 style="width: 100%"
                 @change="handleShippingTypeChange"
               >
-                <el-option label="璐ц溅" value="璐ц溅" />
-                <el-option label="蹇��" value="蹇��" v-if="false" />
+                <el-option label="蹇��" value="蹇��" />
+                <el-option label="璐ц溅" value="璐ц溅" v-if="false"/>
               </el-select>
             </el-form-item>
           </el-col>
@@ -205,7 +204,7 @@
         </el-row>
         <el-row :gutter="30">
           <el-col :span="24" v-if="form.type === '璐ц溅'">
-            <el-form-item label="鍙戣揣杞︾墝鍙凤細" prop="shippingCarNumber">
+            <el-form-item label="鍙戣揣杞︾墝鍙凤細" prop="shippingCarNumber" v-if="false">
               <el-input
                 v-model="form.shippingCarNumber"
                 placeholder="璇疯緭鍏ュ彂璐ц溅鐗屽彿"
@@ -223,7 +222,7 @@
             </el-form-item>
           </el-col>
         </el-row>
-        <el-row :gutter="30" v-if="false">
+        <el-row :gutter="30" >
           <el-col :span="24">
             <el-form-item label="蹇�掑崟鍙凤細" prop="expressNumber">
               <el-input
@@ -283,13 +282,13 @@
           <el-descriptions-item label="瀹℃牳鐘舵��">{{
             getApprovalStatusText(detailRow.status)
           }}</el-descriptions-item>
-          <el-descriptions-item label="鍙戣揣杞︾墝鍙�">{{
+          <el-descriptions-item label="鍙戣揣杞︾墝鍙�" v-if="false">{{
             detailRow.shippingCarNumber || "--"
           }}</el-descriptions-item>
-          <el-descriptions-item label="蹇�掑叕鍙�" v-if="false">{{
+          <el-descriptions-item label="蹇�掑叕鍙�" >{{
             detailRow.expressCompany || "--"
           }}</el-descriptions-item>
-          <el-descriptions-item label="蹇�掑崟鍙�" :span="2" v-if="false">{{
+          <el-descriptions-item label="蹇�掑崟鍙�" :span="2" >{{
             detailRow.expressNumber || "--"
           }}</el-descriptions-item>
           <el-descriptions-item label="鍑哄簱鍗曞彿" :span="2">{{
@@ -385,7 +384,7 @@
     customerName: "",
     specificationModel: "",
     productName: "",
-    type: "璐ц溅", // 璐ц溅, 蹇��
+    type: "蹇��", // 璐ц溅, 蹇��
     shippingDate: "",
     shippingCarNumber: "",
     expressCompany: "",
@@ -476,7 +475,7 @@
       id: row.id ?? null,
       salesContractNo: row.salesContractNo ?? "",
       customerName: row.customerName ?? "",
-      type: row.type || "璐ц溅",
+      type: row.type || "蹇��",
       shippingDate: row.shippingDate || getCurrentDate(),
       shippingCarNumber: row.shippingCarNumber ?? "",
       expressCompany: row.expressCompany ?? "",
diff --git a/src/views/salesManagement/receiptPaymentLedger/index.vue b/src/views/salesManagement/receiptPaymentLedger/index.vue
index 715cb9e..0bc5de3 100644
--- a/src/views/salesManagement/receiptPaymentLedger/index.vue
+++ b/src/views/salesManagement/receiptPaymentLedger/index.vue
@@ -126,6 +126,7 @@
     customewTransactionsDetails,
   } from "@/api/salesManagement/indicatorStats.js";
   import Pagination from "../../../components/PIMTable/Pagination.vue";
+  import { tableAmountFormatter, buildAmountSummaryFormat } from "@/utils/numberFormat";
   const { proxy } = getCurrentInstance();
   const tableData = ref([]);
   const receiptRecord = ref([]);
@@ -173,18 +174,19 @@
       }
     });
   };
-  const formattedNumber = (row, column, cellValue) => {
-    return cellValue ? parseFloat(cellValue).toFixed(2) : "0.00";
-  };
+  const formattedNumber = tableAmountFormatter;
   // 涓昏〃鍚堣鏂规硶
   const summarizeMainTable = param => {
     return proxy.summarizeTable(
       param,
       ["contractAmounts", "shippedAmount", "unshippedAmount"],
-      {
-        ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
-        futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
-      }
+      buildAmountSummaryFormat(
+        ["contractAmounts", "shippedAmount", "unshippedAmount"],
+        {
+          ticketsNum: { noDecimal: true },
+          futureTickets: { noDecimal: true },
+        }
+      )
     );
   };
   // 瀛愯〃鍚堣鏂规硶
@@ -192,10 +194,13 @@
     var summarizeTable = proxy.summarizeTable(
       param,
       ["contractAmount", "shippedAmount", "unshippedAmount"],
-      {
-        ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
-        futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
-      }
+      buildAmountSummaryFormat(
+        ["contractAmount", "shippedAmount", "unshippedAmount"],
+        {
+          ticketsNum: { noDecimal: true },
+          futureTickets: { noDecimal: true },
+        }
+      )
     );
     return summarizeTable;
   };
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index a82f141..23c7438 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -123,15 +123,14 @@
               </el-table-column>
               <el-table-column label="蹇�掑叕鍙�"
                                prop="expressCompany"
-                               v-if="false"
                                show-overflow-tooltip />
               <el-table-column label="蹇�掑崟鍙�"
                                prop="expressNumber"
-                               v-if="false"
                                show-overflow-tooltip />
               <el-table-column label="鍙戣揣杞︾墝"
                                minWidth="100px"
-                               align="center">
+                               align="center"
+                               v-if="false">
                 <template #default="scope">
                   <div>
                     <el-tag type="success"
@@ -250,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>
@@ -380,7 +379,7 @@
                          :reserve-keyword="false"
                          placeholder="璇烽�夋嫨"
                          clearable
-                         :disabled="operationType === 'view'"
+                         disabled
                          @change="changs">
                 <el-option v-for="item in userList"
                            :key="item.userId"
@@ -568,7 +567,7 @@
                          width="160"
                          align="right">
           <template #default="scope">
-            {{ Number(scope.row.totalAmount ?? 0).toFixed(2) }}
+            {{ formatDecimal(scope.row.totalAmount) }}
           </template>
         </el-table-column>
         <el-table-column fixed="right"
@@ -606,35 +605,22 @@
                :rules="productRules"
                ref="productFormRef">
         <el-row :gutter="30">
-          <el-col :span="24">
-            <el-form-item label="浜у搧澶х被锛�"
-                          prop="productCategory">
-              <el-tree-select v-model="productForm.productCategory"
-                              placeholder="璇烽�夋嫨"
-                              clearable
-                              filterable
-                              check-strictly
-                              @change="getModels"
-                              :data="productOptions"
-                              :render-after-expand="false"
-                              style="width: 100%" />
+          <el-col :span="12">
+            <el-form-item label="浜у搧澶х被锛�" prop="productCategory">
+              <el-input v-model="productForm.productCategory"
+                        placeholder="鐐瑰嚮閫夋嫨"
+                        readonly
+                        @click="showProductSelectDialog = true"
+                        style="cursor:pointer" />
             </el-form-item>
           </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="24">
-            <el-form-item label="瑙勬牸鍨嬪彿锛�"
-                          prop="productModelId">
-              <el-select v-model="productForm.productModelId"
-                         placeholder="璇烽�夋嫨"
-                         clearable
-                         @change="getProductModel"
-                         filterable>
-                <el-option v-for="item in modelOptions"
-                           :key="item.id"
-                           :label="item.model"
-                           :value="item.id" />
-              </el-select>
+          <el-col :span="12">
+            <el-form-item label="瑙勬牸鍨嬪彿锛�" prop="productModelId">
+              <el-input v-model="productForm.specificationModel"
+                        placeholder="鐐瑰嚮閫夋嫨"
+                        readonly
+                        @click="showProductSelectDialog = true"
+                        style="cursor:pointer" />
             </el-form-item>
           </el-col>
         </el-row>
@@ -666,11 +652,11 @@
           <el-col :span="12">
             <el-form-item label="鍚◣鍗曚环(鍏�)锛�"
                           prop="taxInclusiveUnitPrice">
-              <el-input-number :step="0.01"
+              <el-input-number :step="0.000001"
                                :min="0"
                                v-model="productForm.taxInclusiveUnitPrice"
                                style="width: 100%"
-                               :precision="2"
+                               :precision="6"
                                placeholder="璇疯緭鍏�"
                                clearable
                                @change="calculateFromUnitPrice" />
@@ -724,20 +710,44 @@
               </el-select>
             </el-form-item>
           </el-col>
-<!--          <el-col :span="12">
+          <el-col :span="12">
             <el-form-item label="鏄惁鐢熶骇锛�"
                           prop="isProduction">
-              <el-radio-group v-model="productForm.isProduction">
-                <el-radio label="鏄�"
-                          :value="true" />
-                <el-radio label="鍚�"
-                          :value="false" />
-              </el-radio-group>
+              <div style="display: flex; align-items: center; width: 100%;">
+                <el-radio-group v-model="productForm.isProduction">
+                  <el-radio label="鏄�"
+                            :value="true" />
+                  <el-radio label="鍚�"
+                            :value="false" />
+                </el-radio-group>
+                <div 
+                  v-if="currentStock !== null" 
+                  :style="{
+                    marginLeft: '20px',
+                    padding: '0 12px',
+                    height: '28px',
+                    lineHeight: '28px',
+                    borderRadius: '4px',
+                    fontSize: '13px',
+                    fontWeight: '500',
+                    display: 'inline-block',
+                    whiteSpace: 'nowrap',
+                    backgroundColor: (productForm.quantity || 0) > currentStock ? '#fff0f0' : '#f0f9eb',
+                    border: (productForm.quantity || 0) > currentStock ? '1px solid #ffcccc' : '1px solid #e1f3d8',
+                    color: (productForm.quantity || 0) > currentStock ? '#f56c6c' : '#67c23a'
+                  }"
+                >
+                  <span>褰撳墠搴撳瓨锛歿{ currentStock }} {{ productForm.unit || '' }}</span>
+                </div>
+              </div>
             </el-form-item>
-          </el-col>-->
+          </el-col>
         </el-row>
       </el-form>
     </FormDialog>
+    <ProductSelectDialog v-model="showProductSelectDialog"
+                         @confirm="handleProductSelect"
+                         single />
     <!-- 瀵煎叆寮圭獥 -->
     <FormDialog v-model="importUpload.open"
                 :title="importUpload.title"
@@ -813,7 +823,7 @@
                     <span class="label">鍙戣揣鏃ユ湡锛�</span>
                     <span class="value">{{ formatDate(item.createTime) }}</span>
                   </div>
-                  <div>
+                  <div v-if="false">
                     <span class="label">鍙戣揣杞︾墝鍙凤細</span>
                     <span class="value">{{ item.shippingCarNumber }}</span>
                   </div>
@@ -916,11 +926,11 @@
                          placeholder="璇烽�夋嫨鍙戣揣绫诲瀷"
                          style="width: 100%"
                          @change="handleDeliveryTypeChange">
-                <el-option label="璐ц溅"
-                           value="璐ц溅" />
                 <el-option label="蹇��"
-                           v-if="false"
                            value="蹇��" />
+                <el-option v-if="false"
+                           label="璐ц溅"
+                           value="璐ц溅" />
               </el-select>
             </el-form-item>
           </el-col>
@@ -952,7 +962,7 @@
           </el-col>
         </el-row>
         <el-row :gutter="30"
-                v-if="false">
+                >
           <el-col :span="24">
             <el-form-item label="蹇�掑崟鍙凤細"
                           prop="expressNumber">
@@ -1051,13 +1061,21 @@
     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";
   import dayjs from "dayjs";
   import FileUpload from "@/components/AttachmentUpload/file/index.vue";
+  import {
+    tableAmountFormatter,
+    formatDecimal,
+    buildAmountSummaryFormat,
+  } from "@/utils/numberFormat";
   import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
+  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
   import { getCurrentDate } from "@/utils/index.js";
   import { listCustomer } from "@/api/basicData/customer.js";
 
@@ -1086,6 +1104,7 @@
   const total = ref(0);
   const fileList = ref([]);
   const deliveryFileList = ref([]);
+  const showProductSelectDialog = ref(false);
 
   // 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
   const operationType = ref("");
@@ -1128,7 +1147,9 @@
   const currentId = ref("");
   const productFormData = reactive({
     productForm: {
+      productId: "",
       productCategory: "",
+      productModelId: "",
       specificationModel: "",
       unit: "",
       quantity: "",
@@ -1140,11 +1161,8 @@
       isProduction: false,
     },
     productRules: {
-      productCategory: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-      productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-      specificationModel: [
-        { required: true, message: "璇烽�夋嫨", trigger: "change" },
-      ],
+      productCategory: [{ required: true, message: "璇烽�夋嫨浜у搧澶х被", trigger: "change" }],
+      productModelId: [{ required: true, message: "璇烽�夋嫨瑙勬牸鍨嬪彿", trigger: "change" }],
       unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
       quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
       taxInclusiveUnitPrice: [
@@ -1197,6 +1215,27 @@
       item?.qualifiedQuantity ??
       item?.stockQuantity;
     return quantity ?? 0;
+  };
+  const currentStock = ref(null);
+  const fetchCurrentStock = async (productModelId) => {
+    if (!productModelId) {
+      currentStock.value = null;
+      return;
+    }
+    try {
+      const res = await getStockInventoryByModelId(productModelId);
+      const rawList = Array.isArray(res?.data)
+        ? res.data
+        : res?.data?.records || res?.data?.rows || [];
+      let total = 0;
+      rawList.forEach(item => {
+        total += Number(getDeliveryBatchQuantity(item) || 0);
+      });
+      currentStock.value = total;
+    } catch (e) {
+      console.error(e);
+      currentStock.value = 0;
+    }
   };
   const getCurrentDeliveryRowQuantity = () => {
     return Number(currentDeliveryRow.value?.noQuantity || 0);
@@ -1283,7 +1322,7 @@
       shippingCarNumber: "",
       expressCompany: "",
       expressNumber: "",
-      type: "璐ц溅", // 璐ц溅, 蹇��
+      type: "蹇��", // 璐ц溅, 蹇��
     },
     deliveryRules: {
       shippingCarNumber: [
@@ -1401,12 +1440,7 @@
       return productOptions.value;
     });
   };
-  const formattedNumber = (row, column, cellValue) => {
-    if (cellValue === undefined || cellValue === null || cellValue === "") {
-      return "0.00";
-    }
-    return parseFloat(cellValue).toFixed(2);
-  };
+  const formattedNumber = tableAmountFormatter;
   const findLedgerRecordByRow = row => {
     if (!row) return null;
     if (
@@ -1460,9 +1494,21 @@
     }
     return formattedNumber(row, column, cellValue);
   };
+  // ProductSelectDialog 閫変腑鍥炶皟
+  const handleProductSelect = (products) => {
+    if (!products || !products.length) return;
+    const p = products[0];
+    productForm.value.productId = p.productId;
+    productForm.value.productCategory = p.productName || '';
+    productForm.value.productModelId = p.id;
+    productForm.value.specificationModel = p.model || '';
+    productForm.value.unit = p.unit || '';
+    showProductSelectDialog.value = false;
+  };
   // 鑾峰彇tree瀛愭暟鎹�
   const getModels = value => {
     productForm.value.productCategory = findNodeById(productOptions.value, value);
+    currentStock.value = null;
     modelList({ id: value }).then(res => {
       modelOptions.value = res;
     });
@@ -1472,9 +1518,11 @@
     if (index !== -1) {
       productForm.value.specificationModel = modelOptions.value[index].model;
       productForm.value.unit = modelOptions.value[index].unit;
+      fetchCurrentStock(value);
     } else {
       productForm.value.specificationModel = null;
       productForm.value.unit = null;
+      currentStock.value = null;
     }
   };
   const findNodeById = (nodes, productId) => {
@@ -1569,11 +1617,15 @@
   };
   // 涓昏〃鍚堣鏂规硶
   const summarizeMainTable = param => {
-    return proxy.summarizeTable(param, [
-      "contractAmount",
-      "taxInclusiveTotalPrice",
-      "taxExclusiveTotalPrice",
-    ]);
+    return proxy.summarizeTable(
+      param,
+      ["contractAmount", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice"],
+      buildAmountSummaryFormat([
+        "contractAmount",
+        "taxInclusiveTotalPrice",
+        "taxExclusiveTotalPrice",
+      ])
+    );
   };
   // 瀛愯〃鍚堣鏂规硶
   const summarizeChildrenTable = (param, parentRow) => {
@@ -1595,11 +1647,19 @@
         return "";
       });
     }
-    return proxy.summarizeTable(param, [
-      "taxInclusiveUnitPrice",
-      "taxInclusiveTotalPrice",
-      "taxExclusiveTotalPrice",
-    ]);
+    return proxy.summarizeTable(
+      param,
+      [
+        "taxInclusiveUnitPrice",
+        "taxInclusiveTotalPrice",
+        "taxExclusiveTotalPrice",
+      ],
+      buildAmountSummaryFormat([
+        "taxInclusiveUnitPrice",
+        "taxInclusiveTotalPrice",
+        "taxExclusiveTotalPrice",
+      ])
+    );
   };
   // 鎵撳紑寮规
   const openForm = async (type, row) => {
@@ -1727,7 +1787,7 @@
       const quantity = Number(p.quantity ?? 0) || 0;
       const unitPrice = Number(p.unitPrice ?? 0) || 0;
       const taxRate = "13"; // 榛樿 13%锛屼究浜庣洿鎺ユ彁浜わ紙濡傞渶鍙湪浜у搧涓嚜琛屼慨鏀癸級
-      const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
+      const taxInclusiveTotalPrice = formatDecimal(unitPrice * quantity);
       const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(
         taxInclusiveTotalPrice,
         taxRate
@@ -1739,7 +1799,7 @@
         unit: p.unit || "",
         quantity: quantity,
         taxRate: taxRate,
-        taxInclusiveUnitPrice: unitPrice.toFixed(2),
+        taxInclusiveUnitPrice: formatDecimal(unitPrice),
         taxInclusiveTotalPrice: taxInclusiveTotalPrice,
         taxExclusiveTotalPrice: taxExclusiveTotalPrice,
         invoiceType: "澧炴櫘绁�",
@@ -1798,6 +1858,7 @@
 
     productOperationType.value = type;
     productForm.value = {};
+    currentStock.value = null;
     if (type === "add") {
       productForm.value.isProduction = true;
     }
@@ -1824,6 +1885,7 @@
           );
           if (currentModel) {
             productForm.value.productModelId = currentModel.id;
+            fetchCurrentStock(currentModel.id);
           }
         }
       } catch (e) {
@@ -2026,359 +2088,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();
@@ -2413,7 +2147,7 @@
     const total = products.reduce((sum, product) => {
       return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
     }, 0);
-    return total.toFixed(2);
+    return formatDecimal(total);
   };
 
   // 鐢ㄤ簬鎵撳嵃鐨勮绠楀嚱鏁�
@@ -2430,7 +2164,7 @@
     const total = products.reduce((sum, product) => {
       return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
     }, 0);
-    return total.toFixed(2);
+    return formatDecimal(total);
   };
 
   const mathNum = () => {
@@ -2471,7 +2205,7 @@
     isCalculating.value = true;
 
     // 璁$畻鍚◣鍗曚环 = 鍚◣鎬讳环 / 鏁伴噺
-    productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
+    productForm.value.taxInclusiveUnitPrice = formatDecimal(totalPrice / quantity);
 
     // 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
     if (productForm.value.taxRate) {
@@ -2508,12 +2242,12 @@
     // 鍏堣绠楀惈绋庢�讳环 = 涓嶅惈绋庢�讳环 / (1 - 绋庣巼/100)
     const taxRateDecimal = taxRate / 100;
     const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
-    productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
+    productForm.value.taxInclusiveTotalPrice = formatDecimal(inclusiveTotalPrice);
 
     // 璁$畻鍚◣鍗曚环 = 鍚◣鎬讳环 / 鏁伴噺
-    productForm.value.taxInclusiveUnitPrice = (
+    productForm.value.taxInclusiveUnitPrice = formatDecimal(
       inclusiveTotalPrice / quantity
-    ).toFixed(2);
+    );
 
     isCalculating.value = false;
   };
@@ -2536,7 +2270,7 @@
     isCalculating.value = true;
 
     // 璁$畻鍚◣鎬讳环
-    productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
+    productForm.value.taxInclusiveTotalPrice = formatDecimal(unitPrice * quantity);
 
     // 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
     if (productForm.value.taxRate) {
@@ -2568,7 +2302,7 @@
     isCalculating.value = true;
 
     // 璁$畻鍚◣鎬讳环
-    productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
+    productForm.value.taxInclusiveTotalPrice = formatDecimal(unitPrice * quantity);
 
     // 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
     if (productForm.value.taxRate) {
@@ -2709,6 +2443,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 => {
     // 妫�鏌ユ槸鍚﹀彲浠ュ彂璐�
@@ -2727,7 +2483,7 @@
       shippingCarNumber: "",
       expressCompany: "",
       expressNumber: "",
-      type: "璐ц溅",
+      type: "蹇��",
       batchNo: [],
       batchNoList,
     };
diff --git a/src/views/salesManagement/salesQuotation/index.vue b/src/views/salesManagement/salesQuotation/index.vue
index fce764f..455f686 100644
--- a/src/views/salesManagement/salesQuotation/index.vue
+++ b/src/views/salesManagement/salesQuotation/index.vue
@@ -65,7 +65,7 @@
         </el-table-column>
         <el-table-column prop="totalAmount" label="鎶ヤ环閲戦" width="120">
           <template #default="scope">
-            楼{{ scope.row.totalAmount.toFixed(2) }}
+            楼{{ formatDecimal(scope.row.totalAmount) }}
           </template>
         </el-table-column>
         <el-table-column label="鎿嶄綔" width="200" fixed="right" align="center">
@@ -215,7 +215,7 @@
             <el-table-column prop="unitPrice" label="鍗曚环">
               <template #default="scope">
                 <el-form-item :prop="`products.${scope.$index}.unitPrice`" class="product-table-form-item">
-                  <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%" />
+                  <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="6" :step="0.000001" style="width: 100%" />
                 </el-form-item>
               </template>
             </el-table-column>
@@ -267,7 +267,7 @@
 <!--          <el-tag :type="getStatusType(currentQuotation.status)">{{ currentQuotation.status }}</el-tag>-->
 <!--        </el-descriptions-item>-->
         <el-descriptions-item label="鎶ヤ环鎬婚" :span="2">
-          <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">楼{{ currentQuotation.totalAmount?.toFixed(2) }}</span>
+          <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">楼{{ formatDecimal(currentQuotation.totalAmount) }}</span>
         </el-descriptions-item>
       </el-descriptions>
 
@@ -279,7 +279,7 @@
           <el-table-column prop="unit" label="鍗曚綅" />
           <el-table-column prop="unitPrice" label="鍗曚环">
             <template #default="scope">
-              楼{{ scope.row.unitPrice.toFixed(2) }}
+              楼{{ formatDecimal(scope.row.unitPrice) }}
             </template>
           </el-table-column>
         </el-table>
@@ -303,6 +303,7 @@
 import {modelList, productTreeList} from "@/api/basicData/product.js";
 import {listCustomer} from "@/api/basicData/customer.js";
 import { userListNoPage } from "@/api/system/user.js";
+import { formatDecimal, roundAmount } from '@/utils/numberFormat';
 
 // 鍝嶅簲寮忔暟鎹�
 const loading = ref(false)
@@ -346,6 +347,7 @@
 
 const baseRules = {
   customer: [{ required: true, message: '璇烽�夋嫨瀹㈡埛', trigger: 'change' }],
+  customerId: [{ required: true, message: '璇烽�夋嫨瀹㈡埛', trigger: 'change' }],
   salesperson: [{ required: true, message: '璇烽�夋嫨涓氬姟鍛�', trigger: 'change' }],
   quotationDate: [{ required: true, message: '璇烽�夋嫨鎶ヤ环鏃ユ湡', trigger: 'change' }],
   validDate: [{ required: true, message: '璇烽�夋嫨鏈夋晥鏈�', trigger: 'change' }],
@@ -686,11 +688,11 @@
         return
       }
 
-      // 璁$畻鎵�鏈変骇鍝佺殑鍗曚环鎬诲拰
-      form.totalAmount = form.products.reduce((sum, product) => {
+      // 璁$畻鎵�鏈変骇鍝佺殑鍗曚环鎬诲拰锛屼繚鐣欏叚浣嶅皬鏁�
+      form.totalAmount = roundAmount(form.products.reduce((sum, product) => {
         const price = Number(product.unitPrice) || 0
         return sum + price
-      }, 0)
+      }, 0))
 
       form.customer = customerOption.value.find(item => item.id === form.customerId)?.customerName || ''
       if (isEdit.value) {

--
Gitblit v1.9.3