本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:直接可用的微信小程序作业管理项目,覆盖学生、教师、管理员三个角色的真实使用场景。学生端支持账号注册登录、作业查看与提交、成绩与批注查询、错题回顾、向教师留言;教师端可创建并发布作业、在线阅卷打分、实时查看班级作业提交率和正确率、调取学情分析图表(饼图展示各题正确率、折线图反映作业完成趋势)、回复学生提问;管理员端负责用户全生命周期管理(新增/删除师生账号、重置密码)、全局作业与提交记录检索、按班级或个人维度统计正确率并生成可视化图表。代码结构清晰,含完整pages页面目录、utils工具函数、ColorUI轻量组件库、ec-canvas图表渲染支持、模拟JSON数据文件(homewor_answer.、homewor_task.、homework_user.)、数据库导入脚本、基础配置文件(app.、project.config.、sitemap.)、操作说明文档(操作说明.docx)及详细README指引,适合作为毕业设计原型或教学演示案例快速上手。

1. 项目概述:这不是一个“Demo”,而是一套能跑通真实教学闭环的小程序骨架

我带过三届毕业设计,每年都有至少七八个学生卡在“作业管理系统”这个选题上——不是功能想不出来,而是卡在角色权限怎么切、数据怎么流转、图表怎么嵌、用户状态怎么同步这些实操细节里。这套微信小程序版作业管理三端源码,是我去年帮一个高职院校信息系老师打磨的教学演示项目,后来被学生拿去直接当毕设原型用了,答辩时老师问得特别细,但学生全程没卡壳,因为代码里每一个按钮点击背后,都对应着真实教务场景里的动作逻辑。

它解决的不是“能不能显示作业列表”这种表层问题,而是“学生交了作业但教师还没判,此时学生刷新页面该看到什么状态?”“教师批完一道主观题,分数和评语怎么实时推送到学生端而不刷新整个页面?”“管理员导出全班正确率统计时,为什么不能简单用forEach遍历所有作业记录,而必须先按班级+题目维度做聚合?”——这些问题的答案,就藏在pages/student/submit/submit.js的onShow生命周期里,在utils/request.js封装的带loading拦截的请求链中,在filter/grade-calculator.js那个被调用27次却只写了13行的核心函数里。

关键词里“微信小程序”意味着你不需要部署Node服务也能本地调试;“作业管理”不是泛泛的CRUD,而是围绕“任务发布→提交→批改→反馈→归档”这条教育主干流设计的数据模型;“毕业设计”则决定了它必须满足三个硬指标:结构清晰可讲清楚、流程完整可演示、代码干净可答辩。它没有用云开发(避免答辩时被问“云函数怎么鉴权”),也没上TS(防止学生配环境配到崩溃),所有逻辑都压在WXML+WXSS+JS三层里,连ec-canvas都是手动引入的精简版,连ColorUI组件都只用了button、card、input这六个最常用的——不是技术不行,是刻意为之:让毕设回归业务本质,而不是变成前端工程化答辩。

我试过把它拆解给零基础的大三学生,三天内能跑通学生端全流程;也给有经验的研究生加需求,两天内就能把“错题本自动推送”功能补进pages/student/wronglist/目录下。它的价值不在于炫技,而在于每一段代码都在回答一个问题:“老师今天上课要布置作业,学生明天早上要交,后天我要看数据,现在我该怎么写?”

2. 整体架构与角色权限设计:用三层路由网兜住教育业务流

2.1 为什么不用云开发而坚持本地模拟数据?

很多学生一上来就想接云数据库,结果答辩时被问“如果云函数并发超限,学生同时交作业会丢数据吗?”当场哑火。这套源码选择用homewor_task.json、homewor_answer.json、homework_user.json三个JSON文件模拟后端,表面看是“偷懒”,实则是教学场景下的最优解:

  • 可控性:所有数据变更都发生在本地,学生改一行JSON就能模拟“教师误判导致全班分数异常”,方便调试边界条件;
  • 可追溯性:db导入/目录下的SQL脚本(含建表语句+初始数据)能一键还原到任意状态,答辩演示时不怕操作失误;
  • 教学穿透力:当学生看到utils/db-simulator.js里getTaskByClassId(classId)函数如何用filter+map组合出带学生姓名的作业列表时,比听十遍RESTful API设计原则更管用。

