自己开发封装的微信小程序计算器组件,复制即可用(完整代码)

写在前面

我在开发自己用的账本类微信小程序时,因为嫌老是要切屏打开计算器,算完之后还要照着计算结果手动记录很不方便,尤其是当计算出来的结果位数较长时,容易看错或打错,因此想自己开发封装一个计算器组件在需要用的页面里,这样不仅可以减少切屏的次数,还可以直接去拿到计算结果自动记录,非常的方便。

按惯例先贴效果再上代码,啰嗦的话后面再讲。

效果预览

在这里插入图片描述

完整代码

先贴目录,自己对应下面的代码复制到哪个对应的文件里(后续等我把自己的项目整理差不多了,有空写一个demo放到github上,这样下载起来会更方便一些。现在就先复制吧):
在这里插入图片描述

// component/calculator/calculator.js
var util = require("../../utils/util.js");
Component({

  /**
   * 组件的属性列表
   */
  properties: {

  },


  /**
   * 页面的初始数据
   */
  data: {
    pageHeight: wx.getSystemInfoSync().windowHeight,//页面高度
    finalResult: 0,//显示屏上的结果
    operationBtnsOn: false,//是否按下了操作按钮
    preResult: 0,//上次的结果
    operationBtnType: '',//操作
    savePre: false,//是否保存上一次的显示屏内容
    clearResult: false,//是否清除显示屏上的结果
    resultText: "",//输入记录
    historyOperationList: [],//历史操作列表
    figures: 8,//保留位数
  },

  methods: {
    /**
    * 点击清空
    */
    cleanResult: function () {
      this.setData({
        finalResult: 0,
        operationBtnsOn: false,
        operationBtnType: '',
        preResult: 0,
        savePre: false,
        clearResult: false,
        resultText: ''
      })
    },

    /**
     * 点击退格
     */
    deleteInput: function () {
      if (this.data.finalResult == "" || this.data.finalResult == "-") {
        this.setData({
          finalResult: 0
        })
      }
      if (typeof this.data.finalResult == "number") {
        this.data.finalResult = this.data.finalResult.toString();
      }
      if (this.data.resultText != "" && Number(this.data.finalResult) != 0) {
        let temp = this.data.resultText.substring(this.data.resultText.length - 1);
        if (temp == "+" || (temp == "-" && this.data.resultText) || temp == "*" || temp == "/") {
          this.setData({
            resultText: this.data.resultText + this.data.finalResult.substring(0, this.data.finalResult.length - 1)
          })
        } else {
          this.setData({
            resultText: this.data.resultText.substring(0, this.data.resultText.length - 1)
          })
        }
      } else {
        if (this.data.finalResult.substring(0, this.data.finalResult.length - 1).length > 0) {
          this.setData({
            resultText: this.data.resultText.substring(0, this.data.resultText.length - 1)
          })
        } else {
          if (this.data.resultText.length > 0) {
            this.setData({
              resultText: this.data.resultText.substring(0, this.data.resultText.length - 1)
            })
          }
        }
      }
      if (Number(this.data.finalResult) != 0 && Number(this.data.finalResult.substring(0, this.data.finalResult.length - 1)) != 0) {
        this.setData({
          finalResult: this.data.finalResult.substring(0, this.data.finalResult.length - 1)
        })
      } else {
        if (this.data.finalResult.substring(0, this.data.finalResult.length - 1).length > 1) {
          this.setData({
            finalResult: this.data.finalResult.substring(0, this.data.finalResult.length - 1)
          })
        } else {
          this.setData({
            finalResult: 0
          })
        }
      }
    },

    /**
     * 点击输入
     */
    inputNum: function (e) {
      var finalResult = this.data.finalResult;
      if (Number(finalResult) == 0) {
        if (finalResult.length > 8) {
          return;
        }
        if (e.currentTarget.dataset.num == ".") {
          if (finalResult === "-0") {
            finalResult = "-0."
          } else {
            finalResult = "0."
          }
        } else {
          if (finalResult.toString().substring(0, 2) == "0." && finalResult.length >= 2) {
            finalResult += e.currentTarget.dataset.num;
          } else {
            if (finalResult === "-0.") {
              finalResult = "-0." + e.currentTarget.dataset.num;
            } else {
              if (finalResult === "-0") {
                finalResult = "-" + e.currentTarget.dataset.num;
              } else {
                finalResult = e.currentTarget.dataset.num;
              }
            }

          }
        }
        this.setData({
          savePre: false
        })
      } else {
        if (this.data.savePre) {
          if (finalResult.length > 8 && !this.data.operationBtnsOn) {
            return;
          }
          if (e.currentTarget.dataset.num == ".") {
            if (finalResult == "-") {
              finalResult = "-0.";
            } else {
              finalResult = "0.";
            }
          } else {
            if (finalResult == "-") {
              finalResult = "-" + e.currentTarget.dataset.num;
            } else {
              finalResult = e.currentTarget.dataset.num;
            }
          }

          this.setData({
            savePre: false
          })
        } else {
          if (finalResult.length > 8) {
            return;
          }
          if (this.data.clearResult) {
            finalResult = e.currentTarget.dataset.num;
            this.setData({
              clearResult: false
            })
          } else {
            if (finalResult == ".") {
              finalResult = "0." + e.currentTarget.dataset.num;
            } else {
              if (finalResult.indexOf(".") > -1) {
                if (e.currentTarget.dataset.num != ".") {
                  finalResult = finalResult + e.currentTarget.dataset.num;
                }
              } else {
                finalResult = finalResult + e.currentTarget.dataset.num;
              }
            }
          }
        }
      }
      this.setData({
        finalResult: finalResult,
        resultText: Number(this.data.resultText) == 0 ? e.currentTarget.dataset.num : this.data.resultText + e.currentTarget.dataset.num
      })
    },

    /**
     * 点击相加
     */
    operationPlus: function () {
      if (this.data.operationBtnsOn) {
        this.handleOperation();
        this.setData({
          operationBtnType: 'plus',
          savePre: true,
          clearResult: false
        })
      } else {
        this.setData({
          operationBtnsOn: true,
          operationBtnType: 'plus',
          preResult: this.data.finalResult,
          savePre: true,
          clearResult: false
        })
      }
      this.setData({
        resultText: this.data.resultText + "+"
      })
    },

    /**
     * 点击相减
     */
    operationMinus: function () {
      if (this.data.operationBtnsOn) {
        this.handleOperation();
        this.setData({
          operationBtnType: 'minus',
          savePre: true,
          clearResult: false
        })
      } else {
        this.setData({
          operationBtnsOn: true,
          operationBtnType: 'minus',
          preResult: this.data.finalResult,
          savePre: true,
          clearResult: false
        })
      }
      this.setData({
        resultText: this.data.resultText + "-"
      })
    },

    /**
     * 点击相乘
     */
    operationMultiply: function () {
      if (this.data.operationBtnsOn) {
        this.handleOperation();
        this.setData({
          operationBtnType: 'multiply',
          savePre: true,
          clearResult: false
        })
      } else {
        this.setData({
          operationBtnsOn: true,
          operationBtnType: 'multiply',
          preResult: this.data.finalResult,
          savePre: true,
          clearResult: false
        })
      }
      this.setData({
        resultText: this.data.resultText + "x"
      })
    },

    /**
     * 点击相除
     */
    operationDivide: function () {
      if (this.data.operationBtnsOn) {
        this.handleOperation();
        this.setData({
          operationBtnType: 'divide',
          savePre: true,
          clearResult: false
        })
      } else {
        this.setData({
          operationBtnsOn: true,
          operationBtnType: 'divide',
          preResult: this.data.finalResult,
          savePre: true,
          clearResult: false
        })
      }
      this.setData({
        resultText: this.data.resultText + "÷"
      })
    },

    /**
     * 处理连续计算
     */
    handleOperation() {
      if (this.data.operationBtnType == 'plus') {
        this.setData({
          finalResult: util.add(this.data.preResult, this.data.finalResult)
        })
        this.setData({
          preResult: this.data.finalResult,
        })
      }
      if (this.data.operationBtnType == 'minus') {
        this.setData({
          finalResult: util.sub(this.data.preResult, this.data.finalResult)
        })
        this.setData({
          preResult: this.data.finalResult,
        })
      }
      if (this.data.operationBtnType == 'multiply') {
        this.setData({
          finalResult: util.mul(this.data.preResult, this.data.finalResult)
        })
        this.setData({
          preResult: this.data.finalResult,
        })
      }
      if (this.data.operationBtnType == 'divide') {
        this.setData({
          finalResult: util.div(this.data.preResult, this.data.finalResult, this.data.figures)
        })
        this.setData({
          preResult: this.data.finalResult,
        })
      }
    },

    /**
     * 点击等于号
     */
    operationEquals: function () {
      if (this.data.operationBtnsOn) {
        if (this.data.operationBtnType == 'plus') {
          this.setData({
            operationBtnsOn: false,
            clearResult: true,
            savePre: false,
            finalResult: util.add(this.data.preResult, this.data.finalResult)
          })
        } else if (this.data.operationBtnType == 'minus') {
          this.setData({
            operationBtnsOn: false,
            clearResult: true,
            savePre: false,
            finalResult: util.sub(this.data.preResult, this.data.finalResult)
          })
        } else if (this.data.operationBtnType == 'multiply') {
          this.setData({
            operationBtnsOn: false,
            clearResult: true,
            savePre: false,
            finalResult: util.mul(this.data.preResult, this.data.finalResult)
          })
        } else if (this.data.operationBtnType == 'divide') {
          this.setData({
            operationBtnsOn: false,
            clearResult: true,
            savePre: false,
            finalResult: util.div(this.data.preResult, this.data.finalResult, this.data.figures)
          })
        }
      }
      if (this.data.resultText) {
        var resultText = this.data.resultText + "=" + this.data.finalResult;
        this.data.historyOperationList.push(resultText);
        this.setData({
          resultText: ''
        })
      }
      this.showHistory();
    },

    /**
     * 打印历史操作
     */
    showHistory: function () {
      console.log(this.data.historyOperationList)
    },

    //点击切换正负
    opposite: function () {
      if (this.data.savePre) {
        if (this.data.finalResult === "-0") {
          this.setData({
            finalResult: 0
          })
        } else {
          this.setData({
            finalResult: "-0"
          })
        }
      } else {
        if (this.data.finalResult === 0) {
          this.setData({
            finalResult: "-0"
          })
        } else {
          this.setData({
            finalResult: -this.data.finalResult
          })
        }
      }
    }
  }

})
/* component/calculator/calculator.wxss */
.tools_calculator{
    background-color: #333;
    padding: 0 32rpx;
}
.calculator_viewArea{
    min-height: 125px;
    width: 100%;
    position: relative;
}
.calculator_viewArea_result{
    font-size: 100rpx;
    color: #fff;
    text-align: end;
    position: absolute;
    bottom: 0;
    right: 20rpx;
    word-break: break-all;
}
.calculator_operationArea{
    display: flex;
    flex-direction: column;
}
.calculator_operationArea_row{
    display: flex;
    flex-direction: row;
}
.calculator_operationArea_eachBox{
    width: 150rpx;
    height: 150rpx;
    border-radius: 100rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    color: #fff;
    margin: 5px;
    font-size: 60rpx;
}
.greyBox{
    background-color: #999;
}
.blackBox{
    background-color: #666;
}
.orangeBox{
    background-color: orange;
}
.isActive{
    color: orange;
    background-color: #fff;
}
<!-- component/calculator/calculator.wxml -->
<view class="tools_calculator" style="height:{{pageHeight}}px">
    <view class="calculator_viewArea">
        <view class="calculator_viewArea_result">{{finalResult==null?"错误":finalResult}}</view>
    </view>
    <view class="calculator_operationArea">
        <view class="calculator_operationArea_row">
            <view class="calculator_operationArea_eachBox greyBox" bind:tap="cleanResult">C</view>
            <view class="calculator_operationArea_eachBox greyBox" bind:tap="deleteInput"></view>
            <view class="calculator_operationArea_eachBox greyBox"  bind:tap="opposite">±</view>
            <view class="calculator_operationArea_eachBox orangeBox {{savePre&&operationBtnType=='divide'?'isActive':''}}" bind:tap="operationDivide">÷</view>
        </view>
        <view class="calculator_operationArea_row">
            <view class="calculator_operationArea_eachBox blackBox" bind:tap="inputNum" data-num="7">7</view>
            <view class="calculator_operationArea_eachBox blackBox" bind:tap="inputNum" data-num="8">8</view>
            <view class="calculator_operationArea_eachBox blackBox" bind:tap="inputNum" data-num="9">9</view>
            <view class="calculator_operationArea_eachBox orangeBox {{savePre&&operationBtnType=='multiply'?'isActive':''}}" bind:tap="operationMultiply">x</view>
        </view>
        <view class="calculator_operationArea_row">
            <view class="calculator_operationArea_eachBox blackBox" bind:tap="inputNum" data-num="4">4</view>
            <view class="calculator_operationArea_eachBox blackBox" bind:tap="inputNum" data-num="5">5</view>
            <view class="calculator_operationArea_eachBox blackBox" bind:tap="inputNum" data-num="6">6</view>
            <view class="calculator_operationArea_eachBox orangeBox {{savePre&&operationBtnType=='minus'?'isActive':''}}" bind:tap="operationMinus">-</view>
        </view> 
        <view class="calculator_operationArea_row">
            <view class="calculator_operationArea_eachBox blackBox" bind:tap="inputNum" data-num="1">1</view>
            <view class="calculator_operationArea_eachBox blackBox" bind:tap="inputNum" data-num="2">2</view>
            <view class="calculator_operationArea_eachBox blackBox" bind:tap="inputNum" data-num="3">3</view>
            <view class="calculator_operationArea_eachBox orangeBox {{savePre&&operationBtnType=='plus'?'isActive':''}}" bind:tap="operationPlus">+</view>
        </view>
        <view class="calculator_operationArea_row">
            <view class="calculator_operationArea_eachBox blackBox" style="width:320rpx;" bind:tap="inputNum" data-num="0">0</view>
            <view class="calculator_operationArea_eachBox blackBox" bind:tap="inputNum" data-num=".">.</view>
            <view class="calculator_operationArea_eachBox orangeBox" bind:tap="operationEquals">=</view>
        </view>
    </view>
