前言
通过exl数据,使用宏将其导出,转换成为sql数据,将整个年级数据转换成为了一张学生表,建立了一张班级表关联学生表。后端采用mybatis-plus主要来开发代码,前端用element-plus来简化和美化页面表单。在写此代码前,我还不太会前端代码的部分,比如说跨域,以及信息的传递。后面了解到axios可以进行跨域配置。
springboot
依赖引入
js
<dependencies>
<!-- Spring Boot Starter 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- MyBatis-Plus 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join-boot-starter</artifactId>
<version>1.5.2</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- Lombok 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
依赖配置以及链接数据库
后端配置数据库来说就比较得心易手了,写的次数也比较多了在资源目录下建立一个.yml格式的配置文件,application.yml
- #应用配置 server: port: 8080 # 服务器端口 servlet: context-path: /api
- #Spring 配置 spring: application: name: school-club-web # 应用名称 datasource: url: jdbc:mysql://localhost:3306/school?useSSL=false&serverTimezone=UTC # 数据库连接URL username: root # 数据库用户名 password: root # 数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver # 数据库驱动 jpa: show-sql: true # 显示SQL语句 hibernate: ddl-auto: update # 数据库表自动更新策略
- #MyBatis-Plus 配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # Mapper文件路径 configuration: map-underscore-to-camel-case: true # 开启驼峰命名映射 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志
- #日志配置 logging: level: root: INFO # 全局日志级别 com.zzh: DEBUG # 项目包日志级别 file: name: logs/school-club-web.log # 日志文件路径 max-size: 10MB # 单个日志文件最大大小 max-history: 7 # 日志文件保留天数
接口
我需要做一个增删改查,分页模糊查询的接口,方便前端调用,我采用的是多模块化开发,所以在common模块里面写了config配置类,在dao模块写了entity实体类,mapper接口。service模块就是service接口以及serviceImpl。web模块用于启动。所以application类和controller类放在了这里,resources配置文件也在这个类。 以下是代码展示
controller
这里我并没有处理全局异常,因为只是一些简单的功能实现
js
@RestController
@RequestMapping("/yjs")
@RequiredArgsConstructor //自动注入 @Autowired
@CrossOrigin(origins = "http://example.com")
public class YjsController {
private final YjsService yjsService;
//查询所有yjs
@PostMapping("/getAllYjs")
public List<Yjs> getAllYjs() {
return yjsService.selectList();
}
//分页模糊查询
@PostMapping("/getAllYjsPageLike")
public IPage<Yjs> getAllYjs(@RequestBody YjsPageDto yjspageDto) {
System.out.println("yjspageDto:"+yjspageDto);
return yjsService.selectPage(yjspageDto.getCurrentPage(), yjspageDto.getPageSize(),yjspageDto.getStudentId(),yjspageDto.getName(),yjspageDto.getGender());
}
//修改
@PostMapping("/updateYjs")
public boolean updateYjs(@RequestBody Yjs yjs) {
System.out.println(yjs);
return yjsService.updateYjs(yjs);
}
//删除
@PostMapping("/deleteYjs")
public boolean deleteYjs(@RequestBody Yjs yjs) {
System.out.println("yjs!!!!!!:" + yjs);
return yjsService.deleteYjs(yjs.getId());
}
//添加
@PostMapping("/createYjs")
@Transactional
public boolean createYjs(@RequestBody Yjs yjs) {
return yjsService.createYjs(yjs);
}
}
service
js
public interface YjsService {
IPage<Yjs> selectPage(int page, int pageSize,int studentId,String name, String gender);
List<Yjs> selectList();
boolean updateYjs(Yjs yjs);
boolean deleteYjs(Integer id);
boolean createYjs(Yjs yjs);
}
serviceimpl
在分页模糊查询那一块,想了许久,最终写出来了, 通过学生的学号,姓名,性别来进行模糊查询,为空则查询全部 还是感叹分页插件的强大,仅仅需要两行代码就能完成分页,当然,还需要配置config,稍后后面会有相关代码。
js
@Service
@RequiredArgsConstructor
public class YjsServiceImpl implements YjsService {
private final YjsMapper yjsMapper;
@Override
public IPage<Yjs> selectPage(int currentPage, int pageSize,int studentId,String name, String gender) {
Page<Yjs> page = new Page<>(currentPage, pageSize);
QueryWrapper<Yjs> queryWrapper = new QueryWrapper<>();
// 根据 studentId 查询
if (studentId != 0) {
queryWrapper.like("student_id", studentId); // 模糊匹配 studentId
}
// 根据 name 进行模糊查询
if (name != null && !name.isEmpty()) {
queryWrapper.like("name", name); // 模糊匹配 name
}
// 根据 gender 进行模糊查询
if (gender != null && !gender.isEmpty()) {
queryWrapper.like("gender", gender); // 模糊匹配 gender
}
return yjsMapper.selectPage(page, queryWrapper);
}
@Override
public List<Yjs> selectList() {
return yjsMapper.selectList(null);
}
@Override
public boolean updateYjs(Yjs yjs) {
return yjsMapper.updateById(yjs)>0;
}
@Override
public boolean deleteYjs(Integer id) {
return yjsMapper.deleteById(id)>0;
}
@Override
public boolean createYjs(Yjs yjs) {
return yjsMapper.insert(yjs)>0;
}
}
mapper
mapper层就比较简单了,因为都继承了BaseMapper<>这个类,是mybatis-plus里面实用的一个类,帮我们写好了与数据库之间的交流语句, 我们只需要实现相应的方法即可。并不需要再去写sql语句了。当然,如果是多表的话,还是需要我们自己去实现,也有进行多表查询的依赖能够帮我们简化就是我们的mybatis-plus-join-boot-starter依赖,在这就不进行讲解了,毕竟还没有用到。
js
@Mapper
public interface YjsMapper extends MPJBaseMapper<Yjs> {
}
config
配置类config里面最主要的当然是跨域配置,没有他我们可不能进行跨域与前端进行交互。 然后就是启动mybatis-plus的分页组件,帮助我们能够将总页数之类的自动进行计算。然后返回给前端。
js
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 允许所有路径
.allowedOrigins("http://localhost:3000") // 允许的前端域名
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的 HTTP 方法
.allowedHeaders("*") // 允许的请求头
.allowCredentials(true) ;// 允许携带凭证(如 cookies)
}
};
}
}
js
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 数据库类型
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
application
最后我们启动他,就大功告成了我们的后端开发
js
@SpringBootApplication
@EntityScan(basePackages = "com.zzh.entity")
public class StudentApplication {
public static void main(String[] args) {
SpringApplication.run(StudentApplication.class, args);
}
}
Vue3页面
跨域配置
target:后面就是后端的域名啦,注意要和后端保持一致,不然就链接不上,页面会404。
js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
server: {
port: 3000,
hmr:true,
proxy:{
'/api': {
target: 'http://localhost:8080/',
changeOrigin: true,
pathRewrite:{
"^api":"/api"
}
}
},
},
})
主页面
由于只有一个表单,所以我就不配置路由了,全部写在App组件即可 主要就是通过element-plus组件里的一些自带的样式表单,来进行代码开发 添加按钮,点击会打开一个表单,填写学生信息,最后提交信息传给后端。 删除按钮会将当前学生信息一整个传给后端,后端接收到了以后会根据id进行删除。编辑学生信息跟删除大差不差,将当前编辑过的学生信息传给后端,后端根据id更新学生信息。查询的话就是输入学号,姓名,性别,不输入默认为null,以及分页的数据页码和每页展示条数。传给后端,后端进行处理,返回筛选后的数据。
js
<template>
<el-container>
<el-main>
<!-- 添加按钮 -->
<el-button type="primary" @click="handleAdd">添加学生</el-button>
<div style="display: flex; justify-content: flex-end; margin-bottom: 20px;">
<el-input v-model="searchStudentId" placeholder="请输入学号" style="width: 200px; margin-right: 10px;" clearable />
<el-input v-model="searchName" placeholder="请输入姓名" style="width: 200px; margin-right: 10px;" clearable />
<el-input v-model="searchGender" placeholder="请输入性别" style="width: 200px; margin-right: 10px;" clearable />
<el-button type="primary" @click="handleSearch">搜索</el-button>
</div>
<!-- 学生列表 -->
<el-table :data="filteredStudentList" style="width: 100%">
<el-table-column v-if="false" type="id" label="id" width="80" />
<el-table-column prop="studentId" label="学号" width="100" />
<el-table-column prop="name" label="姓名" width="120" />
<el-table-column prop="gender" label="性别" width="80" />
<el-table-column prop="usualGrades" label="平时成绩" width="100" />
<el-table-column prop="finalGrades" label="期末成绩" width="100" />
<el-table-column prop="teachingPracticum" label="教学实习" width="120" />
<el-table-column prop="generalComments" label="总体评价" />
<!-- 操作列 -->
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination v-model:current-page="currentPage" :page-size="pageSize" :total="total"
layout="prev, pager, next, jumper" @current-change="handlePageChange" />
</el-main>
</el-container>
<!-- 编辑对话框 -->
<el-dialog v-model="dialogVisible" title="编辑学生信息" width="30%">
<el-form :model="editForm" label-width="80px">
<el-form-item label="学号">
<el-input v-model="editForm.studentId" disabled />
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="editForm.name" />
</el-form-item>
<el-form-item label="性别">
<el-input v-model="editForm.gender" />
</el-form-item>
<el-form-item label="平时成绩">
<el-input v-model="editForm.usualGrades" />
</el-form-item>
<el-form-item label="期末成绩">
<el-input v-model="editForm.finalGrades" />
</el-form-item>
<el-form-item label="教学实习">
<el-input v-model="editForm.teachingPracticum" />
</el-form-item>
<el-form-item label="总体评价">
<el-input v-model="editForm.generalComments" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveEdit">保存</el-button>
</template>
</el-dialog>
<!-- 添加对话框 -->
<el-dialog v-model="addDialogVisible" title="添加学生信息" width="30%">
<el-form :model="addForm" label-width="80px">
<el-form-item label="学号">
<el-input v-model="addForm.studentId" />
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="addForm.name" />
</el-form-item>
<el-form-item label="性别">
<el-input v-model="addForm.gender" />
</el-form-item>
<el-form-item label="平时成绩">
<el-input v-model="addForm.usualGrades" />
</el-form-item>
<el-form-item label="期末成绩">
<el-input v-model="addForm.finalGrades" />
</el-form-item>
<el-form-item label="教学实习">
<el-input v-model="addForm.teachingPracticum" />
</el-form-item>
<el-form-item label="总体评价">
<el-input v-model="addForm.generalComments" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveAdd">保存</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import axios from 'axios';
// 学生列表数据
const studentList = ref([]);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
const searchStudentId = ref('');
const searchName = ref('');
const searchGender = ref('');
// 编辑对话框相关
const dialogVisible = ref(false);
const editForm = ref({
studentId: '',
name: '',
gender: '',
usualGrades: '',
finalGrades: '',
teachingPracticum: '',
generalComments: ''
});
// 添加对话框相关
const addDialogVisible = ref(false);
const addForm = ref({
studentId: '',
name: '',
gender: '',
usualGrades: '',
finalGrades: '',
teachingPracticum: '',
generalComments: ''
});
// 获取分页数据
const fetchPageData = async () => {
// console.log('Fetching page data...', searchStudentId.value, searchName.value, searchGender.value);
try {
const response = await axios.post('http://localhost:8080/api/yjs/getAllYjsPageLike', {
currentPage: currentPage.value,
pageSize: pageSize.value,
studentId: searchStudentId.value,
name: searchName.value,
gender: searchGender.value
}, {
headers: {
'Content-Type': 'application/json' // 设置请求头为 JSON 格式
}
});
studentList.value = response.data.records; // 当前页数据
total.value = response.data.total; // 总记录数
} catch (error) {
console.error('获取分页数据失败:', error);
}
};
// 分页切换
const handlePageChange = (page) => {
currentPage.value = page;
fetchPageData();
};
// 编辑操作
const handleEdit = (row) => {
editForm.value = { ...row }; // 将当前行的数据复制到编辑表单中
dialogVisible.value = true; // 打开编辑对话框
};
// 保存编辑
const saveEdit = async () => {
try {
const response = await axios.post('http://localhost:8080/api/yjs/updateYjs', editForm.value);
console.log('更新学生信息成功:', response);
dialogVisible.value = false; // 关闭对话框
// 重新获取数据
fetchPageData();
} catch (error) {
console.error('更新学生信息失败:', error);
}
};
// 添加操作
const handleAdd = () => {
addForm.value = {
studentId: '',
name: '',
gender: '',
usualGrades: '',
finalGrades: '',
teachingPracticum: '',
generalComments: ''
}; // 清空表单
addDialogVisible.value = true; // 打开添加对话框
};
// 保存添加
const saveAdd = async () => {
try {
const response = await axios.post('http://localhost:8080/api/yjs/createYjs', addForm.value);
console.log('添加学生信息成功:', response);
addDialogVisible.value = false; // 关闭对话框
// 重新获取数据
fetchPageData();
} catch (error) {
console.error('添加学生信息失败:', error);
}
};
// 删除操作
const handleDelete = async (row) => {
try {
await axios.post('http://localhost:8080/api/yjs/deleteYjs', row);
fetchPageData();
} catch (error) {
console.error('删除学生信息失败:', error);
}
};
// 搜索操作
const handleSearch = () => {
// 如果需要后端处理搜索逻辑,可以调用 fetchPageData
fetchPageData();
};
// 模糊查找逻辑
const filteredStudentList = computed(() => {
return studentList.value; // 如果没有搜索条件,返回全部数据
});
// 页面加载时获取第一页数据
onMounted(() => {
fetchPageData();
});
</script>
<style scoped>
.el-main {
padding: 20px;
}
</style>
总结
还是有一部分并没有完成的很好,比如说我建立的表关系,实际上就用到了一个学生表,并没有关联到班级表,我的想法是通过学生表的id来模糊查询班级表的班级名称。因为学号比如是2025042620,那么班级号就是20250426.班主任和辅导员同理。最后还是没来得及实现,下次一定做的更好。最后展示一下成果吧
