// 商品价格计算相关 import { times, plus, minus, divide } from 'number-precision' // 价格格式化 export function formatPrice(value, needSymbol = true) { // 不能在最后一步显示之前格式化999 111, 否则可能出现单价1买999个得到999价格被格式化成poa的情况 if (value === 0) { return needSymbol ? '$0.00' : '0.00' } else { return needSymbol ? `$${value}` : `${value}` } } // 简单的乘法换算 function multiply(value, ratio = 100) { return parseFloat((value * ratio).toPrecision(12)) } // 小数处理, 四舍五入, 为toFixed做准备 export function round(number, ratio = 100) { return Math.round(multiply(number, ratio)) / ratio } /** * 将字符串简单转换成数字, 并可数倍转换. 注意, 这个适用于整数倍(商品件数), 非整数的不能用这个处理小数. * @param {*} value 目标值 * @param {*} ratio 倍数, 默认1 * @returns number | origin, 转换后的数字, 无法转成数字的返回原值 */ export function transformNumber(value, ratio = 1) { const v = Number(value) if (Number.isNaN(v)) { return value } else { // 保留两位小数 return divide(Math.trunc(times(times(v, ratio), 100)), 100) } } // unit 单个商品的, 没有乘数量. 因为含有111 999 这些极端值, 没有格式化输出之前直接乘回出问题的. export const getUnit = function (buyNum, index, attributeList, basePriceList) { // 算出购买数量位于 价格阶梯的 哪个区间 const candidate = Object.entries(attributeList).filter( item => buyNum >= item[1] ) let key = 'website_qty1' if (candidate.length) { key = candidate.pop()[0] } return transformNumber(basePriceList[index][key], buyNum) } // 打印和附加价格 的steup之和. 商品基础价格现在没有setup了. export const getSetup = function (buyNum, form, additionList) { const sum = Object.entries(form).reduce((total, current) => { let s = 0 // 打印服务表单的数据跟附加服务表单的数据结构不一致, 以数字id字符串键名的是打印服务的数据 if (/\d+/.test(current[0]) && current[1].enable) { const temp = current[1].colorForm.filter( i => i.id === current[1].printService ) let colorNumber = 1 if (temp.length) { colorNumber = temp[0].colorNumber } const decoration = current[1].decorationList.filter( i => i.id === current[1].printService ) let setup = 0 if (decoration.length) { setup = plus( Number(decoration[0].website_setup), times(Number(decoration[0].supplier_setup), colorNumber - 1) ) } s = plus(s, setup) } else if (current[1].length) { // 附加服务的表单数据, 有值说明该项有选中了附加服务 const addition = additionList[current[0]].filter(addition => current[1].includes(addition.id) ) if (addition.length) { const temp = addition.reduce((t, c) => { let value = Number(c.website_setup) if ([5, 6].includes(c.website_setup_id)) { // 5是poa, 6是waived. 这种情况一般setup是留空的, 不留空大概是异常数据, 重置0保险一点. value = 0 } return plus(t, Number.isNaN(value) ? 0 : value) }, 0) s = plus(s, temp) } } total = plus(total, s) return total }, 0) return transformNumber(sum) } // 打印价格的 阶梯基础价*购买数量. export const getPrint = function (buyNum, form, attributeList) { // 算出购买数量位于 价格阶梯的 哪个区间 const candidate = Object.entries(attributeList).filter( item => buyNum >= item[1] ) const key = `website_qty${candidate.length}` const key2 = `supplier_qty${candidate.length}` // 如果其中一项为POA, 则‘和’都是POA const result = Object.entries(form).reduce((total, current) => { if (total === 'POA') { return total } let sum = 0 if (/\d+/.test(current[0]) && current[1].enable) { const temp = current[1].colorForm.filter( i => i.id === current[1].printService ) let colorNumber = 1 if (temp.length) { colorNumber = temp[0].colorNumber } const decoration = current[1].decorationList.filter( i => i.id === current[1].printService ) // 打印价格的基础价. 其中数字111(代表'-') 和999(代表'POA') const p1 = transformNumber(decoration[0][key]) if (p1 === 999 || p1 === 111 || typeof p1 !== 'number') { return 'POA' } // 打印价格的附加价 const p2 = transformNumber(decoration[0][key2]) if (p2 === 999 || p2 === 111 || typeof p2 !== 'number') { return 'POA' } let price = 0 if (decoration.length) { price = plus(p1, times(p2, colorNumber - 1)) } sum = plus(sum, price) } total = plus(total, sum) return total }, 0) return transformNumber(result, buyNum) } // 附加服务除了packing之外的总价 export const getAddon = function (buyNum, form, attributeList, additionList) { // 算出购买数量位于 价格阶梯的 哪个区间 const candidate = Object.entries(attributeList).filter( item => buyNum >= item[1] ) const key = `website_qty${candidate.length}` const result = Object.entries(form).reduce((total, current) => { if (total === 'POA') { return total } let sum = 0 if (!/\d+/.test(current[0]) && current[0] !== 'packaging') { sum = additionList[current[0]] .filter(item => current[1].includes(item.id)) .reduce((t, c) => { if (t === 'POA') { return t } let temp = transformNumber(c[key]) if (temp === 999 || temp === 111 || typeof temp !== 'number') { temp = 0 return 'POA' } t = plus(t, temp) return t }, 0) } return plus(total, sum) }, 0) return transformNumber(result, buyNum) } // 附加服务中 packing 的价格 export const getPackaging = function ( buyNum, form, attributeList, additionList ) { // 算出购买数量位于 价格阶梯的 哪个区间 const candidate = Object.entries(attributeList).filter( item => buyNum >= item[1] ) const key = `website_qty${candidate.length}` const result = Object.entries(form).reduce((total, current) => { if (total === 'POA') { return total } let sum = 0 if (!/\d+/.test(current[0]) && current[0] === 'packaging') { sum = additionList[current[0]] .filter(item => current[1].includes(item.id)) .reduce((t, c) => { if (t === 'POA') { return t } let temp = transformNumber(c[key]) if (temp === 999 || temp === 111 || typeof temp !== 'number') { temp = 0 return 'POA' } t = plus(t, temp) return t }, 0) } return plus(total, sum) }, 0) return transformNumber(result, buyNum) } // 运费计算. 从product页面抄过来的逻辑. +号是隐式类型转换 export const getFright = function (buyNum, config, freight, weight, ratio = 1) { // 单独批次数量的总重 const totalWeight = Math.ceil(times(+weight.unit_w_local, buyNum)) const expressFactor = plus( 1, divide(plus(+config.express_freight, +config.fuel), 100) ) const AAEFactor = plus( 1, divide(plus(+config.bag_freight, +config.fuel), 100) ) let frightCost = 0 if (freight.type === 1) { if (totalWeight > 20) { const a1 = minus(totalWeight, 20) const a2 = times(a1, +freight.basic) const a3 = plus(+freight.pickup, a2) frightCost = times(a3, expressFactor) } else { frightCost = times(+freight.pickup, expressFactor) } } else if (freight.type === 2) { const a1 = times(totalWeight, +freight.basic) const a2 = plus(+freight.pickup, a1) frightCost = times(a2, AAEFactor) } else { frightCost = 0 } return transformNumber(frightCost, ratio) }