</view>

component/calculator/calculator.json”文件:

{
  "component": true
}

utils/utils.js”文件:

/*
    函数,加法函数,用来得到精确的加法结果 
    说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
    参数:arg1:第一个加数;arg2第二个加数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
    返回值:两数相加的结果
*/
function add(arg1, arg2) {
    arg1 = arg1.toString(), arg2 = arg2.toString();
    var arg1Arr = arg1.split("."), arg2Arr = arg2.split("."), d1 = arg1Arr.length == 2 ? arg1Arr[1] : "", d2 = arg2Arr.length == 2 ? arg2Arr[1] : "";
    var maxLen = Math.max(d1.length, d2.length);
    var m = Math.pow(10, maxLen);
    var result = Number(((arg1 * m + arg2 * m) / m).toFixed(maxLen));
    var d = arguments[2];
    return typeof d === "number" ? Number((result).toFixed(d)) : result;
}

/*
    函数:减法函数,用来得到精确的减法结果 
    说明:函数返回较为精确的减法结果。
    参数:arg1:第一个加数;arg2第二个加数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
    返回值:两数相减的结果
*/
function sub(arg1, arg2) {
    return this.add(arg1, -Number(arg2), arguments[2]);
}

/*
    函数:乘法函数,用来得到精确的乘法结果 
    说明:函数返回较为精确的乘法结果。
    参数:arg1:第一个乘数;arg2第二个乘数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
    返回值:两数相乘的结果
*/
function mul(arg1, arg2) {
    var r1 = arg1.toString(), r2 = arg2.toString(), m, resultVal, d = arguments[2];
    m = (r1.split(".")[1] ? r1.split(".")[1].length : 0) + (r2.split(".")[1] ? r2.split(".")[1].length : 0);
    resultVal = Number(r1.replace(".", "")) * Number(r2.replace(".", "")) / Math.pow(10, m);
    return typeof d !== "number" ? Number(resultVal) : Number(resultVal.toFixed(parseInt(d)));
}

