【SpringBoot + Vue 实战项目-师生课程管理系统(附源码地址)】基于Vue3开发前端页面部分,实现教师、学生两种角色的页面功能展示

基于Vue3开发前端页面部分,实现教师、学生两种角色的页面功能展示

1. 项目总览

在这里插入图片描述

可移步我的主页查看项目总览博客

另外,我清楚我的前端部分写的不是很规范,而且也有一部分代码冗余,甚至还有Vue2和Vue3的混用,但是能正确跑起来!那就万岁!因为团队中没有前端,我的前端是现学的,很多规范还没来得及了解。所以我的第一步肯定是完成,之后再去完善,如果我的代码能对你有些许的启发,那就是好的。由于系统前后端都是我一个人开发的话,对接起来比较方便,没有沟通的代价。有什么不足之处,还望大佬们不吝赐教!

很多前端样式都是靠Element-Plus的,自己写的样式也有但比较少。

2. 前端技术栈

Vue3、VueRouter、Vuex、Element-Plus、Axios等

3. 系统目录结构

在这里插入图片描述

目录结构也可以看出,有一些图片我直接和代码文件放在一起了。主要是因为懒,但这可不是好习惯,大家可以自行查看更改!

4. 用户通用功能界面

主要包括:用户注册、用户登录、用户退出、获取用户信息、更新用户信息、上传用户头像等功能

用户注册界面

<template>
  <div >
    <el-row>
      <el-col :span = "24">
        <el-text class="w-150px mb-2" truncated>
          用户注册
        </el-text>
      </el-col>
      <el-col :span = "24">
        <div class="flex items-center justify-center">
    <el-form :model="user" :rules="rules" ref="formRef" class="w-[250px]">
      <el-form-item prop="username" :span = "24">
        <el-text class="w-150px mb-2" truncated>
          用户名:
        </el-text>
        <el-input
            v-model="user.username"
            style="width: 240px"
            placeholder="请输入用户名"
            clearable
        />
        <br><br>
      </el-form-item>
      <el-form-item prop="password" class="flex items-center justify-center">
        <el-text class="w-150px mb-2" truncated>
          密码:
        </el-text>
        <el-input
            v-model="user.password"
            style="width: 240px"
            type="password"
            placeholder="请输入密码"
            show-password
        />
        <br><br>
      </el-form-item>

      <el-form-item prop="email">
        <el-text class="w-150px mb-2" truncated>
          邮箱:
        </el-text>
        <el-input
            v-model="user.email"
            style="width: 240px"
            type="text"
            placeholder="请输入邮箱"
            clearable
        />
        <br><br>
      </el-form-item>

      <el-form-item prop="phone">
        <el-text class="w-150px mb-2" truncated>
          手机:
        </el-text>
        <el-input
            v-model="user.phone"
            style="width: 240px"
            type="text"
            placeholder="请输入手机"
            clearable
        />
        <br><br>
      </el-form-item>

      <el-form-item prop="gender">
        <el-text class="w-150px mb-2" truncated>
          性别:
        </el-text>
        <el-select
            v-model="user.gender"
            placeholder="请选择性别"
            style="width: 240px"
        >
          <el-option
              v-for="item in options"
              :key="item.value"
              :label="item.label"
              :value="item.value"
          />
        </el-select>
        <br><br>
      </el-form-item>

      <el-form-item prop="role">
        <el-text class="w-150px mb-2" truncated>
          身份:
        </el-text>
        <el-radio-group v-model="user.role">
          <el-radio :value=1>学生</el-radio>
          <el-radio :value=2>教师</el-radio>
        </el-radio-group>
        <br><br>
      </el-form-item>

      <el-form-item>
        <el-button round color="#626aef" class="w-[250px]" type="primary" @click="register" :loading="loading">
          注 册
        </el-button>
      </el-form-item>
    </el-form>
          </div>
      </el-col>
    </el-row>
  </div>
</template>

<script>

import { ref, reactive} from 'vue'
import api from "../../api"
import { toast } from "../../utils/popup"
import router from "@/router";


export default {
  name: "register",
  setup(){
    const user = reactive({
      username: '',
      password: '',
      email: '',
      phone: '',
      gender: '',
      role: ''
    })

    const rules = {
      username: [
        { required: true, message: "用户名不能为空", trigger: "blur" },
        { min: 6, max: 20, message: "用户名长度必须是6-20个字符", trigger: "blur" },
      ],
      password: [
        {
          required: true,
          message: "密码不能为空",
          trigger: "blur",
        },
        { min: 6, max: 20, message: "密码长度必须是6-20个字符", trigger: "blur" },

      ],
      email: [
        { required: true, message: "邮箱不能为空", trigger: "blur" },
        { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] },
      ],
      phone: [
        { required: true, message: "手机不能为空", trigger: "blur" },
        {
          pattern: /^1[3-9]\d{9}$/,
          message: "请输入正确的手机号码",
          trigger: "blur",
        },
      ],
      gender: [
        { required: true, message: "性别不能为空", trigger: "blur" },
      ],
      role: [
        { required: true, message: "身份不能为空", trigger: "blur" },
      ]
    };

    const formRef = ref(null) // 表单引用

    function register() {
      console.log("register")
      formRef.value.validate((valid) => {
        if (valid) {
          api.register(user).then(res => {
            if (res.data.code === 200) {
              toast("注册成功,请继续登录", "success")
              setTimeout(() => {
                router.push("/")
              }, 1000)
            }else {
              toast(res.data.msg, "error")
            }
          })
        }else {
          toast("表单有误", "error")
        }
      })
    }

    const value = ref('')
    const options = [
      {
        value: true,
        label: '男',
      },
      {
        value: false,
        label: '女',
      }
    ]

    return{
      user,
      register,
      options,
      rules,
      formRef
    }

  }
}
</script>

<style scoped>

</style>

注册界面展示
在这里插入图片描述

用户登录界面

<template>
    <div id="bg">
        <div id="title_bar">
          <span class="title_con">青云智汇</span>
          <span class="title_con_para">--更懂学生的智教平台</span>
          <img id="logo" src="../../assets/myLogo.jpg">
        </div>
        <div id="hengline"></div>
        <div id="main">
            <div id="title_blank"><span class="con_title_sp">欢迎登录</span></div>
            <el-form :model="form" :rules="rules" ref="formRef" class="w-[300px]">
                <el-form-item prop="username" class="form-item-up">
                <el-text class="w-150px mb-2" truncated>
                    用户名:
                </el-text>
                <el-input
                    v-model="form.username"
                    style="width: 240px"
                    placeholder="请输入用户名"
                    clearable
                    @focus="clearValidationMessage('username')"
                />
                </el-form-item>
                <el-form-item prop="password" class="form-item" >
                <el-text class="w-150px mb-2" truncated>&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;&nbsp;   
                </el-text>
                <el-input
                    v-model="form.password"
                    style="width: 240px"
                    type="password"
                    placeholder="请输入密码"
                    show-password
                    @keyup.enter.prevent="login"
                />
                </el-form-item>
                <el-form-item class="form-item">
                <el-button round color="#1343db" class="login-btn" @click="login" :loading="loading">
                    登 录
                </el-button>
                </el-form-item>
                <el-form-item>
                <el-button  @click="drawer = true" class="register-btn" :style="{ opacity: dynamicOpacity }" >注 册</el-button>
                <el-dialog v-model="drawer" size = '100%' direction='btt'
                            :before-close="handleClose">
        
                    <register></register>
                </el-dialog>
                </el-form-item>
            </el-form>
    
        </div>
    </div>
  </template>

