逻辑

核心功能:

管理员可以查看所有专业列表(带分页)
能按专业名称搜索专业
可以新增/编辑/删除专业信息
每个专业必须绑定到一个学院(通过下拉框选择)

数据关系:

专业和学院是父子关系(一个学院有多个专业)
前端显示专业时会同时显示所属学院名称(而不是只显示学院ID)

操作流程:

点”新增” → 填专业名称 + 选择所属学院 → 保存
点”编辑” → 修改专业名称或调整所属学院 → 保存
点”删除” → 确认弹窗 → 真的删了

创建数据库

1
2
3
4
5
6
CREATE TABLE `speciality` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '专业名称',
`college_id` int DEFAULT NULL COMMENT '学院ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='专业信息';

image-20250425210418400

entity—Speciality.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.example.entity;

public class Speciality {
private Integer id;
private String name;
private Integer collegeId;
private String collegeName;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getCollegeId() {
return collegeId;
}

public void setCollegeId(Integer collegeId) {
this.collegeId = collegeId;
}

public String getCollegeName() {
return collegeName;
}

public void setCollegeName(String collegeName) {
this.collegeName = collegeName;
}
}

前端

Manager.vue

1
2
3
4
<el-menu-item index="/speciality" v-if ="data.user.role === 'ADMIN'">
<el-icon><School /></el-icon>
<span>专业信息</span>
</el-menu-item>

index.js

配置路由

1
{ path: 'speciality', component: () => import('@/views/manager/Speciality.vue')},

Speciality.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<template>
<div>

<div class="card" style="margin-bottom: 5px;">
<el-input v-model="data.name" style="width: 300px; margin-right: 10px" placeholder="请输入专业名称查询"></el-input>
<el-button type="primary" @click="load">查询</el-button>
<el-button type="info" style="margin: 0 10px" @click="reset">重置</el-button>
</div>

<div class="card" style="margin-bottom: 5px">
<div style="margin-bottom: 10px">
<el-button type="primary" @click="handleAdd">新增</el-button>
</div>
<el-table :data="data.tableData" stripe>
<el-table-column label="专业名称" prop="name"></el-table-column>
<el-table-column label="所属学院" prop="collegeName"></el-table-column>
<el-table-column label="操作" align="center" width="160">
<template #default="scope">
<el-button type="primary" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>

<div class="card">
<el-pagination background layout="prev, pager, next" v-model:page-size="data.pageSize" v-model:current-page="data.pageNum" :total="data.total"/>
</div>

<el-dialog title="专业信息" width="40%" v-model="data.formVisible" :close-on-click-modal="false" destroy-on-close>
<el-form :model="data.form" label-width="100px" style="padding-right: 50px">
<el-form-item label="专业名称" prop="name">
<el-input v-model="data.form.name" autocomplete="off" />
</el-form-item>
<el-form-item label="所属学院" prop="collegeId">
<!-- 专业信息是关联学院的,所以在新增(编辑)专业信息的时候,需要选择对应的学院,绑定起来。-->
<el-select v-model="data.form.collegeId" placeholder="请选择学院" style="width: 100%">
<el-option v-for="item in data.collegeDate"
:key="item.id"
:label="item.name"
:value="item.id">

</el-option>
<!-- v-model="data.form.collegeId" 绑定专业信息的学院id-->
<!-- placeholder="请选择学院" 提示用户选择学院-->
<!-- style="width: 100%" 设置下拉框的宽度为100%-->
<!-- v-for="item in data.collegeDate" 遍历学院信息-->
<!-- :key="item.id" 设置每个学院的key为id-->
<!-- :label="item.name" 设置每个学院的label为name-->
<!-- :value="item.id" 设置每个学院的value为id-->
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="data.formVisible = false">取 消</el-button>
<el-button type="primary" @click="save">保 存</el-button>
</span>
</template>
</el-dialog>

</div>
</template>

<script setup>
import request from "@/utils/request";
import {reactive} from "vue";
import {ElMessageBox, ElMessage} from "element-plus";


const data = reactive({
pageNum: 1,
pageSize: 10,
total: 0,
formVisible: false,
form: {},
tableData: [],
name: null,
collegeDate: []// 用于存储学院信息
})

// 分页查询
const load = () => {
request.get('/speciality/selectPage', {
params: {
pageNum: data.pageNum,
pageSize: data.pageSize,
name: data.name
}
}).then(res => {
data.tableData = res.data?.list
data.total = res.data?.total
})
}

// 新增
const handleAdd = () => {
data.form = {}
data.formVisible = true
}

// 编辑
const handleEdit = (row) => {
data.form = JSON.parse(JSON.stringify(row))
data.formVisible = true
}

// 新增保存
const add = () => {
request.post('/speciality/add', data.form).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}

// 编辑保存
const update = () => {
request.put('/speciality/update', data.form).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}

// 弹窗保存
const save = () => {
// data.form有id就是更新,没有就是新增
data.form.id ? update() : add()
}

// 删除
const handleDelete = (id) => {
ElMessageBox.confirm('删除后数据无法恢复,您确定删除吗?', '删除确认', { type: 'warning' }).then(res => {
request.delete('/speciality/deleteById/' + id).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
} else {
ElMessage.error(res.msg)
}
})
}).catch(err => {})
}

// 重置
const reset = () => {
data.name = null
load()
}


const loadCollege= () => {//查询所有学院信息
request.get('/college/selectAll').then(res => {//调用后端接口查询所有学院信息
if (res.code === '200') {//如果查询成功,将查询到的学院信息赋值给data.collegeDate
data.collegeDate = res.data
}else {//如果查询失败,提示用户
ElMessage.error(res.msg)
}
})
}

