第一步 创建模块


第二步 创建项目结构
后端:

前端:

第三步 编写代码
后端:
1.MessageController.java
package com.itheima.controller;
import com.itheima.service.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Map;
@Controller
public class MessageController {
private final MessageService messageService;
@Autowired
public MessageController(MessageService messageService) {
this.messageService = messageService;
}
// 首页展示所有留言
@GetMapping("/")
public String index(Model model) {
Map<String, Object> data = messageService.getAllMessagesWithOriginalInfo();
model.addAttribute("messages", data.get("messages"));
model.addAttribute("messageMap", data.get("messageMap"));
return "index";
}
// 发表新留言
@PostMapping("/postMessage")
public String postMessage(
@RequestParam("username") String username,
@RequestParam("content") String content) {
messageService.postMessage(username, content);
return "redirect:/";
}
// 转发留言(修改为支持评论参数)
@PostMapping("/forwardMessage")
public String forwardMessage(
@RequestParam("messageId") Integer messageId,
@RequestParam("username") String username,
@RequestParam(value = "comment", required = false, defaultValue = "") String comment) {
messageService.forwardMessage(messageId, username, comment);
return "redirect:/";
}
}
2.Message.java
package com.itheima.entity;
import java.time.LocalDateTime;
public class Message {
private Integer id;
private String username;
private String content;
private LocalDateTime createTime;
private boolean isForward;
private Integer originalId;
private String comment; // 添加评论字段,用于存储转发时的评论
// 构造函数
public Message() {
this.createTime = LocalDateTime.now();
}
public Message(String username, String content) {
this.username = username;
this.content = content;
this.createTime = LocalDateTime.now();
this.isForward = false;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public boolean isForward() {
return isForward;
}
public void setForward(boolean forward) {
isForward = forward;
}
public Integer getOriginalId() {
return originalId;
}
public void setOriginalId(Integer originalId) {
this.originalId = originalId;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
}
3.MessageRepositoryImpl.java
package com.itheima.repository.impl;
import com.itheima.entity.Message;
import com.itheima.repository.MessageRepository;
import org.springframework.stereotype.Repository;
import java.util.*;
@Repository
public class MessageRepositoryImpl implements MessageRepository {
private List<Message> messages = new ArrayList<>();
@Override
public void save(Message message) {
if (message.getId() == null) {
Integer maxId = getMaxId();
message.setId(maxId != null ? maxId + 1 : 1);
}
messages.add(message);
}
@Override
public List<Message> findAll() {
return new ArrayList<>(messages);
}
@Override
public Message findById(Integer id) {
return messages.stream()
.filter(message -> message.getId().equals(id))
.findFirst()
.orElse(null);
}
@Override
public Integer getMaxId() {
if (messages.isEmpty()) {
return null;
}
return messages.stream()
.map(Message::getId)
.max(Comparator.naturalOrder())
.orElse(null);
}
}
4.MessageRepository.java
package com.itheima.repository;
import com.itheima.entity.Message;
import java.util.List;
public interface MessageRepository {
void save(Message message);
List<Message> findAll();
Message findById(Integer id);
Integer getMaxId();
}
5.MessageService.java
package com.itheima.service;
import com.itheima.entity.Message;
import com.itheima.repository.MessageRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class MessageService {
private final MessageRepository messageRepository;
@Autowired
public MessageService(MessageRepository messageRepository) {
this.messageRepository = messageRepository;
}
// 发表新留言
public void postMessage(String username, String content) {
Message message = new Message(username, content);
messageRepository.save(message);
}
// 获取所有留言(按创建时间降序排列,最新的在最上方)
public List<Message> getAllMessages() {
List<Message> messages = messageRepository.findAll();
// 按创建时间降序排序
messages.sort(Comparator.comparing(Message::getCreateTime).reversed());
return messages;
}
// 获取所有留言及其相关信息(用于前端展示)
public Map<String, Object> getAllMessagesWithOriginalInfo() {
List<Message> messages = getAllMessages();
Map<Integer, Message> messageMap = new HashMap<>();
// 先构建所有消息的ID到消息对象的映射
for (Message message : messages) {
messageMap.put(message.getId(), message);
}
Map<String, Object> result = new HashMap<>();
result.put("messages", messages);
result.put("messageMap", messageMap);
return result;
}
// 转发留言(修改为支持评论)
public void forwardMessage(Integer messageId, String username, String comment) {
Message originalMessage = messageRepository.findById(messageId);
if (originalMessage != null) {
// 创建转发的新消息
Message forwardedMessage = new Message();
forwardedMessage.setUsername(username);
forwardedMessage.setContent(originalMessage.getContent());
forwardedMessage.setForward(true);
forwardedMessage.setOriginalId(messageId);
forwardedMessage.setComment(comment); // 设置评论内容
messageRepository.save(forwardedMessage);
}
}
}
前端:
1.index.html
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>留言板</title>
<style>
/* 基础样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', 'PingFang SC', Arial, sans-serif;
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
/* 标题样式 */
header {
text-align: center;
margin-bottom: 30px;
padding-bottom: 15px;
border-bottom: 2px solid #e1e8ed;
}
h1 {
color: #2c3e50;
font-size: 2.2rem;
font-weight: 600;
margin-bottom: 10px;
}
/* 表单区域样式 */
.message-form {
background-color: #fff;
padding: 25px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
margin-bottom: 30px;
transition: transform 0.3s ease;
}
.message-form:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
.message-form h2 {
color: #34495e;
font-size: 1.5rem;
margin-bottom: 20px;
font-weight: 500;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
font-weight: 500;
margin-bottom: 8px;
color: #555;
font-size: 0.95rem;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 12px 15px;
border: 2px solid #e1e8ed;
border-radius: 8px;
font-size: 1rem;
font-family: inherit;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
.form-group textarea {
resize: vertical;
min-height: 100px;
line-height: 1.5;
}
/* 按钮样式 */
.btn {
display: inline-block;
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
text-align: center;
}
.btn-primary {
background-color: #3498db;
color: white;
}
.btn-primary:hover {
background-color: #2980b9;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(52, 152, 219, 0.3);
}
.btn-secondary {
background-color: #95a5a6;
color: white;
padding: 8px 16px;
font-size: 0.85rem;
}
.btn-secondary:hover {
background-color: #7f8c8d;
transform: translateY(-1px);
}
/* 留言列表样式 */
.messages-section {
background-color: #fff;
padding: 25px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.messages-section h2 {
color: #34495e;
font-size: 1.5rem;
margin-bottom: 20px;
font-weight: 500;
}
.no-messages {
text-align: center;
color: #95a5a6;
padding: 40px 20px;
font-style: italic;
}
.messages-list {
list-style: none;
}
.message-item {
padding: 15px 0;
border-bottom: 1px solid #f0f0f0;
transition: background-color 0.2s ease;
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.message-item:hover {
background-color: #f8f9fa;
}
.message-item:last-child {
border-bottom: none;
}
.message-content {
flex-grow: 1;
margin-right: 15px;
}
.message-username {
font-weight: 600;
color: #2c3e50;
margin-right: 8px;
}
.message-text {
color: #555;
word-break: break-word;
}
/* 转发相关样式 */
.forward-form {
margin-left: auto;
}
.forward-area {
margin-top: 10px;
padding: 10px;
background-color: #f8f9fa;
border-radius: 8px;
}
.forward-comment {
color: #e74c3c;
font-style: italic;
margin-top: 5px;
}
.original-message {
background-color: #e8f4fd;
padding: 8px 12px;
border-radius: 6px;
margin-top: 8px;
font-size: 0.9em;
border-left: 3px solid #3498db;
}
/* 响应式设计 */
@media (max-width: 600px) {
.container {
padding: 15px;
}
h1 {
font-size: 1.8rem;
}
.message-form,
.messages-section {
padding: 20px;
}
.message-item {
flex-direction: column;
}
.forward-form {
margin-top: 10px;
align-self: flex-end;
}
}
/* 添加一些动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.5s ease;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>留言板</h1>
</header>
<!-- 发表留言表单 -->
<div class="message-form fade-in">
<h2>发表留言</h2>
<form action="/postMessage" method="post">
<div class="form-group">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required placeholder="请输入您的用户名">
</div>
<div class="form-group">
<label for="content">留言内容:</label>
<textarea id="content" name="content" required placeholder="请输入您的留言内容..."></textarea>
</div>
<button type="submit" class="btn btn-primary">发表留言</button>
</form>
</div>
<!-- 所有留言列表 -->
<div class="messages-section fade-in">
<h2>所有留言</h2>
<div th:if="${messages.empty}" class="no-messages">
暂无留言,快来发表第一条留言吧!
</div>
<ul th:unless="${messages.empty}" class="messages-list">
<li th:each="message : ${messages}" class="message-item fade-in">
<div class="message-content">
<!-- 显示用户名 -->
<span th:text="${message.username}" class="message-username"></span>
<!-- 对于普通消息,显示内容 -->
<span th:if="!${message.forward}" th:text="${message.content}" class="message-text"></span>
<!-- 对于转发消息,显示'被转发用户名:留言'的形式 -->
<span th:if="${message.forward}" class="message-text" th:text="|被转发${messageMap[message.originalId].username}:${messageMap[message.originalId].content}|"></span>
<!-- 显示转发的评论 -->
<div th:if="${message.comment != null and not #strings.isEmpty(message.comment)}" class="forward-comment">
评论: <span th:text="${message.comment}"></span>
</div>
</div>
<!-- 转发表单,添加评论输入框 -->
<div class="forward-form">
<button type="button" class="btn btn-secondary forward-btn" th:attr="data-id=${message.id}">转发</button>
<!-- 转发评论表单 (默认隐藏) -->
<form th:id="forwardForm_+${message.id}" action="/forwardMessage" method="post" style="display: none; margin-top: 10px;">
<input type="hidden" name="messageId" th:value="${message.id}">
<div class="form-group" style="margin-bottom: 10px;">
<input type="text" name="username" placeholder="您的用户名" required style="padding: 6px 10px; width: 100%;">
</div>
<div class="form-group" style="margin-bottom: 10px;">
<textarea name="comment" placeholder="添加您的评论..." style="padding: 6px 10px; width: 100%; min-height: 60px;"></textarea>
</div>
<div style="display: flex; gap: 5px;">
<button type="submit" class="btn btn-primary" style="padding: 6px 12px; font-size: 0.8rem;">确认转发</button>
<button type="button" class="btn btn-secondary cancel-forward" th:attr="data-id=${message.id}" style="padding: 6px 12px; font-size: 0.8rem;">取消</button>
</div>
</form>
</div>
</li>
</ul>
</div>
</div>
<script>
// JavaScript代码保持不变
document.addEventListener('DOMContentLoaded', function() {
// 为表单添加提交确认
const form = document.querySelector('form[action="/postMessage"]');
if (form) {
form.addEventListener('submit', function(e) {
const username = document.getElementById('username').value;
const content = document.getElementById('content').value;
if (!username.trim() || !content.trim()) {
alert('请填写完整的用户名和留言内容!');
e.preventDefault();
}
});
}
// 转发按钮点击事件
const forwardButtons = document.querySelectorAll('.forward-btn');
forwardButtons.forEach(button => {
button.addEventListener('click', function() {
const messageId = this.getAttribute('data-id');
const forwardForm = document.getElementById('forwardForm_' + messageId);
forwardForm.style.display = 'block';
this.style.display = 'none';
});
});
// 取消转发按钮点击事件
const cancelButtons = document.querySelectorAll('.cancel-forward');
cancelButtons.forEach(button => {
button.addEventListener('click', function() {
const messageId = this.getAttribute('data-id');
const forwardForm = document.getElementById('forwardForm_' + messageId);
const forwardButton = document.querySelector('.forward-btn[data-id="' + messageId + '"]');
forwardForm.style.display = 'none';
forwardButton.style.display = 'inline-block';
});
});
// 转发表单提交确认
const forwardForms = document.querySelectorAll('form[action="/forwardMessage"]');
forwardForms.forEach(form => {
form.addEventListener('submit', function(e) {
const username = this.querySelector('input[name="username"]').value;
if (!username.trim()) {
alert('请填写您的用户名!');
e.preventDefault();
}
});
});
});
</script>
</body>
</html>
配置:
application.properties.java
spring.thymeleaf.cache=false
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html
server.port=8080
spring.application.name=springboot-messageboard-demo
第四步 运行
启动项目,输入网站:localhost:8080

项目界面:

效果


736

被折叠的 条评论
为什么被折叠?



