2025-07-16 15:07:16 +08:00

711 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<!-- 白色底部区域 -->
<view class="neighbor_body">
<!-- 正式页面内容 -->
<view class="body">
<view class="text-with-block">
<view class="left">
<text class="text">邻里互助详情</text>
<view class="slanted-block"></view>
</view>
</view>
<view class="interact_body">
<view class="body_item">
<view class="item_header">
<view class="left_section">
<image class="avatar" src="/static/imgs/index/nav.png"></image>
<view class="info">
<text class="name">樱桃小丸子</text>
<text class="date">03-24 07:30</text>
</view>
</view>
</view>
<view class="body_content">
<h3>寻找拼车伙伴</h3>
<view class="content_font">
每天早晨7:30从社区出发前往政务中心上班有同路的朋友可以拼车分担车费
</view>
<view class="content_img">
<image src="/static/imgs/index/swiper.png" mode="aspectFill"></image>
</view>
</view>
<!-- 评论输入框 -->
<view class="comment_input_area">
<input v-model="commentContent" class="comment_input" placeholder="写下你的评论..."
:focus="isInputFocus" @confirm="submitComment" />
<button class="submit_btn" @click="submitComment">发送</button>
</view>
<!-- 评论列表 -->
<view class="comment_list">
<view class="comment_title">全部评论 ({{ totalComments }})</view>
<!-- 主评论 -->
<view class="comment_item" v-for="(comment, index) in comments" :key="comment.id">
<image class="comment_avatar" :src="comment.avatar"></image>
<view class="comment_content">
<view class="comment_info">
<text class="comment_user">{{ comment.username }}</text>
<text class="comment_time">{{ comment.time }}</text>
</view>
<view class="comment_text">{{ comment.content }}</view>
<view class="comment_actions">
<text class="reply_btn" @click="showReplyInput(comment.id)">回复</text>
<text class="like_btn" @click="toggleCommentLike(comment)">
<image v-if="comment.isLiked" src="/static/icons/liked.png"
class="like_icon"></image>
<image v-else src="/static/icons/like.png" class="like_icon"></image>
{{ comment.likeCount || 0 }}
</text>
</view>
<!-- 回复输入框 -->
<view class="reply_input_area" v-if="activeReplyId === comment.id">
<input v-model="replyContent" class="reply_input" placeholder="写下你的回复..."
:focus="isReplyFocus" @confirm="submitReply(comment.id)" />
<button class="submit_btn" @click="submitReply(comment.id)">发送</button>
</view>
<!-- 子评论 -->
<view class="child_comments" v-if="comment.replies && comment.replies.length > 0">
<view class="child_comment_item" v-for="(reply, rIndex) in comment.replies"
:key="rIndex">
<image class="child_comment_avatar" :src="reply.avatar"></image>
<view class="child_comment_content">
<view class="comment_info">
<text class="comment_user">{{ reply.username }}</text>
<text class="comment_time">{{ reply.time }}</text>
</view>
<view class="comment_text">
<text class="reply_to"
v-if="reply.replyTo">@{{ reply.replyTo }}</text>
{{ reply.content }}
</view>
<view class="comment_actions">
<text class="reply_btn"
@click="showReplyInput(comment.id, reply.username)">回复</text>
<text class="like_btn" @click="toggleReplyLike(comment, reply)">
<image v-if="reply.isLiked" src="/static/icons/liked.png"
class="like_icon"></image>
<image v-else src="/static/icons/like.png" class="like_icon">
</image>
{{ reply.likeCount || 0 }}
</text>
</view>
</view>
</view>
<!-- 查看更多回复 -->
<view class="view_more_replies"
v-if="comment.replies.length < comment.replyCount"
@click="loadMoreReplies(comment)">
查看全部{{ comment.replyCount }}条回复
</view>
</view>
</view>
</view>
<view v-if="comments.length === 0" class="no_comments">
暂无评论快来发表第一条评论吧~
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 引入底部 -->
<Footer></Footer>
</view>
</template>
<script>
import Footer from '@/components/footer_common.vue';
export default {
components: {
Footer
},
data() {
return {
commentContent: '',
replyContent: '',
isInputFocus: false,
isReplyFocus: false,
activeReplyId: null,
replyToUser: '',
comments: [{
id: 1,
username: '邻居老王',
avatar: '/static/imgs/index/nav.png',
content: '我正好也去政务中心上班,可以一起拼车吗?',
time: '03-24 09:30',
isLiked: false,
likeCount: 3,
replyCount: 2,
replies: [{
id: 11,
username: '樱桃小丸子',
avatar: '/static/imgs/index/nav.png',
content: '当然可以啊,你住在哪栋楼?',
time: '03-24 09:45',
isLiked: true,
likeCount: 1,
replyTo: '邻居老王'
},
{
id: 12,
username: '邻居老王',
avatar: '/static/imgs/index/nav.png',
content: '我住3栋你呢',
time: '03-24 09:50',
isLiked: false,
likeCount: 0,
replyTo: '樱桃小丸子'
}
]
},
{
id: 2,
username: '社区小李',
avatar: '/static/imgs/index/nav.png',
content: '我每周一三五去那边,可以固定拼车',
time: '03-24 10:15',
isLiked: false,
likeCount: 1,
replyCount: 1,
replies: [{
id: 21,
username: '樱桃小丸子',
avatar: '/static/imgs/index/nav.png',
content: '好的,那周一见!',
time: '03-24 10:30',
isLiked: false,
likeCount: 0,
replyTo: '社区小李'
}]
}
]
}
},
computed: {
totalComments() {
let count = this.comments.length;
this.comments.forEach(comment => {
count += comment.replies.length;
});
return count;
}
},
methods: {
submitComment() {
if (this.commentContent.trim() === '') return;
const newComment = {
id: Date.now(),
username: '当前用户',
avatar: '/static/imgs/index/nav.png',
content: this.commentContent,
time: this.formatDate(new Date()),
isLiked: false,
likeCount: 0,
replyCount: 0,
replies: []
};
this.comments.unshift(newComment);
this.commentContent = '';
this.isInputFocus = false;
},
showReplyInput(commentId, replyTo = '') {
this.activeReplyId = commentId;
this.replyToUser = replyTo;
this.replyContent = '';
this.$nextTick(() => {
this.isReplyFocus = true;
});
},
submitReply(commentId) {
if (this.replyContent.trim() === '') return;
const parentComment = this.comments.find(c => c.id === commentId);
if (!parentComment) return;
const newReply = {
id: Date.now(),
username: '当前用户',
avatar: '/static/imgs/index/nav.png',
content: this.replyContent,
time: this.formatDate(new Date()),
isLiked: false,
likeCount: 0,
replyTo: this.replyToUser || parentComment.username
};
parentComment.replies.push(newReply);
parentComment.replyCount = parentComment.replies.length;
this.replyContent = '';
this.activeReplyId = null;
this.isReplyFocus = false;
this.replyToUser = '';
},
toggleCommentLike(comment) {
comment.isLiked = !comment.isLiked;
comment.likeCount += comment.isLiked ? 1 : -1;
},
toggleReplyLike(comment, reply) {
reply.isLiked = !reply.isLiked;
reply.likeCount += reply.isLiked ? 1 : -1;
},
loadMoreReplies(comment) {
// 模拟加载更多回复
const newReplies = [{
id: Date.now() + 1,
username: '热心邻居',
avatar: '/static/imgs/index/nav.png',
content: '我也想去政务中心,可以一起吗?',
time: '03-25 08:00',
isLiked: false,
likeCount: 0,
replyTo: comment.username
},
{
id: Date.now() + 2,
username: '社区志愿者',
avatar: '/static/imgs/index/nav.png',
content: '拼车注意安全哦!',
time: '03-25 08:30',
isLiked: false,
likeCount: 0,
replyTo: comment.username
}
];
comment.replies = [...comment.replies, ...newReplies];
comment.replyCount = comment.replies.length;
},
formatDate(date) {
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${month}-${day} ${hours}:${minutes}`;
}
}
}
</script>
<style lang="scss" scoped>
.container {
display: flex;
flex-direction: column;
align-items: center;
background: #f8faff;
min-height: 100vh;
padding-bottom: 120rpx;
.neighbor_head {
width: 100%;
height: 360rpx;
opacity: 1;
background: linear-gradient(270deg, rgba(146, 161, 252, 1) 0%, rgba(181, 229, 255, 1) 100%);
position: relative;
}
.neighbor_body {
width: 100%;
background: #fff;
min-height: calc(100vh - 360rpx);
border-top-left-radius: 40rpx;
border-top-right-radius: 40rpx;
position: relative;
top: -40rpx;
z-index: 9;
padding-bottom: 120rpx;
box-shadow: 0 -10rpx 20rpx rgba(0, 0, 0, 0.05);
.body {
width: 90%;
margin: 0 auto;
padding-top: 40rpx;
.text-with-block {
display: flex;
justify-content: space-between;
padding: 20rpx 0;
height: 60rpx;
/* 增加高度 */
margin-bottom: 40rpx;
position: relative;
.left {
position: relative;
display: flex;
align-items: center;
.text {
font-size: 38rpx;
font-weight: bold;
color: #333;
position: relative;
z-index: 2;
padding-left: 20rpx;
background: linear-gradient(to right, #4a8cff, #80b3ff);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: 0 2rpx 4rpx rgba(74, 140, 255, 0.2);
}
.slanted-block {
position: absolute;
left: 0;
bottom: 0;
transform: skewX(-15deg);
width: 240rpx;
height: 16rpx;
/* 调整为更细的线条 */
background: linear-gradient(90deg, rgba(74, 140, 255, 0.6), rgba(128, 179, 255, 0.3));
z-index: 1;
border-radius: 8rpx;
transition: all 0.3s ease;
}
&:hover .slanted-block {
height: 20rpx;
background: linear-gradient(90deg, rgba(74, 140, 255, 0.8), rgba(128, 179, 255, 0.5));
}
}
}
.interact_body {
width: 100%;
margin-top: 20rpx;
.body_item {
margin: 0 auto 40rpx;
background: #fff;
border-radius: 24rpx;
padding: 30rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.item_header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 30rpx;
border-bottom: 1rpx solid #f5f5f5;
.left_section {
display: flex;
align-items: center;
.avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 20rpx;
border: 2rpx solid #f0f0f0;
}
.info {
display: flex;
flex-direction: column;
.name {
font-size: 30rpx;
color: #333;
font-weight: 500;
}
.date {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
}
}
}
}
.body_content {
padding: 30rpx 0;
h3 {
font-weight: 600;
height: 80rpx;
line-height: 80rpx;
color: #333;
font-size: 34rpx;
margin-bottom: 10rpx;
}
.content_font {
font-weight: normal;
color: #555;
font-size: 28rpx;
line-height: 48rpx;
margin-top: 20rpx;
}
.content_img {
width: 100%;
height: 400rpx;
margin: 40rpx 0;
border-radius: 16rpx;
overflow: hidden;
background: #f5f5f5;
image {
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
/* 评论输入框区域 */
.comment_input_area {
display: flex;
align-items: center;
margin: 30rpx 0;
padding: 20rpx 0;
border-top: 1rpx solid #f5f5f5;
border-bottom: 1rpx solid #f5f5f5;
.comment_input {
flex: 1;
height: 80rpx;
background: #f8f8f8;
border-radius: 40rpx;
padding: 0 30rpx;
font-size: 28rpx;
color: #333;
}
.submit_btn {
width: 140rpx;
height: 80rpx;
line-height: 80rpx;
background: #4a8cff;
color: #fff;
border-radius: 40rpx;
margin-left: 20rpx;
font-size: 28rpx;
font-weight: normal;
}
}
/* 评论列表 */
.comment_list {
margin-top: 20rpx;
.comment_title {
font-size: 30rpx;
font-weight: 600;
color: #333;
margin-bottom: 30rpx;
padding-left: 10rpx;
}
.comment_item {
display: flex;
padding: 30rpx 0;
border-bottom: 1rpx solid #f5f5f5;
.comment_avatar {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
margin-right: 20rpx;
border: 2rpx solid #f0f0f0;
}
.comment_content {
flex: 1;
.comment_info {
display: flex;
align-items: center;
margin-bottom: 10rpx;
.comment_user {
font-size: 28rpx;
color: #4a8cff;
font-weight: 500;
margin-right: 20rpx;
}
.comment_time {
font-size: 24rpx;
color: #999;
}
}
.comment_text {
font-size: 28rpx;
color: #333;
line-height: 1.6;
margin-bottom: 20rpx;
.reply_to {
color: #4a8cff;
margin-right: 10rpx;
}
}
.comment_actions {
display: flex;
align-items: center;
.reply_btn,
.like_btn {
font-size: 24rpx;
color: #999;
margin-right: 30rpx;
display: flex;
align-items: center;
.like_icon {
width: 28rpx;
height: 28rpx;
margin-right: 8rpx;
}
}
.reply_btn {
&:active {
color: #4a8cff;
}
}
.like_btn {
&:active {
color: #ff4d4f;
}
}
}
/* 回复输入框 */
.reply_input_area {
display: flex;
align-items: center;
margin-top: 20rpx;
padding: 20rpx 0;
.reply_input {
flex: 1;
height: 70rpx;
background: #f8f8f8;
border-radius: 35rpx;
padding: 0 30rpx;
font-size: 26rpx;
color: #333;
}
.submit_btn {
width: 120rpx;
height: 70rpx;
line-height: 70rpx;
background: #4a8cff;
color: #fff;
border-radius: 35rpx;
margin-left: 20rpx;
font-size: 26rpx;
font-weight: normal;
}
}
/* 子评论 */
.child_comments {
margin-top: 20rpx;
padding-left: 20rpx;
border-left: 4rpx solid #f0f0f0;
.child_comment_item {
display: flex;
padding: 25rpx 0;
border-bottom: 1rpx dashed #f0f0f0;
&:last-child {
border-bottom: none;
}
.child_comment_avatar {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
margin-right: 20rpx;
border: 2rpx solid #f0f0f0;
}
.child_comment_content {
flex: 1;
.comment_info {
display: flex;
align-items: center;
margin-bottom: 8rpx;
.comment_user {
font-size: 26rpx;
color: #4a8cff;
font-weight: 500;
margin-right: 15rpx;
}
.comment_time {
font-size: 22rpx;
color: #999;
}
}
.comment_text {
font-size: 26rpx;
color: #555;
line-height: 1.5;
margin-bottom: 15rpx;
}
.comment_actions {
.reply_btn,
.like_btn {
font-size: 22rpx;
}
}
}
}
.view_more_replies {
font-size: 24rpx;
color: #4a8cff;
padding: 15rpx 0;
text-align: center;
&:active {
opacity: 0.8;
}
}
}
}
}
.no_comments {
text-align: center;
color: #999;
font-size: 28rpx;
padding: 60rpx 0;
background: #fafafa;
border-radius: 12rpx;
margin-top: 30rpx;
}
}
}
}
}
}
}
</style>