2 Star 0 Fork 0

guzige2012 / online-teaching-manager-wx

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

在线教学管理小程序开发文档

项目名称

在线教学管理系统

需求描述

​ 本项目旨在为教师和学生提供一个在线教学管理平台,教师可以发布课程、布置作业、批改作业、发布公告等,学生可以查看课程、提交作业、查看公告等。项目包括一个后端(NodeJS+express+MySQL)、一个小程序应用(原生+vant)。

功能说明

  1. 用户管理
    • 注册:用户可以注册为教师或学生账号。
    • 登录:用户可以使用账号和密码登录系统。
    • 修改个人信息:用户可以修改自己的个人信息,如姓名、密码等。
    • 注销:用户可以注销自己的账号。
  2. 课程管理
    • 发布课程:教师可以发布新的课程,包括课程名称、课程描述、课程封面等信息。
    • 修改课程:教师可以修改自己发布的课程信息。
    • 删除课程:教师可以删除自己发布的课程。
    • 查看课程:学生可以查看所有已发布的课程;教师可以查看自己发布的课程。
  3. 作业管理
    • 布置作业:教师可以为课程布置作业,包括作业名称、作业描述、截止日期等信息。
    • 修改作业:教师可以修改自己布置的作业信息。
    • 删除作业:教师可以删除自己布置的作业。
    • 提交作业:学生可以在截止日期前提交作业。
    • 批改作业:教师可以批改学生提交的作业,并给出评分和评语。
    • 查看作业:学生可以查看自己已提交的作业及教师的批改结果。
  4. 公告管理
    • 发布公告:教师可以发布公告,包括公告标题、公告内容等信息。
    • 修改公告:教师可以修改自己发布的公告信息。
    • 删除公告:教师可以删除自己发布的公告。
    • 查看公告:学生可以查看所有已发布的公告。

详细设计

教师端

  1. 用户管理
    • 注册:用户可以选择身份注册为教师账号,输入用户名、密码、姓名等信息进行注册。
    • 登录:用户输入用户名和密码登录系统,登录成功后跳转到相应的管理页面。
    • 修改个人信息:用户可以在个人中心页面修改自己的个人信息,如姓名、密码等。
    • 注销:用户可以在个人中心页面注销自己的账号。
  2. 课程管理
    • 发布课程:教师点击页面中的“发布课程”按钮,切换到发布课程表单,输入课程名称、课程描述、上传课程封面等信息后提交。
    • 课程列表:教师在课程列表页面查看自己发布的所有课程。
    • 修改课程:教师在课程列表中点击某个课程的“修改”按钮,弹出修改课程表单,修改课程信息后提交。
    • 删除课程:教师在课程列表中点击某个课程的“删除”按钮,弹出确认删除提示框,确认后删除课程。
  3. 作业管理
    • 布置作业:教师点击页面中的“布置作业”按钮,切换到布置作业表单,选择所属课程、输入作业名称、作业描述、截止日期等信息后提交。
    • 作业列表:教师在作业列表页面查看自己发布的所有作业。
    • 修改作业:教师在作业列表页面点击某个作业的“修改”按钮,弹出修改作业表单,修改作业信息后提交。
    • 删除作业:教师在作业列表页面点击某个作业的“删除”按钮,弹出确认删除提示框,确认后删除作业。
    • 作业提交列表:教师在作业提交列表页面查看跟自己相关的作业完成情况
    • 批改作业:教师在作业提交页面点击某个已提交作业的“批改”按钮,弹出批改作业表单,输入评分和评语后提交。
  4. 公告管理
    • 发布公告:教师在公告页面点击“发布公告”按钮,切换发布公告表单,输入公告标题、公告内容等信息后提交。
    • 公告列表:教师在公告列表页面查看自己发布的公告。
    • 修改公告:教师在公告列表页面点击某个公告的“修改”按钮,弹出修改公告表单,修改公告信息后提交。
    • 删除公告:教师在公告列表页面点击某个公告的“删除”按钮,弹出确认删除提示框,确认后删除公告。

学生端

  1. 用户管理
    • 注册:用户可以选择注册为学生账号,输入用户名、密码、姓名等信息进行注册。
    • 登录:用户输入用户名和密码登录系统,登录成功后跳转到相应的管理页面。
    • 修改个人信息:用户可以在个人中心页面修改自己的个人信息,如姓名、密码等。
    • 注销:用户可以在个人中心页面注销自己的账号。
  2. 课程管理
    • 查看课程:学生在课程列表页面查看已发布的所有课程,点击某个课程查看课程详情。
  3. 作业管理
    • 未完成列表:学生在未完成列表中查看老师布置的所有作业,点击列表跳转到作业详情,在详情页面点击”提交作业“按钮,切换到作业提交表单,输入作业内容、选择附件进行提交。
    • 已完成列表:学生在已完成列表页面查看自己已提交的作业及教师的批改结果。
  4. 公告管理
    • 查看公告:学生在公告列表页面查看所有已发布的公告,点击某个公告查看公告详情。

web管理端-在线教学管理系统

数据库设计

ER图

QQ图片20230529173653

数据字典(MySQL)

image-20230604120257121

image-20230604120347066

架构设计(MySQL)

Diagram 1

建表语句(MySQL)

用户表(user)

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `username` varchar(255) NOT NULL COMMENT '用户名',
  `password` varchar(255) NOT NULL COMMENT '密码',
  `name` varchar(255) NOT NULL COMMENT '姓名',
  `role` enum('teacher','student') NOT NULL COMMENT '角色(教师或学生)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';

课程表(course)

CREATE TABLE `course` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '课程ID',
  `name` varchar(255) NOT NULL COMMENT '课程名称',
  `description` text NOT NULL COMMENT '课程描述',
  `cover` varchar(255) NOT NULL COMMENT '课程封面',
  `teacher_id` int(11) NOT NULL COMMENT '发布教师ID',
  FOREIGN KEY (`teacher_id`) REFERENCES `user` (`id`),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='课程表';

作业表(homework)

CREATE TABLE `homework` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '作业ID',
  `name` varchar(255) NOT NULL COMMENT '作业名称',
  `description` text NOT NULL COMMENT '作业描述',
  `deadline` datetime NOT NULL COMMENT '截止日期',
  `course_id` int(11) NOT NULL COMMENT '所属课程ID',
  FOREIGN KEY (`course_id`) REFERENCES `course` (`id`),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='作业表';

作业提交表(homework_submission)

CREATE TABLE `homework_submission` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '提交ID',
  `student_id` int(11) NOT NULL COMMENT '提交学生ID',
  `homework_id` int(11) NOT NULL COMMENT '作业ID',
  `content` text NOT NULL COMMENT '作业内容',
  `file` varchar(255) DEFAULT NULL COMMENT '附件',
  `score` int(11) DEFAULT NULL COMMENT '评分',
  `comment` text DEFAULT NULL COMMENT '评语',
  FOREIGN KEY (`student_id`) REFERENCES `user` (`id`),
  FOREIGN KEY (`homework_id`) REFERENCES `homework` (`id`),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='作业提交表';

公告表(announcement)

CREATE TABLE `announcement` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '公告ID',
  `title` varchar(255) NOT NULL COMMENT '公告标题',
  `content` text NOT NULL COMMENT '公告内容',
  `teacher_id` int(11) NOT NULL COMMENT '发布教师ID',
  FOREIGN KEY (`teacher_id`) REFERENCES `user` (`id`),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='公告表';

完整SQL:

/*
 Navicat Premium Data Transfer

 Source Server         : mysql5
 Source Server Type    : MySQL
 Source Server Version : 50717 (5.7.17-log)
 Source Host           : localhost:3306
 Source Schema         : online_edu_manager

 Target Server Type    : MySQL
 Target Server Version : 50717 (5.7.17-log)
 File Encoding         : 65001

 Date: 04/06/2023 11:55:25
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for announcement
-- ----------------------------
DROP TABLE IF EXISTS `announcement`;
CREATE TABLE `announcement`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '公共ID',
  `title` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '公告标题',
  `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '公告内容',
  `teacher_id` int(11) NOT NULL COMMENT '外键,关联user表',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `teacher_id`(`teacher_id`) USING BTREE,
  CONSTRAINT `announcement_ibfk_1` FOREIGN KEY (`teacher_id`) REFERENCES `user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for course
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '课程ID',
  `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '课程名称',
  `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '课程描述',
  `cover` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '课程封面',
  `teacher_id` int(11) NOT NULL COMMENT '外键,关联user表',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `teacher_id`(`teacher_id`) USING BTREE,
  CONSTRAINT `course_ibfk_1` FOREIGN KEY (`teacher_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for homework
-- ----------------------------
DROP TABLE IF EXISTS `homework`;
CREATE TABLE `homework`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '作业ID',
  `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '作业名称',
  `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '作业描述',
  `deadline` datetime NULL DEFAULT NULL COMMENT '截止日期',
  `course_id` int(11) NULL DEFAULT NULL COMMENT '外键,关联course',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `course_id`(`course_id`) USING BTREE,
  CONSTRAINT `homework_ibfk_1` FOREIGN KEY (`course_id`) REFERENCES `course` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for homework_submission
-- ----------------------------
DROP TABLE IF EXISTS `homework_submission`;
CREATE TABLE `homework_submission`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '提交ID',
  `student_id` int(11) NOT NULL COMMENT '外键,关联user表',
  `homework_id` int(11) NOT NULL COMMENT '外键,关联homework表',
  `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '作业内容',
  `file` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '附件,可选',
  `score` int(11) NULL DEFAULT NULL COMMENT '评分',
  `comment` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '评语',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `student_id`(`student_id`) USING BTREE,
  INDEX `homework_id`(`homework_id`) USING BTREE,
  CONSTRAINT `homework_submission_ibfk_1` FOREIGN KEY (`student_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `homework_submission_ibfk_2` FOREIGN KEY (`homework_id`) REFERENCES `homework` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `username` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
  `password` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
  `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '姓名',
  `role` enum('teacher','student') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色(老师、学生)',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

后端接口开发

项目结构

online-teaching-management-system
  - static   #存放小程序上传的图片
  - utils
    - db.js
  - controllers
    - userController.js
    - courseController.js
    - homeworkController.js
    - announcementController.js
  - models
    - userModel.js
    - courseModel.js
    - homeworkModel.js
    - announcementModel.js
  - routes
    - userRoutes.js
    - courseRoutes.js
    - homeworkRoutes.js
    - announcementRoutes.js
  - app.js
  - uploadserver.js  #这是一个图片服务器,负责图片的上传和下载

插件安装

npm init -y // 会帮助我们生成一个package.json
npm install express mysql body-parser cors multer uuid

文件服务器搭建

在根目录下新建跟app.js同级别的uploadserver.js文件,用来搭建文件服务器,并新建跟uploadserver.js同级别的static文件夹,用来存放上传的文件,编写代码如下:

// 图片上传服务器
const express = require("express")
const app = express()
const port = 9001 // 图片上传服务器的端口号

// 配置跨域
const cors = require("cors")
app.use(cors({
    origin: "*"
}))

// 配置multer文件上传中间件配置
const multer = require("multer")
// 使用uuid动态生成不重名的图片
const uuid = require("uuid")
// 配置上传参数
const uploadTools = multer({
    storage: multer.diskStorage({// 该存储方案将会把文件直接存入磁盘
        destination: (req, file, callback) => {// 处理存储
            callback(null, 'static')
        },
        filename: (req, file, callback) => {// 处理文件名,在这对文件名进行重新命名,防止文件名重复被替换
            // 通过file,获取原始文件名,比如:wanyin.jpeg
            let name = file.originalname
            console.log("--------文件名:", name)
            // 截取文件的扩展名,比如:.jpeg .png
            let ext = name.substr(name.lastIndexOf('.'))
            // 通过uuid生成一个16进制的随机文件名
            let newName = uuid.v4() + ext
            // 通过callback进行重新设置
            callback(null, newName)
        }
    })
})

//  配置static目录为静态资源托管文件夹,这样就可以直接通过:http://localhost:9001/文件名 访问static目录下的资源
app.use(express.static("static"))
//  配置上传请求
//  上传action: http://localhost:9001/upload
app.post('/upload', uploadTools.single('file'), (req, res) => {
    // 这是需要响应给前端的图片的地址,如果真机调试,这里的localhost换成你的电脑
    let url = "http://localhost:9001/" + req.file.filename
    console.log("图片上传成功!", url)
    res.status(200).json({code:1,msg:"图片上传成功!",url})
})

// 监听服务器启动状态
app.listen(port, () => {
    console.log("文件上传服务器启动成功!")
})

执行以下命令,启动文件服务器。

node uploadserver.js

基础服务器配置

app.js:服务端主配置文件

// 这个就是后端服务器的入口文件
// 引入所需要的模块
const express = require("express") // 有了这个模块,我们就可以通过js做后端开发
const bodyParser = require("body-parser")// 有了这个模块,就可以自动实现对象和json字符出之间的转换
const mysql = require("mysql") // 有了这个代码模块,我们就可以使用js访问和操作数据库
// 创建后端服务
const app = express()

// 配置MySQL连接
const db = mysql.createConnection({
    host: "localhost",// 主机
    user: "root",// 数据库默认的账户
    password: "123456", // 数据库访问密码 
    database: "online_edu_manager", // 需要连接到的数据库名称
})
// 连接数据库
db.connect((err) => {
    if (err) {
        console.log("数据库连接失败!");
        throw err;
    }
    console.log("数据库连接成功!");
})
// 测试访问
app.get('/', (req, res) => {
    res.send('Hello World!');
});
// 配置body-parser中间件
app.use(bodyParser.urlencoded({ extended: false })) // 防止数据乱码
app.use(bodyParser.json()) // 对象转json

// 监听服务器是否启动
app.listen(3000, () => {
    console.log("服务器启动成功:http://localhost:3000");
})

请输入以下命令启动服务器:

node app.js

在浏览器访问http://localhost:3000

针对上面的文件进行优化,将数据库配置和连接抽取到utils/db.js文件中:

db.js:用于连接MySQL数据库

/**
 * MySQL数据库的配置与连接
 */

