feat(currency): 引入精确货币舍入工具函数并优化价格计算逻辑

- 在 ruoyi.js 中新增 preciseCurrencyRound 函数用于处理货币精度
- 替换 ConfigInfo.vue 和 ProductConfig.vue 中的 Math.round 为精确舍入方法
- 移除 ProductConfig.vue 组件内原有的 preciseCurrencyRound 方法定义
- 优化库存管理页面排序字段,从 deliveryTime 改为 createTime
- 新增项目与订单详情抽屉组件,替换原有占位提示逻辑
- 设置默认排序方式为按 approveTime 降序排列已审批订单列表
master
chenhao 2025-11-25 15:16:54 +08:00
parent 87c5fade50
commit d745fe6de8
5 changed files with 45 additions and 31 deletions

View File

@ -226,3 +226,9 @@ export function getNormalPath(p) {
export function blobValidate(data) { export function blobValidate(data) {
return data.type !== 'application/json' 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');
}

View File

@ -148,6 +148,8 @@ export default {
customerName: null, customerName: null,
dutyName: null, dutyName: null,
approveNode: null, approveNode: null,
orderByColumn:'approveTime',
isAsc: 'desc'
}, },
}; };
}, },

View File

@ -206,6 +206,8 @@
</template> </template>
<script> <script>
import {preciseCurrencyRound} from "@/utils/ruoyi";
export default { export default {
name: "ConfigInfo", name: "ConfigInfo",
props: { props: {
@ -300,8 +302,7 @@ export default {
if (discount === 1) { if (discount === 1) {
return product.allPrice; return product.allPrice;
} }
const discountedUnitPrice = product.price * discount; const roundedDiscountedUnitPrice = preciseCurrencyRound( product.price * discount);
const roundedDiscountedUnitPrice = Math.round(discountedUnitPrice * 100) / 100;
return roundedDiscountedUnitPrice * product.quantity; return roundedDiscountedUnitPrice * product.quantity;
}, },
calculateTotal(productList, discount) { calculateTotal(productList, discount) {

View File

@ -275,20 +275,33 @@
</el-row> </el-row>
</el-form> </el-form>
</el-drawer> </el-drawer>
<!-- 项目详情抽屉 -->
<project-detail-drawer :visible.sync="projectDrawerVisible" :project-id="currentProjectId" />
<!-- 订单详情抽屉 -->
<order-detail-drawer :visible.sync="orderDrawerVisible" :order-id="currentOrderId" title="订单详情" />
</div> </div>
</template> </template>
<script> <script>
import { listStock, getStock, addStock, updateStock, delStock, exportStock } from "@/api/inventory/stock"; import { listStock, getStock, addStock, updateStock, delStock, exportStock } from "@/api/inventory/stock";
import Pagination from '@/components/Pagination'; // Assuming standard path import Pagination from '@/components/Pagination';
import OrderDetailDrawer from "@/views/project/order/OrderDetailDrawer.vue";
import ProjectDetailDrawer from "@/views/project/info/ProjectDetailDrawer.vue"; // Assuming standard path
export default { export default {
name: "Stock", name: "Stock",
components: { components: {
ProjectDetailDrawer, OrderDetailDrawer,
Pagination, Pagination,
}, },
data() { data() {
return { return {
currentOrderId: null,
projectDrawerVisible: false,
orderDrawerVisible: false,
currentProjectId: null,
// //
loading: true, loading: true,
// //
@ -318,7 +331,7 @@ export default {
orderCode: null, orderCode: null,
stockStatus: null, stockStatus: null,
deliveryTimeRange: [], // For el-date-picker daterange deliveryTimeRange: [], // For el-date-picker daterange
orderByColumn: 't2.deliveryTime', orderByColumn: 't1.createTime',
isAsc: 'desc', isAsc: 'desc',
}, },
// //
@ -551,19 +564,13 @@ export default {
}, },
/** 查看项目详情 */ /** 查看项目详情 */
viewProjectDetail(projectId) { viewProjectDetail(projectId) {
// Placeholder: In a real app, this would open a dialog/drawer or navigate to a project detail page. this.currentProjectId = projectId;
// Example using El-drawer if a component exists: this.projectDrawerVisible = true;
// this.$refs.projectDetailDrawer.open(projectId);
// Or routing:
// this.$router.push('/project/detail/' + projectId);
this.$modal.alert('查看项目详情 ID: ' + projectId);
console.log('Viewing Project Detail ID:', projectId);
}, },
/** 查看订单详情 */ /** 查看订单详情 */
viewDetail(orderId) { viewDetail(orderId) {
// Placeholder: In a real app, this would open a dialog/drawer or navigate to an order detail page. this.currentOrderId = orderId;
this.$modal.alert('查看订单详情 ID: ' + orderId); this.orderDrawerVisible = true;
console.log('Viewing Order Detail ID:', orderId);
}, },
/** 查看审批日志 */ /** 查看审批日志 */
viewApproveLog(orderId) { viewApproveLog(orderId) {

View File

@ -254,6 +254,7 @@
<script> <script>
import SelectProduct from '@/views/system/product/selectProduct.vue'; import SelectProduct from '@/views/system/product/selectProduct.vue';
import {preciseCurrencyRound} from "@/utils/ruoyi";
export default { export default {
name: 'ProductConfig', name: 'ProductConfig',
@ -321,8 +322,8 @@ export default {
quantity: item.quantity || 0, quantity: item.quantity || 0,
taxRate: item.taxRate || 13, taxRate: item.taxRate || 13,
cataloguePriceFormat: this.formatAmount(item.cataloguePrice), cataloguePriceFormat: this.formatAmount(item.cataloguePrice),
guidanceDiscountFormat: item.guidanceDiscount ? this.preciseCurrencyRound(item.guidanceDiscount * 100, 2) : '', guidanceDiscountFormat: item.guidanceDiscount ? preciseCurrencyRound(item.guidanceDiscount * 100, 2) : '',
discountFormat: item.discount ? this.preciseCurrencyRound(item.discount * 100, 2) : '', discountFormat: item.discount ? preciseCurrencyRound(item.discount * 100, 2) : '',
priceFormat: this.formatAmount(item.price), priceFormat: this.formatAmount(item.price),
allPriceFormat: this.formatAmount(item.allPrice), allPriceFormat: this.formatAmount(item.allPrice),
catalogueAllPriceFormat: this.formatAmount(item.catalogueAllPrice) catalogueAllPriceFormat: this.formatAmount(item.catalogueAllPrice)
@ -412,9 +413,9 @@ export default {
row.cataloguePrice = product.cataloguePrice; row.cataloguePrice = product.cataloguePrice;
row.cataloguePriceFormat = this.formatAmount(product.cataloguePrice); row.cataloguePriceFormat = this.formatAmount(product.cataloguePrice);
row.guidanceDiscount = product.guidanceDiscount; row.guidanceDiscount = product.guidanceDiscount;
row.guidanceDiscountFormat = product.guidanceDiscount ? this.preciseCurrencyRound(product.guidanceDiscount * 100, 2) : ''; row.guidanceDiscountFormat = product.guidanceDiscount ? preciseCurrencyRound(product.guidanceDiscount * 100, 2) : '';
row.discount = product.guidanceDiscount || 0; row.discount = product.guidanceDiscount || 0;
row.discountFormat = product.guidanceDiscount ? this.preciseCurrencyRound(product.guidanceDiscount * 100, 2) : ''; row.discountFormat = product.guidanceDiscount ? preciseCurrencyRound(product.guidanceDiscount * 100, 2) : '';
this.calculateRow(row); this.calculateRow(row);
this.emitChange(); this.emitChange();
@ -438,14 +439,14 @@ export default {
if (isNaN(price)) { if (isNaN(price)) {
price = 0; price = 0;
} }
row.price = this.preciseCurrencyRound(price, 2); row.price = preciseCurrencyRound(price, 2);
row.priceFormat = this.formatAmount(row.price); row.priceFormat = this.formatAmount(row.price);
// 0 // 0
if (row.cataloguePrice && row.cataloguePrice > 0) { if (row.cataloguePrice && row.cataloguePrice > 0) {
const discount = row.price / row.cataloguePrice; const discount = row.price / row.cataloguePrice;
row.discount = this.preciseCurrencyRound(discount, 4); // 4 row.discount = preciseCurrencyRound(discount, 4); // 4
row.discountFormat = this.preciseCurrencyRound(discount * 100, 2); // 2 row.discountFormat = preciseCurrencyRound(discount * 100, 2); // 2
} else { } else {
// 00 // 00
row.discount = 0; row.discount = 0;
@ -453,9 +454,9 @@ export default {
} }
// //
row.allPrice = this.preciseCurrencyRound(row.price * row.quantity, 2); row.allPrice = preciseCurrencyRound(row.price * row.quantity, 2);
row.allPriceFormat = this.formatAmount(row.allPrice); row.allPriceFormat = this.formatAmount(row.allPrice);
row.catalogueAllPrice = this.preciseCurrencyRound(row.cataloguePrice * row.quantity, 2); row.catalogueAllPrice = preciseCurrencyRound(row.cataloguePrice * row.quantity, 2);
row.catalogueAllPriceFormat = this.formatAmount(row.catalogueAllPrice); row.catalogueAllPriceFormat = this.formatAmount(row.catalogueAllPrice);
this.emitChange(); this.emitChange();
@ -466,16 +467,16 @@ export default {
} }
// = * // = *
const discount = row.discountFormat ? row.discountFormat / 100 : 0; const discount = row.discountFormat ? row.discountFormat / 100 : 0;
row.discount = this.preciseCurrencyRound(discount, 4); row.discount = preciseCurrencyRound(discount, 4);
row.price = this.preciseCurrencyRound(row.cataloguePrice * discount, 2); row.price =preciseCurrencyRound(row.cataloguePrice * discount, 2);
row.priceFormat = this.formatAmount(row.price); row.priceFormat = this.formatAmount(row.price);
// = * // = *
row.allPrice = this.preciseCurrencyRound(row.price * row.quantity, 2); row.allPrice = preciseCurrencyRound(row.price * row.quantity, 2);
row.allPriceFormat = this.formatAmount(row.allPrice); row.allPriceFormat = this.formatAmount(row.allPrice);
// = * // = *
row.catalogueAllPrice = this.preciseCurrencyRound(row.cataloguePrice * row.quantity, 2); row.catalogueAllPrice = preciseCurrencyRound(row.cataloguePrice * row.quantity, 2);
row.catalogueAllPriceFormat = this.formatAmount(row.catalogueAllPrice); row.catalogueAllPriceFormat = this.formatAmount(row.catalogueAllPrice);
this.emitChange(); this.emitChange();
@ -487,9 +488,6 @@ export default {
maximumFractionDigits: 2 maximumFractionDigits: 2
}); });
}, },
preciseCurrencyRound(value, decimals = 2) {
return Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals);
},
emitChange() { emitChange() {
const data = { const data = {
softwareProjectProductInfoList: this.softwareList.map(item => ({ softwareProjectProductInfoList: this.softwareList.map(item => ({
@ -537,7 +535,7 @@ export default {
if (isNaN(cataloguePrice)) { if (isNaN(cataloguePrice)) {
cataloguePrice = 0; cataloguePrice = 0;
} }
row.cataloguePrice = this.preciseCurrencyRound(cataloguePrice, 2); row.cataloguePrice = preciseCurrencyRound(cataloguePrice, 2);
row.cataloguePriceFormat = this.formatAmount(row.cataloguePrice); row.cataloguePriceFormat = this.formatAmount(row.cataloguePrice);
// Recalculate based on new catalogue price // Recalculate based on new catalogue price