commit bc94c75b544676e57aec4c943628e9e30ac7bfc2
Author: Shuery-Shuai <2463253700@qq.com>
Date: Fri Oct 2 00:28:37 2026 +0800
first commit
diff --git a/backend/Dockerfile b/backend/Dockerfile
new file mode 100644
index 0000000..7764c16
--- /dev/null
+++ b/backend/Dockerfile
@@ -0,0 +1,6 @@
+FROM python:3.14-slim
+WORKDIR /app
+COPY requirements.txt .
+RUN pip install --no-cache-dir -r requirements.txt
+COPY ./app /app/app
+CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
\ No newline at end of file
diff --git a/backend/app/__init__.py b/backend/app/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/backend/app/auth.py b/backend/app/auth.py
new file mode 100644
index 0000000..d2288e9
--- /dev/null
+++ b/backend/app/auth.py
@@ -0,0 +1,25 @@
+from jose import JWTError, jwt
+from datetime import datetime, timedelta
+from fastapi.security import OAuth2PasswordBearer
+from fastapi import Depends, HTTPException
+
+SECRET_KEY = "your-secret-key-change-in-production" # 生产环境用环境变量
+ALGORITHM = "HS256"
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/token")
+
+def create_access_token(data: dict, expires_delta: timedelta):
+ to_encode = data.copy()
+ expire = datetime.utcnow() + expires_delta
+ to_encode.update({"exp": expire})
+ return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
+
+def get_current_user(token: str = Depends(oauth2_scheme)):
+ try:
+ payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
+ username: str = payload.get("sub")
+ role: str = payload.get("role")
+ if username is None:
+ raise HTTPException(401, "无效令牌")
+ return {"username": username, "role": role}
+ except JWTError:
+ raise HTTPException(401, "令牌验证失败")
\ No newline at end of file
diff --git a/backend/app/crud.py b/backend/app/crud.py
new file mode 100644
index 0000000..e69de29
diff --git a/backend/app/database.py b/backend/app/database.py
new file mode 100644
index 0000000..e69de29
diff --git a/backend/app/main.py b/backend/app/main.py
new file mode 100644
index 0000000..61cc50a
--- /dev/null
+++ b/backend/app/main.py
@@ -0,0 +1,35 @@
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+from .database import engine, Base
+from .routers import violations, debts, rewards, stats
+
+Base.metadata.create_all(bind=engine)
+
+app = FastAPI(title="自律公约执行系统")
+
+# CORS
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"], # 生产环境应限制域名
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+app.include_router(violations.router, prefix="/api/violations", tags=["violations"])
+app.include_router(debts.router, prefix="/api/debts", tags=["debts"])
+app.include_router(rewards.router, prefix="/api/rewards", tags=["rewards"])
+app.include_router(stats.router, prefix="/api/stats", tags=["stats"])
+
+@app.get("/")
+def root():
+ return {"status": "ok"}
+
+@app.on_event("startup")
+def init_users():
+ db = next(get_db())
+ if not db.query(User).first():
+ db.add_all([
+ User(username="parent", hashed_password=pwd_context.hash("family2026"), role="parent"),
+ User(username="child", hashed_password=pwd_context.hash("self2026"), role="child")
+ ])
+ db.commit()
\ No newline at end of file
diff --git a/backend/app/models.py b/backend/app/models.py
new file mode 100644
index 0000000..c86f921
--- /dev/null
+++ b/backend/app/models.py
@@ -0,0 +1,55 @@
+from passlib.context import CryptContext
+from sqlalchemy import Column, Integer, String, Date, Boolean, Float, DateTime
+from sqlalchemy.ext.declarative import declarative_base
+
+pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+Base = declarative_base()
+
+class Violation(Base):
+ __tablename__ = "violations"
+ id = Column(Integer, primary_key=True, index=True)
+ date = Column(Date, nullable=False)
+ type = Column(String, nullable=False)
+ clause = Column(String)
+ sanction_level = Column(Integer) # 1/2/3
+ executed = Column(Boolean, default=False)
+ confirmed = Column(Boolean, default=False)
+ remark = Column(String) # 含屏幕时间
+ star_mark = Column(Boolean, default=False)
+
+class Debt(Base):
+ __tablename__ = "debts"
+ id = Column(Integer, primary_key=True, index=True)
+ date = Column(Date)
+ task_name = Column(String)
+ owed_amount = Column(Float)
+ interest_rate = Column(Float, default=1.5)
+ due_amount = Column(Float)
+ completed = Column(Boolean, default=False)
+ completion_date = Column(Date)
+
+class Reward(Base):
+ __tablename__ = "rewards"
+ id = Column(Integer, primary_key=True, index=True)
+ type = Column(String) # 全勤/时长之星等
+ achieve_date = Column(Date)
+ content = Column(String)
+ exchange_date = Column(Date)
+ confirmed = Column(Boolean, default=False)
+
+class Parameter(Base):
+ __tablename__ = "parameters"
+ id = Column(Integer, primary_key=True, index=True)
+ name = Column(String, unique=True)
+ value = Column(String)
+ effective_date = Column(Date)
+
+class User(Base):
+ __tablename__ = "users"
+ id = Column(Integer, primary_key=True)
+ username = Column(String, unique=True, nullable=False)
+ hashed_password = Column(String, nullable=False)
+ role = Column(String, default="child") # parent / child
+
+ def verify_password(self, password: str) -> bool:
+ return pwd_context.verify(password, self.hashed_password)
\ No newline at end of file
diff --git a/backend/app/routers/auth.py b/backend/app/routers/auth.py
new file mode 100644
index 0000000..b665251
--- /dev/null
+++ b/backend/app/routers/auth.py
@@ -0,0 +1,19 @@
+from fastapi import APIRouter, Depends, HTTPException
+from fastapi.security import OAuth2PasswordRequestForm
+from sqlalchemy.orm import Session
+from ..database import get_db
+from .. import models, auth
+from datetime import timedelta
+
+router = APIRouter()
+
+@router.post("/token")
+def login(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
+ user = db.query(models.User).filter(models.User.username == form_data.username).first()
+ if not user or not user.verify_password(form_data.password):
+ raise HTTPException(401, "用户名或密码错误")
+ access_token = auth.create_access_token(
+ data={"sub": user.username, "role": user.role},
+ expires_delta=timedelta(hours=24)
+ )
+ return {"access_token": access_token, "token_type": "bearer"}
\ No newline at end of file
diff --git a/backend/app/routers/debts.py b/backend/app/routers/debts.py
new file mode 100644
index 0000000..e69de29
diff --git a/backend/app/routers/rewards.py b/backend/app/routers/rewards.py
new file mode 100644
index 0000000..e69de29
diff --git a/backend/app/routers/stats.py b/backend/app/routers/stats.py
new file mode 100644
index 0000000..e69de29
diff --git a/backend/app/routers/violations.py b/backend/app/routers/violations.py
new file mode 100644
index 0000000..ff503f4
--- /dev/null
+++ b/backend/app/routers/violations.py
@@ -0,0 +1,14 @@
+from fastapi import APIRouter, Depends
+from sqlalchemy.orm import Session
+from ..database import get_db
+from .. import crud, schemas
+
+router = APIRouter()
+
+@router.post("/", response_model=schemas.ViolationOut)
+def create_violation(violation: schemas.ViolationCreate, db: Session = Depends(get_db)):
+ return crud.create_violation(db, violation)
+
+@router.get("/", response_model=list[schemas.ViolationOut])
+def list_violations(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
+ return crud.get_violations(db, skip=skip, limit=limit)
\ No newline at end of file
diff --git a/backend/app/schemas.py b/backend/app/schemas.py
new file mode 100644
index 0000000..e69de29
diff --git a/backend/requirements.txt b/backend/requirements.txt
new file mode 100644
index 0000000..d5ba3f8
--- /dev/null
+++ b/backend/requirements.txt
@@ -0,0 +1,6 @@
+fastapi
+uvicorn[standard]
+sqlalchemy
+pydantic
+python-jose[cryptography]
+passlib[bcrypt]
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..8328198
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,21 @@
+services:
+ backend:
+ build: ./backend
+ container_name: convention-backend
+ ports:
+ - "44310:8000"
+ volumes:
+ - ./backend/data:/app/data # SQLite 文件持久化
+ environment:
+ - SECRET_KEY=your-long-random-string
+ - DATABASE_URL=sqlite:///./data/convention.db
+ restart: unless-stopped
+
+ frontend:
+ build: ./frontend
+ container_name: convention-frontend
+ ports:
+ - "4431:80"
+ depends_on:
+ - backend
+ restart: unless-stopped
\ No newline at end of file
diff --git a/docs/家庭共修公约.html b/docs/家庭共修公约.html
new file mode 100644
index 0000000..73f19e4
--- /dev/null
+++ b/docs/家庭共修公约.html
@@ -0,0 +1,1668 @@
+
+
+
+
+
+ 家庭共修公约(第一版)
+
+
+
+
+
+
+
家庭共修公约
+
(第一版)
+
+
+
+
+
+
+
+ 家庭共修公约(第一版)
+
+ 序言
+ 制定目的
+
+ 为规范本人日常行为,培养时间管理能力与自律意识,保障学业与身心健康协调发展,特制定本公约。
+
+
+ 适用范围
+
+ 适用于学期内周一至周日及各类假期(节假日、寒暑假另有特别规定见第八章)。
+
+
+ 效力等级
+
+ 本公约由本人自愿订立 ,经家长及监督人确认后生效,具有家庭内部约束力 。双方理解本公约不创设法律义务,但愿意以家庭荣誉和彼此信任共同遵守。
+
+
+ 解释权归属
+
+ 本公约条款如产生歧义,由订立人与监督人协商解释。协商不成的,以最有利于本人习惯养成的方向解释。双方在履约过程中对条款理解产生分歧的,应在《违规记录表》备注栏 中如实记录分歧要点,并于当周回顾时协商处理。若经协商认为有必要修改条款,则按第二十一条程序启动修订。
+
+
+ 第一章 定义与术语解释
+ 第一条 核心术语定义
+ 1.1 A 类任务(不可补任务)
+
+ 指具有严格时限要求 、过期即丧失履行意义的事项。学期内包括:
+
+
+ (一)按时起床(以闹钟设定时间为准);
+ (二)整理内务(叠被子、整理床铺、恢复书桌整洁);
+
+ (三)当天必须完成的学校作业(含书面作业与口头作业)。
+
+
+ 寒暑假期间的 A 类任务定义见第八章特别规定。
+
+ 1.2 B 类任务(可补任务)
+
+ 指具备弹性时限 、允许在一定周期内延期补做的事项。包括:
+
+
+
+ (一)跳绳(每日固定个数,具体见附件五《执行参数配置表》);
+
+
+ (二)背单词(每日固定数量,具体见附件五《执行参数配置表》);
+
+
+ (三)每日复习与预习(时长见附件五《执行参数配置表》);
+
+
+ (四)其他由本人列入计划的日常训练项目,详见附件六《自定义 B
+ 类任务清单》。
+
+
+
+ 1.3 C 类任务(记录义务)
+ 指不触发制裁 、但须如实记录的事项。包括:
+
+ (一)每日屏幕时间日志(对比第八条建议值);
+
+ (二)冷静期记录(与监督人发生冲突后,在《违规记录表》备注栏简要写下触发点和改进想法)。
+
+
+
+ C
+ 类任务仅用于自我觉察与复盘 ,不纳入违规计分。记录载体为附件一《违规记录表》备注栏,日常可通过网站
+ nurture.ppuc.lssa.fun 在线填写,线下表格仅作备份。
+
+
+ 1.4 违规
+
+ 指未按照本公约规定的时限、标准或流程 完成相应事项的行为。
+
+
+ 1.5 补做
+
+ 指对 B
+ 类任务在未完成当日后的次日 内予以补充完成的行为。
+
+
+ 1.6 利息倍数
+
+ 指补做时需在原任务量基础上增加的比例系数。当前数值见附件五《执行参数配置表》,后续可根据阶段变化由双方协商调整。
+
+
+ 1.7 监督人
+
+ 指本公约的执行监督方,即父母或兄长(或其中一方),职责包括:
+
+
+ (一)核实违规事实是否成立;
+ (二)记录违规次数与债务明细;
+ (三)确认惩罚措施的执行情况;
+ (四)发放奖励。
+
+
+ 1.8 连续周期
+
+ 指从某个日期起算,期间无任何违规行为 的累计天数。若发生违规,则连续周期中断,自次日起重新起算。
+
+
+ 1.9 学期与假期
+
+ “学期”指学校教学周,“假期”包括周末、法定节假日、寒暑假。不同时期的规则适用以第八章为准。
+
+
+ 1.10 任务分类逻辑
+
+ 本公约所涉行为,按内在属性分为四类,帮助理解公约设计逻辑。具体条款效力仍以
+ A/B/C 类区分及后续各章规定为准:
+
+
+
+ 生存自律类 :起床、就寝、整理内务、如厕/洗澡时长(第三章)、夜间手机管制(第四章)——关乎基本生理节律、睡眠健康与环境秩序。
+
+
+ 学业任务类 :作业、复习预习、复习计划、查题规则(第二章
+ A 类、B 类、第七章)——直接关联学习产出。
+
+
+ 自主训练类 :跳绳、背单词、课外阅读(第二章
+ B 类)——可补的成长性任务。
+
+
+ 过程规范类 :听歌规则、查题流程、屏幕时间日志、冷静期记录(第七章、C
+ 类)——任务进行中的行为边界与自我觉察。
+
+
+
+ 第二章 任务完成管理
+ 第二条 A 类任务(不可补任务)管理
+ 2.1 履行标准
+
+
+ (一)起床 :学期内闹钟响铃后,须在10 分钟内 完成坐起、双脚落地、离开床铺,方视为“按时起床”。假期标准见第八章。
+
+
+ (二)整理内务 :起床后5 分钟内 完成叠被子、整理床铺;学习结束后10 分钟内 将书桌恢复整洁(物品归位、无杂物堆积),方视为“内务达标”。
+
+
+ (三)作业 :须在当日 21:00 前 完成全部学校布置的书面作业及口头作业,方视为“按时完成作业”。若作业量较大,经监督人书面确认后可适当延后至
+ 22:00。
+
+
+ 2.2 违规认定
+ 有下列情形之一的,认定为违规:
+
+ (一)超过起床时限未离开床铺的;
+ (二)未在规定时间内完成内务整理,或整理未达标的;
+
+ (三)超过作业时限仍有未完成的学校作业的(因不可抗力如停电、断网等情形除外)。
+
+
+ 2.3 违规后果
+
+ 经认定违规的,立即执行“餐后厨房与餐厅清洁义务” ,标准如下:
+
+
+ (一)将餐桌上的餐具、厨余等清理干净;
+ (二)使用洗洁精清洗所有碗筷并归位;
+ (三)擦净灶台表面油渍及水渍;
+ (四)用抹布擦拭餐桌桌面及椅子表面;
+ (五)将厨房垃圾桶内垃圾清出并更换新垃圾袋;
+ (六)使用拖把拖净厨房及餐厅地面(含餐桌下方区域)。
+
+
+ 执行期限 :须在违规当日下一餐结束后 30 分钟内 启动清洁工作,且须经监督人检查确认合格。
+
+ 2.4 不可抗力例外
+
+ 因突发疾病、学校临时安排、家庭紧急事务等原因导致无法履行 A
+ 类任务的,经监督人书面确认后,不计为违规。
+
+
+ 第三条 B 类任务(可补任务)管理
+ 3.1 履行标准
+
+
+ (一)跳绳 :每日须完成跳绳,具体个数见附件五《执行参数配置表》;
+
+
+ (二)背单词 :每日须完成新单词记忆及复习,具体数量见附件五《执行参数配置表》;
+
+
+ (三)复习与预习 :每日须完成课后复习与次日预习,具体时长见附件五《执行参数配置表》;
+
+
+ (四)其他 B 类任务以附件六《自定义 B
+ 类任务清单》书面列明为准。
+
+
+ 3.2 完成时限
+
+ B 类任务须在当日 22:00 前 完成。假期弹性时限见第八章。
+
+ 3.3 “晚做”认定与处理
+
+ 若在当日 22:00
+ 前开始执行但比预定时间晚开始的,按以下方式处理:
+
+
+
+ (一)今日晚做 X 分钟 ,次日须提早 X 分钟 开始执行同一任务;
+
+
+ (二)若次日晚做累计超过15 分钟 ,则视为本条
+ 3.4 款的“完全未完成”情形。
+
+
+ 3.4 “完全未完成”认定与处理
+ 有下列情形之一的,认定为完全未完成:
+
+ (一)当日 22:00 前未启动该项任务的;
+
+ (二)中途停止后未在 15
+ 分钟内恢复,且最终未达到个数/时长标准的。
+
+
+
+ 处理方式 :次日须补做,补做量 = 原定量 × 利息倍数 (利息倍数见附件五)。
+
+ 3.5 追加后果(二次违约)
+
+ 若次日仍未按 3.4
+ 款完成补做的,触发本公约第五章(违规分级制裁)中的三级制裁 。
+
+ 3.6 正当理由例外
+
+ 因身体不适、学校考试等正当理由未完成的,经监督人确认后可不计入违规,但须在后续3 天内 择期补做。
+
+ 3.7 “早鸟”启动奖励
+
+ 若本人于当日20:00 前 主动启动所有 B
+ 类任务,并在 21:00
+ 前全部完成,可在当日的《违规记录表》上获得一个“星标” 。累计获得3 个“星标” ,可抵消一次一级制裁中的打扫义务(即豁免一次第二章的餐后清洁)。星标一经使用即清零。
+
+
+ 第三章 时长管控
+ 第四条 如厕(上厕所)时长管理
+ 4.1 时限标准
+ 单次如厕时长不得超过15 分钟 。
+ 4.2 计时方式
+
+ 设置手机闹钟为13 分钟 ,响铃时须立即结束并离开卫生间。
+
+ 4.3 违规认定
+
+ 超过 15 分钟未离开,或未在闹钟响铃后 3
+ 分钟内离开的,认定为超时违规。
+
+ 4.4 违规后果
+
+ 自离开卫生间起后 24 小时内 ,不得将手机、平板电脑等电子产品带入卫生间及浴室。监督人有权在本人进入前要求将手机放于客厅指定位置。
+
+ 4.5 累犯加重
+ 连续两日超时,禁带期限延长至48 小时 。
+
+ 第五条 洗澡(淋浴)时长管理
+ 5.1 时限标准
+ 单次淋浴时长不得超过20 分钟 。
+ 5.2 计时方式
+
+ 闹钟设定为18 分钟 ,响铃后须在 2
+ 分钟内关闭水流。
+
+ 5.3 违规认定
+ 超 20 分钟未关水认定为超时违规。
+ 5.4 违规后果
+
+ 同第四条禁带令。同日如厕和洗澡均超时,禁带期限以较晚结束者起算
+ 24 小时,不重复叠加。
+
+
+ 第四章 电子产品与睡眠管理
+ 第六条 夜间手机管制
+ 6.1 手机归宿
+
+ 每日22:30 (假期可延至
+ 23:00,见第八章),本人须将手机、平板等娱乐电子设备放置于客厅指定充电区域 ,不得以任何理由保留于卧室。
+
+ 6.2 清晨管控
+
+ 次日完成 A
+ 类任务“按时起床”前,不得取回手机 。起床后至上学前如需使用,须在餐厅或客厅等公共区域,不得带回卧室。
+
+ 6.3 违规后果
+
+ 违反上述任一款,次日全天执行“手机禁用令” ——手机仅保留通话与基础查资料功能,由监督人代为保管。
+
+
+ 第七条 按时就寝
+ 7.1 就寝时限
+
+ 每日22:50 前 须躺下准备入睡(熄灯或仅保留弱光阅读灯),不得再进行任何需使用电子设备的活动。
+
+ 7.2 违规认定
+
+ 超过 22:50
+ 仍未躺下,或在床上使用手机、平板等设备的,认定为就寝违规。
+
+ 7.3 违规后果
+
+ 就寝违规计入违规累计(每次计 1
+ 次),并触发次日“手机禁用令” (同 6.3 款)。
+
+
+ 第八条 日常屏幕时间建议与记录
+ 8.1 建议值
+
+ 每日除学习必要外的娱乐屏幕时间,建议不超过1 小时 (具体可参考附件五执行参数)。
+
+ 8.2 记录义务(C 类任务)
+
+ 本人须在每日 22:30
+ 前,在《违规记录表》备注栏 如实填写当日娱乐屏幕时间估算值。未填写的,不触发制裁,但视为未完成
+ C 类任务。
+
+
+ 第五章 违规分级与制裁
+ 第九条 违规累计与分级制度
+ 9.1 统计周期
+
+ 以一个自然周(周一至周日 )为统计周期,周日
+ 24:00 归零重计。
+
+ 9.2 违规计分规则
+
+
+ (一)A 类任务违规(起床/内务/作业):每次计 1 次 ;
+
+
+ (二)B 类任务当日完全未完成:每次计 1 次 ;
+
+ (三)如厕超时:每次计 1 次 ;
+ (四)洗澡超时:每次计 1 次 ;
+ (五)查题违规:每次计 1 次 ;
+ (六)听歌违规:每次计 1 次 ;
+
+ (七)夜间手机管制违规:每次计 1 次 ;
+
+ (八)就寝违规:每次计 1 次 。
+
+
+ C
+ 类任务(屏幕时间日志、冷静期记录)不计入违规 。
+
+ 9.3 违规分级制裁
+ 9.3.1 一级制裁
+ 触发条件 :单次违规(未达到累计条件)。
+
+ 制裁措施 :执行对应条款具体后果(如打扫、禁带令等)。
+
+ 9.3.2 二级制裁
+
+ 触发条件 :同一周期内累计达到2 次 违规。
+
+
+ 制裁措施 :一级制裁全部内容 +
+ 次日全天禁止手机带入卫生间。
+
+ 9.3.3 三级制裁
+
+ 触发条件 :累计达到3 次或以上 违规,或 B 类任务次日仍未补做。
+
+
+ 制裁措施 :执行一、二级全部内容 +
+ 扣除“发呆自由时间”1 小时 (若无则顺延至下周扣除)。
+
+ 9.4 周期清零“安全垫”
+
+ 每周日
+ 22:00,监督人与本人共同回顾。若本人当周达到三级制裁,但在周日晚间主动完成一次“厨房深度清洁” (标准高于第二章日常打扫,需经监督人验收),可将三级制裁降为二级执行。此条款为一次性修复机会,不可累积 。
+
+ 9.5 制裁执行方式
+
+ 监督人执行时须引用具体条款 ,本人须配合,不得拒绝。
+
+
+ 第六章 奖励机制
+ 第十条 任务全勤奖励
+ 10.1 达成条件
+
+ 一周内 A 类任务无违规 ,B
+ 类任务无完全未完成记录 ,且累计违规不超过 1 次 。
+
+ 10.2 奖励内容
+
+ 次周兑换一样指定美食(奶茶、蛋糕、炸鸡等),由监督人采购或制作。
+
+ 10.3 奖励不可累积
+ 当周奖励须在次周内 兑换,逾期作废。
+
+ 第十一条 时长管控之星成就
+ 11.1 达成条件
+ 连续7 日 无如厕/洗澡超时违规。
+ 11.2 奖励内容
+
+ 达成当周周末兑换1 小时“绝对自由发呆时间” 。
+
+
+
+ 可自由发呆、写日记、望窗外等,监督人不得以任何理由干扰、催促或提问。
+
+ 不得使用任何电子产品。
+ 可拆分为两个 30 分钟,同日用完。
+
+ 11.3 “免干扰”保障
+
+ 本人兑换发呆时间时,须提前告知监督人并放置“请勿打扰”标识。一旦开始计时,若非安全紧急情况,监督人任何一次打断都将导致本次发呆计时重置,从头开始 。确保奖励体验绝对完整。
+
+ 11.4 奖励不可累积
+ 仅当周周末有效,特殊情况下经同意可顺延一次。
+
+ 第十二条 奖励同时享有规则
+
+ 同时达成第十条与第十一条条件的,两项奖励可兼得 。
+
+
+ 第七章 学习过程管理
+ 第十三条 写作业期间听歌管理
+ 13.1 允许规则
+
+ 写作业期间允许使用手机播放音乐,但须同时满足 以下条件:
+
+
+
+ (一)手机屏幕朝下平放于桌面 ,全程不得翻起查看;
+
+
+ (二)仅佩戴单只耳机 ,另一只耳朵保持开放以接收外界声音(如监督人提醒);
+
+
+ (三)歌单须为当日首次开始写作业前已选定 ,播放过程中不得触碰手机进行切歌、选歌、查看歌词等操作。
+
+
+ 13.2 违规认定
+ 有下列情形之一的,认定为违反听歌规则:
+
+ (一)手机屏幕朝上或经常被翻起的;
+ (二)佩戴两只耳机的;
+ (三)写作业期间频繁操作手机切歌、换歌、查看通知的;
+
+ (四)监督人连续呼叫两次未应答的(因单耳未戴好除外)。
+
+
+ 13.3 违规后果
+
+ 经认定听歌违规的,当日剩余写作业时间不得再以任何形式使用手机播放音乐 ,仅可接受由监督人用外部音响播放纯音乐(曲目由监督人选定)。同时,该次违规计入违规累计。
+
+
+ 第十四条 查题管理
+ 14.1 允许规则
+
+ 写作业过程中遇疑难题目,允许使用手机查阅 AI
+ 或网络资料,但须同时满足 以下条件:
+
+
+
+ (一)攒题原则 :遇到不会的题目,须先在草稿纸上用圆圈标记题号并写下卡住的关键步骤,至少累积 2 道题目后 方可集中查题;
+
+
+ (二)站立原则 :查题时,本人须从椅子上站起 ,保持站立姿势操作手机(可站在书桌旁或阳台门口),全程不得坐回椅子;
+
+
+ (三)限时原则 :单次查题时长不得超过5 分钟 ;
+
+
+ (四)闭环原则 :查题完毕后,须在锁屏手机 后方可坐回椅子;锁屏后须将手机正面朝下扣放于桌面左上角 (伸手不可达的位置)。
+
+
+ 14.2 违规认定
+ 有下列情形之一的,认定为违反查题规则:
+
+ (一)未攒够 2 道题即单独查题的;
+ (二)查题时坐于椅子上的;
+ (三)单次查题超过 5 分钟的;
+ (四)查完后未锁屏即坐回椅子的;
+ (五)查完后未将手机扣放至指定位置的。
+
+ 14.3 违规后果
+ 经认定查题违规的:
+
+ (一)该次违规计入违规累计;
+
+ (二)若一周内累计3 次 查题违规,则剥夺自主查题权 3 天 :此期间内查题须将手机交给监督人,由监督人代为查询后口述答案,本人不得触碰手机。监督人代查后,应给予本人足够的消化时间,不得催促。
+
+
+ 14.4 查题收尾义务
+
+ 查完 AI
+ 并锁定答案后,本人须用红笔在草稿纸上将该题的关键解题步骤完整写出一遍 。未完成该步骤的,视为该题仍未“彻底完成”,不得计入作业完成清单。
+
+
+ 第八章 特别时期管理规定
+ 第十五条 不可抗力
+
+ 因突发疾病、学校临时活动、家庭紧急事务、断电断网等不可抗力导致违约,本人须在事发后1 小时内 口头告知,监督人确认后记为“豁免”。
+
+ 第十六条 考试周特别规定
+ 16.1 适用期间
+ 期中、期末、月考前三日至考试结束当日 。
+ 16.2 规则调整
+
+ A 类任务“作业”调整为“当日复习计划完成”;
+
+ B 类任务跳绳可替换为室内拉伸 10
+ 分钟,复习预习及背单词可暂停;
+
+ 如厕/洗澡限时、整理内务、按时就寝不变。
+
+ 16.3 奖励调整
+
+ 考试周内达成“时长管控之星”,奖励翻倍为2 小时发呆自由时间 。
+
+
+ 第十七条 周末与短假期弹性规定
+ 17.1 适用时间
+ 周六、周日及 3 天以内法定节假日。
+ 17.2 弹性内容
+
+
+ 起床时间自动延后1 小时 ,A
+ 类任务时限相应调整;
+
+ B 类任务完成时限延至当日 23:00 ;
+ 就寝时限顺延至23:50 ;
+ 其他规则不变。
+
+ 17.3 取消弹性
+ 若周末有补习或特殊安排,经双方同意可按学期日标准执行。
+
+ 第十八条 寒暑假特别规定
+ 18.1 适用期间
+ 以学校正式公布的寒暑假起止日期为准。
+ 18.2 起床时限
+
+ 每日起床闹钟可设定不晚于上午 9:00 ,须在 10
+ 分钟内完成起床。
+
+ 18.3 A 类任务重新定义
+ 寒暑假期间,A 类任务为:
+
+
+ “每日有效学习时长不少于 4 小时” (建议上午 2
+ 小时、下午 2 小时)。本人须在前一天 22:00
+ 前将次日学习计划书面提交监督人。未完成当日学习时长的,按 A
+ 类违规处理,执行餐后清洁义务;
+
+ 整理内务标准不变。
+
+ 18.4 B 类任务
+
+ 跳绳、背单词维持不变;
+ 复习预习暂停(由每日学习计划覆盖);
+
+ 新增“每日课外阅读 30 分钟” 作为 B
+ 类任务,同样适用补做与利息规则。
+
+
+ 18.5 手机归宿与就寝时间
+
+ 夜间手机管制时间顺延至23:00 ;
+ 就寝时限顺延至23:20 。
+
+ 18.6 寒暑假专属奖励
+
+ 连续21 天 全勤(无任何违规)可兑换一次“半日自由出行” (地点、交通须经家长同意并确保安全)。该奖励与周奖励不冲突。
+
+ 18.7 收心与放松期
+
+
+ 假期开始后前 2 天 为放松期,可豁免一次违规;
+
+
+ 开学前2 天 为收心期,恢复学期作息,起床时间逐日提前 1 小时过渡。
+
+
+
+ 第十九条 模式切换确认
+
+ 每次进入或退出考试周、小长假、寒暑假时,本人与监督人须在《违规记录表》备注栏 共同签字确认,明确当日启用哪套规则。
+
+
+ 第九章 生效、修改与签署
+ 第二十条 生效日期
+
+ 本公约自2026 年 8 月 6 日 起生效,有效期至2027 年 8 月 6 日 ,期满可续签或修订。
+
+ 第二十一条 修改程序
+
+ (一)双方协商一致后方可修改;
+ (二)修改须书面记录、签字 ;
+
+ (三)重大修改(涉及核心条款超过 3 条)须经3 天冷静期 。
+
+
+ 第二十二条 试行期
+
+ 生效后前 14 天 为试行期,期间本人可提出异议,经同意可临时调整。试行期结束后正式执行。
+
+
+ 第十章 附则
+ 第二十三条 监督人义务
+
+ (一)不得在条款外另行追加惩罚;
+ (二)执行制裁时须引用条款,不得情绪化斥责;
+
+ (三)每日 22:30 前完成《违规记录表》填写,鼓励通过网站
+ nurture.ppuc.lssa.fun 在线记录;
+
+ (四)奖励达成后24 小时内 兑现;
+
+ (五)情绪罚单 :若监督人出现大声呵斥、讽刺挖苦或翻旧账,本人有权指出“您已触发情绪罚单,请暂停
+ 10 分钟”,监督人应立即停止并离开。同一周内累计收到2 次 情绪罚单,本人自动获得额外30 分钟发呆自由时间 ,以示平衡。
+
+
+ 第二十四条 本人义务
+
+ (一)接受监督与记录,不得隐瞒;
+ (二)配合执行制裁,不得拖延回避;
+
+ (三)对违规认定有申诉权,可在认定后1 小时内 提出异议,监督人当场复核;
+
+
+ (四)如实填写 C
+ 类任务记录(屏幕时间日志、冷静期记录),日常可通过网站
+ nurture.ppuc.lssa.fun 填写。
+
+
+ 第二十五条 定期回顾与暂停机制
+
+ 若连续三周因公约执行导致本人出现明显焦虑、抵触情绪或亲子关系严重紧张,经本人或监督人提议,双方可协商暂停公约一周,进行无惩罚的弹性调整。暂停期间不记录违规,但
+ C 类任务(屏幕时间日志)仍建议如实记录,以供复盘中参考。
+
+
+ 暂停结束后,双方应就触发暂停的条款进行商议,决定是否启动修订程序。
+
+ 第二十六条 公约解释
+
+ 存在歧义或未规定情形,由双方协商解决,以有利于习惯养成的方向解释。
+
+ 第二十七条 电子化辅助执行
+
+ 本公约的执行可借助电子化工具辅助记录与提醒。家长已开发公约执行网站,网址为:https://nurture.ppuc.lssa.fun。网站将作为附件日常填写与记录的主要平台 ,包括但不限于违规记录、奖励兑换、债务补做、星标备忘录、执行参数配置及自定义任务清单等。电子化工具仅作为记录与提醒的辅助手段 ,不改变本公约条款的约束力与解释方式。若电子化工具中展示的规则与本文本不一致,以本文本为准 。线下签字版本仍为最终依据,双方可定期将网站记录导出打印并签字存档。
+
+
+ 签字区
+
+ 我们理解本公约不创设法律义务,但愿意以家庭荣誉和彼此信任共同遵守。
+
+
+
+ 角色
+ 签名
+ 日期
+
+
+ 订立人(本人)
+
+ 年 月 日
+
+
+ 监督人(父)
+
+ 年 月 日
+
+
+ 监督人(母)
+
+ 年 月 日
+
+
+ 监督人(兄)
+
+ 年 月 日
+
+
+ 注:签名区使用留白,实际签署时请手写填入。
+
+
+
+
+
+ 日期
+ 违规事项
+ 违规条款
+ 触发制裁
+ 执行情况(监督人确认)
+ 本人确认
+ 备注(含屏幕时间/冷静期/分歧记录等)
+
+
+ 月/ 日
+
+
+
+
+
+
+
+
+ 月/ 日
+
+
+
+
+
+
+
+
+ 附件一填写说明
+
+ 填写人 :由监督人主笔填写,本人核对并签字确认。双方对记录内容有分歧时,应在备注栏注明异议。日常记录优先使用网站
+ nurture.ppuc.lssa.fun,本表可每周打印归档。
+
+
+ 填写时机 :每日 22:30
+ 前完成当日记录。若当日无违规,可仅填日期并在“违规事项”列写“无”或留空,但须在备注栏填写屏幕时间估算值(C
+ 类任务)。
+
+ 各列说明 :
+
+
+ 列名
+ 填写内容
+ 示例
+
+
+ 日期
+ 记录发生违规的具体日期,格式为“月/日”
+ 8/6
+
+
+ 违规事项
+ 简要描述违规行为,如“起床超时”“跳绳未完成”“如厕超时”等
+ 作业超时未完成
+
+
+ 违规条款
+ 引用公约中对应的条款编号,便于后续核对
+ 第2.2条(三)
+
+
+ 触发制裁
+ 记录该次违规触发的具体制裁级别和内容
+ 一级制裁:餐后清洁
+
+
+ 执行情况
+ 监督人确认制裁是否在规定时间内完成,是否达标
+ 已完成,验收合格
+
+
+ 本人确认
+ 本人签字或打勾表示认可记录属实
+ ✓
+
+
+ 备注
+
+ 用于补充特殊信息,包括但不限于: ①当日娱乐屏幕时间估算值 (C
+ 类任务,每日必填); ②冷静期记录 (与监督人冲突后的简要复盘); ③模式切换确认 (进入/退出考试周、假期时双方签字); ④获得“星标”的记录; ⑤不可抗力豁免的简要说明; ⑥对公约条款理解产生分歧时,简要记录分歧要点
+
+
+ 屏幕时间约40分钟;对作业时限理解有分歧,已协商;寒假模式切换确认
+
+
+
+ C 类任务记录规范 :
+
+
+ 屏幕时间日志:填写当日除学习必要外的娱乐屏幕时间估算值(以分钟为单位),如约45分钟。允许粗略估计。
+
+
+ 冷静期记录:若当日与监督人发生冲突,在双方冷静后简要记录触发点和个人反思,如因手机问题争执,下次可提前说明使用时长。不强制格式,重在复盘而非追责。
+
+
+ 分歧记录规范 :
+
+ 当双方对条款理解产生争议时,在备注栏记录分歧内容及初步协商结果。若无法当场解决,应注明“待回顾协商”,并于当周回顾时处理。若回顾后认定需要修订条款,则依据第二十一条启动修改程序,并将最终修订情况记载于附件五《执行参数配置表》的“参数变更记录”区域或未来建立的正式修订记录中。
+
+
+
+
+
+
+ 奖励类型
+ 达成日期
+ 达成条件
+ 兑换内容
+ 兑换日期
+ 监督人确认
+
+
+ 任务全勤奖励
+ 月/ 日
+ 一周无违规
+
+ 月/ 日
+
+
+
+ 时长管控之星
+ 月/ 日
+ 连续 7 天不超时
+
+ 月/ 日
+
+
+
+ 寒暑假全勤奖
+ 月/ 日
+ 连续 21 天无违规
+
+ 月/ 日
+
+
+
+ 附件二填写说明
+
+ 填写人 :由本人提议兑换时,双方共同填写。监督人负责最终确认。日常记录可通过网站
+ nurture.ppuc.lssa.fun 完成,本表用于阶段性存档。
+
+
+ 填写时机 :奖励达成条件满足后 24
+ 小时内启动兑换流程,实际兑换完成后记录。
+
+ 各列说明 :
+
+
+ 列名
+ 填写内容
+ 示例
+
+
+ 奖励类型
+ 填写奖励名称,三选一
+ 任务全勤奖励
+
+
+ 达成日期
+ 填写条件达成的周日日期(全勤奖)或达成当日日期
+ 8/12
+
+
+ 达成条件
+ 简要勾选或描述达成条件
+ 一周无违规,A类全勤
+
+
+ 兑换内容
+ 本人指定的具体奖励内容
+ 珍珠奶茶大杯 或 周六下午发呆1小时
+
+
+ 兑换日期
+ 实际兑换的日期
+ 8/13
+
+
+ 监督人确认
+ 监督人签字或打勾,表示奖励已兑现
+ ✓
+
+
+ 兑换时效提醒 :
+
+
+ 任务全勤奖励须在达成次周内 兑换,逾期作废。
+
+
+ 时长管控之星须在达成当周周末 兑换,逾期作废。
+
+
+ 寒暑假全勤奖在达成后7 天内 协商出行时间,不受周限制。
+
+
+
+
+
+
附件3
+
附件三:B 类任务债务补做记录表
+
+
+
+ 日期
+ 亏欠任务
+ 亏欠个数/时长
+ 利息倍数
+ 应补个数/时长
+ 补做日期
+ 补做完成(监督人确认)
+
+
+ 月/ 日
+
+
+
+
+
+
+
+
+ 月/ 日
+
+
+
+
+
+
+
+
+ 附件三填写说明
+
+ 填写人 :由监督人填写,本人在“补做日期”完成后共同核对确认。日常可通过网站
+ nurture.ppuc.lssa.fun 记录债务,本表备用。
+
+
+ 填写时机 :发生 B
+ 类任务“完全未完成”时立即登记债务 ;补做完成后当日更新“补做完成”列。
+
+ 各列说明 :
+
+
+ 列名
+ 填写内容
+ 示例
+
+
+ 日期
+ 发生亏欠的原始日期
+ 8/7
+
+
+ 亏欠任务
+ 具体任务名称
+ 跳绳 或 复习与预习
+
+
+ 亏欠个数/时长
+ 原始应完成但未完成的量
+ 1000个 或 30分钟
+
+
+ 利息倍数
+ 填写附件五中当前生效的利息倍数
+ 1.5
+
+
+ 应补个数/时长
+ 亏欠量 × 利息倍数
+ 1500个 或 45分钟
+
+
+ 补做日期
+ 实际补做的日期,须为亏欠次日(含)以后
+ 8/8
+
+
+ 补做完成
+ 监督人验收后签字或打勾
+ ✓ 已完成1500个
+
+
+ 重要提醒 :
+
+
+ 补做须在亏欠次日 完成。若次日仍未完成,触发三级制裁(见第
+ 3.5 款)。
+
+
+ 因正当理由豁免的亏欠,不填入本表,但须在附件一备注栏注明豁免原因和补做计划(3
+ 天内择期补做,不加利息)。
+
+ 寒暑假期间,“课外阅读”亦按此规则补做。
+
+
+
+
+
+
+ 日期
+ 事项(获星标/豁免/情绪罚单等)
+ 本人确认
+ 监督人确认
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 附件四填写说明
+
+ 填写人 :由本人或监督人提议记录时填写,双方签字确认。优先使用网站
+ nurture.ppuc.lssa.fun 记录,本表备份。
+
+
+ 填写时机 :事件发生当日 记录,确保信息准确。
+
+ 各列说明 :
+
+
+ 列名
+ 填写内容
+ 示例
+
+
+ 日期
+ 事件发生日期
+ 8/8
+
+
+ 事项
+ 简要描述特殊事件类型和内容
+
+ 获星标:今日20点前完成全部B类任务 或 豁免:因感冒免跳绳一天 或
+ 情绪罚单:妈妈本周第2次罚单,我获得30分钟发呆时间
+
+
+
+ 本人确认
+ 本人签字或打勾
+ ✓
+
+
+ 监督人确认
+ 监督人签字或打勾
+ ✓
+
+
+ 星标使用流程 :
+
+
+ 累计获得3 个“星标” 后,本人可在任意一次触发一级制裁(餐后清洁)时提出使用。
+
+
+ 使用时在备注栏写明使用3个星标,豁免本次餐后清洁义务。
+
+
+ 使用后立即在对应星标记录旁标注“已使用”,星标清零,重新计算。
+
+
+
+
+
+
+ 本附件集中记录公约中所有可随阶段调整的数值参数 ,便于双方随时查阅与协商更新。参数一经双方签字确认,即作为公约的有机组成部分,与正文条款具有同等约束力 。参数调整可通过网站
+ nurture.ppuc.lssa.fun 发起并记录,但须线下签字确认。
+
+ 当前生效参数(生效日期:2026 年 8 月 6 日)
+
+
+ 参数名称
+ 适用条款
+ 当前设定值
+ 备注
+
+
+ 跳绳每日个数
+ 第 3.1 条
+ 1000 个
+ 可分多组完成
+
+
+ 背单词每日新词数
+ 第 3.1 条
+ 20 个
+ 须复习前一日单词
+
+
+ 复习与预习每日时长
+ 第 3.1 条
+ 复习 15 分钟 + 预习 15 分钟(合计 30 分钟)
+ 可根据学业阶段调整
+
+
+ 利息倍数
+ 第 1.6 条、第 3.4 条
+ 1.5 倍
+ 补做量 = 原定量 × 此倍数
+
+
+ 娱乐屏幕时间建议上限
+ 第 8.1 条
+ 1 小时/日
+ 非强制性建议
+
+
+ 如厕时限
+ 第 4.1 条
+ 15 分钟
+
+
+
+ 洗澡时限
+ 第 5.1 条
+ 20 分钟
+
+
+
+ 起床时限(学期)
+ 第 2.1 条
+ 10 分钟
+ 学期内标准
+
+
+ 作业完成时限
+ 第 2.1 条
+ 21:00(可延至 22:00)
+
+
+
+ 就寝时限(学期)
+ 第 7.1 条
+ 22:50
+ 学期内标准
+
+
+ 寒暑假每日学习时长
+ 第 18.3 条
+ 4 小时
+
+
+
+ 寒暑假课外阅读时长
+ 第 18.4 条
+ 30 分钟
+
+
+
+ 参数变更记录
+
+ 以下区域用于记录参数的调整历史。每次调整需经双方签字,并注明生效日期。此记录可视为公约的修订补充页 。
+
+
+
+ 变更日期
+ 变更参数
+ 原值 → 新值
+ 变更理由
+ 订立人签字
+ 监督人签字
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 当参数变更达到一定次数或涉及多项时,双方可决定将其汇总,并视情况更新正文条款。
+
+
+
+
+
附件6
+
附件六:自定义 B 类任务清单
+
+
+ 本附件用于记录本人根据 1.2
+ 条(四)款自行列入计划的日常训练项目 。所列任务视为 B
+ 类任务,适用补做、利息及违规计分规则。任务若有完成标准、时长或个数要求,应在此明确写明 。可通过网站
+ nurture.ppuc.lssa.fun 填写并同步更新。
+
+
+
+ 序号
+ 任务名称
+ 完成标准(个数/时长/内容)
+ 每日时限
+ 生效日期
+ 本人签字
+ 监督人签字
+ 备注
+
+
+ 1
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+
+
+
+
+
+ 附件六填写说明
+
+
+ 任务名称 :简洁描述任务,如“平板支撑”“练习吉他”等。
+
+
+ 完成标准 :尽量量化,如“3 组,每组 1
+ 分钟”“练习 20 分钟”等。
+
+
+ 每日时限 :若与通用时限(22:00)一致,可填“同
+ 3.2 条”;若有特殊时段要求,在此注明。
+
+
+ 生效日期 :任务开始实施的日期。若需暂停或删除,在对应行备注栏注明“已于
+ X 月 X 日暂停/删除”,并由双方签字确认。
+
+
+
+
+ 本公约共十章二十七条,附件六份。它是一份活的契约,服务于成长,而非单纯的束缚。愿秩序之下,自由生长。
+
+
+
diff --git a/docs/网站原型.html b/docs/网站原型.html
new file mode 100644
index 0000000..03262a5
--- /dev/null
+++ b/docs/网站原型.html
@@ -0,0 +1,1342 @@
+
+
+
+
+
+ 自律公约 · 辅助执行
+
+
+
+
+
+
+ 9:41 📶 🔋 85%
+
+
+
+
+
+ 📚 学期模式
+ 📅 2026年8月5日 周三
+
+
+
本周累计违规
+
2
+
+ ⚠️ 二级制裁
+
+
+ 连续无违规
+ 3
+ 天
+
+
+
+
📋 二级制裁内容 (第9.3.2款)
+
+ • 执行一级制裁(餐后厨房与餐厅清洁义务)
+ • 次日全天禁止手机带入卫生间
+ • 需在22:00前完成全部B类任务
+
+
+
+ 💡
+ 已达三级制裁,周日晚间完成厨房深度清洁 可降为二级
+ (第9.4款)
+
+
+
+
⭐
+
+ 2
+
+
+ 星标 (3个抵1次)
+
+
+
+
🎁
+
+ 1
+
+
+ 待兑换奖励
+
+
+
+
+
+ 📝 记录违规
+
+
+ 📋 登记债务
+
+
+ 🎁 兑换奖励
+
+
+
+
+
+
🛏️
+
+
起床超时
+
+ 8/5 · 第2.2条(一) · 一级制裁
+
+
+
待确认
+
+
+
📱
+
+
手机管制违规
+
+ 8/4 · 第6.1条 · 二级制裁
+
+
+
已确认
+
+
+
+
+
+
+
+ 违规记录
+
+
+ 债务列表
+
+
+ 奖励记录
+
+
+
+
+
🛏️
+
+
+ 起床超时 第2.2条(一)
+
+
+ 8/5 07:25 · 超时15分钟 · 一级制裁
+
+
+ 📝 屏幕时间: 45分钟 | 状态:
+ 待确认
+
+
+
✓
+
+
+
📱
+
+
+ 手机未归位 第6.1条
+
+
+ 8/4 22:35 · 二级制裁 · 已执行禁带令
+
+
+ 📝 屏幕时间: 60分钟 | 状态:
+ 已确认
+
+
+
✓
+
+
+
+
+
📖
+
+
背单词未完成
+
+ 亏欠:20词 · 利息1.5x · 应补:30词
+
+
+ 截止:8/6 · 待补做
+
+
+
+ 标记完成
+
+
+
+
+
+
🏆
+
+
任务全勤奖励
+
达成:8/3 · 兑换截止:8/10
+
+
待兑换
+
+
+
+ + 新增违规记录
+
+
+
+
+
📊 本周统计 (8/4 - 8/10)
+
+
+
+ ⚡ 制裁等级变化
+
+
周一 一级 →
+
周三 二级
+
+
+
📅 本月汇总
+
+
总违规:3次
+
获得星标:4个
+
兑换奖励:2次
+
最长连续:5天
+
+
+
+
+
+
+
+
+ ⚖️ 本网站仅辅助执行,以线下签字版本为最终依据。
+
+
+
+
+
+ 🏠 首页
+
+
+ 📋 记录1
+
+
+ 📊 统计
+
+
+ 👤 我的
+
+
+
+
+
+
+
📝 记录违规
+
+ 日期
+
+
+ 违规类型
+
+ 请选择
+ 起床超时 (第2.2条一)
+ 内务不达标 (第2.2条二)
+ 作业超时 (第2.2条三)
+ 如厕超时 (第4.3条)
+ 洗澡超时 (第5.3条)
+ 手机未归位 (第6.1条)
+ 就寝违规 (第7.2条)
+ 听歌违规 (第13.2条)
+ 查题违规 (第14.2条)
+ B类未完成 (第3.4条)
+
+
+
+ 引用条款
+
+
+
+ 备注
+ *屏幕时间必填
+
+
+ ⭐
+ 标记“早鸟”获星标
+
+
+ 提交
+
+
+ 取消
+
+
+
+
+
+
+
+
📋 登记B类债务
+
+ 任务
+ 背单词
+ 跳绳
+ 复习预习
+
+
+
+ 亏欠量
+
+
+ 利息倍数
+
+
+ 登记
+
+
+
+
+
+
+
+
🎁 兑换奖励
+
+ 类型
+ 任务全勤奖励
+ 时长管控之星
+
+
+
+ 确认兑换
+
+
+
+
+
+
+
+
⚙️ 执行参数 (附件五)
+
+
+ 参数
+ 值
+
+
+ 跳绳
+ 1000个/日
+
+
+ 背单词
+ 20词/日
+
+
+ 复习预习
+ 各15分钟
+
+
+ 利息倍数
+ 1.5x
+
+
+ 就寝时限
+ 22:50(学期)
+
+
+
+ 关闭
+
+
+
+
+
+
+
+
📖 条款速查
+
+
+
+ 关闭
+
+
+
+
+
+
+
+
📝 自定义B类任务
+
+
+ 关闭
+
+
+
+
+
+
+
+
+
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
new file mode 100644
index 0000000..e331469
--- /dev/null
+++ b/frontend/Dockerfile
@@ -0,0 +1,11 @@
+FROM node:24-alpine as builder
+WORKDIR /app
+COPY package*.json ./
+RUN npm install
+COPY . .
+RUN npm run build
+
+FROM nginx:alpine
+COPY --from=builder /app/dist /usr/share/nginx/html
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+EXPOSE 80
diff --git a/frontend/build.log b/frontend/build.log
new file mode 100644
index 0000000..0032042
--- /dev/null
+++ b/frontend/build.log
@@ -0,0 +1,66 @@
+#0 building with "desktop-linux" instance using docker driver
+
+#1 [internal] load build definition from Dockerfile
+#1 transferring dockerfile: 329B done
+#1 WARN: FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 1)
+#1 DONE 0.0s
+
+#2 [internal] load metadata for docker.io/library/nginx:alpine
+#2 DONE 0.4s
+
+#3 [internal] load metadata for docker.io/library/node:24-alpine
+#3 DONE 0.4s
+
+#4 [internal] load .dockerignore
+#4 transferring context: 2B done
+#4 DONE 0.0s
+
+#5 [internal] load build context
+#5 transferring context: 1.03kB done
+#5 DONE 0.0s
+
+#6 [stage-1 1/3] FROM docker.io/library/nginx:alpine@sha256:4a73073bd557c65b759505da037898b61f1be6cbcc3c2c3aeac22d2a470c1752
+#6 resolve docker.io/library/nginx:alpine@sha256:4a73073bd557c65b759505da037898b61f1be6cbcc3c2c3aeac22d2a470c1752 0.1s done
+#6 DONE 0.1s
+
+#7 [builder 1/6] FROM docker.io/library/node:24-alpine@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43
+#7 resolve docker.io/library/node:24-alpine@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43
+#7 resolve docker.io/library/node:24-alpine@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43 0.1s done
+#7 DONE 0.1s
+
+#6 [stage-1 1/3] FROM docker.io/library/nginx:alpine@sha256:4a73073bd557c65b759505da037898b61f1be6cbcc3c2c3aeac22d2a470c1752
+#6 CACHED
+
+#8 [builder 2/6] WORKDIR /app
+#8 CACHED
+
+#9 [builder 3/6] COPY package*.json ./
+#9 DONE 0.1s
+
+#10 [builder 4/6] RUN npm install --registry=https://registry.npmmirror.com
+#10 0.488 npm error code EJSONPARSE
+#10 0.489 npm error JSON.parse Invalid package.json: JSONParseError: Unexpected end of JSON input while parsing empty string
+#10 0.489 npm error JSON.parse Failed to parse JSON data.
+#10 0.489 npm error JSON.parse Note: package.json must be actual JSON, not just JavaScript.
+#10 0.490 npm error A complete log of this run can be found in: /root/.npm/_logs/2026-08-05T11_12_45_107Z-debug-0.log
+#10 ERROR: process "/bin/sh -c npm install --registry=https://registry.npmmirror.com" did not complete successfully: exit code: 1
+------
+ > [builder 4/6] RUN npm install --registry=https://registry.npmmirror.com:
+0.488 npm error code EJSONPARSE
+0.489 npm error JSON.parse Invalid package.json: JSONParseError: Unexpected end of JSON input while parsing empty string
+0.489 npm error JSON.parse Failed to parse JSON data.
+0.489 npm error JSON.parse Note: package.json must be actual JSON, not just JavaScript.
+0.490 npm error A complete log of this run can be found in: /root/.npm/_logs/2026-08-05T11_12_45_107Z-debug-0.log
+------
+
+ 1 warning found (use docker --debug to expand):
+ - FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 1)
+Dockerfile:4
+--------------------
+ 2 | WORKDIR /app
+ 3 | COPY package*.json ./
+ 4 | >>> RUN npm install --registry=https://registry.npmmirror.com
+ 5 | COPY . .
+ 6 | RUN npm run build
+--------------------
+ERROR: failed to build: failed to solve: process "/bin/sh -c npm install --registry=https://registry.npmmirror.com" did not complete successfully: exit code: 1
diff --git a/frontend/nginx.conf b/frontend/nginx.conf
new file mode 100644
index 0000000..3be4330
--- /dev/null
+++ b/frontend/nginx.conf
@@ -0,0 +1,12 @@
+server {
+ listen 80;
+ location / {
+ root /usr/share/nginx/html;
+ index index.html;
+ try_files $uri $uri/ /index.html;
+ }
+ location /api/ {
+ proxy_pass http://backend:8000;
+ proxy_set_header Host $host;
+ }
+}
\ No newline at end of file
diff --git a/frontend/package.json b/frontend/package.json
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js
new file mode 100644
index 0000000..ee37062
--- /dev/null
+++ b/frontend/src/api/index.js
@@ -0,0 +1,26 @@
+import axios from 'axios'
+
+const apiClient = axios.create({
+ baseURL: import.meta.env.VITE_API_BASE || '/api',
+})
+
+apiClient.interceptors.request.use(config => {
+ const token = localStorage.getItem('token')
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`
+ }
+ return config
+})
+
+apiClient.interceptors.response.use(
+ response => response,
+ error => {
+ if (error.response?.status === 401) {
+ localStorage.removeItem('token')
+ window.location.href = '/login'
+ }
+ return Promise.reject(error)
+ }
+)
+
+export default apiClient
\ No newline at end of file
diff --git a/frontend/src/components/ViolationModal.vue b/frontend/src/components/ViolationModal.vue
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js
new file mode 100644
index 0000000..6083060
--- /dev/null
+++ b/frontend/src/router/index.js
@@ -0,0 +1,31 @@
+import { createRouter, createWebHashHistory } from 'vue-router'
+import Home from '../views/Home.vue'
+import Records from '../views/Records.vue'
+import Stats from '../views/Stats.vue'
+import Mine from '../views/Mine.vue'
+
+const routes = [
+ { path: '/login', component: Login, meta: { public: true } },
+ { path: '/', component: Home, meta: { title: '首页' } },
+ { path: '/records', component: Records, meta: { title: '记录' } },
+ { path: '/stats', component: Stats, meta: { title: '统计' } },
+ { path: '/mine', component: Mine, meta: { title: '我的' } },
+]
+
+const router = createRouter({
+ history: createWebHashHistory(),
+ routes,
+})
+
+router.beforeEach((to, from, next) => {
+ const isPublic = to.matched.some(r => r.meta.public)
+ const token = localStorage.getItem('token')
+ if (!isPublic && !token) {
+ next('/login')
+ } else {
+ next()
+ }
+})
+
+export default router
+export default router
\ No newline at end of file
diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue
new file mode 100644
index 0000000..e71d496
--- /dev/null
+++ b/frontend/src/views/Home.vue
@@ -0,0 +1,35 @@
+
+
+
+ 本周违规:{{ weeklyCount }}
+ {{ sanctionLabel }}
+
+
+
+
+ {{ action.icon }}
+ {{ action.text }}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue
new file mode 100644
index 0000000..f36bfa2
--- /dev/null
+++ b/frontend/src/views/Login.vue
@@ -0,0 +1,44 @@
+
+
+
自律公约系统
+
+
+
+
+
+
+
+ 登录
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/views/Mine.vue b/frontend/src/views/Mine.vue
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/views/Records.vue b/frontend/src/views/Records.vue
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/views/Stats.vue b/frontend/src/views/Stats.vue
new file mode 100644
index 0000000..e69de29
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 0000000..f29e1df
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,67 @@
+# ------------------------------------------------------------
+# 自律公约网站反代配置
+# 域名:nurture.ppuc.lssa.fun
+# ------------------------------------------------------------
+
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+ http2 on;
+
+ server_name nurture.ppuc.lssa.fun;
+
+ # SSL 证书
+ ssl_certificate /etc/ssl/acme/ppuc.lssa.fun.fullchain.crt;
+ ssl_certificate_key /etc/ssl/acme/ppuc.lssa.fun.key;
+
+ # TLS 安全配置
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
+ ssl_prefer_server_ciphers on;
+ ssl_session_timeout 10m;
+
+ # 基础安全头
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-XSS-Protection "1; mode=block" always;
+
+ # --------------------------------------------------------
+ # 1. 前端页面(Vue SPA)
+ # --------------------------------------------------------
+ location / {
+ proxy_pass http://127.0.0.1:4431/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # WebSocket 支持(Vite HMR 或未来实时功能预留)
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+
+ # --------------------------------------------------------
+ # 2. 后端 API
+ # --------------------------------------------------------
+ location /api/ {
+ proxy_pass http://127.0.0.1:44310/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # 传递用户真实 IP 给后端(如需日志)
+ proxy_set_header X-Forwarded-Host $host;
+ }
+}
+
+# ------------------------------------------------------------
+# HTTP → HTTPS 重定向
+# ------------------------------------------------------------
+server {
+ listen 80;
+ listen [::]:80;
+ server_name nurture.ppuc.lssa.fun;
+ return 301 https://$host$request_uri;
+}
\ No newline at end of file