/*
    函数:除法函数,用来得到精确的除法结果 
    说明:函数返回较为精确的除法结果。
    参数:arg1:除数;arg2被除数;d要保留的小数位数(可以不传此参数,如果不传则不处理小数位数)
    返回值:arg1除于arg2的结果
*/
function div(arg1, arg2, d) {
    var r1 = arg1.toString(), r2 = arg2.toString(), m, resultVal;
    m = (r2.split(".")[1] ? r2.split(".")[1].length : 0) - (r1.split(".")[1] ? r1.split(".")[1].length : 0);
    resultVal = Number(r1.replace(".", "")) / Number(r2.replace(".", "")) * Math.pow(10, m);
    return typeof d !== "number" ? Number(resultVal) : Number(resultVal.toFixed(parseInt(d)));
}

module.exports = {
    add: add,
    sub: sub,
    mul: mul,
    div: div
}

下面是要调用的页面的写法:

<!--pages/test/test.wxml-->
<calculator></calculator>

pages/test/test.json”文件:

{
  "usingComponents": {
    "calculator": "/component/calculator/calculator"
  }
}
小结

调用封装的组件应该都会吧,不会的话上面代码也贴了,就是要用的页面比如上文的test页面的json文件里引一下,wxml文件里调一下。

utils里的方法是为了处理js在进行运算时如果有小数会出现精度丢失导致结果不准确的问题的。