<script>
  import { ref, reactive} from 'vue'
  import api from "../../api"
  import { toast } from "../../utils/popup"
  import register from "@/components/user/register"
  import { ElMessageBox } from 'element-plus'
  import { useRouter } from "vue-router";
  import store from '../../store'

  export default {
    dynamicOpacity: 0.4,
    name: "login",
    components:{
      register
    },
    setup() {
      const router = useRouter()
      const form = reactive({
        username: "",
        password: "",
      });
      const drawer = ref(false)
      const rules = {
        username: [
          { required: true, message: "用户名不能为空", trigger: "focus" },
        ],
        password: [
          {
            required: true,
            message: "密码不能为空",
            trigger: "submit",
          },
          {message: "密码不能为空",
            trigger: "input",}
        ],
      };
      const clearValidationMessage = (field) => {
            // 设置该字段的验证消息为空
            form[field + 'ErrorMessage'] = ''
        }
      const formRef = ref(null)
      const loading = ref(false)
      function login() {
        formRef.value.validate((valid) => {
          if (valid) {
            loading.value = true
            api.login(form.username, form.password).then((res) => {
              if(res.data.code == 200){
                toast("登录成功,正在跳转")
                const token = res.data.data.token; // 假设 token 在这个位置
  
                if (token) {
                  localStorage.setItem("access_token", token); // 设置 token 到 localStorage
                }
                // 调用 Vuex 中的 login action
                store.dispatch('login', res.data.data)
  
                if(res.data.data.role == 1){
                  setTimeout(() => {
                    router.push("/");
                  }, 1000)
                }else if(res.data.data.role == 2){
                  setTimeout(() => {
                    router.push("/teacherHome");
                  }, 1000)
                }else{
                  setTimeout(() => {
                    router.push("/adminHome");
                  }, 1000)
                }
              }else {
                toast(res.data.msg, "error")
              }
            }).finally(() => {
              loading.value = false
            })
          } else {
            toast("表单有误,请正确填写表单", "error")
          }
        })
      }
      const handleClose = (done) => {
        ElMessageBox.confirm('确定要取消注册?')
            .then(() => {
              done()
            })
            .catch(() => {
            })
      }
  
      return {
        form,
        login,
        rules,
        formRef,
        drawer,
        handleClose,
        clearValidationMessage
      }
    }
  }
  </script>
  
  <style scoped>
    #main{
        text-align: center;
        background-color: #fff;
        border-radius: 20px;
        width: 400px;
        height: 300px;
        margin: auto;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
    }
    #logo{
        margin-left: 70%;
        width: 165px;
        height: 55px;
    }
    #app{
      margin-top: 0;
    }
    #bg{
        width: 100%;
      min-height: 100vh;
        background-color: #6495ed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
    }
    #title_blank{
        width: 100%;
        height: 60px;
        background-color: #1343db;
        border-top-left-radius: 20px;
        border-top-right-radius: 20px;
        display: flex;
        align-items: center;
    }
    #title_bar{
        background-color: white;
      display: flex; /* 使用 Flexbox 布局 */
      justify-content: space-between; /* 在两端对齐,中间自动分配空间 */
      align-items: center; /* 垂直居中对齐 */
    }
    #hengline{
        margin-top: 5px;
        background-color: white;
        height: 3px;
    }
    .title_con{
        margin-left: 30px;
        color: black;
        font-size: 40px;
        font-family: 隶书;
    }
    .title_con_para{
        color: black;
        font-size: 25px;
        font-family: 隶书;
      margin-right: 10px;
    }
    .con_title_sp{
        color: #fff;
        display: flex;
        align-items: center;
        margin-left: 32%;
        font-size: 30px;
        font-family: 隶书;
    }
    .form-item {
        margin-left: 50px;
        margin-right: 50px;
        display: flex;
        align-items: center;
    }
    .form-item-up {
        margin-top: 40px;
        margin-left: 50px;
        margin-right: 50px;
        display: flex;
        align-items: center;
    }
    .login-btn {
        width: 100%;
    }
    .login-btn:hover {
        transform: scale(1.1,1.1);
    }
    .register-btn {
        width: 80%;
        margin-left: 50px;
        margin-right: 50px;
        border-radius: 20px;
    }
    .register-btn:hover {
        transform: scale(1.1,1.1);
    }

  </style>

登录界面展示
在这里插入图片描述

用户个人信息界面

<template>
    <el-dialog v-model="drawer" size="50%" direction="btt" :before-close="handleClose">
      <div
          title="修改个人信息"
          :visible.sync="dialogVisible"
          width="200px"
          @close="handleClose"
      >
        <el-form
            :model="userUpdateInfo"
            :rules="rules"
            :label-position="labelPosition"
            label-width="auto"
            ref="userInfoFormRef"
        >
          <el-form-item label="昵称" prop="nickname">
            <el-input v-model="userUpdateInfo.nickname" />
          </el-form-item>
          <el-form-item label="邮箱" prop="email">
            <el-input v-model="userUpdateInfo.email" />
          </el-form-item>
          <el-form-item label="手机" prop="phone">
            <el-input v-model="userUpdateInfo.phone" />
          </el-form-item>
          <el-form-item>
            <span slot="footer" class="dialog-footer">
              <el-button @click="drawer = false">取 消</el-button>
              <el-button type="primary" @click="submitForm">确 定</el-button>
            </span>
          </el-form-item>
        </el-form>
      </div>
    </el-dialog>

  <el-dialog v-model="cameraDialog" size="50%" :before-close="handleClose" style="width: 700px">
    <div
        title="制作趣味头像"
        width="150px"
        @close="handleClose"
    >
      <camera  @avatar-updated="onAvatarUpdated"></camera>
    </div>
  </el-dialog>

  <el-descriptions
      class="margin-top"
      :column="2"
      size="large"
      border
  >
    <el-descriptions-item>
      <template #label>
        <div class="cell-item">
          <el-icon :style="iconStyle">
            <user />
          </el-icon>
          用户名
        </div>
      </template>
      {{ $store.state.user.username }}
    </el-descriptions-item>
    <el-descriptions-item>
      <template #label>
        <div class="cell-item">
          <el-icon :style="iconStyle">
            <user />
          </el-icon>
          昵称
        </div>
      </template>
      {{ $store.state.user.nickname }}
    </el-descriptions-item>
    <el-descriptions-item>
      <template #label>
        <div class="cell-item">
          <el-icon :style="iconStyle">
            <Female />
          </el-icon>
          性别
        </div>
      </template>
      <el-tag size="small">{{ $store.state.user.gender }}</el-tag>
    </el-descriptions-item>
    <el-descriptions-item>
      <template #label>
        <div class="cell-item">
          <el-icon :style="iconStyle">
            <iphone />
          </el-icon>
          联系方式
        </div>
      </template>
      {{ $store.state.user.phone }}
    </el-descriptions-item>
    <el-descriptions-item>
      <template #label>
        <div class="cell-item">
          <el-icon :style="iconStyle">
            <ChatLineRound />
          </el-icon>
          电子邮箱
        </div>
      </template>
      {{ $store.state.user.email }}
    </el-descriptions-item>
    <el-descriptions-item>
      <template #label>
        <div class="cell-item">
          <el-icon :style="iconStyle">
            <tickets />
          </el-icon>
          用户身份
        </div>
      </template>
      <el-tag size="small">{{ userRole }}</el-tag>
    </el-descriptions-item>
    <el-descriptions-item>
      <template #label>
        <div class="cell-item">
          <el-icon :style="iconStyle">
            <Avatar />
          </el-icon>
          用户头像
        </div>
      </template>
      <uploadAvatar></uploadAvatar>
      <el-button  @click="cameraDialog = true">制作趣味头像</el-button>
    </el-descriptions-item>
  </el-descriptions>
  <br>
    <el-button type="primary" @click="drawer = true">修改个人信息</el-button>


