/** * productContent 工具类 * 用于处理合同产品内容的各种操作 */ // 默认产品内容结构 const DEFAULT_PRODUCT_CONTENT = { products: [], productName: '', remark: '', totalQuantity: 0, totalTaxTotal: 0, totalAmountInWords: '零元整' }; /** * 解析 productContent JSON 字符串 * @param {string} jsonStr - JSON 字符串 * @returns {Object} 解析后的对象 */ export function parseProductContent(jsonStr) { if (!jsonStr) { return { ...DEFAULT_PRODUCT_CONTENT }; } try { const data = JSON.parse(jsonStr); return { ...DEFAULT_PRODUCT_CONTENT, ...data, products: data.products || [] }; } catch (error) { console.error('解析 productContent 失败:', error); return { ...DEFAULT_PRODUCT_CONTENT }; } } /** * 将对象转换为 productContent JSON 字符串 * @param {Object} data - 产品内容对象 * @returns {string} JSON 字符串 */ export function stringifyProductContent(data) { const content = { ...DEFAULT_PRODUCT_CONTENT, ...data }; return JSON.stringify(content, null, 2); } /** * 计算产品列表的总数量 * @param {Array} products - 产品列表 * @returns {number} 总数量 */ export function calculateTotalQuantity(products) { if (!products || !Array.isArray(products)) { return 0; } return products.reduce((total, item) => { return total + (parseFloat(item.quantity) || 0); }, 0); } /** * 计算产品列表的含税总额 * @param {Array} products - 产品列表 * @returns {number} 含税总额 */ export function calculateTotalTaxTotal(products) { if (!products || !Array.isArray(products)) { return 0; } return products.reduce((total, item) => { return total + (parseFloat(item.taxTotal) || 0); }, 0); } /** * 计算单个产品的含税总额 * @param {Object} product - 产品对象 * @returns {number} 含税总额 */ export function calculateProductTaxTotal(product) { if (!product) return 0; const quantity = parseFloat(product.quantity) || 0; const taxPrice = parseFloat(product.taxPrice) || 0; 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 - 产品内容对象 * @returns {Object} 更新后的产品内容对象 */ export function recalculateTotals(content) { if (!content) { return { ...DEFAULT_PRODUCT_CONTENT }; } const products = (content.products || []).map(product => calculateProductFields(product, 'quantity') ); const totalQuantity = calculateTotalQuantity(products); const totalTaxTotal = calculateTotalTaxTotal(products); return { ...content, products, totalQuantity, totalTaxTotal, totalAmountInWords: convertToChinese(totalTaxTotal) }; } /** * 将订单明细项转换为产品项 * @param {Object} orderItem - 订单明细对象 * @returns {Object} 产品项对象 */ export function convertOrderItemToProduct(orderItem) { if (!orderItem) return {}; const product = { spec: orderItem.finishedProductSpec || '', material: orderItem.material || '', quantity: parseFloat(orderItem.weight) || 0, taxPrice: parseFloat(orderItem.contractPrice) || 0, taxDivisor: parseFloat(orderItem.taxDivisor) || 1.13, remark: orderItem.remark || '' }; return calculateProductFields(product, 'quantity'); } /** * 将订单明细列表转换为产品内容对象 * @param {Array} orderItems - 订单明细列表 * @param {string} productName - 产品名称 * @param {string} remark - 备注 * @returns {Object} 产品内容对象 */ export function convertOrderItemsToContent(orderItems, productName = '', remark = '') { const products = (orderItems || []).map(convertOrderItemToProduct); const content = { products, productName, remark }; return recalculateTotals(content); } /** * 添加产品项到产品内容 * @param {Object} content - 产品内容对象 * @param {Object} product - 要添加的产品项 * @returns {Object} 更新后的产品内容对象 */ export function addProduct(content, product) { if (!content) content = { ...DEFAULT_PRODUCT_CONTENT }; if (!product) return content; const newContent = { ...content, products: [...(content.products || []), { ...product }] }; return recalculateTotals(newContent); } /** * 删除产品内容中的产品项 * @param {Object} content - 产品内容对象 * @param {number} index - 要删除的索引 * @returns {Object} 更新后的产品内容对象 */ export function removeProduct(content, index) { if (!content || !content.products || index < 0 || index >= content.products.length) { return content; } const newProducts = [...content.products]; newProducts.splice(index, 1); const newContent = { ...content, products: newProducts }; return recalculateTotals(newContent); } /** * 更新产品内容中的产品项 * @param {Object} content - 产品内容对象 * @param {number} index - 要更新的索引 * @param {Object} product - 新的产品项数据 * @returns {Object} 更新后的产品内容对象 */ export function updateProduct(content, index, product) { if (!content || !content.products || index < 0 || index >= content.products.length || !product) { return content; } const newProducts = [...content.products]; newProducts[index] = { ...newProducts[index], ...product }; const newContent = { ...content, products: newProducts }; return recalculateTotals(newContent); } /** * 数字金额转中文大写 * @param {number} amount - 数字金额 * @returns {string} 中文大写金额 */ export function convertToChinese(amount) { if (amount === 0) return '零元整'; const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']; const units = ['', '拾', '佰', '仟']; const bigUnits = ['', '万', '亿']; let integerPart = Math.floor(amount); let decimalPart = Math.round((amount - integerPart) * 100); let result = ''; // 处理整数部分 if (integerPart > 0) { let unitIndex = 0; let bigUnitIndex = 0; while (integerPart > 0) { let section = integerPart % 10000; if (section > 0) { let sectionResult = ''; let temp = section; let unitIndexInSection = 0; while (temp > 0) { let digit = temp % 10; if (digit > 0) { sectionResult = digits[digit] + units[unitIndexInSection] + sectionResult; } else { // 避免连续的零 if (sectionResult && !sectionResult.startsWith('零')) { sectionResult = '零' + sectionResult; } } temp = Math.floor(temp / 10); unitIndexInSection++; } result = sectionResult + bigUnits[bigUnitIndex] + result; } integerPart = Math.floor(integerPart / 10000); bigUnitIndex++; } result += '元'; } // 处理小数部分 if (decimalPart === 0) { result += '整'; } else { const jiao = Math.floor(decimalPart / 10); const fen = decimalPart % 10; if (jiao > 0) { result += digits[jiao] + '角'; } if (fen > 0) { result += digits[fen] + '分'; } } return result; } /** * 获取默认产品内容对象 * @returns {Object} 默认产品内容 */ export function getDefaultProductContent() { return { ...DEFAULT_PRODUCT_CONTENT }; } /** * ProductContent 工具类 */ export default { parse: parseProductContent, stringify: stringifyProductContent, calculateTotalQuantity, calculateTotalTaxTotal, calculateProductTaxTotal, calculateProductNoTaxPrice, calculateProductNoTaxTotal, calculateProductTaxAmount, calculateProductFields, recalculateTotals, convertOrderItemToProduct, convertOrderItemsToContent, addProduct, removeProduct, updateProduct, convertToChinese, getDefault: getDefaultProductContent };