commit
f1773af0ce
@ -0,0 +1,3 @@
|
|||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/pick_student/pick_student.sqlite3" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,12 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredIdentifiers">
|
||||||
|
<list>
|
||||||
|
<option value="test.fre.*" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="rp" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/pick_student.iml" filepath="$PROJECT_DIR$/.idea/pick_student.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,65 @@
|
|||||||
|
# locustfile.py
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
from locust import HttpUser, TaskSet, task, between
|
||||||
|
|
||||||
|
class UserBehavior(TaskSet):
|
||||||
|
|
||||||
|
def on_start(self):
|
||||||
|
"""在测试开始时执行,进行登录并存储 token 和 username"""
|
||||||
|
# 假设有一个登录端点,可以获取 token 和 username
|
||||||
|
response = self.client.post("http://127.0.0.1:8000/teacher/jwt/token", data={
|
||||||
|
"username": "starhun",
|
||||||
|
"password": "123456"
|
||||||
|
})
|
||||||
|
if response.status_code == 200:
|
||||||
|
self.token = response.json()["access_token"]
|
||||||
|
self.username = response.json().get("username")
|
||||||
|
else:
|
||||||
|
self.token = None
|
||||||
|
self.username = None
|
||||||
|
|
||||||
|
@task(1)
|
||||||
|
def get_classes(self):
|
||||||
|
"""测试获取班级列表"""
|
||||||
|
if self.token:
|
||||||
|
with self.client.get("http://127.0.0.1:8000/teacher/pick_student/getclasses", headers={"Authorization": f"Bearer {self.token}"}) as response:
|
||||||
|
if response.status_code != 200:
|
||||||
|
response.failure(f"Failed to get classes: {response.text}")
|
||||||
|
|
||||||
|
@task(2)
|
||||||
|
def create_class(self):
|
||||||
|
characters = string.ascii_letters + string.digits
|
||||||
|
# 使用 random.choice() 从字符集合中随机选择字符
|
||||||
|
random_string = ''.join(random.choice(characters) for i in range(10))
|
||||||
|
"""测试创建班级"""
|
||||||
|
if self.token:
|
||||||
|
with self.client.post("http://127.0.0.1:8000/teacher/pick_student/create_class", params={"class_name": random_string,"class_time":"周三 5-6节"}, headers={"Authorization": f"Bearer {self.token}"}, catch_response=True) as response:
|
||||||
|
if response.status_code != 200:
|
||||||
|
response.failure(f"Failed to create class: {response.text}")
|
||||||
|
|
||||||
|
@task(3)
|
||||||
|
def random_pick(self):
|
||||||
|
"""测试随机点名"""
|
||||||
|
if self.token:
|
||||||
|
params = {"class_name": "aa"}
|
||||||
|
with self.client.get("http://127.0.0.1:8000/teacher/pick_student/random_pick/", params=params, headers={"Authorization": f"Bearer {self.token}"}, catch_response=True) as response:
|
||||||
|
if response.status_code != 200:
|
||||||
|
response.failure(f"Failed to random pick: {response.text}")
|
||||||
|
|
||||||
|
@task(4)
|
||||||
|
def upload_students(self):
|
||||||
|
"""测试上传学生信息"""
|
||||||
|
if self.token:
|
||||||
|
files = {
|
||||||
|
"file": ("stduents.xlsx", open("D:\python\code\pick_student\stduents.xlsx", "rb"), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||||
|
}
|
||||||
|
data = {"class_name": "班级1"}
|
||||||
|
with self.client.post("http://127.0.0.1:8000/teacher/pick_student/upload_students/", files=files, params=data, headers={"Authorization": f"Bearer {self.token}"}, catch_response=True) as response:
|
||||||
|
if response.status_code != 200:
|
||||||
|
response.failure(f"Failed to upload students: {response.text}")
|
||||||
|
|
||||||
|
class WebsiteUser(HttpUser):
|
||||||
|
tasks = [UserBehavior]
|
||||||
|
wait_time = between(1, 5)
|
@ -0,0 +1,2 @@
|
|||||||
|
from .main import app1
|
||||||
|
from .login import app2
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,68 @@
|
|||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from pick_student import models,schemas
|
||||||
|
from pick_student.schemas import read_class
|
||||||
|
|
||||||
|
|
||||||
|
def get_student_by_id(db: Session, stduent_id: int,class_name: str,user_name: str):
|
||||||
|
return db.query(models.Student).filter(models.Student.student_id == stduent_id).filter(models.Student.class_name == class_name).filter(models.Student.user_name == user_name).first()
|
||||||
|
|
||||||
|
def get_student_by_name(db: Session, name: str):
|
||||||
|
return db.query(models.Student).filter(models.Student.name == name).first()
|
||||||
|
def get_students(db: Session, class_name: str, user_name: str,skip: int = 0, limit: int = 100):
|
||||||
|
return db.query(models.Student).filter(models.Student.class_name == class_name).filter(models.Student.user_name == user_name).offset(skip).limit(limit).all()
|
||||||
|
|
||||||
|
def create_student(db: Session, student: schemas.create_student):
|
||||||
|
db_student = models.Student(**student.dict())
|
||||||
|
db.add(db_student)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_student)
|
||||||
|
return db_student
|
||||||
|
def update_student(db: Session, student: schemas.update_student,class_name: str, user_name: str):
|
||||||
|
db_student = get_student_by_id(db, student.student_id, class_name,user_name)
|
||||||
|
db_student.scores = student.scores
|
||||||
|
db_student.consecutive_calls=student.consecutive_calls
|
||||||
|
db_student.is_master=student.is_master
|
||||||
|
db_student.updated_at=student.updated_at
|
||||||
|
db_student.master_uses=student.master_uses
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_student)
|
||||||
|
return db_student
|
||||||
|
|
||||||
|
|
||||||
|
def delete_all_students(db: Session,class_name: str,user_name: str):
|
||||||
|
db.query(models.Student).filter(models.Student.class_name == class_name).filter(models.Student.user_name == user_name).delete()
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
def get_class_by_id(db: Session, class_id: int):
|
||||||
|
return db.query(models.Class).filter(models.Class.id == class_id).first()
|
||||||
|
def create_class(db: Session,cclass: schemas.create_class):
|
||||||
|
db_class = models.Class(**cclass.dict())
|
||||||
|
db.add(db_class)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_class)
|
||||||
|
return db_class
|
||||||
|
|
||||||
|
def get_class_by_name(db: Session, class_name: str,user_name: str):
|
||||||
|
return db.query(models.Class).filter(models.Class.class_name == class_name).filter(models.Class.user_name == user_name).first()
|
||||||
|
|
||||||
|
def get_user(db: Session, username:str):
|
||||||
|
user= db.query(models.Teacher).filter(models.Teacher.user_name == username).first()
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def create_teacher(db: Session, user_name: str, password_hash: str):
|
||||||
|
db_teacher = models.Teacher(user_name=user_name, password_hash=password_hash)
|
||||||
|
db.add(db_teacher)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_teacher)
|
||||||
|
return db_teacher
|
||||||
|
def get_classes_by_teacher_username(db: Session, user_name: str):
|
||||||
|
db_classes = db.query(models.Class).filter(models.Class.user_name == user_name).all()
|
||||||
|
return db_classes
|
||||||
|
def delete_class(db: Session, class_name: str, user_name: str):
|
||||||
|
|
||||||
|
db.query(models.Student).filter(models.Student.class_name == class_name).filter(models.Student.user_name == user_name).delete()
|
||||||
|
db.commit()
|
||||||
|
deleted_rows=db.query(models.Class).filter(models.Class.class_name == class_name).filter(models.Class.user_name == user_name).delete()
|
||||||
|
print(f"Deleted rows: {deleted_rows}")
|
||||||
|
db.commit()
|
@ -0,0 +1,18 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import sessionmaker,declarative_base
|
||||||
|
SQLALCHEMY_DATABASE_URL = 'sqlite:///./pick_student.sqlite3'
|
||||||
|
engine = create_engine(
|
||||||
|
SQLALCHEMY_DATABASE_URL, echo=True,connect_args={'check_same_thread': False}
|
||||||
|
)
|
||||||
|
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine,expire_on_commit=True)
|
||||||
|
Base = declarative_base()
|
||||||
|
def get_db():
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
except Exception as e:
|
||||||
|
db.close()
|
||||||
|
raise e # 重新抛出异常
|
||||||
|
finally:
|
||||||
|
db.close()
|
@ -0,0 +1,94 @@
|
|||||||
|
import secrets
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer
|
||||||
|
from jose import jwt, JWTError
|
||||||
|
from passlib.context import CryptContext
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from pick_student.database import SessionLocal, get_db
|
||||||
|
from pick_student.crud import get_user, create_teacher
|
||||||
|
|
||||||
|
app2=APIRouter()
|
||||||
|
|
||||||
|
SECRET_KEY = secrets.token_urlsafe()
|
||||||
|
ALGORITHM = "HS256"
|
||||||
|
ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
||||||
|
class Token(BaseModel):
|
||||||
|
access_token: str
|
||||||
|
token_type: str
|
||||||
|
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
|
||||||
|
def verify_password(plain_password: str, hashed_password:str):
|
||||||
|
return pwd_context.verify(plain_password, hashed_password)
|
||||||
|
|
||||||
|
def get_password_hash(password: str):
|
||||||
|
return pwd_context.hash(password)
|
||||||
|
def jwt_authenticate_user(db: Session, username: str, password: str):
|
||||||
|
user = get_user(db, username)
|
||||||
|
if not user:
|
||||||
|
return False
|
||||||
|
if not verify_password(password, user.password_hash):
|
||||||
|
return False
|
||||||
|
return user
|
||||||
|
def create_access_token(data: dict, expires_delta=None):
|
||||||
|
to_encode = data.copy()
|
||||||
|
if expires_delta:
|
||||||
|
expire = datetime.utcnow() + expires_delta
|
||||||
|
else:
|
||||||
|
expire = datetime.utcnow() + timedelta(minutes=15)
|
||||||
|
to_encode.update({"exp": expire})
|
||||||
|
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||||
|
@app2.post("/jwt/token", response_model=Token)
|
||||||
|
def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
|
||||||
|
user = jwt_authenticate_user(db, form_data.username, form_data.password)
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=401, detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"})
|
||||||
|
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||||
|
access_token = create_access_token(
|
||||||
|
data={"sub": user.user_name}, expires_delta=access_token_expires
|
||||||
|
)
|
||||||
|
return {"access_token": access_token, "token_type": "bearer"}
|
||||||
|
|
||||||
|
|
||||||
|
@app2.post("/jwt/register", response_model=Token)
|
||||||
|
def register_user(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
|
||||||
|
# 1. 检查用户名是否已经存在
|
||||||
|
existing_user = get_user(db, form_data.username)
|
||||||
|
if existing_user:
|
||||||
|
raise HTTPException(status_code=400, detail="Username already registered")
|
||||||
|
# 2. 对密码进行哈希处理
|
||||||
|
hashed_password = get_password_hash(form_data.password)
|
||||||
|
|
||||||
|
# 3. 创建新的用户并存储到数据库
|
||||||
|
user = create_teacher(db=db, user_name=form_data.username, password_hash=hashed_password)
|
||||||
|
|
||||||
|
# 4. 注册成功后生成 access_token
|
||||||
|
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||||
|
access_token = create_access_token(
|
||||||
|
data={"sub": user.user_name}, expires_delta=access_token_expires
|
||||||
|
)
|
||||||
|
|
||||||
|
# 5. 返回 access_token 和 token_type
|
||||||
|
return {"access_token": access_token, "token_type": "bearer","user_name": user.user_name}
|
||||||
|
|
||||||
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/teacher/jwt/token")
|
||||||
|
def get_current_teacher(db: Session = Depends(get_db), token: str = Depends(oauth2_scheme)):
|
||||||
|
credentials_exception = HTTPException(
|
||||||
|
status_code=401,
|
||||||
|
detail="Could not validate credentials",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||||
|
username: str = payload.get("sub")
|
||||||
|
if username is None:
|
||||||
|
raise credentials_exception
|
||||||
|
except JWTError:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
user = get_user(db, username=username)
|
||||||
|
if user is None:
|
||||||
|
raise credentials_exception
|
||||||
|
return user
|
@ -0,0 +1,43 @@
|
|||||||
|
from sqlalchemy import func
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
from sqlalchemy.sql.schema import Column, ForeignKey
|
||||||
|
from sqlalchemy.sql.sqltypes import Integer, String, FLOAT, DateTime, Boolean
|
||||||
|
from pick_student.database import Base, engine
|
||||||
|
|
||||||
|
class Student(Base): # 类名使用大写
|
||||||
|
__tablename__ = "student"
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
student_id = Column(Integer, nullable=False, comment='学号')
|
||||||
|
name = Column(String, nullable=False, comment='姓名')
|
||||||
|
scores = Column(FLOAT, comment='得分', default=0)
|
||||||
|
consecutive_calls = Column(Integer, default=0) # 记录连续被点名的次数
|
||||||
|
is_master = Column(Boolean, default=False) # 是否进入知识大师状态
|
||||||
|
master_uses = Column(Integer, default=0) # 知识大师状态下的剩余次数
|
||||||
|
class_name = Column(String, ForeignKey('class.class_name'), nullable=False)
|
||||||
|
user_name = Column(String, ForeignKey('teacher.user_name'), nullable=False)# 外键引用修正
|
||||||
|
class_ = relationship("Class", back_populates="students")
|
||||||
|
created_at = Column(DateTime, server_default=func.now(), comment='创建时间')
|
||||||
|
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment='更新时间')
|
||||||
|
__mapped_args__ = {"order_by": id}
|
||||||
|
|
||||||
|
class Teacher(Base):
|
||||||
|
__tablename__ = "teacher"
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
user_name = Column(String, nullable=False, comment='教师用户名')
|
||||||
|
password_hash = Column(String, nullable=False, comment='密码')
|
||||||
|
created_at = Column(DateTime, server_default=func.now(), comment='创建时间')
|
||||||
|
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment='更新时间')
|
||||||
|
classes = relationship("Class", back_populates="teacher")
|
||||||
|
|
||||||
|
|
||||||
|
# 班级表
|
||||||
|
class Class(Base):
|
||||||
|
__tablename__ = "class"
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
class_name = Column(String, nullable=False, comment='班级名称')
|
||||||
|
class_time = Column(String, nullable=False, comment='开班时间')
|
||||||
|
user_name = Column(String, ForeignKey('teacher.user_name'), nullable=False)
|
||||||
|
teacher = relationship("Teacher", back_populates="classes")
|
||||||
|
students = relationship("Student", back_populates="class_") # 使用 class_ 避免与类名冲突
|
||||||
|
created_at = Column(DateTime, server_default=func.now(), comment='创建时间')
|
||||||
|
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment='更新时间')
|
@ -0,0 +1,40 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
def call_student(student, correct: bool):
|
||||||
|
if student.is_master:
|
||||||
|
student.master_uses -= 1
|
||||||
|
if student.master_uses <= 0:
|
||||||
|
student.is_master = False # 退出知识大师状态
|
||||||
|
|
||||||
|
if correct:
|
||||||
|
student.consecutive_calls += 1
|
||||||
|
if student.consecutive_calls >= 3:
|
||||||
|
student.consecutive_calls = 0
|
||||||
|
student.is_master = True
|
||||||
|
student.master_uses = 3 # 未来三次被点名概率降低
|
||||||
|
else:
|
||||||
|
student.consecutive_calls = 0
|
||||||
|
|
||||||
|
return student
|
||||||
|
def weighted_random_selection(students,reverse=False):
|
||||||
|
total_score = sum([student.scores for student in students])
|
||||||
|
# 计算每个学生的权重
|
||||||
|
probabilities = []
|
||||||
|
for student in students:
|
||||||
|
if student.is_master and student.master_uses > 0:
|
||||||
|
# 如果是知识大师,概率大幅降低
|
||||||
|
weight = (total_score - student.scores + 1) / (total_score + 1) * 0.1
|
||||||
|
elif reverse and student.scores <= 5:
|
||||||
|
# 反转机制:低分学生被点名概率增加
|
||||||
|
weight = (total_score - student.scores + 1) / (total_score + 1) * 2 # 概率翻倍
|
||||||
|
else:
|
||||||
|
# 正常情况下的加权选择,分数越高概率越低
|
||||||
|
weight = (total_score - student.scores + 1) / (total_score + 1)
|
||||||
|
probabilities.append(weight)
|
||||||
|
# 使用随机加权选择
|
||||||
|
selected = random.choices(students, weights=probabilities, k=1)[0]
|
||||||
|
return selected
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
class create_student(BaseModel):
|
||||||
|
student_id: int
|
||||||
|
name: str
|
||||||
|
class_name: str
|
||||||
|
user_name: str
|
||||||
|
class update_student(create_student):
|
||||||
|
student_id : int
|
||||||
|
name: str
|
||||||
|
class_name: str
|
||||||
|
scores: float
|
||||||
|
consecutive_calls: int
|
||||||
|
master_uses: int
|
||||||
|
is_master: bool
|
||||||
|
class read_student(create_student):
|
||||||
|
id: int
|
||||||
|
student_id: int
|
||||||
|
name: str
|
||||||
|
scores: float
|
||||||
|
updated_at: str
|
||||||
|
consecutive_calls: int
|
||||||
|
is_master:bool
|
||||||
|
master_uses: int
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
class create_class(BaseModel):
|
||||||
|
user_name: str
|
||||||
|
class_name: str
|
||||||
|
class_time: str
|
||||||
|
class Config:
|
||||||
|
from_attributes = True # 使模型与 SQLAlchemy ORM 模型兼容
|
||||||
|
class create_teacher(BaseModel):
|
||||||
|
user_name: str
|
||||||
|
password: str
|
||||||
|
|
||||||
|
|
||||||
|
class read_class(BaseModel):
|
||||||
|
id: int
|
||||||
|
class_name: str
|
||||||
|
updated_at: str
|
||||||
|
class_time: str
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
@ -0,0 +1,22 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
import uvicorn
|
||||||
|
from starlette.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
|
from pick_student import app1,app2
|
||||||
|
|
||||||
|
app=FastAPI(
|
||||||
|
title="学生点名系统",
|
||||||
|
description="学生点名系统",
|
||||||
|
version="1.0.0",
|
||||||
|
)
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"], # 允许所有方法
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
app.include_router(app1, prefix="/teacher/pick_student",tags=["学生点名系统"])
|
||||||
|
app.include_router(app2, prefix="/teacher",tags=["教师登录系统"])
|
||||||
|
if __name__=='__main__':
|
||||||
|
uvicorn.run('run:app',host='127.0.0.1',port=8000,reload=True)
|
Binary file not shown.
@ -0,0 +1,86 @@
|
|||||||
|
import unittest
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from pick_student.database import Base
|
||||||
|
from pick_student.login import get_password_hash
|
||||||
|
from pick_student.main import get_db
|
||||||
|
from run import app # 从 run.py 导入主 app
|
||||||
|
from pick_student.crud import create_teacher
|
||||||
|
|
||||||
|
# 设置测试数据库
|
||||||
|
SQLALCHEMY_DATABASE_URL = "sqlite:///./test_test.db" # 使用不同的数据库文件名
|
||||||
|
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
|
||||||
|
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
# 创建所有表
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
# 重写 get_db 函数用于测试
|
||||||
|
def override_get_db():
|
||||||
|
db = TestingSessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
except Exception as e:
|
||||||
|
db.close()
|
||||||
|
raise e # 重新抛出异常
|
||||||
|
finally:
|
||||||
|
db.close() # 确保数据库连接被正确关闭
|
||||||
|
|
||||||
|
# 覆盖 app 中的依赖项
|
||||||
|
app.dependency_overrides[get_db] = override_get_db
|
||||||
|
|
||||||
|
# 创建测试客户端
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
class TestAuthRoutes(unittest.TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
# 在测试开始时设置初始数据库状态
|
||||||
|
db = TestingSessionLocal()
|
||||||
|
hashed_password = get_password_hash("password") # 假设已经生成一个哈希密码
|
||||||
|
create_teacher(db, user_name="test_teacher", password_hash=hashed_password)
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
def test_login_success(self):
|
||||||
|
# 测试登录成功的情况
|
||||||
|
response = client.post(
|
||||||
|
"/teacher/jwt/token",
|
||||||
|
data={"username": "test_teacher", "password": "password"},
|
||||||
|
)
|
||||||
|
print(response.json())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
data = response.json()
|
||||||
|
self.assertIn("access_token", data)
|
||||||
|
self.assertIn("token_type", data)
|
||||||
|
|
||||||
|
def test_login_failure(self):
|
||||||
|
# 测试登录失败的情况
|
||||||
|
response = client.post(
|
||||||
|
"/teacher/jwt/token",
|
||||||
|
data={"username": "wrong_teacher", "password": "wrong_password"},
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 401)
|
||||||
|
data = response.json()
|
||||||
|
self.assertEqual(data["detail"], "Incorrect username or password")
|
||||||
|
|
||||||
|
def test_register_user(self):
|
||||||
|
# 测试注册新用户
|
||||||
|
response = client.post(
|
||||||
|
"/teacher/jwt/register",
|
||||||
|
data={"username": "new_teacher", "password": "new_password"},
|
||||||
|
)
|
||||||
|
print(response.json())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
data = response.json()
|
||||||
|
self.assertIn("access_token", data)
|
||||||
|
self.assertIn("token_type", data)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
# 清理测试数据库
|
||||||
|
Base.metadata.drop_all(bind=engine)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
@ -0,0 +1,153 @@
|
|||||||
|
import io
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
from pick_student import crud, schemas
|
||||||
|
from pick_student.database import Base
|
||||||
|
from pick_student.login import get_password_hash
|
||||||
|
from pick_student.main import get_db
|
||||||
|
from run import app # 从 run.py 导入主 app
|
||||||
|
from pick_student.crud import create_teacher, create_class
|
||||||
|
|
||||||
|
# 设置测试数据库
|
||||||
|
SQLALCHEMY_DATABASE_URL = "sqlite:///./test_test.db" # 使用不同的数据库文件名
|
||||||
|
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
|
||||||
|
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
# 创建所有表
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
# 重写 get_db 函数用于测试
|
||||||
|
def override_get_db():
|
||||||
|
db = TestingSessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
except Exception as e:
|
||||||
|
db.close()
|
||||||
|
raise e # 重新抛出异常
|
||||||
|
finally:
|
||||||
|
db.close() # 确保数据库连接被正确关闭
|
||||||
|
|
||||||
|
# 覆盖 app 中的依赖项
|
||||||
|
app.dependency_overrides[get_db] = override_get_db
|
||||||
|
|
||||||
|
# 创建测试客户端
|
||||||
|
client = TestClient(app)
|
||||||
|
class TestMainRoutes(unittest.TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
# 在测试开始时设置初始数据库状态
|
||||||
|
db = TestingSessionLocal()
|
||||||
|
hashed_password = get_password_hash("password") # 假设已经生成一个哈希密码
|
||||||
|
create_teacher(db, user_name="test_teacher", password_hash=hashed_password)
|
||||||
|
crud.create_class(db, cclass=schemas.create_class(class_name="2班", user_name="test_teacher",class_time="周三 5-6节"))
|
||||||
|
crud.create_class(db, cclass=schemas.create_class(class_name="3班", user_name="test_teacher",class_time="周三 5-6节"))
|
||||||
|
crud.create_student(db, student=schemas.create_student(class_name="2班", name="Alice", student_id=101, user_name="test_teacher"))
|
||||||
|
crud.create_student(db, student=schemas.create_student(class_name="2班", name="Bob", student_id=102,user_name="test_teacher"))
|
||||||
|
crud.create_student(db, student=schemas.create_student(class_name="2班", name="Charlie", student_id=103, user_name="test_teacher"))
|
||||||
|
crud.create_student(db, student=schemas.create_student(class_name="3班", name="David", student_id=104, user_name="test_teacher"))
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
def test_create_classroom(self):
|
||||||
|
# 测试创建班级的成功情况
|
||||||
|
response = client.post(
|
||||||
|
"/teacher/pick_student/create_class/",
|
||||||
|
params={"class_name": "1班","class_time":"周三 5-6节"},
|
||||||
|
headers={"Authorization": "Bearer " + self.get_access_token()}
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.json()["message"], "班级创建成功")
|
||||||
|
|
||||||
|
def test_upload_students(self):
|
||||||
|
# 测试上传学生信息的成功情况
|
||||||
|
class_name = "1班"
|
||||||
|
data = {
|
||||||
|
'name': ['Alice', 'Bob'],
|
||||||
|
'student_id': [101, 102]
|
||||||
|
, 'user_name': ['test_teacher', 'test_teacher']
|
||||||
|
}
|
||||||
|
df = pd.DataFrame(data)
|
||||||
|
excel_buffer = io.BytesIO()
|
||||||
|
df.to_excel(excel_buffer, index=False)
|
||||||
|
excel_buffer.seek(0)
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
"/teacher/pick_student/upload_students/",
|
||||||
|
files={"file": ("students.xlsx", excel_buffer, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
|
||||||
|
params={"class_name": class_name},
|
||||||
|
headers={"Authorization": "Bearer " + self.get_access_token()}
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.json()["message"], "Student list uploaded successfully.")
|
||||||
|
def test_get_students(self):
|
||||||
|
response = client.get(
|
||||||
|
"/teacher/pick_student/get_students/",
|
||||||
|
params={"class_name": "2班"},
|
||||||
|
headers={"Authorization": "Bearer " + self.get_access_token()}
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
def test_update_score(self):
|
||||||
|
response = client.put(
|
||||||
|
"/teacher/pick_student/update_score/",
|
||||||
|
params={"class_name": "2班", "student_id": 101, "arrival": True, "question_repeated": False, "question_correct": 2.5},
|
||||||
|
headers={"Authorization": "Bearer " + self.get_access_token()}
|
||||||
|
)
|
||||||
|
print(response.json())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertIn("scores", response.json())
|
||||||
|
def test_is_reverse_day(self):
|
||||||
|
response = client.put(
|
||||||
|
"/teacher/pick_student/is_reverse_day/",
|
||||||
|
params={"flag": True},
|
||||||
|
headers={"Authorization": "Bearer " + self.get_access_token()}
|
||||||
|
)
|
||||||
|
print(response.json())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.json(), {"message": "Flag has been set True"})
|
||||||
|
def test_random_pick(self):
|
||||||
|
# 测试随机点名的功能
|
||||||
|
response = client.get(
|
||||||
|
"/teacher/pick_student/random_pick/",
|
||||||
|
params={"class_name": "2班"},
|
||||||
|
headers={"Authorization": "Bearer " + self.get_access_token()}
|
||||||
|
)
|
||||||
|
print(response.json())
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
data = response.json()
|
||||||
|
self.assertIn("name", data)
|
||||||
|
self.assertIn("student_id", data)
|
||||||
|
|
||||||
|
def test_get_classes(self):
|
||||||
|
response = client.get(
|
||||||
|
"/teacher/pick_student/getclasses/",
|
||||||
|
headers={"Authorization": "Bearer " + self.get_access_token()}
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
data = response.json()
|
||||||
|
def test_delete_class(self):
|
||||||
|
response = client.delete(
|
||||||
|
"/teacher/pick_student/delete_class/",
|
||||||
|
params={"class_name": "3班"},
|
||||||
|
headers={"Authorization": "Bearer " + self.get_access_token()}
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
def get_access_token(self):
|
||||||
|
# 获取访问令牌
|
||||||
|
response = client.post(
|
||||||
|
"/teacher/jwt/token",
|
||||||
|
data={"username": "test_teacher", "password": "password"},
|
||||||
|
)
|
||||||
|
return response.json()["access_token"]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
# 清理测试数据库
|
||||||
|
Base.metadata.drop_all(bind=engine)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
Binary file not shown.
@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar"]
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
# Vue 3 + Vite
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||||
|
|
||||||
|
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
|
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>萝卜册登录</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
@ -0,0 +1,483 @@
|
|||||||
|
{
|
||||||
|
"name": "roll-book",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "roll-book",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"vue": "^3.5.10"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
|
"vite": "^5.4.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-string-parser": {
|
||||||
|
"version": "7.25.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz",
|
||||||
|
"integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
|
"version": "7.25.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz",
|
||||||
|
"integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/parser": {
|
||||||
|
"version": "7.25.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz",
|
||||||
|
"integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/types": "^7.25.7"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"parser": "bin/babel-parser.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/types": {
|
||||||
|
"version": "7.25.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz",
|
||||||
|
"integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-string-parser": "^7.25.7",
|
||||||
|
"@babel/helper-validator-identifier": "^7.25.7",
|
||||||
|
"to-fast-properties": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-x64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
|
"version": "4.24.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz",
|
||||||
|
"integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@types/estree": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
|
"version": "5.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz",
|
||||||
|
"integrity": "sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vite": "^5.0.0",
|
||||||
|
"vue": "^3.2.25"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-core": {
|
||||||
|
"version": "3.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.11.tgz",
|
||||||
|
"integrity": "sha512-PwAdxs7/9Hc3ieBO12tXzmTD+Ln4qhT/56S+8DvrrZ4kLDn4Z/AMUr8tXJD0axiJBS0RKIoNaR0yMuQB9v9Udg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.25.3",
|
||||||
|
"@vue/shared": "3.5.11",
|
||||||
|
"entities": "^4.5.0",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"source-map-js": "^1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-dom": {
|
||||||
|
"version": "3.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.11.tgz",
|
||||||
|
"integrity": "sha512-pyGf8zdbDDRkBrEzf8p7BQlMKNNF5Fk/Cf/fQ6PiUz9at4OaUfyXW0dGJTo2Vl1f5U9jSLCNf0EZJEogLXoeew==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-core": "3.5.11",
|
||||||
|
"@vue/shared": "3.5.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-sfc": {
|
||||||
|
"version": "3.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.11.tgz",
|
||||||
|
"integrity": "sha512-gsbBtT4N9ANXXepprle+X9YLg2htQk1sqH/qGJ/EApl+dgpUBdTv3yP7YlR535uHZY3n6XaR0/bKo0BgwwDniw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.25.3",
|
||||||
|
"@vue/compiler-core": "3.5.11",
|
||||||
|
"@vue/compiler-dom": "3.5.11",
|
||||||
|
"@vue/compiler-ssr": "3.5.11",
|
||||||
|
"@vue/shared": "3.5.11",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"magic-string": "^0.30.11",
|
||||||
|
"postcss": "^8.4.47",
|
||||||
|
"source-map-js": "^1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-ssr": {
|
||||||
|
"version": "3.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.11.tgz",
|
||||||
|
"integrity": "sha512-P4+GPjOuC2aFTk1Z4WANvEhyOykcvEd5bIj2KVNGKGfM745LaXGr++5njpdBTzVz5pZifdlR1kpYSJJpIlSePA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-dom": "3.5.11",
|
||||||
|
"@vue/shared": "3.5.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/reactivity": {
|
||||||
|
"version": "3.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.11.tgz",
|
||||||
|
"integrity": "sha512-Nqo5VZEn8MJWlCce8XoyVqHZbd5P2NH+yuAaFzuNSR96I+y1cnuUiq7xfSG+kyvLSiWmaHTKP1r3OZY4mMD50w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/shared": "3.5.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/runtime-core": {
|
||||||
|
"version": "3.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.11.tgz",
|
||||||
|
"integrity": "sha512-7PsxFGqwfDhfhh0OcDWBG1DaIQIVOLgkwA5q6MtkPiDFjp5gohVnJEahSktwSFLq7R5PtxDKy6WKURVN1UDbzA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/reactivity": "3.5.11",
|
||||||
|
"@vue/shared": "3.5.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/runtime-dom": {
|
||||||
|
"version": "3.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.11.tgz",
|
||||||
|
"integrity": "sha512-GNghjecT6IrGf0UhuYmpgaOlN7kxzQBhxWEn08c/SQDxv1yy4IXI1bn81JgEpQ4IXjRxWtPyI8x0/7TF5rPfYQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/reactivity": "3.5.11",
|
||||||
|
"@vue/runtime-core": "3.5.11",
|
||||||
|
"@vue/shared": "3.5.11",
|
||||||
|
"csstype": "^3.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/server-renderer": {
|
||||||
|
"version": "3.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.11.tgz",
|
||||||
|
"integrity": "sha512-cVOwYBxR7Wb1B1FoxYvtjJD8X/9E5nlH4VSkJy2uMA1MzYNdzAAB//l8nrmN9py/4aP+3NjWukf9PZ3TeWULaA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-ssr": "3.5.11",
|
||||||
|
"@vue/shared": "3.5.11"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "3.5.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/shared": {
|
||||||
|
"version": "3.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.11.tgz",
|
||||||
|
"integrity": "sha512-W8GgysJVnFo81FthhzurdRAWP/byq3q2qIw70e0JWblzVhjgOMiC2GyovXrZTFQJnFVryYaKGP3Tc9vYzYm6PQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/csstype": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/entities": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/esbuild": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@esbuild/aix-ppc64": "0.21.5",
|
||||||
|
"@esbuild/android-arm": "0.21.5",
|
||||||
|
"@esbuild/android-arm64": "0.21.5",
|
||||||
|
"@esbuild/android-x64": "0.21.5",
|
||||||
|
"@esbuild/darwin-arm64": "0.21.5",
|
||||||
|
"@esbuild/darwin-x64": "0.21.5",
|
||||||
|
"@esbuild/freebsd-arm64": "0.21.5",
|
||||||
|
"@esbuild/freebsd-x64": "0.21.5",
|
||||||
|
"@esbuild/linux-arm": "0.21.5",
|
||||||
|
"@esbuild/linux-arm64": "0.21.5",
|
||||||
|
"@esbuild/linux-ia32": "0.21.5",
|
||||||
|
"@esbuild/linux-loong64": "0.21.5",
|
||||||
|
"@esbuild/linux-mips64el": "0.21.5",
|
||||||
|
"@esbuild/linux-ppc64": "0.21.5",
|
||||||
|
"@esbuild/linux-riscv64": "0.21.5",
|
||||||
|
"@esbuild/linux-s390x": "0.21.5",
|
||||||
|
"@esbuild/linux-x64": "0.21.5",
|
||||||
|
"@esbuild/netbsd-x64": "0.21.5",
|
||||||
|
"@esbuild/openbsd-x64": "0.21.5",
|
||||||
|
"@esbuild/sunos-x64": "0.21.5",
|
||||||
|
"@esbuild/win32-arm64": "0.21.5",
|
||||||
|
"@esbuild/win32-ia32": "0.21.5",
|
||||||
|
"@esbuild/win32-x64": "0.21.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/estree-walker": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/magic-string": {
|
||||||
|
"version": "0.30.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
|
||||||
|
"integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/nanoid": {
|
||||||
|
"version": "3.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||||
|
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/picocolors": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/postcss": {
|
||||||
|
"version": "8.4.47",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||||
|
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^3.3.7",
|
||||||
|
"picocolors": "^1.1.0",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup": {
|
||||||
|
"version": "4.24.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
|
||||||
|
"integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "1.0.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rollup": "dist/bin/rollup"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@rollup/rollup-android-arm-eabi": "4.24.0",
|
||||||
|
"@rollup/rollup-android-arm64": "4.24.0",
|
||||||
|
"@rollup/rollup-darwin-arm64": "4.24.0",
|
||||||
|
"@rollup/rollup-darwin-x64": "4.24.0",
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf": "4.24.0",
|
||||||
|
"@rollup/rollup-linux-arm64-gnu": "4.24.0",
|
||||||
|
"@rollup/rollup-linux-arm64-musl": "4.24.0",
|
||||||
|
"@rollup/rollup-linux-powerpc64le-gnu": "4.24.0",
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu": "4.24.0",
|
||||||
|
"@rollup/rollup-linux-s390x-gnu": "4.24.0",
|
||||||
|
"@rollup/rollup-linux-x64-gnu": "4.24.0",
|
||||||
|
"@rollup/rollup-linux-x64-musl": "4.24.0",
|
||||||
|
"@rollup/rollup-win32-arm64-msvc": "4.24.0",
|
||||||
|
"@rollup/rollup-win32-ia32-msvc": "4.24.0",
|
||||||
|
"@rollup/rollup-win32-x64-msvc": "4.24.0",
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-js": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/to-fast-properties": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite": {
|
||||||
|
"version": "5.4.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz",
|
||||||
|
"integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"esbuild": "^0.21.3",
|
||||||
|
"postcss": "^8.4.43",
|
||||||
|
"rollup": "^4.20.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"vite": "bin/vite.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": "^18.0.0 || >=20.0.0",
|
||||||
|
"less": "*",
|
||||||
|
"lightningcss": "^1.21.0",
|
||||||
|
"sass": "*",
|
||||||
|
"sass-embedded": "*",
|
||||||
|
"stylus": "*",
|
||||||
|
"sugarss": "*",
|
||||||
|
"terser": "^5.4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"less": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"lightningcss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass-embedded": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"stylus": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sugarss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"terser": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vue": {
|
||||||
|
"version": "3.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.11.tgz",
|
||||||
|
"integrity": "sha512-/8Wurrd9J3lb72FTQS7gRMNQD4nztTtKPmuDuPuhqXmmpD6+skVjAeahNpVzsuky6Sy9gy7wn8UadqPtt9SQIg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-dom": "3.5.11",
|
||||||
|
"@vue/compiler-sfc": "3.5.11",
|
||||||
|
"@vue/runtime-dom": "3.5.11",
|
||||||
|
"@vue/server-renderer": "3.5.11",
|
||||||
|
"@vue/shared": "3.5.11"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "roll-book",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"vue": "^3.5.10",
|
||||||
|
"vue-router": "^4.4.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
|
"vite": "^5.4.8"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<router-view path=""></router-view>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Global styles */
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
</style>
|
After Width: | Height: | Size: 742 B |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 749 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 496 B |
@ -0,0 +1,8 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import './style.css'
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
app.use(router)
|
||||||
|
app.mount('#app')
|
@ -0,0 +1,45 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
import LoginPage from '../views/LoginPage.vue'
|
||||||
|
import RegisterPage from '../views/RegisterPage.vue'
|
||||||
|
import ClassManagement from '../views/ClassManagement.vue'
|
||||||
|
import CreateClass from '../views/CreateClass.vue'
|
||||||
|
import StudentManagement from '../views/StudentManagement.vue'
|
||||||
|
import RollBook from '../views/RollBook.vue'
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'ClassManagement',
|
||||||
|
component: ClassManagement
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'Login',
|
||||||
|
component: LoginPage
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/register',
|
||||||
|
name: 'Register',
|
||||||
|
component: RegisterPage
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/create_class',
|
||||||
|
name: 'CreateClass',
|
||||||
|
component: CreateClass
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/student_management/:className',
|
||||||
|
name: 'StudentManagement',
|
||||||
|
component: StudentManagement
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/random_pick/:className',
|
||||||
|
name: 'RollBook',
|
||||||
|
component: RollBook
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
@ -0,0 +1,309 @@
|
|||||||
|
<template>
|
||||||
|
<div class="class-management-page">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="bgdiagram_1"></div>
|
||||||
|
<div class="bgdiagram_2"></div>
|
||||||
|
<div class="bgdiagram_3"></div>
|
||||||
|
<div class="bgdiagram_4"></div>
|
||||||
|
<div class="bgdiagram_5"></div>
|
||||||
|
<span class="NickName">{{nickname}}</span>
|
||||||
|
<router-link to="/login">
|
||||||
|
<span class="login_in" v-if="nickname ===''">登录</span>
|
||||||
|
</router-link>
|
||||||
|
<router-link to="/login">
|
||||||
|
<span class="quitLogin">退出</span>
|
||||||
|
</router-link>
|
||||||
|
<span class="classCount">班级数 1</span>
|
||||||
|
<router-link to="/create_class">
|
||||||
|
<button class="createClass">+新建班级</button>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="class_manage">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>班级名</th>
|
||||||
|
<th>上课时间</th>
|
||||||
|
<th>动作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody v-if="paginatedClasses.length">
|
||||||
|
<tr v-for="cclass in paginatedClasses" :key="cclass.id">
|
||||||
|
<td>{{ cclass.class_name }}</td>
|
||||||
|
<td>{{ cclass.class_time }}</td>
|
||||||
|
<td>
|
||||||
|
<button class="button_style" @click="goToStudentManagement(cclass.class_name)">萝卜册</button>
|
||||||
|
<button class="button_style" @click="manageClass(cclass.class_name)">数萝卜</button>
|
||||||
|
<button class="button_style" @click="deleteClass(cclass.class_name)">吃掉</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="pagination">
|
||||||
|
<button @click="prevPage" :disabled="currentPage === 1">上一页</button>
|
||||||
|
<button @click="nextPage" :disabled="currentPage === totalPages">下一页</button>
|
||||||
|
<span>{{ currentPage }} / {{ totalPages }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default{
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nickname: '',
|
||||||
|
className: '',
|
||||||
|
classes: [],
|
||||||
|
classTime: '',
|
||||||
|
errorMessage: '',
|
||||||
|
currentPage: 1, // 当前页
|
||||||
|
pageSize: 5, // 每页显示的班级数量
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// className() {
|
||||||
|
// return this.$route.params.className;
|
||||||
|
// },
|
||||||
|
totalPages() {
|
||||||
|
return Math.ceil(this.classes.length / this.pageSize); // 计算总页数
|
||||||
|
},
|
||||||
|
paginatedClasses() {
|
||||||
|
const start = (this.currentPage - 1) * this.pageSize; // 计算开始索引
|
||||||
|
return this.classes.slice(start, start + this.pageSize); // 返回当前页的学生数据
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchClasses() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get('http://127.0.0.1:8000/teacher/pick_student/getclasses', {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.classes = response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取班级信息失败', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nextPage() {
|
||||||
|
if (this.currentPage < this.totalPages) {
|
||||||
|
this.currentPage++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
prevPage() {
|
||||||
|
if (this.currentPage > 1) {
|
||||||
|
this.currentPage--;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除指定班级
|
||||||
|
async deleteClass(className) {
|
||||||
|
try {
|
||||||
|
const response = await axios.delete(`http://127.0.0.1:8000/teacher/pick_student/delete_class/?class_name=${className}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${localStorage.getItem('access_token')}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
alert(response.data.message); // 显示删除结果
|
||||||
|
this.fetchClasses(); // 重新获取班级列表
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除班级失败', error);
|
||||||
|
alert('删除班级失败,请稍后重试。');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
manageClass(className) {
|
||||||
|
// 使用路由导航
|
||||||
|
this.$router.push({ name: 'RollBook', params: { className } });
|
||||||
|
},
|
||||||
|
|
||||||
|
goToStudentManagement(className) {
|
||||||
|
// 跳转到学生管理页面
|
||||||
|
this.$router.push({ name: 'StudentManagement', params: { className } });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchClasses(); // 页面加载时获取班级信息
|
||||||
|
this.nickname = localStorage.getItem('nickname'); // 从本地存储中检索昵称
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.bgdiagram_1 {
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
background: rgba(94,162,218,1);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
border-top-left-radius: 15px;
|
||||||
|
border-top-right-radius: 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.bgdiagram_2 {
|
||||||
|
width: 197px;
|
||||||
|
height: 197px;
|
||||||
|
background: rgba(217,217,217,0.20000000298023224);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 77px;
|
||||||
|
left: 57px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.bgdiagram_3 {
|
||||||
|
width: 266px;
|
||||||
|
height: 215px;
|
||||||
|
background: rgba(217,217,217,0.20000000298023224);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 132px;
|
||||||
|
left: 666px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bgdiagram_4 {
|
||||||
|
width: 84px;
|
||||||
|
height: 84px;
|
||||||
|
background: linear-gradient(rgba(217,217,217,0.20000000298023224), rgba(115,115,115,0.20000000298023224));
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 36px;
|
||||||
|
left: 1045px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.bgdiagram_5 {
|
||||||
|
width: 80%;
|
||||||
|
height: 60px;
|
||||||
|
background: rgba(217,217,217,0.4000000059604645);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 240px;
|
||||||
|
left: 120px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.class_manage {
|
||||||
|
width: 80%;
|
||||||
|
height: 200px;
|
||||||
|
background: rgba(255,255,255,1);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 324px;
|
||||||
|
left: 120px;
|
||||||
|
border: 1px solid rgba(0,0,0,0.10000000149011612);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.NickName {
|
||||||
|
width: 198px;
|
||||||
|
color: rgba(255,255,255,1);
|
||||||
|
position: absolute;
|
||||||
|
top: 130px;
|
||||||
|
left: 120px;
|
||||||
|
font-family: Inter;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 24px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.quitLogin {
|
||||||
|
width: 60px;
|
||||||
|
color: rgba(255,255,255,1);
|
||||||
|
position: absolute;
|
||||||
|
top: 180px;
|
||||||
|
left: 220px;
|
||||||
|
font-family: Inter;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 24px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.classCount {
|
||||||
|
width: 104px;
|
||||||
|
color: rgba(255,255,255,1);
|
||||||
|
position: absolute;
|
||||||
|
top: 259px;
|
||||||
|
left: 150px;
|
||||||
|
font-family: Inter;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 24px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.createClass {
|
||||||
|
width: 125px;
|
||||||
|
color: rgba(255,255,255,100);
|
||||||
|
background-color: rgba(255, 255, 255, 0);
|
||||||
|
border-width: 1px;
|
||||||
|
position: absolute;
|
||||||
|
top: 258px;
|
||||||
|
left: 318px;
|
||||||
|
font-family: Microsoft Himalaya;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 24px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button_style {
|
||||||
|
width: 90px;
|
||||||
|
height: 40px;
|
||||||
|
background: rgba(24,144,255,1);
|
||||||
|
opacity: 1;
|
||||||
|
color: white;
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
border-top-right-radius: 10px;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login_in {
|
||||||
|
width: 80px;
|
||||||
|
color: rgba(255,255,255,1);
|
||||||
|
position: absolute;
|
||||||
|
top: 180px;
|
||||||
|
left: 120px;
|
||||||
|
font-family: Inter;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 24px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
top: 850px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
text-align: center; /* 居中对齐 */
|
||||||
|
margin: 20px 0; /* 添加上下边距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination button {
|
||||||
|
padding: 10px 20px; /* 添加内边距 */
|
||||||
|
margin: 0 10px; /* 添加左右边距 */
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px; /* 添加圆角 */
|
||||||
|
background: rgba(24, 144, 255, 1);
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination button:disabled {
|
||||||
|
background: rgba(200, 200, 200, 0.5); /* 禁用状态颜色 */
|
||||||
|
cursor: not-allowed; /* 光标样式 */
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,223 @@
|
|||||||
|
<template>
|
||||||
|
<div class="v32_21">
|
||||||
|
<div class="v32_22"></div>
|
||||||
|
<div class="v32_23"></div>
|
||||||
|
<div class="v32_37"></div>
|
||||||
|
<h2 class="v32_24">新建班级</h2>
|
||||||
|
<h4 class="v32_30">班级名称</h4>
|
||||||
|
<h4 class="v32_31">上课时间</h4>
|
||||||
|
<div @submit.prevent="createClass">
|
||||||
|
<input type="text" v-model="className" class="v32_25">
|
||||||
|
<input type="text" v-model="classTime" class="v32_26">
|
||||||
|
<button class="v32_28" @click="createClass">创建班级</button>
|
||||||
|
</div>
|
||||||
|
<p class="error-message" v-if="errorMessage">{{ errorMessage }}</p>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default{
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
className: '',
|
||||||
|
classTime: '',
|
||||||
|
errorMessage: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async createClass() {
|
||||||
|
const username = localStorage.getItem('username');
|
||||||
|
this.errorMessage = ''; // 清空错误信息
|
||||||
|
try {
|
||||||
|
await axios.post('http://127.0.0.1:8000/teacher/pick_student/create_class', null, {
|
||||||
|
params: {
|
||||||
|
user_name: username,
|
||||||
|
class_name: this.className,
|
||||||
|
class_time: this.classTime
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.className = ''; // 清空输入框
|
||||||
|
this.classTime = ''; // 清空输入框
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = '/'; // 假设班级管理页面为 class_management
|
||||||
|
}, 100);
|
||||||
|
} catch (error) {
|
||||||
|
// 处理错误信息
|
||||||
|
if (error.response && error.response.data) {
|
||||||
|
this.errorMessage = error.response.data.detail || '创建班级失败,请重试';
|
||||||
|
} else {
|
||||||
|
this.errorMessage = '网络错误,请重试';
|
||||||
|
}
|
||||||
|
console.error('创建班级失败', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.v32_21 {
|
||||||
|
width: 100%;
|
||||||
|
height: 1024px;
|
||||||
|
background: rgba(250,250,250,1);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.v32_22 {
|
||||||
|
width: 80%;
|
||||||
|
height: 48px;
|
||||||
|
background: rgba(241,248,255,1);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 300px;
|
||||||
|
left: 120px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.v32_23 {
|
||||||
|
width: 80%;
|
||||||
|
height: 500px;
|
||||||
|
background: rgba(255,255,255,1);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 348px;
|
||||||
|
left: 120px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.v32_28 {
|
||||||
|
width: 100px;
|
||||||
|
height: 48px;
|
||||||
|
background: rgba(24,144,255,1);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 680px;
|
||||||
|
left: 160px;
|
||||||
|
border: 0px;
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: white;
|
||||||
|
font-family: Microsoft Himalaya;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.v32_24 {
|
||||||
|
width: 400px;
|
||||||
|
color: rgba(0,0,0,1);
|
||||||
|
position: absolute;
|
||||||
|
top: 270px;
|
||||||
|
left: 500px;
|
||||||
|
font-family: Microsoft Himalaya;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 36px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.v32_25 {
|
||||||
|
width: 400px;
|
||||||
|
height: 60px;
|
||||||
|
background: rgba(255,255,255,1);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 418px;
|
||||||
|
left: 160px;
|
||||||
|
border: 1px solid rgba(0,0,0,0.20000000298023224);
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.v32_26 {
|
||||||
|
width: 700px;
|
||||||
|
height: 60px;
|
||||||
|
background: rgba(255,255,255,1);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 548px;
|
||||||
|
left: 160px;
|
||||||
|
border: 1px solid rgba(0,0,0,0.20000000298023224);
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.v32_30 {
|
||||||
|
width: 200px;
|
||||||
|
color: rgba(0,0,0,1);
|
||||||
|
position: absolute;
|
||||||
|
top: 360px;
|
||||||
|
left: 160px;
|
||||||
|
font-family: Microsoft Himalaya;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 20px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.v32_31 {
|
||||||
|
width: 200px;
|
||||||
|
color: rgba(0,0,0,1);
|
||||||
|
position: absolute;
|
||||||
|
top: 490px;
|
||||||
|
left: 160px;
|
||||||
|
font-family: Microsoft Himalaya;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 20px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.v32_34 {
|
||||||
|
width: 100px;
|
||||||
|
color: rgba(255,255,255,1);
|
||||||
|
position: absolute;
|
||||||
|
top: 686px;
|
||||||
|
left: 160px;
|
||||||
|
|
||||||
|
font-family: Microsoft Himalaya;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 20px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.v32_37 {
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
background: rgba(94,162,218,1);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
border-top-left-radius: 15px;
|
||||||
|
border-top-right-radius: 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
color: red;
|
||||||
|
margin-top: 10px;
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 750px;
|
||||||
|
left: 160px;
|
||||||
|
}
|
||||||
|
</style>>
|
@ -0,0 +1,326 @@
|
|||||||
|
<template>
|
||||||
|
<div class="student-management-page">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="bgdiagram_1"></div>
|
||||||
|
<div class="bgdiagram_5"></div>
|
||||||
|
|
||||||
|
<span class="NickName_style">{{nickname}}</span>
|
||||||
|
<router-link to="/">
|
||||||
|
<span class="return_style">返回</span>
|
||||||
|
</router-link>
|
||||||
|
<span class="show_name">班级名:{{className}}</span>
|
||||||
|
<label class="upload_student" for="file-upload">上传学生</label>
|
||||||
|
<input type="file" id="file-upload" @change="uploadFile" accept=".xlsx" />
|
||||||
|
<!-- 删除所有学生按钮 -->
|
||||||
|
<button class="drop_student" @click="deleteAllStudents">删除学生</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="uploadSuccessMessage" class="success-message">{{ uploadSuccessMessage }}</div>
|
||||||
|
<div v-if="uploadErrorMessage" class="error-message">{{ uploadErrorMessage }}</div>
|
||||||
|
|
||||||
|
<table class="student_manage">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>学号</th>
|
||||||
|
<th>姓名</th>
|
||||||
|
<th>得分</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody v-if="paginatedStudents.length">
|
||||||
|
<tr v-for="student in paginatedStudents" :key="student.id">
|
||||||
|
<td>{{ student.student_id }}</td>
|
||||||
|
<td>{{ student.name }}</td>
|
||||||
|
<td>{{ student.scores }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="pagination">
|
||||||
|
<button @click="prevPage" :disabled="currentPage === 1">上一页</button>
|
||||||
|
<button @click="nextPage" :disabled="currentPage === totalPages">下一页</button>
|
||||||
|
<span>{{ currentPage }} / {{ totalPages }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default{
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
students: [],
|
||||||
|
message: '',
|
||||||
|
uploadSuccessMessage: '', // 上传成功信息
|
||||||
|
uploadErrorMessage: '', // 上传失败信息
|
||||||
|
currentPage: 1, // 当前页
|
||||||
|
pageSize: 20, // 每页显示的学生数量
|
||||||
|
nickname: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchStudents(); // 页面加载时自动获取学生列表
|
||||||
|
this.nickname = localStorage.getItem('nickname'); // 从 localStorage 中获取昵称
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
className() {
|
||||||
|
return this.$route.params.className;
|
||||||
|
},
|
||||||
|
totalPages() {
|
||||||
|
return Math.ceil(this.students.length / this.pageSize); // 计算总页数
|
||||||
|
},
|
||||||
|
paginatedStudents() {
|
||||||
|
const start = (this.currentPage - 1) * this.pageSize; // 计算开始索引
|
||||||
|
return this.students.slice(start, start + this.pageSize); // 返回当前页的学生数据
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchStudents() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`http://127.0.0.1:8000/teacher/pick_student/get_students/?class_name=${this.className}`,{
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.students = response.data;
|
||||||
|
} catch (error) {
|
||||||
|
this.message = '获取学生列表失败';
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async deleteAllStudents() {
|
||||||
|
try {
|
||||||
|
const response = await axios.delete(`http://127.0.0.1:8000/teacher/pick_student/delete_students/?class_name=${this.className}`,{
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.message = response.data.message;
|
||||||
|
this.students = []; // 清空学生列表
|
||||||
|
} catch (error) {
|
||||||
|
this.message = '删除所有学生失败';
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nextPage() {
|
||||||
|
if (this.currentPage < this.totalPages) {
|
||||||
|
this.currentPage++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
prevPage() {
|
||||||
|
if (this.currentPage > 1) {
|
||||||
|
this.currentPage--;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async uploadFile(event) {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
if (file) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post(`http://127.0.0.1:8000/teacher/pick_student/upload_students/?class_name=${this.className}`, formData, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.uploadSuccessMessage = response.data.message; // 显示成功提示
|
||||||
|
this.uploadErrorMessage = ''; // 清空错误信息
|
||||||
|
this.fetchStudents(); // 上传成功后重新获取学生列表
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.uploadSuccessMessage = '';
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
event.target.value = ''; // 清空文件输入框的值
|
||||||
|
} catch (error) {
|
||||||
|
console.error('上传学生信息失败', error);
|
||||||
|
this.uploadErrorMessage = error.response?.data?.detail || '上传失败,请重试。'; // 显示错误提示
|
||||||
|
this.uploadSuccessMessage = ''; // 清空成功信息
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.uploadErrorMessage = '';
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.uploadErrorMessage = '请上传文件。';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.uploadErrorMessage = '';
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.bgdiagram_1 {
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
background: rgba(94,162,218,1);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
border-top-left-radius: 15px;
|
||||||
|
border-top-right-radius: 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.bgdiagram_5 {
|
||||||
|
width: 80%;
|
||||||
|
height: 60px;
|
||||||
|
background: rgba(217,217,217,0.4000000059604645);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 240px;
|
||||||
|
left: 120px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.student_manage {
|
||||||
|
width: 80%;
|
||||||
|
height: 200px;
|
||||||
|
background: rgba(255,255,255,1);
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 324px;
|
||||||
|
left: 120px;
|
||||||
|
border: 1px solid rgba(0,0,0,0.10000000149011612);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.NickName_style {
|
||||||
|
width: 198px;
|
||||||
|
color: rgba(255,255,255,1);
|
||||||
|
position: absolute;
|
||||||
|
top: 130px;
|
||||||
|
left: 120px;
|
||||||
|
font-family: Inter;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 24px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show_name {
|
||||||
|
width: 160px;
|
||||||
|
color: rgba(255,255,255,1);
|
||||||
|
position: absolute;
|
||||||
|
top: 259px;
|
||||||
|
left: 150px;
|
||||||
|
font-family: Inter;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 24px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload_student {
|
||||||
|
width: 125px;
|
||||||
|
color: rgba(255,255,255,100);
|
||||||
|
background-color: rgba(255, 255, 255, 0);
|
||||||
|
border-width: 1px;
|
||||||
|
position: absolute;
|
||||||
|
top: 258px;
|
||||||
|
left: 400px;
|
||||||
|
font-family: Microsoft Himalaya;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 24px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drop_student {
|
||||||
|
width: 125px;
|
||||||
|
color: rgba(255,255,255,100);
|
||||||
|
background-color: rgba(255, 255, 255, 0);
|
||||||
|
border-width: 0px;
|
||||||
|
position: absolute;
|
||||||
|
top: 258px;
|
||||||
|
left: 600px;
|
||||||
|
font-family: Microsoft Himalaya;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 24px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button_style {
|
||||||
|
width: 90px;
|
||||||
|
height: 40px;
|
||||||
|
background: rgba(24,144,255,1);
|
||||||
|
opacity: 1;
|
||||||
|
color: white;
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
border-top-right-radius: 10px;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.return_style {
|
||||||
|
width: 80px;
|
||||||
|
color: rgba(255,255,255,1);
|
||||||
|
position: absolute;
|
||||||
|
top: 180px;
|
||||||
|
left: 120px;
|
||||||
|
font-family: Inter;
|
||||||
|
font-weight: Regular;
|
||||||
|
font-size: 24px;
|
||||||
|
opacity: 1;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
top: 850px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
text-align: center; /* 居中对齐 */
|
||||||
|
margin: 20px 0; /* 添加上下边距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination button {
|
||||||
|
padding: 10px 20px; /* 添加内边距 */
|
||||||
|
margin: 0 10px; /* 添加左右边距 */
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px; /* 添加圆角 */
|
||||||
|
background: rgba(24, 144, 255, 1);
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination button:disabled {
|
||||||
|
background: rgba(200, 200, 200, 0.5); /* 禁用状态颜色 */
|
||||||
|
cursor: not-allowed; /* 光标样式 */
|
||||||
|
}
|
||||||
|
|
||||||
|
#file-upload {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-message {
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
top: 260px;
|
||||||
|
left: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
top: 260px;
|
||||||
|
left: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
})
|
@ -0,0 +1,261 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@babel/helper-string-parser@^7.25.7":
|
||||||
|
version "7.25.7"
|
||||||
|
resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz"
|
||||||
|
integrity sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==
|
||||||
|
|
||||||
|
"@babel/helper-validator-identifier@^7.25.7":
|
||||||
|
version "7.25.7"
|
||||||
|
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz"
|
||||||
|
integrity sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==
|
||||||
|
|
||||||
|
"@babel/parser@^7.25.3":
|
||||||
|
version "7.25.7"
|
||||||
|
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz"
|
||||||
|
integrity sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.25.7"
|
||||||
|
|
||||||
|
"@babel/types@^7.25.7":
|
||||||
|
version "7.25.7"
|
||||||
|
resolved "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz"
|
||||||
|
integrity sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-string-parser" "^7.25.7"
|
||||||
|
"@babel/helper-validator-identifier" "^7.25.7"
|
||||||
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
|
"@esbuild/win32-x64@0.21.5":
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz"
|
||||||
|
integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec@^1.5.0":
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
|
||||||
|
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-msvc@4.24.0":
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz"
|
||||||
|
integrity sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==
|
||||||
|
|
||||||
|
"@types/estree@1.0.6":
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz"
|
||||||
|
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
|
||||||
|
|
||||||
|
"@vitejs/plugin-vue@^5.1.4":
|
||||||
|
version "5.1.4"
|
||||||
|
resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz"
|
||||||
|
integrity sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==
|
||||||
|
|
||||||
|
"@vue/compiler-core@3.5.11":
|
||||||
|
version "3.5.11"
|
||||||
|
resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.11.tgz"
|
||||||
|
integrity sha512-PwAdxs7/9Hc3ieBO12tXzmTD+Ln4qhT/56S+8DvrrZ4kLDn4Z/AMUr8tXJD0axiJBS0RKIoNaR0yMuQB9v9Udg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.25.3"
|
||||||
|
"@vue/shared" "3.5.11"
|
||||||
|
entities "^4.5.0"
|
||||||
|
estree-walker "^2.0.2"
|
||||||
|
source-map-js "^1.2.0"
|
||||||
|
|
||||||
|
"@vue/compiler-dom@3.5.11":
|
||||||
|
version "3.5.11"
|
||||||
|
resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.11.tgz"
|
||||||
|
integrity sha512-pyGf8zdbDDRkBrEzf8p7BQlMKNNF5Fk/Cf/fQ6PiUz9at4OaUfyXW0dGJTo2Vl1f5U9jSLCNf0EZJEogLXoeew==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-core" "3.5.11"
|
||||||
|
"@vue/shared" "3.5.11"
|
||||||
|
|
||||||
|
"@vue/compiler-sfc@3.5.11":
|
||||||
|
version "3.5.11"
|
||||||
|
resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.11.tgz"
|
||||||
|
integrity sha512-gsbBtT4N9ANXXepprle+X9YLg2htQk1sqH/qGJ/EApl+dgpUBdTv3yP7YlR535uHZY3n6XaR0/bKo0BgwwDniw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.25.3"
|
||||||
|
"@vue/compiler-core" "3.5.11"
|
||||||
|
"@vue/compiler-dom" "3.5.11"
|
||||||
|
"@vue/compiler-ssr" "3.5.11"
|
||||||
|
"@vue/shared" "3.5.11"
|
||||||
|
estree-walker "^2.0.2"
|
||||||
|
magic-string "^0.30.11"
|
||||||
|
postcss "^8.4.47"
|
||||||
|
source-map-js "^1.2.0"
|
||||||
|
|
||||||
|
"@vue/compiler-ssr@3.5.11":
|
||||||
|
version "3.5.11"
|
||||||
|
resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.11.tgz"
|
||||||
|
integrity sha512-P4+GPjOuC2aFTk1Z4WANvEhyOykcvEd5bIj2KVNGKGfM745LaXGr++5njpdBTzVz5pZifdlR1kpYSJJpIlSePA==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-dom" "3.5.11"
|
||||||
|
"@vue/shared" "3.5.11"
|
||||||
|
|
||||||
|
"@vue/reactivity@3.5.11":
|
||||||
|
version "3.5.11"
|
||||||
|
resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.11.tgz"
|
||||||
|
integrity sha512-Nqo5VZEn8MJWlCce8XoyVqHZbd5P2NH+yuAaFzuNSR96I+y1cnuUiq7xfSG+kyvLSiWmaHTKP1r3OZY4mMD50w==
|
||||||
|
dependencies:
|
||||||
|
"@vue/shared" "3.5.11"
|
||||||
|
|
||||||
|
"@vue/runtime-core@3.5.11":
|
||||||
|
version "3.5.11"
|
||||||
|
resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.11.tgz"
|
||||||
|
integrity sha512-7PsxFGqwfDhfhh0OcDWBG1DaIQIVOLgkwA5q6MtkPiDFjp5gohVnJEahSktwSFLq7R5PtxDKy6WKURVN1UDbzA==
|
||||||
|
dependencies:
|
||||||
|
"@vue/reactivity" "3.5.11"
|
||||||
|
"@vue/shared" "3.5.11"
|
||||||
|
|
||||||
|
"@vue/runtime-dom@3.5.11":
|
||||||
|
version "3.5.11"
|
||||||
|
resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.11.tgz"
|
||||||
|
integrity sha512-GNghjecT6IrGf0UhuYmpgaOlN7kxzQBhxWEn08c/SQDxv1yy4IXI1bn81JgEpQ4IXjRxWtPyI8x0/7TF5rPfYQ==
|
||||||
|
dependencies:
|
||||||
|
"@vue/reactivity" "3.5.11"
|
||||||
|
"@vue/runtime-core" "3.5.11"
|
||||||
|
"@vue/shared" "3.5.11"
|
||||||
|
csstype "^3.1.3"
|
||||||
|
|
||||||
|
"@vue/server-renderer@3.5.11":
|
||||||
|
version "3.5.11"
|
||||||
|
resolved "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.11.tgz"
|
||||||
|
integrity sha512-cVOwYBxR7Wb1B1FoxYvtjJD8X/9E5nlH4VSkJy2uMA1MzYNdzAAB//l8nrmN9py/4aP+3NjWukf9PZ3TeWULaA==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-ssr" "3.5.11"
|
||||||
|
"@vue/shared" "3.5.11"
|
||||||
|
|
||||||
|
"@vue/shared@3.5.11":
|
||||||
|
version "3.5.11"
|
||||||
|
resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.5.11.tgz"
|
||||||
|
integrity sha512-W8GgysJVnFo81FthhzurdRAWP/byq3q2qIw70e0JWblzVhjgOMiC2GyovXrZTFQJnFVryYaKGP3Tc9vYzYm6PQ==
|
||||||
|
|
||||||
|
csstype@^3.1.3:
|
||||||
|
version "3.1.3"
|
||||||
|
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
|
||||||
|
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
||||||
|
|
||||||
|
entities@^4.5.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz"
|
||||||
|
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||||
|
|
||||||
|
esbuild@^0.21.3:
|
||||||
|
version "0.21.5"
|
||||||
|
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz"
|
||||||
|
integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
|
||||||
|
optionalDependencies:
|
||||||
|
"@esbuild/aix-ppc64" "0.21.5"
|
||||||
|
"@esbuild/android-arm" "0.21.5"
|
||||||
|
"@esbuild/android-arm64" "0.21.5"
|
||||||
|
"@esbuild/android-x64" "0.21.5"
|
||||||
|
"@esbuild/darwin-arm64" "0.21.5"
|
||||||
|
"@esbuild/darwin-x64" "0.21.5"
|
||||||
|
"@esbuild/freebsd-arm64" "0.21.5"
|
||||||
|
"@esbuild/freebsd-x64" "0.21.5"
|
||||||
|
"@esbuild/linux-arm" "0.21.5"
|
||||||
|
"@esbuild/linux-arm64" "0.21.5"
|
||||||
|
"@esbuild/linux-ia32" "0.21.5"
|
||||||
|
"@esbuild/linux-loong64" "0.21.5"
|
||||||
|
"@esbuild/linux-mips64el" "0.21.5"
|
||||||
|
"@esbuild/linux-ppc64" "0.21.5"
|
||||||
|
"@esbuild/linux-riscv64" "0.21.5"
|
||||||
|
"@esbuild/linux-s390x" "0.21.5"
|
||||||
|
"@esbuild/linux-x64" "0.21.5"
|
||||||
|
"@esbuild/netbsd-x64" "0.21.5"
|
||||||
|
"@esbuild/openbsd-x64" "0.21.5"
|
||||||
|
"@esbuild/sunos-x64" "0.21.5"
|
||||||
|
"@esbuild/win32-arm64" "0.21.5"
|
||||||
|
"@esbuild/win32-ia32" "0.21.5"
|
||||||
|
"@esbuild/win32-x64" "0.21.5"
|
||||||
|
|
||||||
|
estree-walker@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz"
|
||||||
|
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
||||||
|
|
||||||
|
magic-string@^0.30.11:
|
||||||
|
version "0.30.11"
|
||||||
|
resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz"
|
||||||
|
integrity sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||||
|
|
||||||
|
nanoid@^3.3.7:
|
||||||
|
version "3.3.7"
|
||||||
|
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz"
|
||||||
|
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
|
||||||
|
|
||||||
|
picocolors@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz"
|
||||||
|
integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==
|
||||||
|
|
||||||
|
postcss@^8.4.43, postcss@^8.4.47:
|
||||||
|
version "8.4.47"
|
||||||
|
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz"
|
||||||
|
integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
|
||||||
|
dependencies:
|
||||||
|
nanoid "^3.3.7"
|
||||||
|
picocolors "^1.1.0"
|
||||||
|
source-map-js "^1.2.1"
|
||||||
|
|
||||||
|
rollup@^4.20.0:
|
||||||
|
version "4.24.0"
|
||||||
|
resolved "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz"
|
||||||
|
integrity sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==
|
||||||
|
dependencies:
|
||||||
|
"@types/estree" "1.0.6"
|
||||||
|
optionalDependencies:
|
||||||
|
"@rollup/rollup-android-arm-eabi" "4.24.0"
|
||||||
|
"@rollup/rollup-android-arm64" "4.24.0"
|
||||||
|
"@rollup/rollup-darwin-arm64" "4.24.0"
|
||||||
|
"@rollup/rollup-darwin-x64" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-arm64-gnu" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-arm64-musl" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-powerpc64le-gnu" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-s390x-gnu" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-x64-gnu" "4.24.0"
|
||||||
|
"@rollup/rollup-linux-x64-musl" "4.24.0"
|
||||||
|
"@rollup/rollup-win32-arm64-msvc" "4.24.0"
|
||||||
|
"@rollup/rollup-win32-ia32-msvc" "4.24.0"
|
||||||
|
"@rollup/rollup-win32-x64-msvc" "4.24.0"
|
||||||
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
source-map-js@^1.2.0, source-map-js@^1.2.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz"
|
||||||
|
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||||
|
|
||||||
|
to-fast-properties@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz"
|
||||||
|
integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==
|
||||||
|
|
||||||
|
vite@^5.0.0, vite@^5.4.8:
|
||||||
|
version "5.4.8"
|
||||||
|
resolved "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz"
|
||||||
|
integrity sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==
|
||||||
|
dependencies:
|
||||||
|
esbuild "^0.21.3"
|
||||||
|
postcss "^8.4.43"
|
||||||
|
rollup "^4.20.0"
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents "~2.3.3"
|
||||||
|
|
||||||
|
vue@^3.2.25, vue@^3.5.10, vue@3.5.11:
|
||||||
|
version "3.5.11"
|
||||||
|
resolved "https://registry.npmjs.org/vue/-/vue-3.5.11.tgz"
|
||||||
|
integrity sha512-/8Wurrd9J3lb72FTQS7gRMNQD4nztTtKPmuDuPuhqXmmpD6+skVjAeahNpVzsuky6Sy9gy7wn8UadqPtt9SQIg==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-dom" "3.5.11"
|
||||||
|
"@vue/compiler-sfc" "3.5.11"
|
||||||
|
"@vue/runtime-dom" "3.5.11"
|
||||||
|
"@vue/server-renderer" "3.5.11"
|
||||||
|
"@vue/shared" "3.5.11"
|
Loading…
Reference in new issue