</template>


<script>

import api from '../../api'
import { ref, onMounted, reactive } from 'vue'
import {ElMessageBox} from "element-plus";
import store from "@/store";
import { toast, showModal } from "@/utils/popup";
import courseVideo from "@/components/courseVideo";
import defaultImg from "@/assets/default.png";
import uploadAvatar from "@/components/uploadAvatar"
import {Avatar} from "@element-plus/icons-vue";
import Camera from "@/components/Camera";



export default {
  name: "studentHome",
  components: {Camera, Avatar, courseVideo,uploadAvatar },

  data() {
    return {
      dialogVisible: true, // 控制对话框的显示与隐藏
      labelPosition: 'right',
    };
  },

  setup() {
    const drawer = ref(false)
    const cameraDialog = ref(false)

    const username = store.getters.getUsername

    const userUpdateInfo = reactive({
      username: username,
      email: '',
      phone: '',
      nickname: '',
    })

    const rules = {
      nickname: [
        { required: true, message: "昵称不能为空", trigger: "blur" },
        { min: 6, max: 20, message: "昵称长度必须是6-20个字符", trigger: "blur" },
      ],
      email: [
        { required: true, message: "邮箱不能为空", trigger: "blur" },
        { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] },
      ],
      phone: [
        { required: true, message: "手机不能为空", trigger: "blur" },
        {
          pattern: /^1[3-9]\d{9}$/,
          message: "请输入正确的手机号码",
          trigger: "blur",
        },
      ],
    };

    const userRole = ref();
    onMounted(() => {
      userRole.value = store.state.user.role == 1 ? '学生' : '教师';
    })

    const handleClose = (done) => {
      ElMessageBox.confirm('确定要取消更改?')
          .then(() => {
            done()
            userInfoFormRef.value.resetFields(); // 清空表单字段
          })
          .catch(() => {
          })
    }

    const userInfoFormRef = ref(null) // 表单引用

    function submitForm() {
      // 提交表单的逻辑,这里简单打印表单数据,实际应提交至后端
      userInfoFormRef.value.validate(valid => {
        if (valid) {
          // 表单验证通过,执行提交逻辑
          // 这里可以调用API更新用户信息,并处理响应
          api.updateUserInfo(userUpdateInfo).then((res) => {
            store.dispatch("updateUserInStore", res.data.data)
          })
          toast('信息修改成功!', 'success');
          drawer.value = false; // 提交成功后关闭对话框
        } else {
          toast("表单有误", "error");
        }
      });
    }

    const AvatarUrl = store.state.user.avatar == null ? defaultImg : store.state.user.avatar;

    function onAvatarUpdated(updated){
      if(updated){
        cameraDialog.value = false
      }
    }


    return {
      drawer,
      handleClose,
      userUpdateInfo,
      rules,
      submitForm,
      userInfoFormRef,
      AvatarUrl,
      userRole,
      cameraDialog,
      onAvatarUpdated,
    }
  }
}
</script>

<style scoped>
.my-info {
  text-align: center;
  font-weight: bold;
  margin-bottom: 20px;
}

