<header>
<h1>评分</h1>
</header>
<main>
<section>
<h2>睡眠 (30%) <span class="score">{{sleepScore.total}}分</span></h2>
<div>
<span>时长:</span>
<input type="number" min="0" max="24" step="1" v-model="sleep.duration.hour">
<span>:</span>
<input type="number" min="0" max="60" step="1" v-model="sleep.duration.minute">
<span class="score">{{sleepScore.duration}}分</span>
</div>
<div>
<span>入睡:</span>
<input type="number" min="0" max="24" step="1" v-model="sleep.bedtime.hour">
<span>:</span>
<input type="number" min="0" max="60" step="1" v-model="sleep.bedtime.minute">
<span class="score">{{sleepScore.bedtime}}分</span>
</div>
<div>
<span>手表评分:</span>
<input type="number" min="0" step="1" v-model="sleep.watchScore">
<span>分</span>
<span class="score">{{sleepScore.watchScore}}分</span>
</div>
</section>
<section>
<h2>习惯 (15%) <span class="score">{{habitScore.total}}分</span></h2>
<div>
<span>Anki:</span>
<input type="number" min="0" step="1" v-model="habit.anki">
<span>张</span>
<span class="score">{{habitScore.anki}}分</span>
</div>
<div>
<span>背单词:复习</span>
<input type="number" min="0" step="1" v-model="habit.words.review">
<span>个,新学</span>
<input type="number" min="0" step="1" v-model="habit.words.learn">
<span>个</span>
<span class="score">{{habitScore.words}}分</span>
</div>
</section>
<section>
<h2>作业 (40%) <span class="score">{{homeworkScore.total}}分</span></h2>
<div>
<span>开始:</span>
<span>+</span>
<input type="number" min="0" step="1" v-model="homework.start">
<span>min</span>
<span class="score">{{homeworkScore.start}}分</span>
</div>
<div>
<span>有效时间:</span>
<input type="number" min="0" step="1" v-model="homework.validTime.hour">
<span>:</span>
<input type="number" min="0" step="1" v-model="homework.validTime.minute">
<span class="score">{{homeworkScore.validTime}}分</span>
</div>
<div>
<span>结束:</span>
<span>+</span>
<input type="number" min="0" step="1" v-model="homework.end">
<span>min</span>
</div>
<div>
<span>总作业量:</span>
<input type="number" min="0" step="1" v-model="homework.total">
<span>min</span>
</div>
<div>
<span>效率:{{(homeworkScore.efficiencyValue * 100).toFixed(2)}}%</span>
<span class="score">{{homeworkScore.efficiency}}分</span>
</div>
</section>
<section>
<h2>卫生 (5%) <span class="score">{{bathScore}}分</span></h2>
<div>
<span>距离上一次洗澡:</span>
<input type="number" min="0" step="1" v-model="bath.days">
<span>天</span>
</div>
<div>
<span>季节</span>
<select v-model="bath.season">
<option value="summer">夏</option>
<option value="spring">春/秋</option>
<option value="winter">冬</option>
</select>
</div>
</section>
<section>
<h2>运动 (10%) <span class="score">{{sportScore}}分</span></h2>
<div>
<span>中高强度时长:</span>
<input type="number" min="0" step="1" v-model="sport.workout">
<span>min</span>
</div>
<div>
<span>1000m</span>
<input type="number" min="0" step="1" v-model="sport.oneKilo.minute">
<span>:</span>
<input type="number" min="0" step="1" v-model="sport.oneKilo.second">
</div>
</section>
<section>
<h2>总评</h2>
<div class="score">{{total.score}} 分</div>
<div class="score">{{total.level}} 级</div>
</section>
</main>
<template>
格式化
支持Emmet,输入 p 后按 Tab键试试吧!
body {
text-align: center;
}
input[type="number"] {
width: 2.5rem;
margin: 0.25rem 0.5rem;
text-align: right;
}
.score {
color: purple;
margin-left: 1rem;
font-weight: bold;
}
h2 > .score {
font-size: 1.25rem;
color: blue;
}
section > .score {
font-size: 1.5rem;
color: darkorange;
}
body {
padding: 3rem 0;
}
// 示例代码
import { reactive, computed, watch } from "vue";
const storageReactive = (key, obj) => {
const storageData = localStorage.getItem(`SC_${key}`);
const result = reactive(storageData ? JSON.parse(storageData) : obj);
watch(() => result, newObj => {
localStorage.setItem(`SC_${key}`, JSON.stringify(newObj));
}, { immediate: true, deep: true });
return result;
};
const sleep = storageReactive("sleep", {
duration: {
hour: 3,
minute: 0,
},
bedtime: {
hour: 24,
minute: 0,
},
watchScore: 50,
});
const habit = storageReactive("habit", {
anki: 0,
words: {
review: 0,
learn: 0,
}
});
const homework = storageReactive("homework", {
start: 180,
end: 360,
validTime: {
hour: 0,
minute: 0,
},
total: 30,
});
const bath = storageReactive("bath", {
days: 7,
season: "spring",
});
const sport = storageReactive("sport", {
workout: 0,
oneKilo: {
minute: 8,
second: 0,
},
});
function linear(x, points, upK) {
for (let i in points) {
const point = points[i]
const [target, score] = point;
if (x <= target) {
if (parseInt(i) === 0) {
return score;
}
const [prevTarget, prevScore] = points[i - 1];
return Math.round((x - prevTarget) / (target - prevTarget) * (score - prevScore) + prevScore);
}
}
const [target, score] = points[points.length - 1];
return Math.round(score + (x - target) * upK);
}
const sleepScore = computed(() => {
const duration = linear(sleep.duration.hour + sleep.duration.minute / 60, [[3, 30], [4.5, 60], [6, 90], [7.5, 100]], 0);
const bedtime = linear((sleep.bedtime.hour + sleep.bedtime.minute / 60 + 8) % 24, [[6.5, 100], [7.5, 80], [8.5, 60], [10.5, 0]], 0);
const watchScore = linear(sleep.watchScore, [[35, 0], [85, 100]], 2);
const total = Math.round(duration * 0.2 + bedtime * 0.4 + watchScore * 0.4);
return {
duration,
bedtime,
watchScore,
total,
};
});
const habitScore = computed(() => {
const anki = linear(habit.anki, [[0, 40], [20, 60], [40, 80], [80, 100]], 0.5);
const words = linear(habit.words.review + habit.words.learn * 3, [[0, 40], [20, 70], [40, 90], [60, 100]], 1);
const total = Math.round(anki * 0.5 + words * 0.5);
return {
anki,
words,
total,
}
});
const homeworkScore = computed(() => {
const start = linear(homework.start, [[0, 140], [40, 100], [80, 60], [120, 0]], 0);
const validTime = linear(homework.validTime.hour * 60 + homework.validTime.minute, [[0, 30], [60, 60], [120, 90], [180, 100]], 0.1);
const efficiencyValue = homework.total / (homework.end - homework.start);
const efficiency = linear(efficiencyValue, [[0.2, 0], [0.5, 60], [0.8, 90], [0.95, 100]], 100);
const total = Math.round(start * 0.4 + validTime * 0.2 + efficiency * 0.4);
return {
start,
validTime,
efficiencyValue,
efficiency,
total,
}
});
const bathScore = computed(() => {
const points = bath.season === "winter" ? [
[0, 100], [2, 90], [5, 60], [8, 0]] : (bath.season === "spring" ? [
[0, 100], [1, 95], [2, 85], [5, 40], [7, 0]
] : [[0, 100], [2, 80], [5, 0]]);
const total = linear(bath.days, points, 0);
return total;
});
const sportScore = computed(() => {
const workout = linear(sport.workout, [[0, 30], [10, 60], [25, 90], [35, 100]], 2);
const oneKilo = linear(sport.oneKilo.minute * 60 + sport.oneKilo.second, [
[180, 200], [270, 120], [300, 100], [360, 60], [480, 0]
], 0);
return Math.round(Math.pow(Math.pow(workout, 4) + Math.pow(oneKilo, 4), 0.25));
});
const total = computed(() => {
const score = Math.round(sleepScore.value.total * 0.3 + habitScore.value.total * 0.15 + homeworkScore.value.total * 0.4 + bathScore.value * 0.05 + sportScore.value * 0.1);
const levelMap = [
[99, "U+"], [97, "U"], [95, "S+"], [93, "S"], [90, "A+"], [87, "A"], [84, "B+"],
[80, "B"], [76, "B-"], [72, "C+"], [66, "C"], [60, "C-"], [53, "D+"], [45, "D"],
[30, "E"]
];
const level = levelMap.find(([threshold]) => score >= threshold)?.[1] || 'F';
return {
score,
level,
};
})