SpringBoot+VUE前后端分离项目学习笔记 - 【25 SpringBoot实现1对1、1对多、多对多关联查询】

news2024/9/19 19:43:05
  • 新增课程Course页面,实现学生选课功能、课程教授老师选择等功能
    • 1. 课程与授课老师是一对一关系
      因为course表仅记录了teacherid,而页面需要的是老师的名字
      • select course.*,sys_user.id from
        course left join sys_user
        **on ** course.teacher_id = sys_user.id
    • 2. 老师与教授课程是一对多关系
    • 3. 学生与已选课程是多对多关系
      2与3都需要同时显示在User页面,因此user表除了join学生选课表,还需要join两个course表来分别获取学术已选课程以及老师教授课程的信息,具体见下
      • select sys_user.*, sc.name as stuCourseName, tc.name as teacherCourseName,
        tc.score as teacherScore, sc.score as stuScore
        from sys_user
        left join student_course
        on sys_user.id = student_course.student_id
        left join course sc
        on student_course.course_id = sc.id
        left join course tc
        on sys_user.id = tc.teacher_id

数据库

Menu表

添加页面顺序字段来控制前端路由出现的菜单层级,后端的entity里Menu记得加上该字段

CREATE TABLE `sys_menu` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '名称',
  `path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '路径',
  `icon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '图标',
  `description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '描述',
  `pid` int DEFAULT NULL COMMENT '父级id',
  `page_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '页面路径',
  `sort_num` int DEFAULT NULL COMMENT '顺序',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

课程表 course

CREATE TABLE `course` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '课程名称',
  `score` int DEFAULT NULL COMMENT '学分',
  `times` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '上课时间',
  `state` tinyint(1) DEFAULT NULL COMMENT '是否开课',
  `teacher_id` int DEFAULT NULL COMMENT '授课老师id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

学术选课表 student_course

CREATE TABLE `student_course`  (
  `student_id` int(11) NOT NULL,
  `course_id` int(11) NOT NULL,
  PRIMARY KEY (`student_id`, `course_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;

后台

UserDTO.java

添加ID信息,用于选课表使用

package com.zj.demo.controller.dao;

import com.zj.demo.entity.Menu;
import lombok.Data;

import java.util.List;

/**
 * 接受前端登录请求的参数
 */
@Data
public class UserDTO {
    private Integer id;
    private String username;
    private String password;
    private String nickname;
    private String avatarUrl;
    private String token;
    private String role;
    private List<Menu> menus;

}

CodeGenerator.java

修改表名设置,右键运行,分别生成相应代码
在这里插入图片描述
在这里插入图片描述

RoleEnum.java

通过前端添加老师和学生两种角色
在这里插入图片描述

package com.zj.demo.common;

public enum RoleEnum {
   ROLE_ADMIN, ROLE_STUDENT,ROLE_TEACHER;
}

在前端用户管理处随机分配几个学生和老师
在这里插入图片描述

Course.java

添加老师名称字段

      @TableField(exist = false)
      private String teacher;

CourseController.java

增加获取老师信息的接口以及学生选课接口

    @GetMapping("/page")
    public Result findPage(@RequestParam String name,
                           @RequestParam Integer pageNum,
                           @RequestParam Integer pageSize) {
//        小白写法按照Course_id获取老师名称
//        QueryWrapper<Course> queryWrapper = new QueryWrapper<>();
//        queryWrapper.orderByDesc("id");
//        Page<Course> page = courseService.page(new Page<>(pageNum, pageSize), queryWrapper);
//        List<Course> records = page.getRecords();
//        for (Course record : records) {
//            User user = userService.getById(record.getTeacherId());
//            if(user != null) {
//                record.setTeacher(user.getNickname());
//            }
//
//        }
        Page<Course> page = courseService.findPage(new Page<>(pageNum, pageSize), name);
        return Result.success(page);
    }
    @PostMapping("/studentCourse/{courseId}/{studentId}")
    public Result studentCourse(@PathVariable Integer courseId, @PathVariable Integer studentId) {
        courseService.setStudentCourse(courseId, studentId);
        return Result.success();
    }

ICourseSerivce.java

package com.zj.demo.service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zj.demo.entity.Course;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author Joyce
 * @since 2023-01-18
 */
public interface ICourseService extends IService<Course> {

    Page<Course> findPage(Page<Course> page, String name);

    void setStudentCourse(Integer courseId, Integer studentId);
}


CourseSerivceImpl.java

package com.zj.demo.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zj.demo.entity.Course;
import com.zj.demo.mapper.CourseMapper;
import com.zj.demo.service.ICourseService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Joyce
 * @since 2023-01-18
 */
@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements ICourseService {
    @Resource
    private CourseMapper courseMapper;

    @Override
    public Page<Course> findPage(Page<Course> page, String name) {
        return courseMapper.findPage(page, name);
    }

    @Transactional
    @Override
    public void setStudentCourse(Integer courseId, Integer studentId) {
        courseMapper.deleteStudentCourse(courseId, studentId);
        courseMapper.setStudentCourse(courseId, studentId);
    }

}


CourseMapper.java

package com.zj.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zj.demo.entity.Course;
import org.apache.ibatis.annotations.Param;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author Joyce
 * @since 2023-01-18
 */
public interface CourseMapper extends BaseMapper<Course> {

    Page<Course> findPage(Page<Course> page, @Param("name") String name);

    void deleteStudentCourse(Integer courseId, Integer studentId);

    void setStudentCourse(Integer courseId, Integer studentId);
}


按alt+enter在xml创建方法
在这里插入图片描述

CourseMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zj.demo.mapper.CourseMapper">
    <delete id="deleteStudentCourse">
        delete from student_course where student_id = #{studentId} and course_id = #{courseId}
    </delete>

    <insert id="setStudentCourse">
        insert into student_course(student_id, course_id) values(#{studentId}, #{courseId})
    </insert>

    <select id="findPage" resultType="com.zj.demo.entity.Course">
        select course.*, sys_user.nickname as teacher  from course
        left join sys_user
        on course.teacher_id = sys_user.id
        <where>
            <if test="name != null and name != ''">
                and name like concat('%', #{name},'%')
            </if>
        </where>
    </select>
</mapper>


User.java

增加两个字段分别表示老师教授课程与学术已选课程

      @TableField(exist = false)
      private List<Course> courses;
    
      @TableField(exist = false)
      private List<Course> stuCourses;

UserController.java

1、新增接口,编写对应方法findUsersByRole,返回符合角色类型的用户【用来给课程提供授课老师选项】
2、改写findpage接口,返回用户角色对应的课程信息

	============1=============
    @GetMapping("/role/{role}")
    public Result findUsersByRole(@PathVariable String role) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("role", role);
        List<User> list = userService.list(queryWrapper);
        return Result.success(list);
    }
    ============2=============
        @GetMapping("/page")
    public Result findPage(@RequestParam Integer pageNum,
                               @RequestParam Integer pageSize,
                               @RequestParam(defaultValue ="") String username,
                               @RequestParam(defaultValue ="") String email,
                               @RequestParam(defaultValue ="") String address
                               ) {
            return Result.success(userService.findPage(new Page<>(pageNum, pageSize), username, email, address));
            }
    

IUserService.java

package com.zj.demo.service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zj.demo.controller.dao.UserDTO;
import com.zj.demo.controller.dao.UserPasswordDTO;
import com.zj.demo.entity.User;

import java.util.List;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author Joyce
 * @since 2023-01-04
 */
public interface IUserService extends IService<User> {

    UserDTO login(UserDTO userDTO);

    User register(UserDTO userDTO);

    void updatePassword(UserPasswordDTO userPasswordDTO);

    Page<User> findPage(Page<User> objectPage, String username, String email, String address);
}

UserServiceImpl.java

    @Override
    public Page<User> findPage(Page<User> objectPage, String username, String email, String address) {
        return userMapper.findPage(page, username, email, address);
    }

UserMapper.java

public interface UserMapper extends BaseMapper<User> {
    @Update("update sys_user set password = #{newPassword} where username = #{username} and password = #{password}")
    int updatePassword(UserPasswordDTO userPasswordDTO);

    Page<User> findPage(Page<User> objectPage, String username, String email, String address);
}

UserMapper.xml

具体sql语句在这里,并且通过resultMap将查询结果封装成对象

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zj.demo.mapper.UserMapper">
    <resultMap id="pageUser" type="com.zj.demo.entity.User">
        <result column="id" property="id" />
        <result column="username" property="username" />
        <result column="nickname" property="nickname" />
        <result column="email" property="email" />
        <result column="phone" property="phone" />
        <result column="address" property="address" />
        <result column="create_time" property="createTime" />
        <result column="avatar_url" property="avatarUrl" />
        <result column="role" property="role" />
        <collection property="courses" javaType="java.util.ArrayList" ofType="com.zj.demo.entity.Course">
            <result column="teacherCourseName" property="name" />
            <result column="teacherScore" property="score" />
        </collection>
        <collection property="stuCourses" javaType="java.util.ArrayList" ofType="com.zj.demo.entity.Course">
            <result column="stuCourseName" property="name" />
            <result column="stuScore" property="score" />
        </collection>
    </resultMap>
    <select id="findPage" resultMap="pageUser">
        select sys_user.*, sc.name as stuCourseName, tc.name as teacherCourseName, tc.score as teacherScore,
        sc.score as stuScore from sys_user
        left join student_course
        on sys_user.id = student_course.student_id
        left join course sc
        on student_course.course_id = sc.id
        left join course tc
        on sys_user.id = tc.teacher_id
        <where>
            <if test="username != null and username != ''">
                and sys_user.username like concat('%', #{username} ,'%')
            </if>
            <if test="email != null and email != ''">
                and sys_user.email like concat('%', #{email} ,'%')
            </if>
            <if test="address != null and address != ''">
                and sys_user.address like concat('%', #{address} ,'%')
            </if>
        </where>
    </select>
</mapper>

前端

Menu.vue

<template>
  <div>
    <div style="margin: 10px 0">
      <el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="name"></el-input>
      <el-button class="ml-5" type="primary" @click="load">搜索</el-button>
      <el-button type="warning" @click="reset">重置</el-button>
    </div>

    <div style="margin: 10px 0">
      <el-button type="primary" @click="handleAdd1">新增 <i class="el-icon-circle-plus-outline"></i></el-button>
      <el-popconfirm
          class="ml-5"
          confirm-button-text='确定'
          cancel-button-text='我再想想'
          icon="el-icon-info"
          icon-color="red"
          title="您确定批量删除这些数据吗?"
          @confirm="delBatch"
      >
        <el-button type="danger" slot="reference">批量删除 <i class="el-icon-remove-outline"></i></el-button>
      </el-popconfirm>
    </div>

    <el-table :data="tableData" border stripe :header-cell-class-name="'headerBg'"
              row-key="id" default-expand-all   @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="id" label="ID"></el-table-column>
      <el-table-column prop="name" label="名称"></el-table-column>
      <el-table-column prop="path" label="路径"></el-table-column>
      <el-table-column prop="pagePath" label="页面路径"></el-table-column>
      <el-table-column label="图标" class-name="fontSize16" align="center" label-class-name="fontSize12">
        <template slot-scope="scope">
          <span :class="scope.row.icon" />
        </template>
      </el-table-column>
      <el-table-column prop="description" label="描述"></el-table-column>
      <el-table-column prop="sortNum" label="顺序"></el-table-column>
      <el-table-column label="操作" width="300" align="center">
        <template slot-scope="scope">
          <el-button type="primary" @click="handleAdd(scope.row.id)" v-if="!scope.row.pid && !scope.row.path">新增子菜单 <i class="el-icon-plus"></i></el-button>
          <el-button type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
          <el-popconfirm
              class="ml-5"
              confirm-button-text='确定'
              cancel-button-text='我再想想'
              icon="el-icon-info"
              icon-color="red"
              title="您确定删除吗?"
              @confirm="del(scope.row.id)"
          >
            <el-button type="danger" slot="reference">删除 <i class="el-icon-remove-outline"></i></el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>


    <el-dialog title="菜单信息" :visible.sync="dialogFormVisible" width="30%" >
      <el-form label-width="80px" size="small">
        <el-form-item label="名称">
          <el-input v-model="form.name" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="路径">
          <el-input v-model="form.path" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="页面路径">
          <el-input v-model="form.pagePath" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="图标">
          <el-select clearable v-model="form.icon" placeholder="请选择" style="width: 100%">
            <el-option v-for="item in options" :key="item.name" :label="item.name" :value="item.value">
              <i :class="item.value"/> {{ item.name }}
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="描述">
          <el-input v-model="form.description" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="顺序">
          <el-input v-model="form.sortNum" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "Menu",
  data() {
    return {
      tableData: [],
      total: 0,
      pageNum: 1,
      pageSize: 10,
      name: "",
      path: "",
      icon: "",
      description: "",
      form: {},
      dialogFormVisible: false,
      multipleSelection: [],
      options:[]
    }
  },
  created() {
    this.load()
  },
  methods: {
    load() {
      this.request.get("/menu", {
        params: {
          name: this.name,
        }
      }).then(res => {
        this.tableData = res.data
      })
    },
    save() {
      this.request.post("/menu", this.form).then(res => {
        if (res.code === '200') {
          this.$message.success("保存成功")
          this.dialogFormVisible = false
          this.load()
        } else {
          this.$message.error("保存失败")
        }
      })
    },
    del(id) {
      this.request.delete("/menu/" + id).then(res => {
        if (res.code === '200') {
          this.$message.success("删除成功")
          this.load()
        } else {
          this.$message.error("删除失败")
        }
      })
    },
    delBatch() {
      let ids = this.multipleSelection.map(v => v.id)  // [{}, {}, {}] => [1,2,3]
      this.request.post("/menu/del/batch", ids).then(res => {
        if (res.code === '200') {
          this.$message.success("批量删除成功")
          this.load()
        } else {
          this.$message.error("批量删除失败")
        }
      })
    },
    handleAdd1() {
      this.dialogFormVisible = true
      this.form = {}
    },
    handleAdd(pid) {
      this.dialogFormVisible = true
      this.form = {}
      if(pid)
      {
        this.form.pid = pid
      }

    },
    handleEdit(row) {
      this.form = row
      this.dialogFormVisible = true
      // 请求图标的数据
      this.request.get("/menu/icons").then(res => {
        this.options = res.data
      })
    },
    handleSelectionChange(val) {
      console.log(val)
      this.multipleSelection = val
    },
    reset() {
      this.name = ""
      this.load()
    },
    handleSizeChange(pageSize) {
      console.log(pageSize)
      this.pageSize = pageSize
      this.load()
    },
    handleCurrentChange(pageNum) {
      console.log(pageNum)
      this.pageNum = pageNum
      this.load()
    },
    exp() {
      window.open(`http://${serverIp}:9090/role/export`)
    },
  }
}
</script>

