+ L014-1 相关物质取消测试状态

+ L014-1 相关物质使用表格展示结果
+ 干燥失重将读写存储的操作解耦合
+ 干燥失重将保存功能使用方法删除
This commit is contained in:
2023-01-07 00:28:14 +08:00
parent 5fd5440966
commit ff726aeec8
3 changed files with 436 additions and 238 deletions

View File

@@ -8,15 +8,21 @@
<title>L014-1 相关物质</title> <title>L014-1 相关物质</title>
<link rel="stylesheet" href="./github.css"> <link rel="stylesheet" href="./github.css">
<link rel="stylesheet" href="./theme.css"> <link rel="stylesheet" href="./theme.css">
<script src="./decimal.js"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script> <script type="module">
import { Decimal } from "./modules/decimal.mjs"
let decimal = Decimal.set({ let decimal = Decimal.set({
rounding: Decimal.ROUND_HALF_EVEN, rounding: Decimal.ROUND_HALF_EVEN,
precision: 12 precision: 12
}) })
$(document).ready(() => {
// 舍入模式:四舍六入五成双
const ROUNDING = Decimal.ROUND_HALF_EVEN
// 计算精度
const PRECISION = 3
// debug 开关
const DEBUG = false const DEBUG = false
// 2.2-Dimer 最大值 // 2.2-Dimer 最大值
@@ -33,9 +39,19 @@
// 2-乙酰噻吩 的 RRF // 2-乙酰噻吩 的 RRF
const AT_RRF = 2.06 const AT_RRF = 2.06
// 计算精度 const msg = `
let PRECISION = new Decimal(3).toNumber() <b>相关参数:</b><br>
&nbsp;&nbsp; 2.2-Dimer 的 RRF: ${DIMER_RRF}<br>
&nbsp;&nbsp; 2-乙酰噻吩的 RRF: ${AT_RRF}<br>
<br>
<b>质量标准:</b><br>
&nbsp;&nbsp; 2.2-Dimer &le; ${DIMER_MAX}% <br>
&nbsp;&nbsp; 2-乙酰噻吩 &le; ${AT_MAX.toFixed(1)}% <br>
&nbsp;&nbsp; 杂质总量 &le; ${TOTAL_IMPURITIES_MAX}% <br>
&nbsp;&nbsp; 纯度 &ge; ${PURTY_MIN}% <br>
`
$(document).ready(() => {
if (DEBUG) { if (DEBUG) {
$("#one-dimer").val(247427) $("#one-dimer").val(247427)
$("#one-at").val(65863) $("#one-at").val(65863)
@@ -48,11 +64,7 @@
$("#two-all").val(11943432) $("#two-all").val(11943432)
} }
$("#precision").on('input', event => { $("#msgbox").html(msg)
let p = $("#precision").val()
$("#precision-text").text(p)
PRECISION = new Decimal(p).toNumber()
})
$("#new_page").click(() => window.open(window.location.href, '_BLANK')) $("#new_page").click(() => window.open(window.location.href, '_BLANK'))
@@ -65,116 +77,140 @@
$("#two-at").val("") $("#two-at").val("")
$("#two-014-1").val("") $("#two-014-1").val("")
$("#two-all").val("") $("#two-all").val("")
$("#msgbox").empty() $("#msgbox").html(msg)
$("#table").hide()
}) })
$("#ok").click(() => { $("#ok").click(() => {
let one_dimer = $("#one-dimer").val() let one_dimer = $("#one-dimer").val()
let one_at = $("#one-at").val()
let one_014_1 = $("#one-014-1").val()
let one_all = $("#one-all").val()
let two_dimer = $("#two-dimer").val() let two_dimer = $("#two-dimer").val()
let two_at = $("#two-at").val() let one_014_1 = $("#one-014-1").val()
let two_014_1 = $("#two-014-1").val() let two_014_1 = $("#two-014-1").val()
let one_at = $("#one-at").val()
let two_at = $("#two-at").val()
let one_all = $("#one-all").val()
let two_all = $("#two-all").val() let two_all = $("#two-all").val()
let one_dimer_ = func_dimer(one_dimer, one_all) let one_dimer_ = func_dimer(one_dimer, one_all).toFixed(PRECISION, ROUNDING)
let one_at_ = func_at(one_at, one_all) let one_at_ = func_at(one_at, one_all).toFixed(PRECISION, ROUNDING)
let one_impurities = func_impurities(one_dimer, one_at, one_014_1, one_all) let one_impurities = func_impurities(one_dimer, one_at, one_014_1, one_all).toFixed(PRECISION, ROUNDING)
let one_purity = func_purity(one_impurities) let one_purity = func_purity(one_impurities).toFixed(PRECISION, ROUNDING)
let two_dimer_ = func_dimer(two_dimer, two_all) let two_dimer_ = func_dimer(two_dimer, two_all).toFixed(PRECISION, ROUNDING)
let two_at_ = func_at(two_at, two_all) let two_at_ = func_at(two_at, two_all).toFixed(PRECISION, ROUNDING)
let two_impurities = func_impurities(two_dimer, two_at, two_014_1, two_all) let two_impurities = func_impurities(two_dimer, two_at, two_014_1, two_all).toFixed(PRECISION, ROUNDING)
let two_purity = func_purity(two_impurities) let two_purity = func_purity(two_impurities).toFixed(PRECISION, ROUNDING)
let msg = ` let average_dimer = averageFixed(average(one_dimer_, two_dimer_))
第一组结果:<br> let average_at = averageFixed(average(one_at_, two_at_))
2.2-Dimer: ${format(one_dimer_, DIMER_MAX)} <br> let average_impurities = averageFixed(average(one_impurities, two_impurities))
2-乙酰噻吩: ${format(one_at_, AT_MAX)} <br> let average_purity = averageFixed(average(one_purity, two_purity))
总杂: ${format(one_impurities, TOTAL_IMPURITIES_MAX)} <br>
纯度: ${format(one_purity, PURTY_MIN, true)} <br>
<br> let data = {
第二组结果:<br> "one": {
2.2-Dimer: ${format(two_dimer_, DIMER_MAX)} <br> "dimer": format(one_dimer_, DIMER_MAX),
2-乙酰噻吩: ${format(two_at_, AT_MAX)} <br> "at": format(one_at_, AT_MAX),
总杂: ${format(two_impurities, TOTAL_IMPURITIES_MAX)} <br> "impurities": format(one_impurities, TOTAL_IMPURITIES_MAX),
纯度: ${format(two_purity, PURTY_MIN, true)} <br> "purity": format(one_purity, 100, PURTY_MIN,)
},
"two": {
"dimer": format(two_dimer_, DIMER_MAX),
"at": format(two_at_, AT_MAX),
"impurities": format(two_impurities, TOTAL_IMPURITIES_MAX),
"purity": format(two_purity, 100, PURTY_MIN)
},
"average": {
"dimer": format(average_dimer, DIMER_MAX),
"at": format(average_at, AT_MAX),
"impurities": format(average_impurities, TOTAL_IMPURITIES_MAX),
"purity": format(average_purity, 100, PURTY_MIN)
}
}
<br> generateTable(data)
平均值:<br>
2.2-Dimer: ${format(func_average(one_dimer_, two_dimer_), DIMER_MAX)} <br>
2-乙酰噻吩: ${format(func_average(one_at_, two_at_), AT_MAX)} <br>
总杂: ${format(func_average(one_impurities, two_impurities), TOTAL_IMPURITIES_MAX)} <br>
纯度: ${format(func_average(one_purity, two_purity), PURTY_MIN, true)} <br>
<br>
操规允许:<br>
2.2-Dimer &le; ${DIMER_MAX}% <br>
2-乙酰噻吩 &le; ${AT_MAX.toFixed(1)}% <br>
总杂 &le; ${TOTAL_IMPURITIES_MAX}% <br>
纯度 &ge; ${PURTY_MIN}% <br>
`
$("#msgbox").html(msg)
}) })
let func_dimer = (dimer, all) => { })
function func_dimer(dimer, all) {
if (dimer == '' || all == '') return 0 if (dimer == '' || all == '') return 0
// dimer% = [dimer / (all * RRF)] * 100 // dimer% = [dimer / (all * RRF)] * 100
let x = decimal.mul(all, DIMER_RRF) let x = decimal.mul(all, DIMER_RRF)
let y = decimal.div(dimer, x) let y = decimal.div(dimer, x)
return decimal.mul(y, 100).toFixed(PRECISION, Decimal.ROUND_HALF_EVEN) return decimal.mul(y, 100)
} }
let func_at = (at, all) => { function func_at(at, all) {
if (at == '' || all == '') return 0 if (at == '' || all == '') return 0
// at% = [at / (all * RRF)] * 100 // at% = [at / (all * RRF)] * 100
let x = decimal.mul(all, AT_RRF) let x = decimal.mul(all, AT_RRF)
let y = decimal.div(at, x) let y = decimal.div(at, x)
return decimal.mul(y, 100).toFixed(PRECISION, Decimal.ROUND_HALF_EVEN) return decimal.mul(y, 100)
} }
// TODO: 解决总杂计算不准的问题 function func_impurities(dimer, at, l014_1, all) {
let func_impurities = (dimer, at, l014_1, all) => {
if (dimer == '' || at == '' || l014_1 == '' || all == '') return 0 if (dimer == '' || at == '' || l014_1 == '' || all == '') return 0
// x = all - dimer - at - l014_1 // x = all - dimer - at - l014_1
// y = (x / all) * 100 // y = (x / all) * 100
// impurities% = y + dimer% + at% // impurities% = y + dimer% + at%
let x = decimal.sub(all, dimer).sub(at).sub(l014_1) let x = decimal.sub(all, dimer).sub(at).sub(l014_1)
let y = decimal.div(x, all).mul(100).toFixed(PRECISION) let y = decimal.div(x, all).mul(100)
let z = decimal.add(y, func_dimer(dimer, all)).add(func_at(at, all)) let z = decimal.add(y, func_dimer(dimer, all)).add(func_at(at, all))
return z.toFixed(PRECISION, Decimal.ROUND_HALF_EVEN) return z
} }
// purty% = 100 - impurities% function func_purity(total_impurities) {
let func_purity = (total_impurities) => {
if (total_impurities == '') return 0 if (total_impurities == '') return 0
return decimal.sub(100, total_impurities).toFixed(PRECISION, Decimal.ROUND_HALF_EVEN) // purty% = 100 - impurities%
return decimal.sub(100, total_impurities)
} }
let func_average = (a, b) => decimal.add(a, b).div(2).toFixed(PRECISION, Decimal.ROUND_HALF_EVEN) function average(a, b) {
return decimal.add(a, b).div(2)
let format = (value, max, more = false) => {
let red = `<span style='color: red;'>${value}%</span>`
if (value == 0) return 'ND'
if (value < 0) return red
if (!more && value >= max) return red
if (more && value <= max) return red
return `${value}%`
} }
})
function format(value, max, min = 0) {
if (value == 0) {
return 'ND' // means 'Not Detected'
}
if (value > max || value < min) {
return `<span style='color: red;'>${value}</span>`
}
return value
}
// 对平均值进行舍入
function averageFixed(value) {
// 大于等于 1 时只保留一位小数,否则保留两位
return value.toFixed(value >= 1 ? 1 : 2, ROUNDING)
}
function generateTable(data) {
$("#table").show()
$("#Dimer>#one").html(data.one.dimer)
$("#2-AT>#one").html(data.one.at)
$("#impurities>#one").html(data.one.impurities)
$("#purity>#one").html(data.one.purity)
$("#Dimer>#two").html(data.two.dimer)
$("#2-AT>#two").html(data.two.at)
$("#impurities>#two").html(data.two.impurities)
$("#purity>#two").html(data.two.purity)
$("#Dimer>#average").html(data.average.dimer)
$("#2-AT>#average").html(data.average.at)
$("#impurities>#average").html(data.average.impurities)
$("#purity>#average").html(data.average.purity)
}
</script> </script>
</head> </head>
<body> <body>
<h3>L014-1 相关物质</h3> <h3>L014-1 相关物质</h3>
<strong>* 此功能未经详细测试,计算结果仅供参考。</strong>
<br><br>
<label for="precision">计算精度:</label><span id="precision-text">3</span>
<input type="range" id="precision" value="3" min="0" max="12">
<div class="one"> <div class="one">
第一组<br> 第一组<br>
<input type="number" name="one-dimer" id="one-dimer" inputmode="numeric" placeholder="2.2-Dimer 峰面积"> <input type="number" name="one-dimer" id="one-dimer" inputmode="numeric" placeholder="2.2-Dimer 峰面积">
@@ -200,6 +236,45 @@
</div> </div>
<br> <br>
<table id="table" style="font-size: small; width: 100%; text-align: center; display: none;">
<caption>计算结果</caption>
<tr>
<th scope="col">/</th>
<th scope="col">第一组(%)</th>
<th scope="col">第二组(%)</th>
<th scope="col">平均值(%)</th>
</tr>
<tr id="Dimer">
<th scope="row">2.2-Dimer</th>
<td id="one"></td>
<td id="two"></td>
<td id="average"></td>
</tr>
<tr id="2-AT">
<th scope="row">2-乙酰噻吩</th>
<td id="one"></td>
<td id="two"></td>
<td id="average"></td>
</tr>
<tr id="max_impurities">
<th scope="row">最大单杂</th>
<td id="one">/</td>
<td id="two">/</td>
<td id="average">/</td>
</tr>
<tr id="impurities">
<th scope="row">杂质总量</th>
<td id="one"></td>
<td id="two"></td>
<td id="average"></td>
</tr>
<tr id="purity">
<th scope="row">纯度</th>
<td id="one"></td>
<td id="two"></td>
<td id="average"></td>
</tr>
</table>
<div id="msgbox"></div> <div id="msgbox"></div>
</body> </body>

152
lod.html
View File

@@ -8,26 +8,23 @@
<title>干燥失重</title> <title>干燥失重</title>
<link rel="stylesheet" href="./github.css"> <link rel="stylesheet" href="./github.css">
<link rel="stylesheet" href="./theme.css"> <link rel="stylesheet" href="./theme.css">
<script src="./decimal.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="module"> <script type="module">
import { Decimal } from './modules/decimal.mjs'
import { IO } from './modules/tools.js' import { IO } from './modules/tools.js'
let io = new IO('lod') let io = new IO('lod')
let debug = false let debug = false
let tip = ` let tip = `
<br><b>操作过程:</b>
<br>&nbsp;&nbsp; 1. 空瓶干燥 3 小时
<br>&nbsp;&nbsp; 2. 冷却称重
<br>&nbsp;&nbsp; 3. 干燥 1 小时
<br>&nbsp;&nbsp; 4. 冷却称重(1) 并加样(2)
<br>&nbsp;&nbsp; 5. 干燥 3 小时
<br>&nbsp;&nbsp; 6. 冷却称重
<br>&nbsp;&nbsp; 7. 干燥 1 小时
<br>&nbsp;&nbsp; 8. 冷却称重(3)
<br> <br>
操作过程:
<br>
空瓶 -> 干燥3小时 -> 冷却称重 -> 干燥1小时 -> 冷却称重(1) -> 加样(2) -> 干燥3小时 -> 冷却称重 -> 干燥1小时 -> 冷却称重(3)
<br>
<br>
记录保存功能使用方法:
<br>
保存:输入样品批号,点击保存,此时第一组及第二组输入框内的数据将被存储。
<br>
恢复:输入或选择样品批号,点击恢复,此时将读取对应数据并自动填入输入框。
<br>
删除:输入或选择样品批号,点击删除,此时将从已存储的数据中删除对应数据。
` `
let decimal = Decimal.set({ let decimal = Decimal.set({
@@ -46,27 +43,23 @@
} }
$("#ok").click(() => { $("#ok").click(() => {
var m0l = $("#m0l").val() let m0l = $("#m0l").val()
var m1l = $("#m1l").val() let m1l = $("#m1l").val()
var m3l = $("#m3l").val() let m3l = $("#m3l").val()
var m0r = $("#m0r").val() let m0r = $("#m0r").val()
var m1r = $("#m1r").val() let m1r = $("#m1r").val()
var m3r = $("#m3r").val() let m3r = $("#m3r").val()
let lod1 = lod(m0l, m1l, m3l)
var lod1 = lod(m0l, m1l, m3l) let lod2 = lod(m0r, m1r, m3r)
var lod2 = lod(m0r, m1r, m3r) let average = decimal.add(lod1, lod2).div(2).toString()
var average = decimal.add(lod1, lod2).div(2).toString()
// TODO 可能干燥失重不应该使用 RSD 值来判断是否平行,可尝试使用 RD 或 RAD 判断
// let rsd = RSD(lod1, lod2)
let format = (value) => { let format = (value) => {
if (value <= 0) return `<span style="color: red;">${value}</span>` if (value <= 0) return `<span style="color: red;">${value}</span>`
return value return value
} }
var msg = ` let msg = `<br>
第一组干燥失重 = ${format(lod1)}<br> 第一组干燥失重 = ${format(lod1)}<br>
第二组干燥失重 = ${format(lod2)}<br> 第二组干燥失重 = ${format(lod2)}<br>
两组干燥失重平均值 = ${format(average)}<br> 两组干燥失重平均值 = ${format(average)}<br>
@@ -80,18 +73,12 @@ ${m0r != '' && m1r != '' ? `第二组恒重后空瓶+样 = ${decimal.add(m0r, m1
$("#clear").click(() => { $("#clear").click(() => {
if (!window.confirm("所填写的数据将被清空,确定?")) return if (!window.confirm("所填写的数据将被清空,确定?")) return
$("#lod-name").val("") $("#lod-name").val("")
$("#m0l").val("") clearInput()
$("#m1l").val("")
$("#m3l").val("")
$("#m0r").val("")
$("#m1r").val("")
$("#m3r").val("")
message(tip)
}) })
$("#new_page").click(() => { $("#new_page").click(() => {
// 将当前页面的数据封装,并通过 URL 参数传递给新页面 // 将当前页面的数据封装,并通过 URL 参数传递给新页面
let data = JSON.stringify(exportData()) let data = JSON.stringify(io.collectData($("input")))
let search = `?data=${data}`.replaceAll('"', '%22') let search = `?data=${data}`.replaceAll('"', '%22')
let url = `${window.location.origin}${window.location.pathname}${search}` let url = `${window.location.origin}${window.location.pathname}${search}`
window.open(url, "_blank") window.open(url, "_blank")
@@ -104,8 +91,7 @@ ${m0r != '' && m1r != '' ? `第二组恒重后空瓶+样 = ${decimal.add(m0r, m1
return return
} }
let lod = exportData() io.write(id, JSON.stringify(io.collectData($("input"))))
io.write(id, JSON.stringify(lod))
loadSavedContentsList() loadSavedContentsList()
}) })
@@ -117,12 +103,7 @@ ${m0r != '' && m1r != '' ? `第二组恒重后空瓶+样 = ${decimal.add(m0r, m1
} }
let lod = JSON.parse(io.read(id)) let lod = JSON.parse(io.read(id))
$("#m0l").val(lod.m0l) io.exportData(lod, $("input"))
$("#m1l").val(lod.m1l)
$("#m3l").val(lod.m3l)
$("#m0r").val(lod.m0r)
$("#m1r").val(lod.m1r)
$("#m3r").val(lod.m3r)
}) })
$("#remove").click(() => { $("#remove").click(() => {
@@ -134,7 +115,8 @@ ${m0r != '' && m1r != '' ? `第二组恒重后空瓶+样 = ${decimal.add(m0r, m1
if (confirm(`确认删除 ${id} 的数据?`)) { if (confirm(`确认删除 ${id} 的数据?`)) {
io.remove(id) io.remove(id)
loadSavedContentsList() loadSavedContentsList()
$("#clear").click() $("#lod-name").val("")
clearInput()
} }
}) })
@@ -148,44 +130,31 @@ ${m0r != '' && m1r != '' ? `第二组恒重后空瓶+样 = ${decimal.add(m0r, m1
} }
}) })
// 需要传递给新页面的数据 function clearInput() {
function exportData() { $("#m0l").val("")
let data = { $("#m1l").val("")
"time": Date.now(), $("#m3l").val("")
"id": $("#lod-name").val(), $("#m0r").val("")
"m0l": $("#m0l").val(), $("#m1r").val("")
"m1l": $("#m1l").val(), $("#m3r").val("")
"m3l": $("#m3l").val(), message(tip)
"m0r": $("#m0r").val(),
"m1r": $("#m1r").val(),
"m3r": $("#m3r").val(),
}
return data
} }
// 需要导入哪些 // 需要导入哪些
function importData(data) { function importData(data) {
io.exportData(data, $("input"))
clearInput()
// 对批号自增 // 对批号自增
let id = data.id let id = data.lod_name
if (data.id.indexOf('-')) { if (data.lod_name.indexOf('-')) {
let x = data.id.split('-') let x = data.lod_name.split('-')
id = x[x.length - 1] id = x[x.length - 1]
} }
console.log(id)
id = decimal.add(id, 1) id = decimal.add(id, 1)
if (data.id.indexOf('-')) { if (data.lod_name.indexOf('-')) {
id = data.id.substring(0, data.id.lastIndexOf('-') + 1) + id id = data.lod_name.substring(0, data.lod_name.lastIndexOf('-') + 1) + id
} }
console.log(id)
$("#lod-name").val(id) $("#lod-name").val(id)
// 暂时不需要自动填充上一个页面传来的数据
// $("#m0l").val(data.m0l)
// $("#m1l").val(data.m1l)
// $("#m3l").val(data.m3l)
// $("#m0r").val(data.m0r)
// $("#m1r").val(data.m1r)
// $("#m3r").val(data.m3r)
} }
function getUrlParams(key) { function getUrlParams(key) {
@@ -196,49 +165,12 @@ ${m0r != '' && m1r != '' ? `第二组恒重后空瓶+样 = ${decimal.add(m0r, m1
} }
} }
// 计算相对标准偏差
// Markdown 公式:$RSD = \frac{SD}{\overline{x}}*100\%$
function RSD(...array) {
let stdev = STDEV(...array)
let average = () => {
let sum = new Decimal(0)
array.forEach(value => sum = sum.plus(new Decimal(value)))
return sum.dividedBy(array.length)
}
let rsd = stdev.dividedBy(average()).times(100)
return rsd.toPrecision(5)
}
// 计算标准偏差
// Markdown 公式:$SD = \sqrt{\frac{\sum_{i=1}^{n}(x_i - \overline{x})^2}{n-1}}$
function STDEV(...array) {
// 计算所有数值的和
var sum = new Decimal(0)
array.forEach(value => sum = sum.plus(new Decimal(value)))
// 计算所有数值的平均值
let average = sum.dividedBy(array.length)
// 计算 ((每个数值减去平均值)的二次方)的和,即公式中分子的结果
var sumEach = new Decimal(0)
array.forEach(value => {
let v = new Decimal(value)
let temp = v.minus(average).toPower(2)
sumEach = sumEach.plus(temp)
})
// 以上结果除以 n-1 并开根号即得
let x = sumEach.dividedBy(array.length - 1).squareRoot()
return x
}
function loadSavedContentsList() { function loadSavedContentsList() {
// load data from localStorage to input. // load data from localStorage to input.
$("#keys").empty() $("#keys").empty()
let keys = io.listKeys().sort() let keys = io.listKeys().sort()
keys.forEach(key => { keys.forEach(key => {
let timestamp = JSON.parse(io.read(key)).time let timestamp = JSON.parse(io.read(key)).timestamp
let time = new Date(timestamp).format("yyyy-MM-dd hh:mm") let time = new Date(timestamp).format("yyyy-MM-dd hh:mm")
let option = document.createElement("option") let option = document.createElement("option")
$(option).attr("value", key) $(option).attr("value", key)

View File

@@ -1,23 +1,47 @@
import Decimal from './decimal.mjs'
class IO { class IO {
/**
* 传入一个组名,返回一个 IO 对象,后续仅对该组内的数据进行读写
* @param {string} scope 组名
*/
constructor(scope) { constructor(scope) {
this.scope = scope this.scope = scope
} }
/**
* 读取一条数据
* @param {string} id 数据的唯一标识符
* @returns 该标识符所代表的数据
*/
read(id) { read(id) {
let key = `${this.scope}/${id}` let key = `${this.scope}/${id}`
return localStorage.getItem(key) return localStorage.getItem(key)
} }
/**
* 写入一条数据
* @param {string} id 数据的唯一标识符
* @param {*} data 该标识符所代表的数据
*/
write(id, data) { write(id, data) {
let key = `${this.scope}/${id}` let key = `${this.scope}/${id}`
localStorage.setItem(key, data) localStorage.setItem(key, data)
} }
/**
* 删除一条数据
* @param {string} id 数据的唯一标识符
*/
remove(id) { remove(id) {
let key = `${this.scope}/${id}` let key = `${this.scope}/${id}`
localStorage.removeItem(key) localStorage.removeItem(key)
} }
/**
* 列出已存储的所有数据的唯一标识符
* @returns 当前组内所存储的所有数据的唯一标识符
*/
listKeys() { listKeys() {
let keys = [] let keys = []
for (const key in localStorage) { for (const key in localStorage) {
@@ -29,6 +53,173 @@ class IO {
} }
return keys return keys
} }
/**
* 确定是否需要收集 value
* @param {*} value 当前值
* @param {boolean} collectEmptyValue 是否收集空值
* @returns
*/
#checkValue = (value, collectEmptyValue) => {
return collectEmptyValue || !(value === null || value === undefined || value == '')
} }
export { IO } /**
* 检查 input 类型,若不是用户输入的内容(如 button 等),则不收集该 input 的数据
* @param {*} type input 类型
* @returns 是否需要收集该 input 的数据
*/
#checkType = (type) => {
let allows = [
// 已列出所有的 input 类型,将不收集数据的类型注释掉即可
// 'button',
'checkbox',
'color',
'date',
'datetime-local',
'email',
// 'file',
// 'hidden',
// 'image',
'month',
'number',
// 'password',
'radio',
'range',
// 'reset',
'search',
// 'submit',
'tel',
'text',
'time',
'url',
'week'
]
return allows.indexOf(type) != -1
}
/**
* 从 input 控件收集用户填入的数据
* @param {HTMLCollection | HTMLInputElement} inputs 需要收集数据的 input 标签
* @param {boolean} collectEmptyValue 是否需要收集空值
* @returns 收集的数据
*/
collectData(inputs, collectEmptyValue = true) {
if (inputs.length == 0) return
let result = {
timestamp: Date.now()
}
let safeKey = (key) => {
return key.replaceAll('-', '_')
}
for (let input of inputs) {
if (this.#checkType(input.type)) {
// 需要分类型进行收集数据
switch (input.type) {
case 'checkbox':
result[safeKey(input.name)] = input.checked
break
case 'radio':
if (input.checked) {
result[safeKey(input.name)] = input.value
}
break
default:
if (this.#checkValue(input.value, collectEmptyValue)) {
result[safeKey(input.name)] = input.value
}
break
}
}
}
return result
}
/**
* 将 data 中的值自动填入到对应的 input 中
* @param {Object} data 需要填入的数据
* @param {HTMLCollection | HTMLInputElement} inputs 需要填入数据到哪些 input
*/
exportData(data, inputs) {
let reSafeKey = (key) => {
return key.replaceAll('-', '_')
}
for (let item in data) {
for (let input of inputs) {
if (reSafeKey(input.name) == item) {
switch (input.type) {
case 'checkbox':
input.checked = data[item]
break
case 'radio':
if (input.value == data[item]) input.checked = true
break
default:
input.value = data[item]
break
}
}
}
}
}
}
class Formula {
static AVERAGE(...array) {
let sum = new Decimal(0)
array.forEach((item) => sum = sum.plus(item))
return sum.dividedBy(array.length)
}
// 计算相对标准偏差
// Markdown 公式:$RSD = \frac{SD}{\overline{x}}*100\%$
// @see Formula.STDEV()
static RSD(...array) {
let stdev = this.STDEV(...array)
let average = () => {
let sum = new Decimal(0)
array.forEach(value => sum = sum.plus(new Decimal(value)))
return sum.dividedBy(array.length)
}
let rsd = stdev.dividedBy(average()).times(100)
return rsd.toPrecision(5)
}
// 计算标准偏差
// Markdown 公式:$SD = \sqrt{\frac{\sum_{i=1}^{n}(x_i - \overline{x})^2}{n-1}}$
static STDEV(...array) {
// 计算所有数值的和
let sum = new Decimal(0)
array.forEach(value => sum = sum.plus(new Decimal(value)))
// 计算所有数值的平均值
let average = sum.dividedBy(array.length)
// 计算 ((每个数值减去平均值)的二次方)的和,即公式中分子的结果
let sumEach = new Decimal(0)
array.forEach(value => {
let v = new Decimal(value)
let temp = v.minus(average).toPower(2)
sumEach = sumEach.plus(temp)
})
// 以上结果除以 n-1 并开根号即得
let x = sumEach.dividedBy(array.length - 1).squareRoot()
return x
}
}
class Deviation {
static AbsoluteDeviation(num, nums) {
// TODO: implement this method
}
}
export { IO, Formula, Deviation }