// 导入我们安装的mysql模块,这里需要注意,如果你的数据库是MySQL8.X,你需要安装mysql2
// 卸载:npm un mysql
// 安装:npm i mysql2
const mysql = require('mysql')

// 配置连接数据库的信息
const connection = mysql.createConnection({
    host:"localhost", // 这里是主机的地址,我们教学都是使用这个
    port:"3306",// 数据库的端口,有些同学装的数据库端口是3307,需要在这里配置,默认是3306
    user:"root",// 数据库默认的管理员账户
    password:"123456",// 数据库连接密码
    database:"online_edu_manager" // 数据库的名字
})

// 连接数据库
connection.connect(err=>{
    if(err){
        console.log("数据库连接失败");
        throw err
        // return
    }
    console.log("数据库连接成功");
})

// 公开,给其他文件调用
module.exports = connection

用户模块接口开发

userModel.js

用户表的数据操作,所有对于user表增删改查的代码都在这个文件。

/**
 * 用户表(user)的操作
 */
const db = require("../utils/db.js")
/**
 * 注册,添加用户,包括老师和学生
 * @param {*} user:存放我们要添加到数据库的用户信息(username,password,name,role)
 * @param {*} callback:回调函数,操作数据库
 */
const addUser = (user,callback)=>{
    // 编写sql,这里需要使用?帮助我们占位
    const sql = "insert into user(username,password,name,role) values(?,?,?,?)"
    // 执行sql语句,同时使用我们传入的数据,替换到对应位置的?
    db.query(sql,[user.username,user.password,user.name,user.role],(err,result)=>{
        if(err){
            callback(err,null) // 失败
        }else{
            callback(null,result.insertId) // 成功
        }
    })
}

/**
 * 登录,查找用户,包括老师和学生
 * @param {*} user:user是一个对象,里面存放前端传入的username,password
 * @param {*} callback 
 */
const login = (user,callback)=>{
    // 编写sql
    const sql = "select id,username,name,role from user where username=? and password=?"
    // 执行sql
    db.query(sql,[user.username,user.password],(err,result)=>{
        if(err){
            callback(err,null) // 失败
        }else{
            callback(null,result[0]) // 我们只要一个信息
        }
    })
}

/**
 * 根据用户ID修改用户信息(除了密码),包括老师和学生
 * @param {*} user:user是一个对象,里面存放前端传入的username,name,id
 * @param {*} callback 
 */
const updateUser = (user,callback)=>{
    // 编写sql
    const sql = "update user set username=?,name=? where id=?"
    // 执行sql
    db.query(sql,[user.username,user.name,user.id],(err,result)=>{
        if(err){
            callback(err,null)// 失败
        }else{
            callback(null,result.affectedRows) // 成功
        }
    })
}

/**
 * 修改密码,根据账户设置新密码,包括老师和学生 {password:"xxx",id:xx}
 * @param {*} user 
 * @param {*} callback 
 */
const changePwd = (user,callback)=>{
    // 编写sql
    const sql = "update user set password=? where id=?"
    // 执行sql
    db.query(sql,[user.password,user.id],(err,result)=>{
        if(err){
            callback(err,null) // 失败
        }else{
            callback(null,result.affectedRows) // 成功
        }
    })
}

/**
 * 注销,根据用户ID删除信息,包括老师和学生
 * @param {*} userId 
 * @param {*} callback 
 */
const logout = (userId,callback)=>{
    // 编写sql
    const sql = "delete from user where id=?"
    // 执行sql
    db.query(sql,[userId],(err,result)=>{
        if(err){
            callback(err,null) // 失败
        }else{
            callback(null,result.affectedRows) // 成功
        }
    })
}

// 公开方法,给外部其他文件访问
module.exports = {
    addUser,
    login,
    updateUser,
    changePwd,
    logout
}

userController.js

用户相关的接口逻辑,所有后端业务逻辑处理(包括前端参数的接收和后端数据的响应)都在这个文件

/**
 * 控制层,实现用户的业务逻辑
 */

const userModel = require("../models/userModels") // module.exports

// 用户注册
const register = (req, res) => {
    // 获取前端页面传递过来的信息
    const { username, password, name, role } = req.body;
    // 非空校验,可选
    if (!username) {
        res.status(200).json({ code: -1, msg:"用户名不能为空" })
    }
    if (!password) {
        res.status(200).json({ code: -1, msg:"密码不能为空" })
    }
    if (!name) {
        res.status(200).json({ code: -1, msg:"姓名不能为空" })
    }
    if (!role) {
        res.status(200).json({ code: -1, msg:"姓名不能为空" })
    }
    // 访问数据库
    userModel.addUser({ username, password, name, role }, (err, userId) => {
        if (err) {
            // res.status().json() 响应信息到前端
            res.status(500).json({code:-3,msg: "服务器错误" })
        } else {
            if (userId) {
                res.status(200).json({ code: 1, msg: "用户注册成功!" })
            } else {
                res.status(200).json({ code: 0, msg: "用户注册失败!" })
            }
        }
    })
}

/**
 * 用户登录
 * @param {*} req:request的缩写,作用是获取到前端的请求信息
 * @param {*} res:response的缩写,作用是将后端数据响应给前端
 */
const login = (req, res) => {
    // 如果前端是通过表单提交的数据,后端获取使用:req.body
    const { username, password } = req.body
    // 非空校验
    if (!username) {
        res.status(200).json({ "code": -1, msg: "用户名不能为空" })
    }
    if (!password) {
        res.status(200).json({ "code": -1, msg: "用户密码不能为空" })
    }
    // 访问数据库
    userModel.login({ username, password }, (err, user) => {
        if (err) {
            console.error("用户登录:",err);
            res.status(500).json({code:-3,msg:"服务器错误"})
        }else{
            if(user){
                res.status(200).json({code:1,msg:"用户登录成功!",data:user})
            }else{
                res.status(200).json({code:0,msg:"用户登录失败!"})
            }
        }
    })
}

/**
 * 根据id修改用户信息
 * @param {*} req 
 * @param {*} res 
 */ 
const updateUser = (req,res)=>{
    // 获取前端传过来的表单信息
    const {id,username,name} = req.body
    // 非空校验
    if(!id){
        res.status(200).json({code:-1,msg:"用户id不能为空"})
    }
    if(!username){
        res.status(200).json({code:-1,msg:"用户登录名不能为空"})
    }
    if(!name){
        res.status(200).json({code:-1,msg:"用户姓名不能为空"})
    }
    // 访问数据库
    userModel.updateUser({id,username,name},(err,rows)=>{
        if(err){
            console.error("根据id修改用户信息:",err);
            res.status(500).json({code:-3,msg:"服务器错误"})
        }else{
            if(rows && rows>0){
                res.status(200).json({code:1,msg:"用户信息修改成功!"})
            }else{
                res.status(200).json({code:0,msg:"用户信息修改失败!"})
            }
        }
    })
}

/**
 * 修改密码
 * @param {*} req 
 * @param {*} res 
 */
const changePwd = (req,res)=>{
    // 获取前端传过来的表单信息
    const {id,password} = req.body 
    // 非空校验
    if(!id){
        res.status(200).json({code:-1,msg:"用户id不能为空"})
    }
    if(!password){
        res.status(200).json({code:-1,msg:"用户密码不能为空"})
    }
    // 访问数据库
    userModel.changePwd({id,password},(err,rows)=>{
        if(err){
            console.error("根据id修改用户密码:",err);
            res.status(500).json({code:-3,msg:"服务器错误"})
        }else{
            if(rows && rows>0){
                res.status(200).json({code:1,msg:"用户密码修改成功!"})
            }else{
                res.status(200).json({code:0,msg:"用户密码修改失败!"})
            }
        }
    })
}

/**
 * 注销
 * @param {*} req 
 * @param {*} res 
 */
const logout = (req,res)=>{
    // 获取地址栏携带的id:req.params
    const {id} = req.params 
    // 非空校验
    if(!id){
        res.status(200).json({code:-1,msg:"用户id不能为空"})
    }
    // 访问数据库
    userModel.logout(id,(err,rows)=>{
        if(err){
            console.error("注销用户:",err);
            res.status(200).json({code:-3,msg:"服务器错误"})
        }else{
            if(rows && rows >0){
                res.status(200).json({code:1,msg:"用户注销成功!"})
            }else{
                res.status(200).json({code:0,msg:"用户注销失败!"})
            }
        }
    })
}
module.exports = {
    register,
    login,
    updateUser,
    changePwd,
    logout
}

userRoutes.js

用户相关的路由,所有用户模块对应的接口都在这个文件进行配置,包括请求地址、请求方式等。

const express = require('express')
const router = express.Router()

// 导入controller
const userController = require("../controllers/userController")

/**
 * 用户注册接口
 * method:post
 * url:http://localhost:3000/user/register
 * params: {username:"",password:"",name:"",role:""}
 */
router.post('/register',userController.register)

/**
 * 用户登录接口
 * method:post
 * url:http://localhost:3000/user/login
 * params:{username:"",password:""}
 */
router.post('/login',userController.login)

/**
 * 用户信息修改接口
 * method:put
 * url:http://localhost:3000/user/update
 * params:{id:'',username:'',name:''}
 */
router.put("/update",userController.updateUser)

/**
 * 用户密码修改接口
 * method:put
 * url:http://localhost:3000/user/changePwd
 * params:{id:'',password:''}
 */
router.put("/changePwd",userController.changePwd)

/**
 * 用户注销接口
 * method:delete
 * url:http://localhost:3000/user/delete/1
 */