<style>
.headerBg {
  background: #eee!important;
}
.fontSize16{
  font-size: 16px;
}
.fontSize12{
  font-size: 12px;
}
</style>


Course.vue

新增课程页面,其中授课老师的选择涉及到一对一关联查询【因为course表仅记录了teacherid,而页面需要的是老师的名字】

<template>
  <div>
    <div style="margin: 10px 0">
      <el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="name"></el-input>
      <el-button class="ml-5" type="primary" @click="load">搜索</el-button>
      <el-button type="warning" @click="reset">重置</el-button>
    </div>
    <div style="margin: 10px 0">
      <el-button type="primary" @click="handleAdd" v-if="user.role === 'ROLE_ADMIN'">新增 <i class="el-icon-circle-plus-outline"></i></el-button>
      <el-popconfirm
          class="ml-5"
          confirm-button-text='确定'
          cancel-button-text='我再想想'
          icon="el-icon-info"
          icon-color="red"
          title="您确定批量删除这些数据吗?"
          @confirm="delBatch"
      >
        <el-button type="danger" slot="reference">批量删除 <i class="el-icon-remove-outline"></i></el-button>
      </el-popconfirm>

    </div>
    <el-table :data="tableData" border stripe :header-cell-class-name="'headerBg'"
              @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="id" label="ID" width="80"></el-table-column>
      <el-table-column prop="name" label="课程名称"></el-table-column>
      <el-table-column prop="score" label="学分"></el-table-column>
      <el-table-column prop="times" label="课时"></el-table-column>
      <el-table-column prop="teacher" label="授课老师"></el-table-column>