.info-container {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.info-item {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}

.info-label {
  font-weight: bold;
  margin-right: 10px;
}

.el-descriptions {
  margin-top: 20px;
}
.cell-item {
  display: flex;
  align-items: center;
}
.margin-top {
  margin-top: 20px;
}
</style>

用户个人信息展示界面
在这里插入图片描述

5. 教师功能界面

主要包括:发布新课程、查看开课的选课情况、教师查看自己开设的课程功能

教师查看自己开设课程界面

<template>
  <el-table :data="myCourses" stripe style="width: 100%">
    <el-table-column label="课程ID" prop="courseId" />
    <el-table-column label="课程名称">
      <template #default="{ row }">
        <router-link :to="{ name: 'courseDetail', params: { courseName: row.courseName }}">
          {{ row.courseName }}
        </router-link>
      </template>
    </el-table-column>
    <el-table-column label="课程类型" prop="courseType" />
    <el-table-column label="先修要求" prop="prerequisites" />
    <el-table-column label="课程简介" prop="courseDescription">
      <template #default="{ row }">
        <div class="description-cell">{{ row.courseDescription }}</div>
      </template>
    </el-table-column>
    <el-table-column label="操作" width="150">
      <template #default="{ row }">
        <el-button size="small" @click="handlePredict(row.courseName)">课程热度预测</el-button>
        <br>
        <el-button size="small" @click="handleDownload(row.courseName)">生成教学ppt</el-button>
      </template>
    </el-table-column>
  </el-table>
  <br>
  <el-button type="primary" @click="drawer = true">去开课</el-button>
  <div v-if="predictUrl">
    <p>课程热度预测图:</p>
    <img :src=predictUrl style="height: 300px; width: 400px"/>
  </div>

  <el-dialog v-model="drawer" size = '100%' direction='btt'
             :before-close="handleClose">

    <div
        title="发布新课程"
        width="600px"
        @close="handleClose"
    >
      <el-form
          :model="newCourseInfo"
          :rules="rules"
          :label-position="labelPosition"
          label-width="auto"
          ref="newCourseFormRef"
      >
        <el-form-item label="课程ID" prop="courseId">
          <el-input v-model="newCourseInfo.courseId" />
        </el-form-item>
        <el-form-item label="课程名" prop="courseName">
          <el-input v-model="newCourseInfo.courseName" />
        </el-form-item>
        <el-form-item label="课程类型" prop="courseType">
          <el-input v-model="newCourseInfo.courseType" />
        </el-form-item>
        <el-form-item label="先修课程" prop="prerequisites">
          <el-input v-model="newCourseInfo.prerequisites" />
        </el-form-item>
        <el-form-item label="课程描述" prop="courseDescription">
          <el-input v-model="newCourseInfo.courseDescription" />
        </el-form-item>
        <el-form-item>
      <span slot="footer" class="dialog-footer">
        <el-button @click="drawer = false">取 消</el-button>
        <el-button type="primary" @click="teaReleaseCourse">确 定</el-button>
      </span>
        </el-form-item>
      </el-form>
    </div>
  </el-dialog>
</template>

<script>
import {ref, onMounted, reactive} from 'vue';
import api from '../../api';
import {ElMessageBox} from "element-plus";
import {toast} from "@/utils/popup";
import axios from "axios";
import path  from "@/api/path"

export default {
  name: "MyCourses",
  setup() {
    const myCourses = ref([]);

    const fetchCourses = () => {
      api.teaGetCourses().then(res => {
        myCourses.value = res.data.data;
      });
    };

    onMounted(fetchCourses);

    const drawer = ref(false)

    const handleClose = (done) => {
      ElMessageBox.confirm('确定要取消开课?')
          .then(() => {
            done()
            userInfoFormRef.value.resetFields(); // 清空表单字段
          })
          .catch(() => {
            // catch error
          })
    }

    const newCourseInfo = reactive({
      courseId: '',
      courseName: '',
      courseType: '',
      prerequisites: '',
      courseDescription: '',
    })
    const rules = {
      courseId: [
        {required: true, message: "课程ID不能为空", trigger: "blur"},
      ],
      courseName: [
        {required: true, message: "课程名不能为空", trigger: "blur"},
        {min: 2, max: 20, message: "课程名长度必须是2-20个字符", trigger: "blur"},
      ],
      courseType: [
        {required: true, message: "课程类型不能为空", trigger: "blur"},
      ],
      prerequisites: [
        {required: true, message: "先修课程描述不能为空", trigger: "blur"},
      ],
      courseDescription: [
        {required: true, message: "课程描述不能为空", trigger: "blur"},
      ],
    };

    const newCourseFormRef = ref(null);

    function teaReleaseCourse() {
      newCourseFormRef.value.validate((valid) => {
        if (valid) {
          drawer.value = false
          api.teaReleaseCourse(newCourseInfo).then(res => {
            if (res.data.code === 200) {
              toast("课程已发布成功,快让学生来选课吧!", 'success')
              fetchCourses()
            } else {
              toast(res.data.msg, 'error')
            }
          })
        } else {
          toast("表单有误", "error");
        }
      })
    }

    function handleDownload(courseName) {
      generatePPT(courseName).then(downloadUrl => {
        if (downloadUrl) {
          toast("生成ppt完成!", "success")
          const link = document.createElement('a');
          link.href = downloadUrl;
          link.download = courseName + `教学.pptx`; // 假设你想以课程名为文件名
          document.body.appendChild(link);
          link.click();
          document.body.removeChild(link);
        } else {
          toast("下载链接无效!", "error")
        }
      }).catch(error => {
        toast("生成ppt失败!", "error")
      });
    }

    async function generatePPT(courseName) {
      toast("生成ppt中,请稍后...", "success")
      try {
        const response = await axios.post(path.remoteBaseUrl+"/ppt", {
        // const response = await axios.post("http://127.0.0.1:5007/ppt", {
          pptName: "请根据以下内容生成ppt,要求ppt样式美观,展现方式多样,底色避免高对比度高饱和度的颜色:" + courseName
        }, {
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
          },
        });
        return response.data.download_url; // 确保此响应包含正确的下载链接
      } catch (error) {
        toast("生成ppt失败!", "error")
      }
    }

    const predictUrl = ref(null)
    function handlePredict(courseName){
      toast("生成预测图中,请稍后...", "warning")
      axios.post(path.remoteBaseUrl+"/transformer", {
        "will_train": false,
        "add_new": { "date": "2015/12/17", "open": Math.floor(Math.random() * (4000 - 2000 + 1)) + 3000
        },
        "mode": 0,
        "courseName": courseName,
        "userName": "666"
      }, {
        headers: {
          'Content-Type': 'application/json'
        },
      }).then(res => {
        predictUrl.value = res.data[1].url
        toast("生成预测图完成!", "success")
      })
    }

    return {
      myCourses,
      drawer,
      handleClose,
      newCourseInfo,
      rules,
      newCourseFormRef,
      teaReleaseCourse,
      handleDownload,
      handlePredict,
      predictUrl,

    };
  }
};
</script>

<style scoped>
.description-cell {
  max-height: 80px;
  overflow: auto;
}
</style>

界面展示
在这里插入图片描述

查看开课的选课情况

<template>

  <el-table :data="myCourses.value" :border="false" stripe style="width: 100%" id="my-table">
    <el-table-column type="expand">
      <template #default="props">
        <div m="4">
          <el-table :data="props.row.studentStudyInfoList" :border="true">
            <el-table-column label="选课学生姓名" prop="studentName" />
            <el-table-column label="学生活跃天数" prop="studyDay" />
            <el-table-column label="学生视频观看数量" prop="todayStudyTime" />
            <el-table-column label="学生章节完成数" prop="studyTimeSum" />
            <el-table-column label="学生预估分数" prop="score" />
            <el-table-column label="操作" width="100">
              <template #default="innerProps">
                <el-button type="text" @click="predictStudyStatue(innerProps.row.studentName, props.row.courseName)">预测学习状态</el-button>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </template>
    </el-table-column>
    <el-table-column label="开课课程名" prop="courseName" />
    <el-table-column label="选修学生人数" prop="studentNum" />
  </el-table>

  <el-button type="primary" round @click="exportClick ">导出表格</el-button>
  <div v-if="predictUrl">
    <p>学生学习状态预测图:</p>
    <img :src="predictUrl" style="width: 400px; height: 300px"/>
  </div>
</template>

<script>
import api from '../../api'
import {ref, onMounted, reactive} from 'vue'
import {mapActions} from 'vuex';
import store from "@/store";
import { toast } from "@/utils/popup";
import FileSaver from 'file-saver'
import * as XLSX from 'xlsx';
import AutoScoring from '@/components/AutoScoring'
import path  from "@/api/path"
import axios from "axios";


export default {
  name: "chosenCourses",
  components: {
    AutoScoring
  },
  methods: {
    ...mapActions(['logout']),
  },

  setup() {
    onMounted(() => {
      teaGetCourseStudyInfo()
    })

    const myCourses = reactive([{}])

    function teaGetCourseStudyInfo() {
      api.teaGetCourseStudyInfo().then(res => {
        if (res.data.code === 200) {
          myCourses.value = res.data.data
        } else {
          toast(res.data.msg, 'error')
        }
      })
    }

    const exportClick = () => {
      // 首先,我们需要构建一个新的数据结构,其中包含主表格和子表格的数据
      // 合并数据,将子表格数据转换为JSON字符串
      // 创建一个新的数据结构,包含主表格和子表格的数据
      const mergedData = [];

      myCourses.value.forEach((course) => {
        course.studentStudyInfoList.forEach((student) => {
          const newRow = {
            courseName: course.courseName,
            teacherName: store.state.user.username,
            studentName: student.studentName,
            todayStudyTime: student.studyDay,
            studyDay: student.todayStudyTime,
            studyTimeSum: student.studyTimeSum,
            score: student.score,
          };
          mergedData.push(newRow);
        });
      });

      // 创建一个新的HTML表格
      const tableElement = document.createElement('table');
      const thead = document.createElement('thead');
      const tbody = document.createElement('tbody');

      // 创建表头
      const headerRow = document.createElement('tr');
      ['开课课程名', '开课教师名', '选修学生姓名', '学生活跃天数', '学生视频观看数量', '学生章节完成数', '学生预估分数'].forEach((header) => {
        const th = document.createElement('th');
        th.textContent = header;
        headerRow.appendChild(th);
      });
      thead.appendChild(headerRow);

      // 创建表格行
      mergedData.forEach((row) => {
        const tr = document.createElement('tr');
        Object.keys(row).forEach((key) => {
          const td = document.createElement('td');
          td.textContent = row[key];
          tr.appendChild(td);
        });
        tbody.appendChild(tr);
      });

      tableElement.appendChild(thead);
      tableElement.appendChild(tbody);

      // 将新的表格转换为工作簿
      const wb = XLSX.utils.table_to_book(tableElement);

      /* get binary string as output */
      const wbout = XLSX.write(wb, {
        bookType: 'xlsx',
        bookSST: true,
        type: 'array',
      });

      try {
        FileSaver.saveAs(new Blob([wbout], {
          type: 'application/octet-stream',
        }), store.state.user.username + '-课程数据.xlsx'); // 自定义文件名
      } catch (e) {
        toast('导出失败'+ e, 'error')
      }
    };
    const drawer = ref(false)

    const newCourseInfo = reactive({
      courseId: '',
      courseName: '',
      courseType: '',
      prerequisites: '',
      courseDescription: '',
    })

    const predictUrl = ref(null)
    function predictStudyStatue(userName, courseName){
      toast("生成预测图中,请稍后...", "warning")
      axios.post(path.remoteBaseUrl+"/transformer", {
        "will_train": false,
        "add_new": { "date": "2015/12/17", "open": Math.floor(Math.random() * (4000 - 2000 + 1)) + 3000},
        "mode": 1,
        "courseName": courseName,
        "userName": userName
      }, {
        headers: {
          'Content-Type': 'application/json'
        }
      }).then(response => {
        predictUrl.value = response.data[1].url
        toast("生成预测图完成!", "success")
      })
    }

    return {
      teaGetCourseStudyInfo,
      myCourses,
      exportClick,
      drawer,
      newCourseInfo,
      predictStudyStatue,
      predictUrl,
    }
  }
}
</script>

<style scoped>

</style>

界面展示
在这里插入图片描述

6. 学生功能界面

主要包括:选课、退选、获取自己的选课信息

我的选课界面

<template>
  <el-card style="width: 100%">
    <el-table :data="myCourses.value" stripe style="width: 100%">
      <el-table-column label="课程名称" prop="courseName" />
      <el-table-column label="授课老师/单位" prop="teacherName" />
      <el-table-column label="活跃天数" prop="studyDay" />
      <el-table-column label="视频观看数量" prop="todayStudyTime" />
      <el-table-column label="章节完成数" prop="studyTimeSum" />
      <el-table-column label="预估学习得分" prop="score">
        <template #default="scope">
          <el-tag v-if="scope.row.score >= 90" size="small" type="success">
            <span>{{ scope.row.score }},很好!继续保持!</span>
          </el-tag>
          <el-tag v-else-if="scope.row.score < 70" size="small" type="warning">
            <span>{{ scope.row.score }},还需要加把劲哦!</span>
          </el-tag>
          <el-tag v-else size="small">
            <span>{{ scope.row.score }},再努力一把会更棒!</span>
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column align="right">
        <template #default="scope">
          <el-button
              size="small"
              @click="$router.push('/courseVideo/'+scope.row.courseName)"
          >
            去学习
          </el-button>
          <el-button
              size="small"
              type="danger"
              @click="stuDeleteCourse(scope.row.courseName)"
          >
            退选
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <el-button class="mt-4" style="width: 100%" @click="$router.push('/courseCenter')">
      去选课
    </el-button>
  </el-card>
</template>

<script>
import api from '../../api'
import {ref, onMounted, reactive} from 'vue'
import {ElMessageBox} from "element-plus";
import { toast, showModal } from "@/utils/popup";
import courseVideo from "@/components/courseVideo";

export default {
  name: "studentCourse",
  components: { courseVideo },

  data() {
    return {
      dialogVisible: true, // 控制对话框的显示与隐藏
      labelPosition: 'right',
    };
  },

  setup() {
    onMounted(() => {
      getCourseStudyInfo()
    })

    // 加载课程数据
    const userInfo = () => {
      api.getUserInfo()
          .then((res) => {
          });
    };

    const handleClose = (done) => {
      ElMessageBox.confirm('确定要取消更改?')
          .then(() => {
            done()
            userInfoFormRef.value.resetFields(); // 清空表单字段
          })
          .catch(() => {
            toast('出错了', 'error')
          })
    }

    const myCourses = reactive([{}])

    function getCourseStudyInfo() {
      api.getCourseStudyInfo()
          .then((res) => {
            myCourses.value = res.data.data
          })
    }

    async function stuDeleteCourse(courseName) {
      try {
        const shouldDelete = await showModal('确定删除该课程吗?', '提示', 'warning')
        if (shouldDelete) {
          api.stuDeleteCourse(courseName)
              .then((res) => {
                getCourseStudyInfo()
              })
          toast('退选课程成功', 'success')
        }
      } catch (err) {
      }
    }


    return {
      handleClose,
      myCourses,
      stuDeleteCourse,
    }
  }
}
</script>

<style scoped>

</style>

界面展示
在这里插入图片描述

7. 课程功能界面

主要包括:课程中心分类分页展示、课程详情页

课程中心界面

<template>
  <el-card style="width: 100%">
    <el-table :data="myCourses.value" stripe style="width: 100%">
      <el-table-column label="课程名称" prop="courseName" />
      <el-table-column label="授课老师/单位" prop="teacherName" />
      <el-table-column label="活跃天数" prop="studyDay" />
      <el-table-column label="视频观看数量" prop="todayStudyTime" />
      <el-table-column label="章节完成数" prop="studyTimeSum" />
      <el-table-column label="预估学习得分" prop="score">
        <template #default="scope">
          <el-tag v-if="scope.row.score >= 90" size="small" type="success">
            <span>{{ scope.row.score }},很好!继续保持!</span>
          </el-tag>
          <el-tag v-else-if="scope.row.score < 70" size="small" type="warning">
            <span>{{ scope.row.score }},还需要加把劲哦!</span>
          </el-tag>
          <el-tag v-else size="small">
            <span>{{ scope.row.score }},再努力一把会更棒!</span>
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column align="right">
        <template #default="scope">
          <el-button
              size="small"
              @click="$router.push('/courseVideo/'+scope.row.courseName)"
          >
            去学习
          </el-button>
          <el-button
              size="small"
              type="danger"
              @click="stuDeleteCourse(scope.row.courseName)"
          >
            退选
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <el-button class="mt-4" style="width: 100%" @click="$router.push('/courseCenter')">
      去选课
    </el-button>
  </el-card>
</template>

<script>
import api from '../../api'
import {ref, onMounted, reactive} from 'vue'
import {ElMessageBox} from "element-plus";
import { toast, showModal } from "@/utils/popup";
import courseVideo from "@/components/courseVideo";

export default {
  name: "studentCourse",
  components: { courseVideo },

  data() {
    return {
      dialogVisible: true, // 控制对话框的显示与隐藏
      labelPosition: 'right',
    };
  },

  setup() {
    onMounted(() => {
      getCourseStudyInfo()
    })

    // 加载课程数据
    const userInfo = () => {
      api.getUserInfo()
          .then((res) => {
          });
    };

    const handleClose = (done) => {
      ElMessageBox.confirm('确定要取消更改?')
          .then(() => {
            done()
            userInfoFormRef.value.resetFields(); // 清空表单字段
          })
          .catch(() => {
            toast('出错了', 'error')
          })
    }

    const myCourses = reactive([{}])

    function getCourseStudyInfo() {
      api.getCourseStudyInfo()
          .then((res) => {
            myCourses.value = res.data.data
          })
    }

    async function stuDeleteCourse(courseName) {
      try {
        const shouldDelete = await showModal('确定删除该课程吗?', '提示', 'warning')
        if (shouldDelete) {
          api.stuDeleteCourse(courseName)
              .then((res) => {
                getCourseStudyInfo()
              })
          toast('退选课程成功', 'success')
        }
      } catch (err) {
      }
    }


    return {
      handleClose,
      myCourses,
      stuDeleteCourse,
    }
  }
}
</script>

<style scoped>

</style>

界面展示
在这里插入图片描述

课程详情页面

<template>
  <div class="container" style="background-color: aliceblue;">

    <!-- 图片需要更换 -->
    <img class="mb-5" src="../../../src/assets/course.jpg" alt="">
    <div class="containeriner">

      <p style="font-weight: bold; font-size: 2.2em;font-family: 隶书;" class="mb-4">{{ courseName }}</p>
      <p class="mr-4" style="font-family: 楷体; font-size: 2rex;">课程id:{{ courseId }}</p>
      <p class="mr-4" style="font-family: 楷体; font-size: 2rex;">课程类型:{{ courseType }}</p>
      <p class="mr-4" style="font-family: 楷体; font-size: 2rex;">授课教师/机构:{{ courseTeacherName }}</p>
    </div>


    <div class="flex items-center justify-center text-gray-400 mt-3">
      <span class="h-[0.1rem] w-50 bg-gray-400"></span>
      <span style="font-family: 隶书; font-size: 3rex;">先修要求</span>
      <span class="h-[0.1rem] w-50 bg-gray-400"></span>
    </div>
    <p style="text-align: center;font-family: 楷体; font-size: 2rex;" class="mr-30 ml-30">{{ coursePrerequisites }}</p>
    <div class="flex items-center justify-center text-gray-400 mt-3">
      <span class="h-[0.1rem] w-55 bg-gray-400"></span>
      <span style="font-family: 隶书; font-size: 3rex;">课程简介</span>
      <span class="h-[0.1rem] w-55 bg-gray-400"></span>
    </div>
    <p style="text-align: center;font-family: 楷体; font-size: 2rex; margin-left: 180px; margin-right: 180px" class="mr-30 ml-30 mb-5">{{ courseDescription }}</p>
    <div class="contanerbtn" style="margin-bottom: 2%;">
      <el-button v-if="$store.state.user.role == 2" round color="#6495ed"
                 class="w-[250px] text-white font-light hover:(bg-[#1343db] text-white) focus:(ring-8  font-semibold)"
                 type="primary" @click="handleDownload">生成课程教学PPT
      </el-button>
      <el-button v-else
                 round color="#6495ed"
                 class="w-[250px] text-white font-light hover:(bg-[#1343db] text-white) focus:(ring-8  font-semibold)"
                 type="primary" @click="selectCourse">加入我的选课
      </el-button>
      <el-button round color="#6495ed"
                 class="w-[250px] text-white font-light hover:(bg-[#1343db] text-white) focus:(ring-8  font-semibold)"
                 type="primary" @click="goBack">返回</el-button>
    </div>
    <XFBigModel :message="message"></XFBigModel>
    <div class="bestsellers-container">
      <chart-word-cloud :key="chartUpdateKey" :series="chartOptions"></chart-word-cloud>
    </div>
  </div>
</template>

<script>
import 'echarts-wordcloud';
import ChartWordCloud from '../../components/ChartWordCloud.vue'
import { useRoute, useRouter } from 'vue-router';
import { computed, onMounted, ref, reactive, nextTick } from "vue";
import api from "../../api"
import {toast} from "../../utils/popup";
import axios from 'axios';
import path  from "@/api/path"
import XFBigModel from "@/components/XFBigModel";

