当前位置: 首页 > news >正文

新增模块介绍:教师代课统计系统(由社区 @记得微笑 贡献)

🌟 新增模块介绍:教师代课统计系统(由社区 @记得微笑 贡献)

项目地址:教师工具箱(Teacher Toolbox)
贡献者:@记得微笑
模块类型:教学管理 → 教师事务支持
技术栈:HTML + JavaScript(纯前端)、Tailwind CSS、Chart.js、SheetJS(xlsx)


📌 模块背景

在日常教学管理中,教师因会议、培训、病假等原因需要临时调课或请其他教师代课,这一流程往往依赖纸质申请或口头沟通,效率低且难以追踪。为解决这一痛点,社区成员 @记得微笑 精心开发了 「教师代课统计系统」,现已作为独立模块集成至 教师工具箱(Teacher Toolbox) 项目中!

该系统采用 纯前端实现,无需后端依赖,开箱即用,特别适合中小型学校或教研组快速部署使用。


🧩 核心功能

  • 多角色权限管理

    • 普通教师:提交代课申请、查看个人记录、修改个人信息
    • 审批管理员:审核/拒绝代课申请
    • 系统管理员:管理教师账户、查看统计报表、导出数据
  • 📝 代课申请全流程

    • 填写课程时间、地点、代课教师、原因等
    • 支持取消“待审批”状态的申请
  • 📊 数据可视化统计

    • 个人代课状态环形图(已批/待审/拒绝)
    • 全局申请状态分布饼图
    • 教师代课次数 Top 5 横向柱状图
  • 📤 Excel 导出支持

    • 一键导出「代课节数统计表」
    • 一键导出「教师请假次数统计表」
  • 👤 教师信息管理

    • 添加/编辑/删除教师
    • 批量导入模板(含填写说明)
    • 支持重置密码、手动修正代课次数(仅限管理员)
  • 🔒 安全与体验

    • 密码修改需验证原密码
    • 工号/用户名唯一性校验
    • 响应式设计,适配 PC 与移动端

🛠️ 技术亮点

  • 零后端依赖:所有数据暂存于内存(刷新丢失),适合演示或轻量使用;实际部署时可轻松对接后端 API。
  • 模块化结构:完全符合 Teacher Toolbox 的双级目录 + JSON 驱动规范,未来可封装为独立工具目录(如 tools/categories/teaching_management/tools/substitute_scheduler/)。
  • 开箱即用:仅需浏览器打开 index.html(建议通过本地 HTTP 服务器运行),无需安装任何依赖。

🙌 致谢

特别感谢社区成员 @记得微笑 的高质量贡献!该模块不仅功能完整、交互流畅,代码结构清晰、注释详尽,充分体现了开源协作的精神。我们期待更多教育工作者和技术爱好者加入 教师工具箱 项目,共同打造真正服务于一线教师的智能工具生态!


