Applet development Learn - 2019 new tax calculator

The official looked small program development guide, made a tax calculator to practice hand.
Results are as follows:


Project is structured as follows:


I. Introduction
1, the main page style using official WeUI.
WeUI is consistent with a micro-channel native visual experience base style library Download https://github.com/Tencent/weui-wxss.
Usage:
(1) create a new directory style, the download source inside weui.wxss WeUI into them;
(2) introducing app.wxss @import 'style / weui.wxss';
2, form a verification WxValidate. js
use is relatively simple:
(. 1) wxml in
input box set value value
<INPUT type = "digit for" class = "weui-INPUT" name = "beforeTax" value = 'form.beforeTax {} {}' />
form tie the method set Submit
<bindsubmit form = 'formSubmit'>
(2) in a JS
onLoad form field validation function initializes this.initValidate ();
formSubmit function calls the function verification
const e.detail.value the params =
IF (this.WxValidate.checkForm! (the params)) {
      const this.WxValidate.errorList error = [0]
      the this.
      false return
}
3, table implementation
applet did not like the page table form controls, search online, with a view to achieve the effect of form.
Where there is a note that the page table only need to set the width of each column in the first row on the line, but with a view of the needs of each row, then set the width of each column, each row or content length
when it is inconsistent with the disorder.

Second, the main source file

1、index.wxml

<form bindsubmit='formSubmit'>
 <view class="weui-cells weui-cells_after-title">     
      <view class="weui-cell weui-cell_input">
          <view class="weui-cell__hd">
              <view class="weui-label">税前工资</view>
          </view>
          <view class="weui-cell__bd">
              <input type="digit" class="weui-input" name="beforeTax" value='{{form.beforeTax}}'/>
          </view>
          <view class="weui-cell__ft">元</view>
      </view>
      <view class="weui-cell weui-cell_input">
          <view class="weui-cell__hd">
              <view class="weui-label">五险一金</view>
          </view>
          <view class="weui-cell__bd">
              <input type="digit" class="weui-input" name="insurance" value="{{form.insurance}}"/>
          </view>
          <view class="weui-cell__ft">元</view>
      </view>
      <view class="weui-cell weui-cell_input">
          <view class="weui-cell__hd">
              <view class="weui-label">附加扣除</view>
          </view>
          <view class="weui-cell__bd">
              <input type="number" class="weui-input" name="otherDeduct" value="{{form.otherDeduct}}"/>
          </view>
          <view class="weui-cell__ft">元</view>
      </view>
      <view class="weui-cell weui-cell_input">
          <view class="weui-cell__hd">
              <view class="weui-label">个税起征</view>
          </view>
          <view class="weui-cell__bd">
              <input class="weui-input" name="threshold" disabled='disabled'  value="5000"/>
          </view>
          <view class="weui-cell__ft">元</view>
      </view>
      <view class="weui-btn-area">
            <button class="weui-btn green-btn" hover-class="green-hover-class" form-type="submit" >计算</button>
        </view>
</view>
<view class="weui-cells__title">计算结果</view>
<view class="weui-cell weui-cell_input">
    <view class="weui-cell__hd">
        <view class="weui-label">应纳所得</view>
    </view>
    <view class="weui-cell__bd">
        {{ resAfterDeduct }}
    </view>
    <view class="weui-cell__ft">元</view>
</view>
<view class="weui-cell weui-cell_input">
    <view class="weui-cell__hd">
        <view class="weui-label">适用税率</view>
    </view>
    <view class="weui-cell__bd">
        {{ resTaxRate }}
    </view>
    <view class="weui-cell__ft">%</view>
</view>
<view class="weui-cell weui-cell_input">
    <view class="weui-cell__hd">
        <view class="weui-label">速算扣除</view>
    </view>
    <view class="weui-cell__bd">
        {{ resQuickDeduct }}
    </view>
    <view class="weui-cell__ft">元</view>
