feat(contract): 新增合同打印功能,重构产品金额计算逻辑

1. 新增合同打印预览功能,支持A4格式打印,包含合同完整信息和产品明细
2. 新增多个产品金额计算工具函数,统一管理产品税额、无税单价等字段计算
3. 重构产品内容组件,新增税率除数、无税单价、税额列,实现字段联动自动计算
4. 新增合同logo静态资源,优化表格布局和样式
This commit is contained in:
2026-06-02 08:56:31 +08:00
parent a425a9052a
commit 6b8eac4139
6 changed files with 799 additions and 531 deletions

View File

@@ -89,6 +89,122 @@ export function calculateProductTaxTotal(product) {
return quantity * taxPrice;
}
/**
* 计算无税单价 = 含税单价 / 税率除数
* @param {Object} product - 产品对象
* @returns {number} 无税单价
*/
export function calculateProductNoTaxPrice(product) {
if (!product) return 0;
const taxPrice = parseFloat(product.taxPrice) || 0;
const taxDivisor = parseFloat(product.taxDivisor) || 1.13;
return taxPrice / taxDivisor;
}
/**
* 计算无税总额 = 数量 * 无税单价
* @param {Object} product - 产品对象
* @returns {number} 无税总额
*/
export function calculateProductNoTaxTotal(product) {
if (!product) return 0;
const quantity = parseFloat(product.quantity) || 0;
const noTaxPrice = parseFloat(product.noTaxPrice) || 0;
return quantity * noTaxPrice;
}
/**
* 计算税额 = 含税总额 - 无税总额
* @param {Object} product - 产品对象
* @returns {number} 税额
*/
export function calculateProductTaxAmount(product) {
if (!product) return 0;
const taxTotal = parseFloat(product.taxTotal) || 0;
const noTaxTotal = parseFloat(product.noTaxTotal) || 0;
return taxTotal - noTaxTotal;
}
/**
* 根据变更字段重新计算产品所有金额字段
* @param {Object} product - 产品对象
* @param {string} changedField - 变更的字段名
* @returns {Object} 更新后的产品对象
*/
export function calculateProductFields(product, changedField = 'quantity') {
if (!product) return product;
const quantity = parseFloat(product.quantity) || 0;
let taxPrice = parseFloat(product.taxPrice) || 0;
let taxDivisor = parseFloat(product.taxDivisor) || 1.13;
let noTaxPrice, noTaxTotal, taxTotal, taxAmount;
switch (changedField) {
case 'quantity':
case 'taxPrice':
taxTotal = quantity * taxPrice;
noTaxPrice = taxPrice / taxDivisor;
noTaxTotal = quantity * noTaxPrice;
taxAmount = taxTotal - noTaxTotal;
break;
case 'taxDivisor':
noTaxPrice = taxPrice / taxDivisor;
taxTotal = quantity * taxPrice;
noTaxTotal = quantity * noTaxPrice;
taxAmount = taxTotal - noTaxTotal;
break;
case 'noTaxPrice':
noTaxPrice = parseFloat(product.noTaxPrice) || 0;
taxPrice = noTaxPrice * taxDivisor;
taxTotal = quantity * taxPrice;
noTaxTotal = quantity * noTaxPrice;
taxAmount = taxTotal - noTaxTotal;
break;
case 'noTaxTotal':
noTaxTotal = parseFloat(product.noTaxTotal) || 0;
noTaxPrice = quantity > 0 ? noTaxTotal / quantity : 0;
taxPrice = noTaxPrice * taxDivisor;
taxTotal = quantity * taxPrice;
taxAmount = taxTotal - noTaxTotal;
break;
case 'taxTotal':
taxTotal = parseFloat(product.taxTotal) || 0;
taxPrice = quantity > 0 ? taxTotal / quantity : 0;
noTaxPrice = taxPrice / taxDivisor;
noTaxTotal = quantity * noTaxPrice;
taxAmount = taxTotal - noTaxTotal;
break;
case 'taxAmount':
taxAmount = parseFloat(product.taxAmount) || 0;
taxTotal = quantity * taxPrice;
noTaxTotal = taxTotal - taxAmount;
noTaxPrice = quantity > 0 ? noTaxTotal / quantity : 0;
taxPrice = quantity > 0 ? noTaxPrice * taxDivisor : 0;
taxTotal = quantity * taxPrice;
noTaxTotal = quantity * noTaxPrice;
taxAmount = taxTotal - noTaxTotal;
break;
default:
taxTotal = quantity * taxPrice;
noTaxPrice = taxPrice / taxDivisor;
noTaxTotal = quantity * noTaxPrice;
taxAmount = taxTotal - noTaxTotal;
}
const round2 = (v) => Math.round(v * 100) / 100;
return {
...product,
quantity,
taxPrice: round2(taxPrice),
noTaxPrice: round2(noTaxPrice),
taxTotal: round2(taxTotal),
noTaxTotal: round2(noTaxTotal),
taxAmount: round2(taxAmount),
taxDivisor
};
}
/**
* 重新计算所有产品的含税总额和总计
* @param {Object} content - 产品内容对象
@@ -99,10 +215,9 @@ export function recalculateTotals(content) {
return { ...DEFAULT_PRODUCT_CONTENT };
}
const products = (content.products || []).map(product => ({
...product,
taxTotal: calculateProductTaxTotal(product)
}));
const products = (content.products || []).map(product =>
calculateProductFields(product, 'quantity')
);
const totalQuantity = calculateTotalQuantity(products);
const totalTaxTotal = calculateTotalTaxTotal(products);
@@ -124,15 +239,16 @@ export function recalculateTotals(content) {
export function convertOrderItemToProduct(orderItem) {
if (!orderItem) return {};
return {
const product = {
spec: orderItem.finishedProductSpec || '',
material: orderItem.material || '',
quantity: parseFloat(orderItem.weight) || 0,
taxPrice: parseFloat(orderItem.contractPrice) || 0,
noTaxPrice: parseFloat(orderItem.itemAmount) || 0,
taxTotal: (parseFloat(orderItem.contractPrice) || 0) * (parseFloat(orderItem.weight) || 0),
taxDivisor: parseFloat(orderItem.taxDivisor) || 1.13,
remark: orderItem.remark || ''
};
return calculateProductFields(product, 'quantity');
}
/**
@@ -304,6 +420,10 @@ export default {
calculateTotalQuantity,
calculateTotalTaxTotal,
calculateProductTaxTotal,
calculateProductNoTaxPrice,
calculateProductNoTaxTotal,
calculateProductTaxAmount,
calculateProductFields,
recalculateTotals,
convertOrderItemToProduct,
convertOrderItemsToContent,