<!--      <el-table-column label="启用">-->
<!--        <template slot-scope="scope">-->
<!--          <el-switch v-model="scope.row.state" active-color="#13ce66" inactive-color="#ccc"-->
<!--                     @change="changeEnable(scope.row)"></el-switch>-->
<!--        </template>-->
<!--      </el-table-column>-->
      <el-table-column label="操作" width="280" align="center">
        <template slot-scope="scope">
          <el-button type="primary" @click="selectCourse(scope.row.id)">选课</el-button>
          <el-button type="success" @click="handleEdit(scope.row)" v-if="user.role === 'ROLE_ADMIN'">编辑 <i class="el-icon-edit"></i></el-button>
          <el-popconfirm
              class="ml-5"
              confirm-button-text='确定'
              cancel-button-text='我再想想'
              icon="el-icon-info"
              icon-color="red"
              title="您确定删除吗?"
              @confirm="del(scope.row.id)"
          >
            <el-button type="danger" slot="reference" v-if="user.role === 'ROLE_ADMIN'">删除 <i class="el-icon-remove-outline"></i></el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>

    <div style="padding: 10px 0">
      <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageNum"
          :page-sizes="[2, 5, 10, 20]"
          :page-size="pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="total">
      </el-pagination>
    </div>

    <el-dialog title="课程信息" :visible.sync="dialogFormVisible" width="30%" >
      <el-form label-width="80px" size="small">
        <el-form-item label="名称">
          <el-input v-model="form.name" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="学分">
          <el-input v-model="form.score" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="课时">
          <el-input v-model="form.times" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="老师">
          <el-select clearable v-model="form.teacherId" placeholder="请选择">
            <el-option v-for="item in teachers" :key="item.id" :label="item.nickname" :value="item.id"></el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </div>
    </el-dialog>

  </div>
