优化 UI 和交互体验

- 按钮改用 SVG 图标,更现代简洁
- 移除按钮文字,纯图标设计
- 添加自定义确认弹窗,替换浏览器默认弹窗
- 删除功能添加确认对话框
- 优化按钮动画和光晕效果
This commit is contained in:
ching 2026-02-21 09:29:13 +00:00
parent b4cd0df014
commit c9ab4da3b5
3 changed files with 871 additions and 770 deletions

View File

@ -1,98 +1,124 @@
const API_BASE = ''; const API = '';
// 状态 // ===== STATE =====
let currentRecordId = null; let currentId = null;
let lastRecordTime = 0; let lastClick = 0;
let longPressTimer = null; let pressTimer = null;
let isLongPress = false; let isLongPress = false;
const LONG_PRESS_DURATION = 500; // 长按触发时间 const DEBOUNCE = 200; // 200ms 防抖
const LONG_PRESS = 600; // 600ms 长按
// DOM 元素 // ===== DOM =====
const triggerArea = document.getElementById('trigger-area');
const dinBtn = document.getElementById('din-btn'); const dinBtn = document.getElementById('din-btn');
const inputOverlay = document.getElementById('input-overlay'); const floatInput = document.getElementById('float-input');
const quickInput = document.getElementById('quick-input'); const floatField = document.getElementById('float-field');
const overlay = document.getElementById('overlay');
const toast = document.getElementById('toast'); const toast = document.getElementById('toast');
const achievementToast = document.getElementById('achievement-toast'); const confirmOverlay = document.getElementById('confirm-overlay');
const confirmOk = document.getElementById('confirm-ok');
const confirmCancel = document.getElementById('confirm-cancel');
// ========== 核心交互:超快记录 ========== // ===== CUSTOM CONFIRM DIALOG =====
function showConfirm(message, title = '确认删除', icon = '🗑️') {
// 点击/触摸开始 return new Promise((resolve) => {
dinBtn.addEventListener('touchstart', handleStart, { passive: false }); document.querySelector('.confirm-title').textContent = title;
dinBtn.addEventListener('mousedown', handleStart); document.querySelector('.confirm-message').textContent = message;
document.querySelector('.confirm-icon').textContent = icon;
// 点击/触摸结束
dinBtn.addEventListener('touchend', handleEnd, { passive: false }); confirmOverlay.classList.add('active');
dinBtn.addEventListener('mouseup', handleEnd);
dinBtn.addEventListener('mouseleave', cancelLongPress); const onOk = () => {
confirmOverlay.classList.remove('active');
// 防止双击缩放 cleanup();
dinBtn.addEventListener('touchmove', (e) => e.preventDefault(), { passive: false }); resolve(true);
};
function handleStart(e) {
e.preventDefault(); const onCancel = () => {
isLongPress = false; confirmOverlay.classList.remove('active');
cleanup();
// 视觉反馈 resolve(false);
dinBtn.classList.add('recording'); };
// 启动长按计时器 const cleanup = () => {
longPressTimer = setTimeout(() => { confirmOk.removeEventListener('click', onOk);
isLongPress = true; confirmCancel.removeEventListener('click', onCancel);
dinBtn.classList.remove('recording'); confirmOverlay.removeEventListener('click', onOverlayClick);
// 长按直接进入输入模式 };
createRecord(true);
}, LONG_PRESS_DURATION); const onOverlayClick = (e) => {
if (e.target === confirmOverlay) {
onCancel();
}
};
confirmOk.addEventListener('click', onOk);
confirmCancel.addEventListener('click', onCancel);
confirmOverlay.addEventListener('click', onOverlayClick);
});
} }
function handleEnd(e) { // ===== BUTTON INTERACTION =====
dinBtn.addEventListener('touchstart', onPress, { passive: false });
dinBtn.addEventListener('mousedown', onPress);
dinBtn.addEventListener('touchend', onRelease, { passive: false });
dinBtn.addEventListener('mouseup', onRelease);
dinBtn.addEventListener('mouseleave', onCancel);
function onPress(e) {
e.preventDefault(); e.preventDefault();
clearTimeout(longPressTimer); if (Date.now() - lastClick < DEBOUNCE) return;
isLongPress = false;
dinBtn.classList.add('recording');
pressTimer = setTimeout(() => {
isLongPress = true;
dinBtn.classList.remove('recording');
record(true); // 长按 = 记录 + 输入
}, LONG_PRESS);
}
function onRelease(e) {
e.preventDefault();
clearTimeout(pressTimer);
dinBtn.classList.remove('recording'); dinBtn.classList.remove('recording');
// 如果不是长按,直接记录
if (!isLongPress) { if (!isLongPress) {
createRecord(false); lastClick = Date.now();
record(false); // 短按 = 仅记录
} }
} }
function cancelLongPress() { function onCancel() {
clearTimeout(longPressTimer); clearTimeout(pressTimer);
dinBtn.classList.remove('recording'); dinBtn.classList.remove('recording');
} }
// ========== 记录逻辑 ========== // ===== RECORD =====
async function createRecord(showInput = false) { async function record(openInput = false) {
// 防抖1秒内不能重复点击 showToast('已记录!');
const now = Date.now();
if (now - lastRecordTime < 1000) return;
lastRecordTime = now;
try { try {
// 立即显示反馈(不等待网络) const res = await fetch(`${API}/api/din`, {
showToast('已记录!');
// 后台发送请求
const res = await fetch(`${API_BASE}/api/din`, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: '' }) body: JSON.stringify({ content: '' })
}); });
const data = await res.json(); const data = await res.json();
currentRecordId = data.id; currentId = data.id;
// 更新统计和列表 // 并行更新 UI
updateStats(); Promise.all([
prependToList(data); refreshStats(),
addChip(data),
checkAchievements()
]);
// 检查成就 if (openInput) {
checkNewAchievements(); openFloatInput(data.created_at, false); // false = 新建模式
// 如果需要输入,显示输入面板
if (showInput) {
showInputPanel(data.created_at);
} }
} catch (err) { } catch (err) {
@ -101,319 +127,290 @@ async function createRecord(showInput = false) {
} }
} }
// ========== 输入面板 ========== // ===== FLOAT INPUT =====
function showInputPanel(timestamp) { function openFloatInput(timestamp, isEdit = false) {
document.getElementById('input-timestamp').textContent = formatTimeOnly(timestamp); document.getElementById('float-time').textContent = formatTime(timestamp);
inputOverlay.classList.add('active'); document.querySelector('.float-title').textContent = isEdit ? '编辑记录' : '添加备注';
quickInput.value = ''; floatField.value = '';
setTimeout(() => quickInput.focus(), 100); floatInput.classList.add('active');
overlay.classList.add('active');
setTimeout(() => floatField.focus(), 50);
} }
function hideInputPanel() { function closeFloatInput() {
inputOverlay.classList.remove('active'); floatInput.classList.remove('active');
quickInput.blur(); overlay.classList.remove('active');
floatField.blur();
} }
// 保存按钮 // 保存
document.getElementById('btn-save').addEventListener('click', async () => { document.getElementById('btn-save').addEventListener('click', async () => {
if (!currentRecordId) return; const content = floatField.value.trim();
if (!content || !currentId) {
const content = quickInput.value.trim(); closeFloatInput();
if (!content) {
hideInputPanel();
return; return;
} }
await fetch(`${API}/api/din/${currentId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content })
});
updateChip(currentId, content);
closeFloatInput();
showToast('已保存');
});
// 删除
document.getElementById('btn-delete').addEventListener('click', async () => {
if (!currentId) {
console.log('No currentId to delete');
closeFloatInput();
return;
}
const confirmed = await showConfirm('确定要删除这条记录吗?此操作不可恢复。');
if (!confirmed) return;
try { try {
await fetch(`${API_BASE}/api/din/${currentRecordId}`, { const res = await fetch(`${API}/api/din/${currentId}`, { method: 'DELETE' });
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content })
});
// 更新列表中的内容 if (!res.ok) {
updateListItem(currentRecordId, content); throw new Error('Delete failed');
hideInputPanel(); }
showToast('已保存');
// 移除 chip - 使用字符串比较
const chip = document.querySelector(`.recent-chip[data-id="${currentId}"]`);
console.log('Looking for chip with id:', currentId, 'found:', chip);
if (chip) chip.remove();
await refreshStats();
closeFloatInput();
showToast('已删除');
currentId = null; // 清除当前ID
} catch (err) { } catch (err) {
showToast('保存失败', false); console.error('Delete error:', err);
showToast('删除失败', false);
} }
}); });
// 跳过按钮 // 跳过
document.getElementById('btn-skip').addEventListener('click', () => { document.getElementById('btn-skip').addEventListener('click', closeFloatInput);
hideInputPanel();
});
// 点击遮罩关闭 // 点击遮罩关闭
inputOverlay.addEventListener('click', (e) => { overlay.addEventListener('click', closeFloatInput);
if (e.target === inputOverlay) {
hideInputPanel();
}
});
// 回车保存 // 回车保存
quickInput.addEventListener('keypress', (e) => { floatField.addEventListener('keypress', e => {
if (e.key === 'Enter') { if (e.key === 'Enter') document.getElementById('btn-save').click();
document.getElementById('btn-save').click();
}
}); });
// 快标签 // 快标签
document.querySelectorAll('.tag').forEach(tag => { document.querySelectorAll('.tag').forEach(tag => {
tag.addEventListener('click', () => { tag.addEventListener('click', () => {
const text = tag.dataset.text; floatField.value = tag.dataset.text;
quickInput.value = text;
document.getElementById('btn-save').click(); document.getElementById('btn-save').click();
}); });
}); });
// ========== 数据更新 ========== // ===== CHIPS =====
async function updateStats() { function addChip(record) {
try { const container = document.getElementById('recent-chips');
const res = await fetch(`${API_BASE}/api/stats`); const empty = container.querySelector('.empty');
const stats = await res.json(); if (empty) empty.remove();
document.getElementById('stat-today').textContent = stats.today;
document.getElementById('stat-week').textContent = stats.week;
document.getElementById('stat-total').textContent = stats.total;
document.getElementById('recent-count').textContent = stats.total;
} catch (err) {
console.error('更新统计失败:', err);
}
}
function prependToList(record) {
const list = document.getElementById('recent-list');
const emptyMsg = list.querySelector('.recent-item .empty');
if (emptyMsg) {
list.innerHTML = '';
}
const item = document.createElement('div'); const chip = document.createElement('div');
item.className = 'recent-item'; chip.className = 'recent-chip';
item.dataset.id = record.id; chip.dataset.id = record.id;
item.innerHTML = ` chip.innerHTML = `
<span class="recent-time">${formatTimeOnly(record.created_at)}</span> <span class="time">${formatTime(record.created_at)}</span>
<span class="recent-content empty">(未备注)</span> <span>新记录</span>
`; `;
list.insertBefore(item, list.firstChild); chip.addEventListener('click', () => {
currentId = String(record.id); // 确保是字符串
console.log('Chip clicked, currentId set to:', currentId);
openFloatInput(record.created_at, true); // true = 编辑模式
});
// 保持最多10条 container.insertBefore(chip, container.firstChild);
while (list.children.length > 10) {
list.removeChild(list.lastChild); // 保持最多 10 个
while (container.children.length > 10) {
container.removeChild(container.lastChild);
} }
} }
function updateListItem(id, content) { function updateChip(id, content) {
const item = document.querySelector(`.recent-item[data-id="${id}"]`); const chip = document.querySelector(`.recent-chip[data-id="${id}"]`);
if (item) { if (chip) {
const contentEl = item.querySelector('.recent-content'); chip.querySelector('span:last-child').textContent = content;
contentEl.textContent = content;
contentEl.classList.remove('empty');
} }
} }
// ========== 成就检查 ========== // ===== STATS =====
let lastAchievementCount = 0; async function refreshStats() {
async function checkNewAchievements() {
try { try {
const res = await fetch(`${API_BASE}/api/achievements`); const res = await fetch(`${API}/api/stats`);
const s = await res.json();
document.getElementById('stat-today').textContent = s.today;
document.getElementById('stat-week').textContent = s.week;
document.getElementById('stat-month').textContent = s.month;
document.getElementById('stat-total').textContent = s.total;
// 增长率
updateGrowth('growth-today', s.day_growth);
updateGrowth('growth-week', s.week_growth);
updateGrowth('growth-month', s.month_growth);
} catch (err) {
console.error(err);
}
}
function updateGrowth(id, value) {
const el = document.getElementById(id);
if (!el) return;
if (value > 0) {
el.textContent = `+${value}%`;
el.classList.remove('negative');
} else if (value < 0) {
el.textContent = `${value}%`;
el.classList.add('negative');
} else {
el.textContent = '-';
}
}
// ===== ACHIEVEMENTS =====
async function checkAchievements() {
try {
const res = await fetch(`${API}/api/achievements`);
const data = await res.json(); const data = await res.json();
if (data.unlocked_count > lastAchievementCount) { document.getElementById('ach-progress').textContent =
// 有新成就解锁 `${data.unlocked_count}/${data.total_count}`;
const newAchievements = data.achievements.filter(a => a.unlocked).slice(-1);
if (newAchievements.length > 0) {
showAchievementToast(newAchievements[0]);
}
}
lastAchievementCount = data.unlocked_count;
} catch (err) {
console.error('检查成就失败:', err);
}
}
function showAchievementToast(achievement) {
const icon = achievementToast.querySelector('.achievement-icon');
const text = document.getElementById('achievement-text');
icon.textContent = achievement.icon;
text.textContent = `解锁:${achievement.name}`;
achievementToast.classList.add('show');
setTimeout(() => {
achievementToast.classList.remove('show');
}, 3000);
}
// ========== 辅助函数 ==========
function showToast(message, success = true) {
toast.textContent = message;
toast.style.background = success ? '#ff4757' : '#ff6b6b';
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 1500);
}
function formatTimeOnly(isoString) {
const date = new Date(isoString);
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
}
// ========== 初始化 ==========
async function init() {
await updateStats();
await loadRecent();
await checkNewAchievements();
}
async function loadRecent() {
try {
const res = await fetch(`${API_BASE}/api/din?limit=10`);
const records = await res.json();
const list = document.getElementById('recent-list'); const list = document.getElementById('achievements-list');
document.getElementById('recent-count').textContent = records.length; list.innerHTML = data.achievements.map(a => `
<div class="achievement-badge ${a.unlocked ? 'unlocked' : ''}" title="${a.desc}">
if (records.length === 0) { <span class="achievement-icon">${a.icon}</span>
list.innerHTML = '<div class="recent-item"><span class="recent-content empty">点击大按钮开始记录</span></div>'; <span class="achievement-name">${a.name}</span>
return;
}
list.innerHTML = records.map(r => `
<div class="recent-item" data-id="${r.id}">
<span class="recent-time">${formatTimeOnly(r.created_at)}</span>
<span class="recent-content ${!r.content ? 'empty' : ''}">${r.content || '(未备注)'}</span>
</div> </div>
`).join(''); `).join('');
} catch (err) { } catch (err) {
console.error('加载记录失败:', err); console.error(err);
} }
} }
// ========== 历史记录面板 ========== // ===== HISTORY PANEL =====
const historyOverlay = document.getElementById('history-overlay'); const historyPanel = document.getElementById('history-panel');
const historyList = document.getElementById('history-list'); const historyList = document.getElementById('history-list');
// 打开历史面板 document.getElementById('btn-view-all').addEventListener('click', () => {
document.getElementById('view-all').addEventListener('click', () => { historyPanel.classList.add('active');
historyOverlay.classList.add('active');
loadHistory(); loadHistory();
}); });
// 关闭历史面板 document.getElementById('history-close').addEventListener('click', () => {
document.getElementById('close-history').addEventListener('click', () => { historyPanel.classList.remove('active');
historyOverlay.classList.remove('active');
});
// 点击遮罩关闭
historyOverlay.addEventListener('click', (e) => {
if (e.target === historyOverlay) {
historyOverlay.classList.remove('active');
}
}); });
async function loadHistory() { async function loadHistory() {
try { try {
const res = await fetch(`${API_BASE}/api/din?limit=100`); const res = await fetch(`${API}/api/din?limit=100`);
const records = await res.json(); const records = await res.json();
if (records.length === 0) { if (records.length === 0) {
historyList.innerHTML = '<div style="text-align:center;color:#666;padding:40px;">暂无记录</div>'; historyList.innerHTML = '<div style="text-align:center;color:#555;padding:40px;">暂无记录</div>';
return; return;
} }
// 按日期分组 historyList.innerHTML = records.map(r => `
const grouped = groupByDate(records); <div class="history-item" data-id="${r.id}">
<span class="history-time">${formatTime(r.created_at)}</span>
historyList.innerHTML = Object.entries(grouped).map(([date, items]) => ` <span class="history-content ${!r.content ? 'empty' : ''}">${r.content || '(未备注)'}</span>
<div class="history-group"> <div class="history-actions">
<div class="history-date-header">${date}</div> <button onclick="editRecord(${r.id}, '${esc(r.content||'')}')" title="编辑"></button>
${items.map(r => ` <button class="btn-delete" onclick="deleteRecord(${r.id})" title="删除">🗑</button>
<div class="history-item" data-id="${r.id}"> </div>
<span class="history-time">${formatTimeOnly(r.created_at)}</span>
<span class="history-content ${!r.content ? 'empty' : ''}">${r.content || '(未备注)'}</span>
<div class="history-actions">
<button onclick="editHistoryItem(${r.id}, '${escapeHtml(r.content || '')}')" title="编辑"></button>
<button class="btn-delete" onclick="deleteHistoryItem(${r.id})" title="删除">🗑</button>
</div>
</div>
`).join('')}
</div> </div>
`).join(''); `).join('');
} catch (err) { } catch (err) {
console.error('加载历史失败:', err); console.error(err);
showToast('加载失败', false);
} }
} }
function groupByDate(records) { window.editRecord = async (id, current) => {
const groups = {}; const content = prompt('修改备注:', current);
records.forEach(r => { if (content === null) return;
const date = new Date(r.created_at);
const key = `${date.getMonth() + 1}${date.getDate()}`; await fetch(`${API}/api/din/${id}`, {
if (!groups[key]) groups[key] = []; method: 'PUT',
groups[key].push(r); headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: content.trim() })
}); });
return groups;
}
// 编辑历史记录
window.editHistoryItem = async function(id, currentContent) {
const newContent = prompt('修改备注:', currentContent);
if (newContent === null) return;
try { loadHistory();
await fetch(`${API_BASE}/api/din/${id}`, { updateChip(id, content);
method: 'PUT', showToast('已更新');
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: newContent.trim() })
});
showToast('已更新');
loadHistory(); // 刷新历史列表
loadRecent(); // 刷新最近列表
} catch (err) {
showToast('更新失败', false);
}
}; };
// 删除历史记录 window.deleteRecord = async (id) => {
window.deleteHistoryItem = async function(id) { const confirmed = await showConfirm('确定要删除这条记录吗?此操作不可恢复。');
if (!confirm('确定删除这条记录?')) return; if (!confirmed) return;
try { await fetch(`${API}/api/din/${id}`, { method: 'DELETE' });
await fetch(`${API_BASE}/api/din/${id}`, { method: 'DELETE' });
loadHistory();
showToast('已删除'); refreshStats();
loadHistory(); // 刷新历史列表
loadRecent(); // 刷新最近列表 const chip = document.querySelector(`.recent-chip[data-id="${id}"]`);
updateStats(); // 刷新统计 if (chip) chip.remove();
} catch (err) {
showToast('删除失败', false); showToast('已删除');
}
}; };
function escapeHtml(text) { // ===== UTILS =====
const div = document.createElement('div');
div.textContent = text; function showToast(msg, success = true) {
return div.innerHTML.replace(/'/g, "&#39;").replace(/"/g, "&quot;"); toast.textContent = msg;
toast.style.background = success ? '#ff6b6b' : '#ff5252';
toast.classList.add('show');
setTimeout(() => toast.classList.remove('show'), 1500);
}
function formatTime(iso) {
const d = new Date(iso);
return `${d.getHours().toString().padStart(2,'0')}:${d.getMinutes().toString().padStart(2,'0')}`;
}
function esc(str) {
return str.replace(/'/g, "\\'").replace(/"/g, '\\"');
}
// ===== INIT =====
async function init() {
await refreshStats();
await checkAchievements();
// 加载最近记录
try {
const res = await fetch(`${API}/api/din?limit=10`);
const records = await res.json();
records.reverse().forEach(r => addChip(r));
} catch (err) {
console.error(err);
}
} }
// 启动
init(); init();

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<meta name="theme-color" content="#0d0d0f"> <meta name="theme-color" content="#1a1a2e">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="din"> <meta name="apple-mobile-web-app-title" content="din">
@ -18,90 +18,135 @@
</script> </script>
</head> </head>
<body> <body>
<!-- 主触发区 - 全屏点击即可记录 --> <div class="container">
<div class="din-trigger" id="trigger-area"> <!-- 头部 -->
<button class="big-btn" id="din-btn" aria-label="记录 din"> <header>
<span class="btn-icon"></span> <h1>🦐 din</h1>
<span class="btn-text">din</span> <p class="subtitle">Do It Now - 想到就做</p>
</button> </header>
<p class="hint">点击任意处记录 · 长按快捷输入</p>
<!-- 统计面板 -->
<section class="stats">
<div class="stat-card">
<div class="stat-value" id="stat-today">0</div>
<div class="stat-label">今日</div>
<div class="stat-growth" id="growth-today">-</div>
</div>
<div class="stat-card">
<div class="stat-value" id="stat-week">0</div>
<div class="stat-label">本周</div>
<div class="stat-growth" id="growth-week">-</div>
</div>
<div class="stat-card">
<div class="stat-value" id="stat-month">0</div>
<div class="stat-label">本月</div>
<div class="stat-growth" id="growth-month">-</div>
</div>
<div class="stat-card total">
<div class="stat-value" id="stat-total">0</div>
<div class="stat-label">总计</div>
</div>
</section>
<!-- 大按钮 -->
<section class="main-action">
<button id="din-btn" class="din-button" aria-label="记录">
<svg class="btn-icon" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="btnGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#ff6b6b;stop-opacity:1" />
<stop offset="100%" style="stop-color:#ee5a5a;stop-opacity:1" />
</linearGradient>
</defs>
<!-- 外发光圆环 -->
<circle cx="60" cy="60" r="56" fill="none" stroke="rgba(255,107,107,0.3)" stroke-width="1"/>
<!-- 主圆形背景 -->
<circle cx="60" cy="60" r="50" fill="url(#btnGrad)"/>
<!-- 闪电图标 -->
<path d="M68 30 L52 54 L62 54 L54 78 L72 50 L60 50 Z"
fill="white"
opacity="0.95"
filter="drop-shadow(0 2px 4px rgba(0,0,0,0.2))"/>
</svg>
</button>
<p class="hint">点击记录 · 长按添加备注</p>
</section>
<!-- 最近记录条 -->
<section class="recent-strip">
<div class="recent-header">
<span class="recent-title">最近记录</span>
<span class="view-all" id="btn-view-all">查看全部 →</span>
</div>
<div class="recent-chips" id="recent-chips">
<div class="recent-chip empty">点击上方按钮开始</div>
</div>
</section>
<!-- 成就展示 -->
<section class="achievements">
<div class="achievements-header">
<span class="achievements-title">成就</span>
<span class="achievements-progress" id="ach-progress">0/24</span>
</div>
<div class="achievements-scroll" id="achievements-list">
<!-- 动态生成 -->
</div>
</section>
</div> </div>
<!-- 底部统计 --> <!-- 遮罩 -->
<div class="stats-bar"> <div class="overlay" id="overlay"></div>
<div class="stat-item">
<div class="stat-num" id="stat-today">0</div> <!-- 快速输入浮层 -->
<div class="stat-label">今日</div> <div class="float-input" id="float-input">
<div class="float-header">
<span class="float-title">添加备注</span>
<span class="float-time" id="float-time">--:--</span>
</div> </div>
<div class="stat-item"> <input type="text" class="float-field" id="float-field" placeholder="做了什么?(可选)" autocomplete="off">
<div class="stat-num" id="stat-week">0</div>
<div class="stat-label">本周</div> <div class="tags">
<span class="tag" data-text="工作">💼 工作</span>
<span class="tag" data-text="学习">📚 学习</span>
<span class="tag" data-text="运动">💪 运动</span>
<span class="tag" data-text="休息">😴 休息</span>
<span class="tag" data-text="创意">💡 创意</span>
</div> </div>
<div class="stat-item">
<div class="stat-num total" id="stat-total">0</div> <div class="float-actions">
<div class="stat-label">总计</div> <button class="btn btn-danger" id="btn-delete">🗑️ 删除</button>
<button class="btn btn-secondary" id="btn-skip">跳过</button>
<button class="btn btn-primary" id="btn-save">保存</button>
</div> </div>
</div> </div>
<!-- 成就按钮 --> <!-- 历史记录面板(侧滑) -->
<button class="achievements-btn" id="achievements-btn" title="成就">🏆</button> <div class="history-panel" id="history-panel">
<div class="history-header">
<!-- 最近记录面板 --> <span class="history-title">历史记录</span>
<div class="recent-panel" id="recent-panel"> <button class="history-close" id="history-close"></button>
<div class="recent-title">
<span>最近记录</span>
<span class="view-all" id="view-all">查看全部 →</span>
</div> </div>
<div class="recent-list" id="recent-list"> <div class="history-list" id="history-list">
<div class="recent-item"> <!-- 动态生成 -->
<span class="recent-content empty">点击大按钮开始记录</span>
</div>
</div>
</div>
<!-- 历史记录面板(全屏) -->
<div class="history-overlay" id="history-overlay">
<div class="history-panel">
<div class="history-header">
<h2>历史记录</h2>
<button class="close-btn" id="close-history"></button>
</div>
<div class="history-list" id="history-list">
<!-- 动态加载 -->
</div>
</div>
</div>
<!-- 快速输入面板 -->
<div class="input-overlay" id="input-overlay">
<div class="input-panel">
<div class="input-header">
<span class="input-title">添加备注</span>
<span class="timestamp" id="input-timestamp">--:--</span>
</div>
<input type="text" class="quick-input" id="quick-input" placeholder="做了什么?(可选)" autocomplete="off">
<div class="quick-tags" id="quick-tags">
<span class="tag" data-text="工作">💼 工作</span>
<span class="tag" data-text="学习">📚 学习</span>
<span class="tag" data-text="运动">💪 运动</span>
<span class="tag" data-text="休息">😴 休息</span>
<span class="tag" data-text="创意">💡 创意</span>
<span class="tag" data-text="其他">📝 其他</span>
</div>
<div class="input-actions">
<button class="btn btn-skip" id="btn-skip">跳过</button>
<button class="btn btn-save" id="btn-save">保存</button>
</div>
</div> </div>
</div> </div>
<!-- Toast --> <!-- Toast -->
<div class="toast" id="toast">已记录!</div> <div class="toast" id="toast">已记录!</div>
<!-- 成就解锁提示 --> <!-- 自定义确认弹窗 -->
<div class="achievement-toast" id="achievement-toast"> <div class="confirm-overlay" id="confirm-overlay">
<span class="achievement-icon">🏆</span> <div class="confirm-box">
<span id="achievement-text">解锁成就</span> <div class="confirm-icon">🗑️</div>
<div class="confirm-title">确认删除</div>
<div class="confirm-message">确定要删除这条记录吗?</div>
<div class="confirm-actions">
<button class="confirm-btn cancel" id="confirm-cancel">取消</button>
<button class="confirm-btn delete" id="confirm-ok">删除</button>
</div>
</div>
</div> </div>
<script src="app.js"></script> <script src="app.js"></script>

File diff suppressed because it is too large Load Diff