业务介绍
公司开会说需要一个计算播种量的小工具,其实各种参数关系比较简单,估计正常的农民很少会用工具来计算,因为早就熟练了.目前拿到的需求中,参数有以下几个(直接贴自定义的变量了):
- density: 密度,
- KWEI: 百粒重(g),
- preHectareWeight: 公顷用量(Kg),
- ridgingSpacing: 垄距cm,
- ridgingSpacingNum: 垄距米间粒数,
- spacing: 株距cm
- toFixedNum: 小数点保留位数
其中toFixedNum:(小数点保留位数)是自己加的,用来测试精度.几个参数之间的计算关系已知的有:
- 公顷用量 = 密度*百粒重/10
- 垄距米间粒数 = (垄距/100)*密度
- 株距(cm) = 100/((垄距/100)*密度)
感觉垄距和密度应该是有换算关系的,但是没有给我.所以暂时无法联动了
项目搭建
为了省去UI,直接用vue的element-ui框架,计算部分使用了math.js库.初始化项目时候加上了vuex等,原因是通过之前保存的模版,省去了自定义的麻烦.
实现部分
路由router.js
router中使用hash模式,直接做了个跳转.如下:
{
path: "/kftools",
name: "kftools",
component: { render: h => h("router-view") },
children: [
{
path: "/kftools/cornsow",
name: "",
component: () =>
import(/* webpackChunkName: "kftools" */ "../views/CornSowTools.vue")
}
]
}
math.js配置
在使用math.js库的时候直接丢进了全局,main.js的内容如下:
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import "element-ui/lib/theme-chalk/index.css";
Vue.config.productionTip = false;
import * as math from "mathjs";
Vue.prototype.$math = math;
import {
Form,
FormItem,
Input,
Button,
Row,
Col,
Select,
Option
} from "element-ui";
Vue.use(Form);
Vue.use(Input);
Vue.use(Button);
Vue.use(FormItem);
Vue.use(Row);
Vue.use(Col);
Vue.use(Select);
Vue.use(Option);
new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");
页面组件
组件起名叫CornSowTools.vue,代码如下:
<template>
<div>
<el-col :span="7">
<el-row>
<el-form
:model="ruleForm"
status-icon
:rules="rules"
ref="ruleForm"
label-width="100px"
class="demo-ruleForm"
>
<el-form-item label="小数点" prop="toFixedNum" style="150">
<el-select v-model="ruleForm.toFixedNum" placeholder="请选择">
<el-option
v-for="item in options"
:key="item"
:label="item"
:value="item"
>
</el-option>
</el-select>
默认2位
</el-form-item>
<el-form-item label="密度" prop="density">
<el-input v-model="ruleForm.density"></el-input>
</el-form-item>
<el-form-item label="百粒重(g)" prop="KWEI">
<el-input v-model="ruleForm.KWEI"></el-input>
</el-form-item>
<el-form-item label="公顷用量(Kg)" prop="preHectareWeight">
<el-input
:disabled="true"
v-model="ruleForm.preHectareWeight"
placeholder="密度*百粒重/10"
></el-input>
</el-form-item>
<el-form-item label="垄距(cm)" prop="ridgingSpacing">
<el-input
v-model="ruleForm.ridgingSpacing"
placeholder="65"
></el-input
>{{ "垄距和密度的换算关系不知道" }}
</el-form-item>
<el-form-item label="垄距米间粒数" prop="ridgingSpacingNum">
<el-input
:disabled="true"
v-model="ruleForm.ridgingSpacingNum"
placeholder="(垄距/100)*密度"
></el-input>
</el-form-item>
<el-form-item label="株距(cm)" prop="spacing">
<el-input
:disabled="true"
v-model="ruleForm.spacing"
placeholder="100/((垄距/100)*密度)"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')"
>提交</el-button
>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</el-row>
</el-col>
</div>
</template>
<script>
import { create, all } from "mathjs/number";
export default {
data() {
const validateNull = (rule, value, callback) => {
if (value === "") {
callback(new Error(rule.messageNull));
} else {
callback();
}
};
const validateNumber = (rule, value, callback) => {
const reg = /^[0-9]+.?[0-9]*$/;
if (!reg.test(value)) {
callback(new Error(rule.message));
} else {
callback();
}
};
return {
/**
*
* density: 密度,
KWEI: 百粒重(g),
preHectareWeight: 公顷用量(Kg),
ridgingSpacing: 垄距cm,
ridgingSpacingNum: 垄距米间粒数,
spacing: 株距cm
toFixedNum: 小数点保留位数
*/
ruleForm: {
density: "",
KWEI: "",
preHectareWeight: "",
ridgingSpacing: 65,
ridgingSpacingNum: "",
spacing: null,
toFixedNum: ""
},
rules: {
density: [
{
required: true,
validator: validateNull,
trigger: "blur",
messageNull: "密度不能为空,请输入密度"
},
{
type: "number",
validator: validateNumber,
message: "密度必须为正数值"
}
],
KWEI: [
{
required: true,
validator: validateNull,
trigger: "blur",
messageNull: "百粒重不能为空,请输入百粒重"
},
{
type: "number",
validator: validateNumber,
message: "百粒重必须为正数值"
}
],
ridgingSpacing: [
{
required: true,
validator: validateNull,
trigger: "blur",
messageNull: "垄距不能为空,请输入垄距"
},
{
type: "number",
validator: validateNumber,
message: "垄距必须为正数值"
}
]
},
options: this.$math.range(0, 4, true)._data
};
},
methods: {
roundFixedNum(value, n) {
return Math.round(value * Math.pow(10, n)) / Math.pow(10, n);
},
submitForm(formName) {
if (this.ruleForm.toFixedNum === "") {
this.ruleForm.toFixedNum = 2;
}
this.$math = create(all);
this.$refs[formName].validate(valid => {
if (valid) {
this.ruleForm.preHectareWeight = this.roundFixedNum(
this.$math
.chain(this.ruleForm.density)
.multiply(this.ruleForm.KWEI)
.divide(10)
.done(),
this.ruleForm.toFixedNum
);
const temp = this.$math
.chain(this.ruleForm.ridgingSpacing)
.divide(100)
.multiply(this.ruleForm.density)
.done();
this.ruleForm.ridgingSpacingNum = this.roundFixedNum(
temp,
this.ruleForm.toFixedNum
);
this.ruleForm.spacing = this.roundFixedNum(
this.$math
.chain(100)
.divide(temp)
.done(),
this.ruleForm.toFixedNum
);
} else {
alert("error:错误提示");
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
};
</script>
TIPS
自定义校验
在使用自定义校验规则时,如以下示例中:
rules: {
density: [
{
required: true,
validator: validateNull,
trigger: "blur",
messageNull: "密度不能为空,请输入密度"
},
{
type: "number",
validator: validateNumber,
message: "密度必须为正数值"
}
],
...略
]
},
messageNull为自定义的一个属性,在rule的对象中,已经有message这个属性可以用.对应初始化在data()中的校验规则如下,分别用来判断""和是否为数字.
const validateNull = (rule, value, callback) => {
if (value === "") {
callback(new Error(rule.messageNull));
} else {
callback();
}
};
const validateNumber = (rule, value, callback) => {
const reg = /^[0-9]+.?[0-9]*$/;
if (!reg.test(value)) {
callback(new Error(rule.message));
} else {
callback();
}
};
math.js的常用方法
- math.sqrt(4) 开方
- math.add( ) 加
- math.subtract( )减
- math.divide( ) 除
- math.multiply( )乘
进行链式操作时可以如下(这里的math放到了vue的全局中,正式使用时注意改代码)
const tmp0 = 9000/100*2
const tmp1 = this.$math.chain(9000)
.divide(100)
.multiply(2)
.done(),
在math.js中要保留多少个数字可以使用math.format(),官方示例如下:
const ans = math.add(0.1, 0.2) // 0.30000000000000004
math.format(ans, {precision: 14}) // '0.3'
本次代码中如果使用此种需求,可以这样:
this.ruleForm.ridgingSpacingNum = this.$math.format(
this.$math
.chain(this.ruleForm.ridgingSpacing)
.divide(100)
.multiply(this.ruleForm.density)
.done(),
4
);
或者
this.ruleForm.ridgingSpacingNum = this.$math.format(
this.$math
.chain(this.ruleForm.ridgingSpacing)
.divide(100)
.multiply(this.ruleForm.density)
.done(),
{ precision: 14 }
);
在初始化下拉选择保留几位小数时,偷懒了下,直接生成数组,如下.其中_data的内部属性是Array类型,而直接返回的类型是Matrix,这块和官网的API说明有歧义,不知道为什么.
this.$math.range(0, 4, true)._data
小数点精度
开始使用toFixed来判断精度,但是后来想起来这个是银行的精度计算.目前的需求是完全的四舍五入.所以只能使用Math.round()来处理,所以定义了一个方法,根据选择的精度,来处理响应的数据,代码如下:
/**
* value 参数数值
* n 保留的小数点位数
*/
roundFixedNum(value, n) {
return Math.round(value * Math.pow(10, n)) / Math.pow(10, n);
},