</template>

<script>

export default {
  name: "Course",
  data() {
    return {
      form: {},
      tableData: [],
      name: '',
      multipleSelection: [],
      pageNum: 1,
      pageSize: 10,
      total: 0,
      dialogFormVisible: false,
      teachers: [],
      user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
    }
  },
  created() {
    this.load()
  },
  methods: {
    selectCourse(courseId) {
      this.request.post('/course/studentCourse/' + courseId + "/" + this.user.id).then(res => {
        if (res.code === '200') {
          this.$message.success("选课成功")
        } else {
          this.$message.success(res.msg)
        }
      })
    },
    load() {
      this.request.get("/course/page", {
        params: {
          pageNum: this.pageNum,
          pageSize: this.pageSize,
          name: this.name,
        }
      }).then(res => {

        this.tableData = res.data.records
        this.total = res.data.total

      })

      this.request.get("/user/role/ROLE_TEACHER").then(res => {
        this.teachers = res.data
      })
    },
    changeEnable(row) {
      this.request.post("/course/update", row).then(res => {
        if (res.code === '200') {
          this.$message.success("操作成功")
        }
      })
    },
    handleAdd() {
      this.dialogFormVisible = true
      this.form = {}
    },
    handleEdit(row) {
      this.form = JSON.parse(JSON.stringify(row))
      this.dialogFormVisible = true
    },
    del(id) {
      this.request.delete("/course/" + id).then(res => {
        if (res.code === '200') {
          this.$message.success("删除成功")
          this.load()
        } else {
          this.$message.error("删除失败")
        }
      })
    },
    handleSelectionChange(val) {
      console.log(val)
      this.multipleSelection = val
    },
    delBatch() {
      let ids = this.multipleSelection.map(v => v.id)  // [{}, {}, {}] => [1,2,3]
      this.request.post("/course/del/batch", ids).then(res => {
        if (res.code === '200') {
          this.$message.success("批量删除成功")
          this.load()
        } else {
          this.$message.error("批量删除失败")
        }
      })
    },
    save() {
      this.request.post("/course", this.form).then(res => {
        if (res.code === '200') {
          this.$message.success("保存成功")
          this.dialogFormVisible = false
          this.load()
        } else {
          this.$message.error("保存失败")
        }
      })
    },
    reset() {
      this.name = ""
      this.load()
    },
    handleSizeChange(pageSize) {
      console.log(pageSize)
      this.pageSize = pageSize
      this.load()
    },
    handleCurrentChange(pageNum) {
      console.log(pageNum)
      this.pageNum = pageNum
      this.load()
    },
    download(url) {
      window.open(url)
    },
  }
}
</script>

<style scoped>

</style>

User.vue

根据用户角色,显示教授课程和已选课程