router.delete("/delete/:id",userController.logout) 

module.exports = router

app.js

项目入口文件,包括依赖管理,服务器配置、服务监听等

// 这个就是后端服务器的入口文件
// 引入所需要的模块
const express = require("express") // 有了这个模块,我们就可以通过js做后端开发
const bodyParser = require("body-parser")// 有了这个模块,就可以自动实现对象和json字符出之间的转换
const cors = require("cors")

// 创建后端服务
const app = express()

// 导入路由
const userRoutes = require("./routes/userRoutes")

// 配置跨域
app.use(cors())

// 配置body-parser中间件
app.use(bodyParser.urlencoded({ extended: false })) // 防止数据乱码
app.use(bodyParser.json()) // 对象转json


// 注册路由
app.use("/user",userRoutes)  // http://localhost:3000/user/register


// 监听服务器是否启动
app.listen(3000, () => {
    console.log("服务器启动成功:http://localhost:3000");
})

课程模块接口开发

courseModel.js

课程表的数据操作,所有对于user表增删改查的代码都在这个文件。

/**
 * 课程表(course)的操作
 */
const db = require("../utils/db")


// 添加课程,跟老师有关
const addCourse = (course, callback) => {
    // 编写sql
    const sql = "insert into course(name,description,cover,teacher_id) values(?,?,?,?)"
    // 执行sql
    db.query(sql, [course.name, course.description, course.cover, course.teacher_id], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results.insertId) // 成功
        }
    })
}

// 修改课程,跟老师有关
const updateCourse = (course, callback) => {
    // 编写sql
    const sql = "update course set name=?,description=?,cover=? where id=?"
    // 执行sql
    db.query(sql, [course.name, course.description, course.cover, course.id], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results.affectedRows) // 成功
        }
    })
}

// 删除课程,跟老师有关
const deleteCourse = (courseId, callback) => {
    // 编写sql
    const sql = "delete from course where id=?"
    // 执行sql
    db.query(sql, [courseId], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results.affectedRows) // 成功
        }
    })
}

// 根据老师ID查询课程
const findCoursesByTeacherId = (course, callback) => {
    // 编写sql
    const sql = "select * from course where teacher_id=? and name like ? limit ?,?"
    // 解构参数
    let { teacherId, keywords, startIndex, pageSize } = course
    // 执行sql
    db.query(sql, [teacherId, `%${keywords}%`, startIndex, pageSize], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results) // 成功
        }
    })
}

// 查询所有课程,给学生用
const findCourses = (course, callback) => {
    // 编写sql
    const sql = `select 
          c.id,
          c.name,
          c.description,
          c.cover,
          u.id as teacher_id,
          u.name as techer_name 
        from
          course c,
          user u 
        where c.teacher_id = u.id 
          and u.name like ? 
        limit ?, ? `
    // 解构参数
    const {keywords,startIndex,pageSize} = course // 关键字,开始索引,每页条数
    // 执行sql
    db.query(sql,[`%${keywords}%`,startIndex,pageSize],(err,results)=>{
        if(err){
            callback(err,null) // 失败
        }else{
            callback(null,results) // 成功
        }
    })
}

// 查询课程总数,预留
const countCourses = (course, callback) => {
   // 编写sql course{keywords,teacherId}
   
}

module.exports = {
    addCourse,
    updateCourse,
    deleteCourse,
    findCoursesByTeacherId,
    findCourses,
    countCourses
}

courseController.js

课程相关的接口逻辑,所有后端业务逻辑处理(包括前端参数的接收和后端数据的响应)都在这个文件

/**
 * 控制层,实现课程的业务逻辑
 */

const courseModel = require("../models/courseModels")

// 添加课程
const addCourse = (req, res) => {
    // 获取前端请求回来的数据
    const { name, description, cover, teacher_id } = req.body
    // 非空校验
    if (!name) {
        res.status(200).json({ code: -1, msg: "课程名称不能为空" })
    }
    if (!description) {
        res.status(200).json({ code: -1, msg: "课程描述不能为空" })
    }
    if (!cover) {
        res.status(200).json({ code: -1, msg: "课程封面不能为空" })
    }
    if (!teacher_id) {
        res.status(200).json({ code: -1, msg: "老师ID不能为空" })
    }
    // 访问数据库
    courseModel.addCourse({ name, description, cover, teacher_id }, (err, courseId) => {
        if (err) {
            console.log("添加课程:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (courseId) {
                res.status(200).json({ code: 1, msg: "课程添加成功!" })
            } else {
                res.status(200).json({ code: 0, msg: "课程添加失败!" })
            }
        }
    })
}

// 修改课程
const updateCourse = (req, res) => {
    // 获取前端传递过来的请求数据
    const { name, description, cover, id } = req.body
    // 非空校验
    if (!name) {
        res.status(200).json({ code: -1, msg: "课程名称不能为空" })
    }
    if (!description) {
        res.status(200).json({ code: -1, msg: "课程描述不能为空" })
    }
    if (!cover) {
        res.status(200).json({ code: -1, msg: "课程封面不能为空" })
    }
    if (!id) {
        res.status(200).json({ code: -1, msg: "课程ID不能为空" })
    }
    // 访问数据库
    courseModel.updateCourse({ name, description, cover, id }, (err, rows) => {
        if (err) {
            console.log("修改课程:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (rows && rows > 0) {
                res.status(200).json({ code: 1, msg: "课程修改成功!" })
            } else {
                res.status(200).json({ code: 0, msg: "课程修改失败!" })
            }
        }
    })
}

// 删除课程
const deleteCourse = (req, res) => {
    // 获取前端地址栏传递的课程ID
    const { id } = req.params
    // 非空校验
    if (!id) {
        res.status(200).json({ code: -1, msg: "课程ID不能为空" })
    }
    // 访问数据库
    courseModel.deleteCourse(id, (err, rows) => {
        if (err) {
            console.log("删除课程:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (rows && rows > 0) {
                res.status(200).json({ code: 1, msg: "课程删除成功!" })
            } else {
                res.status(200).json({ code: 0, msg: "课程删除失败!" })
            }
        }
    })
}

// 根据老师id查询课程
const findCoursesById = (req, res) => {
    // 获取前端地址栏传递的老师ID,关键字,分页信息
    let { teacherId, keywords, startIndex, pageSize } = req.params
    // 处理关键字
    if (keywords == "''") {
        keywords = ""
    }
    // 访问数据库,转过来的数字字符串需要转换为数字
    courseModel.findCoursesByTeacherId({ teacherId:Number(teacherId), keywords, startIndex:Number(startIndex), pageSize:Number(pageSize) }, (err, results) => {
        if (err) {
            console.log("根据老师id查询课程:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (results && results.length > 0) {
                res.status(200).json({ code: 1, msg: "课程列表获取成功!", data: results })
            } else {
                res.status(200).json({ code: 0, msg: "课程列表获取失败!" })
            }
        }
    })
}

// 查询所有课程
const findCourses = (req, res) => {
    // 获取前端地址栏传递的老师ID,关键字,分页信息
    let { keywords, startIndex, pageSize } = req.params
    // 处理关键字
    if (keywords == "''") {
        keywords = ""
    }
    // 访问数据库
    courseModel.findCourses({ keywords, startIndex:Number(startIndex), pageSize:Number(pageSize) } , (err, results) => {
        if (err) {
            console.log("查询所有课程:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (results && results.length > 0) {
                res.status(200).json({ code: 1, msg: "课程列表获取成功!", data: results })
            } else {
                res.status(200).json({ code: 0, msg: "课程列表获取失败!" })
            }
        }
    })
}

module.exports = {
    addCourse,
    updateCourse,
    deleteCourse,
    findCoursesById,
    findCourses
}

courseRoutes.js

课程相关的路由,所有用户模块对应的接口都在这个文件进行配置,包括请求地址、请求方式等。

const express = require('express')
const router = express.Router()

// 导入courseController
const courseController = require("../controllers/courseController")

/**
 * 添加课程接口
 * method:post
 * url:http://localhost:3000/course/addCourse
 * data:{name:"",description:"",cover:"",teacher_id:0}
 */
router.post("/addCourse", courseController.addCourse)

/**
 * 修改课程接口
 * method:put
 * url:http://localhost:3000/course/updateCourse
 * data:{name:"",description:"",cover:"",id:0}
 */
router.put("/updateCourse", courseController.updateCourse)

/**
 * 删除课程接口
 * method:delete
 * url:http://localhost:3000/course/deleteCourse/1
 */
router.delete("/deleteCourse/:id", courseController.deleteCourse)

/**
 * 根据老师id查询课程接口
 * method:get
 * url:http://localhost:3000/course/findCoursesById/1/''/0/5
 */
router.get("/findCoursesById/:teacherId/:keywords/:startIndex/:pageSize", courseController.findCoursesById)

/**
 * 查询所有课程接口
 * method:get
 * url:http://localhost:3000/course/findCourses/''/0/5
 */
router.get("/findCourses/:keywords/:startIndex/:pageSize",courseController.findCourses)

module.exports = router

app.js

项目入口文件,包括依赖管理,服务器配置、服务监听等

// 这个就是后端服务器的入口文件
// 引入所需要的模块
const express = require("express") // 有了这个模块,我们就可以通过js做后端开发
const bodyParser = require("body-parser")// 有了这个模块,就可以自动实现对象和json字符出之间的转换
const cors = require("cors")

// 创建后端服务
const app = express()

// 导入路由
const userRoutes = require("./routes/userRoutes")
const courseRoutes = require("./routes/courseRoutes")
// 配置跨域
app.use(cors())

// 配置body-parser中间件
app.use(bodyParser.urlencoded({ extended: false })) // 防止数据乱码
app.use(bodyParser.json()) // 对象转json


// 注册路由
app.use("/user",userRoutes)  
app.use("/course",courseRoutes)

// 监听服务器是否启动
app.listen(3000, () => {
    console.log("服务器启动成功:http://localhost:3000");
})

作业模块接口开发

homeworkModel.js

作业表的数据操作,所有对于user表增删改查的代码都在这个文件。

/**
 * 作业表(homework)的操作
 */
const db = require("../utils/db")

// 老师添加作业
const addHomework = (homework, callback) => {
    // 编写sql
    const sql = "insert into homework(name,description,deadline,course_id) values(?,?,?,?)"
    // 执行sql
    db.query(sql, [homework.name, homework.description, homework.deadline, homework.course_id], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results.insertId) // 成功
        }
    })
}

// 老师修改作业
const updateHomework = (homework, callback) => {
    // 编写sql
    const sql = "update homework set name=?,description=?,deadline=? where id=?"
    // 执行sql
    db.query(sql, [homework.name, homework.description, homework.deadline, homework.id], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results.affectedRows) // 成功
        }
    })
}

// 老师删除作业
const deleteHomework = (homeworkId, callback) => {
    // 编写sql
    const sql = "delete from homework where id=?"
    // 执行sql
    db.query(sql, [homeworkId], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results.affectedRows) // 成功
        }
    })
}

// 老师/学生查看作业
const findHomeworks = (homework, callback) => {
    console.log("homework:", homework);
    // 创建一个数组,存放参数
    let paramsArr = []
    // 编写sql
    let sql = `
            select 
              h.id,
              c.cover,
              h.name,
              h.description,
              h.deadline,
              c.name as course_name,
              u.name as teacher_name
            from
              user u,
              course c,
              homework h 
            where c.teacher_id = u.id 
              and h.course_id = c.id 
    `
    // 根据老师id查询某个老师的作业
    if (homework && homework.teacherId != -1) {
        sql += `
              and u.id = ?
        `
        paramsArr.push(homework.teacherId)
    }
    // 根据老师名称、课程名称、作业名称模糊查询作业
    sql += `
              and (
                  h.name like ? 
                  or c.name like ? 
                  or u.name like ?
              )
              limit ?,?
    `
    paramsArr.push(`%${homework.keywords}%`) // 作业名称
    paramsArr.push(`%${homework.keywords}%`) // 课程名称
    paramsArr.push(`%${homework.keywords}%`) // 老师名称
    paramsArr.push(homework.startIndex)// 开始索引
    paramsArr.push(homework.pageSize) // 每页显示条数
    console.log(sql, paramsArr);
    // 执行sql
    db.query(sql, paramsArr, (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results) // 成功
        }
    })
}

