Compare commits

...

12 Commits

4 changed files with 139 additions and 26 deletions

15
app.py
View File

@@ -427,7 +427,20 @@ def api_calendar_sync_all():
summary = (evt.get('summary') or '').strip()
if not summary:
continue
text = f"{evt['time']}{summary}" if evt.get('time') else summary
date_str = evt.get('date', '')
if date_str:
try:
parts = date_str.split('-')
date_label = f"{int(parts[1])}{int(parts[2])}"
except:
date_label = date_str
else:
date_label = ''
time_str = evt.get('time', '')
prefix = f"{date_label} {time_str}" if date_label and time_str else \
(f"{date_label}" if date_label else \
(f"{time_str}" if time_str else ''))
text = f"{prefix}{summary}" if prefix else summary
loc = evt.get('location', '')
if loc:
text += f" @{loc}"

View File

@@ -246,6 +246,12 @@
var v = mEls[i].value.trim();
if (v) morningItems.push(v);
}
// 收集日历日程
var calEls = document.querySelectorAll('#morning-calendar-list .cal-task-item span:last-child');
for (var ci=0; ci<calEls.length; ci++) {
var txt = calEls[ci].textContent.trim();
if (txt) { morningItems.push(txt); }
}
var eveningItems = [];
var eRows = document.querySelectorAll('#evening-list .evening-row');
@@ -263,7 +269,7 @@
var countEl = sCards[k].querySelector('.study-card-count');
var name = nameEl ? nameEl.textContent.trim() : '';
var count = parseInt(countEl ? countEl.textContent : '0') || 0;
studyItems.push({ name: name, count: count });
studyItems.push({ name: name, count: count, last_checkin_date: sCards[k].dataset.lastDate || '' });
}
return { morning: morningItems, evening: eveningItems, study: studyItems };
@@ -275,12 +281,29 @@
var evening = data.evening || [];
var study = data.study || [];
// 早间立志
// 本周重点:拆分手动任务 vs 日历日程
document.getElementById('morning-list').innerHTML = '';
document.getElementById('morning-calendar-list').innerHTML = '';
var manualItems = [];
var calItems = [];
if (morning.length === 0) morning = [''];
for (var m=0; m<morning.length; m++) {
var txt = typeof morning[m] === 'string' ? morning[m] : (morning[m].text || '');
addMorningRow(txt);
if (txt && txt.trim().startsWith('【')) { calItems.push(txt); }
else { manualItems.push(txt); }
}
if (manualItems.length === 0) manualItems = [''];
for (var a=0; a<manualItems.length; a++) { addMorningRow(manualItems[a]); }
if (calItems.length > 0) {
document.getElementById('morning-calendar').style.display = 'block';
for (var b=0; b<calItems.length; b++) {
var div = document.createElement('div');
div.className = 'cal-task-item';
div.innerHTML = '<span class="cal-task-dot">&#128197;</span><span>' + esc(calItems[b]) + '</span>';
document.getElementById('morning-calendar-list').appendChild(div);
}
} else {
document.getElementById('morning-calendar').style.display = 'none';
}
// 责善改过
@@ -291,16 +314,25 @@
addEveningRow(item.mistake || '', item.improvement || '');
}
// 勤学 — 渲染预设项目并回填状态
// 勤学 — 渲染已保存习惯 + 未渲染预设
var studyMap = {};
for (var si=0; si<study.length; si++) {
studyMap[study[si].name] = study[si];
}
var rendered = {};
document.getElementById('study-cards').innerHTML = '';
for (var sk=0; sk<study.length; sk++) {
var item = study[sk];
if (item.name) {
renderStudyCard(item.name, item.count || 0, item.last_checkin_date || '');
rendered[item.name] = true;
}
}
for (var sp=0; sp<PRESET_STUDY_ITEMS.length; sp++) {
var sName = PRESET_STUDY_ITEMS[sp];
var saved = studyMap[sName] || { count: 0 };
renderStudyCard(sName, saved.count || 0);
if (!rendered[sName]) {
renderStudyCard(sName, 0, '');
}
}
}
@@ -345,32 +377,66 @@
/* ── 勤学打卡卡片 ── */
function renderStudyCard(name, count) {
function renderStudyCard(name, count, lastDate) {
var container = document.getElementById('study-cards');
var today = fmtDate(new Date());
var checked = count > 0;
var checkedToday = lastDate === today;
var btnText = checkedToday ? '已打卡' : '打卡';
var div = document.createElement('div');
div.className = 'study-card' + (count > 0 ? ' checked' : '');
div.className = 'study-card' + (checked ? ' checked' : '');
div.setAttribute('data-last-date', lastDate || '');
div.innerHTML =
'<button class="study-card-del" onclick="deleteStudyItem(this)" title="删除">×</button>' +
'<div class="study-card-info">' +
'<span class="study-card-name">' + esc(name) + '</span>' +
'<span class="study-card-count">' + count + '</span>' +
'</div>' +
'<button class="study-card-btn" onclick="checkinStudy(this)">打卡</button>';
'<button class="study-card-btn' + (checkedToday ? ' done' : '') + '" onclick="checkinStudy(this)">' + btnText + '</button>';
container.appendChild(div);
}
window.toggleStudyEdit = function() {
var panel = document.getElementById('panel-daily');
var btn = document.getElementById('study-edit-toggle');
if (!panel || !btn) return;
var editing = panel.classList.toggle('editing-study');
btn.classList.toggle('active', editing);
};
window.addStudyItem = function() {
var name = prompt('请输入新习惯名称:');
if (!name || !name.trim()) return;
renderStudyCard(name.trim(), 0, '');
triggerAutoSave();
};
window.deleteStudyItem = function(btn) {
var card = btn.closest('.study-card');
var nameEl = card.querySelector('.study-card-name');
if (nameEl && !confirm('确定删除「' + nameEl.textContent + '」?')) return;
card.remove();
triggerAutoSave();
};
window.checkinStudy = function(btn) {
var card = btn.closest('.study-card');
var today = fmtDate(new Date());
if (card.dataset.lastDate === today) { showToast('今天已打卡', 'info'); return; }
var countEl = card.querySelector('.study-card-count');
var count = parseInt(countEl.textContent) + 1;
countEl.textContent = count;
if (count > 0) card.classList.add('checked');
card.dataset.lastDate = today;
card.classList.add('checked');
btn.classList.add('done');
btn.textContent = '已打卡';
triggerAutoSave();
};
function initStudyPresets() {
document.getElementById('study-cards').innerHTML = '';
for (var i=0; i<PRESET_STUDY_ITEMS.length; i++) {
renderStudyCard(PRESET_STUDY_ITEMS[i], 0);
renderStudyCard(PRESET_STUDY_ITEMS[i], 0, '');
}
}
@@ -415,10 +481,12 @@
html += '<div class="sync-day">';
html += '<div class="sync-day-header"><span>' + day.date + '</span></div>';
day.events.forEach(function(e) {
var timeStr = e.time ? ' (' + e.time + ')' : '';
var timeStr = e.time ? e.time : '';
var dateStr = e.date || '';
html += '<div class="sync-event">' +
'<span class="sync-event-icon">📌</span>' +
'<span class="sync-event-text">' + esc(e.summary) + '<span class="sync-event-time">' + esc(timeStr) + '</span></span>' +
'<span class="sync-event-text">' + esc(e.summary) + '</span>' +
'<span class="sync-event-meta">' + esc(dateStr) + (timeStr ? ' ' + esc(timeStr) : '') + '</span>' +
'</div>';
});
html += '</div>';

View File

@@ -832,9 +832,7 @@ body {
}
/* 编辑模式下才显示的元素 */
.edit-only { display: none; }
.card.editing .edit-only { display: flex; }
.card.editing .btn-del { display: inline-flex; }
.edit-only { display: flex; }
/* 默认隐藏删除按钮 */
.btn-del { display: none; }
@@ -1030,7 +1028,7 @@ body {
gap: 5px;
width: 100%;
padding: 9px;
margin-top: auto;
margin-top: 12px;
border: 1.5px dashed var(--primary);
background: var(--primary-light);
color: var(--primary);
@@ -1462,7 +1460,7 @@ body {
.sync-event + .sync-event { border-top: 1px solid var(--bg); }
.sync-event-icon { flex-shrink: 0; }
.sync-event-text { flex: 1; }
.sync-event-time { font-size: 11px; color: var(--text-muted); margin-left: 6px; }
.sync-event-meta { font-size: 11px; color: var(--text-muted); }
.sync-modal-footer {
padding: 12px 20px;
border-top: 1px solid var(--border);
@@ -1784,12 +1782,31 @@ body {
.quad-grid { grid-template-columns: 1fr; grid-template-rows: auto; }
}
/* 勤学打卡卡片 */
.study-card { display:flex; align-items:center; gap:12px; padding:10px 14px; border:1px solid var(--border); border-radius:var(--radius-sm); background:var(--card); margin-bottom:8px; transition:all 0.15s; }
/* 勤学打卡卡片 - 单行样式 */
#study-cards { display: flex; flex-direction: column; gap: 8px; }
.study-card { position:relative; display:flex; align-items:center; gap:12px; padding:10px 14px; border:1px solid var(--border); border-radius:var(--radius-sm); background:var(--card); transition:all 0.15s; }
.study-card:hover { border-color:var(--primary); box-shadow:var(--shadow); }
.study-card.checked { border-color:var(--success); background:var(--success-light); }
.study-card-info { flex:1; display:flex; align-items:center; justify-content:space-between; gap:8px; }
.study-card-info { display:flex; align-items:center; gap:8px; flex:1; }
.study-card-name { font-size:13px; color:var(--text); font-weight:500; }
.study-card-count { font-size:12px; color:var(--text-muted); font-weight:600; background:var(--bg); border-radius:50%; width:24px; height:24px; display:flex; align-items:center; justify-content:center; }
.study-card-count { font-size:12px; color:var(--text-muted); font-weight:600; background:var(--bg); border-radius:50%; width:24px; height:24px; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.study-card.checked .study-card-count { background:var(--success); color:#FFF; }
.study-card-btn { padding:6px 14px; border:1px solid var(--primary); border-radius:var(--radius-sm); background:transparent; color:var(--primary); font-size:12px; font-weight:500; cursor:pointer; transition:all 0.15s; font-family:inherit; white-space:nowrap; }
.study-card-btn { padding:5px 12px; border:1px solid var(--primary); border-radius:var(--radius-sm); background:transparent; color:var(--primary); font-size:12px; font-weight:500; cursor:pointer; transition:all 0.15s; font-family:inherit; white-space:nowrap; }
.study-card-btn:hover { background:var(--primary); color:#FFF; }
.study-card-btn.done { background:var(--success-light); color:var(--success); border-color:var(--success); cursor:default; }
.study-card-del { display:none; }
#panel-daily.editing-study .study-card-del { display:flex; align-items:center; justify-content:center; position:absolute; top:50%; transform:translateY(-50%); right:54px; border:none; background:var(--danger-light); color:var(--danger); cursor:pointer; font-size:14px; width:22px; height:22px; border-radius:50%; line-height:1; }
#panel-daily.editing-study .study-card-del:hover { background:var(--danger); color:#FFF; }
.edit-only-study { display: none; }
#panel-daily.editing-study .edit-only-study { display: flex; }
/* 编辑模式下显示删除按钮 */
.btn-del { display: none; }
.card.editing .btn-del { display: inline-flex; }
/* 本周日程 */
.morning-calendar { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 12px; }
.morning-calendar-head { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--text-muted); margin-bottom: 8px; }
.cal-task-item { display: flex; align-items: flex-start; gap: 8px; padding: 6px 0; font-size: 13px; color: var(--text-dim); line-height: 1.5; border-bottom: 1px solid var(--bg); }
.cal-task-item:last-child { border-bottom: none; }
.cal-task-dot { flex-shrink: 0; font-size: 14px; margin-top: 1px; }

View File

@@ -157,6 +157,13 @@
<button class="btn-add edit-only" onclick="addMorning()">
<svg class="icon-sm"><use href="#icon-plus"/></svg> 增加一条
</button>
<div class="morning-calendar" id="morning-calendar" style="display:none">
<div class="morning-calendar-head">
<svg class="icon-sm"><use href="#icon-calendar"/></svg>
<span>本周日程</span>
</div>
<div id="morning-calendar-list"></div>
</div>
</div>
<div class="card card-evening daily-card" data-card="evening">
<div class="card-head">
@@ -176,11 +183,19 @@
</div>
<div class="card card-study daily-card" data-card="study">
<div class="card-head">
<svg class="icon-h2"><use href="#icon-book-open"/></svg>
<h3>勤学打卡</h3>
<div class="card-head-left">
<svg class="icon-h2"><use href="#icon-book-open"/></svg>
<h3>勤学打卡</h3>
</div>
<button class="btn-edit-toggle" id="study-edit-toggle" onclick="toggleStudyEdit()" title="编辑">
<svg class="icon-sm"><use href="#icon-pencil"/></svg>
</button>
</div>
<p class="card-desc">本周修行清单</p>
<div id="study-cards"></div>
<button class="btn-add edit-only-study" onclick="addStudyItem()" style="margin-top:10px">
<svg class="icon-sm"><use href="#icon-plus"/></svg> 增加习惯
</button>
</div>
</div>
</div><!-- /daily-main -->