</view>
<view class="weui-cell weui-cell_input">
    <view class="weui-cell__hd">
        <view class="weui-label">应缴税款</view>
    </view>
    <view class="weui-cell__bd">
        {{ resTax }}
    </view>
    <view class="weui-cell__ft">元</view>
</view>
<view class="weui-cell weui-cell_input">
    <view class="weui-cell__hd">
        <view class="weui-label">税后工资</view>
    </view>
    <view class="weui-cell__bd">
        {{ resAfterTax }}
    </view>
    <view class="weui-cell__ft">元</view>
</view>

<view class="table">
      <view class="tr">
        <view class="th">个人所得税税率表</view>       
      </view>
      <view class="tr">
        <view class="th th1">级数</view>
        <view class="th th2">应纳税所得额</view>
        <view class="th th3">税率(%)</view>
        <view class="th th4">速算扣除数</view>
      </view>
      <block wx:for="{{taxArray}}" wx:key="level">
        <view class='tr {{ item.level % 2 == 0 ? "rowColor":"" }}'>
          <view class="td th1">{{item.level}}</view>
          <view class="td th2">{{item.name}}</view>
          <view class="td th3">{{item.rate}}</view>
          <view class="td th4">{{item.deduct}}</view>
        </view>
      </block>
</view>

<view class="weui-footer weui-footer_fixed-bottom">    
    <view class="weui-footer__text">Copyright © 2019 jlc</view>
</view>
</form>

2、index.wxss

.green-btn{
  background-color: #a6a8aa;
  color:#fff;
}
.green-hover-class{
  background-color: #737577;
  color:#fff;
}
.table {
border: 1px solid #ccc;
border-right: 0;
border-bottom: 0;
width: 100%;
margin-top: 20px;
margin-bottom: 50px;
}

.tr {
width: 100%;
display: flex;
justify-content: space-between;
}

.th, .td {
padding: 5px;
border-bottom: 1px solid #c2bdbd;
border-right: 1px solid #c2bdbd;
text-align: center;
width: 100%;
font-size:12px;
}

.th {
font-weight: 400;
background-color: #dadada;
}
.th1{
  width:9%;
}
.th2{
  width:55%;
}
.th3{
  width:15%;
}
.th4{
  width:21%;
}
.rowColor{
  background-color: #dadada;
}

3、index.js

import WxValidate from '../../utils/WxValidate.js'