// 学生提交作业
const subHomework = (homework, callback) => {
    // 编写sql
    const sql = "insert into homework_submission(student_id,homework_id,content,file) values(?,?,?,?)"
    // 执行sql
    db.query(sql, [homework.studentId, homework.homeworkId, homework.content, homework.file], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results.insertId) // 成功
        }
    })
}

// 老师查看学生提交的作业
const findSubHomeworks = (homework, callback) => {
    // 编写sql
    const sql = `
    SELECT
    	h.name as homework_name,
    	h.deadline,
    	c.name as course_name,
    	c.cover,
    	u.name as student_name,
    	hs.content,
    	hs.file
    FROM
        user u,
    	course c,
    	homework h,
    	homework_submission hs 
    WHERE
    	hs.homework_id = h.id and 
    	h.course_id = c.id and 
    	hs.student_id = u.id and 
    	c.teacher_id = ? and
        ISNULL(hs.score)
    	limit ?,?
    `
    // 执行sql
    db.query(sql, [homework.teacherId, homework.startIndex, homework.pageSize], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results) // 成功
        }
    })
}

// 老师批改作业
const correctingHomework = (homework, callback) => {
    // 编写sql
    const sql = "update homework_submission set score=?,comment=? where id=?"
    // 执行sql
    db.query(sql, [homework.score, homework.comment, homework.id], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results.affectedRows) // 成功
        }
    })
}

// 学生查看已批改的作业
const findCorrectedHomeworks = (homework,callback)=>{
    // 编写sql
    const sql = `
        SELECT
        	h.name as homework_name,
        	h.deadline,
        	c.name as course_name,
        	c.cover,
        	u.name as teacher_name,
        	hs.score,
        	hs.comment
        FROM
          user u,
        	course c,
        	homework h,
        	homework_submission hs 
        WHERE
        	hs.homework_id = h.id and 
        	h.course_id = c.id and 
        	c.teacher_id = u.id and 
        	hs.score is not null and 
        	hs.student_id = ?
        	limit ?,?
    `
    // 执行sql
    db.query(sql, [homework.studentId, homework.startIndex, homework.pageSize], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results) // 成功
        }
    })
}

module.exports = {
    addHomework,
    updateHomework,
    deleteHomework,
    findHomeworks,
    subHomework,
    findSubHomeworks,
    correctingHomework,
    findCorrectedHomeworks
}

homeworkController.js

作业相关的接口逻辑,所有后端业务逻辑处理(包括前端参数的接收和后端数据的响应)都在这个文件

/**
 * 控制层,实现作业的业务逻辑
 */
const homeworkModel = require("../models/homeworkModel")

// 老师布置作业
const addHomework = (req, res) => {
    // 获取前端表单提交的内容
    const { name, description, deadline, course_id } = req.body
    if (!name) {
        res.status(200).json({ code: -1, msg: "作业名称不能为空" })
    }
    if (!description) {
        res.status(200).json({ code: -1, msg: "作业描述不能为空" })
    }
    if (!deadline) {
        res.status(200).json({ code: -1, msg: "截至日期不能为空" })
    }
    if (!course_id) {
        res.status(200).json({ code: -1, msg: "课程ID不能为空" })
    }
    // 访问数据库
    homeworkModel.addHomework({ name, description, deadline, course_id }, (err, homeworkId) => {
        if (err) {
            console.log("布置作业:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (homeworkId) {
                res.status(200).json({ code: 1, msg: "作业布置成功!" })
            } else {
                res.status(200).json({ code: 0, msg: "作业布置失败!" })
            }
        }
    })
}

// 老师修改作业
const updateHomework = (req, res) => {
    // 获取前端表单传递过来的请求数据
    const { name, description, deadline, id } = req.body
    if (!name) {
        res.status(200).json({ code: -1, msg: "作业名称不能为空" })
    }
    if (!description) {
        res.status(200).json({ code: -1, msg: "作业描述不能为空" })
    }
    if (!deadline) {
        res.status(200).json({ code: -1, msg: "截至日期不能为空" })
    }
    if (!id) {
        res.status(200).json({ code: -1, msg: "作业ID不能为空" })
    }
    // 访问数据库
    homeworkModel.updateHomework({ name, description, deadline, id }, (err, rows) => {
        if (err) {
            console.log("修改作业:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (rows && rows > 0) {
                res.status(200).json({ code: 1, msg: "作业修改成功!" })
            } else {
                res.status(200).json({ code: 0, msg: "作业修改失败!" })
            }
        }
    })
}

// 老师删除作业
const deleteHomework = (req, res) => {
    // 获取路径上携带的作业id
    const { id } = req.params
    // 非空校验
    if (!id) {
        res.status(200).json({ code: -1, msg: "作业ID不能为空" })
    }
    // 访问数据库
    homeworkModel.deleteHomework(id, (err, rows) => {
        if (err) {
            console.log("删除作业:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (rows && rows > 0) {
                res.status(200).json({ code: 1, msg: "作业删除成功!" })
            } else {
                res.status(200).json({ code: 0, msg: "作业删除失败!" })
            }
        }
    })
}

// 老师/学生查看作业
const findHomeworks = (req, res) => {
    console.log("-----", req.params);
    // 获取路径上携带的信息
    let { teacherId, keywords, startIndex, pageSize } = req.params
    // 处理老师编号
    if (teacherId == -1) {
        teacherId == null // 此时查所有课程
    }
    // 处理关键字
    if (keywords == "''") {
        keywords = ""
    }
    // 访问数据库
    homeworkModel.findHomeworks({ teacherId: Number(teacherId), keywords, startIndex: Number(startIndex), pageSize: Number(pageSize) }, (err, results) => {
        if (err) {
            console.log("查询所有作业:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (results && results.length > 0) {
                res.status(200).json({ code: 1, msg: "作业列表获取成功!", data: results })
            } else {
                res.status(200).json({ code: 0, msg: "作业列表获取失败!" })
            }
        }
    })
}

// 学生提交作业
const subHomework = (req, res) => {
    // 获取前端表单提交的数据
    const { studentId, homeworkId, content, file } = req.body
    // 非空判断
    if (!studentId) {
        res.status(200).json({ code: -1, msg: "学生ID不能为空" })
    }
    if (!homeworkId) {
        res.status(200).json({ code: -1, msg: "作业ID不能为空" })
    }
    if (!content) {
        res.status(200).json({ code: -1, msg: "作业内容不能为空" })
    }
    // 访问数据库
    homeworkModel.subHomework({ studentId, homeworkId, content, file }, (err, subHomeworkId) => {
        if (err) {
            console.log("提交作业:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (subHomeworkId) {
                res.status(200).json({ code: 1, msg: "作业提交成功!" })
            } else {
                res.status(200).json({ code: 0, msg: "作业提交失败!" })
            }
        }
    })
}

// 老师查看已提交作业
const findSubHomeworks = (req, res) => {
    // 获取路径上携带的参数
    let { teacherId, startIndex, pageSize } = req.params
    // 访问数据库
    homeworkModel.findSubHomeworks({ teacherId: Number(teacherId), startIndex: Number(startIndex), pageSize: Number(pageSize) }, (err, results) => {
        if (err) {
            console.log("查询所有已提交作业:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (results && results.length > 0) {
                res.status(200).json({ code: 1, msg: "已提交作业列表获取成功!", data: results })
            } else {
                res.status(200).json({ code: 0, msg: "已提交作业列表获取失败!" })
            }
        } 
    })
}

// 老师批改作业
const correctingHomework = (req, res) => {
    // 获取前端表单传递过来的数据
    const { score, comment, id } = req.body
    // 非空校验
    if (!score) {
        res.status(200).json({ code: -1, msg: "评分不能为空" })
    }
    if (!comment) {
        res.status(200).json({ code: -1, msg: "评语不能为空" })
    }
    if (!id) {
        res.status(200).json({ code: -1, msg: "作业ID不能为空" })
    }
    // 访问数据库
    homeworkModel.correctingHomework({ score, comment, id }, (err, rows) => {
        if (err) {
            console.log("批改作业:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (rows && rows > 0) {
                res.status(200).json({ code: 1, msg: "作业批改成功!" })
            } else {
                res.status(200).json({ code: 0, msg: "作业批改失败!" })
            }
        }
    })
}

// 学生查看已批改的作业
const findCorrectedHomeworks = (req,res)=>{
    // 获取前端路径携带的数据
    let {studentId, startIndex, pageSize} = req.params
    // 访问数据库
    homeworkModel.findCorrectedHomeworks({studentId:Number(studentId), startIndex:Number(startIndex), pageSize:Number(pageSize)},(err,results)=>{
        if (err) {
            console.log("查询已批改的作业:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (results && results.length > 0) {
                res.status(200).json({ code: 1, msg: "已批改作业列表获取成功!", data: results })
            } else {
                res.status(200).json({ code: 0, msg: "已批改作业列表获取失败!" })
            }
        }
    })
}
module.exports = {
    addHomework,
    updateHomework,
    deleteHomework,
    findHomeworks,
    subHomework,
    findSubHomeworks,
    correctingHomework,
    findCorrectedHomeworks
}

homeworkRoutes.js

作业相关的路由,所有用户模块对应的接口都在这个文件进行配置,包括请求地址、请求方式等。

const express = require("express")
const router = express.Router() 

// 导入homeworkController
const homeworkController = require("../controllers/homeworkController")

/**
 * 老师布置作业接口
 * method:post
 * url:http://localhost:3000/homework/addHomework
 * data:{name,description,deadline,course_id}
 */
router.post("/addHomework",homeworkController.addHomework)

/**
 * 老师修改作业接口
 * method:put
 * url:http://localhost:3000/homework/updateHomework
 * data:{name,description,deadline,id}
 */
router.put("/updateHomework",homeworkController.updateHomework)

/**
 * 老师删除作业接口
 * method:delete
 * url:http://localhost:3000/homework/deleteHomework/作业id
 */
router.delete("/deleteHomework/:id",homeworkController.deleteHomework)

/**
 * 老师/学生查询作业接口
 * method:get
 * url:http://localhost:3000/homework/findHomeworks/老师ID/关键字(可匹配课程名|老师名|作业名)/开始索引/每页条数
 * 注意:如果不是查询某个老师的课程,就给teacherId设置为-1,举例如下:
 * 1- 查询某个老师布置的所有作业:http://localhost:3000/homework/findHomeworks/2/''/0/5
 * 2- 查询所有作业:http://localhost:3000/homework/findHomeworks/-1/''/0/5
 */
router.get("/findHomeworks/:teacherId/:keywords/:startIndex/:pageSize",homeworkController.findHomeworks)

/**
 * 学生提交作业接口
 * method:post
 * url:http://localhost:3000/homework/subHomework
 * data:{studentId,homeworkId,content,file[可为空]}
 */
router.post("/subHomework",homeworkController.subHomework)

/**
 * 老师查看已提交作业接口
 * method:get
 * url:http://localhost:3000/homework/findSubHomeworks/老师ID/开始索引/每页显示条数
 */
router.get("/findSubHomeworks/:teacherId/:startIndex/:pageSize",homeworkController.findSubHomeworks)

/**
 * 老师批改作业接口
 * method:put
 * url:http://localhost:3000/homework/correctingHomework
 * data:{score,comment,id}
 */
router.put("/correctingHomework",homeworkController.correctingHomework)

