1343 lines
44 KiB
HTML
1343 lines
44 KiB
HTML
<!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>
|