759 lines
23 KiB
Vue
759 lines
23 KiB
Vue
<template>
|
||
<div>
|
||
<div class="message-board">
|
||
<!-- 留言内容区 -->
|
||
<div class="message-list">
|
||
<h3 class="title">留言板</h3>
|
||
<!-- 加载状态 -->
|
||
<div v-if="loading" class="loading-container">
|
||
<el-skeleton :count="5" />
|
||
</div>
|
||
|
||
<!-- 留言列表 -->
|
||
<transition-group name="message-item" tag="div" v-else>
|
||
<!-- 留言板留言 (articleid为空的留言) -->
|
||
<div v-if="messageBoardData.length > 0" class="message-section">
|
||
<h4>留言板留言</h4>
|
||
<!-- 主留言和回复树结构 -->
|
||
<div v-for="mainMsg in messageBoardData" :key="mainMsg.id" class="message-tree">
|
||
<!-- 主留言 -->
|
||
<div class="message-item" @mouseenter="hoverId = mainMsg.id" @mouseleave="hoverId = null">
|
||
<div class="message-avatar-container">
|
||
<img :src="getAvatar(mainMsg.email)" alt="头像" class="message-avatar" />
|
||
</div>
|
||
<div class="message-content-container">
|
||
<div class="message-item-top">
|
||
<div class="message-nickname">{{ mainMsg.nickname || '匿名用户' }}</div>
|
||
<div class="message-time">{{ formatRelativeTime(mainMsg.createdAt) }}</div>
|
||
</div>
|
||
<div class="message-content">{{ mainMsg.content }}</div>
|
||
<!-- 回复按钮 -->
|
||
<div class="message-item-bottom">
|
||
<button class="reply-btn" @click="handleReply(mainMsg)"
|
||
:class="{ visible: hoverId === mainMsg.id }">
|
||
回复
|
||
</button>
|
||
</div>
|
||
<!-- 回复列表 -->
|
||
<div v-if="mainMsg.replies.length> 0" class="replies">
|
||
<div v-for="reply in mainMsg.replies" :key="reply.id" class="reply-item">
|
||
<div class="message-avatar-container">
|
||
<img :src="getAvatar(reply.email)" alt="头像" class="message-avatar" />
|
||
</div>
|
||
<div class="message-content-container">
|
||
<div class="message-item-top">
|
||
<div class="message-nickname">{{ reply.nickname || '匿名用户' }}</div>
|
||
<div class="message-time">{{ formatRelativeTime(reply.createdAt) }}
|
||
</div>
|
||
</div>
|
||
<div class="message-content">
|
||
<span class="reply-to">@{{ mainMsg.nickname || '匿名用户' }}:</span>
|
||
{{ reply.content }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 文章相关留言 (articleid不为空的留言) -->
|
||
<div v-if="articleRelatedData.length > 0" class="message-section">
|
||
<h4>文章留言</h4>
|
||
<div v-for="articleGroup in articleRelatedData" :key="articleGroup.articleId"
|
||
class="article-message-group">
|
||
<div class="article-message-header">
|
||
<span class="article-title">{{ articleGroup.articleTitle }}</span>
|
||
</div>
|
||
<div class="article-message-content">
|
||
<div v-for="mainMsg in articleGroup.messages" :key="mainMsg.messageid" class="message-tree">
|
||
<!-- 主留言 -->
|
||
<div class="message-item" @mouseenter="hoverId = mainMsg.messageid"
|
||
@mouseleave="hoverId = null">
|
||
<div class="message-avatar-container">
|
||
<img :src="getAvatar(mainMsg.email)" alt="头像" class="message-avatar" />
|
||
</div>
|
||
<div class="message-content-container">
|
||
<div class="message-item-top">
|
||
<div class="message-nickname">{{ mainMsg.nickname || '匿名用户' }}</div>
|
||
<div class="message-time">{{ formatRelativeTime(mainMsg.createdAt) }}
|
||
</div>
|
||
</div>
|
||
<div class="message-content">{{ mainMsg.content }}</div>
|
||
<!-- 回复按钮 -->
|
||
<div class="message-item-bottom">
|
||
<button class="reply-btn" @click="handleReply(mainMsg)"
|
||
:class="{ visible: hoverId === mainMsg.messageid }">
|
||
回复
|
||
</button>
|
||
</div>
|
||
<!-- 回复列表 -->
|
||
<div v-if="mainMsg.replies && mainMsg.replies.length" class="replies">
|
||
<div v-for="reply in mainMsg.replies" :key="reply.messageid"
|
||
class="reply-item">
|
||
<div class="message-avatar-container">
|
||
<img :src="getAvatar(reply.email)" alt="头像"
|
||
class="message-avatar" />
|
||
</div>
|
||
<div class="message-content-container">
|
||
<div class="message-item-top">
|
||
<div class="message-nickname">{{ reply.nickname || '匿名用户' }}
|
||
</div>
|
||
<div class="message-time">{{
|
||
formatRelativeTime(reply.createdAt) }}</div>
|
||
</div>
|
||
<div class="message-content">
|
||
<span class="reply-to">@{{ mainMsg.nickname || '匿名用户'
|
||
}}:</span>
|
||
{{ reply.content }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</transition-group>
|
||
|
||
<div v-if="!loading && messageBoardData.length === 0 && articleRelatedData.length === 0"
|
||
class="message-empty">
|
||
还没有留言,快来抢沙发吧!
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 留言输入区 -->
|
||
<div class="message-form-section">
|
||
<h2>发送评论(请正确填写邮箱地址,否则将会当成垃圾评论处理)</h2>
|
||
<div v-if="replyingTo.id" class="reply-preview">
|
||
<span>
|
||
正在回复 <b>{{ replyingTo.nickname }}</b> 的评论:
|
||
</span>
|
||
<div class="reply-preview-content">
|
||
{{ replyingTo.content }}
|
||
</div>
|
||
<button class="reply-cancel-btn" @click="cancelReply">取消回复</button>
|
||
</div>
|
||
<el-form :model="form" label-width="0">
|
||
<el-form-item>
|
||
<el-input v-model="form.content" placeholder="评论内容" type="textarea" rows="4" clearable
|
||
:disabled="submitting" />
|
||
</el-form-item>
|
||
<div class="form-input-row">
|
||
<el-form-item>
|
||
<el-input v-model="form.nickname" placeholder="昵称" clearable :disabled="submitting" />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-input v-model="form.email" placeholder="邮箱/QQ号" clearable :disabled="submitting" />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-input v-model="form.captcha" placeholder="验证码" clearable :disabled="submitting" />
|
||
</el-form-item>
|
||
</div>
|
||
<div class="form-input-row">
|
||
<el-form-item>
|
||
<el-button type="primary" @click="onSubmit" :loading="submitting"
|
||
:disabled="!form.content || !form.nickname">
|
||
发送
|
||
</el-button>
|
||
</el-form-item>
|
||
</div>
|
||
</el-form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { reactive, ref, onMounted } from 'vue'
|
||
import { messageService } from '@/services'
|
||
import { formatRelativeTime } from '@/utils/dateUtils'
|
||
import { ElMessage } from 'element-plus'
|
||
import { useRoute } from 'vue-router'
|
||
|
||
const route = useRoute()
|
||
const hoverId = ref(null)
|
||
const messageBoardData = ref([]) // 留言板留言(articleid为空的主留言及其回复)
|
||
const articleRelatedData = ref([]) // 文章相关留言(articleid不为空的主留言及其回复,按文章分组)
|
||
const loading = ref(false)
|
||
const submitting = ref(false)
|
||
const replyingTo = ref({ id: null, nickname: '', content: '' })
|
||
|
||
// 生成头像URL
|
||
const getAvatar = (email) => {
|
||
if (!email) return 'https://www.gravatar.com/avatar?d=mp&s=40'
|
||
return `https://www.gravatar.com/avatar/${email}?d=mp&s=40`
|
||
}
|
||
|
||
// 从后端获取留言列表
|
||
const fetchMessages = async () => {
|
||
try {
|
||
loading.value = true
|
||
const res = await messageService.getAllMessages()
|
||
const allMessages = res.data || []
|
||
// 按articleId和parentId分类留言
|
||
const boardMsgs = []
|
||
const articleMsgsMap = new Map()
|
||
|
||
// 首先处理所有留言
|
||
const allMessagesWithReplies = allMessages.map(msg => ({
|
||
...msg,
|
||
replies: []
|
||
}))
|
||
// 分离主留言和回复
|
||
const mainMessages = []
|
||
const replies = []
|
||
|
||
allMessagesWithReplies.forEach(msg => {
|
||
if (msg.parentid && msg.parentid > 0) {
|
||
replies.push(msg)
|
||
} else {
|
||
mainMessages.push(msg)
|
||
}
|
||
})
|
||
// 将回复添加到对应的主留言中
|
||
replies.forEach(reply => {
|
||
const parentMsg = mainMessages.find(msg => msg.messageid === reply.parentid)
|
||
console.log('找到的父留言:', mainMessages)
|
||
if (parentMsg) {
|
||
parentMsg.replies.push(reply)
|
||
}
|
||
})
|
||
// 按articleId分类主留言
|
||
mainMessages.forEach(msg => {
|
||
if (msg.articleid) {
|
||
// 文章相关留言
|
||
if (!articleMsgsMap.has(msg.articleid)) {
|
||
articleMsgsMap.set(msg.articleid, [])
|
||
}
|
||
articleMsgsMap.get(msg.articleid).push(msg)
|
||
} else {
|
||
// 留言板留言
|
||
boardMsgs.push(msg)
|
||
}
|
||
})
|
||
// 转换文章留言Map为数组
|
||
articleRelatedData.value = Array.from(articleMsgsMap.entries()).map(([articleId, msgs]) => ({
|
||
articleId,
|
||
articleTitle: `文章 ${articleId}`, // 这里可以根据需要从其他地方获取文章标题
|
||
messages: msgs
|
||
}))
|
||
console.log('主留言和回复分离:', { mainMessages, replies })
|
||
messageBoardData.value = boardMsgs
|
||
|
||
console.log('获取留言列表成功:', { boardMessages: messageBoardData.value, articleMessages: articleRelatedData.value })
|
||
} catch (error) {
|
||
console.error('获取留言列表失败:', error)
|
||
ElMessage.error('获取留言失败,请稍后重试')
|
||
} finally {
|
||
loading.value = false
|
||
console.log('留言列表加载完成,共有' + messageBoardData.value.length + '条留言板留言')
|
||
}
|
||
}
|
||
|
||
// 处理回复
|
||
const handleReply = (msg) => {
|
||
replyingTo.value = {
|
||
id: msg.messageid,
|
||
nickname: msg.nickname || '匿名用户',
|
||
content: msg.content
|
||
}
|
||
form.parentid = msg.messageid
|
||
form.content = `@${replyingTo.value.nickname} `
|
||
// 滚动到输入框
|
||
setTimeout(() => {
|
||
document.querySelector('.message-form-section')?.scrollIntoView({ behavior: 'smooth' })
|
||
}, 100)
|
||
}
|
||
|
||
// 取消回复
|
||
const cancelReply = () => {
|
||
replyingTo.value = { id: null, nickname: '', content: '' }
|
||
form.replyid = null
|
||
form.content = ''
|
||
}
|
||
|
||
// 组件挂载时获取留言列表
|
||
onMounted(() => {
|
||
fetchMessages()
|
||
})
|
||
|
||
const form = reactive({
|
||
parentid: null,
|
||
content: '',
|
||
nickname: '',
|
||
email: '',
|
||
})
|
||
|
||
const onSubmit = async () => {
|
||
if (!form.content || !form.nickname) return
|
||
|
||
try {
|
||
submitting.value = true
|
||
|
||
if (form.replyid) {
|
||
// 回复模式
|
||
const res = await messageService.saveMessage({
|
||
content: form.content,
|
||
nickname: form.nickname,
|
||
email: form.email,
|
||
parentid: form.replyid
|
||
})
|
||
|
||
if (res.success) {
|
||
ElMessage.success('回复成功')
|
||
fetchMessages() // 重新获取列表
|
||
cancelReply()
|
||
} else {
|
||
ElMessage.error('回复失败:' + (res.message || '未知错误'))
|
||
}
|
||
} else {
|
||
// 普通留言
|
||
const res = await messageService.saveMessage({
|
||
content: form.content,
|
||
nickname: form.nickname,
|
||
email: form.email,
|
||
captcha: form.captcha
|
||
})
|
||
|
||
if (res.success) {
|
||
ElMessage.success('留言成功')
|
||
fetchMessages() // 重新获取列表
|
||
resetForm()
|
||
} else {
|
||
ElMessage.error('留言失败:' + (res.message || '未知错误'))
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('提交失败:', error)
|
||
ElMessage.error('网络错误,请稍后重试')
|
||
} finally {
|
||
submitting.value = false
|
||
}
|
||
}
|
||
|
||
// 重置表单
|
||
const resetForm = () => {
|
||
form.replyid = null
|
||
form.content = ''
|
||
form.nickname = ''
|
||
form.email = ''
|
||
form.captcha = ''
|
||
replyingTo.value = { id: null, nickname: '', content: '' }
|
||
}
|
||
|
||
const post_comment_reply_cancel = () => {
|
||
form.content = ''
|
||
form.replyid = null
|
||
replyingTo.value = { id: null, nickname: '', content: '' }
|
||
}
|
||
|
||
</script>
|
||
|
||
<style scoped>
|
||
.message-board {
|
||
max-width: 1000px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.message-list {
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.message-section {
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.message-section h4 {
|
||
color: #3498db;
|
||
margin-bottom: 15px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 2px solid #3498db;
|
||
}
|
||
|
||
.loading-container {
|
||
padding: 20px;
|
||
}
|
||
|
||
.message-item {
|
||
display: flex;
|
||
margin-bottom: 20px;
|
||
padding: 15px;
|
||
background-color: #f8f9fa;
|
||
border-radius: 8px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.message-item:hover {
|
||
background-color: #e9ecef;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.message-avatar-container {
|
||
margin-right: 15px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.message-avatar {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.message-content-container {
|
||
flex: 1;
|
||
}
|
||
|
||
.message-item-top {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.message-nickname {
|
||
font-weight: 600;
|
||
color: #2c3e50;
|
||
}
|
||
|
||
.message-time {
|
||
font-size: 0.8rem;
|
||
color: #7f8c8d;
|
||
}
|
||
|
||
.message-content {
|
||
color: #34495e;
|
||
line-height: 1.6;
|
||
margin-bottom: 10px;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.reply-to {
|
||
color: #e74c3c;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.message-item-bottom {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.reply-btn {
|
||
background: none;
|
||
border: none;
|
||
color: #3498db;
|
||
cursor: pointer;
|
||
font-size: 0.85rem;
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease;
|
||
}
|
||
|
||
.reply-btn.visible {
|
||
opacity: 1;
|
||
}
|
||
|
||
.reply-btn:hover {
|
||
color: #2980b9;
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.replies {
|
||
margin-top: 15px;
|
||
padding-left: 20px;
|
||
border-left: 3px solid #3498db;
|
||
}
|
||
|
||
.reply-item {
|
||
display: flex;
|
||
margin-bottom: 15px;
|
||
padding: 10px;
|
||
background-color: rgba(255, 255, 255, 0.7);
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.reply-item:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.message-empty {
|
||
text-align: center;
|
||
color: #7f8c8d;
|
||
padding: 40px;
|
||
background-color: #f8f9fa;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.message-form-section {
|
||
background-color: white;
|
||
padding: 20px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.message-form-section h2 {
|
||
color: #2c3e50;
|
||
margin-bottom: 20px;
|
||
font-size: 1.3rem;
|
||
}
|
||
|
||
.reply-preview {
|
||
background-color: #e8f4fd;
|
||
padding: 15px;
|
||
border-radius: 6px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.reply-preview-content {
|
||
margin-top: 10px;
|
||
padding: 10px;
|
||
background-color: rgba(255, 255, 255, 0.8);
|
||
border-radius: 4px;
|
||
font-style: italic;
|
||
color: #555;
|
||
}
|
||
|
||
.reply-cancel-btn {
|
||
background: none;
|
||
border: 1px solid #e74c3c;
|
||
color: #e74c3c;
|
||
padding: 4px 12px;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
margin-top: 10px;
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
.reply-cancel-btn:hover {
|
||
background-color: #e74c3c;
|
||
color: white;
|
||
}
|
||
|
||
.form-input-row {
|
||
display: flex;
|
||
gap: 15px;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.form-input-row .el-form-item {
|
||
flex: 1;
|
||
}
|
||
|
||
.article-message-group {
|
||
margin-bottom: 25px;
|
||
padding: 15px;
|
||
background-color: white;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.article-message-header {
|
||
margin-bottom: 15px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 1px solid #ecf0f1;
|
||
}
|
||
|
||
.article-title {
|
||
color: #3498db;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
.form-input-row {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.message-item {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.message-avatar-container {
|
||
margin-right: 0;
|
||
margin-bottom: 10px;
|
||
}
|
||
}
|
||
|
||
.message-list {
|
||
margin-bottom: 24px;
|
||
background: #f8fafd;
|
||
border-radius: 12px;
|
||
padding: 16px;
|
||
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;
|
||
}
|
||
|
||
.message-content {
|
||
color: #333;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.message-time {
|
||
font-size: 12px;
|
||
color: #aaa;
|
||
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;
|
||
padding: 32px 0;
|
||
}
|
||
|
||
.message-form-section {
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||
}
|
||
|
||
.message-form-section h2 {
|
||
font-size: 1.1rem;
|
||
margin-bottom: 18px;
|
||
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;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.form-input-row .el-form-item {
|
||
flex: 1;
|
||
}
|
||
|
||
.el-form-item .el-input__inner,
|
||
.el-form-item .el-textarea__inner {
|
||
min-height: 45px;
|
||
font-size: 1rem;
|
||
}
|
||
|
||
/* 加载状态 */
|
||
.loading-container {
|
||
padding: 40px 20px;
|
||
}
|
||
|
||
/* 过渡动画 */
|
||
.message-item-enter-active,
|
||
.message-item-leave-active {
|
||
transition: all 0.5s ease;
|
||
}
|
||
|
||
.message-item-enter-from,
|
||
.message-item-leave-to {
|
||
opacity: 0;
|
||
transform: translateY(20px);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.message-board {
|
||
padding: 8px 0;
|
||
}
|
||
|
||
.message-form-section {
|
||
padding: 12px;
|
||
}
|
||
|
||
.form-input-row {
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
}
|
||
</style> |