zouyu
2025-11-12 1bef995d9a6cc817bea0318a2a33ea013fe69deb
计算精度问题修复
已添加1个文件
已修改7个文件
262 ■■■■■ 文件已修改
src/main.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/bigEval.js 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionTask/index.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionTask/inspection.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/materialOrder/customsInspectionOrder.vue 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/outsourcingFinishProduct/index.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/productOrder/components/addOrder.vue 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/reportPreparation/index.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js
@@ -50,7 +50,10 @@
// æ—¶é—´è½¬æ¢
import Moment from "moment";
import Big from 'big.js'
// å…¨å±€æ–¹æ³•挂载
Vue.prototype.$Big = Big;
Vue.prototype.getDicts = getDicts;
Vue.prototype.getConfigKey = getConfigKey;
Vue.prototype.parseTime = parseTime;
src/utils/bigEval.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,118 @@
import Big from "big.js";
/**
 * å®‰å…¨è®¡ç®—器(支持 + - * / å’Œæ‹¬å·ï¼‰
 * @param {string} expr æ•°å­¦è¡¨è¾¾å¼ï¼Œå¦‚ "(0.1 + 0.2) * 3"
 * @returns {string} è®¡ç®—结果字符串
 */
export function bigEval(expr) {
  console.log(expr)
  // æ­¥éª¤1:词法分析
  const tokens = tokenize(expr);
  // æ­¥éª¤2:转换为逆波兰表达式(后缀表达式)
  const rpn = shuntingYard(tokens);
  // æ­¥éª¤3:执行计算
  return evaluateRPN(rpn);
}
// è¯æ³•分析器
function tokenize(expr) {
  const tokens = [];
  let numBuffer = "";
  let prevToken = null;
  const flushNumber = () => {
    if (numBuffer) {
      // æ–°å¢žï¼šéªŒè¯æ•°å­—格式(支持负数、小数)
      if (!/^-?(\d+(\.\d*)?|\.\d+)$/.test(numBuffer)) {
        throw new Error(`无效数字格式: ${numBuffer}`);
      }
      if (numBuffer.startsWith(".")) numBuffer = "0" + numBuffer;
      tokens.push({ type: "number", value: numBuffer });
      numBuffer = "";
    }
  };
  for (let i = 0; i < expr.length; i++) {
    const c = expr[i];
    if (/\d|\./.test(c)) {
      numBuffer += c;
    } else if ("+-*/()".includes(c)) {
      // ä¿®æ”¹ï¼šå¢žåŠ  numBuffer ä¸ºç©ºçš„判断,确保负号只能作为数字前缀(关键修复)
      if (c === "-" && numBuffer === "" && (
          !prevToken ||
          (prevToken.type === "operator" && prevToken.value !== ")")
      )) {
        numBuffer += c;
      } else {
        flushNumber();
        tokens.push({ type: "operator", value: c });
      }
      prevToken = tokens[tokens.length - 1];
    } else if (c !== " ") {
      throw new Error(`非法字符: ${c}`);
    }
  }
  flushNumber();
  return tokens;
}
// è°ƒåº¦åœºç®—法生成逆波兰表达式
function shuntingYard(tokens) {
  const output = [];
  const stack = [];
  const precedence = { "+": 1, "-": 1, "*": 2, "/": 2 };
  for (const token of tokens) {
    if (token.type === "number") {
      output.push(token.value);
    } else if (token.value === "(") {
      stack.push(token.value);
    } else if (token.value === ")") {
      while (stack.length && stack[stack.length - 1] !== "(") {
        output.push(stack.pop());
      }
      stack.pop(); // å¼¹å‡ºå·¦æ‹¬å·
    } else {
      while (
        stack.length &&
        stack[stack.length - 1] !== "(" &&
        precedence[stack[stack.length - 1]] >= precedence[token.value]
      ) {
        output.push(stack.pop());
      }
      stack.push(token.value);
    }
  }
  while (stack.length) output.push(stack.pop());
  return output;
}
// æ‰§è¡Œé€†æ³¢å…°è¡¨è¾¾å¼è®¡ç®—
function evaluateRPN(rpn) {
  const stack = [];
  for (const token of rpn) {
    if (/[-]?\d|\./.test(token)) {
      stack.push(new Big(token));
    } else {
      const b = stack.pop();
      const a = stack.pop();
      switch (token) {
        case "+": stack.push(a.plus(b)); break;
        case "-": stack.push(a.minus(b)); break;
        case "*": stack.push(a.times(b)); break;
        case "/":
          if (b.eq(0)) throw new Error("除数不能为零");
          stack.push(a.div(b));
          break;
        default: throw new Error(`未知运算符: ${token}`);
      }
    }
  }
  return stack.pop().toString();
}
src/views/business/inspectionTask/index.vue
@@ -18,6 +18,11 @@
                         :value="a.value"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="供应商名称" prop="supplierName" v-if="tabIndex===2">
            <el-input v-model="queryParams.supplierName" clearable placeholder="请输入" size="small"
                      @keyup.enter.native="goSearch">
            </el-input>
          </el-form-item>
          <el-form-item>
            <el-button size="mini" type="primary" @click="refreshTable()">查询</el-button>
            <el-button size="mini" @click="refresh()">重置</el-button>