/**
 * 学生查看已批改作业接口
 * method:get
 * url:http://localhost:3000/homework/findCorrectedHomeworks/学生ID/开始索引/每页显示条数
 */
router.get("/findCorrectedHomeworks/:studentId/:startIndex/:pageSize",homeworkController.findCorrectedHomeworks)

module.exports = router

app.js

项目入口文件,包括依赖管理,服务器配置、服务监听等

// 这个就是后端服务器的入口文件
// 引入所需要的模块
const express = require("express") // 有了这个模块,我们就可以通过js做后端开发
const bodyParser = require("body-parser")// 有了这个模块,就可以自动实现对象和json字符出之间的转换
const cors = require("cors")

// 创建后端服务
const app = express()

// 导入路由
const userRoutes = require("./routes/userRoutes")
const courseRoutes = require("./routes/courseRoutes")
const homeworkRoutes = require("./routes/homeworkRoutes")
// 配置跨域
app.use(cors())

// 配置body-parser中间件
app.use(bodyParser.urlencoded({ extended: false })) // 防止数据乱码
app.use(bodyParser.json()) // 对象转json


// 注册路由
app.use("/user",userRoutes)  // http://localhost:3000/user/register
app.use("/course",courseRoutes)
app.use("/homework",homeworkRoutes)
// 监听服务器是否启动
app.listen(3000, () => {
    console.log("服务器启动成功:http://localhost:3000");
})

公告模块接口开发

announcementModel.js

公告表的数据操作,所有对于user表增删改查的代码都在这个文件。

/**
 * 公告表(Announcement)的操作
 */
const db = require("../utils/db")


// 添加公告,跟老师有关
const addAnnouncement = (announcement, callback) => {
    // 编写sql
    const sql = "insert into announcement(title,content,teacher_id) values(?,?,?)"
    // 执行sql
    db.query(sql, [announcement.title, announcement.content, announcement.teacherId], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results.insertId) // 成功
        }
    })
}

// 修改公告,跟老师有关
const updateAnnouncement = (announcement, callback) => {
    // 编写sql
    const sql = "update announcement set title=?,content=? where id=?"
    // 执行sql
    db.query(sql, [announcement.title, announcement.content, announcement.id], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results.affectedRows) // 成功
        }
    })
}

// 删除公告,跟老师有关
const deleteAnnouncement = (announcementId, callback) => {
    // 编写sql
    const sql = "delete from announcement where id=?"
    // 执行sql
    db.query(sql, [announcementId], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results.affectedRows) // 成功
        }
    })
}

// 根据老师ID查询公告
const findAnnouncementsByTeacherId = (announcement, callback) => {
    // 编写sql
    const sql = "select * from announcement where teacher_id=? and title like ? limit ?,?"
    // 解构参数
    let { teacherId, keywords, startIndex, pageSize } = announcement
    // 执行sql
    db.query(sql, [teacherId, `%${keywords}%`, startIndex, pageSize], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results) // 成功
        }
    })
}

// 查询所有公告,给学生用
const findAnnouncements = (announcement, callback) => {
    // 编写sql
    const sql = "select * from announcement where title like ? limit ?,?"
    // 解构参数
    let { keywords, startIndex, pageSize } = announcement
    // 执行sql
    db.query(sql, [`%${keywords}%`, startIndex, pageSize], (err, results) => {
        if (err) {
            callback(err, null) // 失败
        } else {
            callback(null, results) // 成功
        }
    })
}



module.exports = {
    addAnnouncement,
    updateAnnouncement,
    deleteAnnouncement,
    findAnnouncementsByTeacherId,
    findAnnouncements
}

announcementController.js

公告相关的接口逻辑,所有后端业务逻辑处理(包括前端参数的接收和后端数据的响应)都在这个文件

/**
 * 控制层,实现公告的业务逻辑
 */

const announcementModel = require("../models/announcementModels")

// 添加公告
const addAnnouncement = (req, res) => {
    // 获取前端请求回来的数据
    const { title, content,teacherId } = req.body
    // 非空校验
    if (!title) {
        res.status(200).json({ code: -1, msg: "公告题目不能为空" })
    }
    if (!content) {
        res.status(200).json({ code: -1, msg: "公告内容不能为空" })
    }
    if (!teacherId) {
        res.status(200).json({ code: -1, msg: "老师ID不能为空" })
    }
    // 访问数据库
    announcementModel.addAnnouncement({ title, content,teacherId }, (err, announcementId) => {
        if (err) {
            console.log("添加公告:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (announcementId) {
                res.status(200).json({ code: 1, msg: "公告添加成功!" })
            } else {
                res.status(200).json({ code: 0, msg: "公告添加失败!" })
            }
        }
    })
}

// 修改公告
const updateAnnouncement = (req, res) => {
    // 获取前端传递过来的请求数据
    const { title, content, id } = req.body
    // 非空校验
    if (!title) {
        res.status(200).json({ code: -1, msg: "公告题目不能为空" })
    }
    if (!content) {
        res.status(200).json({ code: -1, msg: "公告内容不能为空" })
    }
    if (!id) {
        res.status(200).json({ code: -1, msg: "公告ID不能为空" })
    }
    // 访问数据库
    announcementModel.updateAnnouncement({ title, content, id }, (err, rows) => {
        if (err) {
            console.log("修改公告:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (rows && rows > 0) {
                res.status(200).json({ code: 1, msg: "公告修改成功!" })
            } else {
                res.status(200).json({ code: 0, msg: "公告修改失败!" })
            }
        }
    })
}

// 删除公告
const deleteAnnouncement = (req, res) => {
    // 获取前端地址栏传递的公告ID
    const { id } = req.params
    // 非空校验
    if (!id) {
        res.status(200).json({ code: -1, msg: "公告ID不能为空" })
    }
    // 访问数据库
    announcementModel.deleteAnnouncement(id, (err, rows) => {
        if (err) {
            console.log("删除公告:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (rows && rows > 0) {
                res.status(200).json({ code: 1, msg: "公告删除成功!" })
            } else {
                res.status(200).json({ code: 0, msg: "公告删除失败!" })
            }
        }
    })
}

// 根据老师id查询公告
const findAnnouncementsById = (req, res) => {
    // 获取前端地址栏传递的老师ID,关键字,分页信息
    let { teacherId, keywords, startIndex, pageSize } = req.params
    // 处理关键字
    if (keywords == "''") {
        keywords = ""
    }
    // 访问数据库,转过来的数字字符串需要转换为数字
    announcementModel.findAnnouncementsByTeacherId({ teacherId:Number(teacherId), keywords, startIndex:Number(startIndex), pageSize:Number(pageSize) }, (err, results) => {
        if (err) {
            console.log("根据老师id查询公告:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (results && results.length > 0) {
                res.status(200).json({ code: 1, msg: "公告列表获取成功!", data: results })
            } else {
                res.status(200).json({ code: 0, msg: "公告列表获取失败!" })
            }
        }
    })
}

// 查询所有公告
const findAnnouncements = (req, res) => {
    // 获取前端地址栏传递的老师ID,关键字,分页信息
    let { keywords, startIndex, pageSize } = req.params
    // 处理关键字
    if (keywords == "''") {
        keywords = ""
    }
    // 访问数据库
    announcementModel.findAnnouncements({ keywords, startIndex:Number(startIndex), pageSize:Number(pageSize) } , (err, results) => {
        if (err) {
            console.log("查询所有公告:", err);
            res.status(500).json({ code: -3, msg: "服务器错误" })
        } else {
            if (results && results.length > 0) {
                res.status(200).json({ code: 1, msg: "公告列表获取成功!", data: results })
            } else {
                res.status(200).json({ code: 0, msg: "公告列表获取失败!" })
            }
        }
    })
}

module.exports = {
    addAnnouncement,
    updateAnnouncement,
    deleteAnnouncement,
    findAnnouncementsById,
    findAnnouncements
}

announcementRoutes.js

公告相关的路由,所有用户模块对应的接口都在这个文件进行配置,包括请求地址、请求方式等。

const express = require('express')
const router = express.Router()

// 导入announcementController
const announcementController = require("../controllers/announcementController")

/**
 * 添加公告接口
 * method:post
 * url:http://localhost:3000/announcement/addAnnouncement
 * data:{title:"",content:"",teacher_id:0}
 */
router.post("/addAnnouncement", announcementController.addAnnouncement)

/**
 * 修改公告接口
 * method:put
 * url:http://localhost:3000/announcement/updateAnnouncement
 * data:{title:"",content:"",id:0}
 */
router.put("/updateAnnouncement", announcementController.updateAnnouncement)

/**
 * 删除公告接口
 * method:delete
 * url:http://localhost:3000/announcement/deleteAnnouncement/公告ID
 */
router.delete("/deleteAnnouncement/:id", announcementController.deleteAnnouncement)

/**
 * 根据老师id查询公告接口
 * method:get
 * url:http://localhost:3000/announcement/findAnnouncementsById/老师ID/(''|关键字)/开始索引/每页显示条数
 */
router.get("/findAnnouncementsById/:teacherId/:keywords/:startIndex/:pageSize", announcementController.findAnnouncementsById)

/**
 * 查询所有公告接口
 * method:get
 * url:http://localhost:3000/announcement/findAnnouncements/(''|关键字)/开始索引/每页显示条数
 */
router.get("/findAnnouncements/:keywords/:startIndex/:pageSize", announcementController.findAnnouncements)

module.exports = router

app.js

项目入口文件,包括依赖管理,服务器配置、服务监听等

// 这个就是后端服务器的入口文件
// 引入所需要的模块
const express = require("express") // 有了这个模块,我们就可以通过js做后端开发
const bodyParser = require("body-parser")// 有了这个模块,就可以自动实现对象和json字符出之间的转换
const cors = require("cors")

// 创建后端服务
const app = express()

// 导入路由
const userRoutes = require("./routes/userRoutes")
const courseRoutes = require("./routes/courseRoutes")
const homeworkRoutes = require("./routes/homeworkRoutes")
const announcementRoutes = require("./routes/announcementRoutes")
// 配置跨域
app.use(cors())

// 配置body-parser中间件
app.use(bodyParser.urlencoded({ extended: false })) // 防止数据乱码
app.use(bodyParser.json()) // 对象转json


// 注册路由
app.use("/user",userRoutes)  // http://localhost:3000/user/register
app.use("/course",courseRoutes)
app.use("/homework",homeworkRoutes)
app.use("/announcement",announcementRoutes)

// 监听服务器是否启动
app.listen(3000, () => {
    console.log("服务器启动成功:http://localhost:3000");
})

后端接口测试

我们的接口开发好之后,我们需要使用第三方的PostMan/ApiPost等接口测试工具进行测试。

文件上传接口测试

图片上传

image-20230608081227767

用户模块接口测试

用户注册

image-20230601100312626

用户登录

image-20230601100540251

根据id修改用户信息

image-20230601100710830

修改密码

image-20230601100756462

注销用户

image-20230601100846683

课程模块接口测试

添加课程

image-20230601162853978

修改课程

image-20230601164816868

删除课程

image-20230601165012505

根据老师id查询课程

image-20230601163753853

查询所有课程

image-20230601164504284

作业模块接口测试

布置作业

image-20230602104325409

修改作业

image-20230602104405016

删除作业

image-20230602104444499

查询作业

image-20230602104525711

提交作业

image-20230604123957014

老师查看已提交作业

image-20230604124102575

批改作业

image-20230604124159894

学生查看已批改作业

image-20230604124326736

公告模块接口测试

添加公共

image-20230604143113629

修改公告

image-20230604143202861

删除公告

image-20230604143244684

根据老师ID查询公告

image-20230604143813267

查询所有公告

image-20230604143913579

小程序开发

创建工程

image-20230606082715444

项目结构

image-20230609080302139

集成vantUI

参照:https://vant-contrib.gitee.io/vant-weapp/#/quickstart