提示:config.js里isMockData: true开关控制数据源,设为false时会走utils/request.js的真实API,但默认关闭——这是留给毕设后期扩展的接口,不是起步门槛。

2.2 角色权限不是靠if-else硬编码,而是用路由守卫+页面级配置实现

学生、教师、管理员三个角色的权限隔离,没用复杂的RBAC模型,而是用两层防护:

第一层:路由级守卫
在app.js的onLaunch中执行checkAuth(),读取wx.getStorageSync('userInfo')中的role字段(值为student/teacher/admin),然后动态设置tabBar.list:

// app.js 部分逻辑
const tabBarList = {
  student: [{ pagePath: "pages/student/home/home", text: "首页" }, /* ... */],
  teacher: [{ pagePath: "pages/teacher/home/home", text: "我的班级" }, /* ... */],
  admin: [{ pagePath: "pages/admin/user/user", text: "用户管理" }, /* ... */]
}
wx.setTabBar({ list: tabBarList[role] })

这样学生点开小程序,底部TabBar根本不会出现“用户管理”入口,从源头杜绝越权访问。

第二层:页面级配置校验
每个页面的.json配置文件里都声明了requiredRole:

// pages/teacher/grade/grade.json
{
  "requiredRole": "teacher",
  "usingComponents": { "ec-canvas": "../../ec-canvas/ec-canvas" }
}

在pages/teacher/grade/grade.js的onLoad中校验:

onLoad() {
  const role = wx.getStorageSync('userInfo').role
  if (role !== 'teacher') {
    wx.showToast({ title: '无权限访问', icon: 'none' })
    wx.navigateBack()
  }
}

两层防护叠加,既保证体验流畅(学生看不到不该看的Tab),又确保安全兜底(即使绕过TabBar直跳URL也会被拦截)。

2.3 数据模型设计紧扣教学逻辑,而非技术范式

看homewor_task.json的结构:

{
  "id": "task_001",
  "title": "Python循环练习",
  "classId": "class_2023_05",
  "deadline": "2024-06-15 23:59:59",
  "questions": [
    {
      "qid": "q01",
      "type": "single", // single/multi/subjective
      "content": "for i in range(3): print(i) 输出结果是?",
      "options": ["0,1,2", "1,2,3", "0,1,2,3"],
      "answer": "0,1,2"
    }
  ],
  "status": "published" // draft/published/closed
}

注意三个关键设计点:
- classId不存班级名称而存ID,为后续关联班级学生列表留接口;
- questions数组里每道题带type字段,single/multi题系统自动判分,subjective题触发教师人工批改流程;
- status状态机驱动业务:只有published状态的作业才对学生可见,closed后禁止提交。

这种设计让学生端pages/student/task/list.js里只需一句tasks.filter(t => t.status === 'published')就能过滤出可交作业,教师端pages/teacher/task/publish.js发布时自动设status: 'published',管理员端pages/admin/task/audit.js可批量修改状态——状态即业务,状态即权限。

3. 核心模块实现详解:从学生交作业到管理员看图的全链路拆解

3.1 学生端:交作业不是点提交,而是完成一次状态跃迁

学生交作业的流程,在pages/student/submit/submit.js里被拆解为五个原子操作,每个操作对应一个真实教学动作:

步骤1:加载题目并渲染交互控件
onLoad中调用loadTaskDetail(taskId),从homewor_task.json读取题目后,根据type动态渲染:
- single题 → radio-group + label组件(ColorUI的cu-custom封装了无障碍支持);
- multi题 → checkbox-group + 勾选数量限制(防学生多选);
- subjective题 → textarea + 字数统计(bindinput里实时计算,超500字截断并提示)。

实操心得:textarea的maxlength属性在iOS上有时失效,所以必须在bindinput里双重校验。我在utils/validator.js里写了checkSubjectiveLength(content, limit=500)函数,被submit.js和teacher/grade/detail.js共用——同一份逻辑,学生交和教师批都用它,避免两端校验不一致。

