Files
NurtureTime/docs/网站原型.html
2026-10-02 00:28:37 +08:00

1343 lines
44 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="zh-CN">
<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"
/>
<title>自律公约 · 辅助执行</title>
<style>
:root {
--primary: #4a7ed9;
--primary-light: #eef3fb;
--success: #52b788;
--success-light: #edf7f1;
--warning: #f0a04b;
--warning-light: #fef8f1;
--danger: #e85d5d;
--danger-light: #fef5f5;
--bg: #f2f3f7;
--card: #ffffff;
--text: #2c3e50;
--text-secondary: #6b7d8e;
--text-light: #95a5b8;
--border: #e8ecf1;
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
--radius-sm: 8px;
--radius: 12px;
--radius-lg: 16px;
--radius-xl: 20px;
--font-xs: 11px;
--font-sm: 12px;
--font-md: 14px;
--font-lg: 16px;
--font-xl: 18px;
--font-2xl: 22px;
--font-3xl: 32px;
--font-4xl: 48px;
--transition: 0.2s ease;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
background: #e8ecf2;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
-webkit-font-smoothing: antialiased;
}
.phone-frame {
width: 400px;
height: 860px;
background: #1a1a1a;
border-radius: 32px;
padding: 12px;
box-shadow:
0 20px 60px rgba(0, 0, 0, 0.3),
0 0 0 2px #333;
position: relative;
}
.phone-screen {
width: 100%;
height: 100%;
background: var(--bg);
border-radius: 22px;
overflow: hidden;
display: flex;
flex-direction: column;
position: relative;
}
.status-bar {
background: #fff;
padding: 10px 20px 6px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: var(--font-xs);
font-weight: 600;
color: #1a1a1a;
flex-shrink: 0;
border-bottom: 1px solid var(--border);
}
.main-content {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
scroll-behavior: smooth;
}
.main-content::-webkit-scrollbar {
width: 0;
}
.page {
display: none;
padding: 16px;
padding-bottom: 80px;
animation: fadeIn 0.25s ease;
}
.page.active {
display: block;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.bottom-nav {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: #fff;
display: flex;
justify-content: space-around;
padding: 8px 8px 12px;
border-top: 1px solid var(--border);
z-index: 100;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.04);
}
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
cursor: pointer;
padding: 6px 14px;
border-radius: 12px;
transition: var(--transition);
color: var(--text-light);
font-size: var(--font-xs);
font-weight: 500;
user-select: none;
position: relative;
}
.nav-item .nav-icon {
font-size: 22px;
transition: var(--transition);
line-height: 1;
}
.nav-item.active {
color: var(--primary);
}
.nav-item.active::after {
content: "";
position: absolute;
top: -9px;
width: 20px;
height: 3px;
background: var(--primary);
border-radius: 0 0 3px 3px;
}
.nav-badge {
position: absolute;
top: 0;
right: 4px;
width: 18px;
height: 18px;
background: var(--danger);
color: #fff;
border-radius: 50%;
font-size: 10px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
}
.card {
background: var(--card);
border-radius: var(--radius-lg);
padding: 16px;
margin-bottom: 12px;
box-shadow: var(--shadow-sm);
border: 1px solid var(--border);
transition: var(--transition);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.card-title {
font-weight: 700;
font-size: var(--font-lg);
color: var(--text);
}
.tag {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 10px;
border-radius: 20px;
font-size: var(--font-xs);
font-weight: 600;
letter-spacing: 0.3px;
}
.tag-mode {
background: #e8f0fe;
color: #3b6ec4;
}
.tag-level-1 {
background: #fff3e0;
color: #e67e22;
}
.tag-level-2 {
background: #ffe8e0;
color: #d35400;
}
.tag-level-3 {
background: #ffd6d6;
color: #c0392b;
}
.tag-success {
background: #e6f7ee;
color: #27ae60;
}
.big-number {
font-size: var(--font-4xl);
font-weight: 800;
line-height: 1;
letter-spacing: -2px;
}
.big-number.warning {
color: var(--warning);
}
.big-number.danger {
color: var(--danger);
}
.sanction-card {
border-radius: var(--radius-lg);
padding: 14px 16px;
margin-bottom: 8px;
border-left: 5px solid transparent;
transition: var(--transition);
}
.sanction-card.level-1 {
background: #fffdf7;
border-left-color: #f0a04b;
}
.sanction-card.level-2 {
background: #fffbf8;
border-left-color: #e8833a;
}
.sanction-card.level-3 {
background: #fff8f8;
border-left-color: #e85d5d;
}
.quick-actions {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-bottom: 12px;
}
.quick-action-btn {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
padding: 14px 10px;
border-radius: var(--radius);
border: 2px dashed var(--border);
cursor: pointer;
transition: var(--transition);
font-size: var(--font-xs);
font-weight: 600;
color: var(--text-secondary);
text-align: center;
background: #fafbfc;
}
.quick-action-btn:active {
border-color: var(--primary);
background: var(--primary-light);
color: var(--primary);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 10px 20px;
border-radius: 25px;
font-weight: 600;
font-size: var(--font-md);
cursor: pointer;
transition: var(--transition);
border: none;
letter-spacing: 0.3px;
user-select: none;
}
.btn-primary {
background: var(--primary);
color: #fff;
box-shadow: 0 4px 12px rgba(74, 126, 217, 0.3);
}
.btn-outline {
background: #fff;
color: var(--primary);
border: 2px solid var(--primary);
}
.btn-sm {
padding: 6px 14px;
font-size: var(--font-sm);
border-radius: 20px;
}
.btn-block {
width: 100%;
justify-content: center;
}
.list-item {
display: flex;
align-items: center;
padding: 12px 14px;
background: #fff;
border-radius: var(--radius);
margin-bottom: 8px;
gap: 12px;
border: 1px solid var(--border);
transition: var(--transition);
cursor: pointer;
}
.list-item:active {
background: #f9fafb;
}
.check-circle {
width: 22px;
height: 22px;
border-radius: 50%;
border: 2px solid #d0d7e0;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: var(--transition);
flex-shrink: 0;
font-size: 12px;
color: transparent;
}
.check-circle.checked {
background: var(--success);
border-color: var(--success);
color: #fff;
}
.modal-overlay {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 200;
display: flex;
align-items: flex-end;
animation: fadeInOverlay 0.2s ease;
border-radius: 22px;
overflow: hidden;
}
@keyframes fadeInOverlay {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.modal-sheet {
background: #fff;
width: 100%;
max-height: 85%;
border-radius: 20px 20px 0 0;
padding: 8px 16px 20px;
overflow-y: auto;
animation: slideUp 0.3s ease;
box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.2);
}
@keyframes slideUp {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
.modal-handle {
width: 36px;
height: 4px;
background: #d0d7e0;
border-radius: 2px;
margin: 8px auto 14px;
}
.modal-title {
font-weight: 700;
font-size: var(--font-xl);
text-align: center;
margin-bottom: 16px;
color: var(--text);
}
.form-group {
margin-bottom: 14px;
}
.form-label {
display: block;
font-weight: 600;
font-size: var(--font-sm);
color: var(--text);
margin-bottom: 5px;
}
.form-input,
.form-select,
.form-textarea {
width: 100%;
padding: 10px 14px;
border: 2px solid var(--border);
border-radius: var(--radius-sm);
font-size: var(--font-md);
font-family: inherit;
transition: var(--transition);
background: #fafbfc;
color: var(--text);
}
.form-select {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236B7D8E' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'%3E%3C/path%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 36px;
}
.form-textarea {
min-height: 70px;
resize: vertical;
}
.clause-ref {
display: inline-block;
background: #f0f4f8;
color: #4a5568;
padding: 2px 8px;
border-radius: 4px;
font-size: var(--font-xs);
font-weight: 600;
font-family: "SF Mono", "Menlo", monospace;
}
.toast {
position: absolute;
top: 60px;
left: 50%;
transform: translateX(-50%);
background: #2c3e50;
color: #fff;
padding: 10px 20px;
border-radius: 25px;
font-size: var(--font-sm);
font-weight: 600;
z-index: 300;
animation:
toastIn 0.3s ease,
toastOut 0.3s ease 2s forwards;
pointer-events: none;
white-space: nowrap;
}
@keyframes toastIn {
from {
opacity: 0;
transform: translateX(-50%) translateY(-20px);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
}
@keyframes toastOut {
from {
opacity: 1;
}
to {
opacity: 0;
transform: translateX(-50%) translateY(-20px);
}
}
.mode-indicator {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: 20px;
font-weight: 700;
font-size: var(--font-sm);
}
.mode-semester {
background: #e3f2fd;
color: #1565c0;
}
.mode-weekend {
background: #fff3e0;
color: #e65100;
}
.mode-holiday {
background: #e8f5e9;
color: #2e7d32;
}
.streak-card {
text-align: center;
padding: 16px;
background: linear-gradient(135deg, #e8f8f0, #d5f5e3);
border-radius: var(--radius-lg);
margin-bottom: 12px;
border: 2px solid #a9dfbf;
}
.alert {
padding: 10px 14px;
border-radius: var(--radius-sm);
font-size: var(--font-sm);
font-weight: 500;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 8px;
}
.alert-warning {
background: #fef9e7;
color: #b7950b;
border: 1px solid #f9e79f;
}
.alert-success {
background: #e8f8f0;
color: #1e8449;
border: 1px solid #a9dfbf;
}
.alert-info {
background: #e8f4fd;
color: #2471a3;
border: 1px solid #d0e8f8;
}
@media (max-width: 440px) {
.phone-frame {
width: 100%;
height: 100vh;
border-radius: 0;
padding: 0;
box-shadow: none;
background: transparent;
}
.phone-screen {
border-radius: 0;
}
}
.expandable {
cursor: pointer;
}
.detail-list {
font-size: var(--font-sm);
color: var(--text-secondary);
line-height: 1.6;
padding-left: 10px;
}
.inline-code {
background: #f0f0f0;
padding: 2px 6px;
border-radius: 4px;
font-family: monospace;
font-size: var(--font-xs);
}
</style>
</head>
<body>
<div class="phone-frame">
<div class="phone-screen" id="app">
<div class="status-bar">
<span class="time">9:41</span><span>📶 🔋 85%</span>
</div>
<div class="main-content" id="mainContent">
<!-- 首页 -->
<div class="page active" id="page-home">
<div
style="
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
"
>
<span class="mode-indicator mode-semester" id="modeBadge"
>📚 学期模式</span
>
<span
style="font-size: var(--font-sm); color: var(--text-secondary)"
>📅 2026年8月5日 周三</span
>
</div>
<div class="card" style="text-align: center; padding: 20px 16px">
<div class="big-number-label">本周累计违规</div>
<div class="big-number warning" id="weeklyCount">2</div>
<div style="margin-top: 4px">
<span
class="tag tag-level-2"
id="sanctionLevelTag"
style="font-size: var(--font-md); padding: 6px 14px"
>⚠️ 二级制裁</span
>
</div>
<div
style="
margin-top: 10px;
font-size: var(--font-sm);
color: var(--text-secondary);
"
>
连续无违规
<strong style="color: var(--success)" id="streakDays">3</strong>
天
</div>
</div>
<div class="sanction-card level-2" id="sanctionDetailCard">
<div class="sanction-title">📋 二级制裁内容 (第9.3.2款)</div>
<div class="sanction-desc">
• 执行一级制裁(餐后厨房与餐厅清洁义务)<br />
• 次日全天禁止手机带入卫生间<br />
• 需在22:00前完成全部B类任务
</div>
</div>
<div
class="alert alert-warning"
id="safetyValveAlert"
style="display: none"
>
💡
已达三级制裁,周日晚间完成<strong>厨房深度清洁</strong>可降为二级
(第9.4款)
</div>
<div style="display: flex; gap: 10px; margin-bottom: 12px">
<div
class="card"
style="flex: 1; text-align: center; padding: 12px"
>
<div style="font-size: 28px">⭐</div>
<div
style="font-weight: 700; font-size: var(--font-xl)"
id="starCount"
>
2
</div>
<div
style="font-size: var(--font-xs); color: var(--text-light)"
>
星标 (3个抵1次)
</div>
</div>
<div
class="card"
style="flex: 1; text-align: center; padding: 12px"
>
<div style="font-size: 28px">🎁</div>
<div
style="
font-weight: 700;
font-size: var(--font-xl);
color: var(--success);
"
>
1
</div>
<div
style="font-size: var(--font-xs); color: var(--text-light)"
>
待兑换奖励
</div>
</div>
</div>
<div class="quick-actions">
<div
class="quick-action-btn"
onclick="openModal('modalViolation')"
>
<span class="btn-icon">📝</span>记录违规
</div>
<div class="quick-action-btn" onclick="openModal('modalDebt')">
<span class="btn-icon">📋</span>登记债务
</div>
<div class="quick-action-btn" onclick="openModal('modalReward')">
<span class="btn-icon">🎁</span>兑换奖励
</div>
</div>
<div class="card">
<div class="card-header">
<span class="card-title">📌 近期记录</span
><span
style="font-size: var(--font-xs); color: var(--text-light)"
onclick="switchTab('records')"
>查看全部 →</span
>
</div>
<div class="list-item">
<div class="item-icon" style="background: #fff3e0">🛏️</div>
<div class="item-info">
<div class="item-title">起床超时</div>
<div class="item-subtitle">
8/5 · <span class="clause-ref">第2.2条(一)</span> · 一级制裁
</div>
</div>
<span class="tag tag-level-1">待确认</span>
</div>
<div class="list-item">
<div class="item-icon" style="background: #ffe8e0">📱</div>
<div class="item-info">
<div class="item-title">手机管制违规</div>
<div class="item-subtitle">
8/4 · <span class="clause-ref">第6.1条</span> · 二级制裁
</div>
</div>
<span class="tag tag-success">已确认</span>
</div>
</div>
</div>
<!-- 记录页 -->
<div class="page" id="page-records">
<div style="display: flex; gap: 8px; margin-bottom: 14px">
<button
class="btn btn-primary btn-sm"
id="tabBtn-violations"
onclick="switchRecordTab('violations')"
>
违规记录
</button>
<button
class="btn btn-outline btn-sm"
id="tabBtn-debts"
onclick="switchRecordTab('debts')"
>
债务列表
</button>
<button
class="btn btn-outline btn-sm"
id="tabBtn-rewards"
onclick="switchRecordTab('rewards')"
>
奖励记录
</button>
</div>
<div id="recordList-violations">
<div class="list-item">
<div class="item-icon" style="background: #fff3e0">🛏️</div>
<div class="item-info">
<div class="item-title">
起床超时 <span class="clause-ref">第2.2条(一)</span>
</div>
<div class="item-subtitle">
8/5 07:25 · 超时15分钟 · 一级制裁
</div>
<div class="item-subtitle">
📝 屏幕时间: 45分钟 | 状态:
<span style="color: #e67e22">待确认</span>
</div>
</div>
<span class="check-circle" onclick="toggleConfirm(this)"
>✓</span
>
</div>
<div class="list-item">
<div class="item-icon" style="background: #ffe8e0">📱</div>
<div class="item-info">
<div class="item-title">
手机未归位 <span class="clause-ref">第6.1条</span>
</div>
<div class="item-subtitle">
8/4 22:35 · 二级制裁 · 已执行禁带令
</div>
<div class="item-subtitle">
📝 屏幕时间: 60分钟 | 状态:
<span style="color: #27ae60">已确认</span>
</div>
</div>
<span class="check-circle checked">✓</span>
</div>
</div>
<div id="recordList-debts" style="display: none">
<div class="list-item">
<div class="item-icon" style="background: #fff3e0">📖</div>
<div class="item-info">
<div class="item-title">背单词未完成</div>
<div class="item-subtitle">
亏欠:20词 · 利息1.5x · 应补:<strong>30词</strong>
</div>
<div class="item-subtitle">
截止:8/6 · <span style="color: #e67e22">待补做</span>
</div>
</div>
<button
class="btn btn-sm btn-outline"
onclick="markDebtDone(this)"
>
标记完成
</button>
</div>
</div>
<div id="recordList-rewards" style="display: none">
<div class="list-item">
<div class="item-icon" style="background: #e8f8f0">🏆</div>
<div class="item-info">
<div class="item-title">任务全勤奖励</div>
<div class="item-subtitle">达成:8/3 · 兑换截止:8/10</div>
</div>
<span class="tag tag-success">待兑换</span>
</div>
</div>
<button
class="btn btn-primary btn-block"
style="margin-top: 8px"
onclick="openModal('modalViolation')"
>
+ 新增违规记录
</button>
</div>
<!-- 统计页 -->
<div class="page" id="page-stats">
<h3 style="margin-bottom: 12px">📊 本周统计 (8/4 - 8/10)</h3>
<div class="streak-card">
<div class="streak-number" id="streakBigNumber">3</div>
<div class="streak-label">🔥 连续无违规天数</div>
</div>
<div class="card">
<div class="card-title" style="margin-bottom: 8px">
⚡ 制裁等级变化
</div>
<span class="tag tag-level-1">周一 一级</span> →
<span class="tag tag-level-2">周三 二级</span>
</div>
<div class="card">
<div class="card-title">📅 本月汇总</div>
<div
style="
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
font-size: var(--font-sm);
"
>
<div>总违规:3次</div>
<div>获得星标:4个</div>
<div>兑换奖励:2次</div>
<div>最长连续:5天</div>
</div>
</div>
</div>
<!-- 我的页 -->
<div class="page" id="page-mine">
<div class="card" style="text-align: center; padding: 20px">
<div style="font-size: 48px">👦</div>
<div style="font-weight: 700">订立人 · 小明</div>
</div>
<div class="card" style="padding: 4px">
<div class="list-item" onclick="openModal('modalParams')">
<div class="item-icon" style="background: #e8f0fe">⚙️</div>
<div class="item-info">
<div class="item-title">执行参数配置</div>
</div>
<span>›</span>
</div>
<div class="list-item" onclick="openModal('modalClause')">
<div class="item-icon" style="background: #fff3e0">📖</div>
<div class="item-info">
<div class="item-title">公约条款速查</div>
</div>
<span>›</span>
</div>
<div class="list-item" onclick="openModal('modalCustomTasks')">
<div class="item-icon" style="background: #e8f8f0">📝</div>
<div class="item-info">
<div class="item-title">自定义B类任务</div>
</div>
<span>›</span>
</div>
<div class="list-item" onclick="switchMode()">
<div class="item-icon" style="background: #fef5f5">🔄</div>
<div class="item-info">
<div class="item-title">切换时期模式</div>
</div>
<span>›</span>
</div>
</div>
<div class="alert alert-info" style="font-size: var(--font-xs)">
⚖️ 本网站仅辅助执行,以线下签字版本为最终依据。
</div>
</div>
</div>
<div class="bottom-nav">
<div
class="nav-item active"
data-tab="home"
onclick="switchTab('home')"
>
<span class="nav-icon">🏠</span>首页
</div>
<div
class="nav-item"
data-tab="records"
onclick="switchTab('records')"
>
<span class="nav-icon">📋</span>记录<span
class="nav-badge"
id="pendingBadge"
>1</span
>
</div>
<div class="nav-item" data-tab="stats" onclick="switchTab('stats')">
<span class="nav-icon">📊</span>统计
</div>
<div class="nav-item" data-tab="mine" onclick="switchTab('mine')">
<span class="nav-icon">👤</span>我的
</div>
</div>
<!-- 模态:记录违规 -->
<div
class="modal-overlay"
id="modalViolation"
style="display: none"
onclick="if (event.target === this) closeModal('modalViolation');"
>
<div class="modal-sheet" onclick="event.stopPropagation()">
<div class="modal-handle"></div>
<div class="modal-title">📝 记录违规</div>
<div class="form-group">
<label class="form-label">日期</label
><input type="date" class="form-input" value="2026-08-05" />
</div>
<div class="form-group">
<label class="form-label">违规类型</label>
<select
class="form-select"
id="vioType"
onchange="updateVioClause()"
>
<option value="">请选择</option>
<option value="起床超时">起床超时 (第2.2条一)</option>
<option value="内务不达标">内务不达标 (第2.2条二)</option>
<option value="作业超时">作业超时 (第2.2条三)</option>
<option value="如厕超时">如厕超时 (第4.3条)</option>
<option value="洗澡超时">洗澡超时 (第5.3条)</option>
<option value="手机管制违规">手机未归位 (第6.1条)</option>
<option value="就寝违规">就寝违规 (第7.2条)</option>
<option value="听歌违规">听歌违规 (第13.2条)</option>
<option value="查题违规">查题违规 (第14.2条)</option>
<option value="B类未完成">B类未完成 (第3.4条)</option>
</select>
</div>
<div class="form-group">
<label class="form-label">引用条款</label
><input type="text" class="form-input" id="vioClause" readonly />
</div>
<div class="form-group">
<label class="form-label">当前制裁等级</label>
<div
id="currentSanctionPreview"
style="
padding: 8px;
background: #fff8f8;
border-radius: 8px;
font-weight: 600;
"
>
⚠️ 本周累计2次 → 触发二级制裁
</div>
</div>
<div class="form-group">
<label class="form-label"
>备注
<span style="color: var(--danger)">*屏幕时间必填</span></label
><textarea
class="form-textarea"
id="vioRemark"
placeholder="填写屏幕时间估算值(如45分钟)及其他备注..."
></textarea>
</div>
<div class="form-group">
<label
><input type="checkbox" id="starMark" /> ⭐
标记“早鸟”获星标</label
>
</div>
<button
class="btn btn-primary btn-block"
onclick="submitViolation()"
>
提交
</button>
<button
class="btn btn-outline btn-block"
style="margin-top: 8px"
onclick="closeModal('modalViolation')"
>
取消
</button>
</div>
</div>
<!-- 模态:债务 -->
<div
class="modal-overlay"
id="modalDebt"
style="display: none"
onclick="if (event.target === this) closeModal('modalDebt');"
>
<div class="modal-sheet" onclick="event.stopPropagation()">
<div class="modal-handle"></div>
<div class="modal-title">📋 登记B类债务</div>
<div class="form-group">
<label>任务</label
><select class="form-select">
<option>背单词</option>
<option>跳绳</option>
<option>复习预习</option>
</select>
</div>
<div class="form-group">
<label>亏欠量</label
><input
type="number"
class="form-input"
placeholder="20词/1000个"
/>
</div>
<div class="form-group">
<label>利息倍数</label
><input class="form-input" value="1.5x" readonly />
</div>
<button
class="btn btn-primary btn-block"
onclick="
closeModal('modalDebt');
showToast('债务已登记');
"
>
登记
</button>
</div>
</div>
<!-- 模态:奖励 -->
<div
class="modal-overlay"
id="modalReward"
style="display: none"
onclick="if (event.target === this) closeModal('modalReward');"
>
<div class="modal-sheet">
<div class="modal-handle"></div>
<div class="modal-title">🎁 兑换奖励</div>
<div class="form-group">
<label>类型</label
><select class="form-select">
<option>任务全勤奖励</option>
<option>时长管控之星</option>
</select>
</div>
<button
class="btn btn-success btn-block"
onclick="
closeModal('modalReward');
showToast('兑换申请已提交');
"
>
确认兑换
</button>
</div>
</div>
<!-- 模态:参数 -->
<div
class="modal-overlay"
id="modalParams"
style="display: none"
onclick="if (event.target === this) closeModal('modalParams');"
>
<div class="modal-sheet">
<div class="modal-handle"></div>
<div class="modal-title">⚙️ 执行参数 (附件五)</div>
<table style="width: 100%; font-size: var(--font-sm)">
<tr>
<th>参数</th>
<th>值</th>
</tr>
<tr>
<td>跳绳</td>
<td>1000个/日</td>
</tr>
<tr>
<td>背单词</td>
<td>20词/日</td>
</tr>
<tr>
<td>复习预习</td>
<td>各15分钟</td>
</tr>
<tr>
<td>利息倍数</td>
<td>1.5x</td>
</tr>
<tr>
<td>就寝时限</td>
<td>22:50(学期)</td>
</tr>
</table>
<button
class="btn btn-outline btn-block"
onclick="closeModal('modalParams')"
>
关闭
</button>
</div>
</div>
<!-- 模态:条款速查 -->
<div
class="modal-overlay"
id="modalClause"
style="display: none"
onclick="if (event.target === this) closeModal('modalClause');"
>
<div class="modal-sheet">
<div class="modal-handle"></div>
<div class="modal-title">📖 条款速查</div>
<input
class="form-input"
placeholder="搜索条款..."
oninput="filterClauses(this.value)"
/>
<div id="clauseList" style="max-height: 300px; overflow-y: auto">
<div class="list-item clause-item" data-kw="起床 超时">
<div><span class="clause-ref">第2.2条(一)</span> 起床超时</div>
</div>
<div class="list-item clause-item" data-kw="内务 不达标">
<div>
<span class="clause-ref">第2.2条(二)</span> 内务不达标
</div>
</div>
<div class="list-item clause-item" data-kw="作业 超时">
<div><span class="clause-ref">第2.2条(三)</span> 作业超时</div>
</div>
<div class="list-item clause-item" data-kw="如厕 超时">
<div><span class="clause-ref">第4.3条</span> 如厕超时</div>
</div>
<div class="list-item clause-item" data-kw="洗澡 超时">
<div><span class="clause-ref">第5.3条</span> 洗澡超时</div>
</div>
<div class="list-item clause-item" data-kw="手机 管制">
<div><span class="clause-ref">第6.1条</span> 夜间手机管制</div>
</div>
<div class="list-item clause-item" data-kw="就寝 违规">
<div><span class="clause-ref">第7.2条</span> 就寝违规</div>
</div>
<div class="list-item clause-item" data-kw="听歌 违规">
<div><span class="clause-ref">第13.2条</span> 听歌违规</div>
</div>
<div class="list-item clause-item" data-kw="查题 违规">
<div><span class="clause-ref">第14.2条</span> 查题违规</div>
</div>
<div class="list-item clause-item" data-kw="星标 早鸟">
<div><span class="clause-ref">第3.7条</span> 早鸟星标</div>
</div>
<div class="list-item clause-item" data-kw="情绪罚单">
<div><span class="clause-ref">第23条(五)</span> 情绪罚单</div>
</div>
</div>
<button
class="btn btn-outline btn-block"
onclick="closeModal('modalClause')"
>
关闭
</button>
</div>
</div>
<!-- 模态:自定义任务 -->
<div
class="modal-overlay"
id="modalCustomTasks"
style="display: none"
onclick="if (event.target === this) closeModal('modalCustomTasks');"
>
<div class="modal-sheet">
<div class="modal-handle"></div>
<div class="modal-title">📝 自定义B类任务</div>
<div class="list-item">
<div>🏃 晨跑3公里 (7:30前)</div>
<span class="tag tag-success">生效</span>
</div>
<button
class="btn btn-outline btn-block"
onclick="closeModal('modalCustomTasks')"
>
关闭
</button>
</div>
</div>
<div class="toast" id="toast" style="display: none"></div>
</div>
</div>
<script>
let weeklyCount = 2,
starCount = 2,
streakDays = 3,
pending = 1;
function switchTab(t) {
document
.querySelectorAll(".page")
.forEach((p) => p.classList.remove("active"));
document.getElementById("page-" + t).classList.add("active");
document
.querySelectorAll(".nav-item")
.forEach((n) => n.classList.remove("active"));
document
.querySelector(`.nav-item[data-tab="${t}"]`)
.classList.add("active");
}
function switchRecordTab(s) {
["violations", "debts", "rewards"].forEach((t) => {
document.getElementById("recordList-" + t).style.display = "none";
document.getElementById("tabBtn-" + t).className =
"btn btn-outline btn-sm";
});
document.getElementById("recordList-" + s).style.display = "block";
document.getElementById("tabBtn-" + s).className =
"btn btn-primary btn-sm";
}
function openModal(id) {
document.getElementById(id).style.display = "flex";
}
function closeModal(id) {
document.getElementById(id).style.display = "none";
}
function showToast(m) {
const t = document.getElementById("toast");
t.textContent = m;
t.style.display = "block";
t.style.animation = "none";
t.offsetHeight;
t.style.animation = "toastIn 0.3s ease, toastOut 0.3s ease 2s forwards";
setTimeout(() => (t.style.display = "none"), 2300);
}
function updateVioClause() {
const s = document.getElementById("vioType").value;
const map = {
起床超时: "第2.2条(一)",
内务不达标: "第2.2条(二)",
作业超时: "第2.2条(三)",
如厕超时: "第4.3条",
洗澡超时: "第5.3条",
手机管制违规: "第6.1条",
就寝违规: "第7.2条",
听歌违规: "第13.2条",
查题违规: "第14.2条",
B类未完成: "第3.4条",
};
document.getElementById("vioClause").value = map[s] || "";
}
function submitViolation() {
const remark = document.getElementById("vioRemark").value;
if (!remark.match(/屏幕时间|分钟/)) {
showToast("请填写屏幕时间");
return;
}
weeklyCount++;
document.getElementById("weeklyCount").textContent = weeklyCount;
if (document.getElementById("starMark").checked) {
starCount++;
document.getElementById("starCount").textContent = starCount;
}
pending++;
document.getElementById("pendingBadge").textContent = pending;
closeModal("modalViolation");
updateSanctions();
showToast("违规已记录");
}
function updateSanctions() {
const tag = document.getElementById("sanctionLevelTag"),
card = document.getElementById("sanctionDetailCard");
if (weeklyCount >= 3) {
tag.textContent = "🔴 三级制裁";
tag.className = "tag tag-level-3";
card.className = "sanction-card level-3";
card.querySelector(".sanction-title").textContent =
"🚨 三级制裁 (第9.3.3款)";
card.querySelector(".sanction-desc").innerHTML =
"• 一、二级全部内容<br>• 扣除发呆自由时间1小时";
document.getElementById("safetyValveAlert").style.display = "flex";
} else if (weeklyCount >= 2) {
tag.textContent = "⚠️ 二级制裁";
tag.className = "tag tag-level-2";
card.className = "sanction-card level-2";
card.querySelector(".sanction-title").textContent =
"📋 二级制裁 (第9.3.2款)";
card.querySelector(".sanction-desc").innerHTML =
"• 餐后清洁义务<br>• 次日禁带手机入卫生间";
document.getElementById("safetyValveAlert").style.display = "none";
} else {
tag.textContent = "💡 一级制裁";
tag.className = "tag tag-level-1";
card.className = "sanction-card level-1";
card.querySelector(".sanction-title").textContent =
"📝 一级制裁 (第9.3.1款)";
card.querySelector(".sanction-desc").innerHTML =
"• 餐后厨房与餐厅清洁义务(六项标准)";
document.getElementById("safetyValveAlert").style.display = "none";
}
}
function toggleConfirm(el) {
el.classList.toggle("checked");
pending += el.classList.contains("checked") ? -1 : 1;
document.getElementById("pendingBadge").textContent = pending;
}
function markDebtDone(b) {
b.textContent = "已完成";
b.className = "btn btn-sm btn-success";
b.disabled = true;
showToast("补做已验收");
}
function switchMode() {
const modes = ["semester", "weekend", "holiday"],
names = ["学期", "周末", "寒暑假"];
let cur = modes.indexOf(window.currentMode || "semester");
let next = (cur + 1) % 3;
window.currentMode = modes[next];
document.getElementById("modeBadge").textContent =
(next === 0 ? "📚" : next === 1 ? "🏖️" : "⛱️") +
" " +
names[next] +
"模式";
showToast("切换至" + names[next] + "模式");
}
function filterClauses(q) {
document.querySelectorAll(".clause-item").forEach((i) => {
i.style.display =
!q || i.dataset.kw.includes(q.toLowerCase()) ? "flex" : "none";
});
}
updateSanctions();
</script>
</body>
</html>