添加工具:比例计算、生成批量下载链接、Unix 时间戳转换

This commit is contained in:
2021-11-14 16:32:20 +08:00
parent e621b09a78
commit c342f06c4c
4 changed files with 925 additions and 10 deletions

View File

@@ -11,7 +11,7 @@ let navTools = {
// 工具标题
title: '计算下载用时',
// 工具简介
desc: '根据设定的文件大小和下载速度简单计算大约下载完成所需的时间',
desc: '根据设定的文件大小和下载速度简单计算大约下载完成所需的时间',
// 组件名称
component: 'CalcDownloadTime',
// 更新时间
@@ -20,8 +20,12 @@ let navTools = {
enabled: false
},
'ratio': {
title: '计算比例',
component: 'CalcRatio'
title: '比例计算',
desc: '按设定的比例计算给出的数值所对应的数值。',
component: 'CalcRatio',
update: '2021-11-14',
version: '1',
enabled: true
},
'simple': {
title: '简易计算器',
@@ -35,13 +39,15 @@ let navTools = {
list: {
'links': {
title: '生成批量下载链接',
desc: '根据设置,生成有一定规律的用于批量下载的链接',
desc: '根据设置,生成有一定规律的用于批量下载的链接',
component: 'GenLinks',
enabled: false
update: '2021-11-14',
version: '1',
enabled: true
},
'random-str': {
title: '生成随机字符串',
desc: '生成随机组合的字符串,可用于密码',
desc: '生成随机组合的字符串,可用于密码',
component: 'GenRandomStr',
update: '2021-05-04',
version: '1',
@@ -68,9 +74,11 @@ let navTools = {
},
'timestamp': {
title: 'Unix 时间戳转换',
desc: 'Unix 时间戳转时间 / 时间转 Unix 时间戳',
desc: '时间戳转时间 / 时间转时间戳',
component: 'ConvertTimestamp',
enabled: false
update: '2021-11-14',
version: '1',
enabled: true
},
}
},
@@ -84,13 +92,13 @@ let navTools = {
},
'dynmap-renderdata-gen': {
title: 'Dynmap renderdata 生成',
desc: '生成用于 Minecraft Dynmap 插件 / 模组的 renderdata 数据',
desc: '生成用于 Minecraft Dynmap 插件 / 模组的 renderdata 数据',
component: 'MinecraftDynmapRenderdataGen',
enabled: false
},
'uuidConverter': {
title: 'UUID 转换',
desc: 'UUID 与 UUID Least、UUID Most 相互转换',
desc: 'UUID 与 UUID Least、UUID Most 相互转换',
component: 'MinecraftUUIDConverter',
enabled: false
},

View File

@@ -0,0 +1,135 @@
<template>
<div class="tool-page">
<div class="calc-mode">
<div class="title">计算模式</div>
<div class="content">
<el-select v-model="mode" size="small">
<el-option label="1 -> 2" value="1-to-2"></el-option>
<el-option label="2 -> 1" value="2-to-1"></el-option>
</el-select>
</div>
</div>
<div class="digits">
<div class="title">小数位数</div>
<div class="content">
<el-input-number
v-model="digits"
controls-position="right"
size="small"
:min="0"
:max="10"
:step="1"
step-strictly
@change="update()"
></el-input-number>
</div>
</div>
<div class="ratio-base">
<div class="title">基础比例</div>
<div class="content">
<el-input-number
v-model="base.a"
controls-position="right"
size="small"
:step="1"
@change="update()"
></el-input-number>
<span class="split">:</span>
<el-input-number
v-model="base.b"
controls-position="right"
size="small"
:step="1"
@change="update()"
></el-input-number>
</div>
</div>
<div class="ratio-calc">
<div class="title">计算比例</div>
<div class="content">
<el-input-number
v-model="calc.a"
controls-position="right"
size="small"
:step="1"
:disabled="mode === '2-to-1'"
@change="update()"
></el-input-number>
<span class="split">:</span>
<el-input-number
v-model="calc.b"
controls-position="right"
size="small"
:step="1"
:disabled="mode === '1-to-2'"
@change="update()"
></el-input-number>
</div>
</div>
</div>
</template>
<script>
import { divide, multiply, round } from 'mathjs';
export default {
name: 'CalcRatio',
data() {
return {
// 基础比例
base: {
a: 1,
b: 1,
},
// 计算比例
calc: {
a: 1,
b: 1,
},
// 小数位数
digits: 5,
// 模式
mode: '1-to-2',
}
},
methods: {
/**
* 计算
*/
update() {
const base = this.base;
const calc = this.calc;
const digits = this.digits;
const mode = this.mode;
const ratio = base.a / base.b;
if (mode === '1-to-2') {
calc.b = round(divide(calc.a, ratio), digits);
} else if (mode === '2-to-1') {
calc.a = round(multiply(calc.b, ratio), digits);
}
},
},
}
</script>
<style lang="less" scoped>
.el-input-number,
.el-select {
width: 10rem;
}
.split {
display: inline-block;
width: 2em;
text-align: center;
font-weight: bold;
}
</style>

View File

@@ -0,0 +1,323 @@
<template>
<div class="tool-page">
<div class="ctrl">
<div class="title">控制</div>
<div class="content">
<!-- 转换模式 -->
<div class="item">
<span class="label">转换模式</span>
<el-select v-model="mode" size="medium">
<el-option
v-for="item in modes"
:key="item.name"
:label="item.label"
:value="item.name"
></el-option>
</el-select>
</div>
<!-- 时间戳类型 -->
<div class="item">
<span class="label">时间戳类型</span>
<el-select v-model="tsType" size="medium">
<el-option
v-for="item in tsTypes"
:key="item.name"
:label="item.label"
:value="item.name"
></el-option>
</el-select>
</div>
<!-- 操作 -->
<div class="item">
<span class="label">操作</span>
<el-button
:type="update ? 'success' : 'warning'"
size="small"
plain
@click="toggleUpdate()"
>状态{{ update ? '自动更新' : '暂停更新' }}</el-button>
<el-button
type="primary"
size="small"
plain
@click="convert()"
>转换</el-button>
<el-button
type="danger"
size="small"
plain
@click="clear()"
>清空</el-button>
</div>
</div>
</div>
<div class="current">
<div class="title">当前</div>
<div class="content">
<div class="item">
<span class="label">时间</span>
<el-input
v-model="current.t"
size="medium"
readonly
></el-input>
</div>
<div class="item">
<span class="label">时间戳</span>
<el-input
v-model="current.ts"
size="medium"
readonly
></el-input>
</div>
</div>
</div>
<div class="inputs">
<div class="title">输入</div>
<div class="content">
<div class="notice">
<p>注意</p>
<p>本地时间 -> 时间戳的模式中若省略时间将会加上本地时区与零时区的时差后计算</p>
<p>例如北京时间UTC+8 08:00 计算东京时间UTC+9 09:00 计算</p>
</div>
<el-input
v-model="textInputs"
:placeholder="placeholder"
size="medium"
></el-input>
</div>
</div>
<div class="outputs">
<div class="title">结果</div>
<div class="content">
<el-input
v-model="textOutputs"
size="medium"
></el-input>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ConvertTimestamp',
data() {
return {
// 转换模式
mode: 't-to-ts',
modes: [
{ name: 't-to-ts', label: '本地时间 -> 时间戳' },
{ name: 'ts-to-t', label: '时间戳 -> 本地时间' },
],
// 定时器
timer: null,
// 是否更新当前时间
update: true,
// 时间戳类型
tsType: 'ms',
tsTypes: [
{ name: 'ms', label: '毫秒' },
{ name: 's', label: '秒' },
],
// 当前时间
current: {
t: '',
ts: '',
},
// 输入
textInputs: '',
// 输出
textOutputs: '',
}
},
computed: {
// 输入占位文本
placeholder() {
const mode = this.mode;
const type = this.tsType;
if (mode === 't-to-ts') {
return '参考格式:年-月-日 时:分:秒';
} else if (mode === 'ts-to-t') {
if (type === 's') {
return '参考范例1577808000';
} else if (type === 'ms') {
return '参考范例1577808000000';
}
}
return '';
},
},
created () {
this.init();
},
beforeDestroy () {
clearInterval(this.timer);
},
methods: {
/**
* 初始化
*/
init() {
this.timer = setInterval(() => {
if (this.update) {
this.current.t = this.tsToTime();
this.current.ts = this.timeToTs();
}
}, 1000);
},
/**
* 清空
*/
clear() {
this.textInputs = '';
this.textOutputs = '';
},
/**
* 转换
*/
convert() {
const mode = this.mode;
if (mode === 't-to-ts') {
this.textOutputs = this.timeToTs(this.textInputs);
} else if (mode === 'ts-to-t') {
this.textOutputs = this.tsToTime(this.textInputs);
}
},
/**
* 时间 -> 时间戳
*
* @param {string} [timeStr] 时间字符串(年-月-日 时:分:秒)
* @returns {string} 转换结果
*/
timeToTs(timeStr) {
const date = (timeStr ? new Date(timeStr) : new Date());
if (date.toString() === 'Invalid Date') {
return '格式错误';
}
const result = date.getTime();
if (this.tsType === 'ms') {
// 毫秒
return result;
} else {
// 秒
return (result / 1000).toFixed(0);
}
},
/**
* 时间戳 -> 时间
*
* @param {(number|string)} [ts] 时间戳
* @returns {string} 转换结果(年-月-日 时:分:秒)
*/
tsToTime(ts = null) {
if (ts === '') {
ts = null;
}
// 是否有参数
const tsParam = (ts !== null);
// 时间戳是秒
if (tsParam && this.tsType === 's') {
ts += '000';
}
// 转为数值
const timestamp = parseInt(ts);
if (tsParam && isNaN(timestamp)) {
return '格式错误';
}
const date = (tsParam ? new Date(timestamp) : new Date());
const t = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
};
// 补零
for (let key in t) {
(t[key] < 10) && (t[key] = ('0' + t[key]));
}
return `${t.y}-${t.m}-${t.d} ${t.h}:${t.i}:${t.s}`;
},
/**
* 切换自动更新
*/
toggleUpdate() {
this.update = !this.update;
},
},
}
</script>
<style lang="less" scoped>
.ctrl, .current {
.content {
display: flex;
flex-wrap: wrap;
}
.item {
margin: 0.5rem 0.5rem;
}
.label {
display: inline-block;
margin-right: 0.5em;
}
}
.ctrl {
.el-select {
width: 12em;
}
}
.current {
.el-input {
width: 20em;
}
.label {
width: 3.5em;
text-align: right;
}
}
.inputs, .outputs {
.el-input {
max-width: 25em;
}
}
.notice {
margin-bottom: 1em;
}
</style>

View File

@@ -0,0 +1,449 @@
<template>
<div class="tool-page">
<div class="link-base">
<div class="title">链接模板</div>
<div class="content">
<el-input
v-model="linkBase"
size="medium"
placeholder="使用 {n} 表示变量"
clearable
></el-input>
</div>
</div>
<div class="config">
<div class="title">参数配置</div>
<div class="content">
<el-radio-group v-model="mode">
<!-- 等差数列 -->
<div class="mode-item mode-as">
<div class="config-row">
<el-radio label="as">等差数列</el-radio>
</div>
<div class="config-row">
<div class="config-item">
<span class="label">首项</span>
<el-input-number
v-model="modes.as.first"
controls-position="right"
size="medium"
:step="1"
step-strictly
></el-input-number>
</div>
<div class="config-item">
<span class="label">公差</span>
<el-input-number
v-model="modes.as.diff"
controls-position="right"
size="medium"
:step="1"
step-strictly
></el-input-number>
</div>
<div class="config-item">
<span class="label">项数</span>
<el-input-number
v-model="modes.as.count"
controls-position="right"
size="medium"
:step="1"
step-strictly
></el-input-number>
</div>
</div>
<div class="config-row">
<div class="config-item">
<span class="label">格式</span>
<el-checkbox
v-model="modes.as.zero"
>补零</el-checkbox>
<el-checkbox
v-model="modes.as.reverse"
>倒序</el-checkbox>
</div>
</div>
</div>
<!-- 等差数列 -->
<!-- 等比数列 -->
<div class="mode-item mode-ps">
<div class="config-row">
<el-radio label="ps">等比数列</el-radio>
</div>
<div class="config-row">
<div class="config-item">
<span class="label">首项</span>
<el-input-number
v-model="modes.ps.first"
controls-position="right"
size="medium"
:step="1"
step-strictly
></el-input-number>
</div>
<div class="config-item">
<span class="label">公比</span>
<el-input-number
v-model="modes.ps.diff"
controls-position="right"
size="medium"
:step="1"
step-strictly
></el-input-number>
</div>
<div class="config-item">
<span class="label">项数</span>
<el-input-number
v-model="modes.ps.count"
controls-position="right"
size="medium"
:step="1"
step-strictly
></el-input-number>
</div>
</div>
<div class="config-row">
<div class="config-item">
<span class="label">格式</span>
<el-checkbox
v-model="modes.ps.zero"
>补零</el-checkbox>
<el-checkbox
v-model="modes.ps.reverse"
>倒序</el-checkbox>
</div>
</div>
</div>
<!-- 等比数列 -->
<!-- 字母变化 -->
<div class="mode-item mode-lc">
<div class="config-row">
<el-radio label="lc">字母变化</el-radio>
</div>
<div class="config-row">
<span class="label"></span>
<el-input
v-model="modes.lc.start"
size="medium"
:maxlength="1"
></el-input>
<span class="label"></span>
<el-input
v-model="modes.lc.end"
size="medium"
:maxlength="1"
></el-input>
<el-checkbox
v-model="modes.lc.reverse"
>倒序</el-checkbox>
</div>
</div>
<!-- 字母变化 -->
</el-radio-group>
</div>
</div>
<div class="action">
<div class="title">操作</div>
<div class="content">
<el-button
type="primary"
size="small"
plain
@click="generate()"
>生成链接</el-button>
<el-button
type="danger"
size="small"
plain
@click="clear()"
>清空结果</el-button>
</div>
</div>
<div class="link-result">
<div class="title">生成结果</div>
<div class="content">
<el-input
v-model="linkResult"
type="textarea"
:rows="10"
></el-input>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'GenLinks',
data() {
return {
// 链接模板
linkBase: '',
// 生成结果
linkResult: '',
// 正则,匹配链接变量
linkReg: /\{n\}/g,
// 当前模式
mode: 'as',
modes: {
// 等差数列Arithmetic Sequence
as: {
first: 0,
diff: 1,
count: 10,
zero: false,
reverse: false,
},
// 等比数列Proportional Sequence
ps: {
first: 1,
diff: 2,
count: 10,
zero: false,
reverse: false,
},
// 字母变化Letter Change
lc: {
start: 'a',
end: 'z',
reverse: false,
},
},
}
},
methods: {
/**
* 清空
*/
clear() {
this.linkResult = '';
},
/**
* 生成
*/
generate() {
const mode = this.mode;
switch (mode) {
case 'as':
this.linkResult = this.generateSeq('as');
break;
case 'ps':
this.linkResult = this.generateSeq('ps');
break;
case 'lc':
this.linkResult = this.generateLetter();
break;
default:
break;
}
},
/**
* 生成数列
*
* @param {string} typs 类型as - 等差数列ps - 等比数列)
* @returns {string} 生成结果
*/
generateSeq(type) {
const linkBase = this.linkBase;
const linkReg = this.linkReg;
if (!linkBase) {
return '';
}
const data = this.modes[type];
if (!data) {
return '';
}
const nFirst = data.first
const nCount = data.count;
const nDiff = data.diff;
const nResult = {
digits: 0, // 最大位数
numbers: [], // 生成的数值
};
const links = [];
// 等差数列公式
const expAS = (i) => {
return (nFirst + (i - 1) * nDiff);
};
// 等比数列公式
const expPS = (i) => {
return (nFirst * Math.pow(nDiff, (i - 1)));
};
// 实际使用的公式
const exp = (type === 'as' ? expAS : expPS);
// 生成数值
for (let i = 1; i <= nCount; i++) {
// 等差数列 / 等比数列
const n = exp(i);
const digits = Math.abs(n).toString().length;
(digits > nResult.digits) && (nResult.digits = digits);
nResult.numbers.push(n);
}
// 补零
if (data.zero) {
const digits = nResult.digits;
const base = Math.pow(10, digits);
const numbers = nResult.numbers;
for (let i = 0; i < numbers.length; i++) {
const n = numbers[i];
if (n >= 0) {
numbers[i] = (n / base).toFixed(digits).substr(2);
} else {
numbers[i] = '-' + (n / base).toFixed(digits).substr(3);
}
}
}
// 倒序
if (data.reverse) {
nResult.numbers.reverse();
}
// 生成链接
nResult.numbers.forEach((n) => {
links.push(linkBase.replace(linkReg, n));
});
return links.join('\n');
},
/**
* 生成字母
*/
generateLetter() {
const linkBase = this.linkBase;
const linkReg = this.linkReg;
if (!linkBase) {
return '';
}
const data = this.modes.lc;
// 编码数值
const cStart = data.start.charCodeAt(0);
const cEnd = data.end.charCodeAt(0);
const chars = [];
const links = [];
// 生成字母
if (cStart >= 65 && cStart <= 122 && cEnd >= 65 && cEnd <= 122) {
if (cStart < cEnd) {
for (let i = cStart; i <= cEnd; i++) {
// 跳过符号 [ \ ] ^ _ `
if (i >= 91 && i <= 96) {
continue;
}
chars.push(String.fromCharCode(i));
}
} else if (cStart > cEnd) {
return '字母先后顺序有误。注意:大写字母需要在前。';
} else if (cStart === cEnd) {
return '仅有 1 条链接,无需生成。';
} else {
return '未知错误。';
}
} else {
return '输入有误,请检查。';
}
// 倒序
if (data.reverse) {
chars.reverse();
}
// 生成链接
chars.forEach((c) => {
links.push(linkBase.replace(linkReg, c));
});
return links.join('\n');
},
},
}
</script>
<style lang="less" scoped>
.config {
.el-input-number {
width: 8rem;
}
.mode-item {
padding: 0.5rem 0;
&:not(:last-child) {
border-bottom: 0.0625rem solid #EEE;
}
}
.config-row {
display: flex;
align-items: center;
flex-wrap: wrap;
padding: 0.25rem 0;
}
.config-item {
margin: 0.25rem 0;
}
.label {
display: inline-block;
margin: 0 1em;
font-size: 0.8rem;
}
}
.mode-lc {
/deep/ .el-checkbox {
margin: 0 1em;
}
/deep/ .el-input {
width: 3.5em;
}
/deep/ .el-input__inner {
text-align: center;
}
}
</style>