动态图表 -- 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.mzph.cn/news/904297.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++学习之shell高级和正则表达式

目录 1.正则表达式 2.C中使用正则 3.复习 4.sort命令 5.uniq命令 6.wc命令 7.grep命令 8.find命令 9.xargs命令 10.sed命令 11.awk命令 12.crontab 1.正则表达式 1 管道 使用| 将多个命令拼接在一起 原理&#xff0c;就是将前一个命令的标准输出作为后一个…

【Vue】 实现TodoList案例(待办事项)

目录 组件化编码流程&#xff08;通用&#xff09; 1.实现静态组件&#xff1a;抽取组件&#xff0c;使用组件实现静态页面效果 2.展示动态数据&#xff1a; 1. 常规 HTML 属性 3.交互——从绑定事件监听开始 什么时候要用 event&#xff1a; 什么时候不需要用 event&am…

【Bootstrap V4系列】学习入门教程之 组件-卡片(Card)

Bootstrap V4系列 学习入门教程之 组件-卡片&#xff08;Card&#xff09; 卡片&#xff08;Card&#xff09;一、Example二、Content types 内容类型2.1 Body 主体2.2 Titles, text, and links 标题、文本和链接2.3 Images 图片2.4 List groups 列表组2.5 Kitchen sink 洗涤槽…

java学习之数据结构:四、树(代码补充)

这部分主要是用代码实现有序二叉树、树遍历、删除节点 目录 1.构建有序二叉树 1.1原理 1.2插入实现 2.广度优先遍历--队列实现 3.深度优先遍历--递归实现 3.1先序遍历 3.2中序遍历 3.3后序遍历 4.删除 4.1删除叶子节点 4.2删除有一棵子树的节点 4.3删除有两棵子树的节…

架构进阶:什么是数据架构,如何理解数据架构?(华为)

数据架构是企业架构的重要组成部分,DAMA、IBM 及国内大厂对其定义各有侧重。它包含数据资产目录、数据标准、数据模型和数据分布四个组件。数据资产目录可梳理企业数据资产,数据标准统一数据含义和规则,数据模型反映业务对象关联关系,数据分布呈现数据流动情况。数据架构是…

Unity SpriteEditor(精灵图片编辑器)

&#x1f3c6; 个人愚见&#xff0c;没事写写笔记 &#x1f3c6;《博客内容》&#xff1a;Unity3D开发内容 &#x1f3c6;&#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f50e;SpriteEditor&#xff1a; 精灵图片编辑器 &#x1f4cc;用于编辑2D游戏开发中使用的Sp…

【网络原理】从零开始深入理解HTTP的报文格式(一)

本篇博客给大家带来的是网络HTTP协议的知识点, 重点介绍HTTP的报文格式. &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅&#x1f68…

ElasticSearch深入解析(九):Object、Nested、Flattened类型

文章目录 一、Object 类型&#xff1a;默认的嵌套对象处理方式核心原理典型场景关键限制 二、Nested 类型&#xff1a;解决嵌套数组的关联查询核心原理典型场景使用示例注意事项 三、Join 类型&#xff1a;跨文档的父子关联核心原理典型场景使用示例注意事项 四、Flattened 类型…

36、C#中的⽅法声明参数关键字params,ref,out的意义及⽤法

在C#中&#xff0c;params、ref 和 out 是方法声明中用于修饰参数的关键字&#xff0c;它们各自有不同的用途和语义。以下是它们的详细说明和用法&#xff1a; 1、 params 关键字 意义 params 允许方法接受可变数量的参数&#xff0c;这些参数会被编译为一个数组。适用于参数…

【大模型实战篇】华为信创环境采用vllm部署QwQ-32B模型

1. 背景 本文分享在华为昇腾机器上部署QwQ-32B模型的实践。 首先华为自己是提供了一套在信创机器&#xff08;NPU&#xff09;上部署模型的方案【1】&#xff0c;但是部署之后&#xff0c;测试发现会有输出截断的现象。QwQ-32B本身是支持128k的最大上下文长度&#xff0c;定位…

