修复考勤管理bug并加强了信息保护
This commit is contained in:
@@ -44,7 +44,8 @@ class Settings:
|
|||||||
|
|
||||||
JWT_SECRET_KEY: str = os.getenv("JWT_SECRET_KEY", "")
|
JWT_SECRET_KEY: str = os.getenv("JWT_SECRET_KEY", "")
|
||||||
JWT_ALGORITHM: str = os.getenv("JWT_ALGORITHM", "HS256")
|
JWT_ALGORITHM: str = os.getenv("JWT_ALGORITHM", "HS256")
|
||||||
JWT_EXPIRE_MINUTES: int = int(os.getenv("JWT_EXPIRE_MINUTES", "30"))
|
JWT_EXPIRE_MINUTES: int = int(os.getenv("JWT_EXPIRE_MINUTES", "60"))
|
||||||
|
JWT_IDLE_TIMEOUT_MINUTES: int = int(os.getenv("JWT_IDLE_TIMEOUT_MINUTES", "10"))
|
||||||
|
|
||||||
PASSWORD_SALT: str = os.getenv("PASSWORD_SALT", "")
|
PASSWORD_SALT: str = os.getenv("PASSWORD_SALT", "")
|
||||||
DEBUG_ENABLED: bool = os.getenv("DEBUG_ENABLED", "False").lower() == "true"
|
DEBUG_ENABLED: bool = os.getenv("DEBUG_ENABLED", "False").lower() == "true"
|
||||||
@@ -63,7 +64,7 @@ class Settings:
|
|||||||
MONITOR_MAX_SUBTRACT: int = int(os.getenv("MONITOR_MAX_SUBTRACT", "-5"))
|
MONITOR_MAX_SUBTRACT: int = int(os.getenv("MONITOR_MAX_SUBTRACT", "-5"))
|
||||||
|
|
||||||
STUDY_COMMISSIONER_MAX_POINTS: int = int(os.getenv("STUDY_COMMISSIONER_MAX_POINTS", "5"))
|
STUDY_COMMISSIONER_MAX_POINTS: int = int(os.getenv("STUDY_COMMISSIONER_MAX_POINTS", "5"))
|
||||||
ATTENDANCE_REP_MAX_POINTS: int = int(os.getenv("ATTENDANCE_REP_MAX_POINTS", "5"))
|
ATTENDANCE_REP_MAX_POINTS: int = int(os.getenv("ATTENDANCE_REP_MAX_POINTS", "8"))
|
||||||
LABOR_REP_MAX_POINTS: int = int(os.getenv("LABOR_REP_MAX_POINTS", "1"))
|
LABOR_REP_MAX_POINTS: int = int(os.getenv("LABOR_REP_MAX_POINTS", "1"))
|
||||||
VOLUNTEER_REP_MAX_POINTS: int = int(os.getenv("VOLUNTEER_REP_MAX_POINTS", "5"))
|
VOLUNTEER_REP_MAX_POINTS: int = int(os.getenv("VOLUNTEER_REP_MAX_POINTS", "5"))
|
||||||
|
|
||||||
|
|||||||
@@ -92,15 +92,16 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|||||||
logger.warning(f"[Auth] {path} - Redis Token不匹配, user_id={user_id}, stored={'有' if stored_token else '无'}")
|
logger.warning(f"[Auth] {path} - Redis Token不匹配, user_id={user_id}, stored={'有' if stored_token else '无'}")
|
||||||
return self._cors_response(request, 401, "令牌已失效,请重新登录")
|
return self._cors_response(request, 401, "令牌已失效,请重新登录")
|
||||||
|
|
||||||
|
# 将用户信息存储到request.state
|
||||||
# 将用户信息存储到request.state
|
# 将用户信息存储到request.state
|
||||||
request.state.user_id = payload.get("user_id")
|
request.state.user_id = payload.get("user_id")
|
||||||
request.state.username = payload.get("username")
|
request.state.username = payload.get("username")
|
||||||
|
request.state.real_name = payload.get("real_name") or payload.get("username")
|
||||||
request.state.user_type = payload.get("user_type")
|
request.state.user_type = payload.get("user_type")
|
||||||
request.state.student_id = payload.get("student_id")
|
request.state.student_id = payload.get("student_id")
|
||||||
request.state.role = payload.get("role")
|
request.state.role = payload.get("role")
|
||||||
|
# 刷新Token过期时间(空闲超时:10分钟无操作则需重新登录)
|
||||||
# 刷新Token过期时间
|
await RedisClient.expire(f"user_token:{user_id}", settings.JWT_IDLE_TIMEOUT_MINUTES * 60)
|
||||||
await RedisClient.expire(f"user_token:{user_id}", settings.JWT_EXPIRE_MINUTES * 60)
|
|
||||||
|
|
||||||
logger.debug(f"[Auth] {path} - 认证成功, user_id={user_id}, username={payload.get('username')}")
|
logger.debug(f"[Auth] {path} - 认证成功, user_id={user_id}, username={payload.get('username')}")
|
||||||
|
|
||||||
@@ -148,6 +149,7 @@ async def get_current_user(request: Request) -> Dict[str, Any]:
|
|||||||
return {
|
return {
|
||||||
"user_id": request.state.user_id,
|
"user_id": request.state.user_id,
|
||||||
"username": request.state.username,
|
"username": request.state.username,
|
||||||
|
"real_name": getattr(request.state, 'real_name', None) or request.state.username,
|
||||||
"user_type": request.state.user_type,
|
"user_type": request.state.user_type,
|
||||||
"student_id": request.state.student_id,
|
"student_id": request.state.student_id,
|
||||||
"role": request.state.role
|
"role": request.state.role
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ class PermissionChecker:
|
|||||||
检查是否可以撤销扣分记录
|
检查是否可以撤销扣分记录
|
||||||
班主任:可以撤销任何记录
|
班主任:可以撤销任何记录
|
||||||
班长:可以撤销任何记录
|
班长:可以撤销任何记录
|
||||||
|
考勤委员:可以撤销自己的记录
|
||||||
其他:只能撤销自己的记录
|
其他:只能撤销自己的记录
|
||||||
"""
|
"""
|
||||||
sql = "SELECT recorder_id FROM conduct_records WHERE record_id = %s"
|
sql = "SELECT recorder_id FROM conduct_records WHERE record_id = %s"
|
||||||
|
|||||||
@@ -83,20 +83,24 @@ class ConductModel:
|
|||||||
start_date: str = None,
|
start_date: str = None,
|
||||||
end_date: str = None,
|
end_date: str = None,
|
||||||
student_id: int = None,
|
student_id: int = None,
|
||||||
include_revoked: bool = True
|
include_revoked: bool = True,
|
||||||
|
related_type: str = None
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""获取所有记录(班主任/班长专用)"""
|
"""获取所有记录(班主任/班长专用)"""
|
||||||
# 空字符串转为None
|
# 空字符串转为None
|
||||||
if start_date == "":
|
if start_date == "":
|
||||||
start_date = None
|
start_date = None
|
||||||
if end_date == "":
|
if end_date == "":
|
||||||
end_date = None
|
end_date = None
|
||||||
|
if related_type == "":
|
||||||
|
related_type = None
|
||||||
sql = """
|
sql = """
|
||||||
SELECT cr.*, s.name as student_name, s.student_no, u.real_name as recorder_name
|
SELECT cr.*, s.name as student_name, s.student_no, u.real_name as recorder_name,
|
||||||
|
ru.real_name as revoker_name
|
||||||
FROM conduct_records cr
|
FROM conduct_records cr
|
||||||
JOIN students s ON cr.student_id = s.student_id
|
JOIN students s ON cr.student_id = s.student_id
|
||||||
JOIN users u ON cr.recorder_id = u.user_id
|
JOIN users u ON cr.recorder_id = u.user_id
|
||||||
|
LEFT JOIN users ru ON cr.revoked_by = ru.user_id
|
||||||
WHERE 1=1
|
WHERE 1=1
|
||||||
"""
|
"""
|
||||||
if not include_revoked:
|
if not include_revoked:
|
||||||
@@ -115,6 +119,10 @@ class ConductModel:
|
|||||||
sql += " AND DATE(cr.created_at) <= %s"
|
sql += " AND DATE(cr.created_at) <= %s"
|
||||||
params.append(end_date)
|
params.append(end_date)
|
||||||
|
|
||||||
|
if related_type:
|
||||||
|
sql += " AND cr.related_type = %s"
|
||||||
|
params.append(related_type)
|
||||||
|
|
||||||
sql += " ORDER BY cr.created_at DESC LIMIT %s OFFSET %s"
|
sql += " ORDER BY cr.created_at DESC LIMIT %s OFFSET %s"
|
||||||
params.extend([limit, offset])
|
params.extend([limit, offset])
|
||||||
|
|
||||||
@@ -128,7 +136,7 @@ class ConductModel:
|
|||||||
page: int = 1,
|
page: int = 1,
|
||||||
page_size: int = 20
|
page_size: int = 20
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""获取分组后的操行分记录(同批次合并)"""
|
"""获取分组后的操行分记录(同批次合并)"""
|
||||||
if start_date == "":
|
if start_date == "":
|
||||||
start_date = None
|
start_date = None
|
||||||
if end_date == "":
|
if end_date == "":
|
||||||
@@ -215,7 +223,7 @@ class ConductModel:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def restore_record(record_id: int, restorer_id: int) -> bool:
|
async def restore_record(record_id: int, restorer_id: int) -> bool:
|
||||||
"""反撤销(恢复)已撤销的记录"""
|
"""反撤销(恢复)已撤销的记录"""
|
||||||
try:
|
try:
|
||||||
sql = """
|
sql = """
|
||||||
UPDATE conduct_records
|
UPDATE conduct_records
|
||||||
|
|||||||
@@ -105,4 +105,11 @@ class UserModel:
|
|||||||
"""更新用户状态(0=禁用,1=启用)"""
|
"""更新用户状态(0=禁用,1=启用)"""
|
||||||
sql = "UPDATE users SET status = %s WHERE user_id = %s"
|
sql = "UPDATE users SET status = %s WHERE user_id = %s"
|
||||||
result = await execute_update(sql, (status, user_id))
|
result = await execute_update(sql, (status, user_id))
|
||||||
|
return result > 0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def update_real_name(user_id: int, real_name: str) -> bool:
|
||||||
|
"""更新用户真实姓名"""
|
||||||
|
sql = "UPDATE users SET real_name = %s WHERE user_id = %s"
|
||||||
|
result = await execute_update(sql, (real_name, user_id))
|
||||||
return result > 0
|
return result > 0
|
||||||
@@ -87,7 +87,7 @@ async def import_students(request: Request, file: UploadFile = File(...)):
|
|||||||
initial_points=60
|
initial_points=60
|
||||||
)
|
)
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="import_students",
|
operator_role="班主任", operation_type="import_students",
|
||||||
target_type="student",
|
target_type="student",
|
||||||
details=f"批量导入: 成功{result['success_count']}人, 失败{result['failed_count']}人",
|
details=f"批量导入: 成功{result['success_count']}人, 失败{result['failed_count']}人",
|
||||||
@@ -113,7 +113,7 @@ async def add_student(request: Request, req: AddStudentRequest):
|
|||||||
)
|
)
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="add_student",
|
operator_role="班主任", operation_type="add_student",
|
||||||
target_type="student", target_id=result.get("student_id"),
|
target_type="student", target_id=result.get("student_id"),
|
||||||
details=f"新增学生: {req.name}({req.student_no})",
|
details=f"新增学生: {req.name}({req.student_no})",
|
||||||
@@ -192,7 +192,7 @@ async def add_conduct_points(request: Request, req: AddPointsRequest):
|
|||||||
if result["success"]:
|
if result["success"]:
|
||||||
role = await PermissionChecker.get_user_role(user["user_id"])
|
role = await PermissionChecker.get_user_role(user["user_id"])
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role=role, operation_type="add_points",
|
operator_role=role, operation_type="add_points",
|
||||||
target_type="conduct",
|
target_type="conduct",
|
||||||
details=f"批量加减分: {req.points_change}分, 原因: {req.reason}, 对象: {req.student_ids}",
|
details=f"批量加减分: {req.points_change}分, 原因: {req.reason}, 对象: {req.student_ids}",
|
||||||
@@ -215,7 +215,7 @@ async def revoke_conduct_record(request: Request, req: RevokeRequest):
|
|||||||
role = await PermissionChecker.get_user_role(user["user_id"])
|
role = await PermissionChecker.get_user_role(user["user_id"])
|
||||||
record = result.get("record", {})
|
record = result.get("record", {})
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role=role, operation_type="revoke_record",
|
operator_role=role, operation_type="revoke_record",
|
||||||
target_type="conduct", target_id=req.record_id,
|
target_type="conduct", target_id=req.record_id,
|
||||||
details=(
|
details=(
|
||||||
@@ -242,7 +242,7 @@ async def restore_conduct_record(request: Request, req: RevokeRequest):
|
|||||||
if result["success"]:
|
if result["success"]:
|
||||||
record = result.get("record", {})
|
record = result.get("record", {})
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="restore_record",
|
operator_role="班主任", operation_type="restore_record",
|
||||||
target_type="conduct", target_id=req.record_id,
|
target_type="conduct", target_id=req.record_id,
|
||||||
details=(
|
details=(
|
||||||
@@ -266,7 +266,8 @@ async def get_conduct_history(
|
|||||||
page_size: int = Query(20, ge=1, le=1000),
|
page_size: int = Query(20, ge=1, le=1000),
|
||||||
start_date: Optional[str] = None,
|
start_date: Optional[str] = None,
|
||||||
end_date: Optional[str] = None,
|
end_date: Optional[str] = None,
|
||||||
grouped: bool = Query(False)
|
grouped: bool = Query(False),
|
||||||
|
related_type: Optional[str] = None
|
||||||
):
|
):
|
||||||
"""获取操行分历史记录"""
|
"""获取操行分历史记录"""
|
||||||
try:
|
try:
|
||||||
@@ -278,7 +279,8 @@ async def get_conduct_history(
|
|||||||
page_size=page_size,
|
page_size=page_size,
|
||||||
start_date=start_date,
|
start_date=start_date,
|
||||||
end_date=end_date,
|
end_date=end_date,
|
||||||
grouped=grouped
|
grouped=grouped,
|
||||||
|
related_type=related_type
|
||||||
)
|
)
|
||||||
return success_response(data=result)
|
return success_response(data=result)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -335,7 +337,7 @@ async def create_assignment(
|
|||||||
)
|
)
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="create_assignment",
|
operator_role="班主任", operation_type="create_assignment",
|
||||||
target_type="homework",
|
target_type="homework",
|
||||||
details=f"发布作业: {title}",
|
details=f"发布作业: {title}",
|
||||||
@@ -362,7 +364,7 @@ async def update_submission_status(request: Request, req: UpdateHomeworkStatusRe
|
|||||||
)
|
)
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role=role, operation_type="update_submission",
|
operator_role=role, operation_type="update_submission",
|
||||||
target_type="homework", target_id=req.submission_id,
|
target_type="homework", target_id=req.submission_id,
|
||||||
details=f"状态: {req.status}",
|
details=f"状态: {req.status}",
|
||||||
@@ -393,7 +395,7 @@ async def add_attendance(request: Request, req: AddAttendanceRequest):
|
|||||||
)
|
)
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role=role, operation_type="add_attendance",
|
operator_role=role, operation_type="add_attendance",
|
||||||
target_type="attendance",
|
target_type="attendance",
|
||||||
details=f"学生ID: {req.student_id}, 日期: {req.date}, 状态: {req.status}",
|
details=f"学生ID: {req.student_id}, 日期: {req.date}, 状态: {req.status}",
|
||||||
@@ -440,7 +442,7 @@ async def add_admin(request: Request, req: AddAdminRequest):
|
|||||||
)
|
)
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="add_admin",
|
operator_role="班主任", operation_type="add_admin",
|
||||||
target_type="admin",
|
target_type="admin",
|
||||||
details=f"新增管理员: {req.real_name}({req.username}), 角色: {req.role_type}",
|
details=f"新增管理员: {req.real_name}({req.username}), 角色: {req.role_type}",
|
||||||
@@ -477,16 +479,24 @@ async def update_admin(request: Request, user_id: int, req: UpdateAdminRequest):
|
|||||||
return error_response(message="无效的角色类型", code=400)
|
return error_response(message="无效的角色类型", code=400)
|
||||||
|
|
||||||
from models.admin_role import AdminRoleModel
|
from models.admin_role import AdminRoleModel
|
||||||
|
from models.user import UserModel
|
||||||
|
|
||||||
|
# 更新角色
|
||||||
result = await AdminRoleModel.update_role(
|
result = await AdminRoleModel.update_role(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
role_type=req.role_type
|
role_type=req.role_type
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 更新姓名
|
||||||
|
if req.real_name:
|
||||||
|
await UserModel.update_real_name(user_id, req.real_name)
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="update_admin",
|
operator_role="班主任", operation_type="update_admin",
|
||||||
target_type="admin", target_id=user_id,
|
target_type="admin", target_id=user_id,
|
||||||
details=f"更新管理员角色为: {req.role_type}",
|
details=f"更新管理员角色为: {req.role_type}, 姓名: {req.real_name}",
|
||||||
ip=request.client.host
|
ip=request.client.host
|
||||||
)
|
)
|
||||||
return success_response(message="管理员更新成功")
|
return success_response(message="管理员更新成功")
|
||||||
@@ -515,7 +525,7 @@ async def delete_admin(request: Request, user_id: int):
|
|||||||
# 再删除用户账号(软删除,将状态设为禁用)
|
# 再删除用户账号(软删除,将状态设为禁用)
|
||||||
await UserModel.update_status(user_id, 0)
|
await UserModel.update_status(user_id, 0)
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="delete_admin",
|
operator_role="班主任", operation_type="delete_admin",
|
||||||
target_type="admin", target_id=user_id,
|
target_type="admin", target_id=user_id,
|
||||||
details=f"删除管理员: ID={user_id}",
|
details=f"删除管理员: ID={user_id}",
|
||||||
@@ -548,7 +558,7 @@ async def reset_admin_password(request: Request, user_id: int, req: ResetPasswor
|
|||||||
updated = await UserModel.update_password(user_id, req.new_password)
|
updated = await UserModel.update_password(user_id, req.new_password)
|
||||||
if updated:
|
if updated:
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="reset_password",
|
operator_role="班主任", operation_type="reset_password",
|
||||||
target_type="admin", target_id=user_id,
|
target_type="admin", target_id=user_id,
|
||||||
details=f"重置管理员密码: {target_user['real_name']}({target_user['username']})",
|
details=f"重置管理员密码: {target_user['real_name']}({target_user['username']})",
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ async def create_semester(request: Request, req: CreateSemesterRequest):
|
|||||||
)
|
)
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="create_semester",
|
operator_role="班主任", operation_type="create_semester",
|
||||||
target_type="semester", target_id=result.get("semester_id"),
|
target_type="semester", target_id=result.get("semester_id"),
|
||||||
details=f"创建学期: {req.semester_name}",
|
details=f"创建学期: {req.semester_name}",
|
||||||
@@ -92,7 +92,7 @@ async def activate_semester(request: Request, semester_id: int):
|
|||||||
)
|
)
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="activate_semester",
|
operator_role="班主任", operation_type="activate_semester",
|
||||||
target_type="semester", target_id=semester_id,
|
target_type="semester", target_id=semester_id,
|
||||||
details=f"激活学期ID: {semester_id}",
|
details=f"激活学期ID: {semester_id}",
|
||||||
@@ -120,7 +120,7 @@ async def update_semester(request: Request, semester_id: int, req: UpdateSemeste
|
|||||||
)
|
)
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="update_semester",
|
operator_role="班主任", operation_type="update_semester",
|
||||||
target_type="semester", target_id=semester_id,
|
target_type="semester", target_id=semester_id,
|
||||||
details=f"编辑学期ID: {semester_id}",
|
details=f"编辑学期ID: {semester_id}",
|
||||||
@@ -145,7 +145,7 @@ async def delete_semester(request: Request, semester_id: int):
|
|||||||
)
|
)
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="delete_semester",
|
operator_role="班主任", operation_type="delete_semester",
|
||||||
target_type="semester", target_id=semester_id,
|
target_type="semester", target_id=semester_id,
|
||||||
details=f"删除学期ID: {semester_id}",
|
details=f"删除学期ID: {semester_id}",
|
||||||
@@ -170,7 +170,7 @@ async def associate_records(request: Request, semester_id: int):
|
|||||||
)
|
)
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="associate_records",
|
operator_role="班主任", operation_type="associate_records",
|
||||||
target_type="semester", target_id=semester_id,
|
target_type="semester", target_id=semester_id,
|
||||||
details=f"关联数据到学期ID: {semester_id}, 结果: {result.get('data', {})}",
|
details=f"关联数据到学期ID: {semester_id}, 结果: {result.get('data', {})}",
|
||||||
@@ -203,7 +203,7 @@ async def archive_semester(
|
|||||||
if reset_scores:
|
if reset_scores:
|
||||||
log_detail += " 并重置学生操行分"
|
log_detail += " 并重置学生操行分"
|
||||||
await LogService.write_operation_log(
|
await LogService.write_operation_log(
|
||||||
operator_id=user["user_id"], operator_name=user["username"],
|
operator_id=user["user_id"], operator_name=user["real_name"],
|
||||||
operator_role="班主任", operation_type="archive_semester",
|
operator_role="班主任", operation_type="archive_semester",
|
||||||
target_type="semester", target_id=semester_id,
|
target_type="semester", target_id=semester_id,
|
||||||
details=log_detail,
|
details=log_detail,
|
||||||
|
|||||||
@@ -82,11 +82,12 @@ class AttendanceService:
|
|||||||
user = await UserModel.get_by_user_id(recorder_id)
|
user = await UserModel.get_by_user_id(recorder_id)
|
||||||
recorder_name = user.get("real_name", "班主任") if user else "班主任"
|
recorder_name = user.get("real_name", "班主任") if user else "班主任"
|
||||||
# 使用中文状态
|
# 使用中文状态
|
||||||
|
# 使用中文状态
|
||||||
status_text = ATTENDANCE_STATUS_MAP.get(status, status)
|
status_text = ATTENDANCE_STATUS_MAP.get(status, status)
|
||||||
await ConductModel.create_record(
|
await ConductModel.create_record(
|
||||||
student_id=student_id,
|
student_id=student_id,
|
||||||
points_change=points_change,
|
points_change=points_change,
|
||||||
reason=f"考勤异常: {status_text}",
|
reason=f"考勤:{status_text}",
|
||||||
recorder_id=recorder_id,
|
recorder_id=recorder_id,
|
||||||
recorder_name=recorder_name,
|
recorder_name=recorder_name,
|
||||||
related_type="attendance",
|
related_type="attendance",
|
||||||
@@ -98,8 +99,6 @@ class AttendanceService:
|
|||||||
|
|
||||||
# 标记已应用扣分
|
# 标记已应用扣分
|
||||||
await AttendanceModel.mark_deduction_applied(attendance_id)
|
await AttendanceModel.mark_deduction_applied(attendance_id)
|
||||||
await AttendanceModel.mark_deduction_applied(attendance_id)
|
|
||||||
|
|
||||||
logger.info(f"用户[{recorder_id}] 添加考勤记录[{attendance_id}] -> {status}")
|
logger.info(f"用户[{recorder_id}] 添加考勤记录[{attendance_id}] -> {status}")
|
||||||
|
|
||||||
return {"success": True, "message": "考勤记录添加成功"}
|
return {"success": True, "message": "考勤记录添加成功"}
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ class AuthService:
|
|||||||
username=user["username"],
|
username=user["username"],
|
||||||
user_type=user["user_type"],
|
user_type=user["user_type"],
|
||||||
student_id=user["student_id"],
|
student_id=user["student_id"],
|
||||||
role=role
|
role=role,
|
||||||
|
real_name=user["real_name"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# 存储Token到Redis
|
# 存储Token到Redis
|
||||||
|
|||||||
@@ -196,7 +196,8 @@ class ConductService:
|
|||||||
page_size: int = 20,
|
page_size: int = 20,
|
||||||
start_date: Optional[str] = None,
|
start_date: Optional[str] = None,
|
||||||
end_date: Optional[str] = None,
|
end_date: Optional[str] = None,
|
||||||
grouped: bool = False
|
grouped: bool = False,
|
||||||
|
related_type: Optional[str] = None
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""获取历史记录"""
|
"""获取历史记录"""
|
||||||
# 空字符串转为None
|
# 空字符串转为None
|
||||||
@@ -204,6 +205,8 @@ class ConductService:
|
|||||||
start_date = None
|
start_date = None
|
||||||
if end_date == "":
|
if end_date == "":
|
||||||
end_date = None
|
end_date = None
|
||||||
|
if related_type == "":
|
||||||
|
related_type = None
|
||||||
|
|
||||||
role = await PermissionChecker.get_user_role(user_id)
|
role = await PermissionChecker.get_user_role(user_id)
|
||||||
offset = (page - 1) * page_size
|
offset = (page - 1) * page_size
|
||||||
@@ -224,7 +227,8 @@ class ConductService:
|
|||||||
offset=offset,
|
offset=offset,
|
||||||
start_date=start_date,
|
start_date=start_date,
|
||||||
end_date=end_date,
|
end_date=end_date,
|
||||||
student_id=student_id
|
student_id=student_id,
|
||||||
|
related_type=related_type
|
||||||
)
|
)
|
||||||
|
|
||||||
# 获取总数
|
# 获取总数
|
||||||
@@ -240,6 +244,9 @@ class ConductService:
|
|||||||
if end_date:
|
if end_date:
|
||||||
count_conditions.append("DATE(cr.created_at) <= %s")
|
count_conditions.append("DATE(cr.created_at) <= %s")
|
||||||
count_params.append(end_date)
|
count_params.append(end_date)
|
||||||
|
if related_type:
|
||||||
|
count_conditions.append("cr.related_type = %s")
|
||||||
|
count_params.append(related_type)
|
||||||
count_where = " AND ".join(count_conditions)
|
count_where = " AND ".join(count_conditions)
|
||||||
count_sql = f"""
|
count_sql = f"""
|
||||||
SELECT COUNT(*) as total FROM conduct_records cr
|
SELECT COUNT(*) as total FROM conduct_records cr
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class JWTHandler:
|
|||||||
"""JWT Token处理类"""
|
"""JWT Token处理类"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_token(user_id: int, username: str, user_type: str, student_id: int = None, role: str = None) -> str:
|
def create_token(user_id: int, username: str, user_type: str, student_id: int = None, role: str = None, real_name: str = None) -> str:
|
||||||
"""
|
"""
|
||||||
创建JWT Token
|
创建JWT Token
|
||||||
"""
|
"""
|
||||||
@@ -33,6 +33,7 @@ class JWTHandler:
|
|||||||
'user_type': user_type,
|
'user_type': user_type,
|
||||||
'student_id': student_id,
|
'student_id': student_id,
|
||||||
'role': role,
|
'role': role,
|
||||||
|
'real_name': real_name,
|
||||||
'exp': datetime.utcnow() + timedelta(minutes=settings.JWT_EXPIRE_MINUTES),
|
'exp': datetime.utcnow() + timedelta(minutes=settings.JWT_EXPIRE_MINUTES),
|
||||||
'iat': datetime.utcnow(),
|
'iat': datetime.utcnow(),
|
||||||
'iss': settings.APP_NAME
|
'iss': settings.APP_NAME
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ include __DIR__ . '/../includes/header.php';
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>姓名</label>
|
<label>姓名</label>
|
||||||
<input type="text" id="editAdminRealName" disabled>
|
<input type="text" id="editAdminRealName" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>角色</label>
|
<label>角色</label>
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ include __DIR__ . '/../includes/header.php';
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.student-cell { display: flex; flex-direction: column; align-items: center; }
|
||||||
|
.student-cell-name { font-size: 14px; font-weight: 500; }
|
||||||
|
.student-cell-no { font-size: 11px; color: #999; }
|
||||||
|
</style>
|
||||||
<script>
|
<script>
|
||||||
let currentStatus = 'absent';
|
let currentStatus = 'absent';
|
||||||
let studentsData = [];
|
let studentsData = [];
|
||||||
@@ -116,14 +121,16 @@ async function loadStudents() {
|
|||||||
|
|
||||||
// 渲染学生方格
|
// 渲染学生方格
|
||||||
function renderStudentGrid() {
|
function renderStudentGrid() {
|
||||||
|
const currentSlot = document.getElementById('attendanceSlot').value;
|
||||||
let html = '';
|
let html = '';
|
||||||
studentsData.forEach(student => {
|
studentsData.forEach(student => {
|
||||||
const hasRecord = existingRecords.some(r => r.student_id === student.student_id);
|
const hasRecord = existingRecords.some(r => r.student_id === student.student_id && (!r.slot || r.slot === currentSlot));
|
||||||
html += `<div class="student-cell${hasRecord ? ' has-record' : ''}"
|
html += `<div class="student-cell${hasRecord ? ' has-record' : ''}"
|
||||||
data-id="${student.student_id}"
|
data-id="${student.student_id}"
|
||||||
data-name="${escapeHtml(student.name)}"
|
data-name="${escapeHtml(student.name)}"
|
||||||
onclick="toggleStudent(this)">
|
onclick="toggleStudent(this)">
|
||||||
${escapeHtml(student.name)}
|
<span class="student-cell-name">${escapeHtml(student.name)}</span>
|
||||||
|
<span class="student-cell-no">${escapeHtml(student.student_no)}</span>
|
||||||
</div>`;
|
</div>`;
|
||||||
});
|
});
|
||||||
if (studentsData.length === 0) {
|
if (studentsData.length === 0) {
|
||||||
@@ -154,7 +161,8 @@ function deselectAllStudents() {
|
|||||||
// 加载当天已有考勤记录(用于标记 .has-record)
|
// 加载当天已有考勤记录(用于标记 .has-record)
|
||||||
async function loadExistingRecords() {
|
async function loadExistingRecords() {
|
||||||
const date = document.getElementById('attendanceDate').value;
|
const date = document.getElementById('attendanceDate').value;
|
||||||
const res = await apiGet('/api/admin/attendance/records', { date });
|
const slot = document.getElementById('attendanceSlot').value;
|
||||||
|
const res = await apiGet('/api/admin/attendance/records', { date, slot });
|
||||||
if (res && res.success) {
|
if (res && res.success) {
|
||||||
existingRecords = res.data.records || [];
|
existingRecords = res.data.records || [];
|
||||||
renderStudentGrid(); // 重新渲染以标记 has-record
|
renderStudentGrid(); // 重新渲染以标记 has-record
|
||||||
@@ -232,11 +240,14 @@ async function loadAttendanceRecords() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 日期变化时重新加载
|
// 日期或时段变化时重新加载
|
||||||
document.getElementById('attendanceDate').addEventListener('change', function() {
|
document.getElementById('attendanceDate').addEventListener('change', function() {
|
||||||
loadExistingRecords();
|
loadExistingRecords();
|
||||||
loadAttendanceRecords();
|
loadAttendanceRecords();
|
||||||
});
|
});
|
||||||
|
document.getElementById('attendanceSlot').addEventListener('change', function() {
|
||||||
|
loadExistingRecords();
|
||||||
|
});
|
||||||
|
|
||||||
// 页面初始化
|
// 页面初始化
|
||||||
loadStudents();
|
loadStudents();
|
||||||
|
|||||||
@@ -41,6 +41,15 @@ include __DIR__ . '/../includes/header.php';
|
|||||||
<option value="">全部</option>
|
<option value="">全部</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="filter-group">
|
||||||
|
<label>扣分类型</label>
|
||||||
|
<select id="historyRelatedType">
|
||||||
|
<option value="">全部</option>
|
||||||
|
<option value="manual">手动加减分</option>
|
||||||
|
<option value="homework">作业</option>
|
||||||
|
<option value="attendance">考勤</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<button class="btn btn-primary" onclick="loadHistory(1)">查询</button>
|
<button class="btn btn-primary" onclick="loadHistory(1)">查询</button>
|
||||||
<?php if ($role === '班主任'): ?>
|
<?php if ($role === '班主任'): ?>
|
||||||
<button class="btn btn-secondary" onclick="exportHistoryRecords()">导出历史记录</button>
|
<button class="btn btn-secondary" onclick="exportHistoryRecords()">导出历史记录</button>
|
||||||
@@ -88,12 +97,15 @@ async function loadHistory(page = 1) {
|
|||||||
const endDate = document.getElementById('historyEndDate').value;
|
const endDate = document.getElementById('historyEndDate').value;
|
||||||
const studentId = document.getElementById('historyStudentId').value;
|
const studentId = document.getElementById('historyStudentId').value;
|
||||||
|
|
||||||
|
const relatedType = document.getElementById('historyRelatedType').value;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
page, page_size: 20,
|
page, page_size: 20,
|
||||||
start_date: startDate,
|
start_date: startDate,
|
||||||
end_date: endDate
|
end_date: endDate
|
||||||
};
|
};
|
||||||
if (studentId) params.student_id = studentId;
|
if (studentId) params.student_id = studentId;
|
||||||
|
if (relatedType) params.related_type = relatedType;
|
||||||
|
|
||||||
const res = await apiGet('/api/admin/conduct/history', params);
|
const res = await apiGet('/api/admin/conduct/history', params);
|
||||||
|
|
||||||
@@ -110,13 +122,15 @@ async function loadHistory(page = 1) {
|
|||||||
<td>${escapeHtml(record.recorder_name)}</td>`;
|
<td>${escapeHtml(record.recorder_name)}</td>`;
|
||||||
<?php if ($role === '班主任'): ?>
|
<?php if ($role === '班主任'): ?>
|
||||||
if (record.is_revoked == 1) {
|
if (record.is_revoked == 1) {
|
||||||
html += `<td><span class="text-muted" style="margin-right:4px;">已撤销</span><button class="btn btn-sm btn-secondary" onclick="restoreRecord(${record.record_id})">反撤销</button></td>`;
|
const revokerInfo = record.revoker_name ? `由 ${escapeHtml(record.revoker_name)} 撤销` : '已撤销';
|
||||||
|
html += `<td><span class="text-muted" style="margin-right:4px;">${revokerInfo}</span><button class="btn btn-sm btn-secondary" onclick="restoreRecord(${record.record_id})">反撤销</button></td>`;
|
||||||
} else {
|
} else {
|
||||||
html += `<td><button class="btn btn-sm btn-danger" onclick="revokeRecord(${record.record_id})">撤销</button></td>`;
|
html += `<td><button class="btn btn-sm btn-danger" onclick="revokeRecord(${record.record_id})">撤销</button></td>`;
|
||||||
}
|
}
|
||||||
<?php elseif ($role === '班长'): ?>
|
<?php elseif ($role === '班长'): ?>
|
||||||
if (record.is_revoked == 1) {
|
if (record.is_revoked == 1) {
|
||||||
html += `<td><span class="text-muted">已撤销</span></td>`;
|
const revokerInfo = record.revoker_name ? `由 ${escapeHtml(record.revoker_name)} 撤销` : '已撤销';
|
||||||
|
html += `<td><span class="text-muted">${revokerInfo}</span></td>`;
|
||||||
} else {
|
} else {
|
||||||
html += `<td><button class="btn btn-sm btn-danger" onclick="revokeRecord(${record.record_id})">撤销</button></td>`;
|
html += `<td><button class="btn btn-sm btn-danger" onclick="revokeRecord(${record.record_id})">撤销</button></td>`;
|
||||||
}
|
}
|
||||||
@@ -164,10 +178,12 @@ async function exportHistoryRecords() {
|
|||||||
showToast('正在导出历史记录...', 'info');
|
showToast('正在导出历史记录...', 'info');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const relatedType = document.getElementById('historyRelatedType').value;
|
||||||
const params = { page: 1, page_size: 1000 };
|
const params = { page: 1, page_size: 1000 };
|
||||||
if (startDate) params.start_date = startDate;
|
if (startDate) params.start_date = startDate;
|
||||||
if (endDate) params.end_date = endDate;
|
if (endDate) params.end_date = endDate;
|
||||||
if (studentId) params.student_id = studentId;
|
if (studentId) params.student_id = studentId;
|
||||||
|
if (relatedType) params.related_type = relatedType;
|
||||||
|
|
||||||
const res = await apiGet('/api/admin/conduct/history', params);
|
const res = await apiGet('/api/admin/conduct/history', params);
|
||||||
if (res && res.success && res.data.records) {
|
if (res && res.success && res.data.records) {
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ async function loadStudents(page = 1) {
|
|||||||
<td>${escapeHtml(student.student_no)}</td>
|
<td>${escapeHtml(student.student_no)}</td>
|
||||||
<td><a href="/admin/history.php?student_id=${student.student_id}" style="color: #3498db; text-decoration: none;">${escapeHtml(student.name)}</a></td>
|
<td><a href="/admin/history.php?student_id=${student.student_id}" style="color: #3498db; text-decoration: none;">${escapeHtml(student.name)}</a></td>
|
||||||
<td>${student.total_points}</td>
|
<td>${student.total_points}</td>
|
||||||
${userRole === '班主任' ? `<td>${student.parent_phone || '-'}</td>` : ''}
|
${userRole === '班主任' ? `<td>${student.parent_phone ? student.parent_phone.slice(0,3) + '******' + student.parent_phone.slice(-2) : '-'}</td>` : ''}
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-sm btn-primary" onclick="showSinglePointsModal(${student.student_id}, '${escapeHtml(student.name)}')">加减分</button>
|
<button class="btn btn-sm btn-primary" onclick="showSinglePointsModal(${student.student_id}, '${escapeHtml(student.name)}')">加减分</button>
|
||||||
${userRole === '班主任' ? `<button class="btn btn-sm btn-secondary" onclick="showEditStudentModal(${student.student_id}, '${escapeHtml(student.student_no)}', '${escapeHtml(student.name)}', '${escapeHtml(student.parent_phone || '')}')">编辑</button>
|
${userRole === '班主任' ? `<button class="btn btn-sm btn-secondary" onclick="showEditStudentModal(${student.student_id}, '${escapeHtml(student.student_no)}', '${escapeHtml(student.name)}', '${escapeHtml(student.parent_phone || '')}')">编辑</button>
|
||||||
|
|||||||
@@ -63,6 +63,35 @@ include __DIR__ . '/../includes/header.php';
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 编辑科目模态框 -->
|
||||||
|
<div id="editSubjectModal" class="modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3>编辑科目</h3>
|
||||||
|
<button class="modal-close" onclick="closeModal('editSubjectModal')">×</button>
|
||||||
|
</div>
|
||||||
|
<form onsubmit="event.preventDefault(); submitEditSubject()">
|
||||||
|
<input type="hidden" id="editSubjectId">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>科目名称</label>
|
||||||
|
<input type="text" id="editSubjectName" required placeholder="例如:语文、数学">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>科目代码</label>
|
||||||
|
<input type="text" id="editSubjectCode" placeholder="例如:CHI、MATH">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>排序序号</label>
|
||||||
|
<input type="number" id="editSubjectSortOrder" placeholder="数字越小越靠前" min="0">
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="submit" class="btn btn-primary">保存</button>
|
||||||
|
<button type="button" class="btn" onclick="closeModal('editSubjectModal')">取消</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.subject-list {
|
.subject-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -113,6 +142,7 @@ async function loadSubjects() {
|
|||||||
<span class="subject-status ${sub.is_active ? 'subject-status-active' : 'subject-status-inactive'}">
|
<span class="subject-status ${sub.is_active ? 'subject-status-active' : 'subject-status-inactive'}">
|
||||||
${sub.is_active ? '启用' : '禁用'}
|
${sub.is_active ? '启用' : '禁用'}
|
||||||
</span>
|
</span>
|
||||||
|
<button class="btn btn-sm btn-primary" onclick="showEditSubjectModal(${sub.subject_id}, '${escapeHtml(sub.subject_name)}', '${escapeHtml(sub.subject_code || '')}', ${sub.sort_order || 0})">编辑</button>
|
||||||
<button class="btn btn-sm" onclick="toggleSubject(${sub.subject_id}, ${!sub.is_active})">
|
<button class="btn btn-sm" onclick="toggleSubject(${sub.subject_id}, ${!sub.is_active})">
|
||||||
${sub.is_active ? '禁用' : '启用'}
|
${sub.is_active ? '禁用' : '启用'}
|
||||||
</button>
|
</button>
|
||||||
@@ -161,6 +191,39 @@ async function submitAddSubject() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showEditSubjectModal(subjectId, name, code, sortOrder) {
|
||||||
|
document.getElementById('editSubjectId').value = subjectId;
|
||||||
|
document.getElementById('editSubjectName').value = name;
|
||||||
|
document.getElementById('editSubjectCode').value = code;
|
||||||
|
document.getElementById('editSubjectSortOrder').value = sortOrder;
|
||||||
|
document.getElementById('editSubjectModal').style.display = 'flex';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitEditSubject() {
|
||||||
|
const subjectId = document.getElementById('editSubjectId').value;
|
||||||
|
const subjectName = document.getElementById('editSubjectName').value.trim();
|
||||||
|
const subjectCode = document.getElementById('editSubjectCode').value.trim();
|
||||||
|
const sortOrder = document.getElementById('editSubjectSortOrder').value;
|
||||||
|
|
||||||
|
if (!subjectName) {
|
||||||
|
showToast('请填写科目名称', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = { subject_name: subjectName };
|
||||||
|
if (subjectCode) data.subject_code = subjectCode;
|
||||||
|
if (sortOrder !== '') data.sort_order = parseInt(sortOrder);
|
||||||
|
|
||||||
|
const res = await apiPut(`/api/subject/update/${subjectId}`, data);
|
||||||
|
if (res && res.success) {
|
||||||
|
showToast('科目更新成功');
|
||||||
|
closeModal('editSubjectModal');
|
||||||
|
loadSubjects();
|
||||||
|
} else {
|
||||||
|
showToast(res?.message || '更新失败', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function closeModal(modalId) {
|
function closeModal(modalId) {
|
||||||
const modal = document.getElementById(modalId);
|
const modal = document.getElementById(modalId);
|
||||||
if (modal) modal.style.display = 'none';
|
if (modal) modal.style.display = 'none';
|
||||||
|
|||||||
Reference in New Issue
Block a user