feat(金额显示): 统一金额单位为万元并优化产品内容处理

refactor(产品内容): 提取产品内容处理逻辑到独立工具类
style(合同表单): 调整技术附件和商务附件顺序
This commit is contained in:
2026-05-11 10:38:29 +08:00
parent 969dad7501
commit 20cefe115d
9 changed files with 458 additions and 362 deletions

View File

@@ -0,0 +1,315 @@
/**
* 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} content - 产品内容对象
* @returns {Object} 更新后的产品内容对象
*/
export function recalculateTotals(content) {
if (!content) {
return { ...DEFAULT_PRODUCT_CONTENT };
}
const products = (content.products || []).map(product => ({
...product,
taxTotal: calculateProductTaxTotal(product)
}));
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 {};
return {
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),
remark: orderItem.remark || ''
};
}
/**
* 将订单明细列表转换为产品内容对象
* @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,
recalculateTotals,
convertOrderItemToProduct,
convertOrderItemsToContent,
addProduct,
removeProduct,
updateProduct,
convertToChinese,
getDefault: getDefaultProductContent
};