<template>
  <div>
    <div style="margin: 10px 0">
      <el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="username"></el-input>
      <el-input style="width: 200px" placeholder="请输入邮箱" suffix-icon="el-icon-message" class="ml-5" v-model="email"></el-input>
      <el-input style="width: 200px" placeholder="请输入地址" suffix-icon="el-icon-position" class="ml-5" v-model="address"></el-input>
      <el-button class="ml-5" type="primary" @click="load">搜索</el-button>
      <el-button type="warning" @click="reset">重置</el-button>
    </div>

    <div style="margin: 10px 0">
      <el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i></el-button>
      <el-popconfirm
          class="ml-5"
          confirm-button-text='确定'
          cancel-button-text='我再想想'
          icon="el-icon-info"
          icon-color="red"
          title="您确定批量删除这些数据吗?"
          @confirm="delBatch"
      >
        <el-button type="danger" slot="reference">批量删除 <i class="el-icon-remove-outline"></i></el-button>
      </el-popconfirm>
      <el-upload :action="'http://' + serverIp + ':9090/user/import'" :show-file-list="false" accept="xlsx" :on-success="handleExcelImportSuccess" style="display: inline-block">
        <el-button type="primary" class="ml-5">导入 <i class="el-icon-bottom"></i></el-button>
      </el-upload>
      <el-button type="primary" @click="exp" class="ml-5">导出 <i class="el-icon-top"></i></el-button>
    </div>

    <el-table :data="tableData" border stripe :header-cell-class-name="'headerBg'"  @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="id" label="ID" width="80"></el-table-column>
      <el-table-column prop="username" label="用户名" width="140"></el-table-column>
      <el-table-column prop="role" label="角色">
        <template slot-scope="scope">
          <el-tag type="primary" v-if="scope.row.role === 'ROLE_ADMIN'">管理员</el-tag>
          <el-tag type="warning" v-if="scope.row.role === 'ROLE_TEACHER'">老师</el-tag>
          <el-tag type="success" v-if="scope.row.role === 'ROLE_STUDENT'">学生</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="nickname" label="昵称" width="120"></el-table-column>
      <el-table-column prop="email" label="邮箱"></el-table-column>
      <el-table-column prop="phone" label="电话"></el-table-column>
      <el-table-column prop="address" label="地址"></el-table-column>
      <el-table-column label="操作"  width="500" align="center">
        <template slot-scope="scope">
          <el-button type="primary" @click="lookCourse(scope.row.courses)" v-if="scope.row.role === 'ROLE_TEACHER'">查看教授课程 <i class="el-icon-document"></i></el-button>
          <el-button type="warning" @click="lookStuCourse(scope.row.stuCourses)" v-if="scope.row.role === 'ROLE_STUDENT'">查看已选课程 <i class="el-icon-document"></i></el-button>
          <el-button type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
          <el-popconfirm
              class="ml-5"
              confirm-button-text='确定'
              cancel-button-text='我再想想'
              icon="el-icon-info"
              icon-color="red"
              title="您确定删除吗?"
              @confirm="del(scope.row.id)"
          >
            <el-button type="danger" slot="reference">删除 <i class="el-icon-remove-outline"></i></el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
    <div style="padding: 10px 0">
      <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageNum"
          :page-sizes="[2, 5, 10, 20]"
          :page-size="pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="total">
      </el-pagination>
    </div>

    <el-dialog title="用户信息" :visible.sync="dialogFormVisible" width="30%" >
      <el-form label-width="80px" size="small">
        <el-form-item label="用户名">
          <el-input v-model="form.username" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="角色">
          <el-select clearable v-model="form.role" placeholder="请选择角色" style="width: 100%">
            <el-option v-for="item in roles" :key="item.name" :label="item.name" :value="item.flag"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="昵称">
          <el-input v-model="form.nickname" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="邮箱">
          <el-input v-model="form.email" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="电话">
          <el-input v-model="form.phone" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="地址">
          <el-input v-model="form.address" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </div>
    </el-dialog>

    <el-dialog title="课程信息" :visible.sync="vis" width="30%" >
      <el-table :data="courses" border stripe>
        <el-table-column prop="name" label="课程名称"></el-table-column>
        <el-table-column prop="score" label="学分"></el-table-column>
      </el-table>
    </el-dialog>

    <el-dialog title="课程信息" :visible.sync="stuVis" width="30%" >
      <el-table :data="stuCourses" border stripe>
        <el-table-column prop="name" label="课程名称"></el-table-column>
        <el-table-column prop="score" label="学分"></el-table-column>
      </el-table>
    </el-dialog>
  </div>
</template>

<script>
import {serverIp} from "../../public/config";

export default {
  name: "User",
  data() {
    return {
      serverIp: serverIp,
      tableData: [],
      total: 0,
      pageNum: 1,
      pageSize: 10,
      username: "",
      email: "",
      address: "",
      form: {},
      dialogFormVisible: false,
      multipleSelection: [],
      roles: [],
      courses: [],
      vis: false,
      stuCourses: [],
      stuVis: false
    }
  },
  created() {
    this.load()
  },
  methods: {
    lookCourse(courses) {
      this.courses = courses
      this.vis = true
    },
    lookStuCourse(stuCourses) {
      this.stuCourses = stuCourses
      this.stuVis = true
    },
    load() {
      this.request.get("/user/page", {
        params: {
          pageNum: this.pageNum,
          pageSize: this.pageSize,
          username: this.username,
          email: this.email,
          address: this.address,
        }
      }).then(res => {

        this.tableData = res.data.records
        this.total = res.data.total

      })

      this.request.get("/role").then(res => {
        this.roles = res.data
      })
    },
    save() {
      this.request.post("/user", this.form).then(res => {
        if (res.code === '200') {
          this.$message.success("保存成功")
          this.dialogFormVisible = false
          this.load()
        } else {
          this.$message.error("保存失败")
        }
      })
    },
    handleAdd() {
      this.dialogFormVisible = true
      this.form = {}
    },
    handleEdit(row) {
      this.form = JSON.parse(JSON.stringify(row))
      this.dialogFormVisible = true
    },
    del(id) {
      this.request.delete("/user/" + id).then(res => {
        if (res.code === '200') {
          this.$message.success("删除成功")
          this.load()
        } else {
          this.$message.error("删除失败")
        }
      })
    },
    handleSelectionChange(val) {
      console.log(val)
      this.multipleSelection = val
    },
    delBatch() {
      let ids = this.multipleSelection.map(v => v.id)  // [{}, {}, {}] => [1,2,3]
      this.request.post("/user/del/batch", ids).then(res => {
        if (res.code === '200') {
          this.$message.success("批量删除成功")
          this.load()
        } else {
          this.$message.error("批量删除失败")
        }
      })
    },
    reset() {
      this.username = ""
      this.email = ""
      this.address = ""
      this.load()
    },
    handleSizeChange(pageSize) {
      console.log(pageSize)
      this.pageSize = pageSize
      this.load()
    },
    handleCurrentChange(pageNum) {
      console.log(pageNum)
      this.pageNum = pageNum
      this.load()
    },
    exp() {
      window.open(`http://${serverIp}:9090/user/export`)
    },
    handleExcelImportSuccess() {
      this.$message.success("导入成功")
      this.load()
    }
  }
}
</script>


