关于我用springboot加vue3写了一个全年级的查询表

前言

通过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.班主任和辅导员同理。最后还是没来得及实现,下次一定做的更好。最后展示一下成果吧

相关推荐
电商api接口开发14 分钟前
ASP.NET MVC 入门指南三
后端·asp.net·mvc
声声codeGrandMaster14 分钟前
django之账号管理功能
数据库·后端·python·django
我的golang之路果然有问题41 分钟前
案例速成GO+redis 个人笔记
经验分享·redis·笔记·后端·学习·golang·go
嘻嘻嘻嘻嘻嘻ys1 小时前
《Vue 3.3响应式革新与TypeScript高效开发实战指南》
前端·后端
暮乘白帝过重山1 小时前
路由逻辑由 Exchange 和 Binding(绑定) 决定” 的含义
开发语言·后端·中间件·路由流程
CHQIUU1 小时前
告别手动映射:在 Spring Boot 3 中优雅集成 MapStruct
spring boot·后端·状态模式
广西千灵通网络科技有限公司1 小时前
基于Django的个性化股票交易管理系统
后端·python·django
CodeFox1 小时前
动态线程池 v1.2.1 版本发布,告警规则重构,bytebuddy 替换 cglib,新增 jmh 基准测试等!
java·后端
tonydf2 小时前
0帧起手本地跑一下BitNet
后端·ai编程
zzmgc42 小时前
常用JVM配置参数
后端