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

View File

@ -3,7 +3,7 @@
<head>
<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="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-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="din">
@ -18,90 +18,135 @@
</script>
</head>
<body>
<!-- 主触发区 - 全屏点击即可记录 -->
<div class="din-trigger" id="trigger-area">
<button class="big-btn" id="din-btn" aria-label="记录 din">
<span class="btn-icon"></span>
<span class="btn-text">din</span>
</button>
<p class="hint">点击任意处记录 · 长按快捷输入</p>
</div>
<div class="container">
<!-- 头部 -->
<header>
<h1>🦐 din</h1>
<p class="subtitle">Do It Now - 想到就做</p>
</header>
<!-- 底部统计 -->
<div class="stats-bar">
<div class="stat-item">
<div class="stat-num" id="stat-today">0</div>
<!-- 统计面板 -->
<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-item">
<div class="stat-num" id="stat-week">0</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-item">
<div class="stat-num total" id="stat-total">0</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>
<!-- 成就按钮 -->
<button class="achievements-btn" id="achievements-btn" title="成就">🏆</button>
<!-- 遮罩 -->
<div class="overlay" id="overlay"></div>
<!-- 最近记录面板 -->
<div class="recent-panel" id="recent-panel">
<div class="recent-title">
<span>最近记录</span>
<span class="view-all" id="view-all">查看全部 →</span>
</div>
<div class="recent-list" id="recent-list">
<div class="recent-item">
<span class="recent-content empty">点击大按钮开始记录</span>
</div>
</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>
<input type="text" class="float-field" id="float-field" placeholder="做了什么?(可选)" autocomplete="off">
<!-- 历史记录面板(全屏) -->
<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">
<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>
<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 class="float-actions">
<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 class="history-panel" id="history-panel">
<div class="history-header">
<span class="history-title">历史记录</span>
<button class="history-close" id="history-close"></button>
</div>
<div class="history-list" id="history-list">
<!-- 动态生成 -->
</div>
</div>
<!-- Toast -->
<div class="toast" id="toast">已记录!</div>
<!-- 成就解锁提示 -->
<div class="achievement-toast" id="achievement-toast">
<span class="achievement-icon">🏆</span>
<span id="achievement-text">解锁成就</span>
<!-- 自定义确认弹窗 -->
<div class="confirm-overlay" id="confirm-overlay">
<div class="confirm-box">
<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>
<script src="app.js"></script>

File diff suppressed because it is too large Load Diff