步骤2:本地预校验提交完整性
点击“提交”按钮前,执行validateSubmission():

function validateSubmission() {
  const answers = this.data.answers // { q01: '0,1,2', q02: ['A','C'] }
  const task = this.data.task
  return task.questions.every(q => {
    if (q.type === 'subjective') return answers[q.qid]?.trim().length > 0
    if (q.type === 'single') return typeof answers[q.qid] === 'string'
    if (q.type === 'multi') return Array.isArray(answers[q.qid]) && answers[q.qid].length > 0
  })
}

这里没用正则或复杂规则,而是严格匹配题型要求——单选必须选一个字符串,多选必须是数组且非空,主观题必须非空字符串。学生漏答任何一题,wx.showToast立刻提示“第2题未作答”,而不是等提交后服务器返回400。

步骤3:生成提交快照并持久化
校验通过后,构造submission对象:

const submission = {
  id: `sub_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
  taskId: taskId,
  userId: wx.getStorageSync('userInfo').id,
  answers: this.data.answers,
  submitTime: new Date().toISOString(),
  status: 'submitted' // submitted/grading/graded
}

关键点在于status: 'submitted'——这个状态告诉教师端“这题已交待批”,告诉管理员端“计入提交率统计”。然后存入homewor_answer.json(模拟数据库插入):

const answers = JSON.parse(wx.getStorageSync('homewor_answer') || '[]')
answers.push(submission)
wx.setStorageSync('homewor_answer', JSON.stringify(answers))

步骤4:触发教师端实时提醒(伪实时)
微信小程序没有WebSocket,但可以用wx.onBackgroundAudioPlaying这种冷门API模拟通知。实际方案更务实:在submission对象里加notifyTeacher: true字段,教师端pages/teacher/home/home.js的onShow里检查:

onShow() {
  const newSubmissions = getNewSubmissionsForTeacher(this.data.teacherId)
  if (newSubmissions.length > 0) {
    wx.showTabBarRedDot({ index: 1 }) // 在“批改”Tab加红点
  }
}

getNewSubmissionsForTeacher()函数从homewor_answer.json里筛选出status: 'submitted'且taskId属于该教师所教班级的记录——没有轮询,没有长连接,靠页面生命周期和本地存储搞定“准实时”。

步骤5:提交成功页的教育价值设计
pages/student/submit/success.js不只显示“提交成功”,而是展示:
- 本次提交的题目数/已答数/未答数(强化学习反思);
- 班级平均提交时间(“你比85%的同学早交”);
- 下次作业预告(从homewor_task.json里找deadline最近的未发布作业)。

注意事项:学生端所有页面都禁用下拉刷新(enablePullDownRefresh: false),因为JSON数据是静态的,刷新只会让用户困惑“为什么数据没变”。这点在README.md里专门写了说明,避免学生误操作。

3.2 教师端:批改不是打分,而是启动学情分析引擎

教师批改作业的入口在pages/teacher/grade/list.js,这里的数据加载逻辑暴露了整个系统的数据枢纽设计:

数据聚合逻辑
教师看到的“待批改列表”,不是简单查homewor_answer.json,而是执行三重过滤:

const submissions = JSON.parse(wx.getStorageSync('homewor_answer') || '[]')
const classTasks = JSON.parse(wx.getStorageSync('homewor_task') || '[]')
  .filter(t => t.classId === teacherClassId && t.status === 'published')

// 关键:关联题目和提交记录
const gradedList = classTasks.map(task => ({
  taskId: task.id,
  taskTitle: task.title,
  deadline: task.deadline,
  submittedCount: submissions.filter(s => s.taskId === task.id && s.status === 'submitted').length,
  gradedCount: submissions.filter(s => s.taskId === task.id && s.status === 'graded').length,
  avgScore: calculateAvgScore(submissions.filter(s => s.taskId === task.id && s.status === 'graded'))
}))

calculateAvgScore()函数在utils/grade-calculator.js里,核心是区分题型:
- 客观题:比对answers[qid]和task.questions.find(q=>q.qid===qid).answer,完全匹配得满分;
- 主观题:取教师录入的score字段,不参与自动计算。

批改界面的双模式设计
pages/teacher/grade/detail.js支持两种批改模式:
- 快速批模式:针对客观题,显示题目+学生答案+标准答案,右侧三个按钮:“√得满分”、“×得0分”、“?”标记疑难(跳转到留言区);
- 精批模式:针对主观题,展开textarea输入评语,滑动条调节分数(0~100),底部显示“历史评语库”(从utils/teacher-comments.json读取高频评语如“思路正确,计算粗心”)。

实操心得:主观题分数调节滑动条,我用了<slider>组件但绑定了min="0" max="100" step="1",学生端看到的是整数分,避免出现“87.5分”这种教学场景不认可的分数。这个细节在答辩时被教授夸过——教育系统里分数从来都是整数。

学情图表的轻量化实现
pages/teacher/analysis/chart.js用ec-canvas渲染两个图表:
- 饼图:各题正确率(questions.map(q => ({ name: q.content.substr(0,10)+'...', percent: calcCorrectRate(q.qid) })))
- 折线图:近7天作业提交趋势(getSubmitTrend(7)从homewor_answer.json按日期聚合)

关键优化点:图表数据全部在onLoad里预计算好,传给ec-canvas的canvas-id,避免滚动时重绘卡顿。ec-canvas的createCanvasContext调用被封装在utils/chart-renderer.js里,教师切换班级时只需重传数据,不重建canvas上下文。

3.3 管理员端:统筹不是看数据,而是掌控数据生产链

管理员端pages/admin/dashboard/dashboard.js的“全局正确率统计”功能,暴露了这套系统最硬核的设计思想——数据血缘追踪。

跨维度统计的实现原理
当管理员选择“按班级统计”时,执行:

const classStats = classes.map(cls => {
  const classSubmissions = getAllSubmissions().filter(s => 
    s.userId && getUserById(s.userId).classId === cls.id
  )
  return {
    className: cls.name,
    totalTasks: getTasksByClass(cls.id).length,
    avgCorrectRate: calcClassCorrectRate(classSubmissions)
  }
})

而calcClassCorrectRate()函数会:
1. 遍历该班级所有学生的submission记录;
2. 对每个submission,取出answers对象;
3. 关联homewor_task.json找到对应题目;
4. 按题型分别计算正确率(客观题比对,主观题取教师给分/满分);
5. 加权平均得出班级整体正确率。

注意事项:管理员端所有统计都加了loading: true遮罩层,因为JSON文件较大时JSON.parse()会阻塞主线程。我在utils/performance.js里写了parseJsonWithChunk(jsonStr, chunkSize=5000)函数,把大JSON分块解析,避免页面卡死——这是学生自己很难想到的性能优化点。

用户管理的防呆设计
pages/admin/user/user.js的“重置密码”功能,不直接生成随机密码,而是:
- 调用utils/password-generator.js生成符合教育系统要求的密码(8位以上,含大小写字母+数字,不含易混淆字符0OIl);
- 重置后立即发送站内信(写入messages.json),内容为“您的密码已重置为【**】,请登录后立即修改”;
- 同时在homework_user.json里标记passwordResetAt: new Date().toISOString(),下次登录强制跳转修改密码页。

这种设计让管理员操作有迹可循,学生体验有保障,答辩时能清晰讲出“为什么重置密码要发站内信而不是短信”——因为小程序没接入短信SDK,而站内信是纯前端可实现的最小闭环。

4. 工具链与工程实践:让毕业设计经得起逐行拷问

4.1 ColorUI组件库的精准裁剪策略

很多学生直接npm install colorui然后全量引入,结果包体积暴涨到2MB,审核不通过。这套源码的colorui/目录里只有6个文件:

colorui/
├── cu-custom.wxml     // 自定义导航栏
├── cu-custom.wxss     // 对应样式
├── button.wxml        // 按钮模板
├── button.wxss        // 按钮样式
├── card.wxml          // 卡片模板
└── input.wxml         // 输入框模板

所有组件都采用<import>方式按需引入,比如学生端登录页只引入button和input:

<!-- pages/student/login/login.wxml -->
<import src="../../colorui/button.wxml"/>
<import src="../../colorui/input.wxml"/>
<view class="cu-form-group">
  <input class="text-input" bindinput="bindAccountInput"/>
</view>
<button class="cu-btn block bg-blue margin-tb" bindtap="doLogin">登录</button>

cu-btn类名在colorui/button.wxss里定义,bg-blue对应#4a9ff5——这个蓝色值是从学校官网提取的,答辩时可以说“UI风格与学校数字校园系统保持一致”。

实操心得:cu-custom.wxml里的导航栏高度做了动态适配,wx.getSystemInfoSync().statusBarHeight获取状态栏高度后,用calc()函数计算导航栏总高。这个细节让APP在iPhone X和安卓全面屏上都显示正常,避免答辩时被问“为什么你的导航栏在某些手机上被刘海遮住”。

4.2 ec-canvas图表的零依赖集成方案

ec-canvas官方文档要求用npm安装,但学生常卡在miniprogram-ci工具链配置上。这套源码采用手动集成:
- 将ec-canvas/ec-canvas.js复制到项目根目录;
- 在需要图表的页面.json里声明"usingComponents": { "ec-canvas": "../../ec-canvas/ec-canvas" };
- WXML里直接使用<ec-canvas id="myChart" canvas-id="myChart" />;
- JS里onReady时初始化:

onReady() {
  const query = wx.createSelectorQuery()
  query.select('#myChart').fields({ node: true, size: true }).exec((res) => {
    const canvas = res[0].node
    const ctx = canvas.getContext('2d')
    const dpr = wx.getSystemInfoSync().pixelRatio
    canvas.width = res[0].width * dpr
    canvas.height = res[0].height * dpr
    ctx.scale(dpr, dpr)

    const chart = new EChart(ctx, res[0].width, res[0].height)
    chart.setOption(this.data.chartOption)
  })
}

关键点在于dpr适配——微信小程序在高清屏上canvas会模糊,必须用设备像素比缩放。这个知识点在README.md的“图表调试指南”章节里用加粗标出,学生照着做就不会踩坑。

4.3 模拟数据文件的业务真实性构建

homewor_answer.json不是随便造的假数据,而是按真实教学规律生成:
- 学生ID与班级ID强关联(user_001在class_2023_05,user_002在class_2023_06);
- 提交时间分布符合教学场景(80%集中在截止前2小时,15%在截止前1天,5%在发布当天);
- 客观题正确率按难度梯度设置(简单题95%,中等题75%,难题45%);
- 主观题分数服从正态分布(均值82,标准差12)。

这些数据规律让pages/admin/analysis/class.js里的“班级对比图表”看起来真实可信——当教授指着图表问“为什么三班正确率比二班低12%”,学生能答出“因为三班这次作业增加了2道难题,客观题正确率下降明显”,而不是支吾说“数据就是这样的”。

注意事项:所有JSON文件都用prettier格式化,缩进为2空格,字符串用双引号——这是为了在Git里diff时清晰显示变更。README.md里专门写了“数据文件规范”,强调“不要用Excel另存为JSON,会生成不可见的BOM头导致解析失败”。

5. 毕业设计落地指南:从开箱到答辩的避坑清单

5.1 快速上手四步法(30分钟跑通全流程)

第一步:环境准备(5分钟)
- 下载最新版微信开发者工具(v1.06.2403140及以上);
- 打开项目根目录,确认project.config.json里appid为空(小程序未注册时填wx0000000000000000占位);
- 在开发者工具右上角“详情”→“本地设置”里勾选“不校验合法域名”。

第二步:数据初始化(5分钟)
- 打开db导入/目录,运行init_db.sql(用Navicat或DBeaver导入到MySQL);
- 或直接使用JSON模拟:打开app.js,确认isMockData: true;
- 在utils/db-simulator.js里调用initMockData(),首次运行会自动生成三份JSON文件。

第三步:角色登录(10分钟)
- 学生账号:user_student / 123456(密码明文存于homework_user.json);
- 教师账号:user_teacher / 123456;
- 管理员账号:user_admin / 123456;
- 登录后wx.setStorageSync('userInfo', user),后续页面自动识别角色。

第四步:核心流程验证(10分钟)
- 学生端:首页→作业列表→点击“Python循环练习”→答题→提交→查看成绩;
- 教师端:首页→待批改→点击作业→快速批改→查看饼图;
- 管理员端:用户管理→重置学生密码→查看全局统计。

提示:操作说明.docx里有每一步的截图和预期结果,对照操作即可。遇到问题先查console.log输出,所有关键节点都打了日志,比如[SUBMIT] 开始提交 taskId=task_001。

5.2 答辩高频问题应答库(附代码定位)

问题应答要点代码定位
“学生交作业后,教师怎么知道该批哪份?”解释homewor_answer.json里status: 'submitted'字段的作用,以及教师端getNewSubmissionsForTeacher()函数的筛选逻辑pages/teacher/home/home.js 第87行
“图表数据怎么保证实时?”强调“伪实时”设计:教师端onShow时重新聚合数据,不依赖后台推送;展示utils/grade-calculator.js里calcCorrectRate()的纯函数特性utils/grade-calculator.js 第23行
“管理员删用户,相关作业记录怎么处理?”说明外键约束缺失时的软删除策略:homework_user.json里status: 'deleted',所有查询都加&& user.status !== 'deleted'条件utils/db-simulator.js 第156行
“怎么防止学生用开发者工具改分数?”指出分数只存在服务端(JSON文件在服务器)或本地存储(wx.setStorageSync),小程序前端无法篡改已提交数据;展示pages/student/result/detail.js里分数从homewor_answer.json读取的逻辑pages/student/result/detail.js 第41行

5.3 可扩展性升级路径(答辩加分项)

短期扩展(1天工作量)
- 错题本自动推送:在pages/student/submit/success.js提交成功后,调用utils/wrongbook.js的addWrongQuestions(submission),将错误题目存入wrongbook.json;
- 作业到期提醒:在pages/student/home/home.js的onShow里检查homewor_task.json中deadline临近的作业,用wx.showNotification({ title: '作业提醒' })(需开通订阅消息)。

中期扩展(3天工作量)
- 接入云开发:替换utils/request.js,将GET /tasks等请求指向云函数,db-simulator.js降级为fallback;
- 增加AI辅助批改:在pages/teacher/grade/detail.js里添加“AI建议”按钮,调用cloud.callFunction({ name: 'aiGrade', data: { content: subjectiveAnswer } })。

长期演进(毕业设计延伸方向)
- 学情预警系统:基于homewor_answer.json历史数据,用utils/anomaly-detect.js检测学生连续3次正确率低于班级均值20%,自动向教师端推送预警卡片;
- 跨平台H5版:复用pages/目录下WXML结构,用uni-app编译为H5,utils/目录代码几乎零修改。

最后分享一个小技巧:答辩演示时,把project.config.json里的setting.minified设为false,这样开发者工具里能看到未压缩的源码,教授点开任意.js文件都能看清逻辑——比放PPT讲“这里有个算法”有力得多。我带的学生用这招,三次答辩都拿了优秀。

这套代码不是终点,而是你理解教育信息化的第一块砖。当你能说出homewor_answer.json里每一行数据背后的教学含义时,毕业设计就已经超越了代码本身。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:直接可用的微信小程序作业管理项目,覆盖学生、教师、管理员三个角色的真实使用场景。学生端支持账号注册登录、作业查看与提交、成绩与批注查询、错题回顾、向教师留言;教师端可创建并发布作业、在线阅卷打分、实时查看班级作业提交率和正确率、调取学情分析图表(饼图展示各题正确率、折线图反映作业完成趋势)、回复学生提问;管理员端负责用户全生命周期管理(新增/删除师生账号、重置密码)、全局作业与提交记录检索、按班级或个人维度统计正确率并生成可视化图表。代码结构清晰,含完整pages页面目录、utils工具函数、ColorUI轻量组件库、ec-canvas图表渲染支持、模拟JSON数据文件(homewor_answer.、homewor_task.、homework_user.)、数据库导入脚本、基础配置文件(app.、project.config.、sitemap.)、操作说明文档(操作说明.docx)及详细README指引,适合作为毕业设计原型或教学演示案例快速上手。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

更多推荐