当前位置: 首页 > backend >正文

动态图表 -- eg1

问题:

前端vue,后端springboot,实现动态表格样式,(表格List<Student>,Student类有年级,班级,文理科分类,姓名,学号,等属性。先根据年级分类统计作为第一列,同个年级的合并,再根据文理科分类统计作为第二列,同样分类的合并,最后根据班级分类统计,同个班级的合并行,第四列是学生学号,第五列是学生姓名。第一行是合并5列的学校名称,第二行是合并3个格子的“分班信息”,合并2各格子的“学生信息”。学生信息的那2列要能让前端点击任一那两列的格子时带信息跳转,跳转信息包括(学生学号,年级,班级,文理科分类)。以上信息统一由后端返回给前端。表格列数固定,行数不固定,其中,合并行的合并行数或合并列的合并列数,会根据具体数据动态计算),提供完整的前后端代码

动态表格实现(Vue + SpringBoot)

下面我将提供一个完整的解决方案,包括前端Vue和后端SpringBoot的实现。

后端SpringBoot实现

1. 实体类定义

 

java

// Student.java
public class Student {private String grade;       // 年级private String className;   // 班级private String subjectType; // 文理科分类private String name;        // 姓名private String studentId;   // 学号// 构造方法、getter和setterpublic Student() {}public Student(String grade, String className, String subjectType, String name, String studentId) {this.grade = grade;this.className = className;this.subjectType = subjectType;this.name = name;this.studentId = studentId;}// getter和setter方法// ...
}

2. 表格数据结构

 

java

// TableRow.java
public class TableRow {private String grade;          // 年级private String subjectType;    // 文理科分类private String className;      // 班级private String studentId;      // 学号private String studentName;    // 姓名private int gradeRowSpan;      // 年级合并行数private int subjectRowSpan;    // 文理科合并行数private int classRowSpan;      // 班级合并行数// 构造方法、getter和setter// ...
}

3. 控制器和服务层

 

java

// StudentController.java
@RestController
@RequestMapping("/api/students")
public class StudentController {@Autowiredprivate StudentService studentService;@GetMapping("/table-data")public ResponseEntity<List<TableRow>> getTableData() {List<TableRow> tableData = studentService.generateTableData();return ResponseEntity.ok(tableData);}
}
 

java

// StudentService.java
@Service
public class StudentService {// 模拟数据 - 实际应用中可以从数据库获取private List<Student> mockStudents = Arrays.asList(new Student("高一", "一班", "理科", "张三", "2023001"),new Student("高一", "一班", "理科", "李四", "2023002"),new Student("高一", "二班", "文科", "王五", "2023003"),new Student("高二", "三班", "理科", "赵六", "2023004"),new Student("高二", "三班", "理科", "钱七", "2023005"),new Student("高二", "四班", "文科", "孙八", "2023006"),new Student("高三", "五班", "理科", "周九", "2023007"),new Student("高三", "六班", "文科", "吴十", "2023008"));public List<TableRow> generateTableData() {// 1. 按年级、文理科、班级分组Map<String, Map<String, Map<String, List<Student>>>> groupedData = mockStudents.stream().collect(Collectors.groupingBy(Student::getGrade,Collectors.groupingBy(Student::getSubjectType,Collectors.groupingBy(Student::getClassName))));// 2. 构建表格行数据List<TableRow> tableRows = new ArrayList<>();groupedData.forEach((grade, subjectMap) -> {// 计算年级合并行数int gradeCount = subjectMap.values().stream().mapToInt(classMap -> classMap.size()).sum();subjectMap.forEach((subjectType, classMap) -> {// 计算文理科合并行数int subjectCount = classMap.size();classMap.forEach((className, students) -> {// 每个学生或空行(用于合并)if (students.isEmpty()) {// 空行处理(如果有需要)} else {// 添加学生行students.forEach(student -> {TableRow row = new TableRow();row.setGrade(grade);row.setSubjectType(subjectType);row.setClassName(className);row.setStudentId(student.getStudentId());row.setStudentName(student.getName());// 计算合并行数(只在第一行设置)if (tableRows.isEmpty() || !tableRows.get(tableRows.size() - 1).getGrade().equals(grade)) {row.setGradeRowSpan(countGradeRows(groupedData, grade));}if (tableRows.isEmpty() || !tableRows.get(tableRows.size() - 1).getGrade().equals(grade) || !tableRows.get(tableRows.size() - 1).getSubjectType().equals(subjectType)) {row.setSubjectRowSpan(countSubjectRows(groupedData, grade, subjectType));}if (tableRows.isEmpty() || !tableRows.get(tableRows.size() - 1).getGrade().equals(grade) || !tableRows.get(tableRows.size() - 1).getSubjectType().equals(subjectType) || !tableRows.get(tableRows.size() - 1).getClassName().equals(className)) {row.setClassRowSpan(students.size());}tableRows.add(row);});}});});});return tableRows;}private int countGradeRows(Map<String, Map<String, Map<String, List<Student>>>> groupedData, String grade) {return groupedData.get(grade).values().stream().mapToInt(Map::size).sum();}private int countSubjectRows(Map<String, Map<String, Map<String, List<Student>>>> groupedData, String grade, String subjectType) {return groupedData.get(grade).get(subjectType).size();}
}

前端Vue实现

1. 安装依赖

确保已安装Vue和axios:

bash

npm install axios

2. 表格组件实现

 

vue

<template><div class="table-container"><table class="dynamic-table"><!-- 表头 --><thead><tr><th colspan="5" class="school-header">学校名称</th></tr><tr><th colspan="3" class="info-header">分班信息</th><th colspan="2" class="student-header">学生信息</th></tr><tr><th>年级</th><th>文理科</th><th>班级</th><th>学号</th><th>姓名</th></tr></thead><!-- 表格内容 --><tbody><template v-for="(row, index) in tableData" :key="index"><tr><!-- 年级列 --><td v-if="shouldShowGrade(index)" :rowspan="row.gradeRowSpan">{{ row.grade }}</td><!-- 文理科列 --><td v-if="shouldShowSubject(index)" :rowspan="row.subjectRowSpan">{{ row.subjectType }}</td><!-- 班级列 --><td v-if="shouldShowClass(index)" :rowspan="row.classRowSpan">{{ row.className }}</td><!-- 学号列 --><td @click="handleStudentClick(row)">{{ row.studentId }}</td><!-- 姓名列 --><td @click="handleStudentClick(row)">{{ row.studentName }}</td></tr></template></tbody></table><!-- 学生详情弹窗 --><div v-if="showStudentDetail" class="student-detail-modal"><div class="modal-content"><span class="close" @click="showStudentDetail = false">&times;</span><h3>学生详细信息</h3><p>学号: {{ selectedStudent.studentId }}</p><p>年级: {{ selectedStudent.grade }}</p><p>班级: {{ selectedStudent.className }}</p><p>文理科: {{ selectedStudent.subjectType }}</p></div></div></div>
</template><script>
import axios from 'axios';export default {name: 'DynamicTable',data() {return {tableData: [],showStudentDetail: false,selectedStudent: {}};},created() {this.fetchTableData();},methods: {fetchTableData() {axios.get('/api/students/table-data').then(response => {this.tableData = response.data;// 计算合并行数this.calculateRowSpans();}).catch(error => {console.error('获取表格数据失败:', error);});},calculateRowSpans() {// 这里可以在后端已经计算好,前端直接使用// 如果需要在前端计算,可以在这里实现},shouldShowGrade(index) {// 如果是该年级的第一行,显示年级单元格if (index === 0) return true;const currentGrade = this.tableData[index].grade;const prevGrade = this.tableData[index - 1].grade;return currentGrade !== prevGrade;},shouldShowSubject(index) {// 如果是该文理科的第一行,显示文理科单元格if (index === 0) return true;const currentGrade = this.tableData[index].grade;const prevGrade = this.tableData[index - 1].grade;const currentSubject = this.tableData[index].subjectType;const prevSubject = this.tableData[index - 1].subjectType;return currentGrade !== prevGrade || currentSubject !== prevSubject;},shouldShowClass(index) {// 如果是该班级的第一行,显示班级单元格if (index === 0) return true;const currentGrade = this.tableData[index].grade;const prevGrade = this.tableData[index - 1].grade;const currentSubject = this.tableData[index].subjectType;const prevSubject = this.tableData[index - 1].subjectType;const currentClass = this.tableData[index].className;const prevClass = this.tableData[index - 1].className;return currentGrade !== prevGrade || currentSubject !== prevSubject || currentClass !== prevClass;},handleStudentClick(student) {this.selectedStudent = {studentId: student.studentId,grade: student.grade,className: student.className,subjectType: student.subjectType};this.showStudentDetail = true;}}
};
</script><style scoped>
.table-container {width: 100%;overflow-x: auto;
}.dynamic-table {width: 100%;border-collapse: collapse;margin: 20px 0;
}.dynamic-table th, .dynamic-table td {border: 1px solid #ddd;padding: 8px;text-align: center;
}.dynamic-table th {background-color: #f2f2f2;font-weight: bold;
}.school-header {background-color: #4CAF50;color: white;font-size: 1.2em;
}.info-header {background-color: #2196F3;color: white;
}.student-header {background-color: #FF9800;color: white;
}.dynamic-table tr:nth-child(even) {background-color: #f9f9f9;
}.dynamic-table tr:hover {background-color: #f1f1f1;cursor: pointer;
}.student-detail-modal {display: block;position: fixed;z-index: 1;left: 0;top: 0;width: 100%;height: 100%;background-color: rgba(0,0,0,0.4);
}.modal-content {background-color: #fefefe;margin: 10% auto;padding: 20px;border: 1px solid #888;width: 50%;border-radius: 5px;
}.close {color: #aaa;float: right;font-size: 28px;font-weight: bold;cursor: pointer;
}.close:hover {color: black;
}
</style>

3. 主应用入口

 

javascript

// main.js
import Vue from 'vue';
import App from './App.vue';
import axios from 'axios';Vue.config.productionTip = false;// 设置axios基础URL
axios.defaults.baseURL = 'http://localhost:8080'; // 根据实际后端地址调整new Vue({render: h => h(App),
}).$mount('#app');

完整实现说明

  1. 后端部分:
    • 定义了Student实体类存储学生信息
    • 创建了TableRow类来表示表格行数据,包含合并行数信息
    • StudentService处理业务逻辑,将学生数据按年级、文理科、班级分组
    • 计算每个分组的合并行数
    • 提供REST API接口返回表格数据
  2. 前端部分:
    • 使用Vue组件展示动态表格
    • 通过axios从后端获取表格数据
    • 实现合并单元格逻辑(年级、文理科、班级列)
    • 学生信息列添加点击事件,显示学生详情弹窗
    • 添加了表格样式,使界面更美观
  3. 合并单元格逻辑:
    • 后端已经计算好每个单元格的rowspan值
    • 前端通过shouldShowGradeshouldShowSubjectshouldShowClass方法判断是否显示单元格
    • 使用rowspan属性实现单元格合并
  4. 交互功能:
    • 点击学号或姓名列会弹出学生详情信息
    • 详情信息包括学号、年级、班级和文理科分类

扩展建议

  1. 可以添加排序功能,允许用户按不同列排序
  2. 可以添加分页功能,处理大量数据
  3. 可以添加搜索功能,快速定位特定学生
  4. 可以添加导出功能,将表格导出为Excel或PDF

这个实现提供了完整的动态表格功能,包括合并单元格、点击交互和样式美化。根据实际需求,您可以进一步调整和扩展。

http://www.xdnf.cn/news/2993.html

相关文章:

  • Femap许可分配和监控
  • 4月29日星期二今日早报简报微语报早读
  • 优化PCB Via Stub系列(1):一次学会利用层叠设计降低Via Stub损耗
  • 使用 Ziegler-Nichols 法进行 PID 参数整定:实践指南
  • [计算机网络]物理层
  • 力扣-数据结构-二叉树
  • 3D可视化编辑器模版
  • AimRT 从零到一:官方示例精讲 —— 四、logger示例.md
  • 信创产业贡献︱悬镜安全深度参编《2024网信自主创新调研报告》
  • 监控易一体化运维:解锁业务系统管理,助力企业运维升级
  • SOLIDWORKS广东东莞地区代理商哪个服务好?都有哪些代理商?
  • docker 部署前、后端分离项目详细步骤(从打包到部署)
  • 嵌入式学习笔记 - 关于STM32 SPI控制器读取以及写入时,标志位TXE, RXNE的变化
  • 文献阅读(二)植被恢复力变化对不同干旱类型的空间异质性|《Earth‘s Future》
  • 第八章 磁盘管理未完待续
  • 关闭正点原子atk-qtapp-start.service
  • 使用C# ASP.NET创建一个可以由服务端推送信息至客户端的WEB应用(一)
  • 解决vue3 路由query传参刷新后数据丢失的问题
  • [MySQL数据库] InnoDB存储引擎(四): InnoDB磁盘文件
  • 基于Spring Boot+Vue 网上书城管理系统设计与实现(源码+文档+部署讲解)
  • C# 中重启程序通常意味着关闭当前运行的应用程序实例
  • 【语法】C++的继承
  • 云钥科技红外短波工业相机
  • 【开源项目】基于sherpa-onnx的实时语音识别系统 - LiveASR
  • 实习技能记录【4】-----消息分发中的观察者模型
  • 聚焦智能体未来,领驭科技在微软创想未来峰会大放异彩
  • 每日一道leetcode(不会做学习版,多学一题)
  • SpringBoot+Mybatis通过自定义注解实现字段加密存储
  • Astro大屏中关于数据流转的数据接入与数据中心之间的逻辑关系梳理
  • 笔试专题(十二)