很明显可以从界面看出来,我这个是仿ios的计算器写的,所以一些处理也和它类似。我自己在测试的时候是用自己手机先算一遍,再用我写的计算器组件算一遍,去对照结果的,暂时没发现什么问题。

简单说一下思路:就是先点击数字键,相当于输入要参与运算的第一个数字,然后按操作符,这个时候用一个变量记录你已经用了运算符了,那接下来再按数字键就是输入要参与运算的第二个数字了。然后第二个数字输完按等于号计算结果,如果要连续计算,那再按运算符的时候也要把当前结果计算出来。

就差不多这样,思路很简单,但实际处理起来,小数点,正负数,特别是对于“-0,0.xxx”这种的要考虑清楚,还是有很多坑在里面的。

其他暂时没想到什么要注意的。有问题大家一起探讨。

可以优化的点(不影响使用)

1、ios的计算器,不管是输入还是计算结果,都严格控制在一行显示的。我这个呢是输入一行以内,计算结果两行以内。反正不影响使用,我也和ios一样都是输入9位数以内运算,无非就是难看一些。如果你觉得难看呢可以自己改一下,也像ios那样一行显示,确实更好看,而且人家位数多了还改变了字体大小,这个效果呢倒是很好实现,根据屏幕上显示的结果长度去动态改变css就行,写几个不同字体大小的class,去切换,但我想我这个反正是两行了,也没必要搞这个字体大小了,就没弄,想搞的可以弄一下,很简单也更好看。