export default {
  name: "courseDetail",
  components: {
    ChartWordCloud,
    XFBigModel
  },

  setup() {
    const route = useRoute(); // 获取当前路由信息
    const router = useRouter();

    const courseId = ref();
    const courseDescription = ref();
    const courseType = ref();
    const coursePrerequisites = ref();
    const courseTeacherName = ref();
    const chartOptions = reactive({
      series: [
        {
          gridSize: 20,
          data: [
            {
              name: '词云生成中',
              value: 30,
              textStyle: {
                color: '#000', // 字体颜色设置为黑色
              },
            },
            { name: '词云生成中', value: 30 },
            { name: '词云生成中', value: 28 },
            { name: '词云生成中', value: 28 },
            { name: '词云生成中', value: 25 },
            { name: "词云生成中", value: 23 },
            { name: '词云生成中', value: 20 },
            { name: '词云生成中', value: 18 },
            { name: '词云生成中', value: 15 },
            { name: '词云生成中', value: 10 },
          ],
        },
      ],
    })
    const message = ref('');
    onMounted(() => {
      fetchData()
    })
    async function fetchData() {
      try {
        const response = await axios.post(
            path.remoteBaseUrl + '/process_text',
            {text: "我需要你为我介绍这门课的核心内容,要求100个字且不分段:"+courseName.value},
            {headers: {'Content-Type': 'application/json'}}
        );
        message.value = response.data.result + "有疑问就来问我吧!"; // 正确设置message的值
      } catch (error) {
        // 可能需要处理错误情况,比如设置默认值或提示用户
        toast("获取数据失败,请稍后再试", "error")
      }
    }

    onMounted(() => {
      getCourseDetail()  //获取课程详细信息
      axios.post(path.remoteBaseUrl + "/word_cloud", {
        text: courseName.value
      }, {
        headers: {
          'Content-Type': 'application/json'
        }
      }).then(res => {
        chartOptions.series[0].data[0].name = res.data.ls_keyword[0];
        chartOptions.series[0].data[1].name = res.data.ls_keyword[1];
        chartOptions.series[0].data[2].name = res.data.ls_keyword[2];
        chartOptions.series[0].data[3].name = res.data.ls_keyword[3];
        chartOptions.series[0].data[4].name = res.data.ls_keyword[4];
        chartOptions.series[0].data[5].name = res.data.ls_keyword[5];
        chartOptions.series[0].data[6].name = res.data.ls_keyword[6];
        chartOptions.series[0].data[7].name = res.data.ls_keyword[7];
        chartOptions.series[0].data[8].name = res.data.ls_keyword[8];
        chartOptions.series[0].data[9].name = res.data.ls_keyword[9];
        chartUpdateKey.value = Date.now(); // 或者使用任何能表示数据已更新的变量
      })
    })

    const courseName = computed(() => route.params.courseName); // 使用computed属性获取courseName参数
    const chartUpdateKey = ref(0);

    function getCourseDetail() {
      api.getCourseDetail(courseName.value).then(res => {
        courseId.value = res.data.data.courseId;
        courseDescription.value = res.data.data.courseDescription;
        courseType.value = res.data.data.courseType;
        coursePrerequisites.value = res.data.data.prerequisites;
        courseTeacherName.value = res.data.data.teacherName;
        chartUpdateKey.value = Date.now(); // 或者使用任何能表示数据已更新的变量
      })
    };

    const goBack = () => {
      router.back();
    };

    const _nevents = ref(null);
    const _ndays_act = ref(null);
    const _nplay_video = ref(null);
    const _nchapters = ref(null);
    function selectCourse() {
      api.selectCourse(courseName.value).then(res => {
        if (res.data.data == null) {
          toast(res.data.msg, "error")
          return
        } else {
          toast("选课成功", "success")
        }
        _nevents.value = res.data.data.studyTimeSum;
        _ndays_act.value = res.data.data.studyDay;
        _nplay_video.value = res.data.data.studyTimeSum;
        _nchapters.value = res.data.data.todayStudyTime;
        predictScore(_nevents.value, _ndays_act.value, _nplay_video.value, _nchapters.value)
      })
    }
    const myScore = ref(null);
    function updateStudentPredictScore(score){
      myScore.value = Math.round(score * 100) + 50
      if (myScore.value > 100){
        myScore.value = 99
      }
      api.updateStudentPredictScore(courseName.value, myScore.value).then(res => {
      })
    }

    function predictScore(_nevents, _ndays_act, _nplay_video, _nchapters) {
      axios.post(path.remoteBaseUrl + "/score", {
        nevents: _nevents,
        ndays_act: _ndays_act,
        nplay_video: _nplay_video,
        nchapters: _nchapters
      }, {
        headers: {
          'Content-Type': 'application/json'
        },
      }).then(res=>{
        updateStudentPredictScore(res.data.score)
      }).catch((error) => {
        toast("获取数据失败,请稍后再试", "error")
      });
    }
    function handleDownload() {
      generatePPT().then(downloadUrl => {
        if (downloadUrl) {
          toast("生成ppt完成!", "success")
          const link = document.createElement('a');
          link.href = downloadUrl;
          link.download = `${courseName.value}》教学.pptx`; // 假设你想以课程名为文件名
          document.body.appendChild(link);
          link.click();
          document.body.removeChild(link);
        } else {
          toast("下载链接无效!", "error")
        }
      }).catch(error => {
        toast("生成ppt失败!", "error")
      });
    }

    async function generatePPT() {
        toast("生成ppt中,请稍后...", "success")
      try {
        const response = await axios.post(path.remoteBaseUrl + "/ppt", {
          pptName: courseName.value
        }, {
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
          },
        });
        return response.data.download_url; // 确保此响应包含正确的下载链接
      } catch (error) {
          toast("生成ppt失败!", "error")
      }
    }

    return {
      courseName,
      courseId,
      courseDescription,
      courseType,
      coursePrerequisites,
      courseTeacherName,
      getCourseDetail,
      goBack,
      selectCourse,
      chartOptions,
      chartUpdateKey,
      generatePPT,
      handleDownload,
      message
    };
  }
}
</script>

<style scoped lang="less">
.bestsellers-container {
  height: 18.56rem;
  width: 100%;
  background: white;
  #charts-content {
    /* 需要设置宽高后才会显示 */
    width: 100%;
    height: 100%;
  }
}

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.container>img {
  width: 100%;
  height: 5%;
}

.containeriner {
  display: flex;
  flex-direction: column;
  align-items: center;
  // margin-bottom: 50px;
}

.titlebar {
  display: flex;
  flex-direction: horizontal;
  align-items: center;
  // margin-bottom: 50px;
}

.containerbtn {
  display: flex;
  flex-direction: horizontal;
  align-items: center;
  margin-bottom: 50px;
}
</style>

页面展示
在这里插入图片描述

8. 其他模块

api封装

import path  from "./path"
import axios from "../utils/request";

