450 lines
24 KiB
JavaScript
450 lines
24 KiB
JavaScript
import { useState, useEffect } from "react";
|
||
|
||
const STORAGE_KEY = "mama-schedule-v1";
|
||
|
||
const WEEK_SCHEDULE = {
|
||
周一: {
|
||
fixed: [
|
||
{ time: "5:45", task: "起床 · 深度工作 ⭐", type: "work" },
|
||
{ time: "6:45", task: "孩子起床 · 做早饭", type: "family" },
|
||
{ time: "8:00", task: "孩子自己上学 · 继续工作黄金时段", type: "work" },
|
||
{ time: "12:00", task: "午饭", type: "life" },
|
||
{ time: "13:00", task: "下午工作 + 家务30分钟", type: "work" },
|
||
{ time: "15:30", task: "出门接孩子", type: "family" },
|
||
{ time: "16:40", task: "接孩子回家", type: "family" },
|
||
{ time: "17:30", task: "做晚饭", type: "life" },
|
||
{ time: "19:00", task: "陪练琴 / 作业", type: "family" },
|
||
{ time: "20:00", task: "陪读书", type: "family" },
|
||
{ time: "20:30", task: "孩子入睡 · 轻量工作 + 日记", type: "work" },
|
||
{ time: "22:00", task: "🌙 洗漱睡觉", type: "sleep" },
|
||
],
|
||
note: "最自由的一天,保护上午深度工作时间",
|
||
},
|
||
周二: {
|
||
fixed: [
|
||
{ time: "5:45", task: "起床 · 早晨个人时间 / 日记", type: "self" },
|
||
{ time: "6:45", task: "孩子起床 · 做早饭", type: "family" },
|
||
{ time: "7:40", task: "送孩子出门", type: "family" },
|
||
{ time: "8:30", task: "去朋友家工作 ⭐", type: "work" },
|
||
{ time: "15:30", task: "出门接孩子", type: "family" },
|
||
{ time: "16:40", task: "回家 · 做晚饭", type: "life" },
|
||
{ time: "19:00", task: "陪练琴 / 作业", type: "family" },
|
||
{ time: "20:00", task: "陪读书", type: "family" },
|
||
{ time: "20:30", task: "孩子入睡 · 轻量工作 + 回消息", type: "work" },
|
||
{ time: "22:00", task: "🌙 洗漱睡觉", type: "sleep" },
|
||
],
|
||
note: "朋友家是深度工作最佳环境,珍惜这段时间",
|
||
},
|
||
周三: {
|
||
fixed: [
|
||
{ time: "5:45", task: "起床 · 轻量工作 / 回消息", type: "work" },
|
||
{ time: "6:45", task: "孩子起床 · 做早饭", type: "family" },
|
||
{ time: "7:40", task: "送孩子 → 直接去健身 🏋️", type: "self" },
|
||
{ time: "10:30", task: "回家 · 洗澡", type: "self" },
|
||
{ time: "11:00", task: "深度工作 ⭐", type: "work" },
|
||
{ time: "12:30", task: "午饭", type: "life" },
|
||
{ time: "13:00", task: "继续工作", type: "work" },
|
||
{ time: "15:30", task: "出门接孩子", type: "family" },
|
||
{ time: "16:40", task: "回家", type: "family" },
|
||
{ time: "17:30", task: "做晚饭(简化)", type: "life" },
|
||
{ time: "19:00", task: "陪练琴 / 作业", type: "family" },
|
||
{ time: "20:00", task: "陪读书", type: "family" },
|
||
{ time: "20:30", task: "孩子入睡 · 只写日记,不工作", type: "self" },
|
||
{ time: "22:00", task: "🌙 洗漱睡觉", type: "sleep" },
|
||
],
|
||
note: "健身日晚上不安排工作,上午已补回来了",
|
||
},
|
||
周四: {
|
||
fixed: [
|
||
{ time: "5:45", task: "起床 · 深度工作 ⭐", type: "work" },
|
||
{ time: "6:45", task: "孩子起床 · 做早饭", type: "family" },
|
||
{ time: "7:40", task: "送孩子出门", type: "family" },
|
||
{ time: "8:00", task: "回家 · 继续深度工作", type: "work" },
|
||
{ time: "12:00", task: "午饭", type: "life" },
|
||
{ time: "13:00", task: "工作 + 家务30分钟", type: "work" },
|
||
{ time: "15:30", task: "出门接孩子", type: "family" },
|
||
{ time: "16:40", task: "接孩子回家", type: "family" },
|
||
{ time: "17:30", task: "做晚饭", type: "life" },
|
||
{ time: "19:00", task: "陪练琴 / 作业", type: "family" },
|
||
{ time: "20:00", task: "陪读书", type: "family" },
|
||
{ time: "20:30", task: "孩子入睡 · 轻量工作 + 日记", type: "work" },
|
||
{ time: "22:00", task: "🌙 洗漱睡觉", type: "sleep" },
|
||
],
|
||
note: "早晨是黄金深度工作时间,下午被接送切割",
|
||
},
|
||
周五: {
|
||
fixed: [
|
||
{ time: "5:45", task: "起床 · 轻量工作 / 回消息", type: "work" },
|
||
{ time: "6:45", task: "孩子起床 · 做早饭", type: "family" },
|
||
{ time: "7:40", task: "送孩子 → 直接去健身 🏋️", type: "self" },
|
||
{ time: "10:30", task: "回家 · 洗澡", type: "self" },
|
||
{ time: "11:00", task: "深度工作 ⭐", type: "work" },
|
||
{ time: "12:30", task: "午饭", type: "life" },
|
||
{ time: "13:00", task: "继续工作", type: "work" },
|
||
{ time: "15:30", task: "出门接孩子", type: "family" },
|
||
{ time: "16:40", task: "回家", type: "family" },
|
||
{ time: "17:30", task: "做晚饭(简化)", type: "life" },
|
||
{ time: "19:00", task: "陪练琴 / 作业", type: "family" },
|
||
{ time: "20:00", task: "陪读书", type: "family" },
|
||
{ time: "20:30", task: "孩子入睡 · 只写日记,不工作", type: "self" },
|
||
{ time: "22:00", task: "🌙 洗漱睡觉", type: "sleep" },
|
||
],
|
||
note: "周五健身日,晚上放松,迎接周末",
|
||
},
|
||
};
|
||
|
||
const TYPE_STYLE = {
|
||
work: { bg: "#e8f4fd", dot: "#3b82f6", label: "工作" },
|
||
family: { bg: "#fef3c7", dot: "#f59e0b", label: "家庭" },
|
||
self: { bg: "#f0fdf4", dot: "#22c55e", label: "自我" },
|
||
life: { bg: "#fdf4ff", dot: "#a855f7", label: "生活" },
|
||
sleep: { bg: "#f1f5f9", dot: "#64748b", label: "睡眠" },
|
||
};
|
||
|
||
const DAYS = ["周一", "周二", "周三", "周四", "周五"];
|
||
const WEEKS = ["第1-2周", "第3-4周", "第5-6周"];
|
||
const SLEEP_TARGETS = { "第1-2周": "23:00前", "第3-4周": "22:30前", "第5-6周": "22:00前" };
|
||
|
||
function loadData() {
|
||
try {
|
||
const raw = localStorage.getItem(STORAGE_KEY);
|
||
return raw ? JSON.parse(raw) : {};
|
||
} catch { return {}; }
|
||
}
|
||
|
||
function saveData(data) {
|
||
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); } catch {}
|
||
}
|
||
|
||
export default function App() {
|
||
const [tab, setTab] = useState("today");
|
||
const [selectedDay, setSelectedDay] = useState(DAYS[new Date().getDay() - 1] || "周一");
|
||
const [data, setData] = useState(loadData);
|
||
const [newTask, setNewTask] = useState({ text: "", type: "brain" });
|
||
const [weekReview, setWeekReview] = useState({ sleep: "", wins: "", issues: "", next: "" });
|
||
const [currentWeek, setCurrentWeek] = useState("第1-2周");
|
||
const [showAddTask, setShowAddTask] = useState(false);
|
||
|
||
const todayKey = new Date().toISOString().slice(0, 10);
|
||
const tasks = data[todayKey]?.tasks || [];
|
||
const reviews = data.reviews || {};
|
||
|
||
function updateData(newData) {
|
||
setData(newData);
|
||
saveData(newData);
|
||
}
|
||
|
||
function addTask() {
|
||
if (!newTask.text.trim()) return;
|
||
const updated = {
|
||
...data,
|
||
[todayKey]: {
|
||
...data[todayKey],
|
||
tasks: [...tasks, { id: Date.now(), text: newTask.text, type: newTask.type, done: false }],
|
||
},
|
||
};
|
||
updateData(updated);
|
||
setNewTask({ text: "", type: "brain" });
|
||
setShowAddTask(false);
|
||
}
|
||
|
||
function toggleTask(id) {
|
||
const updated = {
|
||
...data,
|
||
[todayKey]: {
|
||
...data[todayKey],
|
||
tasks: tasks.map(t => t.id === id ? { ...t, done: !t.done } : t),
|
||
},
|
||
};
|
||
updateData(updated);
|
||
}
|
||
|
||
function deleteTask(id) {
|
||
const updated = {
|
||
...data,
|
||
[todayKey]: {
|
||
...data[todayKey],
|
||
tasks: tasks.filter(t => t.id !== id),
|
||
},
|
||
};
|
||
updateData(updated);
|
||
}
|
||
|
||
function saveReview() {
|
||
const weekKey = `${currentWeek}-${new Date().toISOString().slice(0, 7)}`;
|
||
const updated = {
|
||
...data,
|
||
reviews: { ...reviews, [weekKey]: { ...weekReview, date: new Date().toLocaleDateString("zh-CN") } },
|
||
};
|
||
updateData(updated);
|
||
setWeekReview({ sleep: "", wins: "", issues: "", next: "" });
|
||
alert("回顾已保存 ✓");
|
||
}
|
||
|
||
const brainTasks = tasks.filter(t => t.type === "brain");
|
||
const lightTasks = tasks.filter(t => t.type === "light");
|
||
|
||
return (
|
||
<div style={{ fontFamily: "'Noto Serif SC', 'Georgia', serif", minHeight: "100vh", background: "#faf8f5", color: "#2c2c2c" }}>
|
||
<style>{`
|
||
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@300;400;500;600&display=swap');
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
button { cursor: pointer; font-family: inherit; }
|
||
textarea, input { font-family: inherit; }
|
||
.tab-btn { background: none; border: none; padding: 10px 18px; font-size: 14px; color: #999; border-bottom: 2px solid transparent; transition: all 0.2s; }
|
||
.tab-btn.active { color: #2c2c2c; border-bottom-color: #2c2c2c; font-weight: 500; }
|
||
.tab-btn:hover { color: #2c2c2c; }
|
||
.day-btn { background: none; border: 1px solid #e0dbd4; border-radius: 20px; padding: 6px 14px; font-size: 13px; color: #666; transition: all 0.2s; }
|
||
.day-btn.active { background: #2c2c2c; color: #fff; border-color: #2c2c2c; }
|
||
.day-btn:hover:not(.active) { border-color: #999; color: #333; }
|
||
.task-card { background: #fff; border-radius: 12px; padding: 14px 16px; margin-bottom: 10px; border: 1px solid #ede8e1; display: flex; align-items: center; gap: 12px; transition: all 0.2s; }
|
||
.task-card:hover { border-color: #ccc; }
|
||
.task-card.done { opacity: 0.45; }
|
||
.check-btn { width: 22px; height: 22px; border-radius: 50%; border: 2px solid #d0c9bf; background: none; flex-shrink: 0; transition: all 0.2s; display: flex; align-items: center; justify-content: center; }
|
||
.check-btn.done { background: #2c2c2c; border-color: #2c2c2c; color: #fff; }
|
||
.check-btn:hover { border-color: #2c2c2c; }
|
||
.del-btn { background: none; border: none; color: #ccc; font-size: 16px; padding: 2px 6px; border-radius: 4px; margin-left: auto; flex-shrink: 0; }
|
||
.del-btn:hover { color: #e55; background: #fff0f0; }
|
||
.timeline-item { display: flex; gap: 14px; margin-bottom: 0; position: relative; }
|
||
.timeline-item:not(:last-child)::before { content: ''; position: absolute; left: 36px; top: 28px; bottom: -2px; width: 1px; background: #ede8e1; }
|
||
.add-btn { background: #2c2c2c; color: #fff; border: none; border-radius: 10px; padding: 10px 20px; font-size: 14px; display: flex; align-items: center; gap: 6px; }
|
||
.add-btn:hover { background: #444; }
|
||
.input-field { border: 1px solid #e0dbd4; border-radius: 10px; padding: 10px 14px; font-size: 14px; width: 100%; outline: none; background: #fff; }
|
||
.input-field:focus { border-color: #2c2c2c; }
|
||
.select-field { border: 1px solid #e0dbd4; border-radius: 10px; padding: 10px 14px; font-size: 14px; background: #fff; outline: none; }
|
||
.select-field:focus { border-color: #2c2c2c; }
|
||
.section-title { font-size: 13px; font-weight: 500; color: #888; letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 12px; }
|
||
.review-input { border: 1px solid #e0dbd4; border-radius: 10px; padding: 12px 14px; font-size: 14px; width: 100%; resize: none; outline: none; background: #fff; min-height: 70px; line-height: 1.6; }
|
||
.review-input:focus { border-color: #2c2c2c; }
|
||
.week-badge { display: inline-block; background: #f0ebe3; border-radius: 6px; padding: 3px 10px; font-size: 12px; color: #666; }
|
||
.progress-bar { height: 6px; background: #ede8e1; border-radius: 3px; overflow: hidden; }
|
||
.progress-fill { height: 100%; background: #2c2c2c; border-radius: 3px; transition: width 0.5s; }
|
||
.empty-state { text-align: center; padding: 30px 20px; color: #bbb; font-size: 14px; line-height: 1.8; }
|
||
`}</style>
|
||
|
||
{/* Header */}
|
||
<div style={{ background: "#fff", borderBottom: "1px solid #ede8e1", padding: "20px 20px 0" }}>
|
||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 16 }}>
|
||
<div>
|
||
<div style={{ fontSize: 11, color: "#aaa", letterSpacing: "0.1em", marginBottom: 4 }}>我的时间管理</div>
|
||
<h1 style={{ fontSize: 22, fontWeight: 600, letterSpacing: "-0.02em" }}>早睡计划</h1>
|
||
</div>
|
||
<div style={{ textAlign: "right" }}>
|
||
<div style={{ fontSize: 11, color: "#aaa", marginBottom: 4 }}>目标睡眠时间</div>
|
||
<div style={{ fontSize: 15, fontWeight: 500, color: "#2c7a2c" }}>{SLEEP_TARGETS[currentWeek]}</div>
|
||
<select value={currentWeek} onChange={e => setCurrentWeek(e.target.value)} style={{ marginTop: 4, border: "1px solid #e0dbd4", borderRadius: 6, padding: "3px 8px", fontSize: 11, color: "#666", background: "#fff", cursor: "pointer" }}>
|
||
{WEEKS.map(w => <option key={w}>{w}</option>)}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 6周进度 */}
|
||
<div style={{ marginBottom: 16 }}>
|
||
<div style={{ display: "flex", justifyContent: "space-between", fontSize: 11, color: "#aaa", marginBottom: 6 }}>
|
||
<span>6周养成计划</span>
|
||
<span>{currentWeek}</span>
|
||
</div>
|
||
<div className="progress-bar">
|
||
<div className="progress-fill" style={{ width: currentWeek === "第1-2周" ? "33%" : currentWeek === "第3-4周" ? "66%" : "100%" }} />
|
||
</div>
|
||
</div>
|
||
|
||
{/* Tabs */}
|
||
<div style={{ display: "flex", gap: 4 }}>
|
||
{[["today", "今日任务"], ["schedule", "日程表"], ["review", "周回顾"]].map(([key, label]) => (
|
||
<button key={key} className={`tab-btn ${tab === key ? "active" : ""}`} onClick={() => setTab(key)}>{label}</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div style={{ padding: "20px 20px 40px", maxWidth: 600, margin: "0 auto" }}>
|
||
|
||
{/* ===== 今日任务 ===== */}
|
||
{tab === "today" && (
|
||
<div>
|
||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 20 }}>
|
||
<div>
|
||
<div style={{ fontSize: 16, fontWeight: 500 }}>{new Date().toLocaleDateString("zh-CN", { month: "long", day: "numeric", weekday: "long" })}</div>
|
||
<div style={{ fontSize: 12, color: "#aaa", marginTop: 2 }}>记录今天要处理的事情</div>
|
||
</div>
|
||
<button className="add-btn" onClick={() => setShowAddTask(!showAddTask)}>
|
||
<span style={{ fontSize: 18, lineHeight: 1 }}>+</span> 添加任务
|
||
</button>
|
||
</div>
|
||
|
||
{showAddTask && (
|
||
<div style={{ background: "#fff", border: "1px solid #ede8e1", borderRadius: 14, padding: 16, marginBottom: 20 }}>
|
||
<input
|
||
className="input-field"
|
||
placeholder="任务内容..."
|
||
value={newTask.text}
|
||
onChange={e => setNewTask({ ...newTask, text: e.target.value })}
|
||
onKeyDown={e => e.key === "Enter" && addTask()}
|
||
style={{ marginBottom: 10 }}
|
||
autoFocus
|
||
/>
|
||
<div style={{ display: "flex", gap: 10, alignItems: "center" }}>
|
||
<select className="select-field" value={newTask.type} onChange={e => setNewTask({ ...newTask, type: e.target.value })}>
|
||
<option value="brain">🧠 需要脑力</option>
|
||
<option value="light">✅ 不需要思考</option>
|
||
</select>
|
||
<button onClick={addTask} style={{ background: "#2c2c2c", color: "#fff", border: "none", borderRadius: 10, padding: "10px 18px", fontSize: 14, whiteSpace: "nowrap" }}>确认</button>
|
||
<button onClick={() => setShowAddTask(false)} style={{ background: "none", border: "1px solid #e0dbd4", borderRadius: 10, padding: "10px 14px", fontSize: 14, color: "#666" }}>取消</button>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* 脑力任务 */}
|
||
<div style={{ marginBottom: 24 }}>
|
||
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 12 }}>
|
||
<span style={{ fontSize: 18 }}>🧠</span>
|
||
<div>
|
||
<div style={{ fontSize: 14, fontWeight: 500 }}>需要脑力的任务</div>
|
||
<div style={{ fontSize: 11, color: "#aaa" }}>安排在早晨 5:45 或上午时段</div>
|
||
</div>
|
||
</div>
|
||
{brainTasks.length === 0 ? (
|
||
<div className="empty-state">还没有脑力任务<br />点击「添加任务」→ 选择「需要脑力」</div>
|
||
) : brainTasks.map(t => (
|
||
<div key={t.id} className={`task-card ${t.done ? "done" : ""}`}>
|
||
<button className={`check-btn ${t.done ? "done" : ""}`} onClick={() => toggleTask(t.id)}>
|
||
{t.done && <span style={{ fontSize: 11 }}>✓</span>}
|
||
</button>
|
||
<span style={{ fontSize: 14, flex: 1, textDecoration: t.done ? "line-through" : "none" }}>{t.text}</span>
|
||
<button className="del-btn" onClick={() => deleteTask(t.id)}>×</button>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* 轻量任务 */}
|
||
<div>
|
||
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 12 }}>
|
||
<span style={{ fontSize: 18 }}>✅</span>
|
||
<div>
|
||
<div style={{ fontSize: 14, fontWeight: 500 }}>不需要思考的任务</div>
|
||
<div style={{ fontSize: 11, color: "#aaa" }}>留到晚上 20:30 后处理</div>
|
||
</div>
|
||
</div>
|
||
{lightTasks.length === 0 ? (
|
||
<div className="empty-state">还没有轻量任务<br />点击「添加任务」→ 选择「不需要思考」</div>
|
||
) : lightTasks.map(t => (
|
||
<div key={t.id} className={`task-card ${t.done ? "done" : ""}`}>
|
||
<button className={`check-btn ${t.done ? "done" : ""}`} onClick={() => toggleTask(t.id)}>
|
||
{t.done && <span style={{ fontSize: 11 }}>✓</span>}
|
||
</button>
|
||
<span style={{ fontSize: 14, flex: 1, textDecoration: t.done ? "line-through" : "none" }}>{t.text}</span>
|
||
<button className="del-btn" onClick={() => deleteTask(t.id)}>×</button>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{tasks.length > 0 && (
|
||
<div style={{ marginTop: 20, background: "#fff", borderRadius: 12, padding: "12px 16px", border: "1px solid #ede8e1" }}>
|
||
<div style={{ display: "flex", justifyContent: "space-between", fontSize: 13, color: "#666" }}>
|
||
<span>完成进度</span>
|
||
<span>{tasks.filter(t => t.done).length} / {tasks.length} 件</span>
|
||
</div>
|
||
<div className="progress-bar" style={{ marginTop: 8 }}>
|
||
<div className="progress-fill" style={{ width: `${tasks.length ? (tasks.filter(t => t.done).length / tasks.length * 100) : 0}%` }} />
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
{/* ===== 日程表 ===== */}
|
||
{tab === "schedule" && (
|
||
<div>
|
||
<div style={{ display: "flex", gap: 8, marginBottom: 20, flexWrap: "wrap" }}>
|
||
{DAYS.map(d => (
|
||
<button key={d} className={`day-btn ${selectedDay === d ? "active" : ""}`} onClick={() => setSelectedDay(d)}>{d}</button>
|
||
))}
|
||
</div>
|
||
|
||
{WEEK_SCHEDULE[selectedDay] && (
|
||
<>
|
||
<div style={{ background: "#fff8e7", border: "1px solid #f5e6bb", borderRadius: 10, padding: "10px 14px", marginBottom: 20, fontSize: 13, color: "#8a6c00" }}>
|
||
💡 {WEEK_SCHEDULE[selectedDay].note}
|
||
</div>
|
||
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||
{WEEK_SCHEDULE[selectedDay].fixed.map((item, i) => {
|
||
const style = TYPE_STYLE[item.type];
|
||
return (
|
||
<div key={i} className="timeline-item" style={{ padding: "4px 0" }}>
|
||
<div style={{ width: 58, flexShrink: 0, fontSize: 12, color: "#999", paddingTop: 8, textAlign: "right", fontVariantNumeric: "tabular-nums" }}>{item.time}</div>
|
||
<div style={{ width: 14, flexShrink: 0, display: "flex", flexDirection: "column", alignItems: "center", paddingTop: 10 }}>
|
||
<div style={{ width: 8, height: 8, borderRadius: "50%", background: style.dot, flexShrink: 0 }} />
|
||
</div>
|
||
<div style={{ flex: 1, background: item.type === "sleep" ? "#f1f5f9" : "#fff", border: `1px solid ${item.type === "sleep" ? "#cbd5e1" : "#ede8e1"}`, borderRadius: 10, padding: "8px 12px", marginBottom: 6 }}>
|
||
<span style={{ fontSize: 13.5, fontWeight: item.type === "sleep" ? 500 : 400 }}>{item.task}</span>
|
||
<span style={{ marginLeft: 8, fontSize: 10, color: style.dot, background: style.bg, padding: "2px 6px", borderRadius: 4 }}>{style.label}</span>
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
{/* ===== 周回顾 ===== */}
|
||
{tab === "review" && (
|
||
<div>
|
||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 20 }}>
|
||
<div>
|
||
<div style={{ fontSize: 16, fontWeight: 500 }}>每周回顾</div>
|
||
<div style={{ fontSize: 12, color: "#aaa", marginTop: 2 }}>每周花10分钟复盘,调整下周计划</div>
|
||
</div>
|
||
<span className="week-badge">{currentWeek}</span>
|
||
</div>
|
||
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
||
{[
|
||
{ key: "sleep", label: "😴 这周睡眠情况", placeholder: "实际几点睡的?有没有改善?" },
|
||
{ key: "wins", label: "✨ 做得好的地方", placeholder: "哪些时间安排运转良好?" },
|
||
{ key: "issues", label: "🔧 遇到的问题", placeholder: "哪里卡住了?什么原因?" },
|
||
{ key: "next", label: "🎯 下周调整计划", placeholder: "下周想改变什么?具体怎么做?" },
|
||
].map(({ key, label, placeholder }) => (
|
||
<div key={key}>
|
||
<div style={{ fontSize: 14, fontWeight: 500, marginBottom: 8 }}>{label}</div>
|
||
<textarea
|
||
className="review-input"
|
||
placeholder={placeholder}
|
||
value={weekReview[key]}
|
||
onChange={e => setWeekReview({ ...weekReview, [key]: e.target.value })}
|
||
/>
|
||
</div>
|
||
))}
|
||
|
||
<button onClick={saveReview} style={{ background: "#2c2c2c", color: "#fff", border: "none", borderRadius: 12, padding: "14px", fontSize: 15, fontFamily: "inherit", marginTop: 4 }}>
|
||
保存本周回顾
|
||
</button>
|
||
</div>
|
||
|
||
{/* 历史回顾 */}
|
||
{Object.keys(reviews).length > 0 && (
|
||
<div style={{ marginTop: 32 }}>
|
||
<div className="section-title">历史回顾</div>
|
||
{Object.entries(reviews).reverse().map(([key, r]) => (
|
||
<div key={key} style={{ background: "#fff", border: "1px solid #ede8e1", borderRadius: 12, padding: 16, marginBottom: 12 }}>
|
||
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: 10 }}>
|
||
<span className="week-badge">{key.split("-")[0]}{key.split("-")[1] ? "-" + key.split("-")[1] : ""}</span>
|
||
<span style={{ fontSize: 12, color: "#aaa" }}>{r.date}</span>
|
||
</div>
|
||
{r.sleep && <div style={{ fontSize: 13, marginBottom: 6 }}><span style={{ color: "#aaa" }}>睡眠:</span>{r.sleep}</div>}
|
||
{r.wins && <div style={{ fontSize: 13, marginBottom: 6 }}><span style={{ color: "#aaa" }}>亮点:</span>{r.wins}</div>}
|
||
{r.issues && <div style={{ fontSize: 13, marginBottom: 6 }}><span style={{ color: "#aaa" }}>问题:</span>{r.issues}</div>}
|
||
{r.next && <div style={{ fontSize: 13 }}><span style={{ color: "#aaa" }}>下周:</span>{r.next}</div>}
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|