<style>
.headerBg {
  background: #eee!important;
}
</style>

页面测试

通过前端页面添加Course路由

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Course页面添加几门课程在这里插入图片描述

Course页面进行学生选课

ttps://img-blog.csdnimg.cn/615a4ef194a84e57ae39010fe987584d.png)

用户页面根据角色显示课程信息

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/172764.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第六章SpringFramework之声明事务

文章目录JdbcTemplate准备工作导入依赖创建jdbc.properties配置Spring的配置文件配置测试类的环境实例声明式事务概念先看看对应的编程式事务声明式事务通过一个案例了解声明式事务前提准备三层架构的构建模拟场景的情况添加事务Spring声明式事务的属性事务注解标识的位置事务属…

手把手教你学51单片机-点亮你的LED

单片机内部资源 Flash——程序存储空间。对于单片机来说 Flash 最大的意义是断电后数据 不丢失。 RAM——数据存储空间。RAM 是单片机的数据存储空间,用来存储程序运行过程中产生的和需要的数据,关电后数据丢失 SFR——特殊功能寄存器。通过对特殊工程寄存器读写操作,可以…

循环语句(循环结构)——“C”

各位CSDN的uu们好呀&#xff0c;我又来啦&#xff0c;今天&#xff0c;小雅兰给大家介绍的是一个知识点&#xff0c;就是循环语句啦&#xff0c;包括while循环、do-while循环、for循环&#xff0c;话不多说&#xff0c;让我们一起进入循环结构的世界吧 首先&#xff0c;我们先来…

利用Python暴力破解邻居家WiFi密码

如觉得博主文章写的不错或对你有所帮助的话&#xff0c;还望大家多多支持呀&#xff01;关注、点赞、收藏、评论。 文章目录一、编写代码二、展示测试结果三、测试四、生成密码本&#xff08;建议自己找一个密码本&#xff09;一、编写代码 在桌面新建一个文件 如果你新建的文…

如何实现everything的http外网访问

Everything是voidtools开发的一款文件搜索工具&#xff0c;官网描述为“基于名称快速定位文件和文件夹。”可以实现快速文件索引、快速搜索、最小资源使用、便于文件分享等功能。 everything部署本地后&#xff0c;可以开启配置Http访问功能&#xff0c;这样在局域网内就可以直…

【自用】Git日常开发教程

因为经常容易忘记指令&#xff08;年纪大了&#xff09;&#xff0c;所以打算记录一下将一堆文件从vscode上传到GitHub仓库 目录软件下载初始化状态过程可能出现的错误其他操作参考资料软件下载 https://gitforwindows.org/ https://code.visualstudio.com/ 初始化状态 过程 …

上海亚商投顾:两市缩量微涨,数字经济概念全线走强

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。市场情绪三大指数今日缩量震荡&#xff0c;黄白二线有所分化&#xff0c;题材概念表现活跃。数字经济概念全线走强&#xff0…

8、MariaDB11数据库安装初始化密码

MariaDB11安装 安装前准备 下载安装包 点我去MariaDB官网下载安装包 查看相关文档 Mariadb Server官方文档 使用zip安装 解压缩zip 将下载到的zip解压缩到想安装的位置。 生成data目录 打开cmd并进入到刚才解压后的bin目录&#xff0c; 执行mysql_install_db.exe程序生…

Flowable进阶学习(二)流程部署的深入解析

文章目录一、流程部署涉及表及其结构1. 部署流程代码示例&#xff1a;2. 流程部署所涉及表&#xff1a;3. 流程部署涉及表的结构、字段解析二、流程部署中数据的存储的过程一、流程部署涉及表及其结构 1. 部署流程代码示例&#xff1a; 设计俩个流程&#xff0c;并压缩成zip包…

项目引入多类数据源依赖,MyBatisPlus 是如何确定使用哪种数据源的?

背景 壬寅年腊月廿八&#xff0c;坚守在工作岗位。看了一下项目的 pom.xml 依赖&#xff0c;发现了好几个数据库连接相关的包&#xff0c;有 commons-dbcp2、c3p0、hikaricp、druid-spring-boot-starter&#xff0c;这可是四种不同的数据库连接池呢&#xff0c;一个项目中引入…

Github访问办法

GitHub GitHub是为开发者提供Git仓库的托管服务。这是一个让开发者与朋友、同事、同学及陌生人共享代码的完美场所。 GitHub除提供Git仓库的托管服务外&#xff0c;还为开发者或团队提供了一系列功能&#xff0c;帮助其高效率、高品质地进行代码编写。 GitHub的创始人之一Chr…

使用GPIO模拟I2C的驱动程序分析

使用GPIO模拟I2C的驱动程序分析 文章目录使用GPIO模拟I2C的驱动程序分析参考资料&#xff1a;一、回顾I2C协议1.1 硬件连接1.3 协议细节二、 使用GPIO模拟I2C的要点三、 驱动程序分析3.1 平台总线设备驱动模型3.2 设备树3.3 驱动程序分析1. I2C-GPIO驱动层次2. 传输函数分析四、…

SQL注入简介与原理

数据来源 本文仅用于信息安全学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 SQL注入 首先从SQL注入存在的代码来看 假如这里的id没有过滤&#xff0c;我们就可以输入…

JDK7-hashmap源码

解决冲突的方法 提高效率 特殊情况扰动算法 当冲突大于一定量时需要扩容 在JDK1.7中&#xff0c;HashMap是由数组单向链表实现的&#xff0c;原理图如下&#xff1a; HashMap基本用法 public static void main(String[] args) {//key-value, 数组存储头指针的引用地址&#xf…

SpringCloud项目日志接入ELK实战

写作背景 前面我对SpringCloud Netflix相关的组件&#xff0c;Eureka、Ribbon、OpenFeign、Hystrix和Zuul都进行了复习&#xff0c;后面随着代码越写越多就想着&#xff0c;要不就慢慢完善这个项目代码&#xff0c;把工作里用到的东西慢慢在这个Demo级别的项目里用起来&#x…

idea高效实用优化技巧

文章目录IDEA介绍配置优化注释模板快捷键硬件升级IDEA介绍 IDEA&#xff0c;全称IntelliJ IDEA&#xff0c;是Java语言的集成开发环境&#xff0c;IntelliJ在业界被公认为最好的java开发工具之一&#xff0c;尤其在智能代码助手、代码自动提示、重构、J2EE支持、Ant、JUnit、C…

gdb使用简介

目录 编译时&#xff0c;加入-g选项 不加-g选项&#xff0c;将不能加断点 加入-g选项&#xff0c;才能加断点 run与start start run break continue 编译时&#xff0c;加入-g选项 不加-g选项&#xff0c;将不能加断点 加入-g选项&#xff0c;才能加断点 run与start …

经典的 海量数据面试题 —— 送你一套僻邪剑谱~

目录 前言 一、哈希切割 题目一 解法一&#xff1a;哈希切割 二、位图的应用 题目一&#xff1a; 解法一&#xff1a;哈希切割 解法二&#xff1a;双位图 解法三&#xff1a;单位图&#xff08;进阶版&#xff09; 题目二 解法一&#xff1a;哈希切割 解法二&#xff…

ChatGPT踩坑(too many signups from the same IP)

ChatGPT踩坑 首先是去OpenAI的官网注册&#xff0c;点击API 进去注册一个账号 然后就会发现注册不成功&#xff0c;继续尝试就会出现下面的情况 后来我发现比较靠谱的方法是&#xff0c;通过google账号注册&#xff08;或许微软账号也行&#xff09;&#xff0c;后面会有一步让…

python经典刷题-关于文件、日期的题

目录标题1&#xff0c;统计目录下所有文件大小-os的使用2&#xff0c;按文件后缀整理文件夹3&#xff0c;递归搜索目录找出最大文件4&#xff0c;计算不同课程的最高分最低分--分组处理5&#xff0c;实现不同文件的关联6&#xff0c;批量txt文件的合并7&#xff0c;统计每个兴趣…