2、我只进行了除法运算时小数点最多保留到8位的限制,加减乘的小数位没做限制,加减乘除的整数位也没做限制。这个和上面那个控制到一行有关,你要是全部控制位数了那肯定就不会超出一行了,但我想想没必要吧,就处理一下无限小数就行了呗,不搞那么麻烦。还是那句话,根据自己需要来。

3、可以看到我的代码里记录了历史操作,也专门写了个方法,但里面只有一个console。这个本来是要做个按钮专门把历史操作反映到界面上的,但因为这个记录啊还有点问题,我就只在按“=”的时候调用一下console,反正用户端也看不出什么,后续再优化什么的都行。

那这个记录有什么问题呢?其实计算结果的记录,正常的加减乘除操作记录都没什么问题,有问题的是:处理正负号,多次按小数点,想打“0.xxx”的时候只打“.xxx”(这个很多人都有吧),各种情况下按退格键这些情况下,会记录每个按下的小数点啦,少记录一个0直接记录.和后面的数字啦等等。但这个地方吧,因为你不能影响到正常操作的记录,也不好判断值也不好判断长度,所以怎么去限制我输入一个小数的时候多次点击小数点,在记录中只记录一个正确位置的小数点就是问题了。还有就是我想输入“0.xxx”,但我先狂按“00000000”然后在输入“0.xx”,那我记录里应该只有“0.xx”,前面那么多无效的0不能记录。

不过呢,我这个记录是每个按键都去记录的。对我这个来说,不能只把记录写在运算符按键上,不记录数字键,然后每次操作运算符的时候记录当前屏幕显示结果。因为此时当前屏幕显示结果已经是运算完的结果了,记录的不是第二个数。这是由于我只用了一个字段去保存计算结果而不保存参与运算的数。如果我用两个字段分别保存参与运算的第一个数和第二个数,然后再用一个字段专门去存运算结果,那这样我只在操作运算符的时候去记录应该就是可行的了。

但这样要改的地方比较多,只能说当初构思的时候还是想的不完善,还有很多要注意的地方。
不需要操作记录的直接把相关代码删掉就行了,这样还能给这个组件瘦瘦身。

4、最后也是最值得优化的地方:代码写的不够简洁漂亮。可以看到我对小数点和负号的处理很粗糙,部分逻辑可能存在冗余。也不知道有没有潜在的bug。目前测试下来暂时没发现什么问题,等待后续再看。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值