load()
loadCollege()
</script>

Controller—SpecialityController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* 专业信息模块前端操作接口入口
* 拿到数据后,调用service层的方法,返回结果
* 前端调用接口时,需要传入参数,调用service层的方法,返回结果
**/
@RestController

@RequestMapping("/speciality")
public class SpecialityController {
@Resource
// 注入SpecialityService
private SpecialityService specialityService;

/**
* 新增专业信息
* */
@PostMapping("/add")
public Result add(@RequestBody Speciality speciality){
specialityService.add(speciality);
return Result.success();
}

/**
* 更新专业信息
*/
@PutMapping("/update")//更新专业信息信息,前端调用接口时,需要传入参数,调用service层的方法,返回结果
public Result update(@RequestBody Speciality speciality){//接收前端传来的参数
specialityService.updateById(speciality);//调用service层的方法,返回结果
return Result.success();
}

/**
* 删除专业信息
*/
@DeleteMapping("/deleteById/{id}")
public Result deleteById(@PathVariable Integer id) {
specialityService.deleteById(id);
return Result.success();
}

/**
* 分页查询
* */
@GetMapping("selectPage")
public Result selectPage(Speciality speciality,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "5") Integer pageSize){
// 调用service层的方法,返回结果
PageInfo<Speciality> pageInfo = specialityService.selectPage(speciality,pageNum,pageSize);
// 总数
return Result.success(pageInfo);
}
/**
* 查询所有专业信息
*/
@GetMapping("/selectAll")
public Result selectAll(){
List<Speciality> list = specialityService.selectAll();
return Result.success(list);
}
}

SpecialityService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* 专业模块业务逻辑接口
* 要把数据往数据库里存,调用mapper层的方法,返回结果
* 业务逻辑接口调用mapper层的方法,返回结果
*/
@Service
public class SpecialityService {
// 注入SpecialityMapper
@Resource
private SpecialityMapper specialityMapper;
/**
* 新增专业
*/
public void add(Speciality speciality) {
specialityMapper.insert(speciality);
}
/**
* 分页查询
*/
public PageInfo<Speciality> selectPage(Speciality speciality,Integer pageNum, Integer pageSize) {
// ToDo 分页查询逻辑处理
// 1. 分页查询专业信息
// 2. 返回专业信息
// 3. 如果没有专业信息,返回空列表
// 4. 如果有专业信息,返回专业信息列表
List<Speciality> list;//定义一个list集合
PageHelper.startPage(pageNum,pageSize);//分页查询
if(ObjectUtil.isNotEmpty(speciality.getName())){//如果name不为空,就查询name对应的专业信息
list = specialityMapper.selectByName(speciality.getName());
}else{//如果name为空,就查询所有专业信息
list = specialityMapper.selectAll();
}
return PageInfo.of(list);//返回专业信息列表
}

//根据id查询专业信息,返回一个speciality对象
public void updateById(Speciality speciality) {//传入一个speciality对象
specialityMapper.updateById(speciality);//调用mapper层的方法,返回结果
}


//根据id删除专业信息
public void deleteById(Integer id) {
specialityMapper.deleteById(id);
}
//查询所有专业信息
public List<Speciality> selectAll() {
return specialityMapper.selectAll();
}
}

Mapper

SpecialityMapper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Mapper
public interface SpecialityMapper {
//新增
void insert(Speciality speciality);

//查询所有
@Select("select speciality.*, college.name as collegeName from speciality left join college on speciality.college_id = college.id")
//查询所有专业信息,并且把专业信息对应的学院名称也查询出来
//left join college on speciality.college_id = college.id
List<Speciality> selectAll();

//根据name查询
@Select("select speciality.*, college.name as collegeName from speciality " +
"left join college on speciality.college_id = college.id " +
"where speciality.name like concat('%',#{name},'%')")
//查询所有专业信息,并且把专业信息对应的学院名称也查询出来
List<Speciality> selectByName(String name);

//修改
void updateById(Speciality speciality);

//删除
@Delete("delete from speciality where id = #{id}")
void deleteById(Integer id);
}

SpecialityMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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.example.mapper.SpecialityMapper">
<insert id="insert" parameterType="com.example.entity.Speciality" useGeneratedKeys="true">
insert into speciality (name, college_id)
values (#{name}, #{collegeId})
</insert>

<update id="updateById" parameterType="com.example.entity.Speciality">
update speciality
set name = #{name}, college_id = #{collegeId}
where id = #{id}
-- 这里的id是实体类中的属性名,不是数据库中的字段名
</update>
</mapper>

如果后面遇到这种什么字段缺少getter/setter方法,除了去这个实体类里面排查一下之外,记住还有一个地方可能出现问题:xxxMapper.xml

当这个字段是有下划线的时候,要注意:获取它的值的变量要是驼峰。

vue :https://element-plus.org/zh-CN/

当你在页面操作的时候,没有反应,怎么办??怎么去排查??

  1. 按F12打开前台页面控制台,看一下前端有没有报错?
  2. 看一下网络请求,接口有没有调用或者参数有没有传对,如果不对,那在前端检查一下参数的传递;如果对了,那就检查一下后端有没有报错,或者有没有其他问题
  3. 检查后端控制台信息,如果报错了,找到报错的地方,断点看一下;如果没有报错,必须要debug跟一下了,看看每一步的数据对不对。

有个关联查询很重要!!!

ambiguous:模棱两可的