This commit is contained in:
qingfeng1121
2025-09-29 18:39:17 +08:00
parent aaf326ed1f
commit 4fe6add803
7 changed files with 245 additions and 404 deletions

View File

@@ -1,21 +1,65 @@
<template>
<div id="message_all">
<!-- 标题 -->
<div class="message-header">
<h1>留言板</h1>
</div>
<!-- 留言内容区 -->
<div class="message-board">
<!-- 留言内容 -->
<div class="message-list">
<div class="message-item" v-for="msg in messages" :key="msg.id">
<div class="message-nickname">{{ msg.nickname }}</div>
<div class="message-content">{{ msg.content }}</div>
<div class="message-time">{{ msg.time }}</div>
<h3>留言板</h3>
<div class="message-item" v-for="msg in messages" :key="msg.id" @mouseenter="hoverId = msg.id"
@mouseleave="hoverId = null">
<div>
<!-- 头像 -->
<img src="https://www.gravatar.com/avatar?d=mp&s=40" alt="头像" class="message-avatar" />
</div>
<div>
<!-- 评论内容 -->
<div class="message-item-top">
<div class="message-nickname">{{ msg.nickname }}</div>
<div class="message-time">{{ msg.time }}</div>
</div>
<div class="message-content">{{ msg.content }}</div>
<!-- 回复按钮 -->
<div class="message-item-bottom">
<button class="reply-btn" @click="message_click(msg)"
:class="{ visible: hoverId === msg.id }">回复</button>
</div>
<!-- 回复列表 -->
<div v-if="msg.replies && msg.replies.length" class="replies">
<div class="reply-item" v-for="reply in msg.replies" :key="reply.id">
<div>
<!-- 头像 -->
<img src="https://www.gravatar.com/avatar?d=mp&s=40" alt="头像" class="message-avatar" />
</div>
<div>
<div class="message-item-top ">
<div class="message-nickname">{{ reply.nickname }}</div>
<div class="message-time">{{ reply.time }}</div>
</div>
<div class="message-content">{{ reply.content }}</div>
<!-- 回复按钮 -->
<div class="message-item-bottom">
<button class="reply-btn" @click="message_click(msg)"
:class="{ visible: hoverId === msg.id }">回复</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-if="messages.length === 0" class="message-empty">还没有留言快来抢沙发吧</div>
</div>
<!-- 留言输入区 -->
<!-- 留言输入区 -->
<div class="message-form-section">
<h2>发送评论请正确填写邮箱地址否则将会当成垃圾评论处理</h2>
<div v-if="message_all.id" class="reply-preview">
<span>
正在回复 <b>{{ message_all.nickname }}</b> 的评论 :
</span>
<div class="reply-preview-content">
{{ message_all.content }}
</div>
<button class="reply-cancel-btn" @click="post_comment_reply_cancel">取消回复</button>
</div>
<el-form :model="form" label-width="0">
<el-form-item>
<el-input v-model="form.content" placeholder="评论内容" type="textarea" rows="4" clearable />
@@ -31,9 +75,11 @@
<el-input v-model="form.captcha" placeholder="验证码" clearable />
</el-form-item>
</div>
<el-form-item>
<el-button type="primary" @click="onSubmit">发送</el-button>
</el-form-item>
<div class="form-input-row">
<el-form-item>
<el-button type="primary" @click="onSubmit">发送</el-button>
</el-form-item>
</div>
</el-form>
</div>
</div>
@@ -41,14 +87,41 @@
<script setup>
import { reactive, ref } from 'vue'
const message_all = ref({})
const hoverId = ref(null)
const messages = ref([
{ id: 1, nickname: 'A', content: '这里是A', time: '2025-09-26 10:00' },
{ id: 2, nickname: 'B', content: '这里是B', time: '2025-09-26 10:05' },
{ id: 3, nickname: 'C', content: '这里是C', time: '2025-09-26 10:10' }
{
id: 1,
nickname: 'A',
content: '这里是A',
time: '2025-09-26 10:00',
replies: [
{
id: 4,
nickname: 'B',
content: 'A我来回复你',
time: '2025-09-26 10:20'
}
]
},
{
id: 2,
nickname: 'B',
content: '这里是B',
time: '2025-09-26 10:05',
replies: []
},
{
id: 3,
nickname: 'C',
content: '这里是C',
time: '2025-09-26 10:10',
replies: []
}
])
const form = reactive({
replyid: null,
content: '',
nickname: '',
email: '',
@@ -57,27 +130,51 @@ const form = reactive({
const onSubmit = () => {
if (!form.content || !form.nickname) return
messages.value.push({
id: messages.value.length + 1,
nickname: form.nickname,
content: form.content,
time: new Date().toLocaleString()
})
if (form.replyid) {
// 回复模式
const parent = messages.value.find(msg => msg.id === form.replyid)
if (parent) {
parent.replies = parent.replies || []
parent.replies.push({
id: Date.now(),
nickname: form.nickname,
content: form.content,
time: new Date().toLocaleString()
})
}
} else {
// 普通留言
messages.value.push({
id: Date.now(),
nickname: form.nickname,
content: form.content,
time: new Date().toLocaleString(),
replies: []
})
}
form.replyid = null
form.content = ''
form.nickname = ''
form.email = ''
form.captcha = ''
message_all.value = {}
}
const message_click = (msg) => {
message_all.value = msg
form.replyid = msg.id
// 可选:填充回复内容到输入框
form.content = `@${msg.nickname} `
}
const post_comment_reply_cancel = () => {
message_all.value = {}
form.content = ''
form.replyid = null
}
</script>
<style scoped>
#message_all {
width: var(--content-width);
margin: var(--content-margin);
}
.message-header {
background: rgba(102, 161, 216, 0.9);
border-radius: 12px;
padding: 20px 0;
text-align: center;
margin-bottom: 20px;
.message-board {
padding: 24px 0;
}
.message-list {
@@ -88,23 +185,46 @@ const onSubmit = () => {
min-height: 120px;
}
.replies {
margin-left: 40px;
border-top: 2px solid #eee;
padding: 12px;
}
.message-item {
background: #fff;
border-radius: 10px;
padding: 14px 18px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.03);
flex-direction: column;
gap: 4px;
position: relative;
margin-left: 1%;
}
.message-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 12px;
float: left;
}
.message-item-top {
display: flex;
justify-content: space-between;
align-items: center;
}
.message-nickname {
font-weight: bold;
color: #409eff;
margin-bottom: 4px;
}
.message-content {
margin-bottom: 4px;
color: #333;
margin-top: 2px;
}
.message-time {
@@ -113,6 +233,30 @@ const onSubmit = () => {
text-align: right;
}
.message-item-bottom {
height: 45px;
/* 固定高度以防跳动 */
text-align: center;
}
.reply-btn {
background: #409eff;
color: #fff;
border: none;
border-radius: 6px;
padding: 4px 12px;
margin-top: 4px;
transition: background 0.2s;
float: right;
visibility: hidden;
/* 默认隐藏但占位 */
}
.message-item:hover .reply-btn,
.reply-btn.visible {
visibility: visible;
}
.message-empty {
color: #bbb;
text-align: center;
@@ -132,6 +276,38 @@ const onSubmit = () => {
color: #333;
}
.reply-preview {
background: #f0f9ff;
border-left: 4px solid #409eff;
padding: 10px 16px;
border-radius: 8px;
margin-bottom: 16px;
color: #333;
}
.reply-preview-content {
margin-top: 6px;
color: #666;
font-size: 0.98rem;
}
.reply-cancel-btn {
background: #fff;
color: #409eff;
border: 1px solid #409eff;
border-radius: 6px;
padding: 2px 10px;
cursor: pointer;
margin-top: 8px;
font-size: 0.95rem;
transition: background 0.2s, color 0.2s;
}
.reply-cancel-btn:hover {
background: #409eff;
color: #fff;
}
.form-input-row {
display: flex;
gap: 16px;
@@ -149,7 +325,7 @@ const onSubmit = () => {
}
@media (max-width: 768px) {
#message_all {
.message-board {
padding: 8px 0;
}