問題:
前端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">×</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');
完整實現說明
- 后端部分:
- 定義了
Student
實體類存儲學生信息 - 創建了
TableRow
類來表示表格行數據,包含合并行數信息 StudentService
處理業務邏輯,將學生數據按年級、文理科、班級分組- 計算每個分組的合并行數
- 提供REST API接口返回表格數據
- 定義了
- 前端部分:
- 使用Vue組件展示動態表格
- 通過axios從后端獲取表格數據
- 實現合并單元格邏輯(年級、文理科、班級列)
- 學生信息列添加點擊事件,顯示學生詳情彈窗
- 添加了表格樣式,使界面更美觀
- 合并單元格邏輯:
- 后端已經計算好每個單元格的rowspan值
- 前端通過
shouldShowGrade
、shouldShowSubject
、shouldShowClass
方法判斷是否顯示單元格 - 使用
rowspan
屬性實現單元格合并
- 交互功能:
- 點擊學號或姓名列會彈出學生詳情信息
- 詳情信息包括學號、年級、班級和文理科分類
擴展建議
- 可以添加排序功能,允許用戶按不同列排序
- 可以添加分頁功能,處理大量數據
- 可以添加搜索功能,快速定位特定學生
- 可以添加導出功能,將表格導出為Excel或PDF
這個實現提供了完整的動態表格功能,包括合并單元格、點擊交互和樣式美化。根據實際需求,您可以進一步調整和擴展。