前端面经-VUE3篇(二)--vue3基础知识(二)计算属性(computed)、监听属性(Watch)

一、计算属性(computed) 计算属性&#xff08;Computed Properties&#xff09;是 Vue 中一种特殊的响应式数据&#xff0c;它能基于已有的响应式数据动态计算出新的数据。 计算属性有以下特性&#xff1a; 自动缓存&#xff1a;只有当它依赖的响应式数据发生变化时&#xff…

[预备知识] 5. 优化理论(一)

优化理论 梯度下降&#xff08;Gradient Descent&#xff09; 数学原理与可视化 梯度下降是优化领域的基石算法&#xff0c;其核心思想是沿负梯度方向迭代更新参数。数学表达式为&#xff1a; θ t 1 θ t − α ∇ θ J ( θ t ) \theta_{t1} \theta_t - \alpha \nabla…

大模型微调Fine-tuning:从概念到实践的全面解析

目录 引言 一、什么是大模型微调&#xff1f; 1.1 预训练与微调的区别 1.2 微调的技术演进 二、为什么需要微调&#xff1f; 2.1 解决大模型的固有局限 2.2 微调的优势 三、主流微调方法 3.1 全参数微调 3.2 参数高效微调&#xff08;PEFT&#xff09; 四、微调实践指…

Docker 使用下 (二)

Docker 使用下 &#xff08;二&#xff09; 文章目录 Docker 使用下 &#xff08;二&#xff09;前言一、初识Docker1.1 、Docker概述1.2 、Docker的历史1.3 、Docker解决了什么问题1.4 、Docker 的优点1.5 、Docker的架构图 二、镜像三、容器四、数据卷4.1、数据卷的概念4.2 、…

洛谷P12238 [蓝桥杯 2023 国 Java A] 单词分类

[Problem Discription] \color{blue}{\texttt{[Problem Discription]}} [Problem Discription] Copy from luogu. [Analysis] \color{blue}{\texttt{[Analysis]}} [Analysis] 既然都是字符串前缀的问题了&#xff0c;那当然首先就应该想到 Trie \text{Trie} Trie 树。 我们可…

pta作业中有启发性的程序题

1 【知识点】&#xff1a;多态 函数接口定义&#xff1a; 以Student为基类&#xff0c;构建GroupA, GroupB和GroupC三个类 裁判测试程序样例&#xff1a; #include<iostream> #include <string> using namespace std;/* 请在这里填写答案 */int main() {const …

Scrapy框架之CrawlSpider爬虫 实战 详解

CrawlSpider 是 Scrapy 框架中一个非常实用的爬虫基类&#xff0c;它继承自 Spider 类&#xff0c;主要用于实现基于规则的网页爬取。相较于普通的 Spider 类&#xff0c;CrawlSpider 可以根据预定义的规则自动跟进页面中的链接&#xff0c;从而实现更高效、更灵活的爬取。 Scr…

Glide 如何加载远程 Base64 图片

最近有个需求&#xff0c;后端给出的图片地址并不是正常的 URL&#xff0c;而且需要一个接口去请求&#xff0c;但是返回的是 base64 数据流。这里不关心为啥要这么多&#xff0c;原因有很多&#xff0c;可能是系统的问题&#xff0c;也可能是能力问题。当然作为我们 Android 程…

004-nlohmann/json 快速认识-C++开源库108杰

了解 nlohmann/json 的特点&#xff1b;理解编程中 “数据战场”划分的概念&#xff1b;迅速上手多种方式构建一个JSON对象&#xff1b; 1 特点与安装 nlohmann/json 是一个在 github 长期霸占 “JSON” 热搜版第1的CJSON处理库。它的最大优点是与 C 标准库的容器数据&#xf…

#基础Machine Learning 算法(上)

机器学习算法的分类 机器学习算法大致可以分为三类&#xff1a; 监督学习算法 (Supervised Algorithms&#xff09;:在监督学习训练过程中&#xff0c;可以由训练数据集学到或建立一个模式&#xff08;函数 / learning model&#xff09;&#xff0c;并依此模式推测新的实例。…