feat(calc): 引入高精度计算工具并替换原有精度处理逻辑
- 新增基于 decimal.js 的高精度计算工具类 - 在 main.js 中全局挂载 $calc 对象供所有组件使用 - 替换项目中多处使用的 preciseCurrencyRound 方法 - 统一处理加减乘除及四舍五入运算,提升数值计算准确性 - 移除旧的 ruoyi.js 中的精确货币舍入函数 - 更新 package.json 添加 decimal.js 依赖项dev_1.0.0
parent
fb00d486e0
commit
7afab80e0c
|
|
@ -48,7 +48,8 @@
|
|||
"vue-cropper": "0.5.5",
|
||||
"vue-router": "3.4.9",
|
||||
"vuedraggable": "2.24.3",
|
||||
"vuex": "3.6.0"
|
||||
"vuex": "3.6.0",
|
||||
"decimal.js": "10.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "4.4.6",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import Cookies from 'js-cookie'
|
|||
|
||||
import Element from 'element-ui'
|
||||
import './assets/styles/element-variables.scss'
|
||||
import * as Calc from '@/utils/calc'
|
||||
|
||||
Vue.prototype.$calc = Calc // 全局挂载
|
||||
import '@/assets/styles/index.scss' // global css
|
||||
import '@/assets/styles/ruoyi.scss' // ruoyi css
|
||||
import App from './App'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import Decimal from 'decimal.js'
|
||||
|
||||
// 默认保留小数位(你可根据业务修改,比如金额一般 2 位)
|
||||
const DEFAULT_DP = 2
|
||||
|
||||
// 内部方法:统一转换 Decimal,避免 null/undefined 报错
|
||||
function D(n) {
|
||||
return new Decimal(n || 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* 保留小数位 & 四舍五入
|
||||
* @param value 数值
|
||||
* @param dp 保留位数(默认 DEFAULT_DP)
|
||||
*/
|
||||
export function toFixed(value, dp = DEFAULT_DP) {
|
||||
return D(value).toDecimalPlaces(dp).toNumber()
|
||||
}
|
||||
|
||||
// 加法
|
||||
export function add(a, b, dp = DEFAULT_DP) {
|
||||
return D(a).plus(b).toDecimalPlaces(dp).toNumber()
|
||||
}
|
||||
|
||||
// 减法
|
||||
export function sub(a, b, dp = DEFAULT_DP) {
|
||||
return D(a).minus(b).toDecimalPlaces(dp).toNumber()
|
||||
}
|
||||
|
||||
// 乘法
|
||||
export function mul(a, b, dp = DEFAULT_DP) {
|
||||
return D(a).times(b).toDecimalPlaces(dp).toNumber()
|
||||
}
|
||||
|
||||
// 除法
|
||||
export function div(a, b, dp = DEFAULT_DP) {
|
||||
return D(a).div(b || 1).toDecimalPlaces(dp).toNumber()
|
||||
}
|
||||
|
|
@ -226,9 +226,4 @@ export function getNormalPath(p) {
|
|||
export function blobValidate(data) {
|
||||
return data.type !== 'application/json'
|
||||
}
|
||||
export function preciseCurrencyRound(amount,decimalPlace) {
|
||||
if (decimalPlace){
|
||||
return Number(Math.round(amount + `e${decimalPlace}`) + `e-${decimalPlace}`);
|
||||
}
|
||||
return Number(Math.round(amount + 'e2') + 'e-2');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -206,7 +206,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {preciseCurrencyRound} from "@/utils/ruoyi";
|
||||
|
||||
export default {
|
||||
name: "ConfigInfo",
|
||||
|
|
@ -302,8 +301,9 @@ export default {
|
|||
if (discount === 1) {
|
||||
return product.allPrice;
|
||||
}
|
||||
const roundedDiscountedUnitPrice = preciseCurrencyRound( product.price * discount);
|
||||
return roundedDiscountedUnitPrice * product.quantity;
|
||||
const roundedDiscountedUnitPrice = this.$calc.mul( product.price,discount);
|
||||
|
||||
return this.$calc.mul(roundedDiscountedUnitPrice , product.quantity);
|
||||
},
|
||||
calculateTotal(productList, discount) {
|
||||
if (!productList) return 0;
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@
|
|||
|
||||
<script>
|
||||
import SelectProduct from '@/views/system/product/selectProduct.vue';
|
||||
import {preciseCurrencyRound} from "@/utils/ruoyi";
|
||||
|
||||
|
||||
export default {
|
||||
name: 'ProductConfig',
|
||||
|
|
@ -322,8 +322,8 @@ export default {
|
|||
quantity: item.quantity || 0,
|
||||
taxRate: item.taxRate || 13,
|
||||
cataloguePriceFormat: this.formatAmount(item.cataloguePrice),
|
||||
guidanceDiscountFormat: item.guidanceDiscount ? preciseCurrencyRound(item.guidanceDiscount * 100, 2) : '',
|
||||
discountFormat: item.discount ? preciseCurrencyRound(item.discount * 100, 2) : '',
|
||||
guidanceDiscountFormat: item.guidanceDiscount ? this.$calc.mul(item.guidanceDiscount, 100) : '',
|
||||
discountFormat: item.discount ? this.$calc.mul(item.discount, 100) : '',
|
||||
priceFormat: this.formatAmount(item.price),
|
||||
allPriceFormat: this.formatAmount(item.allPrice),
|
||||
catalogueAllPriceFormat: this.formatAmount(item.catalogueAllPrice)
|
||||
|
|
@ -413,9 +413,9 @@ export default {
|
|||
row.cataloguePrice = product.cataloguePrice;
|
||||
row.cataloguePriceFormat = this.formatAmount(product.cataloguePrice);
|
||||
row.guidanceDiscount = product.guidanceDiscount;
|
||||
row.guidanceDiscountFormat = product.guidanceDiscount ? preciseCurrencyRound(product.guidanceDiscount * 100, 2) : '';
|
||||
row.guidanceDiscountFormat = product.guidanceDiscount ? this.$calc.mul(product.guidanceDiscount, 100) : '';
|
||||
row.discount = product.guidanceDiscount || 0;
|
||||
row.discountFormat = product.guidanceDiscount ? preciseCurrencyRound(product.guidanceDiscount * 100, 2) : '';
|
||||
row.discountFormat = product.guidanceDiscount ? this.$calc.mul(product.guidanceDiscount, 100) : '';
|
||||
|
||||
this.calculateRow(row);
|
||||
this.emitChange();
|
||||
|
|
@ -439,14 +439,13 @@ export default {
|
|||
if (isNaN(price)) {
|
||||
price = 0;
|
||||
}
|
||||
row.price = preciseCurrencyRound(price, 2);
|
||||
row.price = this.$calc.toFixed(price);
|
||||
row.priceFormat = this.formatAmount(row.price);
|
||||
|
||||
// 如果目录单价存在且不为0,则重新计算折扣
|
||||
if (row.cataloguePrice && row.cataloguePrice > 0) {
|
||||
const discount = row.price / row.cataloguePrice;
|
||||
row.discount = preciseCurrencyRound(discount, 4); // 精度为4位
|
||||
row.discountFormat = preciseCurrencyRound(discount * 100, 2); // 显示为百分比,保留2位小数
|
||||
row.discount = this.$calc.div(row.price,row.cataloguePrice,4) // 精度为4位
|
||||
row.discountFormat = this.$calc.mul(row.discount ,100, 2); // 显示为百分比,保留2位小数
|
||||
} else {
|
||||
// 如果目录单价为0,则折扣也为0
|
||||
row.discount = 0;
|
||||
|
|
@ -454,9 +453,9 @@ export default {
|
|||
}
|
||||
|
||||
// 重新计算总价和目录总价
|
||||
row.allPrice = preciseCurrencyRound(row.price * row.quantity, 2);
|
||||
row.allPrice = this.$calc.mul(row.price , row.quantity, 2);
|
||||
row.allPriceFormat = this.formatAmount(row.allPrice);
|
||||
row.catalogueAllPrice = preciseCurrencyRound(row.cataloguePrice * row.quantity, 2);
|
||||
row.catalogueAllPrice = this.$calc.mul(row.cataloguePrice ,row.quantity, 2);
|
||||
row.catalogueAllPriceFormat = this.formatAmount(row.catalogueAllPrice);
|
||||
|
||||
this.emitChange();
|
||||
|
|
@ -466,17 +465,17 @@ export default {
|
|||
return;
|
||||
}
|
||||
// 计算单价 = 目录单价 * 折扣
|
||||
const discount = row.discountFormat ? row.discountFormat / 100 : 0;
|
||||
row.discount = preciseCurrencyRound(discount, 4);
|
||||
row.price =preciseCurrencyRound(row.cataloguePrice * discount, 2);
|
||||
const discount = row.discountFormat ? this.$calc.div(row.discountFormat , 100,4) : 0;
|
||||
row.discount = discount;
|
||||
row.price =this.$calc.mul(row.cataloguePrice , discount, 2);
|
||||
row.priceFormat = this.formatAmount(row.price);
|
||||
|
||||
// 计算总价 = 单价 * 数量
|
||||
row.allPrice = preciseCurrencyRound(row.price * row.quantity, 2);
|
||||
row.allPrice = this.$calc.mul(row.price , row.quantity, 2);
|
||||
row.allPriceFormat = this.formatAmount(row.allPrice);
|
||||
|
||||
// 计算目录总价 = 目录单价 * 数量
|
||||
row.catalogueAllPrice = preciseCurrencyRound(row.cataloguePrice * row.quantity, 2);
|
||||
row.catalogueAllPrice = this.$calc.mul(row.cataloguePrice , row.quantity, 2);
|
||||
row.catalogueAllPriceFormat = this.formatAmount(row.catalogueAllPrice);
|
||||
|
||||
this.emitChange();
|
||||
|
|
@ -535,7 +534,7 @@ export default {
|
|||
if (isNaN(cataloguePrice)) {
|
||||
cataloguePrice = 0;
|
||||
}
|
||||
row.cataloguePrice = preciseCurrencyRound(cataloguePrice, 2);
|
||||
row.cataloguePrice = this.$calc.toFixed(cataloguePrice, 2);
|
||||
row.cataloguePriceFormat = this.formatAmount(row.cataloguePrice);
|
||||
|
||||
// Recalculate based on new catalogue price
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ import { listAllVendor } from "@/api/base/vendor";
|
|||
import SelectUser from "@/views/system/user/selectUser";
|
||||
import SelectProduct from "@/views/system/product/selectProduct";
|
||||
import { getDicts } from "@/api/system/dict/data";
|
||||
import {preciseCurrencyRound} from "@/utils/ruoyi";
|
||||
|
||||
|
||||
export default {
|
||||
name: "PurchaseOrderDetail",
|
||||
|
|
@ -271,7 +271,7 @@ export default {
|
|||
computed: {
|
||||
totalAmountWithTax() {
|
||||
const total = this.form.omsPurchaseOrderItemList?.reduce((acc, cur) => acc + (cur.amountTotal || 0), 0);
|
||||
this.form.totalAmount=preciseCurrencyRound(total) || 0;
|
||||
this.form.totalAmount=this.$calc.toFixed(total) || 0;
|
||||
return this.form.totalAmount;
|
||||
},
|
||||
totalAmountWithoutTax() {
|
||||
|
|
@ -280,10 +280,10 @@ export default {
|
|||
const taxRate = cur.taxRate || 0;
|
||||
return acc + (amount / (1 + taxRate));
|
||||
}, 0);
|
||||
return (preciseCurrencyRound(total)) || 0;
|
||||
return (this.$calc.toFixed(total)) || 0;
|
||||
},
|
||||
totalTaxAmount() {
|
||||
return (preciseCurrencyRound((this.totalAmountWithTax - this.totalAmountWithoutTax )) ) || 0;
|
||||
return this.$calc.sub(this.totalAmountWithTax , this.totalAmountWithoutTax ) || 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -392,8 +392,8 @@ export default {
|
|||
/** 计算含税小计 */
|
||||
calculateRowTotal(row) {
|
||||
if (row.quantity != null && row.price != null) {
|
||||
row.amountTotal = preciseCurrencyRound(row.quantity * row.price);
|
||||
row.taxTotal = row.amountTotal - preciseCurrencyRound(row.amountTotal / (1 + row.taxRate));
|
||||
row.amountTotal = this.$calc.mul(row.quantity , row.price);
|
||||
row.taxTotal = row.amountTotal - this.$calc.div(row.amountTotal , (1 + row.taxRate));
|
||||
} else {
|
||||
row.amountTotal = 0;
|
||||
row.taxTotal = 0;
|
||||
|
|
@ -401,9 +401,9 @@ export default {
|
|||
},
|
||||
/** 获取厂商列表 */
|
||||
getVendorList() {
|
||||
return listAllVendor().then(res => {
|
||||
this.vendorOptions = res.data;
|
||||
})
|
||||
return listAllVendor().then(res => {
|
||||
this.vendorOptions = res.data;
|
||||
})
|
||||
},
|
||||
/** 制造商选择变化 */
|
||||
handleVendorChange(vendorId) {
|
||||
|
|
@ -414,26 +414,26 @@ export default {
|
|||
this.form.payMethod=this.selectedVendor.payMethod;
|
||||
} else {
|
||||
this.selectedVendor = {};
|
||||
this.form.warehouseId = null;
|
||||
this.currentVendorCode = null;
|
||||
this.form.payMethod = null;
|
||||
this.form.warehouseId = null;
|
||||
this.currentVendorCode = null;
|
||||
this.form.payMethod = null;
|
||||
}
|
||||
},
|
||||
/** 处理采购员选择 */
|
||||
handlePurchaserSelect(user) {
|
||||
|
||||
console.log(user)
|
||||
this.$set(this.form, 'purchaserId', user.userId);
|
||||
this.$set(this.form, 'purchaserName', user.userName);
|
||||
this.$set(this.form, 'purchaserMobile', user.phonenumber);
|
||||
this.$set(this.form, 'purchaserEmail', user.email);
|
||||
this.purchaserSelectOpen = false;
|
||||
this.$set(this.form, 'purchaserId', user.userId);
|
||||
this.$set(this.form, 'purchaserName', user.userName);
|
||||
this.$set(this.form, 'purchaserMobile', user.phonenumber);
|
||||
this.$set(this.form, 'purchaserEmail', user.email);
|
||||
this.purchaserSelectOpen = false;
|
||||
},
|
||||
/** 处理汇智负责人选择 */
|
||||
handleOwnerSelect(user) {
|
||||
this.$set(this.form, 'ownerId', user.userId);
|
||||
this.$set(this.form, 'ownerName', user.userName);
|
||||
this.ownerSelectOpen = false;
|
||||
this.$set(this.form, 'ownerId', user.userId);
|
||||
this.$set(this.form, 'ownerName', user.userName);
|
||||
this.ownerSelectOpen = false;
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@
|
|||
import {getPurchaseorder, getPurchaseOrderHistory} from "@/api/sip/purchaseorder";
|
||||
import {listAllVendor} from "@/api/base/vendor";
|
||||
import {getDicts} from "@/api/system/dict/data";
|
||||
import {preciseCurrencyRound} from "@/utils/ruoyi";
|
||||
|
||||
|
||||
|
||||
export default {
|
||||
|
|
@ -277,7 +277,7 @@ export default {
|
|||
computed: {
|
||||
totalAmountWithTax() {
|
||||
const total = this.form.omsPurchaseOrderItemList?.reduce((acc, cur) => acc + (cur.amountTotal || 0), 0);
|
||||
return preciseCurrencyRound(total) || 0;
|
||||
return this.$calc.toFixed(total) || 0;
|
||||
},
|
||||
totalAmountWithoutTax() {
|
||||
const total = this.form.omsPurchaseOrderItemList?.reduce((acc, cur) => {
|
||||
|
|
@ -285,10 +285,10 @@ export default {
|
|||
const taxRate = cur.taxRate || 0;
|
||||
return acc + (amount / (1 + taxRate));
|
||||
}, 0);
|
||||
return (preciseCurrencyRound(total)) || 0;
|
||||
return (this.$calc.toFixed(total)) || 0;
|
||||
},
|
||||
totalTaxAmount() {
|
||||
return (preciseCurrencyRound((this.totalAmountWithTax - this.totalAmountWithoutTax))) || 0;
|
||||
return (this.$calc.sub(this.totalAmountWithTax , this.totalAmountWithoutTax)) || 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue