目录

一、引言与效果图

 二、源码

0.目录结构:

1.xxxx.wxml文件

 2.样式文件

(1)xxxx.less文件

(2)xxxx.wxss文件 (不会使用 less 的可以用这个)

3.xxxx.json文件

4.accurate.js

5.xxxx.js文件

三、结语


一、引言与效果图

       最近在做一个记账本小程序,其中需要用到计算器,但是在网上找的代码,用起来不是不符合我的想法,就是看着非常难受,于是还是自己写了一个

微信小程序计算器(含源码)、含js精确运算代码

 二、源码

0.目录结构:

名字 ( test ) 是我随便取的,你们可以取别的,但是accurate.js的名字请勿改动

微信小程序计算器(含源码)、含js精确运算代码

1.xxxx.wxml文件

对应上方目录结构的 : test.wxml

<view class="counter">
  <view class="result">
    <view class="showResult">
      <view class="result-num">{{counter.posture[0]}}</view>
      <view class="result-num">{{counter.posture[1]}}</view>
      <view class="result-num">{{counter.posture[2]}}</view>
    </view>
  </view>
  <view class="btns">
    <view class="row">
      <view class="col clear" hover-class="hover-bg" bindtap="resetBtn">C</view>
      <view class="col" hover-class="hover-bg" bindtap="delBtn">DEL</view>
      <view class="col chu" hover-class="hover-bg" bindtap="opBtn" data-val="%">%</view>
      <view class="col" hover-class="hover-bg" bindtap="opBtn" data-val="÷">÷</view>
    </view>
    <view class="row">
      <view class="col" hover-class="hover-bg" bindtap="numBtn" data-val="7">7</view>
      <view class="col" hover-class="hover-bg" bindtap="numBtn" data-val="8">8</view>
      <view class="col" hover-class="hover-bg" bindtap="numBtn" data-val="9">9</view>
      <view class="col" hover-class="hover-bg" bindtap="opBtn" data-val="×">×</view>
    </view>
    <view class="row">
      <view class="col" hover-class="hover-bg" bindtap="numBtn" data-val="4">4</view>
      <view class="col" hover-class="hover-bg" bindtap="numBtn" data-val="5">5</view>
      <view class="col" hover-class="hover-bg" bindtap="numBtn" data-val="6">6</view>
      <view class="col" hover-class="hover-bg" bindtap="opBtn" data-val="-">-</view>
    </view>
    <view class="row">
      <view class="col" hover-class="hover-bg" bindtap="numBtn" data-val="1">1</view>
      <view class="col" hover-class="hover-bg" bindtap="numBtn" data-val="2">2</view>
      <view class="col" hover-class="hover-bg" bindtap="numBtn" data-val="3">3</view>
      <view class="col" hover-class="hover-bg" bindtap="opBtn" data-val="+">+</view>
    </view>
    <view class="row">
      <view class="col zero" hover-class="hover-bg" bindtap="numBtn" data-val="0">0</view>
      <view class="col" hover-class="hover-bg" bindtap="numBtn" data-val=".">.</view>
      <view class="col save" hover-class="hover-bg" bindtap="finish">=</view>
    </view>
  </view>
</view>

 2.样式文件

我是用less写的,所以这里先放less文件(不知道less怎么使用的,可以往下滑,看到wxss文件

对应上方目录结构的 : test.wxss 和 test.less

(1)xxxx.less文件

page {
  display: flex;
  flex-direction: column;
  height: 100%;
  background-image: linear-gradient(to top, #a8edea 0%, #fed6e3 100%);
}
.counter {
  height: 100%;
  display: flex;
  flex-direction: column;
  font-size: 60rpx;
  border-top: 1rpx solid #ccc;
  border-left: 1rpx solid #ccc;
  .result {
    flex: 1;
    position: relative;
    width: 100%;
    box-sizing: border-box;
    display: flex;
    align-items: flex-end;
    justify-content: flex-end;
    border-bottom: 5rpx solid #d5dde1;
    background: rgba(215, 250, 252, 0.253);
    .showResult {
      display: flex;
      align-items: flex-end;
      justify-content: flex-end;
      flex-wrap: wrap;
      font-size: 100rpx;
      width: 100%;
      box-sizing: border-box;
      padding: 20rpx;
        .result-num {
          overflow-x: auto;
          text-align: end;
          word-wrap: break-word;
        }
    }
  }
  .btns {
    flex: 1;
    .row {
      display: flex;
      .col {
        flex-basis: 25%;
        height: 150rpx;
        border-right: 1rpx solid #ccc;
        border-bottom: 1rpx solid #ccc;
        box-sizing: border-box;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 50rpx;
        &.clear {
          color: #f00;
        }
        &.zero {
          flex-basis: 50%;
        }
        &:last-child,
        &.chu {
          color: #008bfc;
        }
        &.save {
          color: #f90808;
          font-size: 80rpx;
          font-weight: 900;
        }
        &.hover-bg {
          background: rgba(255, 255, 255, 0.2);
        }
      }
    }
  }
}

(2)xxxx.wxss文件 (不会使用 less 的可以用这个)

page {
  display: flex;
  flex-direction: column;
  height: 100%;
  background-image: linear-gradient(to top, #a8edea 0%, #fed6e3 100%);
}
.counter {
  height: 100%;
  display: flex;
  flex-direction: column;
  font-size: 60rpx;
  border-top: 1rpx solid #ccc;
  border-left: 1rpx solid #ccc;
}
.counter .result {
  flex: 1;
  position: relative;
  width: 100%;
  box-sizing: border-box;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  border-bottom: 5rpx solid #d5dde1;
  background: rgba(215, 250, 252, 0.253);
}
.counter .result .showResult {
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  flex-wrap: wrap;
  font-size: 100rpx;
  width: 100%;
  box-sizing: border-box;
  padding: 20rpx;
}
.counter .result .showResult .result-num {
  overflow-x: auto;
  text-align: end;
  word-wrap: break-word;
}
.counter .btns {
  flex: 1;
}
.counter .btns .row {
  display: flex;
}
.counter .btns .row .col {
  transition: all 0.3s;
  flex-basis: 25%;
  height: 150rpx;
  border-right: 1rpx solid #ccc;
  border-bottom: 1rpx solid #ccc;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 50rpx;
}
.counter .btns .row .col.clear {
  color: #f00;
}
.counter .btns .row .col.zero {
  flex-basis: 50%;
}
.counter .btns .row .col:last-child,
.counter .btns .row .col.chu {
  color: #008bfc;
}
.counter .btns .row .col.save {
  color: #f90808;
  font-size: 80rpx;
  font-weight: 900;
}
.counter .btns .row .col.hover-bg {
  background: rgba(255, 255, 255, 0.2);
}

3.xxxx.json文件

对应上方目录结构的 : test.json

{
  "usingComponents": {},
  "navigationBarTitleText": "计算器",
  "navigationBarBackgroundColor": "#f4dfe9"
}

4.accurate.js

对应上方目录结构的 : accurate.js ,请勿改名

这个文件是用于进行精确计算的:因为js自带的浮点数计算有bug!用这个函数进行加减乘除,可以防止浮点数运算出现问题

module.exports = {
  //加法 add
  add(arg1, arg2) {
    let r1, r2, m;
    try {
      r1 = arg1.toString().split(".")[1].length;
    } catch (e) {
      r1 = 0;
    }
    try {
      r2 = arg2.toString().split(".")[1].length;
    } catch (e) {
      r2 = 0;
    }
    m = Math.pow(10, Math.max(r1, r2));
    return (arg1 * m + arg2 * m) / m;
  },
  //减法 subtraction
  sub(arg1, arg2) {
    let r1, r2, m, n;
    try {
      r1 = arg1.toString().split(".")[1].length;//获取小数点后的长度
    } catch (e) {
      r1 = 0;
    }
    try {
      r2 = arg2.toString().split(".")[1].length;//获取小数点后的长度
    } catch (e) {
      r2 = 0;
    }
    m = Math.pow(10, Math.max(r1, r2));
    n = (r1 >= r2) ? r1 : r2;
    return ((arg1 * m - arg2 * m) / m).toFixed(n);
  },
  //乘法 multiplication
  mul(arg1, arg2) {
    let m = 0,
      s1 = arg1.toString(),
      s2 = arg2.toString();
    try {
      m += s1.split(".")[1].length;
    } catch (e) {}
    try {
      m += s2.split(".")[1].length;
    } catch (e) {}
    return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
  },
  //除法 division
  div(arg1, arg2) {
    let t1 = 0,
      t2 = 0,
      r1, r2;
    try {
      t1 = arg1.toString().split(".")[1].length;
    } catch (e) {}
    try {
      t2 = arg2.toString().split(".")[1].length;
    } catch (e) {}
    r1 = Number(arg1.toString().replace(".", ""));
    r2 = Number(arg2.toString().replace(".", ""));
    return (r1 / r2) * Math.pow(10, t2 - t1);
  }
}

5.xxxx.js文件

对应上方目录结构的 test.js

我的做法是,使用一个长度为3的数组,存储式子的 “左” “中” “右” 的数字或操作符,然后再进行一些特殊情况的处理

//引入精确计算的js
const acc = require('./accurate.js');
Page({
  data: {
    counter: {
      posture: ['', '', ''], //存放式子,比如   1    +   12 
      ansType: false, //判断是否是手动点击的等于号
    },
  },
  //输入数字
  numBtn(e) {
    let num = e.currentTarget.dataset.val
    let posture = this.data.counter.posture
    if (posture[1] === '') { //如果运算符为空,就在式子数组第0个位置放入内容
      //判断特殊情况1: 小数点
      if (num == '.' && posture[0] == '') { //如果是点,且字符串为空,就默认加上 0
        posture[0] = '0'
      } else if (num == '.' && posture[0].indexOf('.') > -1) { //如果已经有点了还按
        console.log('已经有点,不能再按');
        return
      }
      //判断特殊情况2:如果是手动点了等号之后,再按数字,不应该在原位置加,而是清空再加 (通过其它方式比如 “1+2+“ 调用的finish函数不算,需要是手动点击的,判断方法:手动点击的等号有事件对象e)
      if (this.data.counter.ansType) {
        posture[0] = ''
        this.setData({
          'counter.ansType': false //生效一次即可取消
        })
      }
      //判断特殊情况3:如果这里面只有0,那么就删掉这个0,再增加(想输入小数点除外)
      if (num !== '.' &&posture[0] == '0') {
        posture[0] = ''
      }
      this.setData({
        'counter.posture[0]': posture[0] + num
      })
    } else { //如果运算符不为空,就在式子数组第2个位置放入内容
      //判断特殊情况: 小数点
      if (num == '.' && posture[2] == '') { //如果是点,且字符串为空,就默认加上 0
        posture[2] = '0'
      } else if (num == '.' && posture[2].indexOf('.') > -1) { //如果已经有点了还按
        console.log('已经有点,不能再按');
        return
      }
      //判断特殊情况2:如果这里面只有0,那么就删掉这个0,再增加 (想输入小数点除外)
      if (num !== '.' && posture[2] == '0') {
        posture[2] = ''
      }
      this.setData({
        'counter.posture[2]': posture[2] + num
      })
    }
    console.log(this.data.counter.posture);
  },
  //输入运算符
  opBtn(e) {
    let op = e.currentTarget.dataset.val
    let posture = this.data.counter.posture
    if (posture[2] == '') { //如果式子最后一位为空的话,就把符号放进去运算符位置
      this.setData({
        'counter.posture[1]': op
      })
    } else { //否则就先运算,再放进去
      this.finish()
      this.setData({
        'counter.posture[1]': op
      })
    }
    console.log(this.data.counter.posture);
  },
  //运算
  finish(e) {
    let posture = this.data.counter.posture
    let left = parseFloat(posture[0] || 0) //左数字 如果是空字符串就设置为0
    let right = parseFloat(posture[2] || 0) //右数字 如果是空字符串就设置为0
    let ans = 0 //答案
    console.log(left, right);
    switch (posture[1]) { //根据不同运算符,进行不同的运算
      case '+':
        ans = acc.add(left, right)
        break;
      case '-':
        ans = acc.sub(left, right)
        break;
      case '×':
        ans = acc.mul(left, right)
        break;
      case '÷':
        if (right == 0) { //如果数字不合规
          wx.showToast({
            title: '不能除 0 哦',
            icon: 'none'
          })
          ans = left
        } else {
          let _ans = acc.div(left, right)
          let x = String(_ans).indexOf('.') + 1;
          let y = String(_ans).length - x;
          if (y > 10) {
            ans = _ans.toFixed(10);
          } else {
            ans = _ans
          }
        }
        break;
      case '%':
        if (right == 0) { //如果数字不合规
          wx.showToast({
            title: '不能余 0 哦',
            icon: 'none'
          })
          ans = left
        } else {
          ans = left % right
        }
        break;
      default:
        ans = left
        break;
    }
    console.log(ans);
    posture = ['' + ans, '', ''] //清空数组,把答案放在第一位
    let ansType = false
    if (e) { //如果有事件对象,说明是手动点击的”=“,应该加一个标识符,点了=再点数字的时候,应该把左边数字清空再处理数字
      console.log('手动点击的等于号,后面点击数字时,将清空左边数字');
      ansType = true
    }
    this.setData({
      'counter.posture': posture,
      'counter.ansType': ansType
    })
  },
  //清空
  resetBtn() {
    this.setData({
      'counter.posture': ['', '', ''],
    })
  },
  //退位
  delBtn() {
    let posture = this.data.counter.posture
    //从右到左的顺序删除
    if (posture[2]) { //如果最后一位不为空,就先删它
      posture[2] = posture[2].substr(0, posture[2].length - 1)
    } else if (posture[1]) { //如果符号位不为空,就删它
      posture[1] = ''
    } else if (posture[0]) { //如果第一位不为空,就删它
      posture[0] = posture[0].substr(0, posture[0].length - 1)
    } else return //否则就不做事
    this.setData({
      'counter.posture': posture
    })
    console.log(this.data.counter.posture);
  },
})

三、结语

这次的小程序源码分享到此结束,如果遇到了源码中出现了什么bug,欢迎在评论区指出

如果你有更好的实现方式,希望大家不吝赐教

经过评论区提醒,已对除法结果小数点后长度进行限制,同时修改了部分样式表,使得数字超长时会自动换行

发表回复