▶️ 快速体验

  1. 克隆或下载 Teacher Toolbox项目(假设已集成)
  2. 将本模块放入对应目录(如 tools/categories/teaching_management/tools/substitute_scheduler/
  3. 启动本地服务器:
    npx http-server
    # 或
    python -m http.server 8080
    
  4. 访问 http://localhost:8080,进入「教学管理」→「代课统计系统」即可使用!

💡 提示:当前为纯前端版本,如需持久化数据,请联系开发者或自行对接后端数据库。


代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>教师代课统计系统</title><script src="https://cdn.tailwindcss.com"></script><link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet"><script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script><script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script>
</head>
<body class="bg-gray-100 min-h-screen flex flex-col"><!-- 登录界面 --><div id="loginPage" class="flex-1 flex items-center justify-center p-4"><div class="w-full max-w-md bg-white rounded-lg shadow-xl overflow-hidden transform transition-all duration-300"><div class="bg-blue-600 p-6"><h2 class="text-center text-white text-2xl font-bold"><i class="fa fa-graduation-cap mr-2"></i>教师代课统计系统</h2><p class="text-center text-blue-100 mt-2">请登录您的账号</p></div><div class="p-6"><form id="loginForm"><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="loginUsername">用户名</label><div class="relative"><span class="absolute inset-y-0 left-0 flex items-center pl-3"><i class="fa fa-user text-gray-400"></i></span><input class="shadow appearance-none border rounded w-full py-2 pl-10 pr-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="loginUsername" type="text" required></div></div><div class="mb-6"><label class="block text-gray-700 text-sm font-bold mb-2" for="loginPassword">密码</label><div class="relative"><span class="absolute inset-y-0 left-0 flex items-center pl-3"><i class="fa fa-lock text-gray-400"></i></span><input class="shadow appearance-none border rounded w-full py-2 pl-10 pr-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="loginPassword" type="password" required></div></div><div class="mb-4 hidden" id="loginError"><div class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded relative" role="alert"><i class="fa fa-exclamation-circle mr-2"></i><span class="block sm:inline" id="loginErrorMessage">用户名或密码错误</span></div></div><div class="flex items-center justify-center"><button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-8 rounded focus:outline-none focus:shadow-outline transition duration-200 w-full flex items-center justify-center"><i class="fa fa-sign-in mr-2"></i>登录</button></div></form></div></div></div><!-- 主系统界面 (初始隐藏) --><div id="systemPage" class="hidden flex flex-col min-h-screen"><!-- 顶部导航 --><nav class="bg-blue-600 text-white shadow-md"><div class="container mx-auto"><div class="flex justify-between items-center"><div class="flex items-center"><h1 class="text-xl font-bold p-4"><i class="fa fa-graduation-cap mr-2"></i>教师代课统计系统</h1></div><div class="hidden md:flex items-center space-x-1"><a href="#dashboard" class="nav-link px-4 py-4 hover:bg-blue-700 transition-colors duration-200"><i class="fa fa-dashboard mr-1"></i>仪表盘</a><a href="#substitute" class="nav-link px-4 py-4 hover:bg-blue-700 transition-colors duration-200"><i class="fa fa-exchange mr-1"></i>代课申请</a><a href="#approval" class="nav-link px-4 py-4 hover:bg-blue-700 transition-colors duration-200 admin-only approval-admin-only"><i class="fa fa-check-square-o mr-1"></i>审批管理</a><a href="#teachers" class="nav-link px-4 py-4 hover:bg-blue-700 transition-colors duration-200 admin-only"><i class="fa fa-users mr-1"></i>教师管理</a><a href="#reports" class="nav-link px-4 py-4 hover:bg-blue-700 transition-colors duration-200 admin-only"><i class="fa fa-bar-chart mr-1"></i>统计报表</a><a href="#profile" class="nav-link px-4 py-4 hover:bg-blue-700 transition-colors duration-200"><i class="fa fa-user-circle mr-1"></i>个人设置</a></div><div class="flex items-center p-3"><span class="hidden md:inline-block mr-3" id="currentUser">教师</span><button id="logoutBtn" class="bg-red-500 hover:bg-red-600 px-3 py-1 rounded transition-colors duration-200"><i class="fa fa-sign-out mr-1"></i>退出</button><button id="mobileMenuBtn" class="md:hidden ml-3 text-xl"><i class="fa fa-bars"></i></button></div></div><div id="mobileMenu" class="md:hidden hidden bg-blue-700"><a href="#dashboard" class="block px-4 py-3 hover:bg-blue-600 transition-colors duration-200"><i class="fa fa-dashboard mr-1"></i>仪表盘</a><a href="#substitute" class="block px-4 py-3 hover:bg-blue-600 transition-colors duration-200"><i class="fa fa-exchange mr-1"></i>代课申请</a><a href="#approval" class="block px-4 py-3 hover:bg-blue-600 transition-colors duration-200 admin-only approval-admin-only"><i class="fa fa-check-square-o mr-1"></i>审批管理</a><a href="#teachers" class="block px-4 py-3 hover:bg-blue-600 transition-colors duration-200 admin-only"><i class="fa fa-users mr-1"></i>教师管理</a><a href="#reports" class="block px-4 py-3 hover:bg-blue-600 transition-colors duration-200 admin-only"><i class="fa fa-bar-chart mr-1"></i>统计报表</a><a href="#profile" class="block px-4 py-3 hover:bg-blue-600 transition-colors duration-200"><i class="fa fa-user-circle mr-1"></i>个人设置</a></div></div></nav><!-- 主内容区 --><div class="flex-1 container mx-auto p-4 md:p-6"><!-- 仪表盘界面 --><div id="dashboardSection"><div class="mb-6"><div class="flex flex-wrap items-center text-sm text-gray-500 mb-2"><span>首页</span></div><h2 class="text-2xl font-bold text-gray-800">系统仪表盘</h2><p class="text-gray-600 mt-1">查看系统概览和最新动态</p></div><!-- 仪表盘内容 --><div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"><div class="bg-white rounded-lg shadow p-6"><h3 class="text-lg font-semibold text-gray-700 mb-4">个人代课统计</h3><div class="h-64"><canvas id="personalStatsChart"></canvas></div></div><div class="bg-white rounded-lg shadow p-6"><h3 class="text-lg font-semibold text-gray-700 mb-4">最近申请记录</h3><div class="space-y-4" id="recentApplications"><!-- 最近申请记录将通过JavaScript动态添加 --></div><div id="noRecentApplications" class="text-center py-8 text-gray-500 hidden"><p>暂无申请记录</p></div></div></div></div><!-- 代课申请界面(所有用户可见) --><div id="substituteSection" class="hidden"><div class="mb-6"><div class="flex flex-wrap items-center text-sm text-gray-500 mb-2"><a href="#dashboard" class="hover:text-blue-600">首页</a><i class="fa fa-angle-right mx-2 text-gray-400 text-xs"></i><span>代课申请</span></div><h2 class="text-2xl font-bold text-gray-800">提交代课申请</h2><p class="text-gray-600 mt-1">请填写以下信息提交代课申请,等待审批</p></div><!-- 代课申请表单卡片 --><div class="bg-white rounded-lg shadow-lg overflow-hidden mb-6 transform transition-all duration-300 hover:shadow-xl"><div class="p-4 md:p-6 border-b"><h3 class="text-lg font-semibold text-gray-700">代课申请信息</h3></div><div class="p-4 md:p-6"><form id="substituteForm"><div class="grid grid-cols-1 md:grid-cols-2 gap-6"><div><label class="block text-gray-700 text-sm font-bold mb-2" for="applicantName">申请人 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="applicantName" type="text" readonly><input type="hidden" id="applicantId"></div><div><label class="block text-gray-700 text-sm font-bold mb-2" for="applyDate">申请日期 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="applyDate" type="date" required></div><div><label class="block text-gray-700 text-sm font-bold mb-2" for="courseDate">课程日期 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="courseDate" type="date" required></div><div><label class="block text-gray-700 text-sm font-bold mb-2" for="courseTime">课程时间 <span class="text-red-500">*</span></label><select class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="courseTime" required><option value="">请选择课程时间</option><option value="morning">上午 (8:00-12:00)</option><option value="afternoon">下午 (14:00-18:00)</option><option value="evening">晚上 (19:00-21:00)</option></select></div><div><label class="block text-gray-700 text-sm font-bold mb-2" for="courseName">课程名称 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="courseName" type="text" required></div><div><label class="block text-gray-700 text-sm font-bold mb-2" for="courseLocation">上课地点 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="courseLocation" type="text" required></div><div class="md:col-span-2"><label class="block text-gray-700 text-sm font-bold mb-2" for="substituteTeacher">代课教师 <span class="text-red-500">*</span></label><select class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="substituteTeacher" required><option value="">请选择代课教师</option><!-- 代课教师选项将通过JavaScript动态添加 --></select></div><div class="md:col-span-2"><label class="block text-gray-700 text-sm font-bold mb-2" for="reason">申请原因 <span class="text-red-500">*</span></label><textarea class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="reason" rows="4" required></textarea></div></div><div class="mt-6 flex justify-end"><button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-6 rounded transition-colors duration-200 flex items-center"><i class="fa fa-paper-plane mr-2"></i>提交申请</button></div></form></div></div><!-- 我的申请记录 --><div class="bg-white rounded-lg shadow-lg overflow-hidden transform transition-all duration-300 hover:shadow-xl"><div class="p-4 md:p-6 border-b"><h3 class="text-lg font-semibold text-gray-700">我的申请记录</h3></div><div class="p-4 md:p-6"><div class="overflow-x-auto"><table class="min-w-full divide-y divide-gray-200"><thead class="bg-gray-50"><tr><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">申请ID</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">课程信息</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">代课教师</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">申请日期</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">状态</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">操作</th></tr></thead><tbody id="myApplicationsTable" class="bg-white divide-y divide-gray-200"><!-- 申请记录将通过JavaScript动态添加 --></tbody></table></div><!-- 空状态显示 --><div id="noApplications" class="text-center py-12 text-gray-500 hidden"><div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-gray-100 mb-4"><i class="fa fa-file-text-o text-2xl text-gray-400"></i></div><p class="text-lg">暂无申请记录</p><p class="text-gray-400 mt-2">您尚未提交任何代课申请</p></div></div></div></div><!-- 审批管理界面(仅审批管理员可见) --><div id="approvalSection" class="hidden admin-only approval-admin-only"><div class="mb-6"><div class="flex flex-wrap items-center text-sm text-gray-500 mb-2"><a href="#dashboard" class="hover:text-blue-600">首页</a><i class="fa fa-angle-right mx-2 text-gray-400 text-xs"></i><span>审批管理</span></div><h2 class="text-2xl font-bold text-gray-800">代课申请审批</h2><p class="text-gray-600 mt-1">查看和处理所有教师提交的代课申请</p></div><!-- 审批管理筛选区 --><div class="bg-white rounded-lg shadow-lg overflow-hidden mb-6"><div class="p-4 md:p-6 border-b bg-gray-50"><div class="flex flex-wrap gap-4"><div class="flex-1 min-w-[200px]"><div class="relative"><input type="text" id="approvalSearchInput" placeholder="搜索申请人、代课教师或课程名称..." class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"><i class="fa fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i></div></div><div class="flex gap-3"><select id="approvalStatusFilter" class="border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"><option value="all">所有状态</option><option value="pending">待审批</option><option value="approved">已批准</option><option value="rejected">已拒绝</option></select><button id="refreshApprovalBtn" class="bg-gray-600 hover:bg-gray-700 text-white font-medium py-2 px-4 rounded transition-colors duration-200 flex items-center"><i class="fa fa-refresh mr-2"></i>刷新</button></div></div></div><!-- 审批列表 --><div class="p-4 md:p-6"><div class="overflow-x-auto"><table class="min-w-full divide-y divide-gray-200"><thead class="bg-gray-50"><tr><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">申请ID</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">申请人</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">课程信息</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">代课教师</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">申请日期</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">状态</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">操作</th></tr></thead><tbody id="approvalTable" class="bg-white divide-y divide-gray-200"><!-- 审批记录将通过JavaScript动态添加 --></tbody></table></div><!-- 空状态显示 --><div id="noApprovals" class="text-center py-12 text-gray-500 hidden"><div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-gray-100 mb-4"><i class="fa fa-file-text-o text-2xl text-gray-400"></i></div><p class="text-lg">暂无申请记录</p><p class="text-gray-400 mt-2">当前没有需要处理的代课申请</p></div></div></div></div><!-- 教师管理界面(仅管理员可见) --><div id="teachersSection" class="hidden admin-only"><div class="mb-6"><div class="flex flex-wrap items-center text-sm text-gray-500 mb-2"><a href="#dashboard" class="hover:text-blue-600">首页</a><i class="fa fa-angle-right mx-2 text-gray-400 text-xs"></i><span>教师管理</span></div><h2 class="text-2xl font-bold text-gray-800">教师信息管理</h2><p class="text-gray-600 mt-1">管理教师基本信息,支持批量导入和离职教师删除</p></div><!-- 教师管理卡片 --><div class="bg-white rounded-lg shadow-lg overflow-hidden mb-6 transform transition-all duration-300 hover:shadow-xl"><div class="p-4 md:p-6 border-b flex flex-wrap justify-between items-center gap-4"><h3 class="text-lg font-semibold text-gray-700">教师列表</h3><div class="flex gap-3"><button id="addTeacherBtn" class="bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded transition-colors duration-200 flex items-center"><i class="fa fa-plus mr-2"></i>添加教师</button><button id="batchImportBtn" class="bg-purple-600 hover:bg-purple-700 text-white font-medium py-2 px-4 rounded transition-colors duration-200 flex items-center"><i class="fa fa-upload mr-2"></i>批量导入</button><button id="downloadTemplateBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded transition-colors duration-200 flex items-center"><i class="fa fa-file-excel-o mr-2"></i>下载模板</button></div></div><!-- 搜索和筛选区 --><div class="p-4 md:p-6 border-b bg-gray-50"><div class="flex flex-wrap gap-4"><div class="flex-1 min-w-[200px]"><div class="relative"><input type="text" id="searchInput" placeholder="搜索教师姓名、工号或用户名..." class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"><i class="fa fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i></div></div><div class="flex gap-3"><select id="statusFilter" class="border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"><option value="all">所有状态</option><option value="active">在职</option><option value="leave">离职</option></select><select id="roleFilter" class="border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"><option value="all">所有角色</option><option value="teacher">教师</option><option value="admin">管理员</option><option value="approval_admin">审批管理员</option></select></div></div></div><!-- 教师列表 --><div class="p-4 md:p-6"><div class="overflow-x-auto"><table class="min-w-full divide-y divide-gray-200"><thead class="bg-gray-50"><tr><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">ID</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">姓名</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">工号</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">用户名</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">角色</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">入职日期</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">状态</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">代课次数</th><th class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">操作</th></tr></thead><tbody id="teachersTable" class="bg-white divide-y divide-gray-200"><!-- 教师信息将通过JavaScript动态添加 --></tbody></table></div><!-- 空状态显示 --><div id="noTeachers" class="text-center py-12 text-gray-500 hidden"><div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-gray-100 mb-4"><i class="fa fa-user-o text-2xl text-gray-400"></i></div><p class="text-lg">暂无教师信息</p><p class="text-gray-400 mt-2">点击"添加教师"或"批量导入"按钮添加教师信息</p></div><!-- 分页控件 --><div class="mt-6 flex justify-between items-center"><div class="text-sm text-gray-600">显示 <span id="showingRange">0-0</span> 条,共 <span id="totalCount">0</span></div><div class="flex space-x-2"><button id="prevPage" class="px-3 py-1 border border-gray-300 rounded bg-white text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed" disabled><i class="fa fa-chevron-left"></i></button><button id="nextPage" class="px-3 py-1 border border-gray-300 rounded bg-white text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed" disabled><i class="fa fa-chevron-right"></i></button></div></div></div></div></div><!-- 统计报表界面(仅管理员可见) --><div id="reportsSection" class="hidden admin-only"><div class="mb-6"><div class="flex flex-wrap items-center text-sm text-gray-500 mb-2"><a href="#dashboard" class="hover:text-blue-600">首页</a><i class="fa fa-angle-right mx-2 text-gray-400 text-xs"></i><span>统计报表</span></div><h2 class="text-2xl font-bold text-gray-800">代课统计报表</h2><p class="text-gray-600 mt-1">查看和分析系统中的代课数据统计信息</p></div><!-- 导出按钮 --><div class="mb-6 flex justify-end"><button id="exportSubstituteBtn" class="bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded transition-colors duration-200 flex items-center mr-3"><i class="fa fa-download mr-2"></i>导出代课节数统计</button><button id="exportLeaveBtn" class="bg-purple-600 hover:bg-purple-700 text-white font-medium py-2 px-4 rounded transition-colors duration-200 flex items-center"><i class="fa fa-download mr-2"></i>导出请假天数统计</button></div><!-- 统计卡片 --><div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6"><div class="bg-white rounded-lg shadow p-6 transform transition-all duration-300 hover:shadow-lg hover:-translate-y-1"><div class="flex items-center"><div class="p-3 rounded-full bg-blue-100 text-blue-600 mr-4"><i class="fa fa-file-text-o text-xl"></i></div><div><p class="text-sm text-gray-500">总申请数</p><p class="text-2xl font-bold text-gray-800" id="totalApplications">0</p></div></div></div><div class="bg-white rounded-lg shadow p-6 transform transition-all duration-300 hover:shadow-lg hover:-translate-y-1"><div class="flex items-center"><div class="p-3 rounded-full bg-green-100 text-green-600 mr-4"><i class="fa fa-check-circle text-xl"></i></div><div><p class="text-sm text-gray-500">已批准</p><p class="text-2xl font-bold text-gray-800" id="approvedApplications">0</p></div></div></div><div class="bg-white rounded-lg shadow p-6 transform transition-all duration-300 hover:shadow-lg hover:-translate-y-1"><div class="flex items-center"><div class="p-3 rounded-full bg-yellow-100 text-yellow-600 mr-4"><i class="fa fa-clock-o text-xl"></i></div><div><p class="text-sm text-gray-500">待审批</p><p class="text-2xl font-bold text-gray-800" id="pendingApplications">0</p></div></div></div></div><!-- 图表区域 --><div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6"><div class="bg-white rounded-lg shadow p-6"><h3 class="text-lg font-semibold text-gray-700 mb-4">代课申请状态分布</h3><div class="h-64"><canvas id="statusChart"></canvas></div></div><div class="bg-white rounded-lg shadow p-6"><h3 class="text-lg font-semibold text-gray-700 mb-4">教师代课次数排名</h3><div class="h-64"><canvas id="teacherRankingChart"></canvas></div></div></div></div><!-- 个人设置界面 --><div id="profileSection" class="hidden"><div class="mb-6"><div class="flex flex-wrap items-center text-sm text-gray-500 mb-2"><a href="#dashboard" class="hover:text-blue-600">首页</a><i class="fa fa-angle-right mx-2 text-gray-400 text-xs"></i><span>个人设置</span></div><h2 class="text-2xl font-bold text-gray-800">个人信息设置</h2><p class="text-gray-600 mt-1">管理您的个人信息和账户安全</p></div><div class="grid grid-cols-1 lg:grid-cols-3 gap-6"><!-- 个人信息 --><div class="lg:col-span-2 bg-white rounded-lg shadow-lg overflow-hidden transform transition-all duration-300 hover:shadow-xl"><div class="p-4 md:p-6 border-b"><h3 class="text-lg font-semibold text-gray-700">个人信息</h3></div><div class="p-4 md:p-6"><form id="profileForm"><input type="hidden" id="profileUserId"><div class="grid grid-cols-1 md:grid-cols-2 gap-6"><div><label class="block text-gray-700 text-sm font-bold mb-2" for="profileName">姓名 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="profileName" type="text" required></div><div><label class="block text-gray-700 text-sm font-bold mb-2" for="profileEmployeeId">工号</label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight bg-gray-100" id="profileEmployeeId" type="text" readonly></div><div><label class="block text-gray-700 text-sm font-bold mb-2" for="profileUsername">用户名 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="profileUsername" type="text" required></div><div><label class="block text-gray-700 text-sm font-bold mb-2" for="profileRole">角色</label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight bg-gray-100" id="profileRole" type="text" readonly></div><div class="md:col-span-2"><label class="block text-gray-700 text-sm font-bold mb-2" for="profileHireDate">入职日期</label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight bg-gray-100" id="profileHireDate" type="text" readonly></div></div><div class="mt-6 flex justify-end"><button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-6 rounded transition-colors duration-200 flex items-center"><i class="fa fa-save mr-2"></i>保存修改</button></div></form></div></div><!-- 修改密码 --><div class="bg-white rounded-lg shadow-lg overflow-hidden transform transition-all duration-300 hover:shadow-xl"><div class="p-4 md:p-6 border-b"><h3 class="text-lg font-semibold text-gray-700">修改密码</h3></div><div class="p-4 md:p-6"><form id="changePasswordForm"><input type="hidden" id="passwordUserId"><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="currentPassword">当前密码 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="currentPassword" type="password" required><p id="currentPasswordError" class="text-xs text-red-500 mt-1 hidden">当前密码不正确</p></div><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="newPassword">新密码 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="newPassword" type="password" required><p class="text-xs text-gray-500 mt-1">密码长度至少6位</p></div><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="confirmPassword">确认新密码 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="confirmPassword" type="password" required><p id="passwordMismatchError" class="text-xs text-red-500 mt-1 hidden">两次输入的密码不一致</p></div><div class="mt-6 flex justify-end"><button type="submit" class="bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-6 rounded transition-colors duration-200 flex items-center"><i class="fa fa-key mr-2"></i>更新密码</button></div></form></div></div></div></div></div><!-- 页脚 --><footer class="bg-gray-800 text-white py-6"><div class="container mx-auto px-4"><div class="flex flex-col md:flex-row justify-between items-center"><div class="mb-4 md:mb-0"><p class="text-sm">&copy; 2023 教师代课统计系统 - 版权所有</p></div><div class="flex space-x-6"><a href="#" class="text-gray-300 hover:text-white transition-colors duration-200"><i class="fa fa-question-circle mr-1"></i>帮助中心</a><a href="#" class="text-gray-300 hover:text-white transition-colors duration-200"><i class="fa fa-file-text-o mr-1"></i>使用手册</a><a href="#" class="text-gray-300 hover:text-white transition-colors duration-200"><i class="fa fa-envelope-o mr-1"></i>联系我们</a></div></div></div></footer></div><!-- 添加/编辑教师弹窗 --><div id="teacherModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden opacity-0 transition-opacity duration-300"><div class="bg-white rounded-lg shadow-xl w-full max-w-md transform transition-transform duration-300 scale-95"><div class="px-6 py-4 border-b"><h3 id="teacherModalTitle" class="text-lg font-bold text-gray-700">添加教师</h3></div><div class="px-6 py-4"><form id="teacherForm"><input type="hidden" id="teacherId"><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="teacherNameInput">姓名 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="teacherNameInput" type="text" required></div><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="teacherEmployeeIdInput">工号 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="teacherEmployeeIdInput" type="text" required><p class="text-xs text-gray-500 mt-1">工号为唯一标识符,不可重复</p><p id="employeeIdError" class="text-xs text-red-500 mt-1 hidden">该工号已存在,请使用其他工号</p></div><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="teacherUsernameInput">用户名 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="teacherUsernameInput" type="text" required></div><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="teacherPasswordInput">密码 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="teacherPasswordInput" type="password" required><p class="text-xs text-gray-500 mt-1">新教师默认密码:123456,建议登录后修改</p></div><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="teacherRoleSelect">角色 <span class="text-red-500">*</span></label><select class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="teacherRoleSelect" required><option value="teacher">教师</option><option value="admin">管理员</option><option value="approval_admin">审批管理员</option></select></div><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="teacherHireDateInput">入职日期 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="teacherHireDateInput" type="date" required></div><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="teacherStatusSelect">状态 <span class="text-red-500">*</span></label><select class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="teacherStatusSelect" required><option value="active">在职</option><option value="leave">离职</option></select></div></form></div><div class="px-6 py-4 bg-gray-50 flex justify-end space-x-3"><button id="cancelTeacherBtn" class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-medium py-2 px-4 rounded transition-colors duration-200">取消</button><button id="saveTeacherBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded transition-colors duration-200">保存</button></div></div></div><!-- 审批操作弹窗 --><div id="approvalModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden opacity-0 transition-opacity duration-300"><div class="bg-white rounded-lg shadow-xl w-full max-w-md transform transition-transform duration-300 scale-95"><div class="px-6 py-4 border-b"><h3 id="approvalModalTitle" class="text-lg font-bold text-gray-700">审批申请</h3></div><div class="px-6 py-4"><input type="hidden" id="approvalApplicationId"><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="approvalResult">审批结果 <span class="text-red-500">*</span></label><select class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="approvalResult" required><option value="approved">批准</option><option value="rejected">拒绝</option></select></div><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="approvalComment">审批意见</label><textarea class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="approvalComment" rows="4" placeholder="请输入审批意见(可选)"></textarea></div></div><div class="px-6 py-4 bg-gray-50 flex justify-end space-x-3"><button id="cancelApprovalBtn" class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-medium py-2 px-4 rounded transition-colors duration-200">取消</button><button id="confirmApprovalBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded transition-colors duration-200">确认</button></div></div></div><!-- 重置密码弹窗 --><div id="resetPasswordModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden opacity-0 transition-opacity duration-300"><div class="bg-white rounded-lg shadow-xl w-full max-w-md transform transition-transform duration-300 scale-95"><div class="px-6 py-4 border-b"><h3 id="resetPasswordModalTitle" class="text-lg font-bold text-gray-700">重置密码</h3></div><div class="px-6 py-4"><input type="hidden" id="resetPasswordUserId"><p class="text-gray-600 mb-4">确定要将 <span id="resetPasswordUserName" class="font-medium"></span> 的密码重置为默认密码 <span class="font-mono bg-gray-100 px-1 rounded">123456</span> 吗?</p><p class="text-sm text-amber-600 bg-amber-50 p-2 rounded"><i class="fa fa-info-circle mr-1"></i> 重置后,用户需要使用默认密码登录并及时修改密码</p></div><div class="px-6 py-4 bg-gray-50 flex justify-end space-x-3"><button id="cancelResetPasswordBtn" class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-medium py-2 px-4 rounded transition-colors duration-200">取消</button><button id="confirmResetPasswordBtn" class="bg-red-600 hover:bg-red-700 text-white font-medium py-2 px-4 rounded transition-colors duration-200">确认重置</button></div></div></div><!-- 编辑代课次数弹窗 --><div id="editSubstituteCountModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden opacity-0 transition-opacity duration-300"><div class="bg-white rounded-lg shadow-xl w-full max-w-md transform transition-transform duration-300 scale-95"><div class="px-6 py-4 border-b"><h3 id="editSubstituteCountModalTitle" class="text-lg font-bold text-gray-700">编辑代课次数</h3></div><div class="px-6 py-4"><input type="hidden" id="editSubstituteCountUserId"><div class="mb-4"><label class="block text-gray-700 text-sm font-bold mb-2" for="substituteCount">代课次数 <span class="text-red-500">*</span></label><input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline focus:border-blue-500 transition duration-200" id="substituteCount" type="number" min="0" required><p class="text-sm text-amber-600 bg-amber-50 p-2 rounded mt-2"><i class="fa fa-info-circle mr-1"></i> 仅在记录错误时修改此数值,正常情况由系统自动计算</p></div></div><div class="px-6 py-4 bg-gray-50 flex justify-end space-x-3"><button id="cancelEditSubstituteCountBtn" class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-medium py-2 px-4 rounded transition-colors duration-200">取消</button><button id="confirmEditSubstituteCountBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded transition-colors duration-200">保存修改</button></div></div></div><script>// 模拟数据 - 教师列表 (增加审批管理员角色)let teachers = [{ id: 1, name: '张三', employeeId: 'T001', username: 'zhangsan', password: '123456', role: 'teacher', hireDate: '2020-09-01', status: 'active', substituteCount: 15 },{ id: 2, name: '李四', employeeId: 'T002', username: 'lisi', password: '123456', role: 'teacher', hireDate: '2021-03-15', status: 'active', substituteCount: 8 },{ id: 3, name: '王五', employeeId: 'A001', username: 'wangwu', password: '123456', role: 'admin', hireDate: '2019-05-20', status: 'active', substituteCount: 5 },{ id: 4, name: '赵六', employeeId: 'T003', username: 'zhaoliu', password: '123456', role: 'teacher', hireDate: '2022-01-10', status: 'active', substituteCount: 3 },{ id: 5, name: '钱七', employeeId: 'AP001', username: 'qianqi', password: '123456', role: 'approval_admin', hireDate: '2020-03-10', status: 'active', substituteCount: 7 }];// 模拟数据 - 代课申请let substituteApplications = [{ id: 1, applicantId: 1, applicantName: '张三', substituteTeacherId: 2, substituteTeacherName: '李四', applyDate: '2023-05-10', courseDate: '2023-05-15', courseTime: 'morning', courseName: '高等数学', courseLocation: '1号教学楼301室', reason: '参加学术会议', status: 'approved', comment: '' },{ id: 2, applicantId: 1, applicantName: '张三', substituteTeacherId: 4, substituteTeacherName: '赵六', applyDate: '2023-05-12', courseDate: '2023-05-20', courseTime: 'afternoon', courseName: '线性代数', courseLocation: '2号教学楼205室', reason: '个人事假', status: 'pending', comment: '' },{ id: 3, applicantId: 2, applicantName: '李四', substituteTeacherId: 1, substituteTeacherName: '张三', applyDate: '2023-05-08', courseDate: '2023-05-12', courseTime: 'evening', courseName: '大学物理', courseLocation: '实验楼102室', reason: '生病请假', status: 'approved', comment: '' }];// 当前登录用户let currentUser = null;// 当前页码和每页显示数量let currentPage = 1;const pageSize = 10;// 当前筛选条件let currentFilters = {search: '',status: 'all',role: 'all',approvalStatus: 'all',approvalSearch: ''};// DOM 元素加载完成后初始化document.addEventListener('DOMContentLoaded', function() {// 初始化登录表单提交事件initLoginForm();// 初始化教师管理相关事件initTeacherManagement();// 初始化移动菜单切换initMobileMenu();// 初始化登出功能initLogout();// 初始化导航菜单切换initNavigation();// 初始化代课申请表单initSubstituteForm();// 初始化审批管理功能(修复事件绑定问题)initApprovalManagement();// 初始化导出功能initExportFunctions();// 初始化个人设置和密码修改功能initProfileSettings();});// 初始化登录表单function initLoginForm() {const loginForm = document.getElementById('loginForm');const loginError = document.getElementById('loginError');const loginErrorMessage = document.getElementById('loginErrorMessage');loginForm.addEventListener('submit', function(e) {e.preventDefault();const username = document.getElementById('loginUsername').value.trim();const password = document.getElementById('loginPassword').value.trim();// 查找匹配的用户const user = teachers.find(t => t.username === username && t.password === password);if (user) {// 登录成功currentUser = user;loginError.classList.add('hidden');// 更新当前用户显示let roleText = '';switch(user.role) {case 'admin': roleText = '管理员'; break;case 'approval_admin': roleText = '审批管理员'; break;default: roleText = '教师';}document.getElementById('currentUser').textContent = `${roleText} - ${user.name}`;// 根据角色显示/隐藏元素updateRoleElementsVisibility(user.role);// 切换到系统页面document.getElementById('loginPage').classList.add('hidden');document.getElementById('systemPage').classList.remove('hidden');// 初始化页面数据 - 管理员默认显示仪表盘,教师默认显示代课申请initSystemData(user.role === 'admin' || user.role === 'approval_admin');// 初始化个人设置initProfileFormData();} else {// 登录失败loginErrorMessage.textContent = '用户名或密码错误,请重试';loginError.classList.remove('hidden');// 添加抖动动画效果const loginCard = loginForm.closest('.bg-white');loginCard.classList.add('animate-shake');setTimeout(() => {loginCard.classList.remove('animate-shake');}, 500);}});}// 根据角色显示/隐藏元素function updateRoleElementsVisibility(role) {// 管理员元素(所有管理员可见)const adminElements = document.querySelectorAll('.admin-only');adminElements.forEach(el => {el.style.display = (role === 'admin' || role === 'approval_admin') ? '' : 'none';});// 审批管理元素(仅审批管理员和超级管理员可见)const approvalElements = document.querySelectorAll('.approval-admin-only');approvalElements.forEach(el => {el.style.display = (role === 'admin' || role === 'approval_admin') ? '' : 'none';});}// 初始化系统数据function initSystemData(isAdmin) {// 管理员默认显示仪表盘,教师默认显示代课申请if (isAdmin) {showSection('dashboard');// 高亮仪表盘导航document.querySelector('.nav-link[href="#dashboard"]').classList.add('bg-blue-700');} else {showSection('substitute');// 高亮代课申请导航document.querySelector('.nav-link[href="#substitute"]').classList.add('bg-blue-700');}// 加载教师列表(供管理员查看)renderTeachers();// 初始化代课申请表单数据initSubstituteFormData();// 加载我的申请记录renderMyApplications();// 如果是管理员,加载审批列表和统计数据if (isAdmin) {renderApprovalList();initAdminReports();}// 初始化个人统计数据initPersonalStats();}// 初始化导航菜单切换function initNavigation() {const navLinks = document.querySelectorAll('.nav-link');navLinks.forEach(link => {link.addEventListener('click', function(e) {e.preventDefault();// 移除所有链接的活跃状态navLinks.forEach(l => l.classList.remove('bg-blue-700'));// 添加当前链接的活跃状态this.classList.add('bg-blue-700');// 显示对应的 sectionconst target = this.getAttribute('href').substring(1);showSection(target);});});}// 显示指定的 sectionfunction showSection(sectionId) {// 隐藏所有 sectiondocument.getElementById('dashboardSection').classList.add('hidden');document.getElementById('substituteSection').classList.add('hidden');document.getElementById('approvalSection').classList.add('hidden');document.getElementById('teachersSection').classList.add('hidden');document.getElementById('reportsSection').classList.add('hidden');document.getElementById('profileSection').classList.add('hidden');// 显示目标 sectiondocument.getElementById(`${sectionId}Section`).classList.remove('hidden');// 如果是审批管理页面,刷新列表if (sectionId === 'approval') {renderApprovalList();}}// 初始化教师管理相关事件function initTeacherManagement() {// 添加教师按钮document.getElementById('addTeacherBtn').addEventListener('click', function() {openTeacherModal();});// 取消按钮document.getElementById('cancelTeacherBtn').addEventListener('click', function() {closeTeacherModal();});// 保存教师按钮document.getElementById('saveTeacherBtn').addEventListener('click', function() {saveTeacher();});// 搜索输入document.getElementById('searchInput').addEventListener('input', function(e) {currentFilters.search = e.target.value.trim();currentPage = 1; // 重置到第一页renderTeachers();});// 状态筛选document.getElementById('statusFilter').addEventListener('change', function(e) {currentFilters.status = e.target.value;currentPage = 1; // 重置到第一页renderTeachers();});// 角色筛选document.getElementById('roleFilter').addEventListener('change', function(e) {currentFilters.role = e.target.value;currentPage = 1; // 重置到第一页renderTeachers();});// 上一页按钮document.getElementById('prevPage').addEventListener('click', function() {if (currentPage > 1) {currentPage--;renderTeachers();}});// 下一页按钮document.getElementById('nextPage').addEventListener('click', function() {const filteredTeachers = getFilteredTeachers();const totalPages = Math.ceil(filteredTeachers.length / pageSize);if (currentPage < totalPages) {currentPage++;renderTeachers();}});// 工号输入验证document.getElementById('teacherEmployeeIdInput').addEventListener('blur', validateEmployeeId);// 重置密码相关事件document.getElementById('cancelResetPasswordBtn').addEventListener('click', function() {closeResetPasswordModal();});document.getElementById('confirmResetPasswordBtn').addEventListener('click', function() {confirmResetPassword();});// 编辑代课次数相关事件document.getElementById('cancelEditSubstituteCountBtn').addEventListener('click', function() {closeEditSubstituteCountModal();});document.getElementById('confirmEditSubstituteCountBtn').addEventListener('click', function() {confirmEditSubstituteCount();});}// 初始化移动菜单function initMobileMenu() {const mobileMenuBtn = document.getElementById('mobileMenuBtn');const mobileMenu = document.getElementById('mobileMenu');mobileMenuBtn.addEventListener('click', function() {mobileMenu.classList.toggle('hidden');});// 移动菜单链接点击事件const mobileLinks = mobileMenu.querySelectorAll('a');mobileLinks.forEach(link => {link.addEventListener('click', function() {mobileMenu.classList.add('hidden');});});}// 初始化登出功能function initLogout() {document.getElementById('logoutBtn').addEventListener('click', function() {if (confirm('确定要退出登录吗?')) {currentUser = null;// 重置登录表单document.getElementById('loginForm').reset();// 切换到登录页面document.getElementById('systemPage').classList.add('hidden');document.getElementById('loginPage').classList.remove('hidden');}});}// 初始化代课申请表单function initSubstituteForm() {const substituteForm = document.getElementById('substituteForm');substituteForm.addEventListener('submit', function(e) {e.preventDefault();submitSubstituteApplication();});}// 初始化代课申请表单数据function initSubstituteFormData() {// 设置申请人信息document.getElementById('applicantName').value = currentUser.name;document.getElementById('applicantId').value = currentUser.id;// 设置默认申请日期为今天const today = new Date().toISOString().split('T')[0];document.getElementById('applyDate').value = today;// 加载代课教师选项(排除当前登录用户)const substituteTeacherSelect = document.getElementById('substituteTeacher');substituteTeacherSelect.innerHTML = '<option value="">请选择代课教师</option>';teachers.forEach(teacher => {// 只显示在职且不是当前用户的教师if (teacher.id !== currentUser.id && teacher.status === 'active') {const option = document.createElement('option');option.value = teacher.id;option.textContent = `${teacher.name} (${teacher.employeeId})`;substituteTeacherSelect.appendChild(option);}});}// 提交代课申请function submitSubstituteApplication() {const applicantId = parseInt(document.getElementById('applicantId').value);const applicantName = document.getElementById('applicantName').value;const applyDate = document.getElementById('applyDate').value;const courseDate = document.getElementById('courseDate').value;const courseTime = document.getElementById('courseTime').value;const courseName = document.getElementById('courseName').value;const courseLocation = document.getElementById('courseLocation').value;const substituteTeacherId = parseInt(document.getElementById('substituteTeacher').value);const reason = document.getElementById('reason').value;// 基本验证if (!applyDate || !courseDate || !courseTime || !courseName || !courseLocation || !substituteTeacherId || !reason) {alert('请填写所有必填字段');return;}// 查找代课教师姓名const substituteTeacher = teachers.find(t => t.id === substituteTeacherId);if (!substituteTeacher) {alert('选择的代课教师不存在');return;}// 创建新申请const newId = substituteApplications.length > 0 ? Math.max(...substituteApplications.map(a => a.id)) + 1 : 1;const newApplication = {id: newId,applicantId,applicantName,substituteTeacherId,substituteTeacherName: substituteTeacher.name,applyDate,courseDate,courseTime,courseName,courseLocation,reason,status: 'pending', // 默认为待审批状态comment: ''};// 添加到申请列表substituteApplications.unshift(newApplication);// 重置表单document.getElementById('substituteForm').reset();document.getElementById('applyDate').value = new Date().toISOString().split('T')[0];// 重新渲染申请记录renderMyApplications();// 如果当前用户是管理员,同时更新审批列表if (currentUser && (currentUser.role === 'admin' || currentUser.role === 'approval_admin')) {renderApprovalList();}// 显示成功通知showNotification('代课申请已提交,等待审批');}// 渲染我的申请记录function renderMyApplications() {// 筛选当前用户的申请const myApplications = substituteApplications.filter(app => app.applicantId === currentUser.id);const tableBody = document.getElementById('myApplicationsTable');const noApplications = document.getElementById('noApplications');// 清空表格tableBody.innerHTML = '';if (myApplications.length === 0) {noApplications.classList.remove('hidden');} else {noApplications.classList.add('hidden');// 添加申请记录行myApplications.forEach(application => {const row = document.createElement('tr');row.className = 'hover:bg-gray-50 transition-colors duration-150';// 格式化日期显示const courseDate = new Date(application.courseDate).toLocaleDateString('zh-CN');const applyDate = new Date(application.applyDate).toLocaleDateString('zh-CN');// 状态标签样式let statusClass = '';let statusText = '';switch(application.status) {case 'pending':statusClass = 'bg-yellow-100 text-yellow-800';statusText = '待审批';break;case 'approved':statusClass = 'bg-green-100 text-green-800';statusText = '已批准';break;case 'rejected':statusClass = 'bg-red-100 text-red-800';statusText = '已拒绝';break;default:statusClass = 'bg-gray-100 text-gray-800';statusText = '未知';}// 课程时间文本let courseTimeText = '';switch(application.courseTime) {case 'morning':courseTimeText = '上午 (8:00-12:00)';break;case 'afternoon':courseTimeText = '下午 (14:00-18:00)';break;case 'evening':courseTimeText = '晚上 (19:00-21:00)';break;}row.innerHTML = `<td class="px-4 py-3 whitespace-nowrap">${application.id}</td><td class="px-4 py-3"><div class="font-medium text-gray-900">${application.courseName}</div><div class="text-sm text-gray-500">${courseDate} ${courseTimeText}</div><div class="text-sm text-gray-500">${application.courseLocation}</div></td><td class="px-4 py-3 whitespace-nowrap">${application.substituteTeacherName}</td><td class="px-4 py-3 whitespace-nowrap">${applyDate}</td><td class="px-4 py-3 whitespace-nowrap"><span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${statusClass}">${statusText}</span>${application.comment ? `<div class="text-xs text-gray-500 mt-1">${application.comment}</div>` : ''}</td><td class="px-4 py-3 whitespace-nowrap text-sm font-medium">${application.status === 'pending' ? ` <button class="text-red-600 hover:text-red-900 cancel-application" data-id="${application.id}"> <i class="fa fa-times mr-1"></i>取消 </button> ` : ''}</td>`;tableBody.appendChild(row);});// 添加取消申请事件监听document.querySelectorAll('.cancel-application').forEach(btn => {btn.addEventListener('click', function() {const applicationId = parseInt(this.getAttribute('data-id'));cancelApplication(applicationId);});});}}// 取消申请function cancelApplication(applicationId) {if (confirm('确定要取消该申请吗?')) {substituteApplications = substituteApplications.filter(app => app.id !== applicationId);renderMyApplications();// 如果当前用户是管理员,同时更新审批列表if (currentUser && (currentUser.role === 'admin' || currentUser.role === 'approval_admin')) {renderApprovalList();}showNotification('申请已取消');}}// 初始化审批管理功能(修复事件绑定问题)function initApprovalManagement() {// 审批搜索输入document.getElementById('approvalSearchInput').addEventListener('input', function(e) {currentFilters.approvalSearch = e.target.value.trim();renderApprovalList();});// 审批状态筛选document.getElementById('approvalStatusFilter').addEventListener('change', function(e) {currentFilters.approvalStatus = e.target.value;renderApprovalList();});// 刷新审批列表document.getElementById('refreshApprovalBtn').addEventListener('click', function() {renderApprovalList();showNotification('列表已刷新');});// 取消审批按钮document.getElementById('cancelApprovalBtn').addEventListener('click', function() {closeApprovalModal();});// 确认审批按钮(修复事件绑定)document.getElementById('confirmApprovalBtn').addEventListener('click', function() {processApproval();});}// 渲染审批列表function renderApprovalList() {// 根据筛选条件过滤申请let filteredApplications = [...substituteApplications];// 状态筛选if (currentFilters.approvalStatus !== 'all') {filteredApplications = filteredApplications.filter(app => app.status === currentFilters.approvalStatus);}// 搜索筛选if (currentFilters.approvalSearch) {const searchTerm = currentFilters.approvalSearch.toLowerCase();filteredApplications = filteredApplications.filter(app => app.applicantName.toLowerCase().includes(searchTerm) ||app.substituteTeacherName.toLowerCase().includes(searchTerm) ||app.courseName.toLowerCase().includes(searchTerm));}// 按申请日期降序排序(最新的在前)filteredApplications.sort((a, b) => new Date(b.applyDate) - new Date(a.applyDate));const tableBody = document.getElementById('approvalTable');const noApprovals = document.getElementById('noApprovals');// 清空表格tableBody.innerHTML = '';if (filteredApplications.length === 0) {noApprovals.classList.remove('hidden');} else {noApprovals.classList.add('hidden');// 添加审批记录行filteredApplications.forEach(application => {const row = document.createElement('tr');row.className = 'hover:bg-gray-50 transition-colors duration-150';// 格式化日期显示const courseDate = new Date(application.courseDate).toLocaleDateString('zh-CN');const applyDate = new Date(application.applyDate).toLocaleDateString('zh-CN');// 状态标签样式let statusClass = '';let statusText = '';switch(application.status) {case 'pending':statusClass = 'bg-yellow-100 text-yellow-800';statusText = '待审批';break;case 'approved':statusClass = 'bg-green-100 text-green-800';statusText = '已批准';break;case 'rejected':statusClass = 'bg-red-100 text-red-800';statusText = '已拒绝';break;default:statusClass = 'bg-gray-100 text-gray-800';statusText = '未知';}// 课程时间文本let courseTimeText = '';switch(application.courseTime) {case 'morning':courseTimeText = '上午';break;case 'afternoon':courseTimeText = '下午';break;case 'evening':courseTimeText = '晚上';break;}row.innerHTML = `<td class="px-4 py-3 whitespace-nowrap">${application.id}</td><td class="px-4 py-3 whitespace-nowrap">${application.applicantName}</td><td class="px-4 py-3"><div class="font-medium text-gray-900">${application.courseName}</div><div class="text-sm text-gray-500">${courseDate} ${courseTimeText}</div><div class="text-sm text-gray-500">${application.courseLocation}</div></td><td class="px-4 py-3 whitespace-nowrap">${application.substituteTeacherName}</td><td class="px-4 py-3 whitespace-nowrap">${applyDate}</td><td class="px-4 py-3 whitespace-nowrap"><span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${statusClass}">${statusText}</span>${application.comment ? `<div class="text-xs text-gray-500 mt-1">${application.comment}</div>` : ''}</td><td class="px-4 py-3 whitespace-nowrap text-sm font-medium">${application.status === 'pending' ? ` <button class="text-blue-600 hover:text-blue-900 process-approval" data-id="${application.id}"> <i class="fa fa-check mr-1"></i>处理 </button> ` : ` <button class="text-indigo-600 hover:text-indigo-900 view-application" data-id="${application.id}"> <i class="fa fa-eye mr-1"></i>查看 </button> `}</td>`;tableBody.appendChild(row);});// 重新绑定处理审批事件(修复事件委托问题)document.querySelectorAll('.process-approval').forEach(btn => {btn.addEventListener('click', function() {const applicationId = parseInt(this.getAttribute('data-id'));openApprovalModal(applicationId);});});}}// 打开审批弹窗function openApprovalModal(applicationId) {const modal = document.getElementById('approvalModal');const applicationIdInput = document.getElementById('approvalApplicationId');// 设置申请IDapplicationIdInput.value = applicationId;// 重置表单document.getElementById('approvalResult').value = 'approved';document.getElementById('approvalComment').value = '';// 显示弹窗并添加动画modal.classList.remove('hidden');setTimeout(() => {modal.classList.remove('opacity-0');modal.querySelector('.scale-95').classList.remove('scale-95');modal.querySelector('.scale-95').classList.add('scale-100');}, 10);// 阻止背景滚动document.body.style.overflow = 'hidden';}// 关闭审批弹窗function closeApprovalModal() {const modal = document.getElementById('approvalModal');// 添加关闭动画modal.classList.add('opacity-0');modal.querySelector('.scale-100').classList.remove('scale-100');modal.querySelector('.scale-100').classList.add('scale-95');// 完全隐藏弹窗setTimeout(() => {modal.classList.add('hidden');// 恢复背景滚动document.body.style.overflow = '';}, 300);}// 处理审批(修复功能)function processApproval() {const applicationId = parseInt(document.getElementById('approvalApplicationId').value);const result = document.getElementById('approvalResult').value;const comment = document.getElementById('approvalComment').value.trim();// 查找申请const index = substituteApplications.findIndex(app => app.id === applicationId);if (index === -1) {alert('申请不存在');return;}// 更新申请状态substituteApplications[index].status = result;substituteApplications[index].comment = comment;// 如果批准,更新代课教师的代课次数if (result === 'approved') {const teacherIndex = teachers.findIndex(t => t.id === substituteApplications[index].substituteTeacherId);if (teacherIndex !== -1) {teachers[teacherIndex].substituteCount++;}}// 关闭弹窗closeApprovalModal();// 更新审批列表renderApprovalList();// 更新教师列表(如果当前在教师管理页面)if (!document.getElementById('teachersSection').classList.contains('hidden')) {renderTeachers();}// 更新统计报表(如果当前在报表页面)if (!document.getElementById('reportsSection').classList.contains('hidden')) {initAdminReports();}// 显示成功通知showNotification(`申请已${result === 'approved' ? '批准' : '拒绝'}`);}// 获取筛选后的教师列表function getFilteredTeachers() {return teachers.filter(teacher => {// 搜索筛选const matchesSearch = currentFilters.search === '' || teacher.name.includes(currentFilters.search) ||teacher.employeeId.includes(currentFilters.search) ||teacher.username.includes(currentFilters.search);// 状态筛选const matchesStatus = currentFilters.status === 'all' || teacher.status === currentFilters.status;// 角色筛选const matchesRole = currentFilters.role === 'all' || teacher.role === currentFilters.role;return matchesSearch && matchesStatus && matchesRole;});}// 渲染教师列表function renderTeachers() {const filteredTeachers = getFilteredTeachers();const totalCount = filteredTeachers.length;const totalPages = Math.ceil(totalCount / pageSize);const startIndex = (currentPage - 1) * pageSize;const endIndex = Math.min(startIndex + pageSize, totalCount);const paginatedTeachers = filteredTeachers.slice(startIndex, endIndex);const tableBody = document.getElementById('teachersTable');const noTeachers = document.getElementById('noTeachers');const showingRange = document.getElementById('showingRange');const totalCountEl = document.getElementById('totalCount');const prevPageBtn = document.getElementById('prevPage');const nextPageBtn = document.getElementById('nextPage');// 清空表格tableBody.innerHTML = '';// 显示空状态或表格内容if (totalCount === 0) {noTeachers.classList.remove('hidden');showingRange.textContent = '0-0';prevPageBtn.disabled = true;nextPageBtn.disabled = true;} else {noTeachers.classList.add('hidden');showingRange.textContent = `${startIndex + 1}-${endIndex}`;// 添加教师行paginatedTeachers.forEach(teacher => {const row = document.createElement('tr');row.className = 'hover:bg-gray-50 transition-colors duration-150';// 格式化日期显示const formattedDate = new Date(teacher.hireDate).toLocaleDateString('zh-CN');// 角色文本let roleText = '';switch(teacher.role) {case 'admin': roleText = '管理员'; break;case 'approval_admin': roleText = '审批管理员'; break;default: roleText = '教师';}row.innerHTML = `<td class="px-4 py-3 whitespace-nowrap">${teacher.id}</td><td class="px-4 py-3 whitespace-nowrap">${teacher.name}</td><td class="px-4 py-3 whitespace-nowrap font-mono text-sm">${teacher.employeeId}</td><td class="px-4 py-3 whitespace-nowrap">${teacher.username}</td><td class="px-4 py-3 whitespace-nowrap"><span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${teacher.role === 'admin' ? 'bg-yellow-100 text-yellow-800' : teacher.role === 'approval_admin' ? 'bg-purple-100 text-purple-800' : 'bg-green-100 text-green-800'}">${roleText}</span></td><td class="px-4 py-3 whitespace-nowrap">${formattedDate}</td><td class="px-4 py-3 whitespace-nowrap"><span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${teacher.status === 'active' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}">${teacher.status === 'active' ? '在职' : '离职'}</span></td><td class="px-4 py-3 whitespace-nowrap"><span id="substituteCount-${teacher.id}">${teacher.substituteCount}</span>${currentUser && currentUser.role === 'admin' ? ` <button class="ml-2 text-indigo-600 hover:text-indigo-900 edit-substitute-count" data-id="${teacher.id}" data-count="${teacher.substituteCount}"> <i class="fa fa-pencil text-xs"></i> </button> ` : ''}</td><td class="px-4 py-3 whitespace-nowrap text-sm font-medium"><button class="text-indigo-600 hover:text-indigo-900 mr-3 edit-teacher" data-id="${teacher.id}"><i class="fa fa-pencil mr-1"></i>编辑</button><button class="text-orange-600 hover:text-orange-900 mr-3 reset-password" data-id="${teacher.id}" data-name="${teacher.name}"><i class="fa fa-key mr-1"></i>重置密码</button><button class="text-red-600 hover:text-red-900 delete-teacher" data-id="${teacher.id}"><i class="fa fa-trash mr-1"></i>删除</button></td>`;tableBody.appendChild(row);});// 更新分页按钮状态prevPageBtn.disabled = currentPage === 1;nextPageBtn.disabled = currentPage === totalPages;}// 更新总数显示totalCountEl.textContent = totalCount;// 添加编辑和删除事件监听document.querySelectorAll('.edit-teacher').forEach(btn => {btn.addEventListener('click', function() {const teacherId = parseInt(this.getAttribute('data-id'));openTeacherModal(teacherId);});});document.querySelectorAll('.delete-teacher').forEach(btn => {btn.addEventListener('click', function() {const teacherId = parseInt(this.getAttribute('data-id'));deleteTeacher(teacherId);});});// 绑定重置密码事件document.querySelectorAll('.reset-password').forEach(btn => {btn.addEventListener('click', function() {const teacherId = parseInt(this.getAttribute('data-id'));const teacherName = this.getAttribute('data-name');openResetPasswordModal(teacherId, teacherName);});});// 绑定编辑代课次数事件document.querySelectorAll('.edit-substitute-count').forEach(btn => {btn.addEventListener('click', function() {const teacherId = parseInt(this.getAttribute('data-id'));const count = parseInt(this.getAttribute('data-count'));openEditSubstituteCountModal(teacherId, count);});});}// 打开教师添加/编辑弹窗function openTeacherModal(teacherId = null) {const modal = document.getElementById('teacherModal');const modalTitle = document.getElementById('teacherModalTitle');const teacherForm = document.getElementById('teacherForm');const teacherIdInput = document.getElementById('teacherId');const nameInput = document.getElementById('teacherNameInput');const employeeIdInput = document.getElementById('teacherEmployeeIdInput');const usernameInput = document.getElementById('teacherUsernameInput');const passwordInput = document.getElementById('teacherPasswordInput');const roleSelect = document.getElementById('teacherRoleSelect');const hireDateInput = document.getElementById('teacherHireDateInput');const statusSelect = document.getElementById('teacherStatusSelect');const employeeIdError = document.getElementById('employeeIdError');// 重置表单和错误提示teacherForm.reset();teacherIdInput.value = '';employeeIdError.classList.add('hidden');// 设置今天的日期为默认入职日期const today = new Date().toISOString().split('T')[0];hireDateInput.value = today;if (teacherId) {// 编辑模式modalTitle.textContent = '编辑教师';const teacher = teachers.find(t => t.id === teacherId);if (teacher) {teacherIdInput.value = teacher.id;nameInput.value = teacher.name;employeeIdInput.value = teacher.employeeId;usernameInput.value = teacher.username;passwordInput.value = teacher.password;roleSelect.value = teacher.role;hireDateInput.value = teacher.hireDate;statusSelect.value = teacher.status;// 编辑模式下密码可以为空,表示不修改密码passwordInput.required = false;passwordInput.placeholder = '不修改密码请留空';}} else {// 添加模式modalTitle.textContent = '添加教师';passwordInput.value = '123456';passwordInput.required = true;passwordInput.placeholder = '';}// 显示弹窗并添加动画modal.classList.remove('hidden');setTimeout(() => {modal.classList.remove('opacity-0');modal.querySelector('.scale-95').classList.remove('scale-95');modal.querySelector('.scale-95').classList.add('scale-100');}, 10);// 阻止背景滚动document.body.style.overflow = 'hidden';}// 关闭教师弹窗function closeTeacherModal() {const modal = document.getElementById('teacherModal');// 添加关闭动画modal.classList.add('opacity-0');modal.querySelector('.scale-100').classList.remove('scale-100');modal.querySelector('.scale-100').classList.add('scale-95');// 完全隐藏弹窗setTimeout(() => {modal.classList.add('hidden');// 恢复背景滚动document.body.style.overflow = '';}, 300);}// 验证工号唯一性function validateEmployeeId() {const employeeIdInput = document.getElementById('teacherEmployeeIdInput');const employeeId = employeeIdInput.value.trim();const teacherIdInput = document.getElementById('teacherId');const currentTeacherId = teacherIdInput.value ? parseInt(teacherIdInput.value) : null;const errorEl = document.getElementById('employeeIdError');// 检查工号是否已存在const exists = teachers.some(teacher => teacher.employeeId === employeeId && teacher.id !== currentTeacherId);if (exists) {errorEl.classList.remove('hidden');return false;} else {errorEl.classList.add('hidden');return true;}}// 保存教师信息function saveTeacher() {const teacherIdInput = document.getElementById('teacherId');const nameInput = document.getElementById('teacherNameInput');const employeeIdInput = document.getElementById('teacherEmployeeIdInput');const usernameInput = document.getElementById('teacherUsernameInput');const passwordInput = document.getElementById('teacherPasswordInput');const roleSelect = document.getElementById('teacherRoleSelect');const hireDateInput = document.getElementById('teacherHireDateInput');const statusSelect = document.getElementById('teacherStatusSelect');// 获取表单值const teacherId = teacherIdInput.value ? parseInt(teacherIdInput.value) : null;const name = nameInput.value.trim();const employeeId = employeeIdInput.value.trim();const username = usernameInput.value.trim();let password = passwordInput.value.trim();const role = roleSelect.value;const hireDate = hireDateInput.value;const status = statusSelect.value;// 基本验证if (!name || !employeeId || !username || (!teacherId && !password)) {alert('请填写所有必填字段');return;}// 验证工号唯一性if (!validateEmployeeId()) {return;}if (teacherId) {// 更新现有教师const index = teachers.findIndex(t => t.id === teacherId);if (index !== -1) {// 如果密码为空,则不更新密码if (!password) {password = teachers[index].password;}teachers[index] = {...teachers[index],name,employeeId,username,password,role,hireDate,status};showNotification('教师信息已更新');}} else {// 添加新教师const newId = teachers.length > 0 ? Math.max(...teachers.map(t => t.id)) + 1 : 1;teachers.push({id: newId,name,employeeId,username,password,role,hireDate,status: status || 'active',substituteCount: 0});showNotification('新教师已添加');}// 关闭弹窗并重新渲染列表closeTeacherModal();renderTeachers();// 更新代课教师选择列表initSubstituteFormData();}// 删除教师function deleteTeacher(teacherId) {const teacher = teachers.find(t => t.id === teacherId);if (!teacher) return;// 检查是否有代课申请关联该教师const hasApplications = substituteApplications.some(app => app.applicantId === teacherId || app.substituteTeacherId === teacherId);if (hasApplications) {if (!confirm(`教师"${teacher.name}"有相关的代课申请记录,确定要删除吗?`)) {return;}} else {if (!confirm(`确定要删除教师"${teacher.name}"吗?`)) {return;}}// 从教师列表中删除teachers = teachers.filter(t => t.id !== teacherId);// 删除相关的申请记录substituteApplications = substituteApplications.filter(app => app.applicantId !== teacherId && app.substituteTeacherId !== teacherId);// 重新渲染renderTeachers();renderMyApplications();initSubstituteFormData();// 如果当前用户是管理员,同时更新审批列表if (currentUser && (currentUser.role === 'admin' || currentUser.role === 'approval_admin')) {renderApprovalList();}showNotification('教师已删除');}// 初始化管理员统计报表function initAdminReports() {// 计算统计数据const totalApplications = substituteApplications.length;const approvedApplications = substituteApplications.filter(app => app.status === 'approved').length;const pendingApplications = substituteApplications.filter(app => app.status === 'pending').length;const rejectedApplications = substituteApplications.filter(app => app.status === 'rejected').length;// 更新统计卡片document.getElementById('totalApplications').textContent = totalApplications;document.getElementById('approvedApplications').textContent = approvedApplications;document.getElementById('pendingApplications').textContent = pendingApplications;// 绘制状态分布图表const statusCtx = document.getElementById('statusChart').getContext('2d');new Chart(statusCtx, {type: 'pie',data: {labels: ['已批准', '待审批', '已拒绝'],datasets: [{data: [approvedApplications, pendingApplications, rejectedApplications],backgroundColor: ['#10B981', '#F59E0B', '#EF4444'],borderWidth: 1}]},options: {responsive: true,maintainAspectRatio: false,plugins: {legend: {position: 'bottom'}}}});// 准备教师代课次数排名数据const teacherRanking = [...teachers].filter(t => t.status === 'active').sort((a, b) => b.substituteCount - a.substituteCount).slice(0, 5);// 绘制教师排名图表const rankingCtx = document.getElementById('teacherRankingChart').getContext('2d');new Chart(rankingCtx, {type: 'bar',data: {labels: teacherRanking.map(t => t.name),datasets: [{label: '代课次数',data: teacherRanking.map(t => t.substituteCount),backgroundColor: '#3B82F6',borderWidth: 1}]},options: {responsive: true,maintainAspectRatio: false,indexAxis: 'y',plugins: {legend: {display: false}},scales: {x: {beginAtZero: true,ticks: {precision: 0}}}}});}// 初始化个人统计数据function initPersonalStats() {// 获取当前用户的申请记录const myApplications = substituteApplications.filter(app => app.applicantId === currentUser.id);// 按状态统计const approved = myApplications.filter(app => app.status === 'approved').length;const pending = myApplications.filter(app => app.status === 'pending').length;const rejected = myApplications.filter(app => app.status === 'rejected').length;// 绘制个人统计const ctx = document.getElementById('personalStatsChart').getContext('2d');new Chart(ctx, {type: 'doughnut',data: {labels: ['已批准', '待审批', '已拒绝'],datasets: [{data: [approved, pending, rejected],backgroundColor: ['#10B981', '#F59E0B', '#EF4444'],borderWidth: 1}]},options: {responsive: true,maintainAspectRatio: false,plugins: {legend: {position: 'bottom'}}}});// 渲染最近申请记录const recentApplications = document.getElementById('recentApplications');const noRecentApplications = document.getElementById('noRecentApplications');recentApplications.innerHTML = '';// 取最近3条申请const recentApps = [...myApplications].sort((a, b) => new Date(b.applyDate) - new Date(a.applyDate)).slice(0, 3);if (recentApps.length === 0) {noRecentApplications.classList.remove('hidden');} else {noRecentApplications.classList.add('hidden');recentApps.forEach(app => {// 状态标签样式let statusClass = '';let statusText = '';switch(app.status) {case 'pending':statusClass = 'bg-yellow-100 text-yellow-800';statusText = '待审批';break;case 'approved':statusClass = 'bg-green-100 text-green-800';statusText = '已批准';break;case 'rejected':statusClass = 'bg-red-100 text-red-800';statusText = '已拒绝';break;}const appDate = new Date(app.applyDate).toLocaleDateString('zh-CN');const courseDate = new Date(app.courseDate).toLocaleDateString('zh-CN');const appCard = document.createElement('div');appCard.className = 'border-l-4 p-3 rounded bg-gray-50 hover:bg-gray-100 transition-colors duration-200';appCard.innerHTML = `<div class="flex justify-between items-start"><div><h4 class="font-medium text-gray-900">${app.courseName}</h4><p class="text-sm text-gray-600 mt-1"><span class="mr-3"><i class="fa fa-calendar-o mr-1"></i>${courseDate}</span><span><i class="fa fa-user-o mr-1"></i>${app.substituteTeacherName}</span></p><p class="text-xs text-gray-500 mt-1">申请日期: ${appDate}</p></div><span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${statusClass}">${statusText}</span></div>`;recentApplications.appendChild(appCard);});}}// 初始化导出功能function initExportFunctions() {// 导出代课节数统计document.getElementById('exportSubstituteBtn').addEventListener('click', function() {exportSubstituteStats();});// 导出请假天数统计document.getElementById('exportLeaveBtn').addEventListener('click', function() {exportLeaveStats();});// 下载模板document.getElementById('downloadTemplateBtn').addEventListener('click', function() {downloadTeacherTemplate();});}// 导出代课节数统计function exportSubstituteStats() {// 准备数据const exportData = teachers.filter(t => t.status === 'active').map(teacher => ({'教师ID': teacher.id,'姓名': teacher.name,'工号': teacher.employeeId,'角色': teacher.role === 'admin' ? '管理员' : teacher.role === 'approval_admin' ? '审批管理员' : '教师','代课次数': teacher.substituteCount,'入职日期': new Date(teacher.hireDate).toLocaleDateString('zh-CN'),'状态': '在职'}));// 创建工作簿和工作表const ws = XLSX.utils.json_to_sheet(exportData);const wb = XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb, ws, "代课次数统计");// 生成并下载文件const today = new Date().toLocaleDateString('zh-CN').replace(/\//g, '-');XLSX.writeFile(wb, `教师代课次数统计_${today}.xlsx`);}// 导出请假天数统计function exportLeaveStats() {// 按申请人分组统计const leaveStats = {};substituteApplications.forEach(app => {if (!leaveStats[app.applicantId]) {leaveStats[app.applicantId] = {applicantId: app.applicantId,applicantName: app.applicantName,total: 0,approved: 0,rejected: 0,pending: 0};}leaveStats[app.applicantId].total++;switch(app.status) {case 'approved':leaveStats[app.applicantId].approved++;break;case 'rejected':leaveStats[app.applicantId].rejected++;break;case 'pending':leaveStats[app.applicantId].pending++;break;}});// 转换为数组并准备导出数据const exportData = Object.values(leaveStats).map(stat => ({'教师ID': stat.applicantId,'姓名': stat.applicantName,'总申请次数': stat.total,'已批准次数': stat.approved,'已拒绝次数': stat.rejected,'待审批次数': stat.pending}));// 创建工作簿和工作表const ws = XLSX.utils.json_to_sheet(exportData);const wb = XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb, ws, "请假次数统计");// 生成并下载文件const today = new Date().toLocaleDateString('zh-CN').replace(/\//g, '-');XLSX.writeFile(wb, `教师请假次数统计_${today}.xlsx`);}// 下载教师导入模板function downloadTeacherTemplate() {// 创建模板数据const templateData = [{ '姓名': '张三', '工号': 'T001', '用户名': 'zhangsan', '角色': '教师', '入职日期': '2023-01-01', '状态': '在职' },{ '姓名': '李四', '工号': 'T002', '用户名': 'lisi', '角色': '教师', '入职日期': '2023-02-15', '状态': '在职' }];// 创建工作簿和工作表const ws = XLSX.utils.json_to_sheet(templateData);const wb = XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb, ws, "教师信息");// 添加说明const noteWs = XLSX.utils.json_to_sheet([{ '说明': '1. 角色只能是"教师"、"管理员"或"审批管理员"' },{ '说明': '2. 状态只能是"在职"或"离职"' },{ '说明': '3. 入职日期格式为YYYY-MM-DD' },{ '说明': '4. 工号和用户名必须唯一' }]);XLSX.utils.book_append_sheet(wb, noteWs, "填写说明");// 生成并下载文件XLSX.writeFile(wb, '教师信息导入模板.xlsx');}// 初始化个人设置表单数据function initProfileFormData() {if (!currentUser) return;document.getElementById('profileUserId').value = currentUser.id;document.getElementById('profileName').value = currentUser.name;document.getElementById('profileEmployeeId').value = currentUser.employeeId;document.getElementById('profileUsername').value = currentUser.username;// 设置角色文本let roleText = '';switch(currentUser.role) {case 'admin': roleText = '管理员'; break;case 'approval_admin': roleText = '审批管理员'; break;default: roleText = '教师';}document.getElementById('profileRole').value = roleText;// 设置入职日期document.getElementById('profileHireDate').value = new Date(currentUser.hireDate).toLocaleDateString('zh-CN');// 设置密码修改表单的用户IDdocument.getElementById('passwordUserId').value = currentUser.id;}// 初始化个人设置和密码修改功能function initProfileSettings() {// 个人信息表单提交document.getElementById('profileForm').addEventListener('submit', function(e) {e.preventDefault();updateProfileInfo();});// 密码修改表单提交document.getElementById('changePasswordForm').addEventListener('submit', function(e) {e.preventDefault();changePassword();});// 密码验证document.getElementById('confirmPassword').addEventListener('input', validatePasswordMatch);}// 更新个人信息function updateProfileInfo() {const userId = parseInt(document.getElementById('profileUserId').value);const name = document.getElementById('profileName').value.trim();const username = document.getElementById('profileUsername').value.trim();if (!name || !username) {alert('请填写所有必填字段');return;}// 查找并更新用户信息const index = teachers.findIndex(t => t.id === userId);if (index !== -1) {teachers[index].name = name;teachers[index].username = username;// 更新当前用户信息currentUser.name = name;currentUser.username = username;// 更新显示的用户名let roleText = '';switch(currentUser.role) {case 'admin': roleText = '管理员'; break;case 'approval_admin': roleText = '审批管理员'; break;default: roleText = '教师';}document.getElementById('currentUser').textContent = `${roleText} - ${currentUser.name}`;// 更新申请表单中的申请人姓名document.getElementById('applicantName').value = currentUser.name;// 更新相关列表renderTeachers();renderMyApplications();if (currentUser && (currentUser.role === 'admin' || currentUser.role === 'approval_admin')) {renderApprovalList();}showNotification('个人信息已更新');}}// 验证密码是否匹配function validatePasswordMatch() {const newPassword = document.getElementById('newPassword').value;const confirmPassword = document.getElementById('confirmPassword').value;const errorEl = document.getElementById('passwordMismatchError');if (newPassword && confirmPassword && newPassword !== confirmPassword) {errorEl.classList.remove('hidden');return false;} else {errorEl.classList.add('hidden');return true;}}// 修改密码function changePassword() {const userId = parseInt(document.getElementById('passwordUserId').value);const currentPassword = document.getElementById('currentPassword').value;const newPassword = document.getElementById('newPassword').value;const confirmPassword = document.getElementById('confirmPassword').value;// 基本验证if (!currentPassword || !newPassword || !confirmPassword) {alert('请填写所有字段');return;}// 密码长度验证if (newPassword.length < 6) {alert('密码长度至少为6位');return;}// 密码匹配验证if (!validatePasswordMatch()) {return;}// 查找用户const index = teachers.findIndex(t => t.id === userId);if (index === -1) {alert('用户不存在');return;}// 验证当前密码const currentPasswordError = document.getElementById('currentPasswordError');if (teachers[index].password !== currentPassword) {currentPasswordError.classList.remove('hidden');return;}currentPasswordError.classList.add('hidden');// 更新密码teachers[index].password = newPassword;currentUser.password = newPassword;// 重置表单document.getElementById('changePasswordForm').reset();showNotification('密码已更新,请使用新密码重新登录');// 延迟登出,让用户看到通知setTimeout(() => {// 模拟登出document.getElementById('logoutBtn').click();}, 2000);}// 打开重置密码弹窗function openResetPasswordModal(teacherId, teacherName) {const modal = document.getElementById('resetPasswordModal');const userIdInput = document.getElementById('resetPasswordUserId');const userNameSpan = document.getElementById('resetPasswordUserName');// 设置用户ID和姓名userIdInput.value = teacherId;userNameSpan.textContent = teacherName;// 显示弹窗并添加动画modal.classList.remove('hidden');setTimeout(() => {modal.classList.remove('opacity-0');modal.querySelector('.scale-95').classList.remove('scale-95');modal.querySelector('.scale-95').classList.add('scale-100');}, 10);// 阻止背景滚动document.body.style.overflow = 'hidden';}// 关闭重置密码弹窗function closeResetPasswordModal() {const modal = document.getElementById('resetPasswordModal');// 添加关闭动画modal.classList.add('opacity-0');modal.querySelector('.scale-100').classList.remove('scale-100');modal.querySelector('.scale-100').classList.add('scale-95');// 完全隐藏弹窗setTimeout(() => {modal.classList.add('hidden');// 恢复背景滚动document.body.style.overflow = '';}, 300);}// 确认重置密码function confirmResetPassword() {const teacherId = parseInt(document.getElementById('resetPasswordUserId').value);// 查找用户const index = teachers.findIndex(t => t.id === teacherId);if (index === -1) {alert('用户不存在');return;}// 重置密码为默认值teachers[index].password = '123456';// 关闭弹窗并刷新列表closeResetPasswordModal();renderTeachers();showNotification('密码已重置为默认值123456');}// 打开编辑代课次数弹窗function openEditSubstituteCountModal(teacherId, count) {const modal = document.getElementById('editSubstituteCountModal');const userIdInput = document.getElementById('editSubstituteCountUserId');const countInput = document.getElementById('substituteCount');// 设置用户ID和当前次数userIdInput.value = teacherId;countInput.value = count;// 显示弹窗并添加动画modal.classList.remove('hidden');setTimeout(() => {modal.classList.remove('opacity-0');modal.querySelector('.scale-95').classList.remove('scale-95');modal.querySelector('.scale-95').classList.add('scale-100');}, 10);// 阻止背景滚动document.body.style.overflow = 'hidden';}// 关闭编辑代课次数弹窗function closeEditSubstituteCountModal() {const modal = document.getElementById('editSubstituteCountModal');// 添加关闭动画modal.classList.add('opacity-0');modal.querySelector('.scale-100').classList.remove('scale-100');modal.querySelector('.scale-100').classList.add('scale-95');// 完全隐藏弹窗setTimeout(() => {modal.classList.add('hidden');// 恢复背景滚动document.body.style.overflow = '';}, 300);}// 确认编辑代课次数function confirmEditSubstituteCount() {const teacherId = parseInt(document.getElementById('editSubstituteCountUserId').value);const count = parseInt(document.getElementById('substituteCount').value);if (isNaN(count) || count < 0) {alert('请输入有效的次数');return;}// 查找用户const index = teachers.findIndex(t => t.id === teacherId);if (index === -1) {alert('用户不存在');return;}// 更新代课次数teachers[index].substituteCount = count;// 关闭弹窗并刷新列表closeEditSubstituteCountModal();renderTeachers();// 更新统计报表(如果当前在报表页面)if (!document.getElementById('reportsSection').classList.contains('hidden')) {initAdminReports();}showNotification('代课次数已更新');}// 显示通知function showNotification(message) {// 检查是否已存在通知元素let notification = document.getElementById('notification');if (!notification) {// 创建通知元素notification = document.createElement('div');notification.id = 'notification';notification.className = 'fixed bottom-4 right-4 bg-gray-800 text-white px-4 py-3 rounded shadow-lg transform translate-y-10 opacity-0 transition-all duration-300 z-50 flex items-center';notification.innerHTML = `<i class="fa fa-check-circle text-green-400 mr-2"></i><span id="notificationMessage"></span>`;document.body.appendChild(notification);}// 设置通知内容document.getElementById('notificationMessage').textContent = message;// 显示通知setTimeout(() => {notification.classList.remove('translate-y-10', 'opacity-0');}, 10);// 3秒后隐藏通知setTimeout(() => {notification.classList.add('translate-y-10', 'opacity-0');}, 3000);}</script>
</body>
</html>

让我们一起,用技术赋能教育,让教师工作更轻松!
© 2024 开源教师工具箱 · 社区共建项目

http://www.dtcms.com/a/479171.html

相关文章:

  • 15. shell编程之#!与/bin/bas 之间需要空格吗
  • 套模板网站网络seo优化推广
  • 聪明的上海网站帮别人做网站推广犯法吗
  • HTML 总结
  • HTML应用指南:利用POST请求获取全国塔斯汀门店位置信息
  • 鞍山 网站建设网站规划网站建设报价表
  • 云服务器怎么设置虚拟IP,云服务器能起虚拟ip吗
  • Fast DDS 默认传输机制详解:共享内存与 UDP 的智能选择
  • thinkphp开发企业网站如何做优酷网站点击赚钱
  • 供应链金融对生命科学仪器企业市场竞争力的影响研究
  • 高性能高可用设计
  • 【系统分析师】写作框架:需求分析方法及应用
  • dedecms 做网站青岛企业网站建设公司
  • wordpress网仿站建设项目前期收费查询网站
  • tcp和udp协议报文段的报文格式
  • C#异步编程:async修饰方法的返回类型说明
  • MC33PT2000控制主要功能函数代码详解三
  • C语言--数据类型
  • 需求冻结后仍频繁突破怎么办
  • 做外贸电商网站士兵突击网站怎么做
  • Windows7MasterSetup_1.0.0.25.exe 怎么安装?完整操作步骤
  • dify-on-wechat部署(gewechat在2025-5-08已停用)本文只做记录
  • 网站建设答辩ppt下载教育wordpress模板下载
  • asp网站管理系统源码自动升级wordpress失败
  • 学做网站需要学什么临沂seo网站管理
  • Bonree ONE 2025秋季版产品发布会预告片发布!
  • DNS 会如何影响你的上网体验
  • 上海网站建设那家好搜狗优化好的网站
  • 网站优化的方式建筑工程资料网站
  • 【视觉SLAM十四讲】后端 1