v1.0.0 — 评分规则大改 & UI优化

- 责善改过:纵向排列、输入框样式对齐立志、扁平化去内层卡片嵌套
- 自动保存:blur 触发替代 input 防抖,静默保存
- 日评分:3项全有 → 60分 + 多出条数×5;缺失 → 条数×5
- 周评分:日峰值累加制,取消百分比上限
- 修复 preset-note 溢出、清理冗余代码
This commit is contained in:
mac
2026-06-02 23:31:46 +08:00
parent 2545f39af9
commit 2d6eb4dc35
3 changed files with 77 additions and 75 deletions

27
app.py
View File

@@ -84,26 +84,23 @@ def compute_stats():
evening = data.get('evening', [])
study = data.get('study', [])
total_morning += sum(1 for x in morning if isinstance(x, str) and x.strip())
total_evening += sum(1 for x in evening if (
morning_count = sum(1 for x in morning if isinstance(x, str) and x.strip())
evening_count = sum(1 for x in evening if (
isinstance(x, str) and x.strip() or
isinstance(x, dict) and (x.get('mistake', '') or '').strip()
))
total_study += sum(1 for x in study if x.get('done'))
study_count = sum(1 for x in study if x.get('done'))
day_score = 0
has_morning = any(isinstance(x, str) and x.strip() for x in morning)
has_evening = any(
(isinstance(x, str) and x.strip()) or
(isinstance(x, dict) and (x.get('mistake', '') or '').strip())
for x in evening
)
has_study = any(x.get('done') for x in study)
if has_morning: day_score += 1
if has_evening: day_score += 1
if has_study: day_score += 1
total_morning += morning_count
total_evening += evening_count
total_study += study_count
calendar[d] = 'pass' if day_score >= 2 else 'fail'
# 新评分3 项都有 → 达标 60 分 + 额外加分
all_three = morning_count > 0 and evening_count > 0 and study_count > 0
if all_three:
extra = (morning_count - 1) + (evening_count - 1) + (study_count - 1)
# day_score = 60 + extra * 5 (不在此处使用,仅用于日历状态)
calendar[d] = 'pass' if all_three else 'fail'
return dict(
total_days=total_days, total_morning=total_morning,

View File

@@ -9,7 +9,6 @@
var calYear, calMonth; // 日历显示的月份
var activePanel = 'daily'; // 当前面板
var weekOffset = 0; // 每周评分偏移
var autoSaveTimer = null; // 自动保存防抖
var lastSavedData = null; // 上次保存快照
var lastSavedDate = null; // 上次保存日期
var selectedDate = null; // 日历中选中日期
@@ -399,7 +398,6 @@
/* ================================================================
Load & Auto Save Checkin
================================================================ */
var lastSavedData = null; // 上次保存的数据快照,避免重复保存
window.loadCheckin = function() {
var date = document.getElementById('check-date').value;
@@ -426,22 +424,18 @@
});
};
/** 自动保存(带防抖) */
/** 自动保存 — 失去焦点/勾选/删除时触发 */
function triggerAutoSave() {
clearTimeout(autoSaveTimer);
autoSaveTimer = setTimeout(function() {
var date = document.getElementById('check-date').value;
if (!date) return;
var data = buildData();
var dataStr = JSON.stringify(data);
if (dataStr === lastSavedData) return; // 无变化,跳过
apiSaveCheckin(date, data, function(err){
if (err) { showToast('自动保存失败', 'error'); return; }
lastSavedData = dataStr;
showToast('已自动保存', 'info');
loadStats();
});
}, 1500);
var date = document.getElementById('check-date').value;
if (!date) return;
var data = buildData();
var dataStr = JSON.stringify(data);
if (dataStr === lastSavedData) return;
apiSaveCheckin(date, data, function(err){
if (err) { showToast('保存失败', 'error'); return; }
lastSavedData = dataStr;
loadStats();
});
}
/** 为每日打卡面板绑定自动保存事件 */
@@ -449,12 +443,12 @@
var panel = document.getElementById('panel-daily');
if (!panel) return;
// 输入框 & 文本域:input 事件
panel.addEventListener('input', function(e) {
// 输入框 & 文本域:失去焦点时保存
panel.addEventListener('blur', function(e) {
if (e.target.matches('input[type="text"], textarea, input[type="date"]')) {
triggerAutoSave();
}
});
}, true);
// 复选框change 事件
panel.addEventListener('change', function(e) {
@@ -470,13 +464,6 @@
setTimeout(triggerAutoSave, 100); // 等 DOM 更新后
}
});
// 预设项目勾选切换
panel.addEventListener('change', function(e) {
if (e.target.matches('.preset-check input[type="checkbox"]')) {
triggerAutoSave();
}
});
}
/* ================================================================
@@ -522,21 +509,27 @@
if (dd) {
totalDays++;
var dayScore = 0;
var morning = dd.morning || [];
var hasMorning = morning.some(function(x){ return x && typeof x === 'string' && x.trim(); });
var evening = dd.evening || [];
var hasEvening = evening.some(function(x){
var study = dd.study || [];
var morningCount = morning.filter(function(x){ return x && typeof x === 'string' && x.trim(); }).length;
var eveningCount = evening.filter(function(x){
var mst = typeof x === 'string' ? x : (x.mistake || '');
return mst && typeof mst === 'string' && mst.trim();
});
var study = dd.study || [];
var hasStudy = study.some(function(x){ return x.done; });
if (hasMorning) dayScore++;
if (hasEvening) dayScore++;
if (hasStudy) dayScore++;
}).length;
var studyCount = study.filter(function(x){ return x.done; }).length;
var allThree = morningCount > 0 && eveningCount > 0 && studyCount > 0;
var dayScore, status;
if (allThree) {
dayScore = 60 + (morningCount - 1 + eveningCount - 1 + studyCount - 1) * 5;
status = 'pass';
} else {
dayScore = (morningCount + eveningCount + studyCount) * 5;
status = 'fail';
}
totalScore += dayScore;
var status = dayScore >= 2 ? 'pass' : 'fail';
dayCells.push({
ds: ds, dayScore: dayScore, status: status,
@@ -550,21 +543,21 @@
}
}
var score = totalDays > 0 ? Math.round((totalScore / (totalDays * 3)) * 100) : 0;
var score = totalScore;
document.getElementById('weekly-score').textContent = score;
var ring = document.querySelector('.score-ring');
if (ring) {
var R = 50, C = 2 * Math.PI * R;
ring.setAttribute('stroke-dasharray', C);
ring.setAttribute('stroke-dashoffset', C - (C * score / 100));
ring.setAttribute('stroke-dashoffset', C - (C * totalDays / 7));
}
var txt = '';
if (score >= 90) txt = '卓越!磁场非常强大';
else if (score >= 70) txt = '良好,继续保持';
else if (score >= 50) txt = '一般,需要加强';
else txt = '较弱,急需调整';
if (totalDays >= 7) txt = '全勤!本周每天都有记录';
else if (totalDays >= 5) txt = '良好,保持了大部分记录';
else if (totalDays >= 3) txt = '一般,需要更规律打卡';
else txt = '本周记录偏少,加油!';
document.getElementById('score-text').textContent = txt;
var completedDays = dayCells.filter(function(c){ return c.status === 'pass'; }).length;
@@ -577,11 +570,11 @@
var cell = dayCells[g];
var dateParts = cell.ds.split('-');
var shortDate = dateParts[1] + '/' + dateParts[2];
var badgeText = cell.status === 'pass' ? '完成' : (cell.status === 'fail' ? '未达标' : '未打卡');
var badgeText = cell.status === 'pass' ? '达标' : (cell.status === 'fail' ? '未达标' : '未打卡');
gridHtml += '<div class="day-cell ' + cell.status + '">' +
'<div class="day-label">' + cell.weekday + '</div>' +
'<div class="day-date">' + shortDate + '</div>' +
'<div class="day-score">' + cell.dayScore + '<span style="font-size:11px;font-weight:400">/3</span></div>' +
'<div class="day-score">' + cell.dayScore + '<span style="font-size:11px;font-weight:400"></span></div>' +
'<div class="day-badge">' + badgeText + '</div>' +
'</div>';
}

View File

@@ -618,17 +618,14 @@ body {
/* 责善改过 横排双输入 */
.evening-row {
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 12px 10px 10px;
margin-bottom: 10px;
background: var(--bg);
transition: border-color 0.2s;
padding: 0 0 12px;
margin-bottom: 12px;
border-bottom: 1px solid var(--border);
}
.evening-row:focus-within {
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(74,108,247,0.06);
.evening-row:last-child {
border-bottom: none;
margin-bottom: 0;
}
.evening-header {
@@ -645,10 +642,10 @@ body {
}
.mistake-row {
display: grid;
grid-template-columns: 1fr 1fr;
display: flex;
flex-direction: column;
gap: 8px;
align-items: start;
align-items: stretch;
}
.mistake-row .col {
@@ -664,7 +661,23 @@ body {
padding-left: 2px;
}
.mistake-row input[type="text"] { width: 100%; }
.mistake-row input[type="text"] {
width: 100%;
padding: 9px 12px;
border: 1.5px solid var(--border);
border-radius: var(--radius-sm);
font-size: 13px;
font-family: inherit;
color: var(--text);
background: var(--bg);
transition: border-color 0.2s, background 0.2s;
}
.mistake-row input[type="text"]:focus {
outline: none;
border-color: var(--primary);
background: var(--card);
box-shadow: 0 0 0 3px rgba(74,108,247,0.08);
}
/* 勤学预设项目 */
@@ -705,7 +718,7 @@ body {
.preset-note {
display: none;
width: 100%;
width: calc(100% - 24px);
margin-top: 6px;
margin-left: 24px;
padding: 6px 10px;
@@ -1088,7 +1101,6 @@ body {
.weekly-overview { flex-direction: column; gap: 16px; text-align: center; }
.week-days-grid { grid-template-columns: repeat(4, 1fr); }
.history-grid { grid-template-columns: 1fr; }
.mistake-row { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {