[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="robots" content="noindex,nofollow">
<meta name="generator" content="ShipPage">
<link rel="canonical" href="https://shippage.ai/p/jifen">
<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">
<title>积分小管家 · 未来科技旗舰版</title>
<style>
:root {
--bg-deep: #05060f;
--bg-card: rgba(14, 16, 28, 0.75);
--bg-card-hover: rgba(20, 22, 38, 0.85);
--border-glow: rgba(0, 229, 255, 0.35);
--cyan: #00e5ff;
--cyan-glow: #00f0ff;
--neon-green: #00ff88;
--magenta: #ff2d95;
--purple: #b44dff;
--gold: #ffcc00;
--text-primary: #e0e6f0;
--text-secondary: #8899bb;
--text-dim: #556688;
--radius-lg: 18px;
--radius-md: 12px;
--radius-sm: 8px;
--font-mono: 'SF Mono', 'Cascadia Code', 'Consolas', 'Monaco', monospace;
}
*{margin:0;padding:0;box-sizing:border-box;}
body{
font-family: -apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;
background:var(--bg-deep); min-height:100vh; color:var(--text-primary);
padding-bottom:85px; overflow-x:hidden; -webkit-tap-highlight-color:transparent;
-webkit-font-smoothing:antialiased; position:relative;
}
#particles-canvas{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:0;}
body::before{
content:''; position:fixed; top:0;left:0;width:100%;height:100%; pointer-events:none;z-index:0;
background: linear-gradient(rgba(0,229,255,0.025) 1px,transparent 1px),
linear-gradient(90deg, rgba(0,229,255,0.025) 1px,transparent 1px);
background-size:40px 40px; animation:gridShift 12s linear infinite;
}
@keyframes gridShift{0%,100%{background-position:0 0,0 0;}50%{background-position:20px 20px,20px 20px;}}
body::after{
content:''; position:fixed; top:0;left:0;width:100%;height:100%; pointer-events:none;z-index:1;
background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(0,0,0,0.015) 2px,rgba(0,0,0,0.015) 4px);
animation:scanLines 8s linear infinite;
}
@keyframes scanLines{0%{background-position:0 0;}100%{background-position:0 100px;}}
.header{
background:rgba(10,12,22,0.8); backdrop-filter:blur(20px); -webkit-backdrop-filter:blur(20px);
padding:14px 20px; position:sticky; top:0; z-index:100;
border-bottom:1px solid rgba(0,229,255,0.2); box-shadow:0 2px 30px rgba(0,0,0,0.5),0 0 60px rgba(0,229,255,0.06);
}
.header h1{
font-size:20px; font-weight:800; letter-spacing:3px; text-align:center;
background:linear-gradient(135deg,var(--cyan),var(--purple),var(--cyan)); background-size:200% 200%;
-webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent;
animation:gradientFlow 4s ease infinite; filter:drop-shadow(0 0 12px rgba(0,229,255,0.4));
}
@keyframes gradientFlow{0%,100%{background-position:0% 50%;}50%{background-position:100% 100%;}}
.points-card{
background:linear-gradient(160deg,rgba(14,18,32,0.85),rgba(10,14,26,0.9),rgba(16,20,36,0.8));
margin:18px 16px; padding:26px 20px 22px; border-radius:var(--radius-lg); color:white;
border:1px solid rgba(0,229,255,0.3);
box-shadow:0 0 30px rgba(0,229,255,0.2),0 0 60px rgba(0,229,255,0.08),inset 0 0 30px rgba(0,229,255,0.03);
text-align:center; position:relative; overflow:hidden; z-index:2; animation:cardPulse 4s ease-in-out infinite;
}
@keyframes cardPulse{0%,100%{box-shadow:0 0 30px rgba(0,229,255,0.2);}50%{box-shadow:0 0 45px rgba(0,229,255,0.4);}}
.points-card::before{
content:''; position:absolute; top:8px; left:8px; width:20px; height:20px;
border-top:2px solid var(--cyan); border-left:2px solid var(--cyan); border-radius:4px 0 0 0; opacity:0.7;
animation:cornerGlow 2s ease-in-out infinite;
}
.points-card::after{
content:''; position:absolute; bottom:8px; right:8px; width:20px; height:20px;
border-bottom:2px solid var(--cyan); border-right:2px solid var(--cyan); border-radius:0 0 4px 0; opacity:0.7;
animation:cornerGlow 2s ease-in-out infinite 0.5s;
}
@keyframes cornerGlow{0%,100%{opacity:0.4;}50%{opacity:1;}}
.points-header{display:flex; flex-direction:column; align-items:center; gap:4px; margin-bottom:6px;}
.points-title{font-size:13px; font-weight:600; letter-spacing:4px; text-transform:uppercase; color:rgba(0,229,255,0.7); font-family:var(--font-mono);}
.points-amount{
font-size:62px; font-weight:900; font-family:var(--font-mono); letter-spacing:3px; color:#fff;
text-shadow:0 0 20px rgba(0,229,255,0.7),0 0 40px rgba(0,229,255,0.4),0 0 80px rgba(0,229,255,0.25),0 0 120px rgba(180,77,255,0.3);
animation:numberGlow 3s ease-in-out infinite; line-height:1;
}
@keyframes numberGlow{0%,100%{text-shadow:0 0 20px rgba(0,229,255,0.7);}50%{text-shadow:0 0 30px rgba(0,240,255,0.9),0 0 60px rgba(0,229,255,0.55);}}
.points-today{
display:flex; align-items:center; justify-content:center; gap:10px; font-size:12px;
background:rgba(0,229,255,0.08); border:1px solid rgba(0,229,255,0.2); border-radius:20px;
padding:7px 16px; margin:10px auto 0; width:fit-content; font-family:var(--font-mono);
color:rgba(200,220,240,0.9);
}
.today-divider{width:1px;height:16px;background:rgba(0,229,255,0.4);}
.today-value{font-weight:700;font-size:13px;color:var(--cyan-glow);text-shadow:0 0 8px rgba(0,229,255,0.5);}
.features{
display:grid; grid-template-columns:repeat(3,1fr); gap:10px; padding:0 16px; margin-bottom:14px;
position:relative; z-index:2;
}
.feature-item{
background:var(--bg-card); backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
border-radius:var(--radius-md); padding:14px 6px; text-align:center; cursor:pointer;
border:1px solid rgba(255,255,255,0.06); transition:all 0.3s cubic-bezier(0.4,0,0.2,1);
position:relative; overflow:hidden; z-index:2;
}
.feature-item:active{transform:scale(0.94); border-color:var(--border-glow); box-shadow:0 0 18px rgba(0,229,255,0.3); background:var(--bg-card-hover);}
.feature-icon{font-size:24px; margin-bottom:6px; filter:drop-shadow(0 0 6px rgba(0,229,255,0.3)); transition:transform 0.3s;}
.feature-item:active .feature-icon{transform:scale(1.2);}
.feature-name{font-size:10px; color:var(--text-secondary); font-weight:600; letter-spacing:1px;}
.content-section{
background:var(--bg-card); backdrop-filter:blur(16px); -webkit-backdrop-filter:blur(16px);
margin:0 16px 14px; border-radius:var(--radius-lg); padding:16px;
border:1px solid rgba(255,255,255,0.07); box-shadow:0 4px 30px rgba(0,0,0,0.3); position:relative; z-index:2;
}
.section-title{display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:14px;}
.section-title>span{font-size:16px; font-weight:700; letter-spacing:1.5px; color:var(--text-primary); font-family:var(--font-mono); text-shadow:0 0 10px rgba(0,229,255,0.3);}
.action-btn{
padding:7px 14px; border:1px solid rgba(255,255,255,0.15); border-radius:20px; font-size:11px; font-weight:700;
cursor:pointer; letter-spacing:1px; backdrop-filter:blur(8px); transition:all 0.3s;
}
.action-btn.add{background:rgba(0,229,255,0.15);color:var(--cyan);border-color:rgba(0,229,255,0.4);}
.action-btn.add:active{background:rgba(0,229,255,0.3);box-shadow:0 0 20px rgba(0,229,255,0.5);transform:scale(0.93);}
.action-btn.edit{background:rgba(255,204,0,0.1);color:var(--gold);border-color:rgba(255,204,0,0.4);}
.action-btn.delete{background:rgba(255,45,149,0.12);color:var(--magenta);border-color:rgba(255,45,149,0.4);}
.list-item{
display:flex; justify-content:space-between; align-items:center; padding:14px;
background:rgba(20,24,40,0.5); border-radius:var(--radius-md); margin-bottom:10px;
border:1px solid rgba(255,255,255,0.04); transition:all 0.3s;
}
.item-info{flex:1;} .item-name{font-size:13px;font-weight:600;margin-bottom:3px;} .item-points{font-size:15px;font-weight:700;font-family:var(--font-mono);}
.item-points.income{color:var(--neon-green);text-shadow:0 0 8px rgba(0,255,136,0.4);}
.item-points.expense{color:var(--magenta);text-shadow:0 0 8px rgba(255,45,149,0.4);}
.item-btn{
padding:7px 14px; border-radius:20px; font-size:11px; font-weight:700; cursor:pointer; letter-spacing:1px;
backdrop-filter:blur(6px); transition:all 0.3s; border:1px solid transparent;
}
.item-btn.earn{background:rgba(0,255,136,0.12);color:var(--neon-green);border-color:rgba(0,255,136,0.4);}
.item-btn.earn:active{background:rgba(0,255,136,0.3);box-shadow:0 0 18px rgba(0,255,136,0.5);transform:scale(0.92);}
.item-btn.deduct{background:rgba(255,45,149,0.12);color:var(--magenta);border-color:rgba(255,45,149,0.4);}
.item-btn.deduct:active{background:rgba(255,45,149,0.3);box-shadow:0 0 18px rgba(255,45,149,0.5);}
.item-btn.exchange{background:rgba(180,77,255,0.12);color:var(--purple);border-color:rgba(180,77,255,0.4);}
.week-grid{display:grid; grid-template-columns:repeat(7,1fr); gap:6px;}
.day-item{
cursor:pointer; background:rgba(18,22,36,0.6); border-radius:var(--radius-sm); padding:10px 4px;
text-align:center; border:1px solid rgba(255,255,255,0.04); transition:all 0.3s;
}
.day-item.today{background:linear-gradient(160deg,rgba(0,229,255,0.2),rgba(0,180,220,0.25));border-color:rgba(0,229,255,0.5);}
.day-item.selected{border-color:rgba(180,77,255,0.55);box-shadow:0 0 12px rgba(180,77,255,0.3);}
.day-name{font-size:9px;color:var(--text-dim);} .day-points{font-size:14px;font-weight:700;font-family:var(--font-mono);}
.record-list{max-height:380px;overflow-y:auto;}
.record-item{display:flex; justify-content:space-between; align-items:center; padding:14px 0; border-bottom:1px solid rgba(255,255,255,0.04);}
.record-title{font-size:13px;font-weight:600;} .record-time{font-size:11px;color:var(--text-dim);}
.record-amount{font-size:15px;font-weight:700;font-family:var(--font-mono);}
.record-amount.income{color:var(--neon-green);} .record-amount.expense{color:var(--magenta);}
.month-calendar{background:rgba(18,22,36,0.5);border-radius:var(--radius-md);padding:12px;margin-bottom:12px;}
.year-calendar{display:grid; grid-template-columns:repeat(6,1fr); gap:7px;}
.year-month-card{background:rgba(18,22,36,0.5);border-radius:var(--radius-sm);padding:10px 5px;text-align:center;cursor:pointer;border:1px solid transparent;}
.year-month-card.current{border-color:var(--cyan);}
.modal{
display:none; position:fixed; top:0;left:0;right:0;bottom:0; background:rgba(2,4,10,0.8);
backdrop-filter:blur(6px); z-index:1000; align-items:center; justify-content:center;
}
.modal.show{display:flex; animation:modalFadeIn 0.25s;}
@keyframes modalFadeIn{from{opacity:0;}to{opacity:1;}}
.modal-content{
background:linear-gradient(160deg,rgba(16,20,34,0.95),rgba(12,16,28,0.98)); border-radius:var(--radius-lg);
padding:24px; width:90%; max-width:400px; border:1px solid rgba(0,229,255,0.3);
box-shadow:0 0 40px rgba(0,229,255,0.2); animation:modalSlide 0.35s;
}
@keyframes modalSlide{from{transform:translateY(30px);opacity:0;}to{transform:translateY(0);opacity:1;}}
.modal-title{font-size:17px;font-weight:700;margin-bottom:18px;text-align:center;color:var(--cyan);}
.form-group{margin-bottom:14px;}
.form-label{display:block; font-size:12px; font-weight:700; margin-bottom:7px; color:var(--text-secondary);}
.form-input{
width:100%; padding:11px; background:rgba(10,14,24,0.7); border:1px solid rgba(255,255,255,0.1);
border-radius:var(--radius-sm); color:var(--text-primary); outline:none; font-family:var(--font-mono);
}
.form-input:focus{border-color:var(--cyan);box-shadow:0 0 14px rgba(0,229,255,0.2);}
.modal-footer{display:flex; gap:10px;}
.modal-btn{
flex:1; padding:12px; border-radius:22px; font-weight:700; cursor:pointer; letter-spacing:1px;
transition:all 0.3s; border:1px solid rgba(255,255,255,0.15);
}
.modal-btn.confirm{background:rgba(0,229,255,0.18);color:var(--cyan);border-color:rgba(0,229,255,0.4);}
.modal-btn.cancel{background:rgba(255,255,255,0.05);color:var(--text-secondary);}
.modal-btn.danger{background:rgba(255,45,149,0.18);color:var(--magenta);border-color:rgba(255,45,149,0.4);}
.bottom-nav{
position:fixed; bottom:0; left:0; right:0; background:rgba(10,12,22,0.85);
backdrop-filter:blur(22px); display:flex; justify-content:space-around; padding:10px 0;
padding-bottom:calc(10px + env(safe-area-inset-bottom)); border-top:1px solid rgba(0,229,255,0.2);
z-index:100;
}
.nav-item{text-align:center; cursor:pointer; flex:1; transition:all 0.3s;}
.nav-icon{font-size:22px;} .nav-text{font-size:10px; color:var(--text-dim);}
.nav-item.active .nav-text{color:var(--cyan);text-shadow:0 0 10px rgba(0,229,255,0.5);}
.page{display:none; position:relative; z-index:2;}
.page.active{display:block; animation:pageIn 0.35s;}
@keyframes pageIn{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:translateY(0);}}
.empty-state{text-align:center;padding:36px;color:var(--text-dim);}
.gacha-container{text-align:center; padding:20px;}
.gacha-machine{
width:160px; height:160px; margin:20px auto; background:radial-gradient(circle at 30% 30%, rgba(0,229,255,0.3), rgba(10,14,26,0.9));
border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:48px;
border:3px solid rgba(0,229,255,0.5); box-shadow:0 0 30px rgba(0,229,255,0.4); cursor:pointer;
transition:all 0.2s; animation:float 3s ease-in-out infinite;
}
@keyframes float{0%,100%{transform:translateY(0);}50%{transform:translateY(-8px);}}
.gacha-machine:active{transform:scale(0.9) rotate(10deg);}
.progress-bar{height:12px; background:rgba(255,255,255,0.1); border-radius:6px; overflow:hidden; margin-top:8px;}
.progress-fill{height:100%; background:linear-gradient(90deg,var(--cyan),var(--purple)); border-radius:6px; transition:width 0.5s;}
.badge-grid{display:grid; grid-template-columns:repeat(4,1fr); gap:10px;}
.badge-item{text-align:center; padding:10px; background:rgba(20,24,40,0.5); border-radius:12px; opacity:0.5;}
.badge-item.unlocked{opacity:1; border:1px solid var(--cyan); box-shadow:0 0 12px rgba(0,229,255,0.3);}
</style>
</head>
<body>
<canvas id="particles-canvas"></canvas>
<div class="header"><h1>◆ 积分小管家 · 旗舰版 ◆</h1></div>
<div class="bottom-nav">
<div class="nav-item active" onclick="switchPage('home')"><div class="nav-icon">🏠</div><div class="nav-text">首页</div></div>
<div class="nav-item" onclick="switchPage('records')"><div class="nav-icon">📊</div><div class="nav-text">记录</div></div>
<div class="nav-item" onclick="switchPage('habit')"><div class="nav-icon">📋</div><div class="nav-text">打卡</div></div>
<div class="nav-item" onclick="switchPage('achievement')"><div class="nav-icon">🏆</div><div class="nav-text">成就</div></div>
</div>
<!-- 页面容器 -->
<div class="page active" id="home-page">
<div class="points-card">
<div class="points-header"><div class="points-title">◈ 总积分 ◈</div><div class="points-amount" id="total-points">0</div></div>
<div class="points-today"><span>📅 今日积分</span><span class="today-divider"></span><span class="today-value" id="daily-challenge">赚0分 - 扣0分 = 0分</span></div>
</div>
<div class="features">
<div class="feature-item" onclick="switchPage('earn')"><div class="feature-icon">➕</div><div class="feature-name">赚取积分</div></div>
<div class="feature-item" onclick="switchPage('deduct')"><div class="feature-icon">➖</div><div class="feature-name">扣取积分</div></div>
<div class="feature-item" onclick="switchPage('exchange')"><div class="feature-icon">🏪</div><div class="feature-name">奖励兑换</div></div>
<div class="feature-item" onclick="switchPage('earn-manage')"><div class="feature-icon">💰</div><div class="feature-name">积分项设置</div></div>
<div class="feature-item" onclick="switchPage('deduct-manage')"><div class="feature-icon">⚠️</div><div class="feature-name">扣分项设置</div></div>
<div class="feature-item" onclick="switchPage('exchange-manage')"><div class="feature-icon">🎁</div><div class="feature-name">兑换设置</div></div>
<div class="feature-item" onclick="switchPage('goal')"><div class="feature-icon">🎯</div><div class="feature-name">积分目标</div></div>
<div class="feature-item" onclick="switchPage('gacha')"><div class="feature-icon">🎰</div><div class="feature-name">扭蛋机</div></div>
<div class="feature-item" onclick="switchPage('habit')"><div class="feature-icon">📋</div><div class="feature-name">习惯打卡</div></div>
</div>
<div class="content-section">
<div class="section-title"><span id="week-score-text">本周积分</span></div>
<div class="week-grid" id="current-week-grid"></div>
</div>
<div class="features">
<div class="feature-item" onclick="showClearPointsModal()"><div class="feature-icon">🗑️</div><div class="feature-name">积分清空</div></div>
<div class="feature-item" onclick="systemReset()"><div class="feature-icon">🔄</div><div class="feature-name">系统重置</div></div>
<div class="feature-item" onclick="generateReport()"><div class="feature-icon">📤</div><div class="feature-name">生成战报</div></div>
</div>
</div>
<div class="page" id="earn-page"><div class="content-section"><div class="section-title"><span>赚取积分</span></div><div id="earn-list"></div></div></div>
<div class="page" id="deduct-page"><div class="content-section"><div class="section-title"><span>扣取积分</span></div><div id="deduct-list"></div></div></div>
<div class="page" id="exchange-page"><div class="content-section"><div class="section-title"><span>兑换中心</span></div><div id="exchange-list"></div></div></div>
<div class="page" id="earn-manage-page"><div class="content-section"><div class="section-title"><span>积分项管理</span><button class="action-btn add" onclick="showAddEarnItemModal()">+ 添加</button></div><div id="earn-manage-list"></div></div></div>
<div class="page" id="deduct-manage-page"><div class="content-section"><div class="section-title"><span>扣分项管理</span><button class="action-btn add" onclick="showAddDeductItemModal()">+ 添加</button></div><div id="deduct-manage-list"></div></div></div>
<div class="page" id="exchange-manage-page"><div class="content-section"><div class="section-title"><span>兑换奖励管理</span><button class="action-btn add" onclick="showAddRewardModal()">+ 添加</button></div><div id="reward-manage-list"></div></div></div>
<div class="page" id="records-page">
<div class="content-section"><div class="section-title"><span id="records-week-score-text">本周积分</span><button class="action-btn add" onclick="showAddPointsModal()">补积分</button></div><div class="week-grid" id="records-week-grid"></div></div>
<div class="content-section"><div class="section-title">每日积分记录</div><div class="record-list" id="day-records"></div></div>
<div class="content-section"><div class="section-title">历史积分</div><div id="history-weeks"></div></div>
</div>
<div class="page" id="goal-page">
<div class="content-section"><div class="section-title"><span>积分目标追踪</span><button class="action-btn add" onclick="showAddGoalModal()">+ 新目标</button></div><div id="goal-list"></div></div>
</div>
<div class="page" id="gacha-page">
<div class="content-section"><div class="section-title"><span>积分扭蛋机</span></div>
<div class="gacha-container"><div class="gacha-machine" onclick="playGacha()">🎰</div><p style="color:var(--text-dim);">消耗 20 积分抽一次</p><div id="gacha-result" style="margin-top:16px;font-weight:bold;"></div></div>
</div>
</div>
<div class="page" id="habit-page">
<div class="content-section"><div class="section-title"><span>习惯打卡</span><button class="action-btn add" onclick="showAddHabitModal()">+ 新习惯</button></div><div id="habit-list"></div></div>
</div>
<div class="page" id="achievement-page">
<div class="content-section"><div class="section-title"><span>成就勋章馆</span></div><div class="badge-grid" id="badge-grid"></div></div>
</div>
<!-- 弹窗集合 -->
<div class="modal" id="add-earn-item-modal"><div class="modal-content"><div class="modal-title">添加赚取积分项</div><div class="modal-body"><div class="form-group"><label class="form-label">项目名称</label><input class="form-input" id="earn-item-name" placeholder="例如:完成作业"></div><div class="form-group"><label class="form-label">积分数量</label><input type="number" class="form-input" id="earn-item-points" placeholder="10"></div></div><div class="modal-footer"><button class="modal-btn cancel" onclick="closeModal('add-earn-item-modal')">取消</button><button class="modal-btn confirm" onclick="saveEarnItem()">保存</button></div></div></div>
<div class="modal" id="add-deduct-item-modal"><div class="modal-content"><div class="modal-title">添加扣取积分项</div><div class="modal-body"><div class="form-group"><label class="form-label">项目名称</label><input class="form-input" id="deduct-item-name" placeholder="例如:未完成作业"></div><div class="form-group"><label class="form-label">积分数量</label><input type="number" class="form-input" id="deduct-item-points" placeholder="10"></div></div><div class="modal-footer"><button class="modal-btn cancel" onclick="closeModal('add-deduct-item-modal')">取消</button><button class="modal-btn confirm" onclick="saveDeductItem()">保存</button></div></div></div>
<div class="modal" id="add-reward-modal"><div class="modal-content"><div class="modal-title">添加兑换奖励</div><div class="modal-body"><div class="form-group"><label class="form-label">奖励名称</label><input class="form-input" id="reward-name" placeholder="例如:看电视30分钟"></div><div class="form-group"><label class="form-label">所需积分</label><input type="number" class="form-input" id="reward-points" placeholder="50"></div></div><div class="modal-footer"><button class="modal-btn cancel" onclick="closeModal('add-reward-modal')">取消</button><button class="modal-btn confirm" onclick="saveReward()">保存</button></div></div></div>
<div class="modal" id="exchange-confirm-modal"><div class="modal-content"><div class="modal-title">确认兑换</div><div class="modal-body"><p id="exchange-reward-name"></p><p style="color:var(--purple);font-weight:700;font-size:20px;" id="exchange-reward-points"></p></div><div class="modal-footer"><button class="modal-btn cancel" onclick="closeModal('exchange-confirm-modal')">取消</button><button class="modal-btn confirm" onclick="confirmExchange()">确认兑换</button></div></div></div>
<div class="modal" id="add-points-modal"><div class="modal-content"><div class="modal-title">补积分</div><div class="modal-body"><div class="form-group"><label class="form-label">原因</label><input class="form-input" id="add-points-reason" placeholder="例如:忘记记录"></div><div class="form-group"><label class="form-label">积分数值</label><input type="number" class="form-input" id="add-points-value" placeholder="10"></div><div class="form-group"><label class="form-label">日期</label><input type="date" class="form-input" id="add-points-date"></div></div><div class="modal-footer"><button class="modal-btn cancel" onclick="closeModal('add-points-modal')">取消</button><button class="modal-btn confirm" onclick="addPoints()">确认</button></div></div></div>
<div class="modal" id="clear-points-modal"><div class="modal-content"><div class="modal-title">确认清空积分</div><div class="modal-body"><p>确定要清空所有积分吗?此操作不可恢复!</p></div><div class="modal-footer"><button class="modal-btn cancel" onclick="closeModal('clear-points-modal')">取消</button><button class="modal-btn danger" onclick="confirmClearPoints()">确认清空</button></div></div></div>
<div class="modal" id="delete-confirm-modal"><div class="modal-content"><div class="modal-title">确认删除</div><div class="modal-body"><p>确定要删除此项目吗?</p></div><div class="modal-footer"><button class="modal-btn cancel" onclick="closeModal('delete-confirm-modal')">取消</button><button class="modal-btn danger" onclick="confirmDelete()">确认删除</button></div></div></div>
<div class="modal" id="add-goal-modal"><div class="modal-content"><div class="modal-title">添加积分目标</div><div class="modal-body"><div class="form-group"><label class="form-label">目标名称</label><input class="form-input" id="goal-name" placeholder="如:乐高玩具"></div><div class="form-group"><label class="form-label">所需积分</label><input type="number" class="form-input" id="goal-points" placeholder="200"></div></div><div class="modal-footer"><button class="modal-btn cancel" onclick="closeModal('add-goal-modal')">取消</button><button class="modal-btn confirm" onclick="saveGoal()">保存</button></div></div></div>
<div class="modal" id="add-habit-modal"><div class="modal-content"><div class="modal-title">添加习惯</div><div class="modal-body"><div class="form-group"><label class="form-label">习惯名称</label><input class="form-input" id="habit-name" placeholder="如:早起"></div><div class="form-group"><label class="form-label">积分</label><input type="number" class="form-input" id="habit-points" placeholder="5"></div><div class="form-group"><label class="form-label">周期</label><select class="form-input" id="habit-period"><option value="daily">每天</option><option value="weekly">每周</option></select></div></div><div class="modal-footer"><button class="modal-btn cancel" onclick="closeModal('add-habit-modal')">取消</button><button class="modal-btn confirm" onclick="saveHabit()">保存</button></div></div></div>
<script>
(function(){
const canvas=document.getElementById('particles-canvas'),ctx=canvas.getContext('2d');
let particles=[], w,h;
function resize(){w=canvas.width=window.innerWidth;h=canvas.height=window.innerHeight;}
window.addEventListener('resize',resize); resize();
class P{constructor(){this.reset();}reset(){this.x=Math.random()*w;this.y=Math.random()*h;this.size=Math.random()*2+0.8;this.sx=(Math.random()-0.5)*0.5;this.sy=(Math.random()-0.5)*0.5;this.color=`rgba(${[0,229,255,180,77,255,0,255,136,255,45,149][Math.floor(Math.random()*4)]},`;this.alpha=Math.random()*0.6+0.2;}update(){this.x+=this.sx;this.y+=this.sy;if(this.x<-20||this.x>w+20||this.y<-20||this.y>h+20)this.reset();}draw(){ctx.beginPath();ctx.arc(this.x,this.y,this.size,0,Math.PI*2);ctx.fillStyle=this.color+this.alpha+')';ctx.fill();}}
for(let i=0;i<60;i++)particles.push(new P());
function anim(){ctx.clearRect(0,0,w,h);particles.forEach(p=>{p.update();p.draw();});requestAnimationFrame(anim);}
anim();
})();
const KEYS={
total:'jf_total', earnItems:'jf_earn', deductItems:'jf_deduct', rewards:'jf_rewards',
records:'jf_records', rid:'jf_rid', goals:'jf_goals', habits:'jf_habits',
achievements:'jf_achieve', backpack:'jf_backpack'
};
let totalPoints=0, earnItems=[], deductItems=[], rewards=[], records=[], recordIdCounter=0;
let goals=[], habits=[], unlockedAchievements=[], backpack={};
let currentEditItem=null, currentDeleteItem=null, currentDeleteType='', currentExchangeReward=null;
let selectedDate=null, displayYear=null, displayMonth=null;
function initData(){
totalPoints=parseInt(localStorage.getItem(KEYS.total))||0;
earnItems=JSON.parse(localStorage.getItem(KEYS.earnItems))||[{id:1,name:'完成作业',points:10},{id:2,name:'阅读',points:5}];
deductItems=JSON.parse(localStorage.getItem(KEYS.deductItems))||[{id:1,name:'未完成作业',points:-10}];
rewards=JSON.parse(localStorage.getItem(KEYS.rewards))||[{id:1,name:'看电视30分钟',points:50}];
records=JSON.parse(localStorage.getItem(KEYS.records))||[];
recordIdCounter=parseInt(localStorage.getItem(KEYS.rid))||0;
goals=JSON.parse(localStorage.getItem(KEYS.goals))||[];
habits=JSON.parse(localStorage.getItem(KEYS.habits))||[];
unlockedAchievements=JSON.parse(localStorage.getItem(KEYS.achievements))||[];
backpack=JSON.parse(localStorage.getItem(KEYS.backpack))||{};
}
function save(){
localStorage.setItem(KEYS.total,totalPoints);
localStorage.setItem(KEYS.earnItems,JSON.stringify(earnItems));
localStorage.setItem(KEYS.deductItems,JSON.stringify(deductItems));
localStorage.setItem(KEYS.rewards,JSON.stringify(rewards));
localStorage.setItem(KEYS.records,JSON.stringify(records));
localStorage.setItem(KEYS.rid,recordIdCounter);
localStorage.setItem(KEYS.goals,JSON.stringify(goals));
localStorage.setItem(KEYS.habits,JSON.stringify(habits));
localStorage.setItem(KEYS.achievements,JSON.stringify(unlockedAchievements));
localStorage.setItem(KEYS.backpack,JSON.stringify(backpack));
}
function formatDate(d){return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;}
function addRecord(title,amount,type,date=null,time=null){
const now=new Date(); recordIdCounter++;
records.unshift({id:recordIdCounter,title,amount,type,date:date||formatDate(now),time:time||now.toLocaleTimeString('zh-CN',{hour:'2-digit',minute:'2-digit'})});
checkAchievements();
}
function updateUI(){document.getElementById('total-points').textContent=totalPoints.toLocaleString();}
function updateToday(){
const today=formatDate(new Date()), dayR=records.filter(r=>r.date===today);
const earn=dayR.filter(r=>r.type==='income').reduce((s,r)=>s+r.amount,0);
const deduct=Math.abs(dayR.filter(r=>r.type==='expense').reduce((s,r)=>s+r.amount,0));
document.getElementById('daily-challenge').textContent=`赚${earn}分 - 扣${deduct}分 = ${earn-deduct}分`;
}
function showAlert(msg){
const m=document.createElement('div'); m.className='modal show';
m.innerHTML=`<div class="modal-content"><div class="modal-title">提示</div><div class="modal-body"><p>${msg}</p></div><div class="modal-footer"><button class="modal-btn confirm" onclick="this.closest('.modal').remove()">确定</button></div></div>`;
document.body.appendChild(m); m.addEventListener('click',e=>{if(e.target===m)m.remove();});
}
function closeModal(id){document.getElementById(id).classList.remove('show');}
function switchPage(name){
document.querySelectorAll('.page').forEach(p=>p.classList.remove('active'));
const target=document.getElementById(name+'-page'); if(target)target.classList.add('active');
document.querySelectorAll('.nav-item').forEach(n=>n.classList.remove('active'));
const navMap={home:0, records:1, habit:2, achievement:3};
if(navMap[name]!==undefined)document.querySelectorAll('.nav-item')[navMap[name]].classList.add('active');
if(name==='home'){loadHome();}
else if(name==='records'){loadRecords();}
else if(name==='goal'){loadGoals();}
else if(name==='gacha'){}
else if(name==='habit'){loadHabits();}
else if(name==='achievement'){loadAchievements();}
else if(name==='earn'){loadEarnList();}
else if(name==='deduct'){loadDeductList();}
else if(name==='exchange'){loadExchangeList();}
else if(name==='earn-manage'){loadEarnManage();}
else if(name==='deduct-manage'){loadDeductManage();}
else if(name==='exchange-manage'){loadRewardManage();}
window.scrollTo(0,0);
}
function loadHome(){updateUI();updateToday();loadWeekGrid('current-week-grid','week-score-text');}
function loadRecords(){if(!selectedDate)selectedDate=formatDate(new Date());loadWeekGrid('records-week-grid','records-week-score-text');loadDayRecords();loadHistory();}
function loadWeekGrid(gridId,titleId){
const grid=document.getElementById(gridId), now=new Date(), curDay=now.getDay();
const start=new Date(now); start.setDate(now.getDate()-(curDay===0?6:curDay-1));
let total=0, html=''; const days=['周一','周二','周三','周四','周五','周六','周日'];
days.forEach((d,i)=>{
const date=new Date(start); date.setDate(start.getDate()+i); const ds=formatDate(date);
const dayR=records.filter(r=>r.date===ds); const dayTotal=dayR.reduce((s,r)=>s+r.amount,0); total+=dayTotal;
const isToday=date.toDateString()===now.toDateString(), isSel=selectedDate===ds;
html+=`<div class="day-item ${isToday?'today':''} ${isSel?'selected':''}" onclick="selectDay('${ds}')"><div class="day-name">${d}</div><div class="day-points">${dayTotal>0?'+':''}${dayTotal}</div></div>`;
});
grid.innerHTML=html;
const el=document.getElementById(titleId); if(el)el.textContent=`本周积分 (${total>=0?'+':''}${total})`;
}
function selectDay(ds){selectedDate=ds; switchPage('records');}
function loadDayRecords(){
const c=document.getElementById('day-records');
if(!selectedDate){c.innerHTML='<div class="empty-state">请选择日期</div>';return;}
const dayR=records.filter(r=>r.date===selectedDate);
if(dayR.length===0)c.innerHTML='<div class="empty-state">暂无记录</div>';
else c.innerHTML=dayR.map(r=>`<div class="list-item"><div class="item-info"><div class="item-name">${r.title}</div><div class="record-time">${r.time}</div></div><div class="item-points ${r.type}">${r.type==='income'?'+':''}${r.amount}</div><button class="action-btn delete" onclick="undoRecord(${r.id})">撤销</button></div>`).join('');
}
function loadHistory(){
const container=document.getElementById('history-weeks'); if(!container)return;
const now=new Date(), curYear=now.getFullYear(), curMonth=now.getMonth();
if(displayYear===null){displayYear=curYear; displayMonth=curMonth;}
const dYear=displayYear, dMonth=displayMonth;
const yearStart=formatDate(new Date(dYear,0,1)), yearEnd=formatDate(new Date(dYear,11,31));
const yearRecords=records.filter(r=>r.date>=yearStart&&r.date<=yearEnd);
const yearTotal=yearRecords.reduce((s,r)=>s+r.amount,0);
const daysInMonth=new Date(dYear,dMonth+1,0).getDate();
const monthStart=formatDate(new Date(dYear,dMonth,1)), monthEnd=formatDate(new Date(dYear,dMonth,daysInMonth));
const monthRecords=records.filter(r=>r.date>=monthStart&&r.date<=monthEnd);
const monthTotal=monthRecords.reduce((s,r)=>s+r.amount,0);
const firstDay=new Date(dYear,dMonth,1).getDay(), startPadding=firstDay===0?6:firstDay-1;
let html=`<div class="month-calendar"><div class="section-title">${dYear}年${dMonth+1}月 (${monthTotal>=0?'+':''}${monthTotal})</div><div class="month-weekdays">${['一','二','三','四','五','六','日'].map(w=>`<div class="month-weekday">${w}</div>`).join('')}</div><div class="month-days">`;
for(let p=0;p<startPadding;p++)html+='<div class="month-day empty"></div>';
for(let d=1;d<=daysInMonth;d++){
const ds=formatDate(new Date(dYear,dMonth,d));
const dayR=records.filter(r=>r.date===ds); const dayTotal=dayR.reduce((s,r)=>s+r.amount,0);
const ptsStr=dayTotal!==0?((dayTotal>0?'+':'')+dayTotal):'';
const isToday=ds===formatDate(now)?' today':'';
html+=`<div class="month-day${isToday}"><div class="month-day-num">${d}</div><div class="month-day-pts">${ptsStr}</div></div>`;
}
html+=`</div></div><div class="year-calendar">`;
for(let m=0;m<12;m++){
const ms=formatDate(new Date(dYear,m,1)), me=formatDate(new Date(dYear,m,new Date(dYear,m+1,0).getDate()));
const mRec=records.filter(r=>r.date>=ms&&r.date<=me); const mTotal=mRec.reduce((s,r)=>s+r.amount,0);
html+=`<div class="year-month-card${(dYear===curYear&&m===curMonth)?' current':''}" onclick="displayMonth=${m};loadHistory();">${m+1}月<br>${mTotal>=0?'+':''}${mTotal}</div>`;
}
html+='</div>'; container.innerHTML=html;
}
function loadEarnList(){
const c=document.getElementById('earn-list');
let h=`<div class="quick-add"><input class="qa-name" id="quick-earn-name" placeholder="名称"><input class="qa-points" id="quick-earn-points" type="number" placeholder="积分"><button class="qa-btn" onclick="quickEarn()">赚取</button></div>`;
if(earnItems.length===0)h+='<div class="empty-state">暂无赚取项</div>';
else h+=earnItems.map(i=>`<div class="list-item"><div class="item-info"><div class="item-name">${i.name}</div><div class="item-points income">+${i.points}</div></div><button class="item-btn earn" onclick="earnPoints(${i.id})">赚取</button></div>`).join('');
c.innerHTML=h;
}
function quickEarn(){
const name=document.getElementById('quick-earn-name').value.trim()||formatDate(new Date());
const pts=parseInt(document.getElementById('quick-earn-points').value);
if(!pts||pts<=0)return showAlert('请输入有效积分');
totalPoints+=pts; addRecord(name,pts,'income');
document.getElementById('quick-earn-name').value=''; document.getElementById('quick-earn-points').value='';
save(); updateUI(); updateToday(); loadEarnList();
}
function earnPoints(id){
const item=earnItems.find(i=>i.id===id); if(!item)return;
totalPoints+=item.points; addRecord(item.name,item.points,'income');
save(); updateUI(); updateToday();
}
function loadDeductList(){
const c=document.getElementById('deduct-list');
let h=`<div class="quick-add deduct"><input class="qa-name" id="quick-deduct-name" placeholder="名称"><input class="qa-points" id="quick-deduct-points" type="number" placeholder="积分"><button class="qa-btn" onclick="quickDeduct()">扣取</button></div>`;
if(deductItems.length===0)h+='<div class="empty-state">暂无扣分项</div>';
else h+=deductItems.map(i=>`<div class="list-item"><div class="item-info"><div class="item-name">${i.name}</div><div class="item-points expense">${i.points}</div></div><button class="item-btn deduct" onclick="deductPoints(${i.id})">扣取</button></div>`).join('');
c.innerHTML=h;
}
function quickDeduct(){
const name=document.getElementById('quick-deduct-name').value.trim()||formatDate(new Date());
const pts=parseInt(document.getElementById('quick-deduct-points').value);
if(!pts||pts<=0)return showAlert('请输入有效积分');
totalPoints-=pts; addRecord(name,-pts,'expense');
document.getElementById('quick-deduct-name').value=''; document.getElementById('quick-deduct-points').value='';
save(); updateUI(); updateToday(); loadDeductList();
}
function deductPoints(id){
const item=deductItems.find(i=>i.id===id); if(!item)return;
totalPoints+=item.points; addRecord(item.name,item.points,'expense');
save(); updateUI(); updateToday();
}
function loadExchangeList(){
const c=document.getElementById('exchange-list');
if(rewards.length===0){c.innerHTML='<div class="empty-state">暂无奖励</div>';return;}
c.innerHTML=rewards.map(r=>`<div class="list-item"><div class="item-info"><div class="item-name">${r.name}</div><div class="item-points">${r.points}积分</div></div><button class="item-btn exchange" onclick="showExchangeModal(${r.id})">兑换</button></div>`).join('');
}
function showExchangeModal(id){
const r=rewards.find(x=>x.id===id); if(!r)return;
currentExchangeReward=r;
document.getElementById('exchange-reward-name').textContent=r.name;
document.getElementById('exchange-reward-points').textContent=r.points+'积分';
document.getElementById('exchange-confirm-modal').classList.add('show');
}
function confirmExchange(){
if(!currentExchangeReward)return;
if(totalPoints<currentExchangeReward.points){closeModal('exchange-confirm-modal');showAlert('积分不足');return;}
totalPoints-=currentExchangeReward.points;
addRecord('兑换:'+currentExchangeReward.name,-currentExchangeReward.points,'expense');
save(); updateUI(); updateToday(); closeModal('exchange-confirm-modal');
}
function loadEarnManage(){
const c=document.getElementById('earn-manage-list');
if(earnItems.length===0){c.innerHTML='<div class="empty-state">暂无</div>';return;}
c.innerHTML=earnItems.map(i=>`<div class="list-item"><div class="item-info"><div class="item-name">${i.name}</div><div class="item-points income">+${i.points}</div></div><div class="item-actions"><button class="action-btn edit" onclick="editEarnItem(${i.id})">编辑</button><button class="action-btn delete" onclick="deleteEarnItem(${i.id})">删除</button></div></div>`).join('');
}
function showAddEarnItemModal(){currentEditItem=null; document.getElementById('earn-item-name').value=''; document.getElementById('earn-item-points').value=''; document.getElementById('add-earn-item-modal').classList.add('show');}
function editEarnItem(id){const item=earnItems.find(i=>i.id===id); if(item){currentEditItem=item; document.getElementById('earn-item-name').value=item.name; document.getElementById('earn-item-points').value=item.points; document.getElementById('add-earn-item-modal').classList.add('show');}}
function saveEarnItem(){
const name=document.getElementById('earn-item-name').value.trim(), pts=parseInt(document.getElementById('earn-item-points').value);
if(!name||isNaN(pts))return showAlert('请填写完整');
if(currentEditItem){currentEditItem.name=name; currentEditItem.points=pts;}
else{const id=earnItems.length?Math.max(...earnItems.map(i=>i.id))+1:1; earnItems.push({id,name,points:pts});}
save(); loadEarnManage(); closeModal('add-earn-item-modal');
}
function deleteEarnItem(id){currentDeleteItem=id; currentDeleteType='earn'; document.getElementById('delete-confirm-modal').classList.add('show');}
function loadDeductManage(){
const c=document.getElementById('deduct-manage-list');
if(deductItems.length===0){c.innerHTML='<div class="empty-state">暂无</div>';return;}
c.innerHTML=deductItems.map(i=>`<div class="list-item"><div class="item-info"><div class="item-name">${i.name}</div><div class="item-points expense">${i.points}</div></div><div class="item-actions"><button class="action-btn edit" onclick="editDeductItem(${i.id})">编辑</button><button class="action-btn delete" onclick="deleteDeductItem(${i.id})">删除</button></div></div>`).join('');
}
function showAddDeductItemModal(){currentEditItem=null; document.getElementById('deduct-item-name').value=''; document.getElementById('deduct-item-points').value=''; document.getElementById('add-deduct-item-modal').classList.add('show');}
function editDeductItem(id){const item=deductItems.find(i=>i.id===id); if(item){currentEditItem=item; document.getElementById('deduct-item-name').value=item.name; document.getElementById('deduct-item-points').value=Math.abs(item.points); document.getElementById('add-deduct-item-modal').classList.add('show');}}
function saveDeductItem(){
const name=document.getElementById('deduct-item-name').value.trim(), pts=parseInt(document.getElementById('deduct-item-points').value);
if(!name||isNaN(pts))return showAlert('请填写完整');
if(currentEditItem){currentEditItem.name=name; currentEditItem.points=-pts;}
else{const id=deductItems.length?Math.max(...deductItems.map(i=>i.id))+1:1; deductItems.push({id,name,points:-pts});}
save(); loadDeductManage(); closeModal('add-deduct-item-modal');
}
function deleteDeductItem(id){currentDeleteItem=id; currentDeleteType='deduct'; document.getElementById('delete-confirm-modal').classList.add('show');}
function loadRewardManage(){
const c=document.getElementById('reward-manage-list');
if(rewards.length===0){c.innerHTML='<div class="empty-state">暂无</div>';return;}
c.innerHTML=rewards.map(r=>`<div class="list-item"><div class="item-info"><div class="item-name">${r.name}</div><div class="item-points">${r.points}积分</div></div><div class="item-actions"><button class="action-btn edit" onclick="editReward(${r.id})">编辑</button><button class="action-btn delete" onclick="deleteReward(${r.id})">删除</button></div></div>`).join('');
}
function showAddRewardModal(){currentEditItem=null; document.getElementById('reward-name').value=''; document.getElementById('reward-points').value=''; document.getElementById('add-reward-modal').classList.add('show');}
function editReward(id){const r=rewards.find(x=>x.id===id); if(r){currentEditItem=r; document.getElementById('reward-name').value=r.name; document.getElementById('reward-points').value=r.points; document.getElementById('add-reward-modal').classList.add('show');}}
function saveReward(){
const name=document.getElementById('reward-name').value.trim(), pts=parseInt(document.getElementById('reward-points').value);
if(!name||isNaN(pts))return showAlert('请填写完整');
if(currentEditItem){currentEditItem.name=name; currentEditItem.points=pts;}
else{const id=rewards.length?Math.max(...rewards.map(r=>r.id))+1:1; rewards.push({id,name,points:pts});}
save(); loadRewardManage(); closeModal('add-reward-modal');
}
function deleteReward(id){currentDeleteItem=id; currentDeleteType='reward'; document.getElementById('delete-confirm-modal').classList.add('show');}
function confirmDelete(){
if(currentDeleteType==='earn'){earnItems=earnItems.filter(i=>i.id!==currentDeleteItem);loadEarnManage();}
else if(currentDeleteType==='deduct'){deductItems=deductItems.filter(i=>i.id!==currentDeleteItem);loadDeductManage();}
else if(currentDeleteType==='reward'){rewards=rewards.filter(r=>r.id!==currentDeleteItem);loadRewardManage();}
save(); closeModal('delete-confirm-modal');
}
function showAddPointsModal(){
document.getElementById('add-points-date').value=formatDate(new Date());
document.getElementById('add-points-reason').value=''; document.getElementById('add-points-value').value='';
document.getElementById('add-points-modal').classList.add('show');
}
function addPoints(){
const reason=document.getElementById('add-points-reason').value.trim();
const val=parseInt(document.getElementById('add-points-value').value);
const date=document.getElementById('add-points-date').value;
if(!reason||isNaN(val)||!date)return showAlert('请填写完整');
totalPoints+=val; addRecord('补积分:'+reason,val,'income',date);
save(); updateUI(); updateToday(); closeModal('add-points-modal'); loadRecords();
}
function showClearPointsModal(){document.getElementById('clear-points-modal').classList.add('show');}
function confirmClearPoints(){totalPoints=0;records=[];save();updateUI();updateToday();closeModal('clear-points-modal');}
function systemReset(){
if(!confirm('确定重置所有数据?'))return;
totalPoints=0; records=[]; recordIdCounter=0;
earnItems=[{id:1,name:'完成作业',points:10}]; deductItems=[{id:1,name:'未完成作业',points:-10}]; rewards=[{id:1,name:'看电视30分钟',points:50}];
goals=[]; habits=[]; unlockedAchievements=[]; backpack={};
save(); updateUI(); updateToday(); loadHome();
}
function undoRecord(id){
const idx=records.findIndex(r=>r.id===id); if(idx===-1)return;
totalPoints-=records[idx].amount; records.splice(idx,1);
save(); updateUI(); updateToday(); loadDayRecords();
}
function loadGoals(){
const c=document.getElementById('goal-list');
if(goals.length===0){c.innerHTML='<div class="empty-state">还没有目标</div>';return;}
c.innerHTML=goals.map(g=>{
const pct=Math.min(100,Math.floor(totalPoints/g.points*100));
return `<div class="list-item"><div class="item-info"><div class="item-name">${g.name} (${totalPoints}/${g.points})</div><div class="progress-bar"><div class="progress-fill" style="width:${pct}%"></div></div></div><button class="action-btn delete" onclick="deleteGoal(${g.id})">删除</button></div>`;
}).join('');
}
function showAddGoalModal(){document.getElementById('goal-name').value='';document.getElementById('goal-points').value='';document.getElementById('add-goal-modal').classList.add('show');}
function saveGoal(){
const name=document.getElementById('goal-name').value.trim(), pts=parseInt(document.getElementById('goal-points').value);
if(!name||isNaN(pts))return showAlert('请填写完整');
const id=goals.length?Math.max(...goals.map(g=>g.id))+1:1;
goals.push({id,name,points:pts}); save(); loadGoals(); closeModal('add-goal-modal');
}
function deleteGoal(id){goals=goals.filter(g=>g.id!==id);save();loadGoals();}
function playGacha(){
if(totalPoints<20)return showAlert('积分不足20');
totalPoints-=20; addRecord('扭蛋抽奖',-20,'expense');
const pool=[
{name:'积分+5',type:'points',value:5},{name:'积分+10',type:'points',value:10},
{name:'免扣金牌',type:'shield'},{name:'兑换券',type:'voucher'},{name:'谢谢参与',type:'none'}
];
const prize=pool[Math.floor(Math.random()*pool.length)];
if(prize.type==='points'){totalPoints+=prize.value;addRecord('扭蛋奖励',prize.value,'income');}
else if(prize.type==='shield'){backpack.shield=(backpack.shield||0)+1;}
else if(prize.type==='voucher'){backpack.voucher=(backpack.voucher||0)+1;}
save(); updateUI(); updateToday();
document.getElementById('gacha-result').innerHTML=`🎉 获得:${prize.name}`;
setTimeout(()=>{document.getElementById('gacha-result').innerHTML='';},2000);
}
function loadHabits(){
const c=document.getElementById('habit-list');
if(habits.length===0){c.innerHTML='<div class="empty-state">还没有习惯</div>';return;}
const today=formatDate(new Date());
c.innerHTML=habits.map(h=>{
const doneToday=records.some(r=>r.title===h.name&&r.date===today);
return `<div class="list-item"><div class="item-info"><div class="item-name">${h.name} (${h.period==='daily'?'每天':'每周'} +${h.points})</div></div><button class="item-btn earn" onclick="doHabit(${h.id})" ${doneToday?'disabled':''}>${doneToday?'已完成':'打卡'}</button></div>`;
}).join('');
}
function showAddHabitModal(){document.getElementById('habit-name').value='';document.getElementById('habit-points').value='';document.getElementById('add-habit-modal').classList.add('show');}
function saveHabit(){
const name=document.getElementById('habit-name').value.trim(), pts=parseInt(document.getElementById('habit-points').value), period=document.getElementById('habit-period').value;
if(!name||isNaN(pts))return showAlert('请填写完整');
const id=habits.length?Math.max(...habits.map(h=>h.id))+1:1;
habits.push({id,name,points:pts,period}); save(); loadHabits(); closeModal('add-habit-modal');
}
function doHabit(id){
const h=habits.find(x=>x.id===id); if(!h)return;
totalPoints+=h.points; addRecord(h.name,h.points,'income');
save(); updateUI(); updateToday(); loadHabits();
}
const ACHIEVEMENTS=[
{id:'first_earn',name:'初尝甜头',desc:'首次赚取积分',check:()=>records.some(r=>r.type==='income')},
{id:'points_100',name:'积分破百',desc:'总积分达到100',check:()=>totalPoints>=100},
{id:'streak_7',name:'连续7天',desc:'连续7天有积分记录',check:()=>{const dates=[...new Set(records.map(r=>r.date))].sort().slice(-7);return dates.length===7;}},
{id:'gacha_5',name:'扭蛋爱好者',desc:'抽扭蛋5次',check:()=>records.filter(r=>r.title==='扭蛋抽奖').length>=5}
];
function checkAchievements(){
ACHIEVEMENTS.forEach(a=>{if(!unlockedAchievements.includes(a.id)&&a.check()){unlockedAchievements.push(a.id);showAlert(`🏆 解锁成就:${a.name}`);}});
save();
}
function loadAchievements(){
const grid=document.getElementById('badge-grid');
grid.innerHTML=ACHIEVEMENTS.map(a=>{
const unlocked=unlockedAchievements.includes(a.id);
return `<div class="badge-item ${unlocked?'unlocked':''}"><div style="font-size:28px;">${unlocked?'🏆':'🔒'}</div><div style="font-size:10px;">${a.name}</div></div>`;
}).join('');
}
function generateReport(){
const canvas=document.createElement('canvas'); canvas.width=400; canvas.height=600;
const ctx=canvas.getContext('2d');
ctx.fillStyle='#0a0c16'; ctx.fillRect(0,0,400,600);
ctx.fillStyle='#00e5ff'; ctx.font='bold 22px sans-serif'; ctx.fillText('积分战报',120,50);
const now=new Date(); const start=new Date(now); start.setDate(now.getDate()-now.getDay()+1);
const weekR=records.filter(r=>{const d=new Date(r.date);return d>=start;});
const earn=weekR.filter(r=>r.type==='income').reduce((s,r)=>s+r.amount,0);
const deduct=Math.abs(weekR.filter(r=>r.type==='expense').reduce((s,r)=>s+r.amount,0));
ctx.fillStyle='#fff'; ctx.font='16px sans-serif';
ctx.fillText(`总积分:${totalPoints}`,30,120);
ctx.fillText(`本周赚取:+${earn} 扣取:-${deduct}`,30,160);
ctx.fillText(`成就:${unlockedAchievements.length}个`,30,200);
canvas.toBlob(blob=>{
const url=URL.createObjectURL(blob); const a=document.createElement('a'); a.href=url; a.download='战报.png'; a.click();
});
}
document.querySelectorAll('.modal').forEach(m=>{m.addEventListener('click',function(e){if(e.target===this)this.classList.remove('show');});});
document.addEventListener('DOMContentLoaded',()=>{initData();loadHome();});
</script>
</body>
</html>