手把手教你写项目之中小学微课学习系统(七):课程展示-知识的殿堂

阅读前请先下载项目源码,边读边看源码以加深理解和实操,
源码地址已放于文章末尾!

效果预览:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

07-课程展示-知识的殿堂

“芝麻开门”之后,我们终于进入了宝库内部!但问题来了,现在的宝库里空空荡荡,跟“毛坯房”一样,这可留不住用户。所以,咱们今天的主要任务,就是把这个“毛坯房”精装修一下,把各种“珍宝”——也就是我们的课程,给陈列展示出来。

我们将分三步走:

  1. 后端施工:编写课程课程分类的CRUD接口。
  2. 前端改造:在主页(index.html)上,通过axios调用后端接口,把课程数据动态地渲染到页面上。
  3. 前后端联调:确保前端能正确拿到数据并展示出来。

第一步:后端施工 - 构建课程API

和之前用户模块的流程一样,我们也是按照 Entity -> Mapper -> Service -> Controller 的顺序来构建我们的课程模块。

1. Entity(实体)

我们在 entity 包下创建 Category.java, Course.java, Lesson.java。这些代码结构和User.java类似,主要是字段不同。(为节省篇幅,这里只展示Course.java,其他的类似)。

entity/Course.java:

package com.weke.learningsystem.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.sql.Timestamp;

@Data
@TableName("course")
public class Course {
    private Long id;
    private String title;
    private String description;
    private String coverImageUrl;
    private Long categoryId;
    private Timestamp createdAt;
}

2. Mapper(数据访问)

mapper 包下创建 CategoryMapper.java, CourseMapper.java。它们都继承自 BaseMapper

mapper/CourseMapper.java:

package com.weke.learningsystem.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.weke.learningsystem.entity.Course;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface CourseMapper extends BaseMapper<Course> {
}

3. Service(业务逻辑)

service 包下创建对应的 Service 接口和实现类。

service/CourseService.java:

package com.weke.learningsystem.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.weke.learningsystem.entity.Course;

public interface CourseService extends IService<Course> {
}

service/CourseServiceImpl.java:

package com.weke.learningsystem.service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.weke.learningsystem.entity.Course;
import com.weke.learningsystem.mapper.CourseMapper;
import org.springframework.stereotype.Service;

@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements CourseService {
}

4. Controller(API接口)

这是后端的“出口”,我们在这里把对课程的查询能力暴露给前端。

controller/CourseController.java:

package com.weke.learningsystem.controller;

import com.weke.learningsystem.entity.Course;
import com.weke.learningsystem.service.CourseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/courses")
public class CourseController {

    @Autowired
    private CourseService courseService;

    // 获取所有课程列表
    @GetMapping
    public List<Course> getAllCourses() {
        return courseService.list();
    }

    // 根据ID获取单个课程的详细信息
    @GetMapping("/{id}")
    public Course getCourseById(@PathVariable Long id) {
        return courseService.getById(id);
    }
    
    // (后面我们还会加上给管理员使用的创建、更新、删除课程的接口)
    // @PostMapping
    // public boolean createCourse(@RequestBody Course course) { ... }
}

好了,后端的部分就暂时告一段落。我们已经拥有了获取所有课程和单个课程详情的API。现在,重启后端服务,确保它正常运行。

第二步:前端改造 - 动态渲染页面

现在,轮到前端“表演”了。打开我们的主页文件 index.html

我们的目标是,当页面加载完成后:

  1. localStorage 中检查是否存在 jwt_token,如果没有,说明用户没登录,直接“踹”回登录页。
  2. 如果已登录,就向后端的 /courses 接口发送一个GET请求。
  3. 拿到课程列表数据后,用JavaScript动态地创建HTML元素,把课程信息一个一个地“塞”到页面的课程列表区域里。

首先,我们需要在 axios 的配置中,加入一个“请求拦截器”(Request Interceptor)。它的作用是,在我们每次发送请求前,都自动检查 localStorage 里有没有Token,如果有,就把它塞到请求的 Authorization 头里。这样,我们就不用每次请求都手动加一遍Token了。

修改 Scripts/api.js:

const apiClient = axios.create({
    baseURL: 'http://localhost:8080',
    timeout: 5000,
});

// 添加请求拦截器
apiClient.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    const token = localStorage.getItem('jwt_token');
    if (token) {
        config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});

然后,创建 Scripts/index.js 文件,并在 index.html 中引入它。

Scripts/index.js:

document.addEventListener('DOMContentLoaded', function() {

    // 1. 登录状态检查
    const token = localStorage.getItem('jwt_token');
    if (!token) {
        // 如果没有token,就跳转回登录页面
        alert('请先登录!');
        window.location.href = 'Login.html';
        return;
    }

    // 2. 获取课程列表
    fetchCourses();

});

function fetchCourses() {
    const courseListContainer = document.getElementById('course-list-container'); // 假设课程列表的容器div的id是这个

    apiClient.get('/courses')
        .then(response => {
            const courses = response.data;
            
            // 每次加载前,先清空容器,防止重复渲染
            courseListContainer.innerHTML = ''; 
            
            // 3. 遍历课程数据,动态创建HTML
            courses.forEach(course => {
                const courseCard = document.createElement('div');
                courseCard.className = 'course-card'; // 假设我们用这个class来定义课程卡片的样式

                courseCard.innerHTML = `
                    <img src="${course.coverImageUrl}" alt="${course.title}">
                    <h3>${course.title}</h3>
                    <p>${course.description}</p>
                    <a href="course-detail.html?id=${course.id}">查看详情</a>
                `;
                
                courseListContainer.appendChild(courseCard);
            });
        })
        .catch(error => {
            console.error('获取课程列表失败:', error);
            if (error.response && error.response.status === 401) {
                // Token失效或未授权
                alert('登录已过期,请重新登录!');
                localStorage.removeItem('jwt_token');
                window.location.href = 'Login.html';
            } else {
                 courseListContainer.innerHTML = '<p>加载课程失败,请稍后再试。</p>';
            }
        });
}

代码解读:

  • apiClient.interceptors.request.use(...):这就是 axios 强大的拦截器功能。它像一个“门卫”,在每个请求“出门”前,都给它盖个“已认证”的章(附上Token)。
  • document.createElement('div')appendChild():这是原生JavaScript操作DOM的核心方法,我们用它来动态地“凭空造物”,把从后端拿到的数据,变成用户看得见的HTML元素。
  • <a href="course-detail.html?id=${course.id}">:我们还在“查看详情”的链接里,巧妙地把课程的id作为URL参数传了过去。这样,在详情页我们就能知道要加载哪个课程的数据了。

第三步:联调与验证

现在,是时候见证奇迹了。

  1. 确保你的数据库里有几条课程的测试数据。(你可以手动INSERT几条)
  2. 确保后端服务已经重启并正常运行。
  3. 确保你之前已经成功登录,并且localStorage里存有jwt_token
  4. 用浏览器打开 index.html

如果一切顺利,你应该能看到页面上不再是静态的假数据,而是真真正正从你数据库里读取出来的课程列表!

至此,我们已经打通了登录后获取核心业务数据的流程,我们的“知识殿堂”终于不再是空架子了!

下一期预告:
课程列表虽然出来了,但用户最关心的还是点进去看视频。下一章,我们将完成课程详情页视频播放功能,让用户能够真正地“学起来”。这将涉及到如何处理URL参数,以及如何在前端集成一个好用的视频播放器。最精彩的部分,即将上演!

源码下载地址:
https://download.youkuaiyun.com/download/THMAIL/91753658

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

THMAIL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值