配置tabBar

第一步:

阿里巴巴矢量图标库:https://www.iconfont.cn/

通过阿里巴巴矢量图标库,设计自己的tabBar图标,将图片下载存放到iamges文件夹,效果如下:

image-20230606093843015

第二步:

pages下创建三个page,效果如下:

image-20230606093910549

第三步:

app.json中配置tabBar,代码如下:

"tabBar": {
    "color": "#8a8a8a",
    "selectedColor": "#07c160",
    "backgroundColor": "#f7f8fa",
    "borderStyle": "white",
    "list": [{
      "pagePath": "pages/home/home",
      "text": "首页",
      "iconPath": "images/icon_home_off.png",
      "selectedIconPath": "images/icon_home_on.png"
    },{
      "pagePath": "pages/announcement/announcement",
      "text":"公告",
      "iconPath": "images/icon_anno_off.png",
      "selectedIconPath": "images/icon_anno_on.png"
    },{
      "pagePath": "pages/mine/mine",
      "text":"我的",
      "iconPath": "images/icon_mine_off.png",
      "selectedIconPath": "images/icon_mine_on.png"
    }]
  },

效果图,如下:

QQ图片20230606213142

配置公共的host(utils/http.js)

// 将host提取出来,这样方便更改
// 电脑端访问
// const base = "http://localhost"
// 手机端访问
const base = "http://172.23.1.10"
const host = `${base}:3000`
const fileHost = `${base}:9001`
module.exports = {
  host,
  fileHost
}

屏蔽掉app.wcss中的代码

/**app.wxss**/
/* .container {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: 200rpx 0;
  box-sizing: border-box;
}  */

登录

首先,我们需要通过U钙网设置一枚LOGO,将设计好的logo存放至images中。

login.wxml

<view class="container">
  <!-- LOGO -->
  <view class="logo">
    <image src="../../images/logo.png" mode="" />
  </view>
  <!-- 表单 -->
  <form bindsubmit="" class="form">
    <van-cell-group>
      <van-field bind:change="getUsername" value="{{ username }}" label="账号" placeholder="请输入用户账号" error-message="{{usernameErrMsg}}" />
      <van-field bind:change="getPassword" value="{{ password }}" type="password" password label="密码" placeholder="请输入用户密码" error-message="{{passwordErrMsg}}" border="{{ false }}" />
    </van-cell-group>
    <view class="login">
      <van-button type="primary" class="login-btn" block bind:click="login">登录</van-button>
    </view>
  </form>
  <view class="register">
    <view class="title">您还没有账号?</view>
    <navigator class="href" url="../register/register">请立即注册</navigator>
  </view>
  <!-- 技术支持 -->
  <view class="footer">
    技术支持:西安粤嵌信息科技有限公司
  </view>
</view>

login.wcss

page{
  width: 100%;
  height: 100%;
  background-color: #f2f2f2;
}
.container{
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: start;
}
.container .logo{
  width: 100%;
  height: 380rpx;
  margin-top: 40rpx;
  display: flex;
  justify-content: center;
}
.container .logo image{
  width: 380rpx;
  height: 380rpx;
}
.container .form{
  margin: 0;
  padding: 0;
  width: 100%;
  margin-top: 40rpx;
}
.login{
  width: 100%;
  box-sizing: border-box;
  margin: 60rpx 0rpx 60rpx 0rpx;
  padding: 0 20rpx;
}
.register{
   width: 100%;
   padding: 20rpx;
   display: flex;
   justify-content: center;
   color: #333;
   font-size: 28rpx;
}
.register .href{
  color:#29a1f7;
}
.footer{
  position: fixed;
  bottom: 60rpx;
  font-size: 24rpx;
  color:#666;
}

login.json

{
  "navigationStyle": "custom",
  "usingComponents": {
    "van-field": "@vant/weapp/field/index",
    "van-button": "@vant/weapp/button/index"
  }
}

login.js