const api = {
    //-----------------------------用户接口-----------------------------------
    //用户登录
    login(username, password){
        return axios.post(path.baseUrl + path.login, null, {
            params:{
                username: username,
                password: password
            },
            headers: {
                "Content-Type":"application/json;charset=utf-8"
            },
        })
    },

    register(user){
        return axios.post(path.baseUrl + path.register, {
            username: user.username,
            password: user.password,
            email: user.email,
            phone: user.phone,
            gender: user.gender,
            role: user.role
        }
        )
    },

    logout(){
        return axios.get(path.baseUrl + path.logout, {
            params:{
                token: localStorage.getItem("access_token")
            }
        })
    },

    updateUserInfo(userUpdateInfo){
        return axios.post(path.baseUrl + path.updateUserInfo, {
            username: userUpdateInfo.username,
            nickname: userUpdateInfo.nickname,
            email: userUpdateInfo.email,
            phone: userUpdateInfo.phone,
            }
        )
    },


    //查看用户信息
    getUserInfo(){
        return axios.get(path.baseUrl + path.userInfo, {
            params:{
                token: localStorage.getItem("access_token")
            }
        })
    },

    //上传用户头像
    uploadAvatar(formData){
        return axios.post(path.baseUrl + path.uploadAvatar, formData, {
            headers: {
                "Content-Type":"multipart/form-data"
            },
        })
    },

    updateAvatarUrl(avatarUrl){
        return axios.post(path.baseUrl + path.updateAvatarUrl, null, {
            params:{
                avatarUrl: avatarUrl,
                token: localStorage.getItem("access_token")
            }
        })
    },



    //--------------------------课程相关接口------------------------------------
    //获取所有课程
    getCourses(pageInfo, courseType){
        return axios.post(path.baseUrl + path.getAllCourses,{
            pageNum: pageInfo.pageNum,
            pageSize: pageInfo.pageSize},
            {
                params:{
                    courseType: courseType

                }
        })
    },

    //获取所有课程类别
    getAllCourseType(){
        return axios.get(path.baseUrl + path.getAllCourseType)
    },

    //根据课程类别获取课程
    getCourseByType(type, pageInfo){
        return axios.post(path.baseUrl + path.getCourseByType, {
            type: type,
            pageNum: pageInfo.pageNum,
            pageSize: pageInfo.pageSize
        })
    },

    //获取课程详情
    getCourseDetail(courseName){
        return axios.get(path.baseUrl + path.courseDetail, {
            params:{
                courseName: courseName
            }
            })
    },

    //---------------------------学生相关接口------------------------------------
    selectCourse(courseName){
        return axios.get(path.baseUrl + path.selectCourse, {
            params:{
                courseName: courseName,
                token: localStorage.getItem("access_token")
            }
        })
    },
    getCourseStudyInfo(){
        return axios.get(path.baseUrl + path.getCourseStudyInfo, {
            params:{
                token: localStorage.getItem("access_token")
            }
        })
    },
    stuDeleteCourse(courseName){
        return axios.get(path.baseUrl + path.stuDeleteCourse, {
            params:{
                courseName: courseName,
                token: localStorage.getItem("access_token")
            }
        })
    },
    updateStudentPredictScore(courseName, score){
        return axios.get(path.baseUrl + path.updateStudentPredictScore, {
            params:{
                courseName: courseName,
                score: score,
                token: localStorage.getItem("access_token")
            }
        })
    },

    //---------------------------教师相关接口------------------------------------
    teaGetCourses(){
        return axios.get(path.baseUrl + path.teaGetCourses, {
            params:{
                token: localStorage.getItem("access_token")
            }
        })
    },

    teaGetCourseStudyInfo(){
        return axios.get(path.baseUrl + path.teaGetCourseStudyInfo, {
            params:{
                token: localStorage.getItem("access_token")
            }
        })
    },
    teaReleaseCourse(newCourseInfo){
        return axios.post(path.baseUrl + path.teaReleaseCourse, {
            courseId: newCourseInfo.courseId,
            courseName: newCourseInfo.courseName,
            courseType: newCourseInfo.courseType,
            prerequisites: newCourseInfo.prerequisites,
            courseDescription: newCourseInfo.courseDescription,
        }, {
            params:{
                token: localStorage.getItem("access_token")
            }
        })
    }

}

export default api

router设置

import { createRouter, createWebHashHistory } from 'vue-router'

const routes = [
    {
  path: '/',
  name: 'index',
      component: () => import(/* webpackChunkName: "about" */ '../views/index'),
      children: [
        {
          path: '/courseCenter',
          name: 'courseCenter',
          component: () => import(/* webpackChunkName: "about" */ '../views/course/courseCenter'),
          meta: {
            // title: '选课中心'
          }
        },
        {
          path: '/about',
          name: 'about',
          component: () => import(/* webpackChunkName: "about" */ '../components/About'),
          meta: {
            // title: '关于我们'
          }
        },
        {
          path: '/login',
          name: 'login',
          component: () => import(/* webpackChunkName: "about" */ '../components/user/login'),
          meta: {
            title: '用户登录'
          }
        },
        {
          path: '/studentInfo',
          name: 'studentInfo',
          meta: {
            requireAuth: true,  // 添加该字段,表示进入这个路由是需要登录的
            // title: '个人信息'
          },
          component: () => import(/* webpackChunkName: "about" */ '../components/user/userInfo'),

        },
        {
          path: '/studentCourse',
          name: 'studentCourse',
          meta: {
            requireAuth: true,  // 添加该字段,表示进入这个路由是需要登录的
            // title: '我的课程'
          },
          component: () => import(/* webpackChunkName: "about" */ '../views/student/studentCourse')
        }

        ],
      meta: {
          // title: '龙兴启智智教平台'
      }

  },
  {
    path: '/login',
    name: 'login',
    component: () => import(/* webpackChunkName: "about" */ '../components/user/login'),
    meta: {
      title: '用户登录'
    }
  },
  {
    path: '/teacherHome',
    name: 'teacherHome',
    meta: {
      requireAuth: true,  // 添加该字段,表示进入这个路由是需要登录的
      // title: '龙兴启智智教平台'
    },
    children: [
      {
        path: '/teacherInfo',
        name: 'teacherInfo',
        meta: {
          requireAuth: true,  // 添加该字段,表示进入这个路由是需要登录的
          // title: '个人信息'
        },
        component: () => import(/* webpackChunkName: "about" */ '../components/user/userInfo')
      },
      {
        path: '/chosenCourses',
        name: 'chosenCourses',
        component: () => import(/* webpackChunkName: "about" */ '../views/teacher/chosenCourses'),
        meta: {
          // title: '学生选课管理'
        }
      },
      {
        path: '/teacherCourse',
        name: 'teacherCourse',
        component: () => import(/* webpackChunkName: "about" */ '../views/teacher/myCourses'),
        meta: {
          // title: '开课课程管理'
        }

      },
      {
        path: '/autoScoring',
        name: 'autoScoring',
        component: () => import(/* webpackChunkName: "about" */ '../components/AutoScoring2'),
        meta: {
          // title: '自动评分'
        }
      }
        ],
    component: () => import(/* webpackChunkName: "about" */ '@/views/teacher/teacherHome')
  },
  {
    path: '/adminHome',
    name: 'adminHome',
    meta: {
      requireAuth: true,  // 添加该字段,表示进入这个路由是需要登录的
    },
    component: () => import(/* webpackChunkName: "about" */ '@/views/admin/adminHome')
  },
  {
    path: '/courseDetail/:courseName',
    name: 'courseDetail',
    meta: {
      requireAuth: true,  // 添加该字段,表示进入这个路由是需要登录的
      // title: '课程详情'
    },
    component: () => import(/* webpackChunkName: "about" */ '@/views/course/courseDetail'),
    props: true, // 添加这一行,以便将路由参数传递给组件
  },
    {
      path: '/courseVideo/:courseName',
      name: 'courseVideo',
      component: () => import(/* webpackChunkName: "about" */ '../components/courseVideo'),
      meta: {
        // title: '课程视频'
      }
    },

]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

router.beforeEach(async(to) => {
  if (to.meta.title) {
    document.title = to.meta.title
  } else {
    document.title = '青云智汇智教平台' //此处写默认的title
  }
})

export default router

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值