@@ -247,6 +252,7 @@
import {upReportUrl} from "@/api/business/insReport";
import onlyoffice from "@/components/Onlyoffice/onlyoffice.vue";
import filePreview from "@/components/Preview/filePreview.vue";
import tab from "../../../plugins/tab";
export default {
  name: 'InspectionTask',
  components: {
@@ -377,6 +383,7 @@
            }
          },
        },
        { label: "零件描述", prop: "partDesc", width: "150px" },
        { label: "样品型号", prop: "sampleModel", width: "120px" },
        {
          label: "紧急程度",
src/views/business/inspectionTask/inspection.vue
@@ -1013,7 +1013,8 @@
    calcInbondLength(startMeterMark,endMeterMark){
      let inboundLength = null;
      if((startMeterMark!=null && startMeterMark!=='') && (endMeterMark!=null&&endMeterMark!=='')){
        inboundLength = Math.abs(endMeterMark - startMeterMark)
        let minus = this.$Big(endMeterMark).minus(this.$Big(startMeterMark));
        inboundLength = Math.abs(minus)
      }
      return inboundLength;
    },
@@ -1564,6 +1565,7 @@
              });
            });
            // èŽ·å–åˆ°æœ€å¤§æ£€éªŒå€¼è¾“å…¥ä¸ªæ•°åŽé‡ç»„æ•°æ®
            console.log("最大个数:", maxNum);
            let obj0 = {
              name: j,
              arr: data[i][j].result,
src/views/business/materialOrder/customsInspectionOrder.vue
@@ -462,6 +462,7 @@
import {dateFormat} from "@/utils/date";
import AuxiliaryWireCore from "../productOrder/components/auxiliaryWireCore.vue";
import cableConfig from "../productOrder/components/cable-config.vue";
import {bigEval} from "../../../utils/bigEval";
export default {
  name: "CustomsInspectionOrder",
  dicts: ['check_type', 'urgency_level'],
@@ -1206,6 +1207,37 @@
        this.saveLoad = false
      })
    },
    /**
     * èŽ·å–å°æ•°çš„æœ€å¤§ä½æ•°
     * @param number  åž‹å·å‚æ•°
     * @param ask     è¦æ±‚值
     * @param calcNum è®¡ç®—值
     */
    getDecimalPlaces(number, ask, calcNum) {
      console.log("计算小数点-->", number, ask, calcNum);
      let count1 = 0;
      let count2 = 0;
      const reg = /(\d+\.)(\d+)/g;
      let matches = [];
      if (ask) {
        matches = ask.match(reg);
      }
      if (
        matches &&
        matches.length > 0 &&
        matches[0].toString().indexOf(".") > -1
      ) {
        count1 = matches[0].toString().split(".")[1].length;
      }
      if (number.toString().indexOf(".") > -1) {
        count2 = number.toString().split(".")[1].length;
      }
      if (calcNum.toString().indexOf(".") > -1) {
        const pointLength2 = calcNum.toString().split(".")[1].length;
        count2 = count2 > pointLength2 ? count2 : pointLength2;
      }
      return count1 > count2 ? count1 : count2;
    },
    handleAsk(ask,symbolItem, value) {
      try{
        let code = [">", "<", "=", ">", "<", "≥", "≤", "±", "*", "/"];
@@ -1218,7 +1250,13 @@
            let index = code.findIndex(b => m.includes(b))
            if (index > -1) {
              let arr = m.split(code[index]).filter(b => !!b)
              let num = eval(this.replaceAll(arr[0], symbolItem, value))
              let calcNum = this.$Big(
                bigEval(this.replaceAll(arr[0], symbolItem, value))
              );
              let num = calcNum.toFixed(
                this.getDecimalPlaces(value, ask, calcNum)
              );
              // let num = eval(this.replaceAll(arr[0], symbolItem, value))
              m = code[index] + '' + num
              arr1.push(m)
            }
@@ -1247,7 +1285,13 @@
          let index = code.findIndex(b => ask.includes(b))
          if (index > -1) {
            let arr = ask.split(code[index]).filter(b => !!b)
            let num = eval(this.replaceAll(arr[0], symbolItem, value))
            let calcNum = this.$Big(
              bigEval(this.replaceAll(arr[0], symbolItem, value))
            );
            let num = calcNum.toFixed(
              this.getDecimalPlaces(value, ask, calcNum)
            );
            // let num = eval(this.replaceAll(arr[0], symbolItem, value))
            return code[index] + '' + num
          }
        }
src/views/business/outsourcingFinishProduct/index.vue
@@ -23,6 +23,11 @@
                      @keyup.enter.native="goSearch">
            </el-input>
          </el-form-item>
          <el-form-item label="供应商名称" prop="supplierName" >
            <el-input v-model="entity.supplierName" clearable placeholder="请输入" size="small"
                      @keyup.enter.native="goSearch">
            </el-input>
          </el-form-item>
          <el-form-item>
            <el-button v-if="tabIndex === 2 || tabIndex === 3 || tabIndex === 4"
                       :icon="!more ? 'el-icon-arrow-down' : 'el-icon-arrow-up'" style="color: #3A7BFA;" type="text"
@@ -30,12 +35,7 @@
            <el-button size="mini" type="primary" @click="goSearch">查询</el-button>
            <el-button size="mini" @click="refresh()">重置</el-button>
          </el-form-item>
          <el-form-item label="供应商名称" prop="supplierName"
                        v-if="(tabIndex === 2 || tabIndex === 3 || tabIndex === 4) && more">
            <el-input v-model="entity.supplierName" clearable placeholder="请输入" size="small"
                      @keyup.enter.native="goSearch">
            </el-input>
          </el-form-item>
          <el-form-item label="样品型号" prop="sampleModel"
                        v-if="(tabIndex === 2 || tabIndex === 3 || tabIndex === 4) && more">
            <el-input v-model="entity.sampleModel" clearable placeholder="请输入" size="small"
src/views/business/productOrder/components/addOrder.vue
@@ -601,6 +601,7 @@
import {selectsStandardMethodByFLSSM} from "@/api/standard/standardLibrary";
import limsTable from "@/components/Table/lims-table.vue";
import {selectCustomPageList} from "@/api/system/customer";
import { bigEval } from "@/utils/bigEval";
import {mapGetters} from "vuex";
import {addQuarter, updateQuarterOnOrder} from "@/api/business/finishedProductSampling";
@@ -1033,6 +1034,37 @@
          return symbolItem
        }
      },
    /**
     * èŽ·å–å°æ•°çš„æœ€å¤§ä½æ•°
     * @param number  åž‹å·å‚æ•°
     * @param ask     è¦æ±‚值
     * @param calcNum è®¡ç®—值
     */
    getDecimalPlaces(number, ask, calcNum) {
      console.log("计算小数点-->", number, ask, calcNum);
      let count1 = 0;
      let count2 = 0;
      const reg = /(\d+\.)(\d+)/g;
      let matches = [];
      if (ask) {
        matches = ask.match(reg);
      }
      if (
        matches &&
        matches.length > 0 &&
        matches[0].toString().indexOf(".") > -1
      ) {
        count1 = matches[0].toString().split(".")[1].length;
      }
      if (number.toString().indexOf(".") > -1) {
        count2 = number.toString().split(".")[1].length;
      }
      if (calcNum.toString().indexOf(".") > -1) {
        const pointLength2 = calcNum.toString().split(".")[1].length;
        count2 = count2 > pointLength2 ? count2 : pointLength2;
      }
      return count1 > count2 ? count1 : count2;
    },
      handleAsk(ask,symbolItem, value) {
        try{
          let code = [">", "<", "=", ">", "<", "≥", "≤", "±"];
@@ -1045,7 +1077,13 @@
              let index = code.findIndex(b => m.includes(b))
              if (index > -1) {
                let arr = m.split(code[index]).filter(b => !!b)
                let num = eval(this.replaceAll(arr[0], symbolItem, value))
                let calcNum = this.$Big(
                  bigEval(this.replaceAll(arr[0], symbolItem, value))
                );
                let num = calcNum.toFixed(
                  this.getDecimalPlaces(value, ask, calcNum)
                );
                // let num = eval(this.replaceAll(arr[0], symbolItem, value))
                m = code[index] + '' + num
                arr1.push(m)
              }
@@ -1074,7 +1112,13 @@
            let index = code.findIndex(b => ask.includes(b))
            if (index > -1) {
              let arr = ask.split(code[index]).filter(b => !!b)
              let num = eval(this.replaceAll(arr[0], symbolItem, value))
              let calcNum = this.$Big(
                bigEval(this.replaceAll(arr[0], symbolItem, value))
              );
              let num = calcNum.toFixed(
                this.getDecimalPlaces(value, ask, calcNum)
              );
              // let num = eval(this.replaceAll(arr[0], symbolItem, value))
              return code[index] + '' + num
            }
          }
src/views/business/reportPreparation/index.vue
@@ -26,6 +26,16 @@
            <el-option v-for="(a, i) in orderTypeList" :key="i" :label="a.label" :value="a.value"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="零件描述" prop="partDesc" >
          <el-input v-model="entity.partDesc" clearable placeholder="请输入" size="small"
                    @keyup.enter.native="refreshTable">
          </el-input>
        </el-form-item>
        <el-form-item label="供应商名称" prop="supplierName" >
          <el-input v-model="entity.supplierName" clearable placeholder="请输入" size="small"
                    @keyup.enter.native="refreshTable">
          </el-input>
        </el-form-item>
        <el-form-item>
          <el-button size="mini" type="primary" @click="refreshTable()">查询</el-button>
          <el-button size="mini" @click="refresh()">重置</el-button>
@@ -38,7 +48,7 @@
    </div>
    <div>
      <lims-table :tableData="valueTableData" :column="column" :page="page" :tableLoading="tableLoading"
        :isSelection="true" :handleSelectionChange="handleChange" :height="'calc(100vh - 290px)'"
        :isSelection="true" :handleSelectionChange="handleChange" :height="'calc(100vh - 320px)'"
        @pagination="pagination" key="valueTableData">
        <div slot="action" slot-scope="scope">
          <el-button size="small" type="text" @click="viewIssued(scope.row)">查看报告</el-button>
@@ -351,6 +361,16 @@
          width: "120px",
        },
        {
          label: "零件描述",
          prop: "partDesc",
          width: "120px",
        },
        {
          label: "供应商名称",
          prop: "supplierName",
          width: "120px",
        },
        {
          label: "报告编号",
          prop: "code",
          width: "160px",