这是我最后一次尝试。我已经遇到这个问题好几个星期了,但没有运气。我有一个带有 thymeleaf 的 spring crud 应用程序,用于圆盘高尔夫应用程序。您可以添加课程,然后为登录用户添加回合(类似于 udisc),一切都很好。用户可以在手风琴中按课程查看所有回合。我使用 Java Map 并循环
Map<Course, List<Round>>
并使用 thymeleaf/html 显示它。您单击课程(手风琴),然后它会打开该课程的所有回合。鉴于我有限的编程技能,我对到目前为止所做的事情印象深刻。
我的问题是(这是我第三次尝试提出这个问题)在手风琴内部的每一轮中都有一个条形图。使用下面的代码,我在第一个 th:each 中得到了所需的水平条形图,但之后的每一轮都没有。当我刷新页面时,因为 Map 的顺序永远不会相同,手风琴中的第一道菜总是有一个循环中第一轮的条形图,但之后就没有了。那么如何在按
Map<Course, List<Round>>
排序的地图中获得每一轮的条形图?
如果我以错误的方式这样做,请提出建议,我愿意接受所有建议。我对 javascript 的了解远远少于 Java,所以将我视为初学者,但我假设有一个 fetch() 函数或必须再次发生才能使其工作的东西。之前问过here哪里有一些屏幕截图。提前致谢。
课程
@Entity
@Table(name = "course")
@Builder
public class Course {
@Id
@Column(name = "course_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "course_id", referencedColumnName = "course_id")
private List<Hole> holes = new ArrayList<>();
@Column(name = "course_par", nullable = false)
private int par;
@Column(name = "record", nullable = false)
private int record;
@Column(name = "course_average", nullable = false)
private double courseAverage;
//getter setters etc
@Entity
@Table(name = "round")
public class Round {
@Id
@Column(name = "round_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long roundId;
@JsonIgnore
@ManyToOne
@JoinColumn(name = "course_id")
private Course course;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "round_id", referencedColumnName = "round_id")
private List<Score> scores = new ArrayList<>();
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@Column(name = "round_date")
@DateTimeFormat(pattern = "dd/MM/yyyy")
private Date roundDate;
@Column(name = "round_total")
private int total;
//getter setters etc
控制器
@GetMapping("/rounds/{id}")
public String roundsHome(@PathVariable(value = "id") Long id,
Model model) {
List<Course> courses = courseService.getAllCourses();
List<Round> rounds = userService.getUserById(id).getRounds();
rounds.sort(Comparator.comparing(Round::getRoundDate).reversed());
Map<Course, List<Round>> mapRoundsByCourse = rounds.stream().collect(Collectors.groupingBy(Round::getCourse));
model.addAttribute("userId", id);
model.addAttribute("roundService", roundService);
model.addAttribute("courses", courses);
model.addAttribute("rounds", mapRoundsByCourse);
return "/discgolf/round/rounds";
}
html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link th:href="@{/css/fontawesome/css/all.css}" rel="stylesheet">
<link rel="stylesheet" type="text/css" th:href="@{/css/bootstrap/bootstrap.min.css}"/>
<link rel="stylesheet" th:href="@{https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css}">
<link rel="stylesheet" type="text/css" th:href="@{/css/discgolf/round.css}">
<title>Rounds</title>
</head>
<body>
<div th:replace="fragments/navbars/libraryNavbar :: navbar"></div>
<div class="container">
<div class="subContainer">
<h1>Rounds</h1>
<a class="col-sm-4" th:href="@{/discgolf}">Disc Golf Home</a>
<div class="row" id="username">
<b class="col-sm-8">Username: <span sec:authentication="principal.username"></span></b>
</div>
</div>
<a>Add Round</a>
<form action="#" th:action="@{/discgolf/newRound}" th:object="${course}"
method="GET">
<div class="form-group">
<div class="form-group blu-margin">
<select th:name="course" class="form-control" onchange="this.form.submit()">
<option th:value="0" th:text="${'Please Select'}"></option>
<option th:each="course : ${courses}"
th:text="${course.name}" >
</option>
</select>
</div>
</div>
</form>
</div>
<div class="container">
<div>
<a>Rounds Played</a>
</div>
<div >
<div th:each="roundCourse : ${rounds}" class="card">
<button class="accordion">
<div class="row">
<span class="col" th:text="${roundCourse.key.name}"></span>
<div class="col">
<label>Course par:</label>
<span th:text="${roundCourse.key.par}"></span>
</div>
<div class="col">
<label>Course average:</label>
<span th:text="${#numbers.formatDecimal(roundCourse.key.courseAverage, 0, 2)}"></span>
</div>
</div>
</button>
<div class="panel">
<div class="row">
<div class="col-3">
<label>Record: </label>
<label th:if="${roundCourse.key.record > 0}" th:text="${'+' + roundCourse.key.record + ' (' + (roundCourse.key.par + roundCourse.key.record) + ')'}"></label>
<label th:if="${roundCourse.key.record < 0}" th:text="${roundCourse.key.record + ' (' + (roundCourse.key.record + roundCourse.key.par) + ')'}"></label>
<label th:if="${roundCourse.key.record == 0}" th:text="${'E (' + (roundCourse.key.record + roundCourse.key.par) + ')'}"></label>
</div>
<div class="col-3">
<label>Times played: </label>
<label th:text="${#lists.size(roundCourse.value)}"></label>
</div>
<div class="col-3">
<label>My best:</label>
<label th:if="${(roundService.getBestRoundScoreByCourseId(userId, roundCourse.key.id) - roundCourse.key.par) == 0}" th:text="${'E'}"></label>
<label th:if="${(roundService.getBestRoundScoreByCourseId(userId, roundCourse.key.id) - roundCourse.key.par) < 0}"
th:text="${roundService.getBestRoundScoreByCourseId(userId, roundCourse.key.id) - roundCourse.key.par}"></label>
<label th:text="${'(' + roundService.getBestRoundScoreByCourseId(userId, roundCourse.key.id) + ')'}"></label>
</div>
<div class="col-3">
<label>My average: </label>
<label th:text="${#numbers.formatDecimal(roundService.getAverageScoreByCourse(userId, roundCourse.key.id), 0, 2)}"></label>
</div>
</div>
<hr>
<th:block th:each="round : ${roundCourse.value}">
<div id="cardBody" class="card-body">
<div class="row">
<div class="col-3">
<label>Date: </label>
<label th:text="${#dates.format(round.roundDate, 'dd-MMM-yyyy')}"></label>
</div>
<div class="col-3">
<label>Score: </label>
<label th:if="${round.total - round.course.par == 0}" th:text="${'E'}"></label>
<label th:if="${round.total - round.course.par > 0}" th:text="${'+' + (round.total - round.course.par)}"></label>
<label th:text="${'(' + round.total + ')'}"></label>
</div>
<div class="col-6">
<div class="container-fluid">
<canvas th:attr="data-counts=${roundService.getListOfScoresByRoundId(round.roundId)}" id="myChart"></canvas>
<!-- <canvas id="myChart"></canvas>-->
</div>
</div>
</div>
<br>
<div >
<table id="courseInfo" class="table table-bordered w-auto">
<th:block th:each="course : ${round.course}">
<tr>
<th th:text="${'Hole'}"></th>
<th th:each="hole : ${course.holes}" th:text="${hole.number}"></th>
<th th:text="${'Total'}"></th>
</tr>
<tr>
<td th:text="${'Par'}"></td>
<td th:each="par : ${course.holes}" th:text="${par.par}"></td>
<td th:text="${course.par}"></td>
</tr>
<tr>
<td th:text="${'Score'}"></td>
<th:block th:each="score : ${round.scores}">
<td th:text="${score.score}" th:style="'background-color: ' + ${score.color}">
</td>
</th:block>
<td th:text="${round.total}"></td>
</tr>
</th:block>
</table>
<br>
<a th:href="@{/discgolf/deleteRound/{id}(id=${round.roundId})}" title="Remove Course"
data-target="#deleteRoundModal" class="table-link danger" id="deleteRoundButton" >
<span id="deleteRound" class="fa-stack">
<i class="fa fa-square fa-stack-2x"></i>
<i class="fa fa-trash-o fa-stack-1x fa-inverse" title="Delete this round"></i>
</span>
</a>
</div>
</div>
<hr>
<div class="modal modal-danger" tabindex="-1" role="dialog" id="deleteRoundModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Confirm Delete</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete this round?</p>
</div>
<div class="modal-footer">
<a href="" class="btn btn-primary" id="delRound">Confirm</a>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</th:block>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="/js/jquery-3.6.0.js"></script>
<script type="text/javascript" src="/js/bootstrap/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script th:src="@{/js/discgolf/userRounds.js}"></script>
</body>
</html>
Javascript
const countsTest = document.getElementById('myChart').getAttribute('data-counts');
const counts = {};
for (const num of countsTest) {
counts[num] = counts[num] ? counts[num] + 1 : 1;
}
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
new Chart(document.getElementById('myChart'),{
type: 'bar',
options: {
responsive: true,
maintainAspectRatio: false,
indexAxis: 'y',
scales: {
x: {
stacked: true,
display: false
},
y: {
stacked: true,
display: false
}
},
plugins: {
legend: {
display: false
}
},
},
data: {
labels: ["Score"],
datasets: [{
data: [counts[2]],
backgroundColor: "#77ACD8"
},{
data: [counts[3]]
},{
data: [counts[4]],
backgroundColor: "#FDD79C"
},{
data: [counts[5]],
backgroundColor: "#FDC26A"
},{
data: [counts[6], counts[7], counts[8], counts[9], counts[10]],
backgroundColor: "#FCAE37"
}]
}
}
);