Page({

  /**
   * 页面的初始数据
   */
  data: {
    showTopTips: false,
    form:{
      beforeTax: 10000,//税前
      insurance: 0,//五险一金
      otherDeduct: 0,//附加扣除
      threshold: 5000//起征点 
    },
    taxArray: [
      { level: 1, name: '未超过3000元的部分', minTax: 0,rate:3, deduct:0 },
      { level: 2, name: '超过3000至12000元的部分', minTax: 3000,rate: 10, deduct: 210 },
      { level: 3, name: '超过12000至25000元的部分', minTax: 12000,rate: 20, deduct: 1410 },
      { level: 4, name: '超过25000至35000元的部分', minTax: 25000,rate: 25, deduct: 2660 },
      { level: 5, name: '超过35000至55000元的部分', minTax: 35000,rate: 30, deduct: 4410 },
      { level: 6, name: '超过55000至80000元的部分', minTax: 55000,rate: 35, deduct: 7160 },
      { level: 7, name: '超过80000元的部分', minTax: 80000, rate: 45, deduct: 15160 }    
    ]
  },
  calTax: function () {
  
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    //初始化表单
    this.initValidate();
  },
    //验证表单字段
  initValidate() {
    const rules = {
      beforeTax: {
        required: true,
        number: true
      },
      insurance: {
        required: true,
        number: true
      },
      otherDeduct: {
        required: true,
        digits: true
      }
    }
    const messages = {
      beforeTax: {
        required: '请填写税前工资',
        number: '请输入正确格式的税前工资'
      },
      insurance: {
        required: '请填写五险一金',
        number: '请填写正确格式的五险一金'
      },
      otherDeduct: {
        required: '请填写附加扣除',
        digits: '附加扣除只能为整数'
      }
    }
    this.WxValidate = new WxValidate(rules, messages)
  },
  //报错 
  showMessage(error) {
    /*wx.showModal({
      content: error.msg,
      showCancel: false,
    })*/
    wx.showToast({
      title: error.msg,
      icon: 'none',
      duration: 3000
    });
  },
  //调用验证函数
  formSubmit: function (e) {
    console.log('form发生了submit事件,携带的数据为:', e.detail.value)
    const params = e.detail.value
    //校验表单
    if (!this.WxValidate.checkForm(params)) {
      const error = this.WxValidate.errorList[0]
      this.showMessage(error)
      return false
    }
    
    var resAfterDeduct = 0;//应纳所得
    var resTaxRate = 0;//税率
    var resQuickDeduct = 0;//速算扣除
    var resTax = 0;//应缴税款
    var resAfterTax = 0;//实发工资

    resAfterDeduct = params.beforeTax - params.insurance - params.otherDeduct - params.threshold;
    if (resAfterDeduct < 0){
      resAfterDeduct = 0;
    }else{
      var arr = this.data.taxArray;
      var arrLength = arr.length;
      for (var i = arrLength - 1; i >= 0; i--) {
        if (resAfterDeduct > arr[i].minTax) {
          resTaxRate = arr[i].rate;
          resQuickDeduct = arr[i].deduct;
          break;
        }
      }
      resTax = resAfterDeduct * (resTaxRate / 100) - resQuickDeduct;
      resAfterTax = params.beforeTax - params.insurance - resTax;
    }
    
      this.setData({
      resAfterDeduct: resAfterDeduct,
      resTaxRate: resTaxRate,
      resQuickDeduct: resQuickDeduct,
      resTax: resTax,
      resAfterTax: resAfterTax
    })
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  }
})

4、WxValidate.js
 

/**
 * 表单验证
 * 
 * @param {Object} rules 验证字段的规则
 * @param {Object} messages 验证字段的提示信息
 * 
 */
class WxValidate {
  constructor(rules = {}, messages = {}) {
    Object.assign(this, {
      data: {},
      rules,
      messages,
    })
    this.__init()
  }

  /**
   * __init
   */
  __init() {
    this.__initMethods()
    this.__initDefaults()
    this.__initData()
  }

  /**
   * 初始化数据
   */
  __initData() {
    this.form = {}
    this.errorList = []
  }

  /**
   * 初始化默认提示信息
   */
  __initDefaults() {
    this.defaults = {
      messages: {
        required: '这是必填字段。',
        email: '请输入有效的电子邮件地址。',
        tel: '请输入11位的手机号码。',
        url: '请输入有效的网址。',
        date: '请输入有效的日期。',
        dateISO: '请输入有效的日期(ISO),例如:2009-06-23,1998/01/22。',
        number: '请输入有效的数字。',
        digits: '只能输入数字。',
        idcard: '请输入18位的有效身份证。',
        equalTo: this.formatTpl('输入值必须和 {0} 相同。'),
        contains: this.formatTpl('输入值必须包含 {0}。'),
        minlength: this.formatTpl('最少要输入 {0} 个字符。'),
        maxlength: this.formatTpl('最多可以输入 {0} 个字符。'),
        rangelength: this.formatTpl('请输入长度在 {0} 到 {1} 之间的字符。'),
        min: this.formatTpl('请输入不小于 {0} 的数值。'),
        max: this.formatTpl('请输入不大于 {0} 的数值。'),
        range: this.formatTpl('请输入范围在 {0} 到 {1} 之间的数值。'),
      }
    }
  }