// pages/login/login.js
const http = require("../../utils/http")
Page({

  /**
   * 页面的初始数据
   */
  data: {
    username: '', // 用来存表单中的账号信息
    password: '', // 用来存表单中的账号密码
    usernameErrMsg: '', // 账号错误提示
    passwordErrMsg: '', // 密码错误提示
  },
  /**
   * 接收用户输入的账号
   * @param {*} e 
   */
  getUsername(e) {
    this.setData({
      username: e.detail
    })
  },
  /**
   * 接收用户输入的密码
   * @param {*} e 
   */
  getPassword(e) {
    this.setData({
      password: e.detail
    })
  },
  /**
   * 登录的方法
   */
  login() {
    let that = this
    // 解构username,password
    const {
      username,
      password
    } = this.data
    // 非空校验
    if (!username || username.length == 0) {
      this.setData({
        usernameErrMsg: "账号不能为空"
      })
      return
    } else {
      // 重置提示框
      this.setData({
        usernameErrMsg: "",
      })
    }
    if (!password || password.length == 0) {
      this.setData({
        passwordErrMsg: "密码不能为空"
      })
      return // return之后,后续代码不再问题
    } else {
      // 重置提示框
      this.setData({
        passwordErrMsg: ""
      })
    }
    // 请求后端服务器
    wx.request({
      url: `${http.host}/user/login`,
      method: "post",
      data: {
        username: username,
        password: password
      },
      success: (res) => {
        if (res && res.data && res.data.code == 1) { // 登录成功
          // 全局保存用户信息,其他地方也能用 localStorage
          wx.setStorageSync('userId', res.data.data.id)
          wx.setStorageSync('username', res.data.data.username)
          wx.setStorageSync('name', res.data.data.name)
          wx.setStorageSync('role', res.data.data.role)
          // 弹出消息提示框
          wx.showToast({
            title: res.data.msg, //提示文字
            duration: 2000, //显示时长
            mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
            icon: 'success', //图标,支持"success"、"loading"  
            success: function () {
                // 登录成功,跳转
                wx.switchTab({
                  url: '/pages/home/home',
                })
            }
          })
        } else { // 登录失败
          wx.showToast({
            title: res.data.msg, //提示文字
            duration: 2000, //显示时长
            mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
            icon: 'error', //图标,支持"success"、"loading"  
          })
        }
      },
      fail: (err) => {
        wx.showToast({
          title: "服务器请求异常", //提示文字
          duration: 2000, //显示时长
          mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
          icon: 'error', //图标,支持"success"、"loading"  
        })
      }
    })
    console.log("请求后端服务器!");
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {

  },

})

界面效果

QQ图片20230606111906

注册

register.wxml

<!-- 表单 -->
<form bindsubmit="" class="form">
  <van-cell-group>
    <van-field bind:change="getUsername" value="{{ username }}" label="账号" placeholder="请输入用户账号" error-message="{{usernameErrMsg}}" />
    <van-field bind:change="getPassword" value="{{ password }}" type="password" password label="密码" placeholder="请输入用户密码" error-message="{{passwordErrMsg}}" />
    <van-field bind:change="getName" value="{{ name }}" label="姓名" placeholder="请输入用户姓名" error-message="{{nameErrMsg}}" />
    <van-cell title="身份" title-width="100px">
      <van-radio-group value="{{ role }}" bind:change="onChange" direction="horizontal" slot="right-icon">
        <van-radio name="teacher">老师</van-radio>
        <van-radio name="student">学生</van-radio>
      </van-radio-group>
    </van-cell>
  </van-cell-group>

  <view class="login">
    <van-button type="primary" class="login-btn" block bind:click="register">注册</van-button>
  </view>
</form>

register.wcss

page{
  width: 100%;
  height: 100%;
  background-color: #f2f2f2;
}
.container{
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: start;
}
.login{
  width: 100%;
  box-sizing: border-box;
  margin: 60rpx 0rpx 60rpx 0rpx;
  padding: 0 20rpx;
}

register.json

{
  "navigationBarTitleText": "用户注册",
  "usingComponents": {
    "van-field": "@vant/weapp/field/index",
    "van-button": "@vant/weapp/button/index",
    "van-cell": "@vant/weapp/cell/index",
    "van-cell-group": "@vant/weapp/cell-group/index",
    "van-radio": "@vant/weapp/radio/index",
    "van-radio-group": "@vant/weapp/radio-group/index"
  }
}

register.js

// pages/register/register.js
const http = require("../../utils/http")
Page({

  /**
   * 页面的初始数据
   */
  data: {
    username: '', // 用来存表单中的账号信息
    password: '', // 用来存表单中的账号密码
    name: '', // 用来存表单中的用户姓名
    usernameErrMsg: '', // 账号错误提示
    passwordErrMsg: '', // 密码错误提示
    nameErrMsg: '', // 姓名错误提示
    role: 'student', // 单选按钮的值
  },
  /**
   * 接收用户输入的账号
   * @param {*} e 
   */
  getUsername(e) {
    this.setData({
      username: e.detail
    })
  },
  /**
   * 接收用户输入的密码
   * @param {*} e 
   */
  getPassword(e) {
    this.setData({
      password: e.detail
    })
  },
  /**
   * 接收用户输入的姓名
   * @param {*} e 
   */
  getName(e) {
    this.setData({
      name: e.detail
    })
  },
  /**
   * 接收用户选择的用户身份
   * @param {*} event 
   */
  onChange(event) {
    this.setData({
      role: event.detail,
    });
  },
  /**
   * 注册的方法
   */
  register() {
    let that = this
    // 解构username,password
    const {
      username,
      password,
      name,
      role,
    } = this.data
    // 非空校验
    if (!username || username.length == 0) {
      this.setData({
        usernameErrMsg: "账号不能为空"
      })
      return
    } else {
      // 重置提示框
      this.setData({
        usernameErrMsg: "",
      })
    }
    if (!password || password.length == 0) {
      this.setData({
        passwordErrMsg: "密码不能为空"
      })
      return // return之后,后续代码不再问题
    } else {
      // 重置提示框
      this.setData({
        passwordErrMsg: ""
      })
    }
    if (!name || name.length == 0) {
      this.setData({
        nameErrMsg: "姓名不能为空"
      })
      return // return之后,后续代码不再问题
    } else {
      // 重置提示框
      this.setData({
        nameErrMsg: ""
      })
    }
    // 请求后端服务器
    wx.request({
      url: `${http.host}/user/register`,
      method: "post",
      data: {
        username,password,name,role
      },
      success: (res) => {
        if (res && res.data && res.data.code == 1) { // 注册成功
          wx.showToast({
            title: res.data.msg, //提示文字
            duration: 2000, //显示时长
            mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
            icon: 'success', //图标,支持"success"、"loading"  
            success: function () {
              // 注册成功,返回到登录
              wx.navigateBack()
            }
          })
        } else { // 注册失败
          wx.showToast({
            title: res.data.msg, //提示文字
            duration: 2000, //显示时长
            mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
            icon: 'error', //图标,支持"success"、"loading"  
          })
        }
      },
      fail: (err) => {
        wx.showToast({
          title: "服务器请求异常", //提示文字
          duration: 2000, //显示时长
          mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
          icon: 'error', //图标,支持"success"、"loading"  
        })
      }
    })
    console.log("请求后端服务器!");
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {

  },

})

界面效果

QQ图片20230606212044

首页

home.wxml

<view class="container">
  <!-- banner -->
  <view class="banner">
    <image src="../../images/banner.png" mode="" />
  </view>
  <!-- item -->
  <view class="item-box">
    <view class="item" bindtap="gotoCourseList">
      <image src="../../images/item_course.png"></image>
      <view class="title">课程管理</view>
    </view>
    <view class="item" >
      <image src="../../images/item_homework.png"></image>
      <view class="title">作业管理</view>
    </view>
  </view>

</view>

home.wcss

page{
  background-color: #f2f2f2;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}
.container{
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: start;
}
.banner{
  width: 100%;
  height: 300rpx;
}
.banner image{
  width: 100%;
  height: 100%;
}
.item-box{
  width: 100%;
  height: 260rpx;
  display: flex;
  justify-content: space-around;
  align-items: center;
  background-color: #fff;
}
.item-box .item{
  width: 50%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
.item-box .item image{
  width: 50px;
  height: 50px;
}
.item-box .item .title{
  margin-top: 10px;
  color:#333;
  font-size: 14px;
}

home.json

{
  "usingComponents": {
      
  }
}

home.js

// pages/home/home.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    role: 'student'
  },
  /**
   * 跳转到课程管理列表界面
   */
  gotoCourseList() {
    let {
      role
    } = this.data
    if (role == 'teacher') {
      // 跳转老师的课程管理
      wx.navigateTo({
        url: '/pages/teacher/courseList/courseList',
      })
    } else {
      // 跳转学生的课程管理
      wx.navigateTo({
        url: '/pages/student/courseList/courseList',
      })
    }
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    // 获取登录时,保存的用户身份信息
    let role = wx.getStorageSync('role')
    this.setData({
      role
    })
  },
})

界面效果

QQ图片20230607160821

课程管理-老师端

courseList.wxml

<view class="container">
    <!-- header -->
    <view class="header">
        <view class="header-l">
            <view class="title">
                我的课程
            </view>
            <view class="total">
                授课讲师:{{name}}
            </view>
        </view>
        <view class="header-r">
            <van-button round icon="add-o" type="info" size="small" color="linear-gradient(to right, #ef0b24, #ef0b24)" >添加课程</van-button>
        </view>
    </view>
    <!-- 搜索框 -->
    <view class="search">
        <van-search value="{{ keywords }}" shape="round" background="#fff" placeholder="请输入搜索关键词" bind:search="searchChange" bind:blur="searchChange" />
    </view>
    <!-- main -->
    <view class="main" wx:if="{{isShow}}">
        <!-- 列表项 courses:后端返回的数组,item:固定写法,从数组中遍历出来的对象-->
        <view class="item" wx:for="{{courses}}" wx:key="*this">
            <!-- 封面图片 -->
            <view class="item-logo">
                <!-- 如果后端返回封面,就使用后端服务器返回的封面 -->
                <image wx:if="{{item.cover}}" src="{{item.cover}}" mode="" />
                <!-- 如果后端没有返回封面,就是用默认的封面 -->
                <image wx:else src="/images/default_cover.png" mode="" />
            </view>
            <!-- 标题、描述、操作按钮 -->
            <view class="content">
                <!-- 课程标题 -->
                <view class="title">{{item.name}}</view>
                <!-- 课程描述 -->
                <view class="detail">
                    {{item.description}}
                </view>
                <!-- 操作按钮 -->
                <view class="btn">
                    <van-button size="mini" plain round type="info" icon="edit" style="margin-right: 2px;" data-course="{{item}}" >编辑</van-button>
                    <van-button size="mini" plain round type="danger" icon="delete-o" data-id="{{item.id}}" >删除</van-button>
                </view>
            </view>
        </view>
    </view>
    <!-- 没有数据的时候显示 -->
    <van-empty wx:else description="未查询到课程信息" class="empty" />

</view>

courseList.wcss

page {
  width: 100%;
  min-height: 100%;
  background-color: #f2f2f2;
}

.container {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}

.header {
  width: 100%;
  height: 100px;
  background: linear-gradient(to right, #07c160, #6fe79d);
  display: flex;
  flex-direction: row;
  align-items: center;
  box-sizing: border-box;
  padding: 0 20px;
  color: #fff;
  border-top: 1px solid #fff;
}

.header-l,
.header-r {
  width: 50%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.header-r {
  display: flex;
  align-items: flex-end;
}

.header-l .total {
  margin-top: 10px;
  font-size: 14px;
}

.main {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.main .item {
  width: 100%;
  height: 100px;
  margin: 5px 0;
  background-color: #fff;
  display: flex;
  box-sizing: border-box;
  align-items: center;
  padding: 10px;
}

.search {
  width: 100%;
}

.main .item .item-logo {
  width: 100%;
  height: 100%;
  margin-right: 10px;
  flex: 3;
}

.main .item-logo image {
  width: 100%;
  height: 100%;
}

.main .item .content {
  display: flex;
  flex-direction: column;
  flex: 7;
  justify-content: flex-start;
  align-items: flex-start;
}

.main .item .content .title {
  color: #333;
  font-size: 14px;
}

.main .item .content .detail {
  color: #888;
  margin: 5px 0;
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  /*  只要超过宽度就换行,不论中文还是英文 */
  word-break: break-all;
  /* 最多展示两行 */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  /* 根据样式设置 */
  height: 2rem;
}

.main .item .content .btn {
  align-self: flex-end;
  justify-content: right;
}

.empty{
  text-align: center;
  color:#666;
}

courseList.json

{
  "navigationBarTitleText": "课程管理",
  "usingComponents": {
    "van-button": "@vant/weapp/button/index",
    "van-card": "@vant/weapp/card/index",
    "van-field": "@vant/weapp/field/index",
    "van-search": "@vant/weapp/search/index",
    "van-empty": "@vant/weapp/empty/index"
  }
}

courseList.js

// pages/courseList/courseList.js
const http = require("../../../utils/http")
import Dialog from '@vant/weapp/dialog/dialog';
Page({

  /**
   * 页面的初始数据
   */
  data: {
    name: wx.getStorageSync('name') || '保密',
    keywords: null,
    userId: wx.getStorageSync('userId'), // 老师id
    isShow: false, // 是否显示列表
    courses: [] // 课程信息
  },

  /**
   * 生命周期函数--监听页面显示
   * @param {*} options 
   */
  onShow() {
    this.getCourses()
  },
  /**
   * 监听搜索框
   */
  searchChange(e) {
    this.setData({
      keywords: e.detail.value
    })
    this.getCourses()
  },
  /**
   * 通过访问服务端获取课程信息
   */
  getCourses() {
    let that = this
    // 获取keywords
    let {
      keywords,
      userId
    } = this.data
    // 处理keywords
    if (!keywords || keywords.length == 0) { // 查所有
      keywords = "''"
    }
    console.log("关键字:", keywords);
    // 访问网络
    wx.request({
      url: `${http.host}/course/findCoursesById/${userId}/${keywords}/0/50`,
      method: "GET",
      success: (res) => {
        if (res && res.data && res.data.data && res.data.data.length > 0) {
          // 设置列表显示
          this.setData({
            isShow: true,
            courses: res.data.data
          })
        } else {
          // 设置列表隐藏,空状态显示
          this.setData({
            isShow: false
          })
        }
        console.log(res);
      }
    })
  },
})

页面效果

QQ图片20230607215313

课程添加-老师端

courseList.wxml

<view class="header-r">
    <van-button round icon="add-o" type="info" size="small" color="linear-gradient(to right, #ef0b24, #ef0b24)" bind:click="gotoAddCourse">添加课程</van-button>
</view>
...省略其他代码,细节请参考 【课程管理-老师端】 对应的代码

courseList.js

/**
   * 跳转到添加课程界面
   */
gotoAddCourse() {
    wx.navigateTo({
        url: '/pages/teacher/courseAdd/courseAdd',
    })
},
...省略其他代码细节请参考 课程管理-老师端 对应的代码

courseAdd.wxml

<!-- 表单 -->
<form bindsubmit="" class="form">
  <van-cell-group>
    <van-cell title="课程封面" title-width="100px">
      <van-uploader bind:delete="deleteImg" max-count="1" file-list="{{ fileList }}" bind:after-read="afterRead" deletable="{{ true }}" slot="right-icon" />
    </van-cell>
    <van-field bind:change="getName" value="{{ name }}" label="课程名称" placeholder="请输入课程名称" error-message="{{nameErrMsg}}" />
    <van-field type="textarea" autosize  border="{{ false }}" bind:change="getDesc" value="{{ desc }}" label="课程描述" placeholder="请输入课程描述" error-message="{{descErrMsg}}" />
  </van-cell-group>

  <view class="login">
    <van-button type="primary" class="login-btn" block bind:click="save">保存</van-button>
  </view>
</form>

courseAdd.wcss

page{
  width: 100%;
  height: 100%;
  background-color: #f2f2f2;
}
.container{
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: start;
}
.login{
  width: 100%;
  box-sizing: border-box;
  margin: 60rpx 0rpx 60rpx 0rpx;
  padding: 0 20rpx;
}
.van-cell__value{
  text-align: left;
}

courseAdd.json

{
  "navigationBarTitleText": "添加课程",
  "usingComponents": {
    "van-field": "@vant/weapp/field/index",
    "van-button": "@vant/weapp/button/index",
    "van-cell": "@vant/weapp/cell/index",
    "van-cell-group": "@vant/weapp/cell-group/index",
    "van-uploader": "@vant/weapp/uploader/index"
  }
}

courseAdd.js

// pages/teacher/courseAdd/courseAdd.js
const http = require("../../../utils/http")
Page({

  /**
   * 页面的初始数据
   */
  data: {
    fileList: [], // 存放选中的图片
    name: '', // 用来存表单中的课程名称
    desc: '', // 用来存表单中的课程描述
    nameErrMsg: '', // 课程名称错误提示
    descErrMsg: '', // 课程描述错误提示
    userId: wx.getStorageSync('userId') // 登录的老师的ID
  },
  /**
   * 接收用户输入的课程名称
   * @param {*} e 
   */
  getName(e) {
    this.setData({
      name: e.detail
    })
  },
  /**
   * 接收用户输入的课程描述
   * @param {*} e 
   */
  getDesc(e) {
    this.setData({
      desc: e.detail
    })
  },
  /**
   * 读取本地图片
   * @param {*} event 
   */
  afterRead(event) {
    let that = this
    const {
      file
    } = event.detail;
    // 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
    wx.uploadFile({
      url: `${http.fileHost}/upload`, // 后端接口地址
      filePath: file.url,
      name: 'file',
      // formData: {
      //   user: 'test'
      // },
      success(res) {
        console.log("文件上传:", res);
        // 上传完成需要更新 fileList
        if (res && res.statusCode == 200 && res.data) {
          console.log("-------------");
          if (res.data) {
            const fileData = JSON.parse(res.data)
            const {
              fileList = [],
            } = that.data;
            fileList.push({
              ...file,
              url: fileData.url,
              isImage: true,
              deletable: true,
              // status: 'uploading',
              // message: '上传中',
            });
            that.setData({
              fileList
            });
            console.log("====", that.data, res.data);
          }
        }
      },
    });
  },
  /**
   * 保存的方法
   */
  save() {
    let that = this
    // 解构name,desc,fileList,userId
    const {
      name,
      desc,
      fileList,
      userId,
    } = this.data
    // 非空校验
    if (!name || name.length == 0) {
      this.setData({
        nameErrMsg: "课程名称不能为空"
      })
      return
    } else {
      // 重置提示框
      this.setData({
        nameErrMsg: "",
      })
    }
    if (!desc || desc.length == 0) {
      this.setData({
        descErrMsg: "课程描述不能为空"
      })
      return // return之后,后续代码不再问题
    } else {
      // 重置提示框
      this.setData({
        descErrMsg: ""
      })
    }
    // 请求后端服务器
    wx.request({
      url: `${http.host}/course/addCourse`,
      method: "post",
      data: {
        name,
        description: desc,
        cover: fileList[0].url, // 
        teacher_id: userId
      },
      success: (res) => {
        if (res && res.data && res.data.code == 1) { // 保存成功
          wx.showToast({
            title: res.data.msg, //提示文字
            duration: 2000, //显示时长
            mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
            icon: 'success', //图标,支持"success"、"loading"  
            success: function () {
              // 保存成功,跳转课程列表
              wx.redirectTo({
                url: '/pages/teacher/courseList/courseList',
              })
            }
          })
        } else { // 保存失败
          wx.showToast({
            title: res.data.msg, //提示文字
            duration: 2000, //显示时长
            mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
            icon: 'error', //图标,支持"success"、"loading"  
          })
        }
      },
      fail: (err) => {
        wx.showToast({
          title: "服务器请求异常", //提示文字
          duration: 2000, //显示时长
          mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
          icon: 'error', //图标,支持"success"、"loading"  
        })
      }
    })
    console.log("请求后端服务器!");
  },
  /**
   * 删除图片
   * @param {*} options 
   */
  deleteImg(e){
    const {fileList} = this.data
    fileList.splice(e.detail.index,1)
    this.setData({
      fileList
    })
  },
})

界面效果

QQ图片20230608103408

课程删除-老师端

courseList.json

{
  "navigationBarTitleText": "课程管理",
  "usingComponents": {
    ...省略其他代码,细节请参考 【课程管理-老师端】 对应的代码
    "van-dialog": "@vant/weapp/dialog/index"
  }
}

courseList.wxml

<view class="container">
    <!-- header -->
    <!-- 操作按钮 -->
    <view class="btn">
        <van-button size="mini" plain round type="danger" icon="delete-o" data-id="{{item.id}}" bind:click="delete">删除</van-button>
    </view>
    ...省略其他代码,细节请参考 【课程管理-老师端】 对应的代码
     <!-- 删除确认框,放到其他代码的最后面 -->
    <van-dialog id="van-dialog" />
</view>

courseList.js

// pages/courseList/courseList.js
const http = require("../../../utils/http")
// 导入删除信息确认框
import Dialog from '@vant/weapp/dialog/dialog';
Page({
  ...省略其他代码细节请参考 课程管理-老师端 对应的代码
  /**
   * 删除课程
   */
  delete(e) {
    let that = this
    // 接收删除按钮 data-* 传递的数据 e.target.dataset.*
    const id = e?.target?.dataset?.id
    if (id) {
      Dialog.confirm({
          selector: "#van-dialog",
          title: '温馨提示',
          message: '是否确定删除当前课程?',
        })
        .then(() => {
          // 请求服务器,删除课程
          wx.request({
            url: `${http.host}/course/deleteCourse/${id}`,
            method: "delete",
            success: (res) => {
              if (res && res.data && res.data.code == 1) { // 删除成功
                wx.showToast({
                  title: res.data.msg, //提示文字
                  duration: 2000, //显示时长
                  mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
                  icon: 'success', //图标,支持"success"、"loading"  
                  success: function () {
                    // 保存成功,跳转课程列表
                    that.getCourses();
                  }
                })
              } else { // 删除失败
                wx.showToast({
                  title: res.data.msg, //提示文字
                  duration: 2000, //显示时长
                  mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
                  icon: 'error', //图标,支持"success"、"loading"  
                })
              }
            },
            fail: (err) => {
              console.error("删除课程:", err);
              wx.showToast({
                title: "服务器请求异常", //提示文字
                duration: 2000, //显示时长
                mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
                icon: 'error', //图标,支持"success"、"loading"  
              })
            }
          })
        })
        .catch(() => {
          // on cancel
        });
    }
    console.log("删除课程:", id);
  }

})

界面效果

QQ图片20230608143108

课程修改-老师端

courseList.wxml

<!-- 操作按钮 -->
<view class="btn">
    <van-button size="mini" plain round type="info" icon="edit" style="margin-right: 2px;" data-course="{{item}}" bind:click="gotoCourseUpdate">编辑</van-button>
</view>
 ...省略其他代码,细节请参考 【课程管理-老师端】 对应的代码

courseList.js

/**
   * 跳转到修改课程界面
   * @param {*} e 
   */
gotoCourseUpdate(e) {
    let that = this
    // 获取编辑按钮携带的参数
    const course = e?.target?.dataset?.course
    if (course) {
        // 跳转到课程修改界面
        wx.navigateTo({
            url: '/pages/teacher/courseUpdate/courseUpdate',
            success: function (res) {
                // 通过eventChannel向被打开页面传送数据
                res.eventChannel.emit('courseUpdate', {
                    data: course
                })
            }
        })
    }

},
...省略其他代码细节请参考 课程管理-老师端 对应的代码

courseUpdate.wxml

<!-- 表单 -->
<form bindsubmit="" class="form">
  <van-cell-group>
    <van-cell title="课程封面" title-width="100px">
      <van-uploader bind:delete="deleteImg" max-count="1" file-list="{{ fileList }}" bind:after-read="afterRead" deletable="{{ true }}" slot="right-icon" />
    </van-cell>
    <van-field bind:change="getName" value="{{ name }}" label="课程名称" placeholder="请输入课程名称" error-message="{{nameErrMsg}}" />
    <van-field type="textarea" autosize  border="{{ false }}" bind:change="getDesc" value="{{ desc }}" label="课程描述" placeholder="请输入课程描述" error-message="{{descErrMsg}}" />
  </van-cell-group>

  <view class="login">
    <van-button type="primary" class="login-btn" block bind:click="save">保存</van-button>
  </view>
</form>

courseUpdate.wcss

/* pages/teacher/courseUpdate/courseUpdate.wxss */
page{
  width: 100%;
  height: 100%;
  background-color: #f2f2f2;
}
.container{
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: start;
}
.login{
  width: 100%;
  box-sizing: border-box;
  margin: 60rpx 0rpx 60rpx 0rpx;
  padding: 0 20rpx;
}
.van-cell__value{
  text-align: left;
}

courseUpdate.json

{
  "navigationBarTitleText": "修改课程",
  "usingComponents": {
    "van-field": "@vant/weapp/field/index",
    "van-button": "@vant/weapp/button/index",
    "van-cell": "@vant/weapp/cell/index",
    "van-cell-group": "@vant/weapp/cell-group/index",
    "van-uploader": "@vant/weapp/uploader/index"
  }
}

courseUpdate.js

// pages/teacher/courseAdd/courseAdd.js
const http = require("../../../utils/http")
Page({

  /**
   * 页面的初始数据
   */
  data: {
    fileList: [], // 存放选中的图片
    id:0,// 用来存表单中的课程ID
    name: '', // 用来存表单中的课程名称
    desc: '', // 用来存表单中的课程描述
    cover: '', // 封面图片
    nameErrMsg: '', // 课程名称错误提示
    descErrMsg: '', // 课程描述错误提示
  },
  /**
   * 接收用户输入的课程名称
   * @param {*} e 
   */
  getName(e) {
    this.setData({
      name: e.detail
    })
  },
  /**
   * 接收用户输入的课程描述
   * @param {*} e 
   */
  getDesc(e) {
    this.setData({
      desc: e.detail
    })
  },
  /**
   * 读取本地图片
   * @param {*} event 
   */
  afterRead(event) {
    let that = this
    const {
      file
    } = event.detail;
    // 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
    wx.uploadFile({
      url: `${http.fileHost}/upload`, // 后端接口地址
      filePath: file.url,
      name: 'file',
      // formData: {
      //   user: 'test'
      // },
      success(res) {
        console.log("文件上传:", res);
        // 上传完成需要更新 fileList
        if (res && res.statusCode == 200 && res.data) {
          if (res.data) {
            const fileData = JSON.parse(res.data)
            const {
              fileList = [],
            } = that.data;
            fileList.push({
              ...file,
              url: fileData.url,
              isImage: true,
              deletable: true,
            });
            that.setData({
              fileList
            });
          }
        }
      },
    });
  },
  /**
   * 保存的方法
   */
  save() {
    let that = this
    // 解构name,desc,fileList,id
    const {
      name,
      desc,
      fileList,
      id,
    } = this.data
    // 非空校验
    if (!name || name.length == 0) {
      this.setData({
        nameErrMsg: "课程名称不能为空"
      })
      return
    } else {
      // 重置提示框
      this.setData({
        nameErrMsg: "",
      })
    }
    if (!desc || desc.length == 0) {
      this.setData({
        descErrMsg: "课程描述不能为空"
      })
      return // return之后,后续代码不再问题
    } else {
      // 重置提示框
      this.setData({
        descErrMsg: ""
      })
    }
    // 请求后端服务器
    wx.request({
      url: `${http.host}/course/updateCourse`,
      method: "put",
      data: {
        name,
        description: desc,
        cover: fileList[0].url, // 封面图片的地址
        id: id
      },
      success: (res) => {
        if (res && res.data && res.data.code == 1) { // 保存成功
          wx.showToast({
            title: res.data.msg, //提示文字
            duration: 2000, //显示时长
            mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
            icon: 'success', //图标,支持"success"、"loading"  
            success: function () {
              // 保存成功,跳转课程列表
              wx.redirectTo({
                url: '/pages/teacher/courseList/courseList',
              })
            }
          })
        } else { // 保存失败
          wx.showToast({
            title: res.data.msg, //提示文字
            duration: 2000, //显示时长
            mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
            icon: 'error', //图标,支持"success"、"loading"  
          })
        }
      },
      fail: (err) => {
        wx.showToast({
          title: "服务器请求异常", //提示文字
          duration: 2000, //显示时长
          mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false  
          icon: 'error', //图标,支持"success"、"loading"  
        })
      }
    })
    console.log("请求后端服务器!");
  },
  /**
   * 删除图片
   * @param {*} options 
   */
  deleteImg(e){
    console.log("===",e.detail.index);
    const {fileList} = this.data
    fileList.splice(e.detail.index,1)
    this.setData({
      fileList
    })
  },
  /**
   * 页面加载后执行
   * @param {*} options 
   */
  onLoad(options) {
    // 接收上一个页面传递的数据
    const that = this
    // 监听acceptDataFromOpenerPage事件,获取上一页面通过eventChannel传送到当前页面的数据
    const eventChannel = this.getOpenerEventChannel()
    eventChannel.on('courseUpdate', function (data) {
      console.log(data) //输出{data: 'test'}
      let fileList;
      // 如果服务器有图片,就显示原来的图片
      if (data.data.cover) {
        fileList = [{
           url:data.data.cover, // 封面图片的路径
           deletable:true,
           isImage:true
        }]
      } else {// 如果服务器没有图片,就置空
        fileList = []
      }
      that.setData({
        id:data.data.id,
        name: data.data.name,
        desc: data.data.description,
        fileList: fileList
      })
    })
  }
})

界面效果

QQ图片20230608170012

附录

前后端交互实现

image-20230531165443839

后端代码分层设计

image-20230601082037041

RestFull接口说明

基于RestFull风格的请求

序号 请求方式 应用场合 举例
1 get 查询 http://localhost:3000/user/userList
2 post 增加、登录 http://localhost:3000/user/addUser
3 put 修改 http://localhost:3000/user/updateUser
4 delete 删除 http://localhost:3000/user/deleteUser/1

关于文件的访问路径

image-20230601144716307

JSON

说明

​ 我们前后端分离开发,会存在一个问题,就是前后端使用的编程语言可能不一样,那么前后端如何交互呢?目前主流的交互是,将数据以字符串的方式进行交互,因为所有的编程语言都支持字符串。

​ 但是普通的字符串无法传输过于复杂的数据,因此JSON就诞生了,JSON就是一种拥有标准格式的字符串。

格式

Array格式

[]

Object格式

{
    "key":value
    ...
}

解释

key:JSON中,key只能是字符串类型

value:JSON中,value支持string,number,boolean,object,array,null

案例

[
    { 
       "id":1,
       "username":"zs",
       "name":"张三",
       "role":"student",
       "hobbies":["唱歌","跳舞","如阿普"],
       "friends":{
           ...
       }
    }
]

空文件

简介

在线教学管理系统小程序端 展开 收起
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/guzige2012/online-teaching-manager-wx.git
git@gitee.com:guzige2012/online-teaching-manager-wx.git
guzige2012
online-teaching-manager-wx
online-teaching-manager-wx
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891