  /**
   * 初始化默认验证方法
   */
  __initMethods() {
    const that = this
    that.methods = {
      /**
       * 验证必填元素
       */
      required(value, param) {
        if (!that.depend(param)) {
          return 'dependency-mismatch'
        } else if (typeof value === 'number') {
          value = value.toString()
        } else if (typeof value === 'boolean') {
          return !0
        }

        return value.length > 0
      },
      /**
       * 验证电子邮箱格式
       */
      email(value) {
        return that.optional(value) || /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(value)
      },
      /**
       * 验证手机格式
       */
      tel(value) {
        return that.optional(value) || /^1[34578]\d{9}$/.test(value)
      },
      /**
       * 验证URL格式
       */
      url(value) {
        return that.optional(value) || /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(value)
      },
      /**
       * 验证日期格式
       */
      date(value) {
        return that.optional(value) || !/Invalid|NaN/.test(new Date(value).toString())
      },
      /**
       * 验证ISO类型的日期格式
       */
      dateISO(value) {
        return that.optional(value) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value)
      },
      /**
       * 验证十进制数字
       */
      number(value) {
        return that.optional(value) || /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value)
      },
      /**
       * 验证整数
       */
      digits(value) {
        return that.optional(value) || /^\d+$/.test(value)
      },
      /**
       * 验证身份证号码
       */
      idcard(value) {
        return that.optional(value) || /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(value)
      },
      /**
       * 验证两个输入框的内容是否相同
       */
      equalTo(value, param) {
        return that.optional(value) || value === that.data[param]
      },
      /**
       * 验证是否包含某个值
       */
      contains(value, param) {
        return that.optional(value) || value.indexOf(param) >= 0
      },
      /**
       * 验证最小长度
       */
      minlength(value, param) {
        return that.optional(value) || value.length >= param
      },
      /**
       * 验证最大长度
       */
      maxlength(value, param) {
        return that.optional(value) || value.length <= param
      },
      /**
       * 验证一个长度范围[min, max]
       */
      rangelength(value, param) {
        return that.optional(value) || (value.length >= param[0] && value.length <= param[1])
      },
      /**
       * 验证最小值
       */
      min(value, param) {
        return that.optional(value) || value >= param
      },
      /**
       * 验证最大值
       */
      max(value, param) {
        return that.optional(value) || value <= param
      },
      /**
       * 验证一个值范围[min, max]
       */
      range(value, param) {
        return that.optional(value) || (value >= param[0] && value <= param[1])
      },
    }
  }

  /**
   * 添加自定义验证方法
   * @param {String} name 方法名
   * @param {Function} method 函数体,接收两个参数(value, param),value表示元素的值,param表示参数
   * @param {String} message 提示信息
   */
  addMethod(name, method, message) {
    this.methods[name] = method
    this.defaults.messages[name] = message !== undefined ? message : this.defaults.messages[name]
  }

  /**
   * 判断验证方法是否存在
   */
  isValidMethod(value) {
    let methods = []
    for (let method in this.methods) {
      if (method && typeof this.methods[method] === 'function') {
        methods.push(method)
      }
    }
    return methods.indexOf(value) !== -1
  }

  /**
   * 格式化提示信息模板
   */
  formatTpl(source, params) {
    const that = this
    if (arguments.length === 1) {
      return function () {
        let args = Array.from(arguments)
        args.unshift(source)
        return that.formatTpl.apply(this, args)
      }
    }
    if (params === undefined) {
      return source
    }
    if (arguments.length > 2 && params.constructor !== Array) {
      params = Array.from(arguments).slice(1)
    }
    if (params.constructor !== Array) {
      params = [params]
    }
    params.forEach(function (n, i) {
      source = source.replace(new RegExp("\\{" + i + "\\}", "g"), function () {
        return n
      })
    })
    return source
  }

  /**
   * 判断规则依赖是否存在
   */
  depend(param) {
    switch (typeof param) {
      case 'boolean':
        param = param
        break
      case 'string':
        param = !!param.length
        break
      case 'function':
        param = param()
      default:
        param = !0
    }
    return param
  }

  /**
   * 判断输入值是否为空
   */
  optional(value) {
    return !this.methods.required(value) && 'dependency-mismatch'
  }

  /**
   * 获取自定义字段的提示信息
   * @param {String} param 字段名
   * @param {Object} rule 规则
   */
  customMessage(param, rule) {
    const params = this.messages[param]
    const isObject = typeof params === 'object'
    if (params && isObject) return params[rule.method]
  }

  /**
   * 获取某个指定字段的提示信息
   * @param {String} param 字段名
   * @param {Object} rule 规则
   */
  defaultMessage(param, rule) {
    let message = this.customMessage(param, rule) || this.defaults.messages[rule.method]
    let type = typeof message

    if (type === 'undefined') {
      message = `Warning: No message defined for ${rule.method}.`
    } else if (type === 'function') {
      message = message.call(this, rule.parameters)
    }

    return message
  }

  /**
   * 缓存错误信息
   * @param {String} param 字段名
   * @param {Object} rule 规则
   * @param {String} value 元素的值
   */
  formatTplAndAdd(param, rule, value) {
    let msg = this.defaultMessage(param, rule)

    this.errorList.push({
      param: param,
      msg: msg,
      value: value,
    })
  }

  /**
   * 验证某个指定字段的规则
   * @param {String} param 字段名
   * @param {Object} rules 规则
   * @param {Object} data 需要验证的数据对象
   */
  checkParam(param, rules, data) {

    // 缓存数据对象
    this.data = data

    // 缓存字段对应的值
    const value = data[param] !== null && data[param] !== undefined ? data[param] : ''

    // 遍历某个指定字段的所有规则,依次验证规则,否则缓存错误信息
    for (let method in rules) {

      // 判断验证方法是否存在
      if (this.isValidMethod(method)) {

        // 缓存规则的属性及值
        const rule = {
          method: method,
          parameters: rules[method]
        }

        // 调用验证方法
        const result = this.methods[method](value, rule.parameters)

        // 若result返回值为dependency-mismatch,则说明该字段的值为空或非必填字段
        if (result === 'dependency-mismatch') {
          continue
        }

        this.setValue(param, method, result, value)

        // 判断是否通过验证,否则缓存错误信息,跳出循环
        if (!result) {
          this.formatTplAndAdd(param, rule, value)
          break
        }
      }
    }
  }

  /**
   * 设置字段的默认验证值
   * @param {String} param 字段名
   */
  setView(param) {
    this.form[param] = {
      $name: param,
      $valid: true,
      $invalid: false,
      $error: {},
      $success: {},
      $viewValue: ``,
    }
  }

  /**
   * 设置字段的验证值
   * @param {String} param 字段名
   * @param {String} method 字段的方法
   * @param {Boolean} result 是否通过验证
   * @param {String} value 字段的值
   */
  setValue(param, method, result, value) {
    const params = this.form[param]
    params.$valid = result
    params.$invalid = !result
    params.$error[method] = !result
    params.$success[method] = result
    params.$viewValue = value
  }

  /**
   * 验证所有字段的规则,返回验证是否通过
   * @param {Object} data 需要验证数据对象
   */
  checkForm(data) {
    this.__initData()

    for (let param in this.rules) {
      this.setView(param)
      this.checkParam(param, this.rules[param], data)
    }

    return this.valid()
  }

  /**
   * 返回验证是否通过
   */
  valid() {
    return this.size() === 0
  }

  /**
   * 返回错误信息的个数
   */
  size() {
    return this.errorList.length
  }

  /**
   * 返回所有错误信息
   */
  validationErrors() {
    return this.errorList
  }
}

export default WxValidate

 

Guess you like

Origin blog.